Question

I'm trying to convert 16 bit stereo sound from a WAVE file to 16 bit mono sound, but I'm having some struggle. I've tried to convert 8 bit stereo sound to mono and it's working great. Here's the piece of code for that:

if( bitsPerSample == 8 )
{
    dataSize /= 2;
    openALFormat = AL_FORMAT_MONO8;

    for( SizeType i = 0; i < dataSize; i++ )
    {
        pData[ i ] = static_cast<Uint8>(
                        (   static_cast<Uint16>( pData[ i * 2 ] ) +
                        static_cast<Uint16>( pData[ i * 2 + 1 ] ) ) / 2
        );
    }

But, now I'm trying to do pretty much the same with 16 bit audio, but I just can't get it to work. I can just hear some kind of weird noise. I've tried to set "monoSample" to "left"(Uint16 monoSample = left;) and the audio data from that channel works very well. The right channel as well. Can anyone of you see what I'm doing wrong? Here's the code(pData is an array of bytes):

if( bitsPerSample == 16 )
{
    dataSize /= 2;
    openALFormat = AL_FORMAT_MONO16;

    for( SizeType i = 0; i < dataSize / 2; i++ )
    {
        Uint16 left =   static_cast<Uint16>( pData[ i * 4 ] ) |
                        ( static_cast<Uint16>( pData[ i * 4 + 1 ] ) << 8 );

        Uint16 right =  static_cast<Uint16>( pData[ i * 4 + 2 ] ) |
                        ( static_cast<Uint16>( pData[ i * 4 + 3 ] ) << 8 );

        Uint16 monoSample = static_cast<Uint16>(
                                (   static_cast<Uint32>( left ) +
                                static_cast<Uint32>( right ) ) / 2
            );

        // Set the new mono sample.
        pData[ i * 2 ] =  static_cast<Uint8>( monoSample );
        pData[ i * 2 + 1 ] =  static_cast<Uint8>( monoSample >> 8 );
    }
}
Was it helpful?

Solution

In a 16 bit stereo WAV file, each sample is 16 bits, and the samples are interleaved. I'm not sure why you're using a bitwise OR, but you can just retrieve the data directly without having to shift. The below non-portable code (assumes sizeof(short) == 2) illustrates this.

unsigned size = header.data_size;
char *data = new char[size];

// Read the contents of the WAV file in to data

for (unsigned i = 0; i < size; i += 4)
{
  short left = *(short *)&data[i];
  short right = *(short *)&data[i + 2];
  short monoSample = (int(left) + right) / 2;
}

Also, while 8 bit WAV files are unsigned, 16 bit WAV files are signed. To average them, make sure you store it in an appropriately sized signed type. Note that one of the samples is promoted to an int temporarily to prevent overflow.

As has been pointed out in the comments below by Stix, simple averaging may not give the best results. Your mileage may vary.

In addition, Greg Hewgill correctly noted that this assumes that the machine is little-endian.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top