/usr/share/SuperCollider/HelpSource/Classes/Routine.schelp is in supercollider-common 1:3.6.3~repack-5.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | class::Routine
categories::Core>Kernel
summary:: Functions that can return in the middle and then resume where they left off
related:: Classes/Stream
description::
Routines are functions that can return in the middle and then resume where
they left off when called again. Routines can be used to implement co-routines
as found in Scheme and some other languages.
Routines are useful for writing things that behave like Streams.
Routines inherit behaviour for math operations and filtering from Stream.
classMethods::
method::new
Creates a Routine instance with the given function.
discussion::
The stackSize and random seed may be overridden if desired.
code::
a = Routine.new({ 1.yield; 2.yield; });
a.next.postln;
a.next.postln;
a.next.postln;
::
instanceMethods::
method::next
The Routine function is either started if it has not been called yet, or it is
resumed from where it left off. The argument inval is passed as the argument
to the Routine function if it is being started, or as the result of the code::yield::
method if it is being resumed from a yield.
returns:: The value that the Routine yields.
discussion::
There are basically 2 conditions for a Routine: one is when the routine starts. The other case is
that the routine continues after it has yielded.
When the routine starts (by calling the above methods), you are passing in a first inval.
This inval is accessible as the routine function argument:
code::
Routine { arg inval;
inval.postln;
}.value("hello routine");
::
When there is a yield in the routine, the next time you call next (or synonym), the routine continues
from there, and you get a chance to pass in a value from the outside. To access that value within the
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":
code::
(
r = Routine { arg inval;
inval.postln;
inval = 123.yield;
inval.postln;
}
)
r.value("hello routine");
r.value("goodbye world");
::
Typically a routine uses a multiple yield, in which the inval is reassigned repeatedly:
code::
(
r = Routine { arg inval;
inval.postln;
5.do { arg i;
inval = (i + 10).yield;
inval.postln;
}
}
)
(
5.do {
r.value("hello routine").postln;
}
)
::
method::value
same as code::next::
method::resume
same as code::next::
method::reset
Causes the Routine to start from the beginning next time it is called.
A Routine cannot reset itself except by calling the code::yieldAndReset:: method.
See also code::yield, yieldAndReset, alwaysYield:: in class link::Classes/Object::
If a Routine's function returns then it will always yield nil until reset.
method::play
In the SuperCollider application, a Routine can be played using a link::Classes/Clock::, as can any link::Classes/Stream::.
every time the Routine yields, it should do so with a float, the clock will interpret that, usually
pausing for that many seconds, and then resume the routine, passing it the clock's current time.
argument::clock
a Clock, TempoClock by default
argument::quant
see the link::Classes/Quant:: helpfile
discussion::
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.
code::
// for 6 seconds, return 200, then continue
(
r = Routine {
199.yield;
189.yield;
200.idle(6);
199.yield;
189.yield;
};
fork {
loop {
r.value.postln;
1.wait;
}
}
);
// the value can also be a stream or a function
(
r = Routine {
199.yield;
189.yield;
Routine { 100.do { |i| i.yield } }.idle(6);
199.yield;
189.yield;
};
fork {
loop {
r.value.postln;
1.wait;
}
}
);
::
subsection::Accessible instance variables
Routine inherits from link::Classes/Thread::, which allows access to some of its state:
code::
(
r = Routine { arg inval;
loop {
// thisThread refers to the routine.
postf("beats: % seconds: % time: % \n",
thisThread.beats, thisThread.seconds, Main.elapsedTime
);
1.0.yield;
}
}.play;
)
r.stop;
r.beats;
r.seconds;
r.clock;
::
method::beats
returns:: The elapsed beats (logical time) of the routine. The beats do not proceed when the routine is not playing.
method::seconds
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.
method::clock
returns:: The thread's clock. If it has not played, it is the SystemClock.
examples::
code::
(
var r, outval;
r = Routine.new({ arg inval;
("->inval was " ++ inval).postln;
inval = 1.yield;
("->inval was " ++ inval).postln;
inval = 2.yield;
("->inval was " ++ inval).postln;
inval = 99.yield;
});
outval = r.next('a');
("<-outval was " ++ outval).postln;
outval = r.next('b');
("<-outval was " ++ outval).postln;
r.reset; "reset".postln;
outval = r.next('c');
("<-outval was " ++ outval).postln;
outval = r.next('d');
("<-outval was " ++ outval).postln;
outval = r.next('e');
("<-outval was " ++ outval).postln;
outval = r.next('f');
("<-outval was " ++ outval).postln;
)
::
code::
// wait
(
var r;
r = Routine {
10.do({ arg a;
a.postln;
// Often you might see Wait being used to pause a routine
// This waits for one second between each number
1.wait;
});
// Wait half second before saying we're done
0.5.wait;
"done".postln;
}.play;
)
::
code::
// waitUntil
(
var r;
r = Routine {
var times = { rrand(1.0, 10.0) }.dup(10) + thisThread.beats;
times = times.sort;
times.do({ arg a;
waitUntil(a);
a.postln;
});
// Wait half second before saying we're done
0.5.wait;
"done".postln;
}.play;
)
::
code::
// Using Routine to set button states on the fly.
(
var update, w, b;
w = SCWindow.new("State Window", Rect(150,SCWindow.screenBounds.height-140,380,60));
// a convenient way to set the button label
update = {
|but, string| but.states = [[string.asString, Color.black, Color.red]];
but.refresh;
};
b = SCButton(w, Rect(10,10,360,40));
b.font_(Font("Impact", 24));
update.value(b, "there is only one state");
// if an action should do something different each time it is called, a routine is the
// right thing to use. This is better than creating variables outside and setting them
// from the action function to keep state from one action to the next
b.action_(Routine { |butt|
rrand(15, 45).do { |i|
update.value(butt, "%. there is still only 1 state".format(i + 2));
0.yield; // stop here
};
w.close;
});
w.front;
)
::
code::
// drawing in a window dynamcially with Pen
(
var w, much = 0.02, string, synth;
w = Window.new("swing", Rect(100, 100, 300, 500)).front;
w.view.background_(Color.new255(153, 255, 102).vary);
string = "swing ".dup(24).join;
w.drawFunc = Routine {
var i = 0;
var size = 40;
var func = { |i, j| sin(i * 0.07 + (j * 0.0023) + 1.5pi) * much + 1 };
var scale;
Pen.font = Font("Helvetica-Bold", 40);
loop {
i = i + 1;
string.do { |char, j|
scale = func.value(i, j).dup(6);
Pen.fillColor = Color.new255(0, 120, 120).vary;
Pen.matrix = scale * #[1, 0, 0, 1, 1, 0];
Pen.stringAtPoint(char.asString,
((size * (j % 9)) - 10) @ (size * (j div: 9))
);
};
0.yield // stop here, return something unimportant
}
};
fork { while { w.isClosed.not } { defer { w.refresh }; 0.04.wait; } };
w.front;
)
::
|