Continuous latency problems

Hello;
I have a simple example (although under the hood the number flow is complex). It is a SynthDef with a FSinOsc wave generator and a percussive envelope. As you can see in the attachment the Pbind generates patterns for what is intended to be a simple granular synthesis. The problem, and it is a very uncomfortable issue, is that it produces latencies constantly. I don’t know if it’s my equipment (Mac M1 with Focusrite Scarlett 2i2), the server, or a combination of everything.
I have consulted this problem before on here but could not resolve the issue.
I would appreciate experience from someone who has had these same problems and solved them.
Regards

SynthDef(\sineGrainWPercEnv, { 
	arg freq = 800, amp = 0.1, envdur = 0.1, pan=0;
    var signal;
    signal = FSinOsc.ar(freq, 0, EnvGen.kr(Env.perc(0.001, envdur), doneAction: 2)*amp);
    OffsetOut.ar(0, Pan2.ar(signal, pan)); 
}).add;


Pbind(
    \instrument, \sineGrainWPercEnv,
    \freq, Pseq([1000, 2000, 4000], inf), // try to add 3000 in here
    \amp, Pfunc({rrand(0.1, 0.2)}),
    \envdur, Pseq([0.01, 0.02, 0.04], inf),
    \dur, Pseq([0.01, 0.02, 0.04], inf), // density
    \pan, Pseq([0.9, -0.9],inf)
).play;

Returns:

-> an EventStreamPlayer
late 0.264374563
late 0.224374563
late 0.214374563
late 0.194374563
late 0.154374563
late 0.144374563
late 0.124374563
late 0.084374563
late 0.074374563
late 0.054374563
late 0.014374563
late 0.004374563
late 0.045447271
late 0.035447271
late 0.015447271
late 0.018561771
late 0.018221563
late 0.093701438
late 0.076921354
late 0.066921354
late 0.046921354
late 0.006921354
late 0.066240938
late 0.046240938
late 0.006240938
late 0.032929813

What is you s.latency setting? For this isolated example I start getting late messages when s.latency is below 0.011, 0.011 is fine, but 0.01 produces late messages. For other more complex code with lots of concurrent pbinds I usually can’t go much lower than 0.025 before seeing late messages. I usually work with s.latency = nil and thus sacrifice precise timing for immidiate firing of events, this approach for everyone though.

Hello;
s.latency returns me 0.2

Yes, that is the standard setting which is way high, so that should not be the problem. What about s.options.hardwareBufferSize?

s.latency returns me 0.2

There is no way that a latency of 200 ms should cause late messages under normal circumstances. Your output also shows a massive amount of jitter (up to 250 ms!), so something is clearly going very wrong.

One thing I can imagine is that the clocks are not properly synchronized between the client and the server.

What’s your OS?

Mac M1 OSX SONOMA & Focusrite Scarlett 2i2

I’m not sure if this is the right direction to take this, but…

  • The server (on Mac) does clock sync in SC_CoreAudio.cpp, syncOSCOffsetWithTimeOfDay(). This function is called every 20 seconds (see resyncThreadFunc() immediately below it).
  • The language does clock sync in PyrSched.cpp, syncOSCOffsetWithTimeOfDay() – also called every 20 seconds (resyncThread()).

Since these are in different processes, those 20-second cycles are not synchronized. Normally this shouldn’t be a problem, because any drift should be on the order of fractions of milliseconds.

The actual gHostOSCoffset ends up being time of day (obtained in the same way in both language and server, so these should definitely correspond) minus “system time.”

The server side uses AudioGetCurrentHostTime() for “system time,” while the language side uses high_resolution_clock::now().time_since_epoch(). Perhaps sometimes, in Mac, these values differ more than expected?

That’s certainly out of my depth, but AFAICS, if there are language-vs-server time discrepancies, this is where they would occur.

hjh

1 Like

I think you’re on the right track! Some remarks:

The server side uses AudioGetCurrentHostTime() for “system time,” while the language side uses high_resolution_clock::now().time_since_epoch().

These two functions actually measure monotonic time. System time (= wall clock time) is measured by gettimeofday.

(AudioGetCurrentHostTime uses the same clock as the timestamps that are passed to the audio callback. That’s how we are actually able to synchronize the audio time to the system time.)

Perhaps sometimes, in Mac, these values differ more than expected?

It is expected that these two clocks may differ significantly. The very point of syncOSCOffsetWithTimeOfDay is to synchronize them. The important part is that the monotonic clocks are steady (no jumps), sufficiently precise and do not drift too much.

Now here’s the big catch: std::chrono::high_resolution_clock is not necessarily monotonic!

Class std::chrono::high_resolution_clock represents the clock with the smallest tick period provided by the implementation. It may be an alias of std::chrono::system_clock or std::chrono::steady_clock, or a third, independent clock.

https://en.cppreference.com/w/cpp/chrono/high_resolution_clock

In particular:

It is often just an alias for std::chrono::steady_clock or std::chrono::system_clock, but which one it is depends on the library or configuration. When it is a system_clock, it is not monotonic (e.g., the time can go backwards). For example, as of 2023, libstdc++ has it aliased to system_clock “until higher-than-nanosecond definitions become feasible”[1], MSVC has it as steady_clock [2], and libc++ uses steady_clock when the C++ standard library implementation supports a monotonic clock and system_clock otherwise[3].

I doubt that AudioGetCurrentHostTime() resp. the CoreAudio callback timestamps are broken, so I suspect the problem is more with std::chrono::high_resolution_clock.

It would be interesting to see both the OSC timestamps of the outgoing bundles on the client and the OSC timestamp for each audio callback on the server. Then we could see if there’s a problem with one particular clock. However, for that we would need to insert some printf statements. Unfortunately, I can’t really spend much time on this issue at the moment…

1 Like

Not sure if this was mentioned yet in the thread – sorry, short on time – but you’d take ar for short Envelopes, e.g. here the attack time is below the usual length of a control block (ca. 1.5 ms for 64 samples).
Yet, I cannot reproduce your errors on my mac setups.

What if you run the code without connecting the Focusrite, using the internal soundcard of the macbook, do you still have the same issues?

Hello, I am now away from home. See you tonight, thank you.