Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / HelpSource / Tutorials / Mark_Polishook_tutorial / 19_Scheduling.schelp
bloba4049e933e695bca380c7254f0b79d3f6163a2e8
1 title:: 19_Scheduling
2 summary:: Mark Polishook tutorial
3 categories:: Tutorials>Mark_Polishook_tutorial
4 related:: Tutorials/Mark_Polishook_tutorial/00_Introductory_tutorial
6 section::Routines and clocks
8 Use clocks to create automated, algorithmic scheduling. Among the things that clocks "play" are routines, tasks, and patterns.
10 To see how a clock "plays" a routine, first examine how a function works in a routine.
12 The first argument (and usually the only argument) to a routine is a function.
14 code::
15 // template for a routine
16 Routine({ ".... code within curly braces is a function .... "});
19 A .yield message to an expression in a function (in a routine) returns a value.
21 code::
22 r = Routine({ "hello, world".yield.postln });
24 // to evaluate a routine, send a .next message
25 // it will "hand over" the value of the expression to which the .yield message is attached
26 r.next;
29 Evaluate (again)
31 code::
32 r.next;
35 The routine above returns nil when its evaluated a second time. This is because once a routine "yields" and if there's no additional code after the .yield message, the routine is finished, over, and done - unless it receives a reset message. Then it can start over again.
37 code::
38 r.next;         // returns nil
39 r.reset;        // reset the routine
40 r.next;         // it works!
43 ////////////////////////////////////////////////////////////////////////////////////////////////////
45 code::
47 r = Routine({
48         "hello, world".yield;
49         "what a world".yield;
50         "i am a world".yield;
51 });
55 The first three .next messages return a string. The fourth .next message returns nil.
57 code::
58 r.next; // returns a string
59 r.next; // returns a string
60 r.next; // returns a string
61 r.next; // returns nil
64 Reset the routine.
66 code::
67 r.reset;
69 r.next;
70 r.next;
71 r.next;
72 r.next;
75 ////////////////////////////////////////////////////////////////////////////////////////////////////
77 Use a .do message in a routine to make a loop.
79 code::
81 r = Routine({
83         // setup code
84         var array;
85         array = [ "hello, world", "what a world", "i am a world" ];
87         // the loop
88         3.do({ array.choose.yield })
90 });
94 Evaluate the routine one more time than the loop in the routine allows.
96 code::
97 4.do({ r.next.postln });
100 The routine returned three strings followed by nil.
102 section::Scheduling routines
104 Rewrite the routine so that it includes a .wait message.
106 code::
108 r = Routine({
110         var array;
111         array = [ "hello, world", "what a world", "i am a world" ];
113         3.do({
114                 1.wait;                 // pause for 1 second
115                 array.choose.postln;
116         })
122 Then "play" the routine, eg, send it a .play message.
124 code::
125 r.play;
128 Append a .reset message to the routine so that it can start over.
130 code::
131 r.reset.play;
134 section::Clocks and the convenience of .play
136 When a routine receives a .play message, control (of the routine) is redirected to a clock. The clock uses the receiver of the .wait message as a unit of time to schedule ("play") the routine.
138 SuperCollider has three clocks, each of which has a help file.
140 code::
141 SystemClock             // the most accurate
142 AppClock                // for use with GUIs
143 TempoClock              // to schedule in beats
146 The .play message is a convenience that allows one to write
148 code::
149 r.reset.play;           // reset the routine before playing it
152 instead of
154 code::
155 SystemClock.play(r)
158 section::Scheduling synths with routines
160 Enclose synths within routines. It's often the case that the synthdef used by the synth in routines should have an envelope with a doneAction parameter set to 2 (to deallocate the memory needed for the synth after its envelope has finished playing).
162 code::
164 // DEFINE A SYNTHDEF
165 SynthDef("fm2", {
166         arg bus = 0, freq = 440, carPartial = 1, modPartial = 1, index = 3, mul = 0.2, ts = 1;
168         // index values usually are between 0 and 24
169         // carPartial :: modPartial => car/mod ratio
171         var mod;
172         var car;
174         mod = SinOsc.ar(
175                 freq * modPartial,
176                 0,
177                 freq * index * LFNoise1.kr(5.reciprocal).abs
178         );
180         car = SinOsc.ar(
181                 (freq * carPartial) + mod,
182                 0,
183                 mul
184         );
186         Out.ar(
187                 bus,
188                 car * EnvGen.kr(Env.sine(1), doneAction: 2, timeScale: ts)
189         )
190 }).add;
194 // DEFINE A ROUTINE
195 r = Routine({
197         12.do({
198                 Synth(
199                         "fm2",
200                         [
201                                 \bus, 2.rand, \freq, 400.0.rrand(1200),
202                                 \carPartial, 0.5.rrand(2), \ts, 0.5.rrand(11)
203                         ]
204                 );
205                 s.queryAllNodes;
206                 "".postln.postln.postln.postln.postln;
207                 2.wait;
208         })
212 // PLAY THE ROUTINE
213 r.reset.play;
216 ////////////////////////////////////////////////////////////////////////////////////////////////////
218 Process synths spawned in a routine through effects that run outside of the routine.
220 code::
222 // DEFINE A SYNTHDEF
223 SynthDef("echoplex", {
224         ReplaceOut.ar(
225                 0,
226                 CombN.ar(
227                         In.ar(0, 1),
228                         0.35,
229                         [Rand(0.05, 0.3), Rand(0.05, 0.3)],
230                         // generate random values every time a synth is created
231                         7,
232                         0.5
233                 )
234         )
235 }).add;
237 // DEFINE GROUPS TO CONTROL ORDER-OF-EXECUTION
238 // attach a ~source group to the head of the rootnode and
239 // an ~effects group to the tail of the rootenode
240 ~source = Group.head(s);
241 ~effect = Group.tail(s);
243 // DEFINE A ROUTINE
244 r = Routine({
246         // loop is the same as inf.do, eg, create an infinite loop that runs forever
247         loop({
248                 Synth.head(     // attach the synth to the head of the ~source group
249                         ~source,
250                         "fm2",
251                         [
252                                 \outbus, 0, \freq, 400.0.rrand(1200), \modPartial, 0.3.rrand(2.0),
253                                 \carPartial, 0.5.rrand(11), \ts, 0.1.rrand(0.2)]
254                 );
255                 s.queryAllNodes;
256                 2.wait;
257         })
260 // TURN ON EFFECTS
261 Synth.head(~effect, "echoplex");
262 Synth.tail(~effect, "echoplex");
264 // PLAY THE ROUTINE
265 r.reset.play;
268 ////////////////////////////////////////////////////////////////////////////////////////////////////
270 go to link::Tutorials/Mark_Polishook_tutorial/20_Debugging::