不确定是否规范化加载为每个2字节的双值

Unsure of normalising double values loaded as 2 bytes each

本文关键字:字节 是否 规范化 加载 不确定      更新时间:2023-10-16

我用来将.wav文件数据读取到2D数组中的代码:

        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];

这是从这里取的例子。我只处理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

|运算符是位OR。)

请注意,您需要小心WAV文件是小端还是大端(但最初的文章很好地解释了这一点)。

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

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

double normalisedValue = buffer[i]/32768.0;

如果buffer是一个字节数组,那么这段代码将把两个连续的字节解释为一个16位整数(假设小端编码)。|运算符将对两个字节的位执行逐位OR。由于我们希望将两个字节解释为单个2字节整数,因此我们必须将其中一个字节的位向左移动8位(1字节)。哪一个取决于它们是按小端序还是大端序排列。Little endian意味着最低有效字节位于第一位,因此我们将第二个字节向左移动8位。

示例:

First byte: 0101 1100
Second byte: 1111 0100

现在移位第二个字节:

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

逐位OR运算(如果其中一个为1,则为1。如果两者都为0,则为0):16位整数:1111 0100 0101 1100

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