oss

Csound 中的音符有多种时钟

10 条来源 9 条一手来源 已翻译 2026年9月20号

正在加载阅读与收藏统计…
正文
Csound 会议的与会者在维也纳 mdw 合影。

2024 年 9 月,第七届国际 Csound 会议在维也纳 mdw 举行,图为与会者合影。摄影 © mdw/Titas Lasickas,图片来自项目的会议报道。[9]

按住合成器的一个音符,慢慢打开滤波器。听来连贯的变化,到了计算机里,却要靠几项工作配合完成:启动音符、更新滤波器、计算波形,并在扬声器需要之前送达采样数据。开源音频编程语言 Csound 将这些工作划分得格外清楚。沿着一个音符在其中的流转,就能看出,一件响应灵敏的乐器,除了较小的音频缓冲区,还要顾及其他环节。[1][4][7]

本文依据 Csound 6 参考手册展开,旧版文档标明的版本号为 6.18.0。[10] 讨论集中在沿用已久的乐谱与乐器集模型,较新的 Csound 7 语法留在本文范围之外。这里值得追问的是:各种音乐指令,分别在哪个环节转化为计算机要做的工作。

乐谱事件借用乐器定义

在 Csound 中,一份描述如何发声的定义称为乐器(instrument)。乐器由称为操作码(opcode)的处理单元组合而成,包括振荡器、滤波器、包络及其他运算。这些定义合在一起,组成乐器集(orchestra)。引擎组织好各自的处理链,再根据乐谱或实时事件将它们激活。[1]

乐谱中的一行可以简短到只有 i 1 0 2 0.15 440。开头几个字段要求 1 号乐器在零时刻开始,持续两拍。若乐谱语句和命令行选项都保持默认速度,这里的拍数便按秒数解释。余下的数值各有什么音乐含义,要由 1 号乐器自行规定。例如,它可以把 p4 用作振幅,把 p5 用作频率。[3]

共享乐器定义时,这一区别尤其需要说清。某一列里的 440 之所以代表频率,是因为接收它的代码按频率解释这个数值。把同一份乐谱交给另一件乐器,若后者在这个位置接收的是 MIDI 音符编号,数值的含义就变了。事件与声音定义之间的接口,建立在这样一份小小的约定上。

同一份乐器定义可以供时间上重叠的多个音符使用,每个普通音符都会分配到独立的数据空间。因此,一个三音和弦可以共用一份合成器定义。[3] 乐谱负责指定实例及其时间安排,乐器则规定这些实例共有的行为。

合在一个文件里,分工依然清楚

乐器集与乐谱可以分别存放,也可以一同打包进 .csd 文档。在最外层的 CsoundSynthesizer 元素之内,CsOptions 存放启动选项,CsInstruments 存放乐器定义,CsScore 存放乐谱指令。[2]

放在同一文档里,两者的关系也更容易查看。修改乐谱,同一件乐器就能演奏另一段乐句;修改乐器,同一组事件就能获得另一种声音。文件合为一份,各部分仍各司其职。

Dave Phillips 在 2005 年 4 月发表于 Linux Journal 的文章中考察了 Csound 5,也记录了项目从两个配套文件转向统一文档的变化。[8] 这份独立的历史报道给出了一条有用的线索:音乐事件与声音合成早已有清楚而持久的分工,单一项目文件的便利建立在这层分工之上。即使事件来自图形界面,这个较早的模型仍有研究价值。

字母前缀标明时间尺度

在传统 Csound 乐器代码里,变量前缀说明数值何时改变。i 变量在初始化阶段确定;k 变量以控制速率更新;a 变量存放音频速率的数据。[4]

以上面的简单音符为例,初始化变量可以保存起始音高,控制信号可以描述不断变化的滤波器设置,音频信号则保存最终送往输出端的波形。选用哪种变量,取决于各部分在时间上需要多细的刻画。

这种记法背后,还有一个容易忽略的实现细节。音频速率变量存放的是采样向量,每轮控制处理都会处理其中的一组采样。Csound 可以在一轮处理中计算一整块音频,同时为每个采样保留各自的数值。控制速率的标量则在同一时段内只给出一个值。[4] 音频按块处理,块内每个采样的数值依然完整保留。

乐器集中的 ksmps 设置,决定一个控制周期包含多少个音频采样。其关系为 kr = sr / ksmps,其中 sr 是采样率,kr 是控制速率。[5] 以 sr = 48000ksmps = 32 为例,每秒有 1,500 个控制周期,每个周期约为 0.667 毫秒。若设为 ksmps = 128,每秒便有 375 个周期,每个约为 2.667 毫秒。这些数值由文档中的关系式推算得出,属于计算示例。

两种设置下,音频仍然每秒采样 48,000 次,改变的是控制值更新的间隔。把这两种分辨率混在一起,就难以解释这样一种现象:录下的波形采样率符合预期,听到的参数扫动却有台阶感。

平滑变化也有专门的运算

Csound 的 interp 操作码让这种区别变得可听。它在相邻控制值之间做线性插值,将控制信号转换为音频信号。手册特意用一个很大的控制块演示其效果:振幅变化在一件乐器里听来粗糙,另一件乐器对包络做了插值,变化便较为平滑。[6]

插值补上的是已有数值之间的过渡;对于两次输入更新之间演奏者做了什么,它掌握的信息仍限于收到的输入值。这里要分清两项操作:平滑参数的变化,以及更频繁地采集参数。音乐人有时两项都需要,但平滑效果只能说明前一项,采集频率要另行确认。

对乐器开发者而言,可以据此做一个有明确目标的实验:保持采样率固定,先比较不同控制块大小的效果,再比较参数经过插值和保持原样时的差别。先听清扫动本身如何变化,再判断音频设备对这些差异有多少影响。

扬声器还有自己的时限

采样计算完成后,还要送出引擎。手册区分了内部的 ksmps 块、由 -b 控制的软件输出缓冲区,以及与 -B 相关的设备端缓冲。[1] 因此,0.667 毫秒的控制周期只反映内部处理节奏,实际演奏的总延迟还包括后续环节

延迟优化指南将这些设置视为需要配合调整的一组参数,结果取决于平台、硬件和乐器的复杂程度。缓冲区越小,留给等待的时间越少,系统在数据交付时限上的余量也越小;音频出现杂音或断续,就显出了系统跟不上处理进度的时刻。[7] 参数变化平滑,输出仍会有断续;播放保持稳定,演奏时也仍会感觉到延迟。

我的实践理解是,要在预期负载下评估乐器。独自创作、将作品渲染成文件的作曲者,可以接受计算耗时超过音乐时长。小型演出团队则需要有人用实际的交互界面和音频配置排练,包括音符最密集的和弦,以及彼此重叠的释放尾音。手册也专门建议,调整缓冲区时要用较长的释放阶段检验系统。[7] 单个短音符播放顺利,所验证的承受能力仍限于这一负载。

项目的 2024 年会议照片里,使用这套软件的人们聚在一起,分享演讲、装置和音乐作品。[9] 放回这样的活动现场,各部分的职责便有了具体含义。乐谱安排一个声音何时出现,乐器定义它如何变化,输出系统则负责按时送达声音。Csound 把这些分工呈现得足够清楚,于是,迟到的音符、带台阶感的操作变化,以及夹着噼啪声的和弦,就成为三个可以分别追查的问题。

参考资料

  1. Csound 6 参考手册,“How Csound works”——操作码、乐器处理链、实时与文件输出,以及三层缓冲。
  2. Csound 6 参考手册,“Unified File Format for Orchestras and Scores”——.csd 容器及其中的选项、乐器和乐谱部分。
  3. Csound 6 参考手册,“i Statement”——乐器激活、参数字段、拍数的解释,以及时间上重叠的音符实例。
  4. Csound 6 参考手册,“Types, Constants and Variables”——初始化、控制速率、音频速率,以及一轮控制处理中的音频向量。
  5. Csound 6 参考手册,“ksmps”——每个控制周期的采样数,以及采样率与控制速率之间的关系。
  6. Csound 6 参考手册,“interp”——控制信号的线性插值,附有振幅包络的对比示例。
  7. Csound 6 参考手册,“Optimizing Audio I/O Latency”——依系统情况调整缓冲区、音频杂音与断续,以及重叠释放尾音的测试。
  8. Dave Phillips,“At the Sounding Edge: What's Going On with Csound?”,Linux Journal,2005 年 4 月 14 日——关于 Csound 5 与统一项目文件的独立历史报道。
  9. Csound,“ICSC 2024 is over”,2024 年 12 月 24 日——维也纳会议报道及署名 mdw/Titas Lasickas 的照片。
  10. Csound Community,The Canonical Csound Reference Manual——标明版本为 6.18.0 的旧版手册索引。
Previous WeeWX 把气象记录留在家中

Recommended In oss

Matched by subject and format