class library: SynthDef - lazy implementation of removeUGen
[supercollider.git] / HelpSource / Classes / Routine.schelp
blobb58be800214d82bcb68010745fe099c10ad318e0
1 class::Routine
2 categories::Core>Kernel
3 summary:: Functions that can return in the middle and then resume where they left off
4 related:: Classes/Stream
6 description::
7 Routines are functions that can return in the middle and then resume where
8 they left off when called again. Routines can be used to implement co-routines
9 as found in Scheme and some other languages.
10 Routines are useful for writing things that behave like Streams.
11 Routines inherit behaviour for math operations and filtering from Stream.
13 classMethods::
15 method::new
17 Creates a Routine instance with the given function.
19 discussion::
20 The stackSize and random seed may be overridden if desired.
22 code::
23 a = Routine.new({ 1.yield; 2.yield; });
24 a.next.postln;
25 a.next.postln;
26 a.next.postln;
29 instanceMethods::
31 method::next
33 The Routine function is either started if it has not been called yet, or it is
34 resumed from where it left off. The argument inval is passed as the argument
35 to the Routine function if it is being started, or as the result of the code::yield::
36 method if it is being resumed from a yield.
38 returns:: The value that the Routine yields.
40 discussion::
41 There are basically 2 conditions for a Routine: one is when the routine starts. The other case is
42 that the routine continues after it has yielded.
44 When the routine starts (by calling the above methods), you are passing in a first inval.
45 This inval is accessible as the routine function argument:
47 code::
48 Routine { arg inval;
49         inval.postln;
50 }.value("hello routine");
53 When there is a yield in the routine, the next time you call next (or synonym), the routine continues
54 from there, and you get a chance to pass in a value from the outside. To access that value within the
55 continuing routine, you have to assign the result of the yield call to a variable. Typically the name inval (or inevent) is reused, instead of declaring a variable like "valuePassedInbyYield":
57 code::
59 r = Routine { arg inval;
60         inval.postln;
61         inval = 123.yield;
62         inval.postln;
66 r.value("hello routine");
67 r.value("goodbye world");
70 Typically a routine uses a multiple yield, in which the inval is reassigned repeatedly:
72 code::
74 r = Routine { arg inval;
75         inval.postln;
76         5.do { arg i;
77                 inval = (i + 10).yield;
78                 inval.postln;
79         }
83 5.do {
84         r.value("hello routine").postln;
89 method::value
91 same as code::next::
93 method::resume
95 same as code::next::
97 method::reset
99 Causes the Routine to start from the beginning next time it is called.
100 A Routine cannot reset itself except by calling the code::yieldAndReset:: method.
102 See also code::yield, yieldAndReset, alwaysYield:: in class link::Classes/Object::
104 If a Routine's function returns then it will always yield nil until reset.
106 method::play
108 In the SuperCollider application, a Routine can be played using a link::Classes/Clock::, as can any link::Classes/Stream::.
109 every time the Routine yields, it should do so with a float, the clock will interpret that, usually
110 pausing for that many seconds, and then resume the routine, passing it the clock's current time.
112 argument::clock
113 a Clock, TempoClock by default
115 argument::quant
116 see the link::Classes/Quant:: helpfile
118 discussion::
119 using link::Classes/Object#idle#Object:idle:: within a routine, return values until this time is over. Time is measured relative to the thread's clock.
120 code::
121 // for 6 seconds, return 200, then continue
123 r = Routine {
124                 199.yield;
125                 189.yield;
126                 200.idle(6);
127                 199.yield;
128                 189.yield;
131 fork {
132         loop {
133                 r.value.postln;
134                 1.wait;
135         }
139 // the value can also be a stream or a function
141 r = Routine {
142                 199.yield;
143                 189.yield;
144                 Routine { 100.do { |i| i.yield } }.idle(6);
145                 199.yield;
146                 189.yield;
149 fork {
150         loop {
151                 r.value.postln;
152                 1.wait;
153         }
158 subsection::Accessible instance variables
160 Routine inherits from link::Classes/Thread::, which allows access to some of its state:
162 code::
164 r = Routine { arg inval;
165         loop {
166                 // thisThread refers to the routine.
167                 postf("beats: % seconds: % time: % \n",
168                         thisThread.beats, thisThread.seconds, Main.elapsedTime
169                 );
170                 1.0.yield;
172         }
173 }.play;
176 r.stop;
177 r.beats;
178 r.seconds;
179 r.clock;
182 method::beats
184 returns:: The elapsed beats (logical time) of the routine. The beats do not proceed when the routine is not playing.
186 method::seconds
188 returns:: The elapsed seconds (logical time) of the routine. The seconds do not proceed when the routine is not playing, it is the converted beat value.
190 method::clock
192 returns:: The thread's clock. If it has not played, it is the SystemClock.
194 examples::
196 code::
198 var r, outval;
199 r = Routine.new({ arg inval;
200         ("->inval was " ++ inval).postln;
201         inval = 1.yield;
202         ("->inval was " ++ inval).postln;
203         inval = 2.yield;
204         ("->inval was " ++ inval).postln;
205         inval = 99.yield;
208 outval = r.next('a');
209 ("<-outval was " ++ outval).postln;
210 outval = r.next('b');
211 ("<-outval was " ++ outval).postln;
212 r.reset; "reset".postln;
213 outval = r.next('c');
214 ("<-outval was " ++ outval).postln;
215 outval = r.next('d');
216 ("<-outval was " ++ outval).postln;
217 outval = r.next('e');
218 ("<-outval was " ++ outval).postln;
219 outval = r.next('f');
220 ("<-outval was " ++ outval).postln;
224 code::
225 // wait
228 var r;
229 r = Routine {
230         10.do({ arg a;
231                 a.postln;
232                 // Often you might see Wait being used to pause a routine
233                 // This waits for one second between each number
234                 1.wait;
235         });
236         // Wait half second before saying we're done
237         0.5.wait;
238         "done".postln;
239 }.play;
243 code::
244 // waitUntil
247 var r;
248 r = Routine {
249         var times = { rrand(1.0, 10.0) }.dup(10) + thisThread.beats;
250         times = times.sort;
251         times.do({ arg a;
252                 waitUntil(a);
253                 a.postln;
254         });
255         // Wait half second before saying we're done
256         0.5.wait;
257         "done".postln;
258 }.play;
262 code::
263 // Using Routine to set button states on the fly.
265 var update, w, b;
266 w = SCWindow.new("State Window", Rect(150,SCWindow.screenBounds.height-140,380,60));
268 // a convenient way to set the button label
269 update = {
270         |but, string| but.states = [[string.asString, Color.black, Color.red]];
271         but.refresh;
274 b = SCButton(w, Rect(10,10,360,40));
275 b.font_(Font("Impact", 24));
277 update.value(b, "there is only one state");
279 // if an action should do something different each time it is called, a routine is the
280 // right thing to use. This is better than creating variables outside and setting them
281 // from the action function to keep state from one action to the next
283 b.action_(Routine { |butt|
284         rrand(15, 45).do { |i|
285                 update.value(butt, "%. there is still only 1 state".format(i + 2));
286                 0.yield; // stop here
287         };
288         w.close;
291 w.front;
295 code::
296 // drawing in a window dynamcially with Pen
298 var w, much = 0.02, string, synth;
300 w = Window.new("swing", Rect(100, 100, 300, 500)).front;
301 w.view.background_(Color.new255(153, 255, 102).vary);
303 string = "swing ".dup(24).join;
305 w.drawHook = Routine {
306         var i = 0;
307         var size = 40;
308         var func = { |i, j| sin(i * 0.07 + (j * 0.0023) + 1.5pi) * much + 1 };
309         var scale;
310         Pen.font = Font("Helvetica-Bold", 40);
311         loop {
312                 i = i + 1;
313                 string.do {     |char, j|
315                         scale = func.value(i, j).dup(6);
317                         Pen.fillColor = Color.new255(0, 120, 120).vary;
318                         Pen.matrix = scale * #[1, 0, 0, 1, 1, 0];
319                         Pen.stringAtPoint(char.asString,
320                                 ((size * (j % 9)) - 10) @ (size * (j div: 9))
321                         );
322                 };
323                 0.yield // stop here, return something unimportant
324         }
327 fork { while { w.isClosed.not } { defer { w.refresh }; 0.04.wait; } };
329 w.front;