naudio / NAudio

Audio and MIDI library for .NET
MIT License
5.58k stars 1.1k forks source link

SampleProvider AIFF 8-bit PCM audio data signedness #1178

Open karip opened 3 months ago

karip commented 3 months ago

AIFF files with 8-bit PCM audio data are read incorrectly by NAudio. Audio bytes seem to be read as unsigned 8-bit integers, but they should be read as signed 8-bit integers.

The test file aiff-samplesize-8.aiff contains 8-bit PCM audio data and it's raw audio data starts with these hex values: 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x80, 0x83, 0x85, 0x88, 0x8A, 0x8D, 0x8F, 0x92

Those hex values can be converted to signed decimal values [-128, 127]: 10, 10, 10, 10, 10, 10, 10, 10, -128, -125, -123, -120, -118, -115, -113, -110

Those values can be converted to floating point values [-1.0, 1.0] by dividing by 128: 0.078125, 0.078125, 0.078125, 0.078125, 0.078125, 0.078125, 0.078125, 0.078125, -1, -0.9765625, -0.9609375, -0.9375, -0.921875, -0.8984375, -0.8828125, -0.859375

However, reading the file with NAudio SampleProvider returns these floating point values: -0.921875, -0.921875, -0.921875, -0.921875, -0.921875, -0.921875, -0.921875, -0.921875, 0, 0.0234375, 0.0390625, 0.0625, 0.078125, 0.1015625, 0.1171875, 0.140625

So, it seems that NAudio isn't reading the values correctly.

I had a look at the NAudio source code. It is using SampleProviderConverters:22 to convert raw audio data to samples. Pcm8BitToSampleProvider:33 converts bytes to floating point values: buffer[outIndex++] = sourceBuffer[n] / 128f - 1.0f;

It should be converting bytes to floating point values like this: buffer[outIndex++] = sourceBuffer[n] < 128 ? sourceBuffer[n] / 128f : (sourceBuffer[n] / 128f) - 2.0f;

Of course, Pcm8BitToSampleProvider.cs can't be changed, because then it would return incorrect values for WAV files, which contain unsigned 8-bit data.

karip commented 3 months ago

I think this could be fixed by converting 8-bit bytes from signed to unsigned in AiffFileReader. It already performs endianness conversion for AIFF files, so it could also perform signedness conversion.