SC:关键字分配,鼠标控制、线性及指数值

关键字分配

在之前的例子中,我们为每条消息包括了绝大部分的引数。如果某些引数没有提供,将使用其默认值。下边的 SinOsc 没有引数,但照样能跑。它用默认值:440,0,1和0,分别代表频率、相位、mul和add。

10.1. 默认值

{SinOsc.ar}.play

你可能也同时注意到,在之前的patch中我们为相位的引数输入了0,尽管它的默认值就是0。那是因为引数必须被以正确的顺序输入。相位是第二引数,而mul是第三个。如果要设置mul的话,那我们就必须为相位输入0,作为一个类似位置标记东西。

如果有10个引数而你只想改变其中第六个的话,这就是个问题。你必须输入前五个引数,这样的话,第六个引数才能位于正确的位置上。这不仅累赘,更会产生错误。

相应的解决方法便是关键字分配。使用关键字可以让你清晰的表明你需要更改的引数。关键字可以在文档文件中找到。SinOsc.ar 的文档中显示:SinOsc.ar(freq, phase, mul, add)。关键字即 freq, phase, mul, 和 add。使用关键字不仅允许你不按顺序输入一个引数,还能混合引数的顺序。这里是用关键字写 SinOsc 的不同版本。它们全部都完全表示同样的意思。

10.2. 关键字

{SinOsc.ar(freq: 440, phase: 0, mul: 0.4, add: 0)}.play;
{SinOsc.ar(phase: 0, freq: 440, add: 0, mul: 0.4)}.play;
{SinOsc.ar(freq: 440, mul: 0.4)}.play;

另一个使用关键字的原因是清晰。关键字提供了对于每个引数意义的阐述,就像简短的注释。

最后一个使用关键字的原因是可移植性。早先我说认识到引数列不可互换是很重要的。设想下边两行代码:Saw.ar(100, 500, 600) 和 SinOsc.ar(100, 500, 600)。Saw 的引数是freq、mul和add。但对 SinOsc 来说则是freq、phase、mul和add。因此两个引数列是不可互换的。我当然可以将100, 500, 和 600用作SinOsc的引数,但它们的意思与用在Saw里是完全不同的。500的相位没有意义,没有add的600 mul值生成负值,可能并非你想要的。

但如我使用关键字(假设两个Ugen有同样的关键字),于是我便能交换对象:SinOsc.ar(freq: 100, mul: 500, add: 1000) 和 Saw.ar(freq: 100, mul: 500, add: 1000) 在一个patch内将会有同样的效果。警告:这既可以是危险的也可以是意外的珍宝。你应始终仔细检查关键字并弄懂它们在一个patch里的意思。

这是第一个使用关键字伴随危险交换(dangerous swapping)的patch。

10.3. 使用关键字第一例

{SinOsc.ar(freq: LFNoise0.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{Saw.ar(freq: LFNoise0.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{SinOsc.ar(freq: LFNoise1.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{Pulse.ar(freq: LFNoise1.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{Pulse.ar(freq: LFSaw.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{LFTri.ar(freq: LFPulse.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play
{LFTri.ar(freq: LFTri.ar(freq: [10, 15], mul: 400, add: 800), mul: 0.3)}.play

MouseX.kr 和 MouseY.kr

上例中,我们改变每个值,运行代码,然后改变值再运行。你可能会希望能搞一个类似旋钮的东西,这样你便可以一次尝试一定范围内的值。这便是 MouseX.krMouseY.kr 将要做的事情。它们将鼠标运动和位置作为值传递给patch使用。前三个引数是minval, maxval, warp。

这些UGen可以将任何静态值替换为与鼠标位置对应的一系列值。作为说明,尝试下例,我用 MouseX 替换了 LFNoise0 的第一引数。

10.4. MouseX

{SinOsc.ar(LFNoise0.ar(MouseX.kr(1, 50), 500, 600), mul: 0.5)}.play;

比改了试、试了改简单许多吧?

下例中,MouseY(Y轴是从上到下)被用来控制振幅。minival是0.9,maxval是0.0。这看起来整倒了,但鼠标最小值的位置实际上是荧幕顶端,最大值是底部,我觉得顶端是0.9更容易理解。另一例加入了MouseX控制频率。最小值是A220,最大值高两个八度,或880。由于鼠标的动作横跨两个八度,你可能可以用它来演奏一小段曲子。首先尝试一个八度,然后五度,然后一个音阶。试试你能否演奏一个简单的曲子,比如一闪一闪亮晶晶,满天都是小星星(译者注:原文这里的曲子为“Mary Had a Little Lamb”)。

10.5. 鼠标控制

{SinOsc.ar(440, mul: MouseY.kr(0.9, 0))}.play;
{SinOsc.ar(MouseX.kr(220, 880), mul: 0.3)}.play;

一个难以演奏成曲的原因是,音与音之间的距离并非总是始终如一的跨过荧幕。因为存在两个八度,你可能会认为第一个八度位于荧幕的中部,但实际上你会发现它大概位于1/3的地方,然后第二个八度跨过剩下的2/3。(注意在小提琴、中提琴长号等上边也存在同样的分布,只不过是相反的方向。即,随着音高的上升,相似音程间距在指板上的距离在逐渐减小。)

真相是,距离是始终如一的:它们是线性的。问题在于我们对音的感知。我们以八度来听音乐,而八度是指数,非线性的。低八度(比如110到220)与更高的八度(1760到3520:1760的差值)相比,其间改变的总量更小(110)。在220到880的线性范围内,荧幕的中央应为550。但如果我们欲用荧幕来表述音阶,中央应为440,左半部分用以表述220到440之间(220的差值)的音,右半部分应给440到880(440的差值)。这个更合乎逻辑的跟踪(tracking)可由warp参数来应用。warp值由一个符号(单引号中的词)设置,可以是linear(线性的)或 exponential(指数的)。你也可以输入0表示线性,输入1表示指数。尝试用下边的调整演奏旋律或主音阶。你会注意到当使用线性warp时,全部音阶都会在荧幕上平均分布。

10.6. 指数改变

{SinOsc.ar(MouseX.kr(220, 880, 'exponential'), mul: 0.3)}.play;

或者

{SinOsc.ar(MouseX.kr(220, 880, 1), mul: 0.3)}.play

按照惯例,你会想要使用指数值来处理频率。

指数值更适于做音调控制的映射,但当选择跨越一段频谱的频率时同样重要。同样地,我们在每个八度中(感觉音是“相等的”分布)聆听音乐(也就是音调)。以下是中央C的六个八度及它们相应的频率。注意C1到C5间的线性距离(32到523,大概500)几乎与C5到C6(523到1046,大概500)间的相等。

音与频率对照图
音与频率对照图

简言之,随着音调的升高,我们通过更远的距离来听到“相同”。每个八度以指数方式倍增。

10.7. 指数的选择

{rrand(30, 4000.0)}.dup(20).sort.round(0.1)
{exprand(30, 4000.0)}.dup(20).sort.round(0.1)

我引入 MouseX 和 Y 作为工具测试音调的值以及进行实时实验,这不禁让人联想起最早期的(并且也非常成功的)电子乐器:泰勒名电子琴。泰勒名琴通过演奏者的手接近天线和突出环(protruding loop)控制音调和振幅。演奏者通过将手向天线或环移近、移远的动作“演奏”泰勒名琴。

运用一些组件,我们做了一个MouseY控制振幅,MouseX控制频率的虚拟泰勒名琴。要模仿那种经典的科幻之音,你需要使用大量的颤音。

离散音调控制,MIDI

另一个很难使用MouseX演奏简单曲子的原因是音调的改变是持续的。我们习惯于以离散的(非连续的)音调聆听和演奏乐器。事实上,演奏持续音调改变的乐器(例如小提琴、人声、长号),用准确的判定演奏普遍被认为是很赞的技巧。

一个生成非连续音的方法是使用 round 消息。运行下例试试是否会更容易演奏个曲子。

10.8. 非连续值

{SinOsc.ar(MouseX.kr(220, 880, 1).round(10), mul: 0.3)}.play
{SinOsc.ar(MouseX.kr(220, 880, 1).round(100), mul: 0.3)}.play

这给我们非连续的音符,但它们看起来并不对。音阶并不按线性值的轨迹走。round(100) 给我们诸如400, 500, 600等等的值。但一个始于220的半音音阶,是220, 233.1, 246.9, 261.6, 277.2, 293.7, 311.1, 329.6, 349.2。尽管运用一切你知道的数学公式,音与音之间的模式并非能被轻松地发现。那是因为它们不仅是指数值,而且还基于平均律音阶(我们将稍后考虑)。我们遇到了无处不在的接口(interface)问题:我们将音和音阶想做数字(如果C4是0,那么E4便比其高4个钢琴琴键),但 SinOsc 只懂得频率。

幸运的是,使用MIDI,我们便可以生成平均律音阶的频率。SC懂得MIDI数字,并能将它们转换为频率。记着C4的MIDI数是60,然后每半级(half-step)是1。因此C5是72,A4是69,以此类推。所有的C是12的倍数:C0 = 12, C1 = 24, C2 = 36, C3 = 48, C4 = 60(十位是八度数+1,个位是(八度数+1)×2)。要将这些转化为频率,使用midicps消息。

10.9. MIDI转换

[57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71].midicps.round(0.1)

现在回到最初那个 MouseX 控制的 patch,带着更多的疑问。我们现在可用MIDI数57(A3,或220Hz)到81(A5,或880Hz)来表述两个八度的范围。但等等先,你是否领先了我一步?你使用线性warp还是指数warp?在这里,我们用线性,因为MIDI值是线性的。这就是说,第一个八度间的差值(57到69)和第二个八度间的差值(69到81)是一样的:12。

这个改进patch的第一版貌似正确的捕捉到了音的轨迹,但值不是离散的。那是因为 MouseX 将始终返回分数(fraction),即MIDI音之间的值,比如62.1453 或 74.874。要强制它们离散化,我们加入round消息,这将使他们以1递增或递减。(试试为四分音和全音阶使用0.5和2.0)。

10.10. MIDI MouseX.kr

{SinOsc.ar(MouseX.kr(59.0, 81.0).midicps, mul: 0.3)}.play
{SinOsc.ar(MouseX.kr(59.0, 81.0).round(1.0).midicps, mul: 0.3)}.play

仍不满意?是否是由于这是半音音阶而你要的全音阶?这样的话,我介绍你使用 DegreeToKey,这超出了本章的范围。我知道最后这一两页显得比较难懂,因此这也是指出音乐是复杂的的好时机。这是你长久以来碰到的最难的难题。它无处不在且易于接受(每个人都有至少一种喜爱的音乐类型,几乎每个人都会唱歌),然而它如何运作的细节甚至可以将最聪明的脑袋搞到短路。

其他外部控制

还有其他在patch内将外部控制连接到值的方法。如果你有一块Wacom输入板,你可以使用TabletX, TabletY, 和 TabletZ。你还可以将控制连接到外部MIDI控制界面或键盘。你可以将行为附到电脑键盘上(keystate)或用按钮和滑杆的GUI。这些对于目前我们对学习来说都显得过于复杂。现在,我只想聚焦于内部自动控制。

练习

下边的patch仅使用了两个 SinOsc 和一个 LFNoise0,但由于他们相互连接的方式(频率调制,后述),却产生了非常丰富的声音肌理。

里边有四个鼠标控制,patch的引数取自鼠标位置。使用鼠标位置来挖掘声音。先试试荧幕的四个角,然后再放到中部。

试着改变 MouseX 的最大(12)和最小([1, 1])范围,进而改变音的范围。第三和第四例的唯一差别是exprand(随机指数值)和 rrand(线性指数值)。注意当使用 rrand 时,绝大多数都是高音;而当使用 exprand 时,音(对于我们“八度”的耳朵来说)就均匀的在频谱上分布。这也显示了我们的耳朵在听指数上升时觉得“均匀”,听线性时则感觉更靠近高值。

10.11. 科幻电脑

(
{
PMOsc.ar(
         LFNoise1.kr(
                MouseX.kr([1, 1], 12),
                mul: MouseY.kr(10, 1000),
                add: 1000),
         LFNoise0.kr(
                MouseX.kr([1, 1], 12),
                mul: MouseY.kr(30, 1000),
                add: 1000),
         MouseY.kr(0.1, 5.0),
         mul: 0.3)
}.play
)

你能听出这两段代码的区别吗?

{Mix.ar( SinOsc.ar({rrand(100.0, 64000.0)}.dup(50)))*0.05}.play
{Mix.ar( SinOsc.ar({exprand(100.0, 64000.0)}.dup(50)))*0.05}.play

这也是一样的,我只是加了几行代码以列印八音度带(octave bands)上的频率。试试将 rrand 换为 exprand

(
{
o = 100.0;
"Octave Band ".post; [o, o*2].postln;
Mix.ar(
      SinOsc.ar(
            // 用 exprand 替代 rrand
           {rrand(o, 6400)}.dup(50).sort.round(0.01).do(
                 {arg i;
                       if(i < (o*2),
                          {i.post; " ".post},
                          {o = o*2; "\n\n".post; "Octave band ".post;
                                [o, o*2].postln; i.post; " ".post;}
                           )
                 })
       )
)*0.1}.play
)
(
// 随机指数
{Mix.fill(12, // number of oscillators
       {arg i;
       Pan2.ar(SinOsc.ar(SinOsc.ar(
                   freq: MouseX.kr(rrand(0.1, 5.0), rrand(3.0, 20.0)), // speed of vibrato
                   mul: MouseY.kr(10, 50), // width of vibrato
                   add: exprand(200, 5000)), // freq of exponential random oscillators
              mul: max(0, LFNoise0.kr(MouseX.kr(rrand(1, 6), rrand(6, 1))))), 1.0.rand2)
})*0.03
}.play
)
(
// 线性随机波
{Mix.fill(12, // 振荡器数量
      {arg i;
      Pan2.ar(SinOsc.ar(SinOsc.ar(
                   freq: MouseX.kr(rrand(0.1, 5.0), rrand(3.0, 20.0)), // speed of vibrato
                   mul: MouseY.kr(10, 50), // width of vibrato
                   add: rrand(200, 5000)), // freq of linear random oscillators
             mul: max(0, LFNoise0.kr(MouseX.kr(rrand(1, 6), rrand(6, 1))))), 1.0.rand2)
})*0.03
}.play
)
Be Sociable, Share!

作者: ww1way

http://about.me/ww1way

发表评论

电子邮件地址不会被公开。 必填项已用*标注