Skip to content

Instantly share code, notes, and snippets.

@quimbs
Last active April 1, 2024 08:32
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save quimbs/9a7bf7ddd161dac727c6 to your computer and use it in GitHub Desktop.
Save quimbs/9a7bf7ddd161dac727c6 to your computer and use it in GitHub Desktop.
Sample implementation of Autocorrelation using Web Audio
var findFundamentalFreq = function(buffer, sampleRate) {
// We use Autocorrelation to find the fundamental frequency.
// In order to correlate the signal with itself (hence the name of the algorithm), we will check two points 'k' frames away.
// The autocorrelation index will be the average of these products. At the same time, we normalize the values.
// Source: http://www.phy.mty.edu/~suits/autocorrelation.html
// Assuming the sample rate is 48000Hz, a 'k' equal to 1000 would correspond to a 48Hz signal (48000/1000 = 48),
// while a 'k' equal to 8 would correspond to a 6000Hz one, which is enough to cover most (if not all)
// the notes we have in the notes.json file.
var n = 1024, bestR = 0, bestK = -1;
for(var k = 8; k <= 1000; k++){
var sum = 0;
for(var i = 0; i < n; i++){
sum += ((buffer[i] - 128) / 128) * ((buffer[i + k] - 128) / 128);
}
var r = sum / (n + k);
if(r > bestR){
bestR = r;
bestK = k;
}
if(r > 0.9) {
// Let's assume that this is good enough and stop right here
break;
}
}
if(bestR > 0.0025) {
// The period (in frames) of the fundamental frequency is 'bestK'. Getting the frequency from there is trivial.
var fundamentalFreq = sampleRate / bestK;
return fundamentalFreq;
}
else {
// We haven't found a good correlation
return -1;
}
};
var frameId;
var detectPitch = function () {
var buffer = new Uint8Array(analyserAudioNode.fftSize);
// See initializations in the AudioContent and AnalyserNode sections of the demo.
analyserAudioNode.getByteTimeDomainData(buffer);
var fundalmentalFreq = findFundamentalFreq(buffer, audioContext.sampleRate);
if (fundalmentalFreq !== -1) {
var note = findClosestNote(fundalmentalFreq, notesArray); // See the 'Finding the right note' section.
var cents = findCentsOffPitch(fundalmentalFreq, note.frequency); // See the 'Calculating the cents off pitch' section.
updateNote(note.note); // Function that updates the note on the page (see demo source code).
updateCents(cents); // Function that updates the cents on the page and the gauge control (see demo source code).
}
else {
updateNote('--');
updateCents(-50);
}
frameId = window.requestAnimationFrame(detectPitch);
};
Copy link

ghost commented Nov 24, 2015

You add the result n-times... Then shouldn't it be var r = sum / n; instead of var r = sum / (n + k);?
I want to understand it.

And I think I found a typo: fundalmental.

+P.S.: Exactly, the values have to be subtracted 127.5 and be divided by 127.5.

@agmitron
Copy link

agmitron commented Apr 24, 2022

Works incorrectly on low frequencies (shows 6000), from 140 Hz to 370 Hz. Where is the mistake?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment