1 title:: 15. Sequencing with Routines and Tasks
2 summary:: Getting Started With SuperCollider
3 categories:: Tutorials>Getting-Started
4 related:: Tutorials/Getting-Started/00-Getting-Started-With-SC
6 When you schedule a function (as in the Scheduling Events tutorial), the function always begins at the beginning and runs through to the end. For sequencing, it's more useful to have a control structure that can run part of the way through, return a value, and then pick up where it left off the next time it's needed. In SuperCollider, this is a Routine.
8 Routines can be used for data processing, e.g.
20 r.next; // get the next value from the Routine
21 6.do({ r.next.postln });
24 The first time you call next, the routine yields strong::"abcde"::. This yield value becomes the result of r.next, and is printed in the post window. On the second next call, execution picks up just after the first yield and continues with the second string, and so forth. When there is nothing more to yield, r.next returns nil.
26 We will come back to the use of routines for data generation. More important for sequencing is what happens when you schedule a routine on a clock, and the routine returns time values.
28 section::Scheduling routines
30 Recall that, when you schedule a function on a clock, numbers returned by the function are treated as time values -- specifically, the amount of time until the function should execute again. The same thing happens with numbers yielded by a routine.
36 delta = rrand(1, 3) * 0.5;
37 "Will wait ".post; delta.postln;
44 TempoClock.default.sched(0, r);
49 Now let's replace the posting statements with instructions to play a synth. Preparation:
53 SynthDef(\singrain, { |freq = 440, amp = 0.2, sustain = 1|
55 sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.perc(0.01, sustain), doneAction: 2);
56 Out.ar(0, sig ! 2); // sig ! 2 is the same as [sig, sig]
62 delta = rrand(1, 3) * 0.5;
63 Synth(\singrain, [freq: exprand(200, 800), amp: rrand(0.1, 0.5), sustain: delta * 0.8]);
70 Scheduling a routine makes a certain sense, but playing a routine seems more intuitive.
78 There you go -- our first sequence.
80 section::Pause and resume: Task
82 Routines have one sticky little characteristic that can limit their usefulness as musical objects. Once you stop a routine, you can only start it over again from the beginning. There is no way to replay the routine from the point where it was stopped.
84 Task is a variation that can be paused and resumed at will. For example, let's iterate over a C major scale. Note that all of SuperCollider's control structures are valid inside a Routine or Task. Note also that we can use 'wait' as a synonym for 'yield'.
90 [60, 62, 64, 65, 67, 69, 71, 72].do({ |midi|
91 Synth(\singrain, [freq: midi.midicps, amp: 0.2, sustain: 0.1]);
98 // probably stops in the middle of the scale
101 t.play; // should pick up with the next note
106 Task will be used for the remainder of this tutorial.
108 section::When do you want to start?
110 By default, strong::play:: applied to a Task starts the Task immediately. Most of the time, many tasks will be running simultaneously, and they should be synchronized. While there might be a virtuoso out there somewhere who can hit the enter key at just right time for precise sync, most of us would prefer a more reliable mechanism.
112 Play takes several arguments to control its behavior.
115 aRoutine.play(clock, quant)
116 aTask.play(argClock, doReset, quant)
120 ## strong::clock:: (Routine) or strong::argClock:: (Task) || Which clock should handle scheduling for this sequence
121 ## strong::doReset:: (Task only) || If true, reset the sequence to the beginning before playing; if false (default), resume
122 ## strong::quant:: || A specification of the exact starting time
125 The quant argument uses a basic model of two numbers, which can be related to the western concept of meter:
127 quant: Corresponds roughly to bar length; the current time is rounded up to the next multiple of this number
128 phase: Position within the bar (0 = beginning of the bar)
130 For convenience, if you just want to start at the beginning of the bar, you can give the bar length as a number. An array of two numbers tells SuperCollider the bar length and the phase.
132 To see how this works in practice, let's take the C major scale above and play two copies of it slightly offset. We'll slow the rhythm down to 16th-notes (0.25) and start the second one 8th-note into the bar. We will need two tasks to do this, which will be manufactured in a function.
139 [60, 62, 64, 65, 67, 69, 71, 72].do({ |midi|
140 Synth(\singrain, [freq: midi.midicps, amp: 0.2, sustain: 0.1]);
148 t = f.value.play(quant: 4); // start on next 4-beat boundary
150 u = f.value.play(quant: [4, 0.5]); // next 4-beat boundary + a half-beat
155 section::Using data routines in note sequencing
157 The previous example controls the generation of one parameter (pitch) by looping over an array inside the Task. What if you want to control several parameters?
159 Remember that routines can also generate data, in addition to their scheduling capabilities. You can refer to as many data routines as you want in your sequence.
165 [60, 72, 71, 67, 69, 71, 72, 60, 69, 67].do({ |midi| midi.yield });
168 [2, 2, 1, 0.5, 0.5, 1, 1, 2, 2, 3].do({ |dur| dur.yield });
171 SynthDef(\smooth, { |freq = 440, sustain = 1, amp = 0.5|
173 sig = SinOsc.ar(freq, 0, amp) * EnvGen.kr(Env.linen(0.05, sustain, 0.1), doneAction: 2);
183 Synth(\smooth, [freq: midi.next.midicps, sustain: delta]);
186 }).play(quant: TempoClock.default.beats + 1.0);
190 Note that routines are used for the data, but task is used for play. Also, unlike the previous infinite sequences, this one stops when it runs out of data. That's the purpose of the while loop -- it continues only as long as the 'dur' data stream keeps pumping out values. (See the link::Reference/Control-Structures:: helpfile for more on strong::while::.)
192 There must be an easier way to write the data streams -- repeatedly writing the same do loop is certainly inconvenient. In fact, there is such a way, covered in the next tutorial: sequencing with patterns.
194 (Here we use quant simply to delay Task onset by one beat. This is because it takes some time for the synthdef to be ready for use on the server. Without it, the first note would not be heard.)
196 section::A note on server messaging and timing
198 Using Synth as in the preceding examples can result in small but sometimes noticeable timing inaccuracies. This is because it takes a short time to transmit OSC messages from your code to the server, and this time is not always constant. SuperCollider deals with this by giving you the option to send the message with a timestamp telling the server exactly when the message should take effect. A strong::latency:: value is used to calculate the timestamp.
200 Latency works by adding itself to the current time on the clock. If all the messages go out with the same latency value, their timing will be precise relative to each other and to the clock. The link::Guides/ServerTiming:: help file explains in more detail how this works, but you don't really need to know all of that in order to use it. The main point is to use a consistent, small latency value for perfect timing. (A Server object has a latency variable that you can use for consistency.)
202 Here's an example illustrating the kinds of inaccuracy you might hear. The inaccuracy may be more or less noticeable on different systems. It uses the \singrain SynthDef above and plays 10 notes per second.
208 Synth(\singrain, [freq: exprand(400, 1200), sustain: 0.08]);
217 The easiest way to add latency to your outgoing Synths is with the Server strong::makeBundle:: method. Don't worry about how it works for now -- the important thing is that it uses the first value for latency, and runs the messages produced by the function according to that latency.
223 s.makeBundle(s.latency, {
224 Synth(\singrain, [freq: exprand(400, 1200), sustain: 0.08]);
236 link::Classes/Routine::, link::Classes/Task::, link::Classes/Quant::, link::Guides/ServerTiming::, link::Guides/Bundled-Messages::
238 section::Suggested Exercise
240 Make a more interesting SynthDef to replace the \smooth SynthDef. Use more arguments for greater variability. Then change the data streams in the 'Over the Rainbow' example and add new data streams to play a different tune, more expressively.
244 This document is part of the tutorial strong::Getting Started With SuperCollider::.
246 Click here to go on to the next section: link::Tutorials/Getting-Started/16-Sequencing-with-Patterns::
248 Click here to return to the table of Contents: link::Tutorials/Getting-Started/00-Getting-Started-With-SC::