Timing accuracy of audio callbacks?

(in a Plugin or Audio App)

QUESTION #1:

Using MacOS 10.14.6, MacPro 12 core (Intel).
Audio Interface: MOTU 896mk Hybrid

I am measuring the time between each audio callback and averaging over time.

If you choose 48k sample rate and a buffer size of 480 samples, it should equal 10 ms between each getNextAudioBlock() or ProcessBlock(), right?

What I don’t understand is that when using the MOTU as an Audio Input or Output, and its Clock Source is set to “Internal”, it runs faster than it is supposed to. Each callback averages to 9.992 ms. Over time it adds up.

This is timing each callback, along with an internal ms counter (first column). So the internal ms counter advances by 10 ms for each callback, while CLBK time shows the ms each callback took, and the average of all accumulated callbacks.

MOTU 896mk as Audio Input, with its clock source set to “internal”:

   internal ms        actual time    callback count  elapsed time  accumulated time   average
----------------|-------------------|---------------------------------------------------------
  00:00:15.590  |  00:00:15.588 695 | CLBK num 1550, time 10.0063, acc 15498.6848, avg 09.9992
  00:00:15.600  |  00:00:15.598 709 | CLBK num 1551, time 10.0138, acc 15508.6986, avg 09.9992
  00:00:15.610  |  00:00:15.608 697 | CLBK num 1552, time 09.9881, acc 15518.6866, avg 09.9992
  00:00:15.620  |  00:00:15.618 667 | CLBK num 1553, time 09.9715, acc 15528.6581, avg 09.9991
  00:00:15.630  |  00:00:15.628 703 | CLBK num 1554, time 10.0352, acc 15538.6933, avg 09.9992
  00:00:15.640  |  00:00:15.638 698 | CLBK num 1555, time 09.9944, acc 15548.6877, avg 09.9992
  00:00:15.650  |  00:00:15.648 690 | CLBK num 1556, time 09.9926, acc 15558.6804, avg 09.9992
  00:00:15.660  |  00:00:15.658 707 | CLBK num 1557, time 10.0162, acc 15568.6965, avg 09.9992
  00:00:15.670  |  00:00:15.668 712 | CLBK num 1558, time 10.0053, acc 15578.7019, avg 09.9992
  00:00:15.680  |  00:00:15.678 715 | CLBK num 1559, time 10.0034, acc 15588.7052, avg 09.9992
  00:00:15.690  |  00:00:15.688 700 | CLBK num 1560, time 09.9839, acc 15598.6891, avg 09.9992

Scroll to the right - average is 9.992 ms.

This is the same thing, with the Audio Input set to the Mac’s “Built-in Input”:

   internal ms        actual time    callback count  elapsed time  accumulated time   average
----------------|-------------------|---------------------------------------------------------
  00:00:11.390  |  00:00:11.390 016 | CLBK num 1130, time 10.0182, acc 11300.0327, avg 10.0000
  00:00:11.400  |  00:00:11.399 991 | CLBK num 1131, time 09.9724, acc 11310.0051, avg 10.0000
  00:00:11.410  |  00:00:11.409 980 | CLBK num 1132, time 09.9924, acc 11319.9975, avg 10.0000
  00:00:11.420  |  00:00:11.419 961 | CLBK num 1133, time 09.9816, acc 11329.9791, avg 10.0000
  00:00:11.430  |  00:00:11.429 956 | CLBK num 1134, time 09.9959, acc 11339.9749, avg 10.0000
  00:00:11.440  |  00:00:11.439 987 | CLBK num 1135, time 10.0290, acc 11350.0039, avg 10.0000
  00:00:11.450  |  00:00:11.449 992 | CLBK num 1136, time 10.0061, acc 11360.0101, avg 10.0000
  00:00:11.460  |  00:00:11.459 977 | CLBK num 1137, time 09.9846, acc 11369.9946, avg 10.0000
  00:00:11.470  |  00:00:11.469 979 | CLBK num 1138, time 10.0010, acc 11379.9956, avg 10.0000
  00:00:11.480  |  00:00:11.479 985 | CLBK num 1139, time 10.0065, acc 11390.0022, avg 10.0000
  00:00:11.490  |  00:00:11.490 003 | CLBK num 1140, time 10.0181, acc 11400.0202, avg 10.0000

So here we have the expected 10.000 ms.

Average callback frequency, 48000 sample rate 480 audio buffer size:

Built-in Line Input:						    10.000 ms
MOTU 896 (clock source: internal):			     9.992 ms
MOTU 896 (clock source: Built-in Line Input):	10.000 ms

Any ideas why that would be?

QUESTION #2:

Also, in general, is it normal to have a large jitter between each callback? Even if they average to 10ms, they look something like this:

callback count  elapsed time
----------------------------
CLBK num 1130, time 10.0182	ms/micros.  (Ideally should be 10.0000)
CLBK num 1131, time 09.9724
CLBK num 1132, time 09.9924
CLBK num 1133, time 09.9816
CLBK num 1134, time 09.9959
CLBK num 1135, time 10.0290
CLBK num 1136, time 10.0061
CLBK num 1137, time 09.9846
CLBK num 1138, time 10.0010
CLBK num 1139, time 10.0065
CLBK num 1140, time 10.0181

Is this sort of fluctuation normal?

How does audio play artifact free with this kind of variation going on?

My guess is that there’s some additional buffering happening at the hardware/driver/OS level, that have you no control over in the client audio software, which allows for slight fluctuations in the callback time intervals.

Is this some actual problem for you? Are you relying on the audio callbacks to work for some other purpose than processing the buffer contents?

My MIDI-generating GUI App relies on an internal 1 ms clock. This clock needs to be advanced somehow, in 1 ms increments. I have been relying on a HighResolutionTimer (1ms) which works well enough. But I was investigating making the app an AudioApp and using getNextCallback() as a sort of timer. I was wondering if it would be any more reliable or stable than a HighResolutionTimer. It seems not.

If you have a callback at a known samplerate and known buffersize, you can calculate the number of ms to advance the clock. But if a buffersize that is supposed to result in 10.000 ms (in a perfect world) is an average of 9.992 ms, and you advance the internal clock 10 ms, eventually you will be ahead of real-time.

For example, at 48k, 1 ms is 48 samples. With a buffer size of 512, that would be 10.6667 ms, so for every 48 samples processed you advance by 1 ms (and carryover the remainder to the next callback). But if it’s NOT actually 10.6667 ms, then it drifts away.

That doesn’t seem to happen with the HighResolutionTimer (when it is actually working correctly):

There’s also the thing that the crystals/clocks used in audio equipment are not necessarily completely precisely at the intended frequency. 44100 Hz as the sample rate might not in actuality be precisely that, for example. So there’s often going to be drift between different devices.

2 Likes

You may find that the clock used for measuring the time is the same as the one used for the builtin audio! I’ve never thought about it before but it makes sense why have two high resolution timers on your Mac? The MOTU on the other hand will definitely have its own clock (some devices allow syncing to the host clock but, to the best of my knowledge, not when they support ADAT or other external clock sources, so I doubt your MOTU will support it).

Ultimately I think you’re measuring how much two clocks disagree. It’s not that one is right and the other is wrong (they will both be wrong to some degree). Who is to say which is more accurate? I’m also not sure at what point I would say it’s too off either :thinking:.

Ultimately I’m not surprised they don’t agree, I’m more surprised that the timer and and builtin audio do agree, but like I say if they are the same clock then that’s expected.

In regards to the irregularity of the callbacks (jitter), this is often down to the hardware/driver or anything else that does buffering in-between and is what I would expect, even if it’s not immediately intuitive (I was also surprised when I first discovered this). I wouldn’t suggest relying on the regularity of callbacks.

1 Like