0

I'm using the WebAudio API and doing a simple delay on the input using DelayNode. The code is as follows (might not work in the snippet because it requests microphone permissions):

const player = document.querySelector("#player")
const handleSuccess = stream => {
    const context = new AudioContext()
    const source = context.createMediaStreamSource(stream)
    const delay = context.createDelay(179) //seconds
    //source.connect(context.destination) connect directly
    source.connect(delay)
    delay.connect(context.destination)
    delay.delayTime.value = 1
}
navigator.mediaDevices.getUserMedia({audio: true, video: false}).then(handleSuccess)

However, when I run a metronome at 60 beats per minute (one click every second), the audio coming from the browser is not delayed by exactly one second, and multiple clicks are heard (the delay is slightly longer than expected). Is there any way to make an exact delay?

vityavv
  • 1,482
  • 12
  • 23

2 Answers2

1

I guess the problem is not the DelayNode itself but rather that there are multiple other hidden delays in the audio pipeline. If you want to hear the previous click from the metronome at the very same time as the current click you would need to reduce the time of your delay to account for those other delays (also known as latency).

The signal takes a bit of time to travel from your microphone through your A/D converter into the browser. This time is usually available as part of the settings of the MediaStream

stream.getAudioTracks()[0].getSettings().latency

Piping the MediaStream into the Web Audio API will probably add some more latency. And the AudioContext itself will add some latency due to its internal buffer.

context.baseLatency

It will also take some time to get the signal out of the computer again. It will travel from the browser to the OS which passes it on to the hardware. This value is also exposed on the AudioContext.

context.outputLatency

In theory you would only need to subtract all those values from the delay time and it would just work. However in reality every hardware/OS/browser combination is a bit different and you will probably need to make some adjustments in order to successfully overlay the previous with the current click depending on your personal setup.

chrisguttandin
  • 7,025
  • 15
  • 21
-4

You can use the async/await syntax (documentation here), here are two examples (taken from here)

const foo = async () => {
  await sleep(1000);
  // do something
}

const foo = async evt => {
  await sleep(1000);
  // do something with evt
}
rzuberi
  • 11
  • 1