一个字符串被以一个数组的形式储存。数组最后一个项目是0。0,或终止,代表了字符串的结束。字符串可以是任何用引号包围的字符组合(字词或数字),比如”my 4 strings”。SC内部,将数组中每个字符以ascii整数形式存储。第一位是’m’ (176),第二位’y’ (121),第三位’ ‘ (空格或 32),第四位也是一个字符,不是数字,’4’ (111)。因为是数组的关系,它可以被含入do函数内。
23.1. 作为数组的字符串
"CharacterArray".at(0) // 尝试将索引数在 0 至 13 间改变 "CharacterArray".at(0).ascii "This is a string that is actually an array".do( {arg each, count; [count, each.ascii, each].postln; })
一个字符串数组是数组的数组或多维数组。数组[“one”, “two” “three”]由三个数组构成。第一个数组包含字符’o’, ‘n’, ‘e’, 和0。第二个数组是’t’, ‘w’, ‘o’, 和0。
数组懂数学。字符串,即使数组懂数学,但也不是你想象的那种。如果你想对一组音移调,你可能会尝试向字符串或字符串数组加一个数字(比如,”C#” + 5),如下例:
23.2. “C” + 5?
("C#" + 5) ( a = ["C#", "D", "Eb", "F", "G"]; a = a + 5; a.postln; )
这个例子能跑,但结果不是你想要的。如果我们需要程序生成数字,但列印字符串,该怎么做呢?可以通过将字符串数组用作参照(reference)来实现。
这是一个字符串数组,其中一行代码列印一个跟随一个随机选择的字符串之一,以及一个随机选择的do:
23.3. pitch array index
( a = ["C", "D", "E", "F", "G"]; a.at(3).postln; //post item at index position 3 in the array a ) ( a = ["C", "D", "E", "F", "G"]; a.at(5.rand).postln; ) ( a = ["C", "D", "E", "F", "G", "A", "B"]; //pitch class array "count\trandom\tpitch at index:".postln; //header 10.do( //do 10 items {arg item, count; //use arguments item and count var pick; pick = a.size.rand; count.post; "\t\t".post; //print the number of this iteration pick.post; "\t\t".post; //print the number I picked a.at(pick).postln; //print the item at that array position }) )
你可以用连结消息++保存postln消息。这用于同时输出两个字符串。因此”this ” ++ “string” 将变为 “this string”。另一个联合数据的方式是列印整个包含一组项目的数组,例如[count, b, a.at(b)].postln;。最后,你可以使用postf,它通过向一个字符串插入一串引数替代”%”字符来格式化一条列印消息。反复运行下例以确定它的确在选取不同的值。第二例是更简明版本。
23.4. 被连结的字符串
( a = ["C", "D", "E", "F", "G", "A", "B"]; 10.do( {arg item, count; var b; b = a.size.rand; ("Item " ++ count ++ " : " ++ b ++ " = " ++ a.at(b)).postln; // 或 // postf("Item % : % = %\n", count, b, a.at(b)) }) )
更简洁
do(10, { ["C", "D", "E", "F", "G", "A", "B"].wrapAt(100.rand).postln;})
现在我便能用12音旋律结合字符串数组了。
23.5. Every 12-tone row with pitch class strings
( //初始化 MIDI, 先运行它 var original, total, begin, next, art, pcstrings, count; original = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; pcstrings = ["C ", "C# ", "D ", "Eb ", "E ", "F ", "F# ", "G ", "Ab ", "A ", "Bb ", "B "]; total = 479001600; count = 0; begin = total.rand; next = 0.125; art = 0.6; ("Total playback time = " ++ (total*next/3600).asString ++ " hours.").postln; r = Task({ inf.do({ var thisVar; thisVar = original.permute(begin + count); thisVar.do({arg e; pcstrings.at(e).post}); "".postln; (thisVar + 60).do({arg note; m.noteOn(1, note, 100); thisThread.clock.sched(next*art, {m.noteOff(1, note, 100); nil}); next.wait }); count = count + 1; }) }) ) //然后这些 r.start; r.stop; 127.do({arg i; m.noteOff(1, i, 0)})
观点一刻
对我来说,计算机辅助创作的目标是机器为我做一切繁琐细节的部分,然后我继续干那些创造性的有趣的部分。下例的结果仅列印音高,而不是音乐。即使如此,我们可以停在这,并且它将在创作练习中很有帮助。这接近于40年前如何做这个事。创作者开始于你现在可以做的事:至少让计算机去处理数字。但占用了四页的代码或上百张穿孔卡片(punch card)。对SC来说,这只用一行。刚好有这样一个例子(第一个值在八分之一内循环,第二个是下一个距八分之一的时间,下一个是MIDI音,下一个是音量):
23.6. 伊利亚克组曲(Illiac suite)?
60.do({[8.rand, 8.rand, (rrand(36, 72)), 10.rand].postln;})
这比希勒(Hiller)50年代的程序先进一大截。但我仍无法将这些东西抄作手稿,然后让其他人演奏它。当今科技的好处是快速的回放。即使我最后的成品是打算为真正的音乐家所用,我仍可以用计算机先做尝试。我可以对比一分钟四次和数月四次的区别。
十年前,我们让CPU能够产生实际的声音,但却要花掉一整夜的时间。在2000年,那段时间被削减到5分钟,但却仍要用上几页代码和两个程序(一个压碎(crunch)数字,另一个生成声音)。今天,声音是实时生成的,而且仅需大约十行代码。
练习,随机学习
这有一个范例。基于几个简单点子的完整创作:三个乐器,一套事件数字,随机选择音高、持续时间、下一事件以及振幅。设置你的MIDI回放,这样通道1,2,3才能有三个不同的乐器。
我需要补充,这个实验并不那么有趣。但我们的目标是快速实验转向。所以听一会儿这个例子,然后操一下那些随机选项再听一会儿。改变持续时间、下一事件、音高选择等等的范围。在随机范围外(比如 duration = rrand(0.5, 2.0)),试试为选择数组设定一个随机索引(比如 duration = durationArray.at(10.rand))。在一个音高数组内尝试不同的scale。用频率替代MIDI值。相比一个无限的do,建立一系列为正式设计定义的do循环,改变每个的参数(20个长循环然后5个短循环,高音然后低音,等等)。
23.7. (倾向性) 随机学习
( a = Task({ inf.do({arg i; var note, dur, next, amp, inst; note = rrand(24, 84); dur = rrand(0.1, 0.5); amp = rrand(30, 127); next = rrand(0.1, 0.5); m.noteOn(1, note, amp); thisThread.clock.sched(dur, {m.noteOff(1, note); nil}); next.wait }) }); b = Task({ inf.do({arg i; var note, dur, next, amp, inst; note = rrand(24, 84); dur = rrand(0.1, 0.5); amp = rrand(30, 127); next = rrand(0.1, 0.5); m.noteOn(2, note, amp); thisThread.clock.sched(dur, {m.noteOff(2, note); nil}); next.wait }) }); c = Task({ inf.do({arg i; var note, dur, next, amp, inst; note = rrand(24, 84); dur = rrand(0.1, 0.5); amp = rrand(30, 127); next = rrand(0.1, 0.5); m.noteOn(3, note, amp); thisThread.clock.sched(dur, {m.noteOff(3, note); nil}); next.wait }) }); ) a.start; b.start; c.start; a.stop; 127.do({arg i; m.noteOff(1, i)}) b.stop; 127.do({arg i; m.noteOff(2, i)}) c.stop; 127.do({arg i; m.noteOff(3, i)})