SC:噪音,减法合成,调试,修改源文件

上一章学习的加法合成通过叠加独立的正弦波以创建复杂的声音。在加法合成中,我们从一个具备丰富频谱的音源开始,并用一个滤波器修改它。其结果听上去更自然,因为自然的声音也是以同样的方式产生的。比如说,拨弦或拨铃的声音,从一个简单的激励源开始。乐器的琴身,本身具有自然的共鸣频率,对声音的特质进行滤波和塑形。经过滤波的讲话,伴随声带表现了一中莫名其妙但却丰富的音源。舌头,鼻窦腔和嘴形决定了讲话中丰富的元、辅音。

包括SC在内的所有合成器,都装备着一堆富频谱音源。如果说这里的“丰富”指的是构成声音的正弦波数量的多少,那么可被论证的一点是,最丰富的音源非纯噪音莫属。在前边的章节中,你可能已经发觉,等音频谱元素越多,生成的声音越趋近于噪音。噪音常被定义为具备均等表述的一切可能频率集合。我们同样用无模式的声波来定义它。我喜欢将之想做我无法辨识的任何数字序列。

噪音

“白”噪音被认为在整个频谱上的能量是均等的。“粉”噪音是向较低频率的指数偏差:每个倍频带(八音度带,octave band)有均等的能量,或者正弦波的数量。记住音乐上的八度是指数的。频率100和200是一个分离的八度,100的区别。但2000高八度是4000,有2000的差异。白噪音均等地表现频率,因此如果在理论上,100到200间有100个频率,那么2000和4000间便有2000个。但那不是我们听到音乐的方式。我们将100到200间的距离理解为与2000到4000间的距离相等。粉噪音纠正了频率的分布以符合我们的听觉习惯。因此如果100到200(一个八音度带)间有100个频率,那么2000到4000(一个八音度带)间也只有100个。粉噪音听起来更自然、饱满。白噪音听起来更亮、更薄。

我们可以通过加法合成创造粉噪音。粉噪音要求一个具备低频的指数随机过程。下边的代码阐述了一个线性随机组和一个指数组。注意指数组带出了更多较低的数字。接下来,这两个函数被用于混合1000条随机选取的正弦波以生成白和粉噪音。(注意CPU占用率,并与一个PinkNoise对象做对比)

15.1. 从零开始的噪音 (rrand, exprand, Mix, fill, SinOsc)

{rrand(1, 1000).round(1)}.dup(100).sort
{exprand(1, 1000).round(1)}.dup(100).sort
{Mix.fill(1000, {SinOsc.ar(rrand(1.0, 20000))})*0.01}.play
{Mix.fill(1000, {SinOsc.ar(exprand(1.0, 20000))})*0.01}.play
{PinkNoise.ar}.play // 对比CPU占用

SC中有大约15种不同的噪音类型可用。它们可以被用作音频信号(你听到的声音)和控制(影响其他你听到的正弦波的模式)。公分母(common denominator)仿佛是随机的,即,我们无法识别声波的模式或者数字的序列。聆听下边各例的噪音。注意它们在scope窗口内的区别。降低你耳塞或监听的音量。它们会很吵。

15.2. 噪音的类型

{WhiteNoise.ar}.scope(1)
{PinkNoise.ar}.scope(1)
{BrownNoise.ar}.scope(1)
{GrayNoise.ar}.scope(1)
{Dust.ar(12)}.scope(1)

减法合成

减法合成使用滤波移除或塑造富音源的频率。三个常见的滤波器是低通(low pass)、高通(high pass)和带通(band pass)。从名字便可解读它们各自的用途。低通滤波器允许低频通过,滤除高频。高通滤波器允许高频通过,滤除低频。带通滤波器滤除高低频,允许一个频率带通过。看下面的图示。从左至右依次为高通、低通和带通。

从左至右:高通、低通和带通滤波器图示
从左至右:高通、低通和带通滤波器图示

这些名字始终令我混淆,因为它们都表述未受到影响的频率。我们用它们来修改频谱的另一端。因此当我使用高通滤波器时,我将它想做低音滤波器;增加或减少低频。第三例,带通滤波器,阐述了这一混淆。它显示高频和低频都被砍掉了,只剩一个带未受影响。但如果你增大整个频谱,将低的和高的升到0,然后带高于0 db,实际上你是在推进频率的中间带。那么下边是图示,阐述了高通、低通、带通滤波器通常的用法,无论他们的名字如何,增大不被“通过”的带。

从左至右:低通滤波器和高通滤波器
从左至右:低通滤波器和高通滤波器

SC中做这些事的对象是RLPF, RHPF, 和 BPF。每个的引数都是欲被做滤波的输入,截止频率,以及共鸣或q的倒数(reciprocal of q)(带宽/截止频率)。截止频率是频率开始被滤除的点。共鸣(rq)影响滤波器的焦点有多窄。共鸣非常窄(比如0.05)的实际结果是滤波器截止频率的那个音。以下为具备宽的和窄的rq值的带通滤波器。

宽、窄rq值的带通滤波器
宽、窄rq值的带通滤波器

下例中,X轴控制截止频率,Y轴控制q。荧幕顶端是一个狭窄的q(0.01),底部是宽的q(2.0)。

15.3. 经过滤波的噪音

(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: 0.7);
cutoff = MouseX.kr(40, 10000, 1);
resonance = MouseY.kr(0.01, 2.0);

RHPF.ar(signal, cutoff, resonance)}.scope(1)
)
(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: 0.7);
cutoff = MouseX.kr(40, 10000, 1);
resonance = MouseY.kr(0.01, 2.0);

RLPF.ar(signal, cutoff, resonance)}.scope(1)
)
(
{
var signal, filter, cutoff, resonance;

signal = PinkNoise.ar(mul: 0.7);
cutoff = MouseX.kr(40, 10000, 1);
resonance = MouseY.kr(0.01, 2.0);

BPF.ar(signal, cutoff, resonance)}.scope(1)
)

下边是用锯齿波(借用高次谐波的富音源)的一个相同的例子。当小的数字被用于噪音的共鸣,你会听到一个单一的与滤波截止频率一致的连续频率。对锯齿波来说,每个高次谐波都将作为接近或超过那个谐波的截止频率。这个同样的滤波效果可以复古到1970年代。

15.4. 被做滤波的锯齿波

(
{
var signal, filter, cutoff, resonance;

signal = Saw.ar([50, 75], mul: 0.7);
cutoff = MouseX.kr(40, 10000, 1);
resonance = MouseY.kr(0.01, 2.0);

RLPF.ar(signal, cutoff, resonance)}.scope(2)
)
(
{
var signal, filter, cutoff, resonance;

signal = Saw.ar([50, 75], mul: 0.7);
cutoff = MouseX.kr(40, 10000, 1);
resonance = MouseY.kr(0.01, 2.0);

BPF.ar(signal, cutoff, resonance)}.scope(2)
)
{RLPF.ar(Saw.ar([100,250],0.1), XLine.kr(8000,400,5), 0.05) }.play;

电压控制滤波器

在古典合成器中,这些模块被称为VCF,或电压控制滤波器(voltage controlled filters)。滤波器的参数可以被像之前例子中我们对频率和振幅的方式来进行控制。

作为窄的rq,音从滤波器截止频率冒出,因此,适用于之前例子中频率的同样类型的控制同样适用于滤波器截止频率,结果也相同。

15.5. 滤波器截止频率做音

// 频率控制 (第一个patch)
{SinOsc.ar(LFNoise0.kr([12, 12], 500, 500), mul: 0.5)}.play
// 用窄rq应用到滤波器截止频率的同样控制源
{RLPF.ar(PinkNoise.ar(0.3), LFNoise0.kr([12, 12], 500, 500), 0.02)}.play
// 教宽的rq不会产生特定的音
{RLPF.ar(PinkNoise.ar(0.9), LFNoise0.kr([12, 12], 500, 500), 1)}.play

这些滤波器是单带(band)的,结果当然简单、人造、复古。真实的声音有许多共鸣频率。要做到那种层次的丰富程度,我们需要有多个共鸣节点的滤波器。

组件建模和Klank

组件建模(component modeling)将合成组件的重心从你想要创造的声音转移到你想虚拟的乐器的身形上。即,相对于塑造一个锯齿波,塑造一个吉他的琴身,而后用富音源激发它。

Klank是一个模拟琴身或空间共鸣节点(node)的组件建模滤波器。它允许你指定一组被过滤(被共鸣)的频率,匹配振幅和衰减率。

Klank的第一个引数是频率规格。它是一个数组的数组(二元数组)。即数组中的每个元素也是一个数组(就像盒子套盒子)。外部的数组以一个“`”(重音标记?)开始,这是为了表明我不需要多通道扩展。第一个内部数组是一组共鸣的频率,第二个是每个频率的振幅度(默认为1),最后一个是每个频率的衰减率。

对于定态声音比如噪音来说,衰减只有很小的影响,所以在第一个例子中,它被留空。因为我们添加了10个回响频率,因此噪音源将很小声:0.01~0.1。第二个例子用Array.series填充数组。搜索它的帮助文档寻求解释。第三例用随机频率载入一个数组。多运行它几次以观察其每次的变化。

在这些例子中,之所以声音会持续不断的演化,正因为激励源(输入)是噪音,或者一组随机的以每秒44K穿梭的数字(上边提到的随机数)。我喜欢将这些例子理解为用弓拉一个共鸣体。弓就是那些随机数列,Klank是塑造声波的共鸣体。由Klank创制的物理空间从噪音中抽取模式(pattern),同时噪音给予空间更多的多样性。

15.6. 共鸣数组

( 
{ 
Klank.ar( 
   `[[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000], //freq array 
   [0.05, 0.2, 0.04, 0.06, 0.11, 0.01, 0.15, 0.03, 0.15, 0.2]], 
   PinkNoise.ar(MouseX.kr(0.01, 0.1))) 
}.scope(1) 
)
( 
{ 
Klank.ar( 
   `[Array.series(10, 50, 50), 
   Array.series(10, 1.0, -0.1)], 
   ClipNoise.ar(0.01) 
) 
}.scope(1) 
)
( 
{ 
Klank.ar( 
   `[{exprand(60, 10000)}.dup(15)], 
   PinkNoise.ar(0.005) 
) 
}.scope(1); 
)

钟鸣(Chimes)

在加法合成章节,我们通过叠加不和谐(随机的)频率以创建典型的铃音。在这个patch里,我们从相反的方向做这一点;移除或过滤一个音源,仅留存一组随机的频率。两者得到的结果几乎毫无二致。那么区别在哪呢?什么时候用加法什么时候用减法呢?运用加法合成,你可以对每个正弦波进行控制,因此可以制造出复杂和非自然的声音,但却是以CPU作为代价。减法合成可能听上去会更自然一些,因为你无法改变每个频率,因此它们更接近自然的声音。但最大的原因其实是效能。加法钟鸣可能要使用400个UGen和10%的CPU,但下边这个Klank(减法合成的一个UGen)钟鸣仅需使用20个Ugen和1%的CPU。

用钟舌撞击钟面是一个噪音的突发(尝试用手握住一个铃铛以使其不能共鸣并用钟舌撞击它。你仅能听到一个短暂的闷响))。铃或钟的物理结构会在特定的频率共鸣。那部分频率会维持,其余的很快消散。正是这部分剩余的频率塑造了钟或铃的特质。所以对这个patch来说,我们将由一个极短的噪音突发开始。

15.7. 钟声突发 (Env, perc, PinkNoise, EnvGen, Spawn, scope)

( 
{ 
var burstEnv, att = 0, burstLength = 0.0001, signal; //变量 
burstEnv = Env.perc(0, burstLength); //envelope times 
signal = PinkNoise.ar(EnvGen.kr(burstEnv, gate: Impulse.kr(1))); //噪音突发 
signal; 
}.play 
)

尝试一下其他的噪音源,比如WhiteNoise, BrownNoise, ClipNoise。同样地,试一下更长的音头和衰减率。

我们用Klank.ar作为共鸣器,用dup方法生成数组。Klank的输入引数为上边那个噪音突发。因为在本例内我们使用一个简短的突发而不是一个固态的噪音作为激励器,衰减开始起作用,所以衰减的引数也被用上了。

15.8. 钟鸣 (normalizeSum, round, Klank, EnvGen, MouseY)

( 
{ 
var chime, freqSpecs, burst, totalHarm = 10; 
var burstEnv, att = 0, burstLength = 0.0001; 

freqSpecs = `[ 
   {rrand(100, 1200)}.dup(totalHarm), //频率数组 
   {rrand(0.3, 1.0)}.dup(totalHarm).normalizeSum.round(0.01), //振幅数组 
   {rrand(2.0, 4.0)}.dup(totalHarm)]; //衰减率数组 

burstEnv = Env.perc(0, burstLength); //包络次数 
burst = PinkNoise.ar(EnvGen.kr(burstEnv, gate: Impulse.kr(1))); //噪音突发 

Klank.ar(freqSpecs, burst)*MouseX.kr(0.1, 0.8) 
}.scope(1) 
)

谐波、振幅和衰减率的数量由totalHarm设置。freqSpecs数组中的每个数组都有各自的函数和dup方法生成。变量burst作为第二引数与freqSpecs一同被插入Klank.ar中并生成结果。

当然,你同样可以使用谐波序列调制铃音。

15.9. 调制的钟鸣 (或拉扯?)

( 
{ 
var chime, freqSpecs, burst, totalHarm = 10; 
var burstEnv, att = 0, burstLength = 0.0001; 

freqSpecs = `[ 
   {rrand(1, 30)*100}.dup(totalHarm), 
   {rrand(0.1, 0.9)}.dup(totalHarm).normalizeSum, 
   {rrand(1.0, 3.0)}.dup(totalHarm)]; 

burstEnv = Env.perc(0, burstLength); 
burst = PinkNoise.ar(EnvGen.kr(burstEnv, gate: Impulse.kr(1))); 

Klank.ar(freqSpecs, burst)*MouseX.kr(0.1, 0.8) 
}.scope(1) 
)

为填充数组,我在1和30间选取一个数字并乘以一个基准频率。可能会存在一些被复制的谐波。这听起来有点近似于一个弦乐,这将在下一张进行说明。如果为这个谐波做一些轻微的去谐,那么它听上去便更像一个铃。如果你去谐正负10%会怎样呢?

你可能会注意到,在一个patch中,有很多可以调整振幅的位置。而绝大多数的电子元音音乐工作室的情况也是一样的。在这个patch中,你可以在EnvGen, PinkNoise, Klank或最后的EnvGen中调整音量。

尝试调整谐波总量以及burstLength。尝试为音头强度加一个变量。一个更柔和的音头意味着一个更柔和的敲击。改变freq数组内频率的范围。改变衰减率选择的范围(你必须确保你输入的频率数量符合totalHarm的规格,因为这是那些数组的大小)。尝试奇次谐波,或者改变去谐谐波度。

纠错,添加注释,平衡嵌套,postln, postcln, postf, catArgs

如何确认某频率集合是你要的(随机,奇数,偶数,去谐等等)?同样地,你怎样解决patch中孤立的问题?

当有些事看起来不正常了,我首先做的,便是孤立和评估小块代码。你可以选择运行单行甚至选中的部分。下例展示了一堆代码中部分的选择。这些小块可以被单独选取和评估。试着选中粗体部分并按enter。

15.10. 运行选中的部分
Array.fill(5, {exprand(10, 100)}).round(1) * ([1, 2, 5, 7].choose)
Array.fill(5, {exprand(10, 100)}).round(1) * ([1, 2, 5, 7].choose)
Array.fill(5, {exprand(10, 100)}).round(1) * ([1, 2, 5, 7].choose)
Array.fill(5, {exprand(10, 100)}).round(1) * ([1, 2, 5, 7].choose)

对待逗号和变量需要小心。比如说,如果你选中并运行:“{rrand(100, 1200)}.dup(totalHarm),” ,因为逗号和遗失的totalHarm变量申明你会碰到错误。但是如果你将totalHarm换为10并选取除逗号之外的一切再运行之,这段代码就可以运行,并且SC将在post窗口列印结果。

你同样可以通过将可疑代码部分用更安全的值替换来孤立错误,或者为一部分代码“添加注释”。再次,你需要对分号和逗号特别小心。注释可以通过在单行前加”//”或在想要注释的开头用”/*”结尾用”*/”将一部分代码进行注释。如果对freqSpecs数组持怀疑态度,你可以在随机数组中插入一个“安全”值:{0.2 /*rrand(0.3, 1.0)*/}.dup(totalHarm)。这将用0.2而不是rrand(0.3, 1.0)填充整个数组。

在Format菜单下选取Syntax Colorize将基于语法为你的代码上色。你可以这么做来看你的注释是否正确。注释应该是红色的。记住程序将忽略所有注释项目。下例中我先写最原始的代码,然后带round的同样的代码,乘,并选择移除函数。全部选取、运行,然后上色。

15.11. 运行选取的代码

Array.fill(5, {exprand(10, 100)}).round(1) * ([1, 2, 5, 7].choose)
Array.fill(5, {exprand(10, 100)}) /* .round(1) * ([1, 2, 5, 7].choose) */

我有时也使用一些列A/B转换器,它有利于代码运行的顺序:自上而下。“安全”的选取是我知道没问题的部分,我会将它们放在疑似有问题的代码下部。由于是在下部,因此它将替换掉上边一切的值,被“怀疑”的值。通过注释底部的代码来开启或关闭“A/B”转换器。运行下边第一例,然后反注释”this I know works”再运行一次。句子“this is experimental”便会被替换。

15.12. 添加注释

(
a = "this is experimental";
// a = "this I know works";
a.postln
)
( // 一个运用在真正代码中的例子
{
var chime, freqSpecs, burst, totalHarm = 10;
var burstEnv, att = 0, burstLength = 0.0001;

freqSpecs = `[ //"实验" 集合
   {rrand(100, 1200)}.dup(totalHarm), //频率数组
   {rrand(0.3, 1.0)}.dup(totalHarm).normalizeSum.round(0.01), //振幅数组
   {rrand(2.0, 4.0)}.dup(totalHarm)]; //衰减率数组

//freqSpecs = `[ //"安全的" 集合
// [100, 200] //频率数组
// [0.5, 0.5] //振幅数组
// [0.1, 0.1]]; //衰减率数组

当第二个freqSpecs被注释以后,它便不再属于这个patch的一部分。如果你取消注释,那么安全集合将替代实验集合(你无需注释实验集合,它会被重新分配)。如果patch仍能正确运行,那么实验部分便一定有错。

当我不能从几页复杂的代码得到任何声音的时候,我也会用这招。以检查究竟是系统错误,还是我代码的错误。我会在函数最后一行放一个简单的SinOsc.ar,结果就是除了这行会被演奏,上边的一切都会被忽略。

另一个典型错误是嵌套的错搭。Com-shift-b 通过高亮两个相邻括号间的内容以“平衡”嵌套。当它无法找到一个匹配的括号时会报错。要查错的话,将鼠标置于一个明显正确的嵌套内并持续按下Com-shift-b。它将持续为你检查嵌套。

最后的纠错策略用post消息将操作的结果列印到post窗口。这与选取和评估一部分代码相似。区别是你可以一次评估若干地方。下边的例子中有一些蓄意的错误代码。当你运行它,会爆出几屏的错误代码。但在顶部,第一行将把信息附加到postln。你能通过列印的数据找到错误的地方吗?(提示:nil意味着没有数据被分配)

15.13. 用postln除错

(
{
var chime, freqSpecs, burst, totalHarm = 10;
var burstEnv, att = 0, burstLength;

freqSpecs = `[
   rrand(100, 1200).dup(totalHarm).postln,
   {rrand(0.3, 1.0)}.dup(totalHarm).normalizeSum.round(0.1).postln,
   {rrand(2.0, 4.0)}.dup(totalHarm).round(0.1).postln];

burstEnv = Env.perc(0, burstLength.postln); //包络时间
burst = PinkNoise.ar(EnvGen.kr(burstEnv, gate: Impulse.kr(1)));

Klank.ar(freqSpecs.postln, burst)*MouseX.kr(0.1, 0.8)
}.scope(1)
)

postln消息可以被嵌入一条消息链,就像本例展示的一样。你可以用find和repalce移除它们。

15.14. 在消息链中用postln除错

(
[[60, 62, 68], [65, 67], [61, 63, 55, 23]]
.choose.postln
.midicps.postln
.round(0.1).postln
.reciprocal.postln
.round(0.0001).postln
)

post消息列印到post窗口,postln列印对象然后重起一行,postcln先于那行进行注释(“//”)。如果你在post窗口写代码的话,这很有用。新的post并不会影响你代码随后的尝试。

如果你反复使用post的话,消息将会汇成一堆。明显的解决方式就是用postln,但那很快就会把post窗口空间耗尽。同样地,如果一切都在新的一行上,也很难检测相关的数据。这里有一些格式化列印信息的方法。第一是将所有东西集合如一个数组。第二是使用catArgs,它用一系列引数连接一个字符串;最后使用postf,它的第一引数是用”%”点缀的字符串,接下来是其他将会被放到并替换字符串内”%”的(按顺序)其他引数。用”\t”结合(tab),列印的信息将会有很高的可读性。用过C的程序员应该都熟知这些惯例。

15.15. 格式化列印信息

(
// 莫名其妙的
var pitch, duration, amplitude, voice;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose.post;
duration = 4.0.rand.round(0.1).post;
amplitude = 1.0.rand.round(0.1).post;
voice = 10.rand.post;
})
)
(
// 铺得太开
var pitch, duration, amplitude, voice;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose.postln;
duration = 4.0.rand.round(0.1).postln;
amplitude = 1.0.rand.round(0.1).postln;
voice = 10.rand.postln;
})
)
(
// 累赘笨重的
var pitch, duration, amplitude, voice;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose.post;
" ".post;
duration = 4.0.rand.round(0.1).post;
" ".post;
amplitude = 1.0.rand.round(0.1).post;
" ".post;
voice = 10.rand.postln;
})
)
(
// 胜任的
var pitch, duration, amplitude, voice;
"pitch, duration, amplitude, voice".postln;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose;
duration = 4.0.rand.round(0.1);
amplitude = 1.0.rand.round(0.1);
voice = 10.rand;
[pitch, duration, amplitude, voice].postln;
})
)
(
// 好一些?
var pitch, duration, amplitude, voice;
"pitch, duration, amplitude, voice".postln;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose;
duration = 4.0.rand.round(0.1);
amplitude = 1.0.rand.round(0.1);
voice = 10.rand;
"P, D, A, V ".catArgs(pitch, duration, amplitude, voice).postln
})
)
(
// 有用的
var pitch, duration, amplitude, voice;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose;
duration = 4.0.rand.round(0.1);
amplitude = 1.0.rand.round(0.1);
voice = 10.rand;
"pch %\tdur %\tamp %\tvce %\n".postf(pitch, duration, amplitude, voice)
})
)
(
// 有用的
var pitch, duration, amplitude, voice;
"pch\t\tdur\t\tamp\t\tvce".postln;
20.do({
pitch = [60, 62, 64, 65, 67, 69, 71].choose;
duration = 4.0.rand.round(0.1);
amplitude = 1.0.rand.round(0.1);
voice = 10.rand;
"%\t\t%\t\t%\t\t%\n".postf(pitch, duration, amplitude, voice)
})
)

修改源代码

现在我们正在进入危险的领域。小心行事。我耍的最后一个小聪明需要你修改源代码。最后两个技巧的问题在于在检测post窗口的数据时,你并不能确定哪个值是哪个。你可以用字符串,比如“choice”.postln, “array”.postln。但这对消息链那个例子无效。你需要的是一个带引数的列印消息,比如postn(id),这里id便是一个标识符。那么45.postn(“freq”)的结果将是freq:45。没有这样的消息?写你自己的。

在我们做这个例子前,让我给你一点点警告。我发觉我对SC改得越来越少,基于两个原因。第一是我在实验室中与一堆机器工作。如果我在我的笔记本上修改了源码,那么我不得不在所有我可能会用到的机器上修改源码,否则,代码便不会运行。同样地,如果升级SC,我也不得不升级那些文件。我发现,一段时间之后,这么做很累。但这不该停止你的尝试。

打开String.sc 和 Object.sc文档。在String.sc文档中找到”post”代码部分,然后如下例般加入第三行代码(加粗部分):

postcln { “// “.post; this.postln; }
postc { “// “.post; this.post; }
postn {arg note; note.post; “: “.post; this.postln; }

接下来,在Object.sc文档中加入这行(加粗部分):

postc { this.asString.postc }
postcln { this.asString.postcln; }
postn {arg note; this.asString.postn(note); }

推出并重启SC,或者在Lang菜单下选择Compile Library。现在你便可以为你的列印项目添加注解了。你也可以说,你现在已是开发团队的一部分。

15.16. postn

(
[[60, 62, 68], [65, 67], [61, 63, 55, 23]]
.choose.postn("choice")
.midicps.postn("midi conversion")
.round(0.1).postn("rounded value")
.reciprocal.postn("recip")
.round(0.0001).postn("rounded recip")
)

练习,钟鸣和洞穴

下边的patch用固态噪音和噪音激发作为Klank的输入。钟鸣和洞穴通过简单叠加它们混合。这个patch使用了很多ugen并有可能涨爆你的cpu。如果这样的话,减少谐波、乐器等的数量。

15.17. 减法合成 (Klank, Decay, Dust, PinkNoise, RLPF, LFSaw)

(
{
var totalInst, totalPartials, baseFreq, ampControl, chimes, cavern;
totalInst = 5; //钟鸣总数
totalPartials = 12; //每个钟鸣的分音数
baseFreq = rrand(200, 1000); //钟鸣的基础频率

chimes =
   Mix.ar(
      {
      Pan2.ar(
         Klank.ar(`[
            {baseFreq*rrand(1.0, 12.0)}.dup(totalPartials),
            Array.rand(totalPartials, 0.3, 0.9),
            Array.rand(totalPartials, 0.5, 6.0)],
            Decay.ar(
               Dust.ar(0.2, 0.02), //Times per second, amp
               0.001, //decay rate
               PinkNoise.ar //Noise
            )), 1.0.rand2) //Pan position
        }.dup(totalInst)
   );

cavern =
   Mix.ar(
     {
     var base;
     base = exprand(50, 500);
        Klank.ar(
           `[ //frequency, amplitudes, and decays
               {rrand(1, 24) * base *
                    rrand(1.0, 1.1)}.dup(totalPartials),
               Array.rand(10, 1.0, 5.0).normalizeSum
           ],
           GrayNoise.ar( [rrand(0.03, 0.1), rrand(0.03, 0.1)])
        )*max(0, LFNoise1.kr(3/rrand(5, 20), mul: 0.005))
      }.dup(5));
cavern + chimes
}.play
)

[新版]

// 洞穴变体
(
{
var totalPartials = 12;

Mix.ar(
   {
   var base;
   base = exprand(50, 1000);
   Pan2.ar(
      Klank.ar(
         `[ //frequency, amplitudes, and decays
            {rrand(1, 24) * base *
               rrand(1.0, 1.1)}.dup(totalPartials),
            Array.rand(10, 1.0, 5.0).normalizeSum
         ],
         GrayNoise.ar( rrand(0.03, 0.1))
      )*max(0, LFNoise1.kr(6, mul: 0.005)),
      LFNoise0.kr(1))
   }.dup(5));
}.play
)
// 回转轮
{
var totalPartials = 4;

Mix.ar(
   {
   var base;
   base = exprand(50, 10000);
   Pan2.ar(
      Klank.ar(
         `[ //frequency, amplitudes, and decays
            {rrand(1, 24) * base *
               rrand(1.0, 1.1)}.dup(totalPartials),
            Array.rand(10, 1.0, 5.0).normalizeSum
         ],
         GrayNoise.ar( rrand(0.03, 0.1))
      )*max(0, SinOsc.kr(6/rrand(1, 10), mul: 0.005)),
      LFNoise1.kr(1))
   }.dup(8));
}.play

这个patch的声音飘进飘出,因此如果没声儿的话稍等会儿。执行它四、五次以得到更丰富的声音。

{
var totalPartials = 3;

Mix.ar(
   {
   var base;
   base = exprand(50, 100);
   Pan2.ar(
      Klank.ar(
         `[ //frequency, amplitudes, and decays
            {rrand(1, 24) * base *
               rrand(1.0, 1.1)}.dup(totalPartials),
            Array.rand(10, 1.0, 5.0).normalizeSum
         ],
         GrayNoise.ar( rrand(0.03, 0.1))
      )*max(0, SinOsc.kr(10/rrand(1, 5), mul: 0.005)),
      LFNoise1.kr(1))
   }.dup(8)) * max(0, LFNoise1.kr(1/10));
}.play
Be Sociable, Share!

作者: ww1way

http://about.me/ww1way

发表评论

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.