Last active
April 1, 2024 08:32
-
-
Save quimbs/9a7bf7ddd161dac727c6 to your computer and use it in GitHub Desktop.
Sample implementation of Autocorrelation using Web Audio
This file contains hidden or 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
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); | |
}; |
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
You add the result n-times... Then shouldn't it be
var r = sum / n;
instead ofvar 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.