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