Classes | Scheduling > Clocks

TempoClock : Clock : Object

tempo based scheduler
Source: Clock.sc
Subclasses: TempoBusClock

Description

TempoClock is a scheduler like SystemClock, but it schedules relative to a tempo in beats per second.

See Clock for general explanation of how clocks operate.

Class Methods

TempoClock.new(tempo, beats, seconds, queueSize: 256)

Creates a new instance of TempoClock.

Arguments:

tempo

The initial tempo. Defaults to 1.

beats

The time in beats, corresponding to the reference time given with the seconds argument. Defaults to 0.

seconds

The reference time in seconds, to which the beats argument corresponds. Defaults to the current Thread's logical time (see Thread: -seconds).

queueSize

The storage size of the scheduling queue. Each scheduled item takes 2 counts of space, so this size divided by 2 gives the amount of items that can be scheduled at a time. See also -queue.

Discussion:

The TempoClock will be created as if it started counting beats at the time given in the seconds argument with the starting amount given in the beats argument. The current count of beats will thus be equal to that starting amount plus the amount of beats that would be counted since the given reference time in seconds, according to the given tempo.

The default arguments create a TempoClock that starts counting beats with 0 at the current logical time.

// Create a TempoClock that starts counting beats with 5 now.
(
t = TempoClock.new(2, 5);
"current beats:" + t.beats;
)

// Create a TempoClock, as if it started counting beats 5 seconds ago with 0.
(
t = TempoClock.new(2, 0, thisThread.seconds - 5);
"current beats:" + t.beats;
)

TempoClock.default

TempoClock.default = value

Sets or gets the permanent default TempoClock instantiated at startup.

TempoClock.default.beats // beats since default TempoClock was started

Forwarding to the default instance

The following methods only forward to the default instance, allowing you to use the TempoClock class itself in place of TempoClock.default.

TempoClock.stop

TempoClock.play(task, quant)

TempoClock.sched(delta, item)

TempoClock.schedAbs(beat, item)

TempoClock.clear(releaseNodes)

TempoClock.tempo

TempoClock.tempo = newTempo

TempoClock.etempo = newTempo

TempoClock.beats

TempoClock.beats =

TempoClock.beats2secs(beats)

TempoClock.secs2beats(secs)

TempoClock.nextTimeOnGrid(quant: 1, phase: 0)

TempoClock.timeToNextBeat(quant: 1)

TempoClock.setTempoAtBeat(newTempo, beats)

TempoClock.setTempoAtSec(newTempo, secs)

TempoClock.setMeterAtBeat(newBeatsPerBar, beats)

TempoClock.beatsPerBar

TempoClock.beatsPerBar = newBeatsPerBar

TempoClock.baseBarBeat

TempoClock.baseBar

TempoClock.playNextBar(task)

TempoClock.beatDur

TempoClock.elapsedBeats

TempoClock.beats2bars(beats)

TempoClock.bars2beats(bars)

TempoClock.bar

TempoClock.nextBar(beat)

TempoClock.beatInBar

Inherited class methods

Undocumented class methods

TempoClock.all

TempoClock.cmdPeriod

Instance Methods

.stop

Destroys the scheduler and releases the OS thread running the scheduler.

.clear(releaseNodes: true)

Removes all tasks from the scheduling queue.

.tempo

.tempo = newTempo

Sets or gets the current tempo in beats per second at the current logical time.

t= TempoClock.new;
t.tempo_(2.0); // equivalent to t.tempo = 2.0;
t.tempo;
t.tempo_(72/60) // 72 beats per minute
t.tempo;

.etempo = newTempo

Sets or gets the current tempo at the current elapsed time.

.permanent

.permanent = value

Sets or gets a Boolean value indicating whether the clock will survive cmd-period. If false the clock is stopped (and thus removed) on cmd-period. If true the clock survives cmd-period. It is false by default.

.beats

.beats = beats

Gets or sets the current logical time in beats according to this clock.

When getting beats, if this clock is the current Thread's associated clock, the Thread's own time in beats is returned, otherwise the Thread's time in seconds converted to beats according to this clock is returned.

After changing beats towards the future, the clock will immediately perform all tasks scheduled until the new time. Likewise, when changing beats towards the past, already scheduled tasks will be postponed, so they will still be performed at the scheduled time in beats.

NOTE: When changing beats, you are only changing the clocks's notion of time, and not the current Thread's logical time, which will stay the same until the Thread is called again. Hence, if this clock is the current Thread's associated clock, and you ask the clock for time in beats just after changing it, you will see no effect. Nevertheless, the effect will be visible immediately on a different Thread.
(
t = TempoClock.new;

t.sched(3, {
    t.beats = 100;
    t.beats.postln; // still 3
    nil
});
)

(
c = TempoClock.new;
fork {
    loop {
        c.beats.postln; // updates, because ".wait" calls the thread
        1.wait;
    }
};
)

c.beats = 100;

.schedAbs(beat, item)

Schedules a task to be performed at a particular time in beats.

When the scheduling time is up, the task's awake method is called. If the method returns a number, the task will be rescheduled for the time equal to the last scheduling time plus the returned value.

See also: Clock: Scheduling, Object: -awake.

.sched(delta, item)

Schedules a task to be performed delta amount of beats after the current Thread's logical time. If this clock is the current Thread's associated clock, the Thread's time in beats is used, otherwise the Thread's time in seconds is converted to beats according to this clock's tempo and time of origin.

When the scheduling time is up, the task's awake method is called. If the method returns a number, the task will be rescheduled for the time equal to the last scheduling time plus the returned value.

See also: Clock: Scheduling, Object: -awake.

.play(task, quant: 1)

Plays task (a function) at the next beat, where quant is 1 by default. Shortcut for -schedAbs; see -seconds and -nextTimeOnGrid for further details on time and quant.

t= TempoClock.default;
t.play({arg beats, time, clock; [beats, time, clock].postln});

.playNextBar(task)

Plays task (a function) at the next bar using -schedAbs.

.queue

Returns the scheduling queue Array in the form [beat, function]. The maximum number of items is determined by the clock's queueSize argument upon instantiation. The default queueSize of 256 allows 128 functions to be in the queue at any time.

.beatDur

Returns the duration in seconds of a current whole beat.

.beatsPerBar

.beatsPerBar = newBeatsPerBar

Gets or sets the number of beats per bar. The default is 4. Setting must be done from within the scheduling thread, e.g.

t= TempoClock.new;
t.schedAbs(t.nextBar, {t.beatsPerBar_(3)});
t.beatsPerBar;

.bar

Returns the current bar. See -bars2beats for returning beat of current bar.

.nextBar(beat)

Returns the number of beats at the next bar line relative to the beat argument. If beat is not supplied, returns the beat at which the next bar begins.

.beatInBar

Returns the current bar beat (as a Float) in relation to -beatsPerBar. Values range from 0 to < beatsPerBar.

.baseBar

Returns bar at which -beatsPerBar was last changed. If beatsPerBar has not been changed since the clock was created, returns 0.

.baseBarBeat

Returns beat at which the -beatsPerBar was last changed. If beatsPerBar has not been changed since the clock was created, returns 0.

.beats2bars(beats)

Returns a bar as a float relative to -baseBarBeat.

.bars2beats(bars)

Returns a beat relative to -baseBar.

t= TempoClock.default;
t.bars2beats(t.bar) // downbeat of the current bar

.timeToNextBeat(quant: 1)

Returns the logical time to next beat. quant is 1 by default, relative to baseBarBeat, see -nextTimeOnGrid.

.nextTimeOnGrid(quant: 1, phase: 0)

With default values, returns the next whole beat. quant is 1 by default, phase is 0. quant is relative to -baseBarBeat, such that

t= TempoClock.default;
t.nextTimeOnGrid(t.beatsPerBar) == t.nextBar // => true

Together quant and phase are useful for finding the next n beat in a bar, e.g. nextTimeOnGrid(4, 2) will return the next 3rd beat of a bar (of 4 beats), whereas nextBar-2 may return an elapsed beat.

.elapsedBeats

Returns the current elapsed time in beats. This is equivalent to tempoClock.secs2beats(Main.elapsedTime). It is often preferable to use -beats instead of elapsedBeats because beats uses a thread's logical time.

.seconds

Returns the current elapsed time. (This method is inherited from Clock.)

.beats2secs(beats)

Converts absolute beats to absolute seconds, returning the elapsed time of the clock at the given beats. Only works for times in the current tempo. If the tempo changes any computed time in future will be wrong.

t= TempoClock.default;
t.beats2secs(t.beats) // equivalent to t.seconds
t.beats2secs(0) // how many seconds after startup did beat 0 occur?

.secs2beats(secs)

Converts absolute seconds to absolute beats. Only works for times in the current tempo. If the tempo changes any computed time in future will be wrong.

Inherited instance methods

Undocumented instance methods

.archiveAsCompileString

.setMeterAtBeat(newBeatsPerBar, beats)

.setTempoAtBeat(newTempo, beats)

.setTempoAtSec(newTempo, secs)

Examples

t = TempoClock(1); // create a TempoClock

// schedule an event at next whole beat
t.schedAbs(t.beats.ceil, { arg beat, sec; [beat, sec].postln; 1 });

t.tempo = 2;
t.tempo = 4;
t.tempo = 0.5;
t.tempo = 1;

t.clear;

t.schedAbs(t.beats.ceil, { arg beat, sec; [beat, sec].postln; 1 });

t.stop;

(
// 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; [\t, beat, sec].postln; 1 });
u.schedAbs(0, { arg beat, sec; [\u, beat, sec].postln; 1 });
)

(
u.tempo = u.tempo * 3;
t.tempo = t.tempo * 3;
)

(
u.tempo = u.tempo * 1/4;
t.tempo = t.tempo * 1/4;
)

(
t.stop;
u.stop;
)

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

// create two clocks, starting at time v.
t = TempoClock(1, 0, v);
u = TempoClock(1, 0, v);

// start two functions at beat zero in each clock.
// t controls u's tempo. They should stay in sync.
t.schedAbs(0, { arg beat, sec; u.tempo = t.tempo * [1,2,3,4,5].choose; [\t, beat, sec].postln; 1 });
u.schedAbs(0, { arg beat, sec; [\u, beat, sec].postln; 1 });
)

(
u.tempo = u.tempo * 3;
t.tempo = t.tempo * 3;
)

(
u.tempo = u.tempo * 1/4;
t.tempo = t.tempo * 1/4;
)

(
t.stop;
u.stop;
)