Strange behavior of TempoClock setTempoAtBeat

Hello,
I am relaying a question from Thor Magnusson regarding the behavior
of TempoClock when setting tempo at next beat.
For both me and Thor, setting the tempo to a lower (i.e. slower) beat value,
stops the scheduled function. Resetting it to the previous value starts it again.
Additionally, the function gets scheduled several times at once for the first beat.
(In this example audible as: creates many synths).

Cheers,
Iannis Zannos

SynthDef(\click, {arg freq=50; Out.ar(0, Ringz.ar(Impulse.ar(0.1), [freq, freq+1], 0.1) * Line.ar(1,0,0.1,1,0,2))}).add

//:
(
// get elapsed time, round up to next second
v = Main.elapsedTime.ceil;

// create two clocks in a 5:2 relation, starting at time v.
t = TempoClock(1, 0, v);
u = TempoClock(0.4, 0, v);

// start two functions at beat zero in each clock.
t.schedAbs(0, { arg beat, sec; Synth(\click, [\freq, 950]); 1 });
u.schedAbs(0, { arg beat, sec; Synth(\click, [\freq, 350]); 1 });
)
//:


// Setting the tempo to a lower value stops the scheduling
// Resetting the tempo to the earlier value starts it again.

u.setTempoAtBeat(1, 1) // continues working
u.setTempoAtBeat(3, 1) // continues working

u.setTempoAtBeat(2, 1) // STOPS!
u.setTempoAtBeat(3, 1) // Starts working again. 

The short answer is: in general, you should not use setTempoAtBeat.

Use tempo_ instead.

The example breaks because e.g. u.setTempoAtBeat(2, 1) sets the clock as if it had changed tempo at beat 1. In all likelihood, beat 1 is very, very far in the past.

Let’s say you are now at beat 50. This is 49 beats after the moment when the tempo changed. The “stops” line sets the tempo to 2/3 its previous value, meaning that each beat is 3/2 its previous duration. Assuming 1/3 second per beat (tempo = 3 from the previous line), those 49 beats were 49/3 = 16 1/3 seconds, but they are now 24.5 seconds. So there is an ~8 second discontinuity in the timeline (and the discontinuity will be larger, the further into the future you go).

In general you don’t want this.

The way to avoid this is to change the tempo only now – i.e., where the “beats” value given to setTempoAtBeat is exactly the current time.

This is what tempo_ does.

So IMO the best advice is to treat setTempoAtBeat as a private method, and avoid calling it directly. The public interface for tempo changing is tempo_.

hjh

Thanks James. I tried it and it works just fine.

Iannis