mackron / dr_libs

Audio decoding libraries for C/C++, each in a single source file.
Other
1.24k stars 205 forks source link

dr_wav 0.13.8 fails to load a .wav (invalid data) #252

Closed SirLynix closed 1 year ago

SirLynix commented 1 year ago

Hi there!

I'm trying to load a one-second .wav (which loads fine using my browser and VLC) but it fails with weird values (max u32): image

Here's my code (basically drwav_init + drwav_read_pcm_frames_s16):

        Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
        {
            drwav wav;
            if (!drwav_init(&wav, &ReadWavCallback, &SeekWavCallback, &stream, nullptr))
                return Err(ResourceLoadingError::Unrecognized);

            CallOnExit uninitOnExit([&] { drwav_uninit(&wav); });

            std::optional<AudioFormat> formatOpt = GuessAudioFormat(wav.channels);
            if (!formatOpt)
            {
                NazaraError("unexpected channel count: " + std::to_string(wav.channels));
                return Err(ResourceLoadingError::Unsupported);
            }

            AudioFormat format = *formatOpt;

            UInt64 sampleCount = wav.totalPCMFrameCount * wav.channels;
            std::unique_ptr<Int16[]> samples = std::make_unique<Int16[]>(sampleCount); //< std::vector would default-init to zero

            if (drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, samples.get()) != wav.totalPCMFrameCount)
            {
                NazaraError("failed to read stream content");
                return Err(ResourceLoadingError::DecodingError);
            }
            if (parameters.forceMono && format != AudioFormat::I16_Mono)
            {
                MixToMono(samples.get(), samples.get(), static_cast<UInt32>(wav.channels), wav.totalPCMFrameCount);

                format = AudioFormat::I16_Mono;
                sampleCount = wav.totalPCMFrameCount;
            }

            return std::make_shared<SoundBuffer>(format, sampleCount, wav.sampleRate, samples.get());
        }

Here's the .wav: https://digitalpulse.software/VousFumezMonsieur.wav

Since other .wav are loading it I expect this .wav is particular (maybe malformed), but I was thinking you would be interested by it.

mackron commented 1 year ago

Thanks for the report. The link to that WAV file is giving me a 404 error. Can you perhaps email it to me?

That dataChunkDataSize definitely looks suspicious (it's set to 0xFFFFFFFF from what I can tell). If I had to guess, I would says it's a malformed file like you suggest, but I'm happy to see if I can get it to work.

SirLynix commented 1 year ago

Sorry, wrong URL, here's the working one: https://files.digitalpulse.software/VousFumezMonsieur.wav

I tried to open it using VLC, Opera and Audacity, all three succeeded.

mackron commented 1 year ago

Thanks. I've pushed a fix to the dev branch. You able to give that a try?

This is happening because the size of the "data" and "RIFF" chunks have not been set properly in the file. It's being set to 0xFFFFFFFF like I suspected which is obviously incorrect. I had to fix this by checking for this specific situation and then calculating the size by simply reading-and-discarding until EOF, accumulating the number of bytes read as it goes (dr_wav does not support querying of the file size in it's backend). Loading invalid files like this will be slightly less efficient due to this workaround. Also, this assumes that the data goes all the way to the end of the file, which is not strictly always the case for WAV. That means if you have a similar file to this, but it's got non-audio chunks at the end of the file, you'll get glitching at the end of the sound.

SirLynix commented 1 year ago

Thank you for the quick fix!