donbrae / blog-comments

Comments for https://www.jamieonkeys.dev
https://www.jamieonkeys.dev
0 stars 0 forks source link

posts/web-audio-api-output-latency/ #5

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

Keeping audio and visuals in sync with the Web Audio API - jamieonkeys

Audio and visuals out of sync when using the Web Audio API? There’s a property for that.

https://www.jamieonkeys.dev/posts/web-audio-api-output-latency/

ElijahPepe commented 2 years ago

If I then switch back to the built-in speaker it‘s 0.02485258333333333 seconds. I can’t explain that; feel free to leave a comment if you know the reason.

outputLatency is the seconds of latency between AudioDestinationNode and the audio playback device (it also happens to be a fixed number when fingerprinting is reduced in Firefox).

The code that's responsible for measuring the latency is as follows:

double MediaTrackGraphImpl::AudioOutputLatency() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mAudioOutputLatency != 0.0) {
    return mAudioOutputLatency;
  }
  MonitorAutoLock lock(mMonitor);
  if (CurrentDriver()->AsAudioCallbackDriver()) {
    mAudioOutputLatency = CurrentDriver()
                              ->AsAudioCallbackDriver()
                              ->AudioOutputLatency()
                              .ToSeconds();
  } else {
    // Failure mode: return 0.0 if running on a normal thread.
    mAudioOutputLatency = 0.0;
  }

  return mAudioOutputLatency;
}

In the backend, latency is measured using cubeb and the cubeb_stream_get_latency function. How cubeb_stream_get_latency is implemented differs depending on the host OS; on macOS, the audiounit_stream_get_latency function is used, which takes the sum of the timestamp difference, stream latency, and the hardware latency.

As for why the latency in your AirPods is so high in comparison to your MacBook speakers, the reason comes down to packet sizes. The streaming Bluetooth packet size is about 1 KB. If you're streaming music at 192 Kb/s, that's about 24 KB/s, resulting in a latency of 42 milliseconds (or 1/24 of a second of latency). Bluetooth has a packet size limit in order to ensure that minimal errors are caused when the packet is received.

Just for fun, on Windows, virtually all audio rendering uses wasapi_stream_get_latency. In most cases, the following equation is used to calculate the frames from the hns (in some circumstances, a different method is used when the hns is 0 due to a Windows 10 bug):

std::ceil((hns - 1) / 10000000.0 * rate)

hns in this equation refers to the REFERENCE_TIME variable latency_hns, which is written to using the GetStreamLatency method in Windows, which IIRC also uses hardware latency.

donbrae commented 2 years ago

@ElijahPepe Many thanks indeed for that informative comment!

reekystive commented 1 year ago

Don't execute this two lines together or you will get 0:

> var audioCtx = new AudioContext()
  audioCtx.outputLatency
< 0

Instead, execute separately:

> var audioCtx = new AudioContext()
< undefined

> audioCtx.outputLatency
< 0.064

I don't know why. I'm testing on Chrome 114.0.5735.133 (macOS).