Skip to content

Instantly share code, notes, and snippets.

@kevinzhang96
Created November 8, 2015 11:59
Show Gist options
  • Save kevinzhang96/e3ca6ae693c4870a6dd4 to your computer and use it in GitHub Desktop.
Save kevinzhang96/e3ca6ae693c4870a6dd4 to your computer and use it in GitHub Desktop.
Script for parsing BPM
var audioobj = document.getElementsByTagName("audio")[0];
// var text = document.getElementById("text");
var actualBPM;
var request = new XMLHttpRequest();
console.log("Audio source: " + audioobj.src);
request.open('GET', audioobj.src, true);
request.responseType = 'arraybuffer';
var targetspd = 1;
request.onload = function() {
console.log("Handling request now");
//Create offline context
var OfflineContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
var offlineContext = new OfflineContext(1, 2, 44100);
offlineContext.decodeAudioData(request.response, function(buffer) {
console.log("1");
// Create buffer source
var source = offlineContext.createBufferSource();
source.buffer = buffer;
// Create filter
var filter = offlineContext.createBiquadFilter();
filter.type = "lowpass";
console.log("2");
// Pipe the song into the filter, and the filter into the offline context
source.connect(filter);
filter.connect(offlineContext.destination);
// Schedule the song to start playing at time:0
source.start(0);
var peaks,
initialThresold = 0.9,
thresold = initialThresold,
minThresold = 0.3,
minPeaks = 30;
console.log("3");
do {
peaks = getPeaksAtThreshold(buffer.getChannelData(0), thresold);
thresold -= 0.05;
} while (peaks.length < minPeaks && thresold >= minThresold);
var intervals = countIntervalsBetweenNearbyPeaks(peaks);
var groups = groupNeighborsByTempo(intervals, buffer.sampleRate);
var top = groups.sort(function(intA, intB) {
return intB.count - intA.count;
}).splice(0, 5);
// text.innerText=Math.round(top[0].tempo);
actualBPM = Math.round(top[0].tempo);
console.log("Actual BPM: " + actualBPM);
audioobj.play();
var c = function() {
audioobj.playbackRate += (targetspd - audio.playbackRate) * 0.1;
//console.log("playbackrate " + audio.playbackRate);
var x = setTimeout(requestAnimationFrame(c), 1000 / 16);
}
c();
});
};
request.send();
function countIntervalsBetweenNearbyPeaks(peaks) {
var intervalCounts = [];
peaks.forEach(function(peak, index) {
for (var i = 0; i < 10; i++) {
var interval = peaks[index + i] - peak;
var foundInterval = intervalCounts.some(function(intervalCount) {
if (intervalCount.interval === interval)
return intervalCount.count++;
});
if (!foundInterval) {
intervalCounts.push({
interval: interval,
count: 1
});
}
}
});
return intervalCounts;
}
function groupNeighborsByTempo(intervalCounts, sampleRate) {
var tempoCounts = [];
intervalCounts.forEach(function(intervalCount, i) {
if (intervalCount.interval !== 0) {
// Convert an interval to tempo
var theoreticalTempo = 60 / (intervalCount.interval / sampleRate);
// Adjust the tempo to fit within the 90-180 BPM range
while (theoreticalTempo < 90) theoreticalTempo *= 2;
while (theoreticalTempo > 180) theoreticalTempo /= 2;
theoreticalTempo = Math.round(theoreticalTempo);
var foundTempo = tempoCounts.some(function(tempoCount) {
if (tempoCount.tempo === theoreticalTempo)
return tempoCount.count += intervalCount.count;
});
if (!foundTempo) {
tempoCounts.push({
tempo: theoreticalTempo,
count: intervalCount.count
});
}
}
});
return tempoCounts;
}
function getPeaksAtThreshold(data, threshold) {
var peaksArray = [];
var length = data.length;
for (var i = 0; i < length;) {
if (data[i] > threshold) {
peaksArray.push(i);
// Skip forward ~ 1/4s to get past this peak.
i += 10000;
}
i++;
}
return peaksArray;
}
var socket = io('http://localhost:3001');
socket.on('kinect', function(msg) {
var bpm = Number(msg);
// $("#disp").text(bpm);
console.log("New bpm: " + bpm);
targetspd = bpm / actualBPM; //Number($("#text").text());
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment