File: TempoClock.schelp

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (394 lines) | stat: -rw-r--r-- 12,568 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
CLASS::TempoClock
categories::Scheduling>Clocks
summary::tempo based scheduler
related::Classes/AppClock, Classes/SystemClock

DESCRIPTION::

TempoClock is a scheduler like link::Classes/SystemClock::, but it schedules relative to a
strong::tempo:: in beats per second.

See link::Classes/Clock:: for general explanation of how clocks operate.

CLASSMETHODS::

private::initClass

method::new
Creates a new instance of TempoClock.

argument:: tempo
The initial link::#-tempo#tempo::. Defaults to code::1::.

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

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

argument:: 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 link::#-queue::.

discussion::
The TempoClock will be created strong::as if:: it started counting beats at the time given in the
code::seconds:: argument with the starting amount given in the code::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 code::0:: at the current
logical time.

code::
// 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;
)
::

method::default
Sets or gets the permanent default TempoClock instantiated at startup.
code::
TempoClock.default.beats // beats since default TempoClock was started
::

subsection:: Forwarding to the default instance

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

method::stop
method::play
method::sched
method::schedAbs
method::clear
method::tempo
method::etempo
method:: beats
method:: beats2secs
method:: secs2beats
method:: nextTimeOnGrid
method:: timeToNextBeat
method:: setTempoAtBeat
method:: setTempoAtSec
method:: setMeterAtBeat
method:: beatsPerBar
method:: baseBarBeat
method:: baseBar
method:: playNextBar
method:: beatDur
method:: elapsedBeats
method:: beats2bars
method:: bars2beats
method:: bar
method:: nextBar
method:: beatInBar
method:: isRunning

INSTANCEMETHODS::

private::prDump, prStart, prStop, prClear

method::stop
Destroys the scheduler and releases the OS thread running the scheduler.

method::clear
Removes all tasks from the scheduling queue.

method::isRunning
returns:: A Boolean, true if the clock is active or false if the clock has been stopped.

method::tempo
Sets or gets the current tempo in beats per second at the current strong::logical time::.
code::
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;
::

method::etempo
Sets or gets the current tempo at the current strong::elapsed time::.

method::permanent
Sets or gets a link::Classes/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.

method::beats

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

When strong::getting:: code::beats::, if this clock is the current Thread's
link::Classes/Thread#-clock#associated clock::, the Thread's own
link::Classes/Thread#-beats#time in beats:: is returned, otherwise the Thread's
link::Classes/Thread#-seconds#time in seconds:: converted to beats according to this
clock is returned.

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

note::
When changing code::beats::, you are only changing the clocks's notion of time, and not
the current Thread's link::Classes/Thread#-beats#logical time::, which will stay the
same until the Thread is called again. Hence, if this clock is the current Thread's
link::Classes/Thread#-clock#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.
::

code::
(
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;

::


method::schedAbs

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

When the scheduling time is up, the task's code::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: link::Classes/Clock#Scheduling::, link::Classes/Object#-awake::.


method::sched

Schedules a task to be performed code::delta:: amount of strong::beats:: after the
current Thread's logical time. If this clock is the current Thread's
link::Classes/Thread#-clock#associated clock::, the Thread's
link::Classes/Thread#-beats#time in beats:: is used, otherwise the Thread's
link::Classes/Thread#-seconds#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 code::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: link::Classes/Clock#Scheduling::, link::Classes/Object#-awake::.


method::play

Plays task (a function) at the next beat, where strong::quant:: is 1 by default. Shortcut for link::#-schedAbs::; see link::#-seconds:: and link::#-nextTimeOnGrid:: for further details on time and quant.
code::
t= TempoClock.default;
t.play({arg beats, time, clock; [beats, time, clock].postln});
::

method::playNextBar
Plays task (a function) at the next bar using link::#-schedAbs::.

method::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.

method::beatDur
Returns the duration in seconds of a current whole beat.

method::beatsPerBar
Gets or sets the number of beats per bar. The default is 4. Setting the meter must be done from within the same clock's scheduling thread. Scheduled functions and playing Routines (or Tasks) are running on the clock's thread, so both of these are valid.

Setting beatsPerBar also updates the barline reference (baseBarBeat). Be careful to call code::beatsPerBar_:: emphasis::only:: on a barline (link::#-nextBar:: below).

code::thisThread.clock:: is always the current clock -- so, writing code::thisThread.clock.beatsPerBar = /* number */:: as a matter of habit will guarantee that the meter change only ever applies to the current clock.

code::
t = TempoClock.new;

t.beatsPerBar = 3;  // error! wrong thread

t.schedAbs(t.nextBar, { t.beatsPerBar_(3) });  // OK

t.beatsPerBar;  // will be reflected after the barline

t.schedAbs(t.nextBar, { thisThread.clock.beatsPerBar_(4) });  // OK

(
r = Routine {
	// by addressing "this" clock,
	// we are always changing meter in the right place
	var clock = thisThread.clock;
	
	clock.beats.debug("start time");
	clock.timeToNextBeat(-1).wait;
	clock.beats.debug("barline");

	// no need to 'sched'
	// because the Routine is already on this clock
	// flip 3/4 -> 4/4 or 4/4 -> 3/4
	clock.beatsPerBar = 7 - clock.beatsPerBar;
	clock.beatsPerBar.debug("set meter to");

	0.01.wait;  // must be after barline for 'timeToNextBeat'
	clock.timeToNextBeat(-1).wait;
	clock.beats.debug("next barline");
}.play;
)
::

method::bar
Returns the current bar. See link::#-bars2beats:: for returning beat of current bar.

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

method::beatInBar
Returns the current bar beat (as a link::Classes/Float::) in relation to link::#-beatsPerBar::. Values range from 0 to < beatsPerBar.

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

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

method::beats2bars
Returns a bar as a float relative to link::#-baseBarBeat::.

method::bars2beats
Returns a beat relative to link::#-baseBar::.
code::
t= TempoClock.default;
t.bars2beats(t.bar) // downbeat of the current bar
::

method::timeToNextBeat
Returns the logical time to next beat. strong::quant:: is 1 by default, relative to baseBarBeat, see link::#-nextTimeOnGrid::.

method::nextTimeOnGrid
With default values, returns the next whole beat. strong::quant:: is 1 by default, strong::phase:: is 0. quant is relative to link::#-baseBarBeat::, such that
code::
t= TempoClock.default;
t.nextTimeOnGrid(t.beatsPerBar) == t.nextBar // => true
::
Together strong::quant:: and strong::phase:: are useful for finding the next n beat in a bar, e.g. code::nextTimeOnGrid(4, 2):: will return the next 3rd beat of a bar (of 4 beats), whereas code::nextBar-2:: may return an elapsed beat.

code::referenceBeat:: may be used to evaluate the quant and phase relative to an arbitrary beat. Normally, no reference beat will be specified (code::nil::) and the clock's current beats will be used.

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

method::seconds
Returns the current elapsed time. (This method is inherited from link::Classes/Clock::.)

method::beats2secs
Converts absolute strong::beats:: to absolute strong::seconds::, returning the elapsed time of the clock at the given strong::beats::. Only works for times in the current tempo. If the tempo changes any computed time in future will be wrong.
code::
t= TempoClock.default;
t.beats2secs(t.beats) // equivalent to t.seconds
t.beats2secs(0) // how many seconds after startup did beat 0 occur?
::

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

EXAMPLES::

code::
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;
::
code::
(
// 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;
)
::
code::
(
// 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;
)
::