不确定将每个加载为 2 个字节的双精度值规范化

Unsure of normalising double values loaded as 2 bytes each

我用来将 .wav 文件数据读入二维数组的代码:

        int signal_frame_width = wavHeader.SamplesPerSec / 100; //10ms frame
        int total_number_of_frames = numSamples / signal_frame_width;
        double** loadedSignal = new double *[total_number_of_frames]; //array that contains the whole signal

        int iteration = 0;
        int16_t* buffer = new int16_t[signal_frame_width];
        while ((bytesRead = fread(buffer, sizeof(buffer[0]), signal_frame_width, wavFile)) > 0)
        {
            loadedSignal[iteration] = new double[signal_frame_width];
            for(int i = 0; i < signal_frame_width; i++){
                //value normalisation:
                int16_t c = (buffer[i + 1] << 8) | buffer[i];
                double normalisedValue = c/32768.0;

                loadedSignal[iteration][i] = normalisedValue;
            }
            iteration++;
        }

问题出在这部分,我不太明白它是如何工作的:

int16_t c = (buffer[i + 1] << 8) | buffer[i];

这是取自 here 的示例。 我只处理 16 位 .wav 文件。如您所见,我的缓冲区正在加载(例如采样频率 = 44.1kHz)441 个元素(每个元素都是 2 字节带符号的样本)。我应该如何更改上面的代码?

您构建代码的原始示例使用了一个数组,其中每个单独的元素代表一个 字节 。因此它需要将两个连续的字节组合成一个 16 位的值,这就是这一行所做的:

int16_t c = (buffer[i + 1] << 8) | buffer[i];

它将索引 i+1 处的字节(此处假定为最高有效字节)左移 8 个位置,然后将索引 i 处的字节与其进行或运算。例如,如果 buffer[i+1]==0x12buffer[i]==0x34,那么您会得到

buffer[i+1] << 8 == 0x12 << 8 == 0x1200
0x1200 | buffer[i] == 0x1200 | 0x34 == 0x1234

| 运算符是按位或。)

请注意,您需要注意您的 WAV 文件是 little-endian 还是 big-endian(但是 original post 解释得很好)。

现在,如果将结果值存储在 有符号 16 位整数中,您将得到介于 −32768 和 +32767 之间的值。实际归一化步骤(除以 32768)的要点只是将值范围降低到 [−1.0, 1.0]。

在你上面的例子中,你似乎已经读入了一个 16 位值的缓冲区。请注意,只有当您的平台的字节序与您正在使用的 WAV 文件的字节序相匹配时,您的代码才会起作用。但是如果这个假设是正确的,那么你不需要你不理解的代码行。您可以直接将每个数组元素转换为双精度数:

double normalisedValue = buffer[i]/32768.0;

如果 buffer 是一个字节数组,那么这段代码会将两个连续的字节解释为一个 16 位整数(假设 little-endian 编码)。 | 运算符将对两个字节的位执行 bit-wise OR。由于我们希望将这两个字节解释为单个 2 字节整数,因此我们必须将其中一个字节的位向左移动 8 位(1 字节)。哪一个取决于它们是按 little-endian 还是 big-endian 顺序排列。 Little-endian表示最低有效字节在前,所以我们将第二个字节向左移动8位。

示例:

First byte: 0101 1100
Second byte: 1111 0100

现在移动第二个字节:

Second "byte": 1111 0100 0000 0000
First "byte":  0000 0000 0101 1100

按位OR-operation(如果其中一个为1,则为1。如果均为0,则为0): 16 位整数:1111 0100 0101 1100

然而,在您的情况下,您的文件中的字节已经被解释为 16 位整数,使用平台具有的任何字节顺序。所以你不需要这一步。但是,为了正确解释文件中的字节,必须假定与写入时相同的 byte-order。因此,通常添加此步骤以确保代码独立于平台的字节顺序工作,而不是依赖文件的预期 byte-order(因为大多数文件格式将指定 byte-order 应该是什么)。