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和其它律动性很强的乐器非常方便。这样的格式看起来很清晰,所以我十分推荐。

results matching ""

    No results matching ""