变量
现在我们回头看最开始的代码:
on note play_note(60,120,0,-1) end on
之前已经说过,给每个音符都配同一个伴奏音着实平平无奇。我们想编写一个更高级的伴奏器,能够生成力度相同的高八度伴奏音,因此便有了:
on note play_note($EVENT_NOTE + 12,$EVENT_VELOCITY,0,-1) end on
这个程序中我们没有指定某一固定的音符序号,而是写了$EVENT_NOTE + 12,也没有指定固定的力度,而是写了$EVENT_VELOCITY。其中的$EVENT_NOTE与$EVENT_VELOCITY便是所谓的内建变量,分别对应着输入音符的序号与力度。
脚本语言的核心就是变量。用计算机术语来说,变量就是命名了的存储空间,用于存放数据。稍后我们会讨论几种类型的变量,但马上我们要学习区分用户定义变量与内建变量。
以上两种变量有两种形式:
- 常规变量,使用美元符号标记(($my_variable或$EVENT_VELOCITY)或“at”符号标记(@my_text)。
- 阵列变量,使用百分符号标记(%my_array[]或%KEY_DOWN[<note-number>])或者感叹号标记(!my_text_array[])。
常规变量可以存储单一整数值或字符串。阵列变量与常规变量类似,但是可以存储多个数值或字符串。阵列是有索引的数组。若有一个表格,每一个索引x都指向地址y,这就是阵列的工作原理。阵列的元素数范围是1~512。
变量的声明
用户定义变量顾名思义需要事先声明。声明这一过程为变量注册一个名称,方便后续调用,同时为变量赋予一个值,将变量初始化。下面是一些变量的声明范例:
on init declare $first_variable declare $second_variable := 12 declare const $third_variable := 24 declare %first_array[4] declare %second_array[3] := (3,7,2) end on
- 第一行标记了初始化回调函数的开始(代码正确解析后,初始化回调函数立即执行)
- 第二行中的declare(声明)指令,声明了一个变量first_variable。但是代码并没有为此变量赋值,所以它的值默认为0。
- 第三行声明了一个变量second_variable,使用:=运算符赋值12。
- 第四行声明了一个特殊的常规变量:名为third_variable的常量。常量与常规变量基本类似,但常量的数值不可更改,并且有助于提高程序效能,因为运行时不需要求值(evaluate)。
- 第五行声明了一个阵列变量first_array,包含了四个元素,全都默认赋值为0。
- 第六行声明了另一个阵列变量second_array,包含了三个元素,分别赋值为3,7和2。
- 第七行标记了初始化回调函数的结尾。
从中不难看出变量的命名有几条规则:非阵列变量的开头都是$,而阵列变量的开头都是%。声明的语法如出一辙,范例中也有明显体现。声明常量时必须使用:=运算符赋值。
使用变量编程
请读者将下列代码粘贴进Kontakt,并在键盘上演奏音符:
on init declare $first_variable declare $second_variable := 12 declare const $third_variable := 24 declare %first_array[4] declare %second_array[3] := (3,7,2) end on on note play_note ($second_variable + 48,$third_variable + 96,... %first_array[2] + $first_variable,%second_array[0] - 4) end on
这段代码与最开始那段代码的作用完全一样,每个音符都会有一个力度为120的伴奏音,音高为C3。看起来似乎是小题大做,但本例的真真目的在于展示如何操纵变量,传达给读者一些最基本的理念。上例中的play_note()函数与play_note (60, 120, 0, -1)完全一致:
- $second_variable + 48=60
- $third_variable + 96=120
- %first_array[2]=0,因为2号元素的值为0
- %second_array[0] – 4等于-1,因为0号元素的值为3,3减去4是-1。
内建变量极为重要,功能强大,不可被定义,随写随用。我们来看两个重要的内建变量(完整内建变量列表请移步查阅书后附录)
$EVENT_NOTE | 触发回调函数的音符的序号 |
$EVENT_VELOCITY | 触发回调函数的音符的力度 |
一个音符触发了回调函数后,此MIDI事件的音符序号便存储进$EVENT_NOTE中,而$EVENT_VELOCITY则是力度。显而易见,这两个变量在初始化回调函数中都不会用到,因为音符不能触发初始化回调函数,何谈音符或力度!
MIDI音符的序号范围是0~127,对应的音名为C-2~G8。$EVENT_VELOCITY的范围是1~127。内建变量的名称都是大写字母,所以用户定义的变量最好使用小写字母命名。
下面的范例包含了这两种类型的内建变量。
on init declare $new_note end on on note $new_note := $EVENT_NOTE + 12 play_note($new_note,$EVENT_VELOCITY,0,-1) end on
与本书最开头的例子一样,它还是生成一个力度为120的伴奏音,音高为C3。我们声明了一个变量$new_note,由于没有进行赋值操作,值默认为0。用户演奏音符时,程序执行音符回调函数,$EVENT_NOTE+12便赋值给了$new_note(:=将其左边的变量值赋给右边的变量)。
下面这段代码可以说有了实质性进步:
on init declare %addNote[12] := (4, 6, 3, 6, 3, 4, 6, 4, 6, 3, 6, 3) declare $keyClass end on on note $keyClass := $EVENT_NOTE mod 12 play_note($EVENT_NOTE + %addNote[$keyClass],$EVENT_VELOCITY,0,-1) end on
现在再演奏,情况就完全不一样了。用户演奏的音符,都有C调的伴奏音。但是在分析代码之前,我们要先搞懂求余(modulo)运算符mod。求余运算符输出两数相除后的余数,例如14 mod 12的值为2,128 mod 10的值为8,等等。
本例中的求余运算符的用法相当常见:$EVENT_NOTE mod 12的结果确定了演奏音符音级(pitch class),所以不管是哪个音组的D音,$keyClass都会是2。
加入演奏音符时D3(62),以$keyClass作为索引序号,程序从%addNote[12]取出的值为3,因此伴奏音序号为62+3=65,即F3。
字符串变量
字符串变量有两种:
- 字符串变量,前缀为@
- 字符串阵列,前缀为!
下面是一个简单范例:
on init declare @text @text := "note played: " declare !list[12] !list[0] := "C" !list[1] := "C#" !list[2] := "D" !list[3] := "D#" !list[4] := "E" !list[5] := "F" !list[6] := "F#" !list[7] := "G" !list[8] := "G#" !list[9] := "A" !list[10] := "A#" !list[11] := "B" end on on note message(@text & !list[$EVENT_NOTE mod 12]) end on
变量固化
在前面的编程基础章节中,我们已经学习了make_persistent()指令:
make_persistent(<变量名>)
加载文件后记住变量值。
用户加载文件或脚本后,程序会执行初始化回调函数,并将固化变量设置为保存时的值。而且用户点击Apply按钮后,固化变量的值也会保存。比如用户加载了Arpeggiator(琶音器)后,操作控件对其节奏进行了修改,然后决定对代码进行修改,先前所作的修改是不会丢失的。
假如用户加载了一个延迟(delay)脚本,其中有变量$Time,用户修改变量后保存代码,再加载另一个脚本。无独有偶,这段脚本中也有一个变量名为$Time,用户是肯定不希望自己在前段代码中做的修改应用到后来代码中的。
脚本调用规则
五组代码的调用顺序是从前往后。键盘发出的MIDI信号传递给第一组代码,第一组代码发送MIDI信号给第二组,第二组再给第三组,以此类推。以一言蔽之,位次靠后的代码从靠前的代码中接受MIDI信号。
如果第一组脚本中某一事件被忽略,那此事件在后续代码中就不会出现了。假如第一组代码中生成了一个音符,第二组代码会收到此音符的序号,与在MIDI键盘上演奏的效果是一样的。
变量只能在自身所处的代码中生效,比如用户可以在不同代码中分别使用$tune_amount这个变量。因此在其他脚本之后插入别的代码不会引起错误,比如:
MIDI锁存器(Latch)→和声器(Harmonize)→琶音器(Arpeggiator)→调音器(Microtuning)
用户可以加载Kontakt 2库中的Ambient Harmonizat ion.nki,其位于:
Kontakt 2 Library/02 – KSP Instruments/06 – Harmonizer/Ambient Harmonization.nki。
- 第一段脚本截获输入的MIDI信号。
- 第二段脚本以特定速率演奏这些音符。
- 第三段脚本按照重复音符构造和弦。
- 第四段脚本将这些音符限定在特定音阶内。