Custom TempoClock and Pattern syncronization

Hi guys,
I’m working on a new project after a while where I have to deal with bars and beat syncronization.
I start with the following question: why these sounds appear to me not to be syncronized at all?

Here’s the steps I followed:

I create a brand new clock on which I schedule some sine beep to be played (metronome sort of)

(
t = TempoClock.new(78/60);
// start counting just after the clock creation
t.sched(0, {
	"bar: %\tbeat:%\t(tot beats:%)\n".postf( t.bar, t.beatInBar, t.beats);
	{Out.ar(0, EnvGen.ar(Env.perc(0.0, 0.1), doneAction:2) * SinOsc.ar(1000) * 0.125)}.play;
	1;
});
)

Then I instantiate a pattern, which is working on the newly created clock.

(
Pbindef(\test,
	\instrument, \default,
	\octave, 5,
	\scale, Scale.major,
	\root, 0,
	\degree, Pseq([0,2,4,7],inf),
	\dur, 1
).play(t, quant:Quant(4,0,0));
)

I would have expected the pattern sounds to be perfectly synchronised with those from the TimeClock, but why are they out of sync? Specifically, in my case, the pattern sounds seem to lag behind those generated by the TimeClock’s scheduling.

Is this a problem caused by my PC’s latency? Is there something I’m not taking into account?

Because, in fact, after evaluating the command Pbindef(\test).stop.clear; I make this change and re-evaluate

(
Pbindef(\test,
	\instrument, \default,
	\octave, 5,
	\scale, Scale.major,
	\root, 0,
	\degree, Pseq([0,2,4,7],inf),
	\dur, 1
).play(t, quant:Quant(4,-0.3,0));
)

The pattern seems to be perfectly in sync to me, as if, by adjusting the phase, the sounds have finally fallen into line.

Is this normal behaviour?

And why exactly is the correct phase for offsetting the pattern relative to the TimeClock set to 0.3 (if I’ve understood correctly, that’s a 300ms advance relative to the bar/beat lines of the TimeClock, is that right?)?
Is this something that could change all the time? Perhaps every time the PC/SuperCollider is restarted?Is this something that can be predicted in advance?

Do you suggest some other methods?

Thank you very much for your support

if you wrap your metronome-sound in a bundle for the server it should be in sync with the Pbindef.

(
t = TempoClock.new(78/60);
// start counting just after the clock creation
t.sched(0, {
	"bar: %\tbeat:%\t(tot beats:%)\n".postf( t.bar, t.beatInBar, t.beats);
		
	s.bind { {Out.ar(0, EnvGen.ar(Env.perc(0.0, 0.1), doneAction:2) * SinOsc.ar(1000) * 0.125)}.play; };
	1;
});
)

1 Like

Thank you @mstep ,
it actually works as you described. But why?
Why wrapping it inside a bundle is making everything in sync?

The Pbindef uses the server latency.
When you {}.play something, the server plays the sound as soon as it can. Using s.bind {} it takes the server latency into account in determining when to play it, so it can be in sync.

https://docs.supercollider.online/Classes/Server.html#-bind

1 Like

Why wrapping it inside a bundle is making everything in sync?

TempoClock -sched or SystemClock -sched only schedules objects on the language, but for correct musical timing you also need to schedule your messages ahead of time on the Server. This is done by sending timestamped OSC bundles. That’s exactly what the EventStreamPlayer (Pbind -play) does under the hood. Note that by default both the EventStreamPlayer and Server -bind use the current Server latency (Server -latency) to determine the timing offset for the OSC bundle timestamp.

See also Scheduling and Server timing | SuperCollider 3.14.0 Help

s.bind { {Out.ar(0, EnvGen.ar(Env.perc(0.0, 0.1), doneAction:2) * SinOsc.ar(1000) * > 0.125)}.play; };

This is a bit problematic because play is an asynchronous operation: it first creates a SynthDef and then creates a Synth from it. You only get correct timing by accident, likely because the Server latency is high enough, but this can break with shorter latencies. (In fact, I’m a bit surprised this works at all.) Instead, you should make a proper SynthDef beforehand and in s.bind only create the Synth.

1 Like

Thank you so much @mstep and @Spacechild1 for your support and further explanation about the topic.

I’ve read the documentation and start understanding better the topic but, I continue having some trouble in extending this and applying it on another context.

I’ve made a new post here about audiofile and pattern syncronization. Would you please let me know if you have any advice?

Thank you so much