Reputation: 12027
I just started looking into the Web Audio API and I'm trying to make a visual sync with the audio. When there is an increase in volume (beat?) in the audio, I want to flash a white color on the screen. What I've done so far:
var mp3 = "08 - No More Sorrow.mp3";
var context = new AudioContext();
var request = new XMLHttpRequest();
request.open('GET', mp3, true);
request.responseType = "arraybuffer";
request.onload = function () {
context.decodeAudioData(request.response, function (buffer) {
var sourceBuffer = context.createBufferSource();
sourceBuffer.buffer = buffer;
sourceBuffer.connect(context.destination);
sourceBuffer.start(context.currentTime);
});
};
request.send();
...which is just playing the audio using the Web Audio API. No idea what to do next. I've checked Beat Detection Using JavaScript and the Web Audio API and Making Audio Reactive Visuals pages, but couldn't really understand anything.
If I were to show you what I want to do without using the Web Audio API, it'd be something like this:
Array.prototype.pushIfExists = function(item) {
if (item) {
this.push(item);
}
}
function random(min, max) {
var min = min || 0;
var max = max || 100;
var num = Math.floor(Math.random() * max);
while (num < min) {
num = Math.floor(Math.random() * max);
}
return num;
}
function avarage(array) {
var sum = 0;
var avarage = 0;
for (var i = 0; i < array.length; i++) {
sum += array[i];
}
avarage = sum / array.length;
return avarage;
}
var beats = [];
var delay = 500;
var delayIncrement = 200;
var threshold = 50;
var thresholdLimit = 100;
var beatAvarageRange = 5;
var flashDuration = 100;
for (var i = 0; i < 100; i++) {
beats.push(random(0, thresholdLimit));
}
for (var i = 0; i < beats.length; i++) {
(function(i) {
setTimeout(function() {
var recentBeats = [];
for (var j = 1; j < beatAvarageRange + 1; j++) {
recentBeats.pushIfExists(beats[i - j]);
}
threshold = avarage(recentBeats);
if (beats[i] > threshold) {
document.body.style.backgroundColor = "white";
setTimeout(function() {
document.body.style.backgroundColor = "black";
}, flashDuration);
}
}, delay);
delay += delayIncrement;
})(i);
}
body {
background-color: black;
}
Upvotes: 2
Views: 1700
Reputation: 12027
I've done some more digging and found a solution. Using the explanation in Exploring the HTML5 Web Audio: visualizing sound | Smartjava.org page, I've come up with the following:
var volumeBars = {
mono : document.getElementById("monoFill")
};
document.getElementById("open-file").onchange = function (evt) {
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
playSound(e.target.result);
}
reader.readAsArrayBuffer(file);
}
var context = new AudioContext();
function playSound(arraybuffer) {
context.close();
context = new AudioContext();
var source = context.createBufferSource();
context.decodeAudioData(arraybuffer, function (buffer) {
source.buffer = buffer;
});
var analyser = context.createAnalyser();
analyser.smoothingTimeConstant = 0.3;
analyser.fftSize = 1024;
jsNode = context.createScriptProcessor(2048, 1, 1);
jsNode.onaudioprocess = function() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
volumeBars.mono.style.height = Math.average(array) * 2 + "px";
volumeBars.mono.innerHTML = Math.floor(Math.average(array));
}
source.connect(analyser);
source.connect(context.destination);
jsNode.connect(context.destination);
analyser.connect(jsNode);
source.start();
}
Math.average = function(arguments) {
var numbers;
if (arguments[0] instanceof Array) {
numbers = arguments[0];
}
else if (typeof arguments[0] == "number") {
numbers = arguments;
}
var sum = 0;
var average = 0;
for (var i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
average = sum / numbers.length;
return average;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: gainsboro;
}
#container {
height: 340px;
}
.bar {
width: 50px;
height: 100%;
position: relative;
float: left;
margin: 20px;
height: calc(100% - 40px);
}
.fill {
background: LawnGreen;
height: 20px;
width: 100%;
box-shadow: 0 0 3px rgba(0,0,0,.25),
inset 1px 1px 1px rgba(255,255,255,.75),
inset -1px -1px 1px rgba(0,0,0,.4);
position: absolute;
bottom: 0;
padding: 5px;
color: rgba(0,0,0,.75);
}
input {
margin: 20px;
}
<div id="container">
<div class="bar" id="mono">
<div class="fill" id="monoFill"></div>
</div>
</div>
<input type="file" id="open-file" accept="audio/*" />
Web Audio API - Volume Meter - JSFiddle
This is not the final visualization I'm trying to do, but creating a volume-meter first seemed as a better idea for understanding how the Web Audio API works.
Upvotes: 2