pdeljanov / Symphonia

Pure Rust multimedia format demuxing, tag reading, and audio decoding library
Mozilla Public License 2.0
2.2k stars 127 forks source link

FL Studio .wav's cannot be read #170

Open kangalio opened 1 year ago

kangalio commented 1 year ago

Trying to read music production software FL Studio's built-in sample file "FPC 1 Kick.wav" throws an Unsupported("wav: unsupported wave format") error

Sample file (rename .png to .wav): FPC 1 Kick

Audacity can read this file already

kangalio commented 1 year ago

Would a PR be accepted which fixes this?

pdeljanov commented 1 year ago

Was a bit hard finding information about this, but it looks like it's a WAV file with Vorbis encoded audio.

ffmpeg has a similar issue here that's kinda useful.

No reason why it couldn't be supported, but WavReader may need some rework to support reading encoded data. The changes made to support ADPCM may be enough, but hard to say until it's actually implemented.

If this is something you want to work on, I'll accept a PR for adding this support.

Thanks!

kangalio commented 1 year ago

Interesting. Symphonia currently supports 7 types of .wav formats

https://github.com/pdeljanov/Symphonia/blob/fcca3257ae2be13b9ba38771bc165cc842e8b596/symphonia-format-wav/src/chunks.rs#L581-L589

But the mentioned mmreg.h file has 265 formats. Would Symphonia ideally eventually support them all?

kangalio commented 1 year ago

I'm currently trying to work on this. Question: how to get a MediaSourceStream from a ReadBytes in order to plug it in to OggReader::try_new? (Is there a better place for questions like this, like a support channel?)

pdeljanov commented 1 year ago

But the mentioned mmreg.h file has 265 formats. Would Symphonia ideally eventually support them all?

Probably not, most are not used anymore or are proprietary codecs with no available specifications. The most likely contenders for inclusion would be the ADPCM codecs since they tend be used in games. I'll likely be leaving these up to contributors though since I don't have any of these files.

I'm currently trying to work on this. Question: how to get a MediaSourceStream from a ReadBytes in order to plug it in to OggReader::try_new?

This wouldn't be correct way to go about this. OGG and WAVE are both distinct container formats so it is not possible to parse a WAVE file as an OGG file.

If you look at the Vorbis specification, it specifies how Vorbis encoded audio data should be packaged in an OGG stream, but there's no specification for how it's packaged in a WAVE file. This Vorbis-in-WAVE format is non-standard. Therefore, some reverse engineering or educated guesses would need to be made on how it's packaged.

I looked at the hex dump of the file and to me it looks like the WAVE DATA chunk contains a single logical OGG stream packaged as per the specification linked above. I imagine there are some limitations placed on the OGG pages and packets to make decoding easier, but hard to know without a large corpus of files or a specification.

You can lift some code from symphonia-format-ogg for experimentation, but it's not possible to use OggReader directly.

(Is there a better place for questions like this, like a support channel?)

Discussions are enabled on this repo., but an issue seems like the right place for this one.

kangalio commented 1 year ago

When I use a hex editor to remove the bytes up to OggS it seems to become a perfectly OGG Vorbis file and plays in anything I've tried. Suggesting that we can indeed parse a WAVE file as an OGG file. This also matches the FL Studio forum thread I found

Should I still just copy paste all the OGG container code? That seems suboptimal at best

One thing I'm still confused about is the bytes inbetween the WAV signature and the OGG file. image As mentioned above, I can strip it straight off, and the remaining cyan part is a perfectly valid .ogg file. But there must be some purpose to the green in-between bytes. Should I just experiment, i.e. change the bytes and re-open the file in FL Studio? Or compare the green bytes of multiple of these ogg-in-wav files against each other?

pdeljanov commented 1 year ago

Those bytes are part of the WAVE file format. A WAVE is split up into a tree of chunks. There are many different chunks, some of which are ignored entirely for our use-case, but others contain information about the codec, duration, metadata, and audio data.

The DATA chunk contains the audio data. In this case, it contains the Vorbis audio data, packetized in OGG packets. Tricky thing is, there can be a chunk after the audio data chunk which means it's not possible to consider the remainder of the file as an OGG file.

This also means that simply calling into OggReader is not a workable solution. I am also hesitant to add symphonia-format-ogg as a dependency of symphonia-format-wav. If we want to share some code, then the correct crate to put that shared code in would be symphonia-xiph-utils. However, I also have a similar file that contains MP3 encoded data so that would create a related problem.

The OGG bitstream format is actually pretty simple which is why I suggested some code duplication. It only gets bad if you can stuff complex OGG streams into a WAVE file. It'd be a bit surprising if you could considering WAVE doesn't have the concept of tracks, but without a spec. it's hard to know for sure.

Unfortunately, this is rapidly becoming a very complex and convoluted problem. Given the niche use-case, I'm feeling that this moment may not be the best time for the project to implement this. I'm too short on bandwidth to give this issue the attention and support it needs. :(

The best way forward would likely be for you to implement your own FormatReader that supports your immediate needs. You can register this with the Symphonia format registry and it should work as usual from there.