document.querySelector('#analyze-pitch button').addEventListener('click', startListening); function startListening() { navigator.mediaDevices.getUserMedia({ audio: true, video: false }) .then(stream => { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const analyser = audioContext.createAnalyser(); const microphone = audioContext.createMediaStreamSource(stream); const scriptProcessor = audioContext.createScriptProcessor(2048, 1, 1); analyser.smoothingTimeConstant = 0.3; analyser.fftSize = 2048; microphone.connect(analyser); analyser.connect(scriptProcessor); scriptProcessor.connect(audioContext.destination); scriptProcessor.onaudioprocess = () => { const buffer = new Float32Array(analyser.fftSize); analyser.getFloatTimeDomainData(buffer); const pitch = autoCorrelate(buffer, audioContext.sampleRate); document.querySelector('#analyze-pitch dd').textContent = pitch.toFixed(2); }; }) .catch(err => { console.error('Error accessing microphone: ' + err); }); } function autoCorrelate(buffer, sampleRate) { const SIZE = buffer.length; const MAX_SAMPLES = Math.floor(SIZE / 2); const MIN_SAMPLES = 0; const GOOD_ENOUGH_CORRELATION = 0.9; let bestOffset = -1; let bestCorrelation = 0; let rms = 0; let foundGoodCorrelation = false; let correlations = new Array(MAX_SAMPLES); for (let i = 0; i < SIZE; i++) { const val = buffer[i]; rms += val * val; } rms = Math.sqrt(rms / SIZE); if (rms < 0.01) // not enough signal return -1; let lastCorrelation = 1; for (let offset = MIN_SAMPLES; offset < MAX_SAMPLES; offset++) { let correlation = 0; for (let i = 0; i < MAX_SAMPLES; i++) { correlation += Math.abs((buffer[i]) - (buffer[i + offset])); } correlation = 1 - (correlation / MAX_SAMPLES); correlations[offset] = correlation; if ((correlation > GOOD_ENOUGH_CORRELATION) && (correlation > lastCorrelation)) { foundGoodCorrelation = true; if (correlation > bestCorrelation) { bestCorrelation = correlation; bestOffset = offset; } } else if (foundGoodCorrelation) { const shift = (correlations[bestOffset + 1] - correlations[bestOffset - 1]) / correlations[bestOffset]; return sampleRate / (bestOffset + (8 * shift)); } lastCorrelation = correlation; } if (bestCorrelation > 0.01) { return sampleRate / bestOffset; } return -1; }