SC:电压控制,低频振荡器,包络,触发器,门限,倒数

早期的合成器模块被命名为类似VCO、VCA或者VCF这种形式。这些缩写代表控制音高的电压控制振荡器,控制振幅度的放大器,以及控制上层谐波结构的滤波器。这种协议有一点点古老了,但它确实是所有合成系统和SC patch的关键核心。控制简单的意味着改变一个参数。当你开大你音箱的音量时,你实际上是在控制其振幅的参数;当你用手指压住吉他的琴弦时,你实际上是在人为的控制琴弦的长度进而控制其频率。电子音乐先锋,其实是一群可以精准地、迅速地、灵活地运用电学控制这些声音参数的人。他们意识到人类以及很多乐器固有的局限性(我们能唱多高的调,我们能演奏多快,一架钢琴的声音能多响)可以用电学生成和控制的声音来超越。他们是正确的,在今天,运用机器生成音乐几乎没有任何限制。实际上,它们能够轻易地超越我们的能力到我们可感知的层面。

电压控制是一个UGen被用于控制另一个UGen的参数或引数的地方。在SC中,几乎所有对象都可以被“电压控制”。某些模块允许你控制一个方面,但绝大部分的组件都允许你控制所有三个参数:频率、滤波或振幅,因此,他们实际上是VCO、VCA和VCF的合体。接下来的例子将集中于音调,或VCO。

在之前的例子中,我们已经见过关于VCO,VCA和VCF的patch,但我们并未这样称呼它们。现在我们知道它们是什么了,让我们看三个例子。在之前的章节中,我们已经见识过经过正确缩放的 LFNoise0 可以生成适于音调使用的一个数字范围。同样地,我可以将它缩放和偏移到适用于振幅和音色的数字。下边第一例展示了原始的用 LFNoise0 控制频率的patch。接下来,同样的 LFNoise0 被缩放和偏移0.5,得到0到1之间的值。然后这个ugen没有被放到频率的口,而是被放到mul,因此此时它控制的是振幅。最后,同样的 LFNoise0 被放到了 Blip 中 knumharmonics 的正确位置。这个引数影响和声(harmonic)的数量,即音色。和声的有效范围是0到20,因此 LFNoise0 被偏移和缩放到生成2到18间的值。

12.1. VCO, VCA, VCF

{Blip.ar(LFNoise0.kr(10, 500, 700), 7, 0.9)}.play
{Blip.ar(300, 7, LFNoise0.kr(10, 0.5, 0.5))}.play
{Blip.ar(300, LFNoise0.kr(10, 8, 10), 0.9)}.play

一个最简单到概念化的控制是 Line.kr。引数为 开始点、结束点和持续。下例展示了一个静态值的正弦波振荡器Ugen,然后是同样的patch,只不过用 Line.kr ugen 替代了 静态的 freq 和 mul 引数。注意起始值都适于每个控制:0到1给振幅,200到2000给频率。最后一例缩短了 Line,因此它听上去像一个包络,接下来将它。

12.2. Line.kr

{SinOsc.ar(freq: 400, mul: 0.7)}.play
{SinOsc.ar(freq: Line.kr(200, 2000, 10), mul: Line.kr(0.9, 0.2, 10))}.play
{SinOsc.ar(freq: Line.kr(800, 400, 0.5), mul: Line.kr(0.9, 0, 0.5))}.play

颤音

一个对于电压控制的简单应用便是颤音。颤音是在振幅(在人声的情况下)和音调(在弦乐的情况下)上的轻微振荡(更方便些,因为我们有振荡器)。弦乐师在颤音间来回卷动手指。实际的音调在,比如说 435 和 445 间运动,本质上每秒5次在曲调(tune)内进出。在之前某些 SinOsc patch中,频率引数为一个固定值(比如440)。要做颤音,我们需要用某些函数或ugen替代那个静态值,并让它始终在435和445间平滑变动。颤音的形状很像一条正弦波:它以一个周期和可预见的比率在两个值间来回移动。我们可否将一条缓慢移动的 SinOsc 经过缩放和位移后用作另一个 SinOsc 的输入或 freq 引数?画一条0为中心1为顶-1为底的正弦波。将0改到440,1改到445,-1改到435。你能否计算出要做多少的缩放和位移才能达到那些值?

答案是缩放5并位移440。记住对于 +/-1 的ugen来说,位移是中心值,缩放是差值。因此440是中心值,5是差值。

这里还有另一个值需要考虑,颤音的速度。什么决定颤音的速度?控制源 SinOsc 的频率。即435到445间移动的频率。大约每秒5次会创造一个自然的颤音。因此颤音 SinOsc 的频率应为5Hz。patch的颤音环节于是应看起来像这样。为清晰,我使用关键字分配。

12.3. 做颤音的 SinOsc

SinOsc.ar(freq: 5, mul: 5, add: 440)

在下边的patch中,频率为5的 Sinosc 被存储至变量“vibrato”。如果我们直接听这个patch中的颤音部分,不会听到任何声音,因为5并不是一个我们可以听到的频率,450 对硬件来说则是一个过高的可被转化为声音的值。但将它放入另一个 SinOsc 的频率引数位的时候,外部的ugen便可以制造我们能听到的音频。

12.4. 颤音

(
{
     var vibrato;
     vibrato = SinOsc.ar(freq: 5, mul: 5, add: 440);
     SinOsc.ar(vibrato, mul: 0.5)
}.play
)

为上例颤音控制的 SinOsc 改变各种参数,然后观察它们对声音的影响。哪个值影响颤音的速度?哪个值影响深度?这是一个相当可信的颤音,如果再天然、加工或平一些的话:就像很多早期的电子音乐。真正的音乐家并不直接创制颤音:他们常常从一个纯粹的音源开始,然后逐渐增加它。为创制一个更自然的颤音,你想改变哪个值?你想运用哪个值?

方框图(Block Diagrams)

方框图用于描绘一个patch中组件的路由和连接。现在在用的有若干形式,但全都使用有输入、输出的盒子,然后使用线和箭头指示它们之间如何彼此相连。它们被用于早期电子patch以协助可视化电子部件的连接。我常感觉应该引入它们,但每次开始写方框图环节的时候,我都会怀疑是否有这种必要,因为基于代码的合成和图表一样易读。以一个简单的正弦振荡器为例。它可能被图解为下边第一例。如果扩充到涵盖 SinOsc 的所有引数并增加一个控制振荡器,它将被图释为第二例。这可能更容易看,但如果你把包围的代码想做盒子(如第三例),并不是那么难。

方框图
方框图

但为了以视觉为导向,我将在本章内增加一些图释。

泰勒明电子琴

泰勒明电子琴
泰勒明电子琴

泰勒明电子琴是第一件电子乐器。它发出的声音如此“外星”因此常被用于科幻电影原声。我将它放在这里作为颤音的应用,因为对于这件乐器,我们一致认同的两个特性是音调(连续非离散的音)间的滑音和稳固宽广的颤音。颤音并非电压控制的结果,而是由演奏者的手产生。也许他们发现需要为单纯的塑料正弦波之声添加一些维度作为补偿吧。

下边是一个将鼠标位置连接到控制的虚拟泰勒明:上下控制音调,左右控制振幅。颤音由一个电压控制振荡器或VCO激活。你几乎能够亲眼目睹宇宙飞船着陆的情景。

12.5. 泰勒明电子琴

(
{
var vibrato;
vibrato = SinOsc.kr(6, mul: 0.02, add: 1);
     SinOsc.ar(
           freq: MouseY.kr(3200, 200, lag: 0.5, warp: 1) *
           vibrato, //颤音
           mul: abs(MouseX.kr(0.02, 1)) //振幅
     )
}.play
)

控制 SinOsc 的 mul 引数 将改变颤音的深度。如果它被设置到0,颤音将消失,如果设置到5,它便位于一个好的深度。但所有值都是静态的。它们并不像自然颤音那样随时间改变。为使颤音深度随时间改变,我们需要用另外的时间变化控制替换静态值5。

有无数的Ugen能做这件事。目前我们可以使用 Line.kr,(同样再看 XLine.kr)。这便是它如何适用于patch的例子(这次有变量):

12.6. 更好的颤音

(
//颤音
{
var depthChange, vibrato;
depthChange = Line.kr(0, 5, 3);
vibrato = SinOsc.ar(freq: 5, mul: depthChange, add: 440);
SinOsc.ar(
      vibrato,
      mul: 0.5)
}.play
)

持续调整颤音的速度(频率)而不是深度是否会更自然?你想要如何改变以达到那样的一个效果?你能都控制吗?你是否还可以使用 Line 或另一个 SinOsc 控制整体的音调(例如,add 值),在比如说,300到900间移动?

对于那些古旧patch合成器的用家来说,SC所能提供的无限量的组合将会是我们感到一点点头晕眼花。即使在现代的合成器上,你很少能够找到多于两三个的 Line 式 Ugen 来创造一个效果。SC,仅受处理器能力限制而不受Ugen的限制。我可以用 Line Ugen 替代这个patch内的所有静态值。然后我仍可以用而外的 Line 替代那些 Ugen 里的值,如此嵌套,直到成千上万的 Line。不信?看这里:

颤音 SinOscLine.kr 均为传统意义上的 “电压”控制。即它们控制着另外ugen的参数(频率)。我们尝试重制一个自然的声响颤音。小提琴很少超过每秒10次的速度或大于40的深度。但对于合成来说,超越这些限制很简单,甚至很诱人。试着增大 nul 和 freq 引数到超越你能想象的自然颤音:33,100,1200等。试着将add改到其他音调。试着用其他控制替代add:SinOscLine。(仔细计算每个位移和缩放确保不出现负值)。由这些过大的值生成的声音对于合成及电子时代来说是唯一的。我确信你可能会习惯于它们的声音,因为他们如此普遍(早期狂想家创作者的遗嘱),但想像你是第一次听到它们。

在两个 SinOsc 对象中,一个生成我们听到的频率。另一个生成音频范围以下的频率(例如 10Hz, 5Hz)。我们并未实际听到声音,但我们确实听到了它的影响。一些合成器使用属于LFO或低频控制以形容这样一种效果。几乎SC里的任何振荡器都可以这样做。这里是使用不同波形的若干例子。为每个LFO进行频率、缩放和位移的实验。确保位移(add)大于缩放(mul)。如果缩放较大,你将得到负值。那些控制的频率不要超过20Hz(以避免你错过一些有意思的事情)。我们稍后详述。

12.7. 其他LFO控制

(
{ //SinOsc (科幻)
var lfo;
lfo = SinOsc.ar(freq: 10, mul: 100, add: 400);
SinOsc.ar(lfo, mul: 0.5)
}.play
)
(
{ //脉冲 (电话铃音)
var lfo;
lfo = LFPulse.ar(freq: 15, mul: 200, add: 1000);
SinOsc.ar(lfo, mul: 0.5)
}.play
)
(
{ //锯齿波
var lfo;
lfo = LFSaw.ar(freq: 2, mul: -100, add: 600);
SinOsc.ar(lfo, mul: 0.5)
}.play
)
(
{ //噪音 (电脑)
var lfo;
lfo = LFNoise0.ar(freq: [28, 27], mul: 1000, add: 2000);
SinOsc.ar(lfo, mul: 0.5)
}.play
)
(
{ //噪音 (躁狂的鸟)
var lfo;
lfo = LFNoise1.ar(freq: [28, 27], mul: 400, add: 2000);
SinOsc.ar(lfo, mul: 0.5)
}.play
)

包络

之前patch里的Line是包络的一种。包络描述始终处于变化的一个单一事件(与周期性重复事件截然相反)。它们通常被用于控制振幅(VCA),但它们可被应用于声音的任何方面,比如频率,或者像上边最后一个patch里的,颤音的深度。

SC里的包络有额外的功能:当它结束后,可以让伺服器停止演奏那个ugen并释放CPU。没有这一特性,作品中的事件将不断累加并最终令CPU过载。

我们都知道一个音乐事件的振幅是始终处于减弱的(这叫做衰减),但同时还有一个参数叫做attack(音头强度),它是一个声音的开始。马林巴琴有一个强烈的音头。风琴有更柔软或长的音头。小提琴依据用弓方式可能有较短或很长的持续音头。钢琴有强烈的音头,但没有木质打击乐器强烈。差异也许只有几微秒,但辨别音头时间极小的变化是很简单的。所有合成器的所有预设均使用包络以描述持续的音量改变。

包络有两种。固定仿真时间(fixed duration)包络和持续(sustain)包括。固定仿真时间包络将始终有相同的持续时间。固定包络常常有一个音头和一个释放(release)。持续包络的持续时间将基于门限(gate)的长度改变,比如说,一个键按下的时间长短。打击乐器,诸如镲、马林巴、钟琴等具有固定仿真时间包络。一旦它们被调动后,你无法控制衰减的周期。风琴、人生、小提琴、铜管乐器等具有持续包络,在键释放或音乐家停止吹奏或拉弓前,每个音符都会持续发声。在那种情况下,音调消失。

最普遍的用以形容包络的术语是 音头强度(attack),衰减(decay),持续(sustain)和释放(release),或者缩写为ADSR。简单的包络可能仅含 AR。下边的图示是attack和release的包络线。

包络线
包络线

SC使用 EnvGenEnv 创制包络。Env 描述包络线的形状,EnvGen 当被提供 触发器(trigger) 和 门限(gate) 时生成那个形状。

触发器,门限,消息,ar(音频率)和 kr(控制率)

当你按下家里买回来的合成器的按键时,第一个送至处理器的信息即开始那个事件的触发器。下边是两个触发器的例子。第一个是一个周期触发器,第二个是随机触发器。前两例并未使用触发器来激活包络。相反它们只是演奏触发器,这听起来将像是爆破声(并非用来做音频输出)。它们都有一个引数。对 Impulse 来说,它是每秒的脉冲数。对 Dust 来说,是平均脉冲密度。

之前我们看到 SinOsc 中的 mul 引数 对应的是音量。EnvGenEnv 可以与触发器合用以提供用于 SinOsc 的 mul 或振幅的包络,就像下边第三、四例。

12.8. 触发器和包络

{Impulse.ar(4, mul: 0.5)}.play
{Dust.ar(5)}.play
(
{
     SinOsc.ar(
           440,
           mul: EnvGen.kr(Env.perc(0.001, 1.0), Impulse.kr(2))
     )
}.play
)
(
{
     SinOsc.ar(
           440,
           mul: EnvGen.kr(Env.perc(0.001, 0.3), Dust.kr(2))
     )
}.play
)

试着改变 ImpulseDust 的引数。Env.perc 的引数是音头强度和衰减。试着改变两者。注意,在下一个触发器开始后,当前事件的衰减将在结束前中断。

SinOsc 使用 ar 消息 而 DustImpulse 使用 kr。ar 代表音频率(audio rate),kr 代表控制率(control rate)。SinOsc 生成实际的音频,因此它需要 44.1k 的采样率。但控制并不被听作音频,而是形状或其他ugen的触发器,无需如此高的分辨率并且如果以一个更低的比率生成将更高效。SinOsc.kr 将生成 SinOsc.ar 1/64th 的正弦波。

任何正值都将被 EnvGen 解释为一个触发器,如下例。SinOsc 重复从正值向负值移动。当 SinOsc 从负值向正值移动时,EnvGen被触发。在第二例中,MouseX被用于映射荧幕X轴到-0.1到0.1之间的值(荧幕中部是0)。当鼠标跨过荧幕中部,一个触发便被送到EnvGen。下一个例子展示了使用 MouseButton 同样的实现。当鼠标被点击,便送出一个触发。最后一例展示了用键盘按键的控制 KeyState。对应按键的数字有点易混淆,并且不值得在此处解释。用下边的表就好。

12.9. 用MouseX做触发

(
{
     SinOsc.ar(
          440,
          mul: EnvGen.kr(Env.perc(0.001, 0.3), SinOsc.ar(2))
     )
}.play
)
(
{
     SinOsc.ar(
          440,
          mul: EnvGen.kr(Env.perc(0.001, 0.3), MouseX.kr(-0.1, 0.1))
     )
}.play
)
(
{
     SinOsc.ar(
          440,
          mul: EnvGen.kr(Env.perc(0.001, 2), MouseButton.kr(0, 1))
     )
}.play
)
(
{
     SinOsc.ar( // 按下 "j" 来激活这个包络
          440,
          mul: EnvGen.kr(Env.perc(0.001, 2), KeyState.kr(38, 0, 1))
     )
}.play
)

// 键盘对应数字一览表:
1-18; 2-19; 3-20; 4-21; 5-23; 6-22; 7-26; 8-28; 0-29; --27; =-24
q-12; w-13; e-14; r-15; t-17; y-16; u-32; i-34; o-31; p-35
a-0; s-1; d-2; f-3; g-5; h-4; j-38; k-40; l-37; ;-41; '-39
z-6; x-7; c-8; v-9; b-11; n-45; m-46; ,- 43; .-47; /-44;

ugen TRand 响应一个触发器,在给定的范围内返回一个随机值。它可被用作SinOsc频率引数的控制源。

下例使用一个变量连接起了若干引数。每次trigger(触发),TRand都会生成一个新的音。同时也会生成一个新的包络,因此它们是一致的。同样地,衰减率(Env.perc的第二引数)与触发速度是成比例的,本例中为1/2。于是每个被触发的事件将在另一个开始前衰减。如果衰减比触发的周期长,事件将彼此重叠。这是真实世界中每天都在上演的,但我调整了此点,以描述一个变量的应用。

试着改变触发速度和音头强度。注意当触发较慢时,衰减更长。你想怎样把音头持续时间和触发时间连接在一起呢?你能将它们分配到两个轨道里去吗?左声道是更快比率(rate)的较低音符,右声道是更慢比率的较高音符?

12.10. 触发器和包络

(
{
var triggerSpeed, trigger;

triggerSpeed = 8;
trigger = Impulse.kr(triggerSpeed);

SinOsc.ar(
      TRand.kr(100, 2000, trigger),
      mul: EnvGen.kr(
            Env.perc(0.001, 1/triggerSpeed),
            trigger
       )
)
}.play
)

持续和频率

一个事件或一系列事件可能被描述为持续时间(每个事件持续几秒)或频率(给定周期内的事件数量)。上面那个patch中,触发速度是频率,也就是说,它被表述为每秒事件发生的量。Env.perc的衰减引数是持续时间,即每个事件持续的时间。但在这个patch内,两者被连在了一起:持续时间必须与每次触发一样长,并且每个新的触发必须发生在每个时序时间的结尾。弄明白它们的关系很重要,如何表述慢于每秒一次的频率或短于一秒的持续时间,以及如何将一个转化为另一个。首先,我们需要明白如何将它们两者都表述为比率(ratio)。

就频率来说,事件的数量是分子,时间(通常是秒)是分母。比如说,40Hz 是 1秒内40个事件,或40/1。当一个频率低于每秒一次,你可以将之表述为小鼠,比如0.1。但你也可以继续沿用分子和分母的方法,增大分母(时间)而不是减少事件的量。比如说,2Hz 是每秒2个事件,或2/1。1Hz是每秒1个事件,或1/1。5秒一个事件是1/5,因此是0.2Hz(每秒0.2个事件)。

对持续时间来说,时间是分子,而事件多少是分母。一个持续5秒的事件可以被表述为五秒一个事件,或5/1。当持续时间低于1秒时你也可以如法炮制:5/1 是5秒一个事件,1/1是1秒一个事件,1/2 是一秒两个事件,每个事件持续1/2秒(0.5)。

持续时间和频率于是就变为了倒数。在两者之间转换只需简单的交换分子与分母。以频率为40Hz为例。每个波的持续时间是多久?1秒内有40个波,或40/1。每个波的持续时间是其倒数:1/40(1秒40个事件)。因此每个独立事件的持续时间为1/40秒。

要转换持续时间到频率,如法炮制。

由于SC懂得小数和分数标记法,我的学生们发现始终使用分数并让SC去做数学是更简单的。因此,相比设置LFO频率到0.1,不如直接输入1/10(10秒一个事件)。3/5 是5秒三个事件,35/41 是41秒35个事件。如果分母是1(15/1 或1秒15个事件),丢掉1直接用15。

同步化 LFO控制

将频率和持续时间表述为分数促进了LFO频率的同步:在古早合成器上很难实现的东西。设想,比如说,一个和弦的六个音,每个都有一个触发、音头、衰减和持续时间。如果第一个触发率为2/1(每秒2个事件),另一个是3/1,然后4/1,5/1等等,然后它们都将被每秒同步一次。同样地,3/15, 4/15, 7/15, 11/15的触发器等等,将被每15秒同步一次。

对频率来说也是如此。如果10个SinOsc ugen有相关的被表述为比率的频率,它们将在分母表述的周期内同步。这句话太晦涩,所以这里是一个例子。5个SinOsc,频率分别为4/10, 3/10, 6/10, 7/10, 将每10秒回到同步状态一次。

12.11. 同步化的LFO和触发器

(
{
SinOsc.ar(SinOsc.ar(4/10, mul: 100, add: 1000), mul: 0.1) +
SinOsc.ar(SinOsc.ar(2/10, mul: 100, add: 1000), mul: 0.1) +
SinOsc.ar(SinOsc.ar(5/10, mul: 100, add: 1000), mul: 0.1)
}.play
)
(
{
var scale = 300, offset = 500;
SinOsc.ar(SinOsc.ar(4/3, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(7/3, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(2/3, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(8/3, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(6/3, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(5/3, mul: scale, add: offset), mul: 0.1)
}.play
)
(
{
var scale = 600, offset = 1000, synch = 10;
SinOsc.ar(SinOsc.ar(4/synch, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(7/synch, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(2/synch, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(8/synch, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(6/synch, mul: scale, add: offset), mul: 0.1) +
SinOsc.ar(SinOsc.ar(5/synch, mul: scale, add: offset), mul: 0.1)
}.play
)
( // 同步化的触发器
{
var synch = 5;
SinOsc.ar(100, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(3/synch))) +
SinOsc.ar(300, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(7/synch))) +
SinOsc.ar(500, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(5/synch))) +
SinOsc.ar(700, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(2/synch))) +
SinOsc.ar(900, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(9/synch))) +
SinOsc.ar(1100, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(6/synch))) +
SinOsc.ar(1300, mul: EnvGen.kr(Env.perc(0, 1), Impulse.kr(1/synch))) * 0.1
}.play
)

频率和持续时间连接

在一个patch内连接持续时间(例如音头和衰减)与频率,我们需要在开始编程前便为二者赋予变量。

12.12. 持续时间、音头强度、衰减

var dur, att, dec, trigFreq;

dur = 10; // 持续10秒
att = dur*0.1;
dec = dur*0.9;
trigFreq = 1/dur;

freq = 10; // 每秒10次
att = 1/freq*0.1;
dec = 1/freq*0.9;
duration = 1/feq;

门限(Gates)

一个门限就是一个触发器和持续时间(触发器是持续时间为0的门限)。触发器被用于固定长度的包络。门限可以被用于固定或持续的包络,但对持续包络是必需的。门限的持续时间(duration)定义持续时间(sustain time)。

管风琴便是门限包络的一个很好的例子。“门限”被按、持续按和释放按键定义。当你按键时,管子发声。如果持续按住的话,它可能会衰减一点点,但会最终达到一个持续水平。只要你的手指不放开,它就会一直保持在那个水平上。当你释放按键时,一段时间之后声音将完全消失。

Env.adsr 是一个门限包络。它在门限持续时间内始终处于“开启”状态。引数即音头(attack),衰减(decay),持续(sustain)和释放(release)。音头是最初升至1.0的长度。它与门限同时开始。衰减是其跌落至持续阶段的时间长短。它在门限的中部。注意持续(sustain)不是持续时间的长度,而是在门限关闭前持续部分的水平。释放是门限关闭后从持续水平回到0的时间长短。以下为一个 adsr 的图释。

门限 plot
门限 plot

MouseX 可以被用作触发器,就像上边这样,但它同时也可以作门限。在 MouseX 为正值的范围内,门将始终“打开”。当门开放的时候,Env.adsr 将有音头(attack),然后衰减(decay)到维持在 持续(sustain) 的水平上,直到门限关闭。当 MouseX 小于0之后,门限关闭,并释放Env.adsr。

尝试改变Env.adsr的值。

第二例中,将 EncGenEnv 作为 SinOsc 的频率引数(用适当改变后的 缩放 和 偏移)。因为你更容易听到音高的改变,因此你能更好的听到包络的形状。

12.13. 用门限做包络

(
{
    SinOsc.ar(440,
         mul: EnvGen.kr(
                 //包络是音头强度,衰减,持续水平,释放
                Env.adsr(0.001, 0.3, 0.2, 0.1),
                MouseX.kr(-0.1, 0.1) //门限
         )
     )
}.play
)
(
{
     SinOsc.ar(
           400 + EnvGen.kr(
                  Env.adsr(0.3, 0.4, 0.2, 1),
                  MouseX.kr(-0.1, 0.1),
                  1000
           ),
          mul: 0.5
)
}.play
)

最后,LFNoise0 是一个在 -1与1间徘徊的随机音源。它可被用于提供一个随机的门限(随机的触发器和随机持续时间)。当它是正值的时候,门限打开;当它小于0,门限关闭。

12.14. 用LFNoise做门限的包络

(
{
     SinOsc.ar(
           LFNoise0.kr(13, 1000, 1200),
           mul: EnvGen.kr(
                 //包络是音头强度,衰减,持续水平,释放
                 Env.adsr(0.001, 0.3, 0.2, 0.1),
                 LFNoise0.kr(3) //门限
            )
      )
}.play
)

即使对于最复杂的合成器来说,你都很难找到多于三个或四个转折点(包络线改变方向的点)的包络。真乐器能在振幅上最更复杂的变化。SC用“new”消息允许做到这么复杂的水平。前两个引数为数组。第一个数组包含每个转折点的水平,第二个数组包含每个转折点间的持续时间。注意,水平数组中必须比时间数组多一个值。下边这个patch用一个复杂的包络控制频率。EnvGen创造0到1的值,因此它最后被*和+缩放并偏移。后边的图示展示了包络的形状。

12.15. 复杂的包络

(
{
SinOsc.ar(
      EnvGen.kr(Env.new(
            [ 0, 0.5, 0, 0.3, 0.3, 0, 0.1, 0, 0.2, 0.3, 0.15, 0.5, 0.25 ],
            [1, 0.5, 0.5, 2, 0.7, 1, 0.3, 0.6, 0.5, 0.8, 0, 0.4])
      ) * 1000 + 200
)
}.play
)
// 这将绘制数组
[ 0, 0.5, 0, 0.3, 0.3, 0, 0.1, 0, 0.2, 0.3, 0.15, 0.5, 0.25 ].plot
复杂的包络线
复杂的包络线

最后,下边是各种铃铛实验,每个事件的音高或者说频率从一个范围选取。对于绝大多数乐器的一个事件来说,高频持续时间短,低频持续时间长。这个patch展示了如何用一个变量将每个事件的持续时间与频率挂钩。有几个ugen你之前从没见过,读读帮助文档就好。

实验程序

现在你已经具备了实验探索的基本工具。用SC做合成即搞懂patch内ugen的参数(引数),并用不同种类的控制进行实验。所有的合成都可以被分解为控制声音的三个元素:频率,振幅,音色,这令我感到可靠而安心。

本教程最后几章顺序展示了我实验进程中的概念:我通常始于一个超简单的patch,可能仅一个振荡器。然后加入两三个相互连接的控制。用MouseX替代现存的静态值,以尝试一个范围的值。随后开始思考还有没有其他的控制源可以使用以自动走过那些值。我为偏移和缩放做计算,然后把它放到patch里。最后一步是用复制、立体声扩张、随机值和混合(mixing)等手段让patch变得更浓厚。请随意用这个教程中以及帮助、示范文档中的例子按上边的方法照做。

练习,铃铛

12.16 铃

(
//连接频率与包络长度
  //高音短, 低音长
{
var frequency;
Mix.ar(
       {
           frequency = rrand(100, 5000);
           Pan2.ar(
	        SinOsc.ar(
	            frequency,
	            mul: EnvGen.kr(
	                 Env.perc(0.001, 500/frequency), 
	                 Dust.kr(0.05),
                         0.2
	            )
	        ), 
	        rrand(-1.0, 1.0)
             ) 
        }.dup(100)
)
}.play
)
(
//连接频率与衰减长度
  //基本上是同样的代码但更紧凑
  //低音短,高音长
{var frequency;
Mix.ar({
          frequency = rrand(100, 3000);
          Pan2.ar(SinOsc.ar(frequency,
	     mul: EnvGen.kr(Env.perc(0.001, frequency/1000), 
	         Dust.kr(0.05), 0.2)), rrand(-1.0, 1.0)) }.dup(100))}.play
)
(//高音短, 低音长
{var frequency;
Mix.ar({
          frequency = rrand(100, 3000);
          Pan2.ar(SinOsc.ar(frequency,
	     mul: EnvGen.kr(Env.perc(200/frequency, 0.0001), 
	         Dust.kr(0.05), 0.2)), rrand(-1.0, 1.0)) }.dup(100))}.play
)
(//低音短, 高音长
{var frequency;
Mix.ar({
           frequency = rrand(100, 1000);
           Pan2.ar(SinOsc.ar(frequency,
	      mul: EnvGen.kr(Env.perc(frequency/500, 0001), 
	          Dust.kr(0.05), 0.05)), rrand(-1.0, 1.0)) }.dup(100))}.play
)
Be Sociable, Share!

Published by

ww1way

http://about.me/ww1way

Leave a Reply

Your email address will not be published. Required fields are marked *