eshaz / wasm-audio-decoders

Browser and NodeJS Web Assembly audio decoder libraries that are highly optimized for size and performance.
399 stars 23 forks source link

Ogg/vorbis decoder #68

Closed dy closed 1 year ago

dy commented 1 year ago

Hi @eshaz! Do you have a consideration of adding ogg/vorbis decoder? Wikipedia seems to store files in ogg format, like https://upload.wikimedia.org/wikipedia/commons/c/cf/Caja_de_m%C3%BAsica_%28PianoConcerto5_Beethoven%29.ogg. ogg-opus-decoder doesn't decode it neither it does throw errors.

There's an example of quite compact decoder in oggmented: https://github.com/jfrancos/oggmented.

dy commented 1 year ago

@eshaz in case you may want to consider, there's direct ogg-vorbis decoder, adoptable by your style: https://github.com/dy/ogg-decoder

eshaz commented 1 year ago

@dy Thanks for the suggestion. I'll get started on this. It should be pretty simple to use the same pattern for Vorbis as the other decoders in this library.

eshaz commented 1 year ago

@dy, I just published @wasm-audio-decoders/ogg-vorbis. Let me know if this works!

dy commented 1 year ago

@eshaz somehow it is failing to decode this file: https://upload.wikimedia.org/wikipedia/commons/c/cf/Caja_de_m%C3%BAsica_%28PianoConcerto5_Beethoven%29.ogg

const run = async () => {
    let {OggVorbisDecoder} = await import('https://cdn.skypack.dev/@wasm-audio-decoders/ogg-vorbis');
    let decoder = new OggVorbisDecoder();
    await decoder.ready;
    let src = 'https://upload.wikimedia.org/wikipedia/commons/c/cf/Caja_de_m%C3%BAsica_%28PianoConcerto5_Beethoven%29.ogg';
    let resp = await fetch(src);
    let arrayBuffer = await resp.arrayBuffer();
    let result = await decoder.decode(arrayBuffer)
    console.log(result)
}

run()

It returns channel data as bunch of Float32Arrays:

channelData: [Float32Array(0), Float32Array(0), Float32Array(0), Float32Array(0)...
eshaz commented 1 year ago

There's a couple things here you will need to change in your example. Let me know if these changes help, if not I'll test this out on my side later tonight.

  1. You should call decodeFile rather than decode since you're decoding one entire file in one method call. decode is meant to be used when streaming in chunks of a file, i.e. streaming radio or reading a file from disk in chunks.
  2. You should create a new Uint8Array from the ArrayBuffer returned from the response and pass that into decodeFile.
const result = await decoder.decodeFile(new Uint8Array(arrayBuffer));
dy commented 1 year ago

Yes, it worked, thank you. There seems to be a bit of difference in API with opus-decoder / mpg123-decoder - they don't have decodeFile method, do they?

eshaz commented 1 year ago

That's right. The decoders without decodeFile don't need that method since they don't have any buffering internally and don't need to be flushed when reaching the end of a file.

decodeFile is really just a convenience method that functionally acts like a call to decode and a call to flush. The codec-parser library I'm using to demux the codec data from Ogg keeps an internal buffer / state that needs to be flushed when there is no more data to read.