15行代码写出简单鼓机
在之前几节,我们完全按照sonic pi脚本语言的逻辑来写鼓。其中本章第二节介绍的spead()函数,是新版本刚刚加入的功能。用这种方式来写节奏,好处是把鼓的节奏变得更抽象了,而且一部分事情可以让机器代劳。而其弱点在于,鼓的节奏淹没在代码里,显得并不直观。你必须像机器一样将算式转述出来,才知道自己究竟写了什么节奏。超越自己控制范围,习惯在DAW里写鼓的人多半会放弃吧?
其实就算是我自己,如果想把ableton live里的节奏loop誊抄到sonic pi,也偏向用鼓机的格式。这和机器如何运行没有直接关系,只是换个写法让自己能看得更明白而已。所以,在这篇文章里,我会介绍目前鼓机的简明写法。但是我也相信,随着新版本发布,一定会有更通俗易懂的框架出现。
用Sonic Pi编写简单鼓机
用代码写鼓机,我们的目标是实现一个长度为16的数列,并用它来表示我们想演奏的节奏。
那么这里有两种选择,一种是ruby特有的ring, 还有一种是常见的array:
# ring
kickPattern=(ring 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0)
# array
kickPattern = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]
之后我们使用这个数组去控制底鼓sample播放的音量。这样做的好处是,我们不光可以控制sample是否被触发,还能像控制velocity一样控制更细微的力度。
#array 的版本
use_bpm 120
kickVol = 1.0
kick_sample = :bd_haus
kickPattern = [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0]
live_loop :kickdrum do
sample kick_sample, amp: kickPattern.ring.tick*kickVol
sleep 0.25
end
#ring的版本
use_bpm 120
kickVol = 1.0
kick_sample = :bd_haus
kickPattern=(ring 1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0)
live_loop :kickdrum do
sample kick_sample, amp: kickPattern.tick(:kick)*kickVol
sleep 0.25
end
像这样,只用1与0控制是否触发,kickVol可以直接设置全局最大音量。
接下来,不光要控制触发与否,还要控制力度。我暂且用1-3档表示由弱至强的力度。如果你希望力度划分更细致些,也可以自己定义,比如5档10档。
use_bpm 120
kickVol = 1.0
kick_sample = :bd_haus
kickPattern=(ring 3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0)
live_loop :kickdrum do
sample kick_sample, amp: kickPattern.tick(:kick)/3.0*kickVol
sleep 0.25
end
kickPattern写出了“强-弱-次强-弱”的序列
之后控制音量的公式kickPattern.tick(:kick)/3.0*kickVol
基本实现了这样的效果
- 3档=音量100%全开
- 2档=音量开至2/3处
- 1档=音量开至1/3处
到这里,单独轨道的乐器就可以按照这个框架来编写节奏了。如果是多个乐器,多个轨道,可以这样写。鼓组的pattern看起来也比以前更清晰了。
use_bpm 120
kickVol = 1.0
kick_sample = :bd_haus
snare_sample = :sn_dolf
elec_sample = :elec_blip
kickPattern =(ring 3,0,0,0, 1,0,0,0, 2,0,0,0, 1,0,0,0)
snarePattern=(ring 0,0,0,0, 3,0,0,0, 0,0,0,0, 3,0,0,0)
elecPattern =(ring 0,2,1,0, 0,3,0,2, 0,1,2,0, 3,2,1,0)
live_loop :kickdrum do
sample kick_sample, amp: kickPattern.tick(:kick)/3.0*kickVol
sample snare_sample, amp: snarePattern.tick(:snare)/3.0*kickVol
sample elec_sample, amp: elecPattern.tick(:elec)/3.0*kickVol*2
sleep 0.25
end
以上写法参考了robin.newman与mehackit关于鼓机的写法。此外还想展示一下in_thread论坛用户ds604写的的鼓机。
use_bpm 120
define :beat1 do |pat|
(ring
*pat
.split(" ")
.map{|bar| bar.split("")}
.flatten
.map{|d| d=="1" ? 1 : 0})
end
inst1 = Hash[
:k0 => :bd_haus,
:s0 => :sn_dolf,
:h0 => :elec_blip,
:h1 =>:perc_snap2
]
pat1 = Hash[
:k0 => "1--- ---- 1--- ---- 1--- ---- 1--- ----",
:s0 => "---- 1--- ---- 1--- ---- 1--- ---- 1---",
:h0 => "1--- 1-11 11-- 1--- 1--- 1-11 11-- 1---",
:h1 => "--1- ---- --1- --1- --1- ---- --1- --1-"
]
live_loop :main do
tick
sample inst1[:k0] if beat1(pat1[:k0]).look == 1
sample inst1[:s0] if beat1(pat1[:s0]).look == 1
sample inst1[:h0] if beat1(pat1[:h0]).look == 1
sample inst1[:h1] if beat1(pat1[:h1]).look == 1
sleep 0.25
end
这可能是十分正统的ruby写法。但使用方式和之前一样,你只需要在inst1里修改采样,在pat1里修改节奏就可以了。
第一个自定义function :beat1用来解析自定义字符串,解析的过程过于技术所以就不细说了。其最终目标是将“1--- 1--- 1--- 1---”转换成机器能读取的ring数列。这是一小节32步的写法,这样写hihat和其它律动性很强的乐器非常方便。这样的格式看起来很清晰,所以我十分推荐。