在学习五线谱时,我们习惯将时间视作横向的空间:小节线将时间切片,音符占据一定的宽度。 在 DAW(如 Cubase/Logic)中,时间是线性的:播放头(Playhead)从左向右扫过,触发沿途的事件。
但在 Sonic Pi 中,时间是代码执行的休止符。
当你按下 Run 时,并非生成了一条完整的音频轨道,而是启动了一系列并行(Concurrent)的线。这就像你在指挥一个乐队,你不是在录音带上剪辑,而是给每位乐手(鼓手、古筝手、大提琴手)分别发了一张谱子,并告诉他们:“你们同时开始,每人按自己的循环去演奏。”
本章的核心目标是让你从“剪辑师”思维转变为“指挥家”思维:
use_bpm)在物理世界,时间以秒为单位。在音乐世界,时间以拍(Beat)为单位。Sonic Pi 的 sleep 命令默认以拍为单位,而不是秒。
默认情况下,Sonic Pi 的 BPM(Beats Per Minute)为 60。 此时:
sleep 1 = 1 拍 = 1 秒。当你设定 use_bpm 120 时:
sleep 1 = 1 拍 = 0.5 秒。sleep 0.5 = 0.5 拍 = 0.25 秒。Rule of Thumb:心理映射表 对于习惯读谱的你,请死记以下对应关系(假设拍号为 4/4):
| 代码指令 | 音乐术语 (Note Value) | 常见用途 |
|---|---|---|
sleep 4 |
全音符 (Whole Note) | Pad 长音、锣声残响 |
sleep 2 |
二分音符 (Half Note) | 贝斯根音、人声长腔 |
sleep 1 |
四分音符 (Quarter Note) | 底鼓 (Kick)、行进的低音 |
sleep 0.5 |
八分音符 (8th Note) | Hi-hat、古筝分解和弦 |
sleep 0.25 |
十六分音符 (16th Note) | Zimmer 式弦乐短奏 (Spiccato)、滚奏 |
sleep 1.0/3 |
三连音 (Triplet) | 史诗战鼓、爵士摇摆感 |
注意:三连音必须写成
1.0/3而不是1/3。因为在编程中整数除法1/3会等于0,而1.0/3会得到0.3333...。
速度决定了作品的物理惯性。
live_loop 是 Sonic Pi 最伟大的发明。它不仅仅是“循环”,它是实时可编辑的独立线程。
想象你在写两行五线谱:
在 Sonic Pi 中,你定义两个 live_loop。当你按下 Run,它们同时启动,并在各自的轨道上无限奔跑。
时间轴 (Time) --->
Live Loop :drums (线 1)
|-- Kick -- sleep 1 -- Snare -- sleep 1 --| (回到开头) ↺
| |
v v
Live Loop :flute (线程 2)
|-- G4 -- sleep 0.5 -- A4 -- sleep 0.5 -- E4 -- sleep 1 --| (回到开头) ↺
在其他编程语言中,修改代码通常需要“停止 -> 编译 -> 重启”。 在 Sonic Pi 中:
live_loop 演奏。这就是为什么它是“Live”的。你可以像 DJ 一样,在演出过程中把鼓点从 4/4 拍改成 3/4 拍,而不会出现音乐中断或爆音。
cue / sync):指挥棒的作用这是新手遇到最大的坑:“为什么跑着跑着,鼓和旋律就错开了?”
假设你有两个循环:
Sonic Pi 的 sync 命令是一个阻塞(Blocking)命令。
当程序执行到 sync :name 时,它会立刻停止执行,进入休眠状态,死死地等待,直到它收到了一个名为 :name 的 cue 信号。
为了写出像电影配乐一样严谨对齐的作品,不要让乐器互相 sync(比如不要让吉他去 sync 鼓,万一你想把鼓静音呢?)。 你需要建立一个看不见的“指挥家”。
代码范式(请背诵):
# 1. 建立指挥家 (Metronome)
live_loop :meter do
sleep 4 # 每 4 拍一个小节
end
# 2. 乐器 A (Kick)
live_loop :kick do
sync :meter # 等待指挥挥下!
sample :bd_haus
sleep 1 # 即使这里写错了导致没对齐...
sample :bd_haus
sleep 1.1 # <--- 故意写错
# ...下一轮开始前,sync 会强制它重新等待指挥棒
end
# 3. 乐器 B (Piano)
live_loop :piano do
sync :meter # 同时也等待指挥棒,确保与 Kick 在同一点启动
play :c4
sleep 4
end
解析:
即使 :kick 内部的时间算错了,它跑完一轮后,会回到开头的 sync :meter。如果此时 :meter 还没走完 4 拍,:kick 就会停下来等;如果 :meter 已经发出了信号,它就会瞬间对齐。
这保证了所有乐器永远在小节线(Downbeat)上重合。
电脑的精确是音乐的敌人。
爵士或古风里的“摇曳感”,本质上是把连续的两个八分音符变成了“长-短”组合。
在 Sonic Pi 中,我们不需要手动算,可以使用 use_swing (新版特性) 或手动构建 helper 函数(经典做法,利于理解)。本阶段建议手动控制:
# 手动 Swing 示例
live_loop :guzheng_swing do
# 第一个音长一点
play :c5
sleep 0.3
# 第二个音短一点
play :d5
sleep 0.2
end
对于古风独奏(Solo),我们不希望它完全对齐。
可以使用 rtand(min, max) 在 sleep 时间上增加极微小的随机量。
sleep 0.25 + rrand(-0.02, 0.02)
这点微小的误差(+/- 20毫秒)是人耳识别“这是真人”还是“这是 MIDI”的关键阈值。
这是 Zimmer / Junkie XL 风格配乐的秘密武器。通过不同长度的循环叠加,制造“一直在变但又有规律”的听感。
3 对 4 (Hemiola):
视觉化:
Loop 4: X . . . X . . . X . . . (稳)
Loop 3: X . . X . . X . . X . . (急)
Result: X . . X X . X . X . X . (复杂的复合节奏)
在 Sonic Pi 中实现极其简单:
sleep 4。sleep 3。这种技法在描写“两军对垒”或“内心纠结”的场景时极为有效。
sleep 控制的是线程挂起的时长。sleep 1 = 四分音符的直觉。古风偏慢(60-90),诗偏快(100+)。live_loop 发送 cue,其他所有乐器 sync 它,以保证永远对齐。sleep 时间制造 Swing 和 Humanize,是打破“机器味”的关键。习题 1:建立你的第一个指挥家 编写代码:
:metronome 循环,每 4 拍 sleep 一次(不需要发声,Sonic Pi 的 loop 机制本身就是对齐点,但为了显式控制,可以使用 dummy loop)。:kick 循环,sync 那个指挥家,并演奏 4 下底鼓。
提示:思考 sync 应该放在 loop 内部的哪里?习题 2:古风的“板眼”练习 设定 BPM 60。
:bd_tek)。:elec_tick 或 :drum_cymbal_closed,用 amp 调小音量)。习题 3:简单的 3 对 2 练习
习题 4:Hans Zimmer 的“行动”模式(16 分音符推进) 很多动作片配乐都有一个从未停歇的“哒哒哒哒”基底。
:ostinato 循环。sleep 0.25)。tick 和取余数 % 操作符,或者简单的 if 判断,或者把 4 个音写成一组重复 4 次。习题 5:古风“散板”前奏模拟(随机时值与留白) 我们要模拟一个古琴乐手在酒醉后的即兴。
:pluck 或 :guit_em9 音色。scale(:c4, :pentatonic))。习题 6:同步灾难修复
(这是一个脑力调试题)
阅读以下代码,找出为什么 :melody 永远不会发声?
live_loop :beat do
sample :bd_haus
sleep 1
end
live_loop :melody do
sync :beats # <--- 注意这里
play :c4
sleep 1
end
提示:检查名字的拼写。
象:点击 Run,没有任何报错,但某个乐器就是不响。
原因:通常是因为 sync 的目标拼写错误(如习题 6),或者 sync 的目标循环本身崩溃/未运行。
调试:在 sync 语句前加一行 print "Waiting...",在之后加一行 print "Resumed!"。如果你只看到 Waiting 却没看到 Resumed,那就是卡在 sync 上了。
现象:Sonic Pi 界面卡死,甚至需要强制关闭软件。
原因:live_loop 中没有 sleep,或者 sleep 0。
原理:计算机试图在 0 秒内执行无限次指令,CPU 瞬间 100%。
防范:养成习惯,写 live_loop 先写 sleep 1,再写其他的。
现象:你以为你改了速度,但有些循环没变。
原因:use_bpm 如果写在 live_loop 内部,它只影响该循环。如果写在外部,它通常只影响后续读取的代码。
最佳实:尽量在文件最顶端设定全局 BPM。如果需要变速,使用 set_bpm(后续章节讲解)或在所有循环内引用同一个变量。
原因:节奏太整齐(Quantized)。 解决:这是“代码音乐”的通病。在第 3.5 节学到的 Humanize 技巧(微小的随机 sleep)和长混响(Reverb)是解决这个问题的核心手段。不要让每个音都精准落在 1.000 秒上。