View this PageEdit this PageUploads to this PageHistory of this PageTop of the SwikiRecent ChangesSearch the SwikiHelp Guide

syncing tempclocks

Home   How To   Code Pool   Public Library   Theory   Events
here's one way of synchronizing clocks. following a master clock, it clears and reschedules the tasks in the new tempo. it does this for all slave clocks at the master's downbeat.

s.boot;
(
SynthDef(\ping, {|freq|
	var e, z;
	e= EnvGen.ar(Env.perc(0, 0.1), doneAction:2);
	z= SinOsc.ar(freq.dup, 0, 0.1);
	OffsetOut.ar(0, z*e);
}).send(s);
)

(
//number 0 is master clock, 1-3 are slaves
~v= Main.elapsedTime.ceil;
~t= {TempoClock(1, 0, ~v)}.dup(4);		//create 4 clocks in an array
~diffs= 0.dup(4);				//array that store offsets compared to master
~tasks= [
	{|beat, sec| Synth(\ping, [\freq,  800]); [\t0, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq,  900]); [\t1, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq, 1000]); [\t2, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq, 1100]); [\t3, beat, sec].postln; 1}
];
~t.do{|x, i| x.sched(~t[0].timeToNextBeat(1), ~tasks[i])}; //start all clocks and their tasks
)

//dis-sync the clocks
(
~t[0].tempo_(1.1);
~t[1].tempo_(1.2);
~t[2].tempo_(1.3);
~t[3].tempo_(1.4);
)

~diffs.postln;	//offsets still all 0

//sync
(
~t[0].sched(~t[0].timeToNextBeat(1), {				//follow master clock
	3.do{|i|						//do for all slaves
		~diffs.put(i+1, ~t[i+1].timeToNextBeat(1));	//store offset
		~t[i+1].tempo_(~t[0].tempo).clear;		//set all to master's tempo
		~t[i+1].sched(0, ~tasks[i+1]);			//reschedule
	};
	nil;
});
)

//after a sync like this you need to schedule using these offsets
~diffs.postln;

//so scheduling on the master clock is easy... (ignore offset as it is always 0)
~t[0].sched(~t[0].timeToNextBeat(1), {|beat, sec| Synth(\ping, [\freq, 2100]); 0.5});

//but note that for any other clock, scheduling need to calculate with the offsets in ~diffs
//example: start something on clock #4
~t[3].sched(~t[3].timeToNextBeat(1)+(1-~diffs[3]), {|beat, sec| Synth(\ping, [\freq, 4100]); 1/3});

~t.do{|x| x.clear};





but one can also adjust by changing tempo to something slower/faster for one intermediate beat. a bit slower but not so brutal.

(
//number 0 is master clock, 1-3 are slaves
~v= Main.elapsedTime.ceil;
~t= {TempoClock(1, 0, ~v)}.dup(4);		//create 4 clocks in an array
~tasks= [
	{|beat, sec| Synth(\ping, [\freq,  800]); [\t0, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq,  900]); [\t1, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq, 1000]); [\t2, beat, sec].postln; 1},
	{|beat, sec| Synth(\ping, [\freq, 1100]); [\t3, beat, sec].postln; 1}
];
~t.do{|x, i| x.sched(~t[0].timeToNextBeat(1), ~tasks[i])}; //start all clocks and their tasks
)

//dis-sync the clocks
(
~t[0].tempo_(1.1);
~t[1].tempo_(1.2);
~t[2].tempo_(1.3);
~t[3].tempo_(1.4);
)

//sync
(
var master= ~t[0];
master.sched(master.timeToNextBeat(1), {			//follow master clock
	var newTempo= master.tempo;				//set all to master's tempo (or other)
	3.do{|i|						//do for all slaves
		var left, temp;
		left= ~t[i+1].timeToNextBeat(1);		//percent left until next beat
		temp= newTempo/left.reciprocal;	//find adjust tempo
		~t[i+1].tempo_(temp);
		master.sched(master.timeToNextBeat(1), {
			~t[i+1].tempo_(newTempo);		//set to newTempo
			nil;
		});
	};
	nil;
});
)

//after a sync like this you not _not need to schedule using offsets

~t[0].sched(~t[0].timeToNextBeat(1), {|beat, sec| Synth(\ping, [\freq, 2100]); 0.5});
~t[3].sched(~t[3].timeToNextBeat(1), {|beat, sec| Synth(\ping, [\freq, 4100]); 1/3});

~t.do{|x| x.clear};



see also tempoclock beatmatching, interpolation, synchronisation

/redFrik

Links to this Page