Search code examples
timersignal-processingfftweb-audio-api

Web audio analyser node - run at regular interval


I want to detect an audio signal (morse code) on specific frequencies using web audio. I retrieve the frequency data using an analyser node's getFloatFrequencyData function.

Now the problem: Using setInterval() to regularly sample the frequency data is not regular enough: the callback gets executed a few milliseconds earlier or later than expected.

How can I retrieve the analyser's frequency data regularly exactly every few miliseconds? I would prefer using the built-in analyser node's FFT functionality instead of resorting to manually processing the audio data via e.g. Goertzel algorithm.

Code sample with the problematic setInterval():

// Analyse microphone audio frequencies: 
function onStream(stream) {
  let audioCtx = new(window.AudioContext || window.webkitAudioContext)(),
    source = audioCtx.createMediaStreamSource(stream),
    analyser = audioCtx.createAnalyser(),
    fft = new Float32Array(analyser.frequencyBinCount);

  source.connect(analyser);

  // Doesn't execute exactly every 100ms as needed - what to do?
  setInterval(() => {
    analyser.getFloatFrequencyData(fft);
    console.log(performance.now(), fft[0]);
  }, 100);
}

navigator.mediaDevices.getUserMedia({audio: true}).then(onStream);


Solution

  • Yeah, you can't really use an analyzer. There's too much uncertainty in when it will get run, and you can't guarantee precisely when it will run. You're better off using a ScriptProcessor for now (AudioWorklet eventually), and doing the FFT (or other recognition code) yourself.