sonic_pi_tutorial

第 5 章:旋律写作与动机发展——从“能哼”到“能撑住一段”

5.1 开篇:旋律的三大物理属性

在 Sonic Pi 中,旋律(Melody)不仅仅是一串 play 命令的列表,它是时间、音高与能量在二维坐标系上的函数。

要写出像样的旋律,你需要关注三个物理属性,并学会控制它们:

  1. 轮廓(Contour / Shape)
    • 音高线是上行(积累能量)、下行(释放能量)、还是波浪形(平稳叙事)?
    • 古风特征:常有大跳(五度/八度)后级进下行。
  2. 密度与呼吸(Density & Breath)
    • 单位时间内有多少个音符?
    • 戏腔特征:字少腔多,一个字可能会拖延 2-4 拍,中间包含复杂的音高变化。
  3. 核心动机(The Seed / Motif)
    • 全曲的 DNA。你不需要一直在发明新旋律,你只需要繁殖同一个种子。

5.2 动机(Motif):万物生于 2-5 个音

动机是音乐中最小的、具有独立性格的结构单元。 初学者的最大误区是试图写出长达 16 小节不重复的“流利旋律”,结果往往是一笔流水账。大师的做法是:写好 3 个音,然后重复它、折磨它、改变它。

5.2.1 动机的算法化变形

假设你的种子动机是 C 大调的 [:c4, :d4, :e4] (Do-Re-Mi),节奏是 [0.5, 0.5, 1]。在 Sonic Pi 中,我们可以通过算法逻辑通过以下方式发展它:

  1. 模进 (Sequence/Transposition)
    • 定义:保持形状,改变起跑线。
    • 操作:将所有音符 + 2(在调内移动)。
    • 结果[:e4, :f4, :g4]。这是推进情绪最稳妥的方法。
  2. 逆行 (Retrograde)
    • 定义:倒着放。
    • 操作.reverse
    • 结果[:e4, :d4, :c4]。常用于句子的“回复”或“收尾”。
  3. 倒影 (Inversion)
    • 定义:像照镜子一样,上行变下行。
    • 操作:以第一个音为轴,原来的 +2 变成 -2
    • 结果[:c4, :b3, :a3]。这种变化听起来有联系,但情绪截然不同(通常更阴郁)。
  4. 节奏增值/减值 (Augmentation/Diminution)
    • 定义:拉长或缩短时间。
    • 操作:将 sleep 时间 * 2/ 2
    • 久石让技巧:在副歌高潮时,将主歌的一句快速旋律拉长,变成宏大的弦乐线条。

Rule of Thumb (动机法则) 一段 8 小节的旋律 = 动机 A (2小节) + 动机 A’ (模进/重复, 2小节) + 动机 B (发展/破坏, 2小节) + 结尾 (2小节)。


5.3 问答句与呼吸:古风/戏腔的“句读感”

华语古风和戏腔旋律非常讲究“句法”。它不像欧美流行音乐那样一直用 16 分音符填满,而是像念诗一样,有逗号句号

5.3.1 问答模型 (Call and Response)

       Question (问) - 悬挂感               Answer (答) - 归属感
      /                     \             /                   \
     /   "床前明月光..."      \           /    "疑是地上霜..."  \
    /                        \         /                      \
---|--------------------------|-------|------------------------|--> Time
   ^           ^              ^       ^           ^            ^
 起音        高潮点         半终止   起音        平稳区       全终止
 (Do)        (Sol/La)       (Re/Sol) (Do/Mi)                 (Do)
  1. 前句(问)
    • 走向通常向上扬,像抛出一个问题。
    • 落点(半终止):千万不要落在主音(Do)上!
    • 古风常用落点2 (Re) —— 最具中国色彩的悬挂音;5 (Sol) —— 宏大的悬挂音。
  2. 后句(答)
    • 走向:向下沉,像回答问题。
    • 落点(全终止):稳稳落在主音 1 (Do)6 (La)(如果是小调色彩)。
  3. 留白(The Void)
    • 在问句和答句之间,必须有空隙。
    • 在 Sonic Pi 代码中,这意味着你的 sleep 总和要大于音符的 release 总和。
    • 戏腔秘籍:在这个空隙里,让 Delay(回声)和 Reverb(混响)去填补。“余音绕梁”指的就是这个空隙。

5.4 倚音、滑音、回音:用最少音符做出“腔”

为什么你的代码写出来的旋律像“机器人”?因为真实的乐器(笛子、二胡)和人声(戏腔)从来不准时、从来不直。

我们要用 Sonic Pi 的参数来模拟这些“不完美”

1. 倚音 (Grace Note) —— “未见其人,先闻其声”

在主音到达之前,极快地掠过一个装饰音。

2. 滑音 (Portamento / Glissando) —— 戏腔的灵魂

戏腔从不直着唱一个音,它总是“滑”向那个音,或者在尾音“滑”走。

3. 颤音与虚指 (Vibrato)

长音如果不动,就是死音。


5.5 五声音阶与调式色彩

虽然 scale(:c, :pentatonic) 很方便,但要写出味道,你需要理解两种核心调式:

1. 宫调式 (Gong Mode) —— 大调色彩

2. 羽调式 (Yu Mode) —— 小调色彩

进阶:偏音的用 (4 和 7) 真的不能用 4 和 7 吗?可以,但要作为经过音(Passing Tone)。


5.6 久石让风格:朴素与留白

久石让(Joe Hisaishi)的旋律之所以动人,往往是因为他不做作

  1. 极简动机:《Summer》的开头只是几个跳跃的断奏;《One Summer’s Day》的开头只是简单的四度下行。
  2. 和声内音 (Chord Tones):他的旋律音大部分时间都乖乖地待在和声里,给人安全感。
  3. 色彩音 (Color Tones):他会在关键的长音上,使用 9音 (Re)6音 (La),而不是主音。
    • :C 大调和弦背景下,旋律长长地停在 D (Re) 上(形成 C add9 听感),那种“明的忧伤”瞬间就出来了。
  4. 共同音 (Common Tone):当背景和弦剧烈变化时(例如 C -> Ab),旋律保持唱同一个音(例如 C 音)。这会产生一种“在变幻世界中保持初心”的听感。

5.7 Hans Zimmer 风格:旋律作为纹理

Hans Zimmer 的逻辑完全不同。对他来说,旋律不是用来哼唱的,而是用来服务节奏和音响的。

  1. Ostinato (固定音型)
    • 一段极快的(16分音符)、像机器一样重复的短句。
    • 作用:它不再是旋律,而是变成了“带音高的节奏”。
    • 写法:使用 ticklook 在一个紧凑的 live_loop 中循环 3-4 个音。
  2. 极慢的长线条
    • 在飞速奔跑的 Ostinato 上方,漂浮着极慢的铜管或弦乐。
    • 可能 4 个小节只换 2 个音。
    • 张力来源:不是靠换音符,而是靠 Cutoff (滤波器开合)Amp (音量推大)
  3. 单一音高重复
    • 就像在敲摩斯密码。比如《盗梦空间》或《沙丘》,有时就是一个音 Du... Du... Du...,靠的是背后的和声在变,从而改变这个音的色彩含义。

5.8 编程实现:数据结构的选择

在 Sonic Pi 中写旋律,建议把音高和时值分开管理。这比 play_pattern_timed 更灵活,允许你对“音高”做数学运算而不影响“节奏”。

# 推荐工程写法:分离数据与执行
use_bpm 68

# 1. 定义数据 (Rings)
# 一个典型的古风羽调式动机
m_notes = (ring :a4, :c5, :d5, :e5, :d5, :c5) 
# 对应的时值 (注意最后是长音)
m_times = (ring 0.5, 0.5, 1.0, 0.5, 0.5, 3.0) 

# 2. 定义演奏逻辑 (Function)
define :play_melody do |notes, times, transpose_amount|
  # 获取当前需要播放的次数 (这里简单取 note 的长度)
  notes.length.times do
    # 获取音符和时间
    n = notes.tick(:n)
    t = times.tick(:t)
    
    # 播放 (加入人性化随机)
    if n != :r # 如果不是休符
      use_synth :pluck
      # 技巧:release 略长于 sleep,制造连奏感(Legato)
      play n + transpose_amount, release: t * 1.2, amp: rrand(0.7, 0.9), cutoff: rrand(80, 110)
    end
    
    sleep t
  end
end

# 3. 在 Live Loop 中组装结构
live_loop :main_theme do
  # 第一次:原样播放 (A段)
  play_melody(m_notes, m_times, 0)
  
  # 第二次:模进,低八度重复 (A'段) - 增加厚度
  play_melody(m_notes, m_times, -12)
  
  # 第三次:移高五度,推向高潮 (B段)
  play_melody(m_notes, m_times, 7)
  
  stop # 演示用,播放完停止
end

5.9 本章小结

  1. 旋律是算法:通过模进重复逆行,你可以用 3 个音繁殖出整首曲子。
  2. 古风/戏腔核心
    • 音阶:多用 1 2 3 5 6,慎用 4 7。
    • 句法:问句(悬挂在 2 或 5),答句(解决到 1 或 6)。
    • 装饰:倚音打头,滑音收尾,中间留白给混响。
  3. 风格二分法
    • 久石让 = 优美线条 + 9音/6音色彩 + 共同音保持。
    • Zimmer = 极快 Ostinato + 极慢长音 + 滤波器动态张力。
  4. 数据结构:将音高 Ring 与时值 Ring 分离,是编写可复用、可变形旋律的关键。

5.10 练习题

基础题(50%)

  1. 基础变形:定义一个包含 4 个音的 ring [:c4, :e4, :f4, :g4]
    • 在第 1 小节原样播放。
    • 在第 2 小节将其 逆行 (.reverse) 播放。
    • 在第 3 小节将其 模进 (所有音 +2) 播放。
  2. 古风五声:只使用 :a4, :c5, :d5, :e5, :g5 (A 小调五声) 写一段 4 小节旋律,要求最后一个音落在 :a4 上。
  3. 倚音模拟:编写代码,在播放主音 :e5 之前,先快速播放一个 :g5 (时值 0.1),模拟笛子的装饰音。

挑战题(50%)

  1. 戏腔尾音设计
    • 编写一个 live_loop
    • 播放一个长音 :a4 (持续 4 拍)。
    • 使用 control 命令,第 2 拍开始,让音高缓慢滑向 :e4,同时让音量 amp 缓慢滑向 0。模拟一句戏腔唱完后的叹息。
  2. Zimmer 式构建 (Build-up)
    • 层 1:写一个 16 分音符的快速 :c3 重复 (Ostinato)。
    • 层 2:写一个极慢的旋律,只有 :c5:d5 两个音交替。
    • 任务:在 8 个小节内,让层 1 的 cutoff 从 60 增加到 120,让层 2 的 amp 从 0.2 增加到 1.0。体验这种非旋律性的能量推进。
  3. 开放题:问答句
    • 创作一段 8 小节旋律。前 4 小节(问)必须让人感觉“没说完”,后 4 小节(答)让人感觉“回家了”。请自行选择调式。

习题参考答案

点击展开参考答案 #### 1. 基础变形 ```ruby live_loop :manipulation do motif = (ring :c4, :e4, :f4, :g4) # 1. 原样 4.times do play motif.tick sleep 0.5 end # 2. 逆行 4.times do play motif.reverse.tick sleep 0.5 end # 3. 模进 (+2) 4.times do play motif.tick + 2 sleep 0.5 end end ``` #### 4. 戏腔尾音设计 ```ruby live_loop :opera_tail do use_synth :saw # 使用 sustain 比较稳的音色 use_synth_defaults attack: 0.5 # 软起头 # 播放主音,持续 4 拍 s = play :a4, sustain: 4, release: 1, note_slide: 2, amp: 1, amp_slide: 2 sleep 2 # 等待 2 拍,保持音准 # 开始滑动:音高下滑,音量淡出 control s, note: :e4, amp: 0 sleep 2 # 等待滑动完成 sleep 1 # 呼吸 end ``` #### 5. Zimmer 式构建 ```ruby live_loop :zimmer_layer1_fast do # 利用 line 生成一个随时间变化的 cutoff 值,周期为 32 拍 (8小节) cut_val = (line 60, 120, steps: 32).tick use_synth :tb303 play :c3, release: 0.2, cutoff: cut_val, amp: 0.5 sleep 0.25 end live_loop :zimmer_layer2_slow do # 利用 line 生成音量变化 amp_val = (line 0.2, 1.0, steps: 2).tick use_synth :prophet # 每 16 拍换一个音 (4小节) play (ring :c5, :d5).look, sustain: 16, release: 2, amp: amp_val, cutoff: 80 sleep 16 end ```

5.11 常见陷阱与错误 (Gotchas)

  1. ticklook 的混乱
    • 如果你在同一个 live_loop 里写了 notes.ticktimes.tick,它们会各自推进,这是对的。
    • 但如果你想获取当前音符对应的时值,第二次必须用 look
    • 错误写法play notes.tick 接着 sleep times.tick。(这样会导致节奏列表跑得比音高列表快一格,或者两者错位)。
    • 正确写法n = notes.tick 然后 t = times.look
  2. 休止符的处理
    • Ring 中不能直接放 nil。建议用 :r0 代表休止符,然后在 play 之前加 if n != :r 的判断。
  3. 滑音时间过长
    • 如果 note_slide 的时间长于音符的实际时长 (sustain + release),那么滑音还没滑完,声音就没了。确保 sustain 足够长以容纳滑音效果。
  4. 五声音阶单调
    • 完全只用五声音阶有时会觉得太“民乐”或太“土”。解决办法是更换和弦。同样的 1 2 3 5 6 旋律,底下配上 IV 级和弦或 VI 级和弦,现代感和电影感马上就出来了。