解交织 PCM (*.wav) 立体声音频数据
Posted
技术标签:
【中文标题】解交织 PCM (*.wav) 立体声音频数据【英文标题】:Deinterleaving PCM (*.wav) stereo audio data 【发布时间】:2014-08-07 13:37:01 【问题描述】:我了解 PCM 数据存储为 [left][right][left][right]...
。我正在尝试将立体声 PCM 转换为单声道 Vorbis (*.ogg),据我所知,这可以通过将左右声道减半 ((left+right)*0.5) 来实现。我实际上是通过像这样修改 libvorbis sdk 中的编码器示例来实现的,
#define READ 1024
signed char readbuffer[READ*4];
这样就读取了 PCM 数据
fread(readbuffer, 1, READ*4, stdin)
然后我将两个通道减半,
buffer[0][i] = ((((readbuffer[i*4+1]<<8) | (0x00ff&(int)readbuffer[i*4]))/32768.f) + (((readbuffer[i*4+3]<<8) | (0x00ff&(int)readbuffer[i*4+2]))/32768.f)) * 0.5f;
它工作得很好,但是,我不明白他们如何从 PCM 数据中解交织左右声道(即所有的位移和“ANDing”和“ORing”)。
【问题讨论】:
readbuffer是如何声明的?#define READ 1024
signed char readbuffer[READ*4];
【参考方案1】:
.wav 文件通常以 little endian 格式存储其 PCM 数据,每个通道每个样本 16 位。对于通常有符号的 16 位 PCM 文件,这意味着数据物理存储为
[LEFT LSB] [LEFT MSB] [RIGHT LSB] [RIGHT MSB] ...
这样每组 4 个字节组成一个立体声 PCM 样本。因此,您可以通过查看字节 4*i
到 4*i+3
,找到示例 i
。
要从两个字节解码单个 16 位值,请执行以下操作:
(MSB << 8) | LSB
由于您的读取缓冲区值存储为 有符号 字符,因此您必须小心,因为 MSB
和 LSB
都将进行符号扩展。这对于 LSB 来说是不可取的;因此,代码使用
0xff & (int)LSB
获取低字节的无符号版本(从技术上讲,这是通过向上转换为 int 并选择低 8 位来实现的;另一种表述是只写 (uint8_t)LSB
)。
请注意,MSB 位于索引 1 和 3,而 LSB 位于索引 0 和 2。所以,
((readbuffer[i*4+1]<<8) | (0x00ff&(int)readbuffer[i*4]))
和
((readbuffer[i*4+3]<<8) | (0x00ff&(int)readbuffer[i*4+2]))
只是通过使用一些位操作将字节组装成数字,将左右通道的值作为 16 位有符号值。
然后,这些值中的每一个都除以 32768.0。请注意,带符号的 16 位值的范围为 [-32768, 32767]
。因此,除以 32768 得出的范围约为 [-1, 1]。将两个相加得到一个 [-2, 2] 范围内的数字,然后将整个乘以 0.5 得到平均值(一个 [-1, 1] 范围内的浮点值)。
【讨论】:
感谢您的回复,现在我完全理解了,但另一个问题突然出现在我的脑海中。如果 PCM 是每个通道每个样本 8 位,这是否意味着每 2 个字节构成一个单通道立体声样本?如果是这样,如何从 1 字节中获取单个 16 位值(填充将如何) @Osofem - 你的第一个问题是。至于第二个问题,上下文是什么?您是否尝试将一些 8 位 PCM 输入数据保存为 WAV 格式(16 位)?如果是这样,您有两个选择:保存为 8 位 WAV 或将 8 位拉伸为 16 位。无论哪种方式,请作为单独的问题发布。以上是关于解交织 PCM (*.wav) 立体声音频数据的主要内容,如果未能解决你的问题,请参考以下文章