Closed chris-rudmin closed 8 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);
Thanks for the snippet. I have started an example in a seperate repo, but it's not complete.
@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.
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
@brancusi: The decoder returns an array of buffers (one for each channel)
{ outputBufferSampleRate : audioContext.sampleRate }
null
payload when it is finished)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.
@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.
@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
@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.
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?
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.
Added a decoder example in the examples folder!
Add documentation on how to use oggopusDecoder