6

我了解 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”)。

4

1 回答 1

9

.wav 文件通常以小端格式存储其 PCM 数据,每个通道每个样本 16 位。对于通常有符号的 16 位 PCM 文件,这意味着数据物理存储为

[LEFT LSB] [LEFT MSB] [RIGHT LSB] [RIGHT MSB] ...

这样每组 4 个字节组成一个立体声 PCM 样本。i因此,您可以通过查看字节4*i来找到样本4*i+3,包括在内。

要从两个字节解码单个 16 位值,请执行以下操作:

(MSB << 8) | LSB

因为您的读取缓冲区值存储为有符号字符,所以您必须小心一点,因为两者都MSBLSB被符号扩展。这对于 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] 范围内的浮点值)。

于 2014-06-17T22:43:35.107 回答