【问题标题】:Convert 24 bit signed int signal to 16 bit signed int signal将 24 位有符号整数信号转换为 16 位有符号整数信号
【发布时间】:2015-02-03 04:42:55
【问题描述】:

尽可能简单地说。

  1. 我从设备接收数据并且信号是 24 位有符号整数。
  2. 我想创建 16 位 WAV 文件。
  3. 为此,我想继续将即将到来的包含 256 个样本的音频缓冲区写入 *.wav 文件流。
  4. 如何将缓冲区值(用指向 256 元素数组的简单指针写入的井值)从 [24 位有符号整数] 转换为 [16 位有符号整数]。

有缩放值吗?例如,当我想将 [24 bit signed int] 转换为 [24 bit signed double] 我是这样做的:

for (int i=0; i<256; i++) {
   bufferInDouble = *((int*)bufferRawData[currentPan]+i) / (double)0x7fffffff;
}

有没有类似的方法?

【问题讨论】:

  • 设备是否对带限信号进行采样?您是否在 24 位过采样?
  • 抱歉 :D 在所有这些位/数据操作方面,我有点新手。在设备文档中有一条信息:“系统使用 24 位信号路径处理所有数据,无论 I/O 格式如何。您可以以任何支持的采样率录制和播放 16 位或 24 位音频文件”。我想知道这是否足以回答您的问题。
  • 在那种情况下,我认为@jaket 的回答就足够了

标签: c++ audio casting signal-processing


【解决方案1】:

该方法会有所不同,具体取决于您是打包 24 位数据还是仅将 24 位数据存储在 32 位字中。我将假设它没有被压缩并且在整数内左对齐,因为这是最常见的。

技术是简单地将数据向下转换为 16 位数据类型

ABCDEFxx => ABCD 

这里我展示了一个 32 位整数的半字节,其中 ABCDEF 包含 24 位音频数据,而 xx 字节不包含任何值。右移 16 位将丢弃 EFxx

short outBuffer[256]
int* inBuffer = ((int*)bufferRawData);
for (int i = 0 ; i < 256 ; ++i)
{
    outBuffer[i] = (short)(inBuffer[i] >> 16);
}

附带说明,在降低位深度时通常需要在 LSB 中添加一些低电平噪声以减少量化失真。这称为抖动。有关信息,请参阅维基百科上的这篇文章Dither

【讨论】:

  • 谢谢。它起作用了,现在我可以识别我创建的 *.wav 中的单词,尽管我的信号比原始信号短 0.5 倍并且速度快 2 倍。看起来我错过了一个平底锅,只听左边的样本......但这是我现在必须处理的另一个问题。
  • 如果您想降低真实 24 位音频的位深度,则抖动不是可选的。如果您跳过抖动步骤,则会引入失真,诚然在非常低的水平,但如果信号具有巨大的动态范围,则可以听到。
【解决方案2】:

一般的答案是除以 256。请注意,右移不能与signed ints(在您的问题中指定)一起使用:5.8/3 in the C++ Standard 表示行为E1 &gt;&gt; E2,其中E1 是一个负符号值,实现定义

另一种可能保留更高音频质量的方法是在现有数据中找到最高幅度值(因此只有在开始缩放之前收到所有数据时才能这样做) - 我们称之为 M - 然后计算出一个缩放因子 F 使得最高值具有高达 16 位有符号值的新幅度:即 32767。

M / F = 32767.0, so
F = M / 32767.0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    相关资源
    最近更新 更多