I'm playing around with the Web Audio API & trying to find a way to import an mp3 (so therefore this is only in Chrome), and generate a waveform of it on a canvas. I can do this in real-time, but my goal is to do this faster than real-time.
All the examples I've been able to find involve reading the frequency data from an analyser object, in a function attached to the onaudioprocess event:
processor = context.createJavascriptNode(2048,1,1);
processor.onaudioprocess = processAudio;
...
function processAudio{
var freqByteData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqByteData);
//calculate magnitude & render to canvas
}
It appears though, that analyser.frequencyBinCount
is only populated when the sound is playing (something about the buffer being filled).
What I want is to be able to manually/programmatically step through the file as fast as possible, to generate the canvas image.
What I've got so far is this:
$("#files").on('change',function(e){
var FileList = e.target.files,
Reader = new FileReader();
var File = FileList[0];
Reader.onload = (function(theFile){
return function(e){
context.decodeAudioData(e.target.result,function(buffer){
source.buffer = buffer;
source.connect(analyser);
analyser.connect(jsNode);
var freqData = new Uint8Array(buffer.getChannelData(0));
console.dir(analyser);
console.dir(jsNode);
jsNode.connect(context.destination);
//source.noteOn(0);
});
};
})(File);
Reader.readAsArrayBuffer(File);
});
But getChannelData() always returns an empty typed array.
Any insight is appreciated - even if it turns out it can't be done. I think I'm the only one the Internet not wanting to do stuff in real-time.
Thanks.
The AudioNode interface is a generic interface for representing an audio processing module.
There is a really amazing 'offline' mode of the Web Audio API that allows you to pre-process an entire file through an audio context and then do something with the result:
var context = new webkitOfflineAudioContext();
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(0);
context.oncomplete = function(e) {
var audioBuffer = e.renderedBuffer;
};
context.startRendering();
So the setup looks exactly the same as the real-time processing mode, except you set up the oncomplete
callback and the call to startRendering()
. What you get back in e.redneredBuffer
is an AudioBuffer
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With