Skip to main content

2 节奏与速度

本章深入探讨音乐中节奏的基本原理。我们从节拍开始——什么是节拍,如何测量节拍,以及如何通过可视化节拍来创作、编辑和演奏音乐。随后,我们将提供不同音乐流派中一些常见节奏动机的示例,以及如何用 Python 编写这些节奏的代码。本章涉及的主要编程概念包括循环、变量、函数调用和参数值传递。本章内容丰富,将为你用代码创作音乐打下坚实的基础。

2.1 节拍与速度

节拍是音乐节奏的基础。在音乐中,“节拍”这个术语有多种不同的含义,但在本章中,我们将其定义为一个时间单位,即一个音符的演奏时长——例如,“休止两拍”或“演奏一个半拍的音符”。基于节拍,音符以重复的模式组合在一起,随着时间的推移,形成我们耳朵能感知到的有节奏的韵律。

速度指的是节奏进行的快慢,即一首乐曲中一个节拍跟随另一个节拍的速度。作为听众,你可以通过用脚跟着节奏打拍来感受速度。衡量速度的标准单位是每分钟节拍数(BPM, beats per minute),即一分钟内演奏的总节拍数。这个数值几乎总是一个整数,如 60、120 或 78。在 60 BPM 的速度下,你的脚每分钟会敲击 60 次(即每秒一拍)。在 120 BPM 时,每秒会有 2 拍;而在 90 BPM 时,每秒则有 1.5 拍。在本章后面,当你开始使用 TunePad 时,可以通过点击项目顶部栏中的 BPM 指示器来设置速度(见图 2.1)。

图2.1 TunePad项目信息栏。你可以点击节拍、拍号或调号来更改项目设置。

不同的音乐流派有其典型的速度范围(尽管每首歌和每个艺术家都有所不同)。例如,嘻哈音乐的速度通常在 60–110 BPM 之间,而摇滚音乐则更快,在 100–140 BPM 之间。浩室(House)、科技舞曲(Techno)、迷幻舞曲(Trance)的速度更快,在 120 到 140 BPM 之间。

流派速度范围(BPM)
摇滚100–140
节奏布鲁斯60–80
流行100–132
雷鬼60–92
嘻哈60–110
回响贝斯130–144
科技舞曲120–140
萨尔萨140–250
巴恰塔120–140

音乐家需要经过练习才能保持稳定的速度演奏,他们有时会使用一种叫做节拍器的设备来帮助自己的演奏与音乐的节奏保持一致。你可以在 TunePad 的鼓单元格中用四行代码创建一个简单的节拍器。将乐器切换为“鼓(Drums)→打击乐音色(Percussion Sounds)”时,效果最佳。

playNote(3, velocity = 100)  # louder 1st note 第一个音符音量更大
playNote(3, velocity = 60)
playNote(3, velocity = 60)
playNote(3, velocity = 60)

你可以通过 BPM 指示器调整节拍器的速度(图 2.1)。这个例子说明,计算机非常擅长保持完美稳定的速度。如果你追求精确性,这非常好,但这样产生的音乐也有可能听起来过于刻板和机械化。真人演奏时,往往会有速度上的细微变化,这可能是为了达到戏剧效果,也可能只是人类演奏的自然结果。根据不同的流派,演奏者可能会加入一些节奏上的细微变化,称为“摇摆(swing)”或“拖拍(shuffle)”,这是一种近乎能感受到而非仅仅听到的节拍来回摇摆的感觉。在本书的后面部分,我们会向你展示如何给计算机生成的音乐增添更具人情味的感觉。

2.2 节奏记谱法

几个世纪以来,音乐家和作曲家们开发了许多不同的书面系统来记录和分享音乐。随着数字制作软件的发明,一些其他用于混音和编辑的交互式表示方法也变得常见起来。下面是同一节奏模式的四种常见视觉表示形式。这个模式的总时长为四拍,可以数作“1 and 2,3 and 4”。前两个音符各为 $\dfrac12$ 拍,接着是一个 1 拍的音符,然后这个模式重复。

2.2.1 表示方法 1:标准西方音乐记谱法

第一种表示方法(如下)展示了标准音乐记谱法(或西方记谱法),这是一种经过数百年发展而来的记录音符的系统。图示左侧的两条粗竖线表明这是节奏记谱法,即其中没有关于音高的信息,只有节奏 timing。长横线上的圆点是音符,其形状表示每个音的演奏时长。有时,不同的打击乐器的音符会画在不同的线上。我们将在图 2.2 中更详细地描述各种音符符号的含义。

图2.2 标准符号示例。

2.2.2 表示方法 2:音频波形

第二种表示方法展示了播放音乐时发送到扬声器的实际音频波形的可视化效果。波形显示了音频信号随时间变化的振幅(或音量)。下一章会详细讨论音频波形,不过现在你可以把波形看作一个图表,它展示了扬声器振动的实际强度随时间的变化。在 TunePad 中创作节拍时,你可以通过点击时间线左上角的小下拉箭头切换到波形视图(图 2.3)。

图2.3 图2.2的波形表示。

2.2.3 表示方法 3:钢琴卷轴(MIDI 卷轴)

第三种表示方法是钢琴卷轴(或 MIDI(音乐设备数字接口)卷帘)。它用实线表示单个音符。线的长度代表单个音符的时长,线的垂直位置代表所演奏的打击乐音色(在本例中是底鼓和军鼓)。这种表示方法在音乐制作软件中越来越常见。许多工具甚至允许通过拖放单个音符来创作和编辑音乐(图 2.4)。

图2.4 图2.2的钢琴卷帘或MIDI卷帘表示。

2.2.4 表示方法 4:Python 代码

最后一种表示方法是 TunePad 中的 Python 代码。在这种表示方法中,每个音符的时长通过 playNote 函数调用的 beats 参数来设置。

playNote(2, beats = 0.5)
playNote(2, beats = 0.5)
playNote(6, beats = 1)
playNote(2, beats = 0.5)
playNote(2, beats = 0.5)
playNote(6, beats = 1)

每种表示方法都有其优缺点;它们在传达某些类型的信息方面表现出色,而在传达其他信息时则效果欠佳。例如,标准节奏记谱法经过几个世纪的完善,被全球广大的音乐群体所理解。另一方面,对于没有学过读乐谱的人来说,它可能会令人困惑。单个音符的时值 timing 是通过音符附带的符尾和符杠来传达的,但水平空间和时值 timing 之间并没有一致的对应关系。

音频波形能很好地展示声音的实际形态——每个音符的延音时长(“释放”)以及音头的尖锐程度(“起音”)。它在音乐制作、混音和母带处理中很有帮助。但另一方面,波形并不能很好地反映音符的音高或作曲家所记录的预期音符时值 timing。

Python 代码对计算机来说比对人类更容易读取——它肯定不是你会交给音乐家视奏的东西。但另一方面,它的优点是可以融入计算机算法中,并以无穷无尽的方式进行操作和转换。

为了将音乐表演——即我们在现场表演中听到的内容——转录到纸上或电脑屏幕上,还有许多其他的记谱系统。每种表示方法的发明都有其特定的目的和/或针对特定的音乐流派。你可以根据具体情境以及自己的角色(是音乐家(以及演奏的乐器类型)、歌手、作曲家、录音师还是制作人)来选择一种表示方法。音乐记谱系统就像发明它们的文化和音乐传统一样丰富多样。使用软件的一个好处是,根据我们要完成的任务,可以轻松地在音乐的多种表示形式之间切换。

2.3 标准节奏记谱法

本节将回顾一种源于欧洲音乐传统的标准音乐记谱系统。这个系统用途广泛,经过长期的完善和改编,在许多国家和地区被应用于越来越多样化的乐器和音乐流派。在本章中,我们从打击乐的节奏音符时值开始,第三章将介绍有音高乐器的相关内容。

图2.5 常见音符符号,从顶部的全音符(四拍)开始,向下到底部的十六分音符。每一行新的音符时长是上一行的一半。

图 2.5 展示了节奏记谱法中最常用的符号。音符用椭圆形的圆点表示,分为空心和实心两种。除了全音符(最上面的)之外,所有音符都带有符尾,符尾可以向上或向下。符尾朝上还是朝下并不重要。比四分音符更快的音符,其符尾上还会有水平的符杠。每增加一个符杠,音符的时值就缩短一半。

还有三个音符:

  • 附点二分音符 beats = 3
  • 附点四分音符 beats = 1.5
  • 附点八分音符 beats = 0.75

more notes

标准记谱法中还包括表示不同时长休止的符号,即“休止符”。

Rests

  • 全休止符
  • 二分休止符
  • 四分休止符
  • 八分休止符
  • 十六分休止符

2.4 拍号

在标准记谱法中,音符被分组为称为小节(measures 或 bars)的段落。每个小节包含固定数量的拍,小节内所有音符的时值总和应等于这个数量。小节与节拍之间的关系由一个分数表示,称为拍号。分子(上面的数字)表示每小节的拍数,分母(下面的数字)表示节拍的时值。

Time signature

  • 四四拍(4/4)或 “普通拍子(Common Time)”:每小节有 4 拍,每拍为一个四分音符。这个拍号有时用一个特殊符号 “C” 表示。
  • 二二拍(2/2)或 “切分拍子(Cut Time)”:每小节有 2 拍,每拍为一个二分音符。切分拍子有时用一个中间有一条线的 “C” 表示。
  • 二四拍(2/4):每小节有 2 拍,以四分音符为一拍。
  • 三四拍(3/4):每小节有 3 拍,以四分音符为一拍。
  • 三八拍(3/8):每小节有 3 拍,以八分音符为一拍。

最常见的拍号是 4/4。它非常常见,因此被称为普通拍子,通常用一个“C”符号表示(如上表所示)。在普通拍子中,每小节有四拍,四分音符“占一拍”,即一拍等于一个四分音符的时长。

在标准记谱法中,竖线将小节分隔开。在下面的例子中,有两个四四拍的小节(每小节四拍,每拍为一个四分音符)。

4/4

如果拍号是 3/4,那么每小节有三拍,每拍的时值是一个四分音符。一些 3/4 拍的歌曲例子有《音乐之声》中的《My Favorite Things》、Jay Z 的《My 1st Song》、Jimi Hendrix 的《Manic Depression》以及 Seal 的《Kiss from a Rose》。

3/4

如果那些音符是八分音符,看起来会是这样:

eight notes

其他常见的拍号包括二四拍(每小节有两个四分音符拍)和二二拍(每小节有两个二分音符拍)。在二二拍中,每小节实际上有四个四分音符,因为一个二分音符的时值等于两个四分音符。因此,二二拍的演奏方式与普通拍子类似,但通常速度更快。它被称为切分拍子,用一个中间有一条线的“C”符号表示(见上表)。

你可以通过点击顶部栏中的拍号指示器来调整 TunePad 项目的拍号(见图 2.1)。

2.5 打击乐音色和乐器

在处理节奏时,你会遇到很多关于不同打击乐器和音色的术语。下面简要介绍一些在数字音乐中常用的鼓音色(图 2.6)。

图2.6 典型架子鼓中的鼓。

鼓的名称描述TunePad 音符编号
底鼓(Kick 或 bass drum)底鼓发出响亮、低沉的敲击声。在摇滚、流行、浩室和电子舞曲中,底鼓通常在第 1 和第 3 拍出现。在嘻哈和放克等其他流派中,底鼓也非常突出,但位置更具变化。0 和 1
军鼓(Snare)军鼓发出独特的尖锐断奏声,能贯穿整个频率范围。军鼓内部装有特殊的金属丝(响弦),使其产生独特的沙沙声。军鼓通常在第 2 和第 4 拍使用。2 和 3
踩镲(Hi-hat)踩镲由两片镲片组成,夹在一根金属杆上。通过脚踏板可以控制镲片的开合。闭合时,踩镲发出明亮的敲击声;打开时,镲片可以自由延音。踩镲几乎是所有流行音乐流派节奏中不可或缺的一部分。4(闭合)、5(打开)
桶鼓(Low, mid, high tom)桶鼓是圆柱形的鼓,其声音不如军鼓尖锐。一套鼓组通常有多个音高略有不同的桶鼓(如低、中、高桶鼓)。6、7、8
吊镲(Crash cymbal)一种大型镲片,发出响亮的撞击声,常作为打击乐的强调音使用。9
拍手和沙锤(Claps and shakers)TunePad 的不同鼓组包含多种流行音乐中常见的其他打击乐音色,包括各种拍手声、沙锤声等。10 和 11

2.5.1 808 鼓组

Roland 808 鼓机于 20 世纪 80 年代初推出,在早期嘻哈音乐(以及其他流派)中产生了巨大影响。808 采用电子合成技术来模拟底鼓、军鼓、踩镲、桶鼓、牛铃和边击等鼓音色。爱好者们还会拆开 808 鼓机,对电路进行改造,创造出全新的音色。如今,“808s”通常指的是低沉、轰鸣的 bass 线条,这种音色最初是通过对 808 底鼓进行调整而产生的。TunePad 的默认鼓组使用的采样听起来类似于原始的 808 电子合成音色(图 2.7)。

图2.7 罗兰德 808 鼓组节奏音序器。

2.5.2 选择 TunePad 乐器

在 TunePad 中编程时,代码产生的声音取决于你选择的乐器。如果你在编写节奏,可以从几种不同的鼓组中选择,包括 808 和摇滚鼓组。你可以通过点击下面显示的选择器来更改乐器(图 2.8)。

图2.8 在TunePad中更改乐器的音色。

2.6 用 Python 编写节奏代码

让我们开始编写代码吧!在开始编码之前,有几件事你需要记住。

2.6.1 语法错误

Python 是一种基于文本的语言,这意味着你输入的代码必须遵循严格的语法规则。当你说英语等自然语言时,语法很重要,但你通常可以灵活运用甚至打破规则,仍然能传达信息。当你说一些模棱两可的话时,可能会带有讽刺、幽默或诗意。但在 Python 中并非如此。Python 没有幽默感,也不欣赏诗意。如果你在编码时犯了语法错误,Python 会给出一个称为语法错误的消息。这些消息可能会让人困惑,但它们是为了帮助你修复代码,就像拼写检查器帮助你纠正拼写错误一样。这是 TunePad 中的一个语法错误示例(图 2.9)。

图2.9 TunePad 中 Python 语法错误的示例。这行代码少了一个括号符号。

这行代码缺少一个右括号,导致出现错误消息“第 5 行输入错误”。请注意,Python 会提示问题所在的位置和修复方法,但这些提示并不总是那么有用,对初学者来说可能会令人沮丧。本章后面会让你练习修复语法错误。

2.6.2 控制流

Python 程序由一系列语句组成。在大多数情况下,每条语句单独占一行。Python 会按照你编写的顺序从上到下读取和执行每一行代码。在编程中,这称为控制流。这与你阅读书中的文字或乐谱上的音符的方式类似。不同之处在于,编程语言还有一些特殊规则可以改变控制流。这些规则包括循环(重复执行代码的某些部分多次)、条件逻辑(仅在满足某些条件时执行代码的某些部分)和用户定义函数(允许你创建自己可以调用的函数)。我们将在本书后面讨论这些特殊的“控制结构”。

2.7 调用函数

在 Python 中,你所做的几乎所有事情都涉及调用函数。函数(有时称为命令或指令)告诉 Python 执行某项操作或计算某个值。例如,playNote 函数告诉 TunePad 发出声音。调用函数需要做三件事:

首先,你要写出函数的名称。函数名称是一个单词(没有空格),可以由字母、数字和下划线(_)组成。多单词函数的命名方式有两种:一种是在单词之间使用下划线,例如:

my_multi_word_function()

另一种是每个新单词的首字母大写,例如 playNote()

其次,在输入函数名称后,必须加上括号。括号告诉 Python 你正在调用一个函数。

第三,在左右括号之间包含你想要传递给函数的参数。参数提供额外的信息或告诉函数如何执行。例如,playNote 语句至少需要一个参数来指定要演奏的音符或音色。有时函数接受多个参数(其中一些可能是可选的)。playNote 函数接受几个可选参数,下一节将详细描述。每个额外的参数用逗号分隔(图 2.10)。

图2.10 在 TunePad 中调用 playNote 函数,括号内有两个参数。

2.8 playNote 函数

playNote 函数告诉 TunePad 演奏打击乐音色或乐音。playNote 函数最多接受四个参数,包含在括号内。

playNote(1, beats = 1, velocity = 100, sustain = 0)
名称描述
note这是一个必需参数,指定要演奏的音符或打击乐音色。具体的音色取决于你在 TunePad 中为该代码选择的乐器。你可以通过将多个音符放在方括号中来同时演奏多个音符。
beats可选参数,指定音符的演奏时长。TunePad 的播放头会按照给定的时长向前移动。这个参数可以是整数(如 1 或 2)、小数(如 1.5 或 0.25)或分数(如 1/2)。
velocity可选参数,指定音符或音色的演奏音量。值为 100 是最大音量,值为 0 是静音。在数字音乐中,力度(velocity)是一个专业术语,指的是敲击乐器的速度或力度。你可以把它想象成鼓被敲击的力度不同,发出的音量也不同。
sustain可选参数,允许音符在不推进播放头的情况下额外延音一定的拍数。

2.8.1 可选参数

有时参数是可选的,这意味着如果你不指定,它们会有一个默认值。对于 playNote 函数,只有 note 参数是必需的。如果你不传递其他参数,它会自动使用默认值。你也可以在函数调用中包含参数的名称。例如,下面四行代码做的是完全相同的事情:都演奏一个 1 拍的音符。前两行没有使用参数名称,后两行包含了参数名称,后面跟着等号(=)和参数值。

playNote(60)  # beats 参数是可选的
playNote(60, 1) # 将 beats 参数设置为 1
playNote(60, beats = 1) # 包含 beats 参数的名称
playNote(note = 60, beats = 1) # 包含 note 和 beats 参数的名称

2.8.2 注释

在上面的代码中,有些文本出现在每行的井号(#)后面。这些文本称为注释。注释是程序员添加的自由形式的说明,用于使代码更容易理解。Python 会忽略注释文本,所以你可以在井号后面写任何内容。你也可以在一行的开头使用井号来暂时禁用代码,这称为“注释掉”代码。

2.9 rest 函数

Silence 是音乐中一个重要的元素。rest 函数产生 silence,即声音的中断。它只接受一个参数,即休止的时长。因此,rest(beats = 2) 将触发两拍的休止。如果你不提供参数,rest 的默认值为 1.0。

rest()  # 休止 1 拍
rest(1.0) # 休止 1 拍
rest(0.25) # 休止 1/4 拍
rest(beats = 0.25) # 休止 1/4 拍

2.10 playNoterest 示例

让我们尝试几个 playNoterest 的示例来热身。这个节奏先演奏两个八分音符(beats = 0.5),然后是一个四分音符(beats = 1),接着重复这个模式一次。

warm up1

用底鼓和军鼓在 TunePad 中编写这个节奏的代码如下:

playNote(1, beats = 0.5)  # 演奏底鼓(1)半拍
playNote(1, beats = 0.5)
playNote(2, beats = 1) # 演奏军鼓(2)1 拍
playNote(1, beats = 0.5) # 演奏底鼓(1)半拍
playNote(1, beats = 0.5)
playNote(2, beats = 1) # 演奏军鼓(2)1 拍

另一个示例是先演奏一个四分音符,接着是 0.5 拍的休止,然后是一个八分音符(beats = 0.5)。这个模式连续重复两次:

example2

playNote(2, beats = 1)    # 演奏军鼓(2)1 拍
rest(beats = 0.5) # 休止半拍
playNote(1, beats = 0.5) # 演奏底鼓(1)半拍
playNote(2, beats = 1) # 演奏军鼓(2)1 拍
rest(beats = 0.5) # 休止半拍
playNote(1, beats = 0.5) # 演奏底鼓(1)半拍

第三个示例是连续演奏八个八分音符(beats = 0.5)。试试看你能否写出产生这个模式的代码。

example 3

# 在这里编写你的 Python 代码








2.11 循环

上一节中的所有示例都包含重复的元素。如果你仔细听,会发现在音乐的各个层面都有重复的元素。有重复的节奏模式、反复出现的旋律动机,以及由重复和细化的歌曲段落所定义的故事情节。事实证明,在音乐和计算机编程中,有很多情况下我们希望反复做某件事。

为了展示我们如何利用 Python 的一些功能,让我们以上一节的最后一个示例为例,我们想在踩镲上打出一连串的八分音符。一种编程方式是连续输入八个 playNote(左下方)。

playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)
playNote(4, beats = 0.5)

这确实能完成任务,但这种代码风格存在一些问题。一个问题是它违反了计算机程序员最重要的特质之一——懒惰!一个“懒惰”的程序员是那种工作聪明而不是工作努力的人。“懒惰”的程序员避免做重复性的、容易出错的工作。而且,“懒惰”的程序员知道有些事情计算机比人类做得更好。

在 Python(以及几乎所有其他编程语言)中,当我们想多次做某件事时,可以使用循环。Python 有多种不同类型的循环,但在这种情况下,我们最好的选择是 for 循环。右侧的代码版本连续重复八次。每次循环迭代时,都会调用 TunePad 的 playNote 函数。

对于之前原始代码,我们需要大量输入(或者更可能是复制粘贴)来编写程序——这是一个警告信号,表明我们还不够“懒惰”。我们生成了大量重复的代码,这使得程序更难阅读(可读性差)、容易出错,而且不够简洁。下面的代码只用三行就完成了同样的工作,而上面的代码用了八行。

for i in range(8):
playNote(4, beats = 0.5)
print(i)

最后,原始的代码更难修改和重用。如果我们想用不同的鼓音色(比如用军鼓代替踩镲)呢?或者,如果我们想打出 16 个十六分音符而不是 8 个八分音符呢?我们必须逐行修改代码,这是一个缓慢且容易出错的过程,绝对不是“懒惰”或简洁的做法。

为了理解为什么后面的代码更好,试着修改后面的代码,让它演奏 16 个十六分音符而不是 8 个八分音符。或者尝试把鼓音色从踩镲换成其他音色。第 3 行的 print 语句只是为了帮助你了解代码的运行情况。如果你点击“显示 Python 输出(Show Python Output)”选项,可以看到在第 1 行创建的变量 i 从 0 计数到 7(图 2.11)。

图2.11 如何在 TunePad 单元格中显示代码的打印输出。

下面详细介绍 for 循环的结构(图 2.12):

图2.12 Python 中 for 循环剖析。

一个带有 range 函数的 for 循环:

  • for 关键字开头
  • 包含一个循环变量名称;可以是任何你想要的名称(上面是“i”)。每次循环时,循环变量的值增加 1。
  • 包含 in 关键字
  • 包含 range 函数,指定重复的次数(上面是 16)
  • 包含一个冒号(:
  • 包含一个缩进 4 个空格的代码块

Python 使用缩进来确定循环内部的内容,即需要重复多次的代码部分。缩进的代码块会按照 range 指定的总次数重复执行。让我们在前面的示例中添加一些额外的内容。在下面的版本中,我们在最后一拍添加了一连串的十六分音符。

for i in range(6):
playNote(4, beats = 0.5)

for i in range(4):
playNote(4, beats = 0.25)

但我们还可以做很多其他事情。如果我们想演奏更快的一连串音符,可以使用这样的代码:

for i in range(8):
playNote(4, beats = 0.125)

或者,如果我们想演奏三连音,即将半拍分成三个相等的部分,可以这样做:

for i in range(3):
playNote(4, beats = 0.25 / 3) # 分成 3 部分

如果你在 TunePad 中打开这个示例,可以尝试不同的数字组合来获得不同的效果:

https://tunepad.com/examples/loops-and-hats

2.12 变量

playNote(0)
playNote(4)
playNote(2)
playNote(4)

变量是你在 Python 程序中给某些信息起的名称。你可以把变量看作一种昵称或别名。与循环类似,变量有助于使你的代码更简洁、更易读,并且在将来更容易修改。例如,左侧的代码在不使用变量的情况下演奏一个鼓模式,右侧的代码使用变量演奏相同的模式。注意,变量通过给各种鼓音色赋予描述性的名称(而不仅仅是数字),使代码更容易理解。

kick = 0
hat = 4
snare = 2

playNote(kick)
playNote(hat)
playNote(snare)
playNote(hat)

在上面的版本中,我们在第 1 行定义了变量 kick,第 2 行定义了变量 hat,第 3 行定义了变量 snare。每个变量都被初始化为对应鼓音色的不同数字。也可以在程序的后面通过给变量赋不同的数字来改变它的值。

kick = 0
playNote(kick) # 演奏音色 0
kick = 1 # 把 kick 设置为不同的值
playNote(kick) # 演奏音色 1

变量名称可以是任何你想要的名称,只要它是一个单词(没有空格),并且只包含字母、数字和下划线(_)。变量名称不能以数字开头,也不能与任何现有的 Python 关键字相同。

当你开始熟悉代码并发挥创造力时,你会发现自己想要尝试不同的音色。你可能想为相同的节奏模式尝试不同的音色,也许把踩镲换成沙锤,以获得更自然的感觉。使用变量可以很容易地通过改变值来进行实验。

for i in range(8):
playNote(4, beats = 0.5)
for i in range(8):
playNote(4, beats = 0.25)
for i in range(4):
playNote(4, beats = 0.5)

再看一个踩镲模式的示例。假设你非常喜欢这个模式,但想知道用不同的打击乐音色会是什么效果。也许你想把音色 4 换成沙锤音色(比如 11)。使用变量的好处是,只要 Python 还没有把某个名称用于其他用途,你就可以给变量起任何有意义的名称。这样你可以让名称对你来说有意义。所以,对于沙锤的例子,我们可以创建一个名为 shake 的变量,并将其设置为 11。当你使用变量 shake 时,你插入的是当前分配给它的任何数字。

shake = 11
for i in range(8):
playNote(shake, beats = 0.5)
for i in range(8):
playNote(shake, beats = 0.25)
for i in range(4):
playNote(shake, beats = 0.5)

随着你编程水平的提高,你会发现循环和变量有助于创建更流畅的工作流程,给你更多的灵活性、自由度和创造力。

如果你想练习使用变量,可以尝试这个练习:

https://tunepad.com/examples/variables

2.13 更多关于语法错误

我们已经提到,Python 代码就像一种有严格语法规则的语言。当你在编码中出错时——每个人都会经常在编码中出错——Python 会反馈错误是什么以及大约在第几行。例如,如果你一直在编写本章的练习,可能会看到类似图 2.13 的消息。

图2.13 Python语法错误示例。命令“ployNote”应该写成“playNote”。

这表明第 6 行有一个错误,可以通过将“ployNote”改为正确的来修复。当你在代码中使用变量或函数时,Python 期望你输入的名称与定义时完全相同。一个简单的拼写错误就会导致程序无法运行,但也很容易修复。在这里,我们只需要把这一行改成 playNote 而不是 ployNote

其他语法错误更棘手。图 2.14 中的消息很令人困惑,因为问题实际上在第 1 行,而语法错误提示是第 2 行。问题是第 1 行缺少一个右括号(图 2.14)。

图2.14 Python语法错误示例。此处问题实际上出在第1行,而非第2行。

程序员用来查找这类错误来源的一种方法是注释掉错误前后的代码行。例如,要注释掉上面代码的第一行,我们可以把它改成这样:

# playNote(60
rest(1)

在第一行的开头添加井号意味着 Python 会忽略它,在这种情况下,这修复了语法错误,并为我们提供了关于问题来源的另一个线索。

另一个非常有用的技巧是把你的错误消息原封不动地粘贴到你喜欢的搜索引擎中。有庞大的 Python 程序员社区,他们几乎解决了所有可能出现的代码问题。你通常可以通过浏览几个顶级搜索结果找到问题的快速解决方案。

如果你想练习修复代码中的语法错误,可以尝试 TunePad 上的神秘旋律挑战之一:

https://tunepad.com/examples/mystery-melody

2.14 播放头

TunePad 中音符的时值 timing 由一个称为播放头的对象的位置决定。在音乐制作的早期,录音是使用模拟磁带进行的。来自麦克风或其他来源的声波信号通过一种称为录音头的机制物理地存储在磁带上。当磁带移动时,录音头会在磁带内部刻下磁性材料的图案,从而创建音乐的录音。为了回放录音,播放头会拾取磁带磁性材料的波动,并将其转换回声波供听众聆听。快进到数字领域,我们不再有播放头或录音头,但在提到声音随时间移动的概念时,我们仍然沿用这个比喻。播放头的概念在音频制作软件中很常见,它代表音频正在播放的时间点。

在 TunePad 中,当你使用 playNote 函数放置一个音符时,播放头会按照 beats 参数指定的音符时长向前移动。有几个函数可以获取播放头的位置信息,并使播放头前后移动。

函数描述
getPlayhead()返回播放头当前的位置(以拍为单位)。请注意,getPlayhead 返回的是已经过的拍数,这意味着如果播放头在轨道的开头,该函数将返回 0。如果已经过了 1.5 拍,getPlayhead 将返回 1.5。如果已经过了 40 拍,它将返回 40,依此类推。
getMeasure()返回当前的小节数(整数)。请注意,getMeasure 返回的是已经过的小节数。因此,如果播放头在轨道的开头或第一小节结束之前的任何位置,该函数将返回 0。如果播放头大于或等于第二小节的开头,getMeasure 将返回 1,依此类推。
getBeat()返回当前小节内已经过的拍数(小数)。例如,如果播放头在一个小节内前进了四分之一拍,getBeat 将返回 0.25。getBeat 返回的值始终小于一个小节的总拍数。
fastForward(beats)使播放头相对于当前位置向前移动指定的拍数。请注意,这与 rest 函数相同。负数的 beats 值会使播放头向后移动。
rewind(beats)使播放头向后移动指定的拍数。这对于同时播放多个音符很有用。beats 参数指定播放头移动的拍数。负数的 beats 值会使播放头向前移动。
rest(beats)使播放头向前移动指定的拍数,不发出声音。这与 fastForward 函数相同。
moveTo(beats)将播放头移动到任意位置。beats 参数指定播放头将被放置的位置(以已经过的拍数表示)。例如,moveTo(0) 会将播放头移动到轨道的开头(0 拍)。moveTo(1) 会将播放头放置在第一拍结束和第二拍开始之前的位置。

我们可以使用 moveTofastForwardrewind 命令来控制播放头相对于我们制作的音乐的位置。rewindfastForward 函数相对于当前时间点使播放头向后或向前移动。moveTo 函数将播放头移动到任意时间点。在 TunePad 中,播放头表示已经过的拍数。因此,要移动到轨道的开头,你可以使用 moveTo(0),这意味着 0 拍已过。要移动到第二拍的开头,你可以使用 moveTo(1),这意味着 1 拍已过。这些命令对于在单个 TunePad 单元格中添加多个重叠的节奏很有用。我们将在第 8 章中详细介绍如何使用这些命令。

2.15 基本的鼓模式

现在让我们编写一些基本的鼓模式代码。这里还有一个指向 TunePad 中代码的链接,你可以用来播放这个节拍并将其变成自己的风格。

2.15.1 四四拍底鼓(Four-on-the-floor)

四拍底鼓模式是浩室(House)、电子舞曲(EDM)、迪斯科(disco)和流行音乐中的主要节拍。它有一个强劲的舞曲节奏,由每个节拍上的四个底鼓敲击组成(因此称为“four beats on the floor”)。这个节拍简单但用途广泛。你可以通过移动踩镲的位置以及在意想不到的地方添加底鼓、军鼓和其他鼓来丰富它。我们可以用三种乐器制作基本模式:底鼓、军鼓和踩镲。

four beats on the floor

你可以在以下链接找到这个示例:

https://tunepad.com/examples/four-on-the-floor

# 定义乐器变量
kick = 0
snare = 2
hat = 4

# 放置四个底鼓
playNote(kick)
playNote(kick)
playNote(kick)
playNote(kick)

moveTo(0) # 重置播放头到开头

# 在偶数拍添加军鼓
rest(1)
playNote(snare)
rest(1)
playNote(snare)

moveTo(0) # 重置播放头到开头

# 用循环制作踩镲模式!
for i in range(8):
playNote(hat, beats = 0.5)

2.15.2 布鲁斯(Blues)

Blues

布鲁斯是一种源于非裔美国人经历的音乐流派,最初是田间歌曲,后来演变为灵歌,最终成为布鲁斯。这个节拍是 3/4 拍,即每小节有三拍。你可以在以下链接找到这个示例:

https://tunepad.com/examples/blues-beat

Blues

# 定义乐器变量
kick = 0
snare = 2
hat = 4

# 放置底鼓和军鼓模式(音型)
playNote(kick, beats = 0.5)
playNote(kick, beats = 0.25)
playNote(snare, beats = 0.25)
rest(.25)
playNote(kick, beats = 0.25)
playNote(kick, beats = 0.25)
rest(.25)
playNote(kick, beats = 0.25)
playNote(snare, beats = 0.25)
playNote(kick, beats = 0.25)
playNote(kick, beats = 0.25)

moveTo(0) # 重置播放头到开头

# 添加踩镲
playNote(4, beats = 0.25)
for i in range(3):
rest(0.25)
playNote(hat, beats = 0.25)
playNote(hat, beats = 0.25)
rest(0.25)
playNote(hat, beats = 0.25)

2.15.3 拉丁(Latin)

拉丁节拍以其切分节奏而闻名,强调小节中的“弱拍”。这个鼓模式长达两个小节。我们的底鼓模式听起来很像心跳,为整个节拍奠定了坚实的基础。我们的军鼓演奏的是克拉维(clave)模式,这在许多非裔古巴音乐中很常见,如萨尔萨(salsa)、曼波(mambo)、雷鬼(reggae)、雷鬼顿(reggaeton)和舞厅(dancehall)。在第二小节中,军鼓在第一拍和第二拍的后半拍(计数为 1 和 2.5)有敲击。最后,我们在每个八分音符上添加一个踩镲。

Latin

你可以在以下链接找到这个示例:

https://tunepad.com/examples/latin-beat

# 定义乐器变量
kick = 0
snare = 3
hat = 4

# 放置底鼓,模拟心跳
for i in range(2):
playNote(kick, beats = 1.5)
playNote(kick, beats = 0.5)
playNote(kick, beats = 1.5)
playNote(kick, beats = 0.5)

moveTo(0) # 重置播放头

# 添加军鼓
rest(1.0)
playNote(snare)
playNote(snare)
rest(1.0)
playNote(snare, beats = 1.5)
playNote(snare, beats = 1.5)
playNote(snare)

moveTo(0) # 重置播放头

# 放置踩镲
for i in range(16):
playNote(hat, beats = 0.5)

2.15.4 雷鬼(Reggae)

Reggae

一种常见的雷鬼节拍是“一击(one-drop)”节拍,得名于第一拍没有敲击。相反,重音在第三拍,这使得雷鬼具有强烈的反拍和轻松的感觉。我们的踩镲使用了摇摆八分音符,并在最后一个音符上添加了一个开镲来增加质感。第一个敲击持续三分之二拍,第二个持续三分之一拍。我们的底鼓和军鼓都在第三拍敲击。你可以在以下链接找到这个示例:

https://tunepad.com/examples/reggae-beat

# 定义乐器变量
kick = 0
snare = 2
hat = 4
open_hat = 5

# 一起放置底鼓和军鼓
rest(2)
playNote([kick, snare])

moveTo(0) # 重置播放头

# 放置摇摆踩镲模式
for i in range(3):
playNote(hat, 2.0 / 3) # 三分之二
playNote(hat, 1.0 / 3) # 三分之一

playNote(hat, 2.0 / 3)
playNote(open_hat, 1.0 / 3)

2.15.5 其他常见模式

以下是其他一些不同流派的鼓模式,你可以尝试自己编写代码。

Other Patterns

2.16 鼓机音序器

鼓机音序器是一种用于创建鼓模式的工具。早期的音序器如 Roland 808(图 2.7)是物理硬件。现在大多数人使用基于软件的音序器,尽管基本原理是相同的:音序器看起来像一个网格,行代表不同的鼓音色,列代表短的时间片段(通常是十六分音符或三十二分音符)。

图2.15 TunePad作曲器界面提供了鼓和贝斯音序器。

TunePad 包含一个鼓音序器(如图 2.15 所示),有助于尝试不同的节奏想法(https://tunepad.com/composer)。你可以通过点击网格的灰色方块在不同的时间片段添加鼓音色,一旦你有了喜欢的模式,就可以将其转换为 Python 代码。将鼓音序器模式转换为代码时,按列而不是按行编写代码可能会有所帮助。这意味着我们从左到右浏览鼓模式。对于每一列,我们查看在该时间片段所有发声的音色。然后,我们可以使用一个 playNote 语句来触发这些音色。这是一个简单的示例模式:

Example Pattern

我们看第一列,发现只有一个底鼓。

playNote(0, beats = 0.25)

我们看第二列,发现是空的,所以我们休止:

rest(0.25)

第三列包括一个踩镲(音符 4)和一个底鼓(音符 0)。要同时演奏这些音色,我们可以用一个 playNote 命令,将这些音色放在方括号中,如下所示:

playNote([0, 4], beats = 0.25)  # 底鼓 + 踩镲

这使用了一种特殊的 Python 结构,称为列表,我们将在接下来的几章中详细介绍。现在,你只需要知道这是一种方便的同时演奏多个音色的方法。如果我们继续用这种按列的策略,完整的代码如下:

playNote(0, beats = 0.25)
rest(0.25)
playNote([0, 4], beats = 0.25) # 底鼓 + 踩镲
rest(0.25)
playNote(2, beats = 0.25)
rest(0.25)
playNote(4, beats = 0.25)
playNote(10, beats = 0.25)
rest(0.25)
playNote(10, beats = 0.25)
playNote([0, 4], beats = 0.25) # 底鼓 + 踩镲
playNote(0, beats = 0.25)
playNote(2, beats = 0.25)
rest(0.25)
playNote(4, beats = 0.25)
rest(0.25)

按列编写代码可能会更快一些,并且产生的代码更简洁。

注意:

1 “beat”一词除了其他含义外,还可以指舞曲中的主要节奏(“drop the beat”,意为“奏起节奏”),或嘻哈歌曲中伴唱的乐器演奏部分(“she produced a beat for a new artist”,意为“她为一位新艺人制作了一段节奏”)。

插曲 2 自定义陷阱(Trap)风格节拍

在这个插曲中,你将运用在前一章学到的技能来创建一个自定义的陷阱舞曲节拍。这个节拍将使用底鼓、军鼓和踩镲。你可以通过访问以下链接在线跟随操作:

https://tunepad.com/interlude/trap-beat

步骤 1:定义变量

首先登录 TunePad 并创建一个名为“Custom Trap Beat”的新项目。在你的项目中添加一个新的“鼓(Drums)”乐器。在这个单元格中,声明鼓音色的变量。

# 鼓的变量
kick = 1
snare = 2
hat = 4

步骤 2:基本鼓模式

这是基本鼓模式的代码。将这段代码添加到变量后面的鼓单元格中:

# 底鼓和军鼓
playNote(kick, beats = 0.75)
playNote(kick, beats = 0.25)
playNote(snare, beats = 1.5)
playNote(kick, beats = 0.75)
playNote(kick, beats = 0.75)
playNote(kick, beats = 2)
playNote(snare, beats = 1)

让我们逐行分析这些代码:

行号鼓的名称鼓的编号拍数Python 代码
1底鼓13/4playNote(kick, beats = 0.75)
2底鼓11/4playNote(kick, beats = 0.25)
3军鼓21.5playNote(snare, beats = 1.5)
4底鼓13/4playNote(kick, beats = 0.75)
5底鼓13/4playNote(kick, beats = 0.75)
6底鼓12playNote(kick, beats = 2)
7军鼓21playNote(snare, beats = 1)

完成后,你的模式应该看起来像图 2.16。

图2.16 基本鼓点节奏。

步骤 3:添加踩镲卷轴和断续音(stutters)

现在为你的项目添加一个新的鼓单元格,用于踩镲卷轴和断续音。为了添加踩镲卷轴,让我们先回顾一下 Python 中的 for 循环(图 2.17)。

图2.17 在 Python 中声明用于踩镲连奏的 for 循环。

for i in range(4):
playNote(hat, beats = 0.25)

for i in range(4):
playNote(hat, beats = 0.25 / 2)

for i in range(8):
playNote(hat, beats = 0.25)

for i in range(5):
playNote(hat, beats = 0.25 / 5)

playNote(hat, beats = 0.25)

你的单元格现在应该有一个像图 2.18 这样的模式。

图2.18 踩镲碎拍奏型。

步骤 4:自定义

在尝试了步骤 3 中的示例后,创建你自己的断续音模式,与你的底鼓和军鼓相配合。你可以使用任何拍数组合,但要确保总和是四拍的倍数,这样你的节拍才能正确循环!以下是一些以不同速度演奏断续音的 for 循环示例:

# 二连音 couplet
for i in range(2):
playNote(hat, beats = 0.25 / 2) # 分成两半

# 三连音 triplet
for i in range(3):
playNote(hat, beats = 0.25 / 3) # 分成三部分

# 四连音 quad
for i in range(4):
playNote(hat, beats = 0.25 / 4) # 分成四部分

# 五连音? fifthlet?
for i in range(5):
playNote(hat, beats = 0.25 / 5) # 分成五部分

通过改变变量的值和切换到不同的鼓组来尝试不同的乐器音色。你也可以尝试改变速度。为了获得更多灵感,这个 TunePad 项目有几个流行的嘻哈节拍模式,你可以进行实验:https://tunepad.com/interlude/drum-examples