Last active
March 23, 2021 09:19
-
-
Save mhseiden/8642472 to your computer and use it in GitHub Desktop.
Downloading, Decoding, and Downsampling an Audio File with the WebAudio API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* In this gist, assume that we're using Chrome, and the following variables are in scope and have already been assigned to the following: | |
* | |
* var url = "http://upload.wikimedia.org/wikipedia/commons/b/be/Toccata_et_Fugue_BWV565.ogg"; | |
* var webaudio = new webkitAudioContext(); | |
* var renderCallback = function renderCallback(data) { return "render!"; } // a function which will render the given audio data on a canvas | |
*/ | |
// Fetch the data with an AJAX request | |
var xhr = new XMLHttpRequest(); | |
// Force the browser to return the result as a byte-array | |
xhr.responseType = "arraybuffer"; | |
// Initialize the AJAX request as a "GET" for the given url | |
xhr.open("GET", url); | |
// Attach a callback to process the OGG data once the AJAX request has finished | |
xhr.addEventListener("readystatechange", function() { | |
// Only try to process the data if the AJAX request is in a finished state | |
if (4 === xhr.readyState) { | |
// Decode the AJAX response (which is an arraybuffer) into its uncompessed LPCM form | |
webaudio.decodeAudioData(xhr.response, function(aud) { | |
// Programmatically determine the number of samples-per-bin used for downsampling | |
var downsample = Math.ceil(aud.getChannelData(0).length / 100000 /* we only want 100k bins, to keep things fast */); | |
// Track the maximum value we've encountered, so that we can normalize the downsampled data | |
var max = Number.MIN_VALUE; | |
// Create an output buffer to hold the downsampled audio data | |
var buf = new Float32Array(aud.length / downsample); | |
// Iterate through the bins in the output buffer to create the downsampled result | |
for(var bin = 0, len1 = buf.length; bin < len1; ++bin) { | |
// Aggregate all of the samples in all of the channels, which belong in this bucket | |
var avg = 0.0; | |
// Iterate through all of the channels in the audio file (making this a mono-downsample) | |
for(var chan = 0, len3 = aud.numberOfChannels; chan < len3; ++chan) { | |
// Use a local variable to make the code a bit more readable :-D | |
var chanData = aud.getChannelData(chan); | |
// Aggregate all of the samples that belongs in this bin | |
for(var idx = bin * downsample, len2 = idx + downsample; idx < len2; ++idx) { | |
avg += chanData[idx]; | |
} | |
} | |
// Find the overall average amplitude using the aggregated sum of amplitudes in the bin | |
buf[bin] = (avg / (aud.numberOfChannels * downsample)); | |
// Update the maximum value seen in the downsampled-output buffer | |
max = Math.max(max, Math.abs(buf[bin])); | |
} | |
// Normalize the output buffer using the maximum value that we saw | |
for(bin = 0; bin < len1; ++bin) { | |
buf[bin] = buf[bin] / max; | |
} | |
// Pass the ouput buffer along to the function which will do the actual rendering | |
renderCallback(buf); | |
}); | |
} | |
}); | |
xhr.send(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment