在开始本课之前,请确保你已载入了上一课的\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))
)
