在开始本课之前,请确保你已载入了上一课的\bleep SynthDef。
目前为止,调度一直由为一个特定的时间调度一个函数来达到。为了遍历一个程序不同的阶段,一个具备一定量执行阶段的函数是很有帮助的。通过.value,它并非一次性被全部评估,但能够在每个多阶段中“产出(yield)”它的当前值。
这是一个例程(routine):
( r=Routine({ 1.yield; 2.yield; 3.yield; }) ) r.value; //运行此行四次
//另一个更多东西一起走的例子 ( var r; r = Routine({ var x; x = 1.0.rand; 2.yield; x.yield; 1000.yield; x.yield; x = 1.0.rand; x.yield; }); 10.do({ r.value.postln }); )
// a routine can also have side effects ( r = Routine({ 1.yield; Synth(\bleep); 2.yield; 1.yield; Synth(\bleep); 1.yield; }); ) r.next; //顺便. r.next是一个同义词. r.value或r.next都返回"yield值". r.next; r.next; r.next;
// 现在,我们可以通过在一个确定的Clock上演奏例程来使用它
(
r = Routine({
0.5.yield;
Synth(\bleep);
1.yield;
0.5.yield;
Synth(\bleep, [\note, 43]);
0.5.yield;
});
SystemClock.sched(0, r);
)
但是,最好的做法常常是仅仅“演奏”例程,同时将它传递入Clock。
r.reset; // 将例程重置为其初始状态 r.play(SystemClock); r.reset; r.play(TempoClock(3));
Yield可以返回任何类型的对象。
然而,一个有意义的时期值需要是一个浮点数或一个整数。
为了清楚的表明我们使用的是一个相对时间,我们用wait替代yield(二者意味是一样的)
TempoClock.default.tempo_(1); //1 bps ( var r; r = Routine.new({ "I just began!".postln; 1.0.wait; "1 second later".postln; 2.0.wait; "finished".postln; }); r.play; //默认到TempoClock.default; )
( var r; r = Routine.new({ 16.do({ arg i; Synth(\bleep, [ \note, 36+(3*i) ]); 0.25.yield; // yield和wait意味着同样的东西 }); }); r.play; )
inf.do可以用来无限进行;但你你必须细心一些,以不会遗漏掉一些正时间的.wait命令。因为否则的话,循环将变得无限快并且SC将崩溃。
( var r; r = Routine.new({ inf.do({ arg i; Synth(\bleep, [ \note, 36+(3*(i%10)) ]); //added %10 to stop it going up forever 0.25.wait; //do not miss me out! }); }); r.play; )
一个任务即一个可以暂停和重新开始的例程。
( t = Task.new({ inf.do({ arg i; // 无限循环 (除非从外部停止) Synth(\bleep, [\note, 36+(2.3*(i%17))]); 0.25.wait; }); }); ) t.play(TempoClock(1.4)); //开启任务 t.pause; //暂停 t.resume; //继续走
有一个特殊的例程捷径也许会有帮助:
{}.fork
这将自动将你的函数转换为一个例程并演奏它;你将Clock作为fork的引数传递给fork。
{5.do{"hello".postln; 1.0.wait} }.fork(TempoClock(1))
( {16.do{arg i; Synth(\bleep, [\note,rrand(48,84) ,\amp, rrand(0.0,0.125)]); 0.125.wait} }.fork(TempoClock(2)) )