2 categories::Core>Kernel
3 summary:: Functions that can return in the middle and then resume where they left off
4 related:: Classes/Stream
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.
17 Creates a Routine instance with the given function.
20 The stackSize and random seed may be overridden if desired.
23 a = Routine.new({ 1.yield; 2.yield; });
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.
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:
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":
59 r = Routine { arg inval;
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:
74 r = Routine { arg inval;
77 inval = (i + 10).yield;
84 r.value("hello routine").postln;
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.
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.
113 a Clock, TempoClock by default
116 see the link::Classes/Quant:: helpfile
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.
121 // for 6 seconds, return 200, then continue
139 // the value can also be a stream or a function
144 Routine { 100.do { |i| i.yield } }.idle(6);
158 subsection::Accessible instance variables
160 Routine inherits from link::Classes/Thread::, which allows access to some of its state:
164 r = Routine { arg inval;
166 // thisThread refers to the routine.
167 postf("beats: % seconds: % time: % \n",
168 thisThread.beats, thisThread.seconds, Main.elapsedTime
184 returns:: The elapsed beats (logical time) of the routine. The beats do not proceed when the routine is not playing.
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.
192 returns:: The thread's clock. If it has not played, it is the SystemClock.
199 r = Routine.new({ arg inval;
200 ("->inval was " ++ inval).postln;
202 ("->inval was " ++ inval).postln;
204 ("->inval was " ++ inval).postln;
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;
232 // Often you might see Wait being used to pause a routine
233 // This waits for one second between each number
236 // Wait half second before saying we're done
249 var times = { rrand(1.0, 10.0) }.dup(10) + thisThread.beats;
255 // Wait half second before saying we're done
263 // Using Routine to set button states on the fly.
266 w = SCWindow.new("State Window", Rect(150,SCWindow.screenBounds.height-140,380,60));
268 // a convenient way to set the button label
270 |but, string| but.states = [[string.asString, Color.black, Color.red]];
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
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 {
308 var func = { |i, j| sin(i * 0.07 + (j * 0.0023) + 1.5pi) * much + 1 };
310 Pen.font = Font("Helvetica-Bold", 40);
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))
323 0.yield // stop here, return something unimportant
327 fork { while { w.isClosed.not } { defer { w.refresh }; 0.04.wait; } };