ddiakopoulos / libnyquist

:microphone: Cross platform C++11 library for decoding audio (mp3, wav, ogg, opus, flac, etc)
BSD 2-Clause "Simplified" License
534 stars 64 forks source link

Fix in-memory WavPack decoding #45

Closed mikeperri closed 3 years ago

mikeperri commented 3 years ago

Hi all,

I noticed that in-memory WavPack decoding wasn't working. I'm pretty sure this was because the WavpackStreamReader64 implementation didn't acknowledge the decoder->dataPos variable in the read_bytes function. So the actual WavPack library expected the file cursor to advance after calling read_bytes, but it always got data starting from byte 0.

I tried changing the read_bytes function so it copied the requested bytes starting from decoder->dataPos, and that worked, BUT when the WavPackInteral destructor was called, which calls WavpackCloseFile, for some reason I got a segfault inside the WavPack library. I started reading the WavPack docs to figure out what might be going on, and I didn't figure out what the problem was, but I noticed there is a function called WavpackOpenRawDecoder that is intended to be used to decode a WavPack file in memory.

I decided to just re-implement WavPackDecoder::LoadFromBuffer with that function. It makes WavPackDecoder.cpp a lot simpler because we can get rid of the WavpackStreamReader64 implementation (read_bytes, write_bytes, etc.) We can also get rid of the data vector and dataPos variable. The only quirk is that WavpackGetNumSamples doesn't work with WavpackRawDecoder. This is explained in the docs. It seems like you just have to parse the first "block" and get its total_samples property. Once you have that, everything else seems to work more or less the same way as it does when reading from a file. I added a private method readNextHeader to get the contents of that header. I also factored out all the common code from the two WavPackInternal constructors into one private method decode(size_t totalSamples). (I'm not sure about the name for that private method, let me know if you have a better one.)

I tried using this implementation to read each of the .wv files (loaded into memory) in test_data/ad_hoc, and they all work. I also checked that reading them as files still works.

Let me know how this looks! I'm not an expert in C or C++ so please let me know if I did anything wacky. Thanks!

ddiakopoulos commented 3 years ago

Thanks for your PR @mikeperri ! I'll take a look at this over the weekend.