chris-rudmin / opus-recorder

A library for encoding and decoding web audio as OggOpus.
Other
961 stars 173 forks source link

Document Decoder #26

Closed chris-rudmin closed 8 years ago

chris-rudmin commented 9 years ago

Add documentation on how to use oggopusDecoder

mudcube commented 9 years ago

This might not be very helpful. This takes a OGG blob and converts it into audio buffers:

var worker = new Worker('./oggopusDecoder.js')
worker.postMessage(recorder.config); // uses same config as for recording
worker.addEventListener("error", function(e) {
    console.log(e);
});
worker.addEventListener("message", function(e) {
    console.log(e.data);
});
///
var reader = new FileReader();
reader.onload = function(event) {
    worker.postMessage({
        command: "decode",
        pages: new Uint8Array(this.result)
    });
};
reader.readAsArrayBuffer(blob);
chris-rudmin commented 9 years ago

Thanks for the snippet. I have started an example in a seperate repo, but it's not complete.

chris-rudmin commented 9 years ago

@super-coder I've removed comments unrelated to this issue. Please open a new issue if you have an issue or questions about the lib.

brancusi commented 9 years ago

Hi,

Would it be possible to outline how to go from encoded blob to playing back via a AudioContext destination? I would like to connect to a gain node and playback through there.

I have tried getting this example working as well as the other repo. I see the output from the decoder is a Float32Array is returned, what is the next step to get this actually playing though?

Thank you

chris-rudmin commented 9 years ago

@brancusi: The decoder returns an array of buffers (one for each channel)

brancusi commented 9 years ago

Thanks @chris-rudmin, got it working!

This returns a promise and resolves with a source that can be started. A little lengthy but works for now. Does anything pop out at you as being a potential problem?

Thanks again for the great library!

/**
   * Create a buffer source from an ogg blob
   * @param {Blob} The ogg blob created by recorderjs
   * @param {AudioContext} An instance of an AudioContext to create the bufferSource with
   * @param {Integer} Numbers of channels, defaults to 2
   * @returns {Promise} A promise that will resolve with an AudioBufferSourceNode prefilled with the ogg blob data, ready for playback
   */
  createBufferSource ( blob, audioContext, channels = 2 ) {
    return new Promise((res, rej) => {
      const decoder = new OpusDecoderWorker();
      const buffers = [];
      const config = {
        outputBufferSampleRate : audioContext.sampleRate,
        bufferLength : 4096
      }

      decoder.onmessage = e => {
        if (e.data === null) {

          var frameCount = buffers.length * config.bufferLength;
          var audioBuffer = audioContext.createBuffer(channels, frameCount, audioContext.sampleRate);

          // Fill the channel data
          for (let channel = 0; channel < channels; channel++) {
            var channgelBuffer = audioBuffer.getChannelData(channel);
            buffers
              .map((buffer, i) => {
                (buffer[channel] || buffer[0])
                  .forEach((frame, j) => {
                    channgelBuffer[(i*config.bufferLength)+j] = frame;
                  });
              });
          }

          const source = audioContext.createBufferSource();
          source.buffer = audioBuffer;
          source.connect(audioContext.destination);

          // Resolve the promise
          res(source);
        } else {
          buffers.push(e.data);
        }
      };

      // Initialize the decoder
      decoder.postMessage({ command: "init", config: config });

      // Load blob
      const reader = new FileReader();
      reader.onload = e => {
        decoder.postMessage({ command: "decode", pages: new Uint8Array(reader.result)});
        decoder.postMessage({ command: 'done' });
      };
      reader.readAsArrayBuffer(blob);

    });
  }

P.S. This is working in a react app using the worker-loader to wrap the worker scripts in case anyone wonders about that new worker line.

chris-rudmin commented 9 years ago

@brancusi Looks good. Is this pure ECMASCript 6 code? I'm unfamiliar with => syntax. Does defining a value as const make it immutable? Seems strange to make an array which you push to a constant.

brancusi commented 9 years ago

@chris-rudmin ok cool. Yes this is es6 syntax. The => is a new way to declare an anonymous function that will bind any this references to current outer scope. Gets us away from having to do, var self = this;

As for Const, it just makes the reference read-only but you can still mutate the underlying array in our case. I think it adds some clarity of intend mostly, none of the refs will be redeclared inside that promise.

Under the hood right now this all gets compiled to es5 any way, so const become var. Babel

chris-rudmin commented 9 years ago

@brancusi One thing I might add, you could get better performance and higher fidelity playback (at the expense of more memory) if you can create the buffer source at with a sample rate of 48000. This is the native decoder rate of opus and it will avoid you using my lo-fi resampler.

brancusi commented 9 years ago

Thanks @chris-rudmin, I will definitely swap that out. Will it matter if the audioContext's rate is different? Or should that be set (can it be set?) before hand to 48000 as well?

chris-rudmin commented 9 years ago

My understanding is that it will use the browsers internal resampling algorithm which should be faster and of higher quality than a resampler implemented in js. However I have not tested this.

chris-rudmin commented 8 years ago

Added a decoder example in the examples folder!