Synchronizing client-side clock and Demand UGens on server

Hello

I’d like to use server-side sequencing with Demand rate UGens, and client-side sequencing with patterns, and share a same clock between them.
Like syncing synths with arpeggiators.

My solution at the moment is using Pbind with type \set, TempoBusClock and Groups, but when changing tempo, synths start to go out of sync.

// Synthdefs

(
SynthDef(\bass, { |baseNote = 40, tempo = 1, out = 0|
	var snd;
	
	var beats = [1, 1, 1, 0.5, 0.5] / tempo;
	var notes = [0, 0, 0, 3, 5];
	
	var trigger = TDuty.kr(Dseq(beats, inf));
	var note = Demand.kr(trigger, 0, baseNote + Dseq(notes, inf));
	snd = LFTri.ar([note.midicps, (note + 0.1).midicps]) * EnvGen.kr(Env.perc, trigger);
	Out.ar(0, snd);
}).add;

SynthDef(\arpeggio, { |baseNote = 40, tempo = 1, out = 0|
	var snd;
	
	var beats = [1, 0.5, 0.5, 2, 1, 2, 0.5, 0.5] / 8 / tempo;
	var notes = [0, 3, 7, 12, 14, 15, 19, 20];
	
	var trigger = TDuty.kr(Dseq(beats, inf));
	var note = Demand.kr(trigger, 0, baseNote + Dseq(notes, inf));
	snd =LFPulse.ar(note.midicps ! 2, width: LFNoise0.kr(tempo.reciprocal).range(0.2, 0.8)) * EnvGen.kr(Env.perc, trigger);
	snd = (snd * 0.4)+ (NHHall.ar(snd) * 0.2);
	Out.ar(0, snd);
}).add;
)

// Instanciate synths but don't start playing
(
~group = Group.new;
~bass = Synth.newPaused(\bass, target: ~group);
~arpeggio = Synth.newPaused(\arpeggio, target: ~group);
)

// Set TempoBusClock
~clock = TempoBusClock.new(~group);

// Play a pattern to control the whole thing
(
~bass.run;
~arpeggio.run;
Pbind(*[
	type: \set,
	id: ~group.nodeID,
	args: #[baseNote],
	baseNote: Pseq([40, 42, 44, 38], inf),
	dur: 4
]).play(~clock);
)

// change tempo
~clock.tempo = 20/60;
~clock.tempo = 45/60;
~clock.tempo = 59/60;

Did I go the wrong route ? Is there a more elegant solution to sync Synths with demand rate Ugens and clocks on the client ?

Thanks in advance !

Geoffroy

The ddwCommon quark has TempoClick: https://github.com/jamshark70/ddwCommon/blob/master/HelpSource/Classes/TempoClick.schelp

But FWIW I gave up on server-side sequencing over a decade ago. You gain no real benefit over patterns and sync is harder. It’s not worth it IMO.

hjh

Thanks a lot James.
I have to rethink the way I can used TDuty with a trigger that gives me the tempo.
That’s tricky indeed !

I’d suggest to think very carefully about what is the concrete benefit you hope to get from server-side sequencing. If there’s another way to get that result using patterns, then the sync problem goes away. But, I could imagine there might be cases where you might need, for instance, some kind of integration between sequence values and continuous signals that might be difficult to do in the language.

Which way you go then depends on which is harder: syncing language and server time, or communicating data between the language and server.

Syncing language and server time is harder than you think. It’s one of those where you think it’s a really cool idea but if it ends up making things harder, then it’s not worth it.

For example, here I see you’re using the SynthDef to produce a short pattern of notes that has been transposed according to baseNote – that is, two levels of sequencing (sequencing individual notes, and sequencing the transposition). Paddp can do that.

(
SynthDef(\arpeggio, { |freq = 40, out = 0, amp = 0.1|
	var snd;
	
	snd = LFPulse.ar(freq, width: Rand(0.2, 0.8)) * EnvGen.kr(Env.perc, doneAction: 2);
	Out.ar(0, (snd * amp).dup);
}).add;
)

(
p = Paddp(
	\midinote, Pseq([40, 42, 44, 38], inf),  // outer level transposition
	Pbind(
		\instrument, \arpeggio,
		// *one* repeat only here, to allow Paddp to get control back
		\midinote, Pseq([0, 3, 7, 12, 14, 15, 19, 20], 1),
		\dur, Pseq([1, 0.5, 0.5, 2, 1, 2, 0.5, 0.5], 1) / 8
	)
).play;
)

p.stop;

Or, if you really want the synth to play the arpeggio, you could make it play one arpeggio only, and then use Pbind with type \note to play a new synth for each arpeggio.

Using either of these approaches, then you don’t have to deal with TempoBusClock, TempoClick, or any other sync mechanism – the tricky part is gone.

hjh