Search code examples
c++audiowav

C++ reading 16bit Wav file


I'm having trouble reading in a 16bit .wav file. I have read in the header information, however, the conversion does not seem to work.

For example, in Matlab if I read in wave file I get the following type of data:

 -0.0064, -0.0047,  -0.0051, -0.0036, -0.0046, -0.0059,  -0.0051

However, in my C++ program the following is returned:

0.960938, -0.00390625, -0.949219, -0.00390625, -0.996094, -0.00390625

I need the data to be represented the same way. Now, for 8 bit .wav files I did the following:

uint8_t c;

for(unsigned i=0; (i < size); i++)
{
    c = (unsigned)(unsigned char)(data[i]);
    double t = (c-128)/128.0;
    rawSignal.push_back(t);
}

This worked, however, when I did this for 16bit:

uint16_t c;

for(unsigned i=0; (i < size); i++)
{
   c = (signed)(signed char)(data[i]);
   double t = (c-256)/256.0;
   rawSignal.push_back(t);
}

Does not work and shows the output (above).

I'm following the standards found Here

Where data is a char array and rawSignal is a std::vector<double> I'm probably just handing the conversion wrong but cannot seem to find out where. Anyone have any suggestions?

Thanks

EDIT:

This is what is now displaying (In a graph):

enter image description here

This is what it should be displaying:

enter image description here


Solution

  • There are a few problems here:

    • 8 bit wavs are unsigned, but 16 bit wavs are signed. Therefore, the subtraction step given in the answers by Carl and Jay are unnecessary. I presume they just copied from your code, but they are wrong.
    • 16 bit waves have a range from -32,768 to 32,767, not from -256 to 255, making the multiplication you are using incorrect anyway.
    • 16-bit wavs are 2 bytes, thus you must read two bytes to make one sample, not one. You appear to be reading one character at a time. When you read the bytes, you may have to swap them if your native endianness is not little-endian.

    Assuming a little-endian architecture, your code would look more like this (very close to Carl's answer):

    for (int i = 0; i < size; i += 2)
    {
        int c = (data[i + 1] << 8) | data[i];
        double t = c/32768.0;
        rawSignal.push_back(t);
    }
    

    for a big-endian architecture:

    for (int i = 0; i < size; i += 2)
    {
        int c = (data[i] << 8) | data[i+1];
        double t = c/32768.0;
        rawSignal.push_back(t);
    }
    

    That code is untested, so please LMK if it doesn't work.