要进行延迟补偿,必须先知道延迟有多少。本章展开描述了延迟的多种来源,以及获取与测量延迟的方式。
由于声波是流体中的机械扰动,其传播速度较慢,约为 340m/s。这一点和延迟补偿的问题并不直接相关,但是前提条件在于麦克风和扬声器的正确安置。
然而需要注意的是,很多音乐家都学着习惯一个固定的延迟值。对于机械乐器(如管风琴)而言尤其如此,因为乐器本身的机制就会引入延迟。举例而言,从原声吉他或钢琴(乐器与人的距离约为 50cm)发出声音的延迟约为 1-2ms,这是由声波从乐器传播到人耳的时长决定的;管风琴可能需要一秒才会有反应。
对于安装多个扬声器阵列的大型场地而言,声速也很重要。此时必须添加适当的延迟处理,从而将多个扬声器的信号和舞台上的声音对齐。
电信号传播迅速(约为光速),这时传播时间可以忽略不计,相较之下,模拟信号与数字信号的互转就需要较长时间。转换的延迟通常低于 1ms,在专业器件上更低。
延迟主要来源于处理循环。虽然每次循环处理的采样数通常由用户选择,但硬件、系统架构和音频驱动都会添加限制。
首先,循环时长(译者注:此处指每次循环处理的采样数)通常是 2 的整数次幂。这并不是硬性限制,而是 CPU 和总线接口的直接结果,因为地址空间是通过二进制管理的。选择非 2 的整数次幂作为循环时长通常无益,因为实际传输的分页数会向上取整至邻近的 2 的整数次幂。例如,传输 86 个采样的 CPU 负载和传输 128 个采样的负载相同,不过时限更小。
常见的消费级声卡(如 Intel HDA)会硬性采用每次循环 32 采样的较低限制,但是,即便如此设定,实际上声卡也无法可靠地对毫秒以下的数据块进行读写。每次循环 256 采样以上的值在消费级硬件上常见。虽然有块更小的专业设备,然而实际上,对于通用的计算机而言,每次 8 个采样一下就不现实了。
对于通用计算机而言,还有许多阻碍低延迟的因素。硬件而言主要和省电相关:例如,CPU 频率和总线频率的扩张(scale)会大幅影响最低响应时长。设计不佳的视频接口、WiFi 接口、USB 设备和设备驱动会长时间独占计算机的资源,使音频接口无法跟上数据流的传输。
大多数现代的机器带有系统管理和硬件健康检测的功能(SMI:系统管理中断),无法安全禁用。这些功能的执行也可能花上较长的时间。
计算机必须遵循固定的时长间隔接收并传输音频数据,这一工作会涉及许多的子系统。
音频界扩会通过发送中断信号报告自身完成了数据获取或回放的循环。操作系统必须先接着执行目前活动的任务,直到能够处理中断信号为止。很多操作系统允许将任务优先处理,可以为特定的进程提供更快的响应时间。实时系统(如 Linux-rt)也允许抢占其他中断的处理,从而进一步降低处理优先中断的时长。
操作系统中,切换任务涉及进程上下文的切换,也会进一步引入延迟。上下文切换中既会引入确定的耗时(与 CPU 速度成线性),用于保存、恢复寄存器和栈的过程,也会引入可变的耗时,这一时长由触及的内存决定(TLB 刷新、缓存一致性)。每次上下文切换的影响大概在 10-100 微秒左右。
很多音频处理算法的计算都需要上下文(即处理的采样前后的内容)。传入的音频信号存入缓冲区,当上下文足够时开始处理。这一操作实质上为传入的信号引入了延迟。
处理信号、添加多个效果以及合成由传入事件触发的声音都涉及对信号作数学运算,对于高性能机器而言可能都太苛刻。
多数 90 年代中期开始设计的 CPU 都带有 SIMD(单指令多数据)指令,可以高效对大块数据进行批量处理。最为熟知的就是 SSE(Streaming SIMD Extension)指令集,常见的后继指令集有 SSE2、SSE3、SSE4 和 AVX 等。对大量数据执行相同的运算时,SIMD 指令能大大提升性能,例如,依次处理 32 个采样要比处理一个采样 32 次快得多。
另一耗时因素则是由函数调用引入的开销。很多情况下,DSP 由多个无法内联的函数执行。尽管现代 CPU 上函数调用的开销在纳秒级,但处理小块的缓冲区时还是需要关注的。
块越大,处理块所需的总体 CPU 时间越少。
块的适宜大小由处理算法和性能与开销的考量决定。通常,缓冲区大小在每次循环 64 到 2048 个采样之间。
以上便是使用计算机时延迟的主要来源,不过可以预测,也能进行优化。
由于第 3 节提到的各个组件会进行复杂的交互,获取系统延迟的唯一可靠方式便是进行测量。这一点的典型例外便是单个厂商包揽了系统中所有组件(包括硬件、固件和软件)的情况。例如,一些苹果麦金塔(Macintosh)的机器就属于这一类。
缓冲区大小功能引入系统延迟也是种常见情况。图 8 展示了这一情况对某款 USB 设备的影响。此处的延迟测量是基于闭环音频回路,通过发送测试信号,并在经过整个回路后重新捕获的方式得到的。
闭合回路有几种不同方式:
- 将麦克风放在扬声器附近。这种方式很少使用,因为声音在空气中传播的延迟广为认知,无需进行测量;
- 使用连接线将音频接口的输出和输入联通。由于使用输入输出的特性不同,回路可能是模拟的,也可能是数字的。数字回路无需考虑 AD/DA 转换的延迟。
使用一系列非谐波泛音的连续信号,即可通过观测每个测试音的相对相位,测出精度高于单个采样的实际延迟。在测量 PreSonus Audiobox 1818VSL 的延迟时,使用了一款称为 jack delay 的工具,此工具可以达到约 1/1000 采样的精度。Ardour DAW 的音频设备校准机制便是基于 jack delay 的代码。
图 8 使用 Presonus Audiobox VSL1818,在 48kHz 采样率下测得的往返延迟。
对于工作室的录制工作而言,得到精确的往返延迟是必需工作。
上述闭环对应了这样一种情况,即音频工作站在播放现有的内容时,或生成节拍器提示音时,音乐家在跟着现有内容演奏并录制要演奏乐器的声音。捕获的声音与回放的声音之间的延迟正是一个往返循环的时长。
让问题更为复杂的是,系统延迟可能无法重现,每次计算机的音频子系统进行重置时延迟都会变。这一点主要关系到 USB 设备。USB 总线会施行更多的时间限制(命令放入队列的间隔最高可达 1ms)。Linux 内核的 snd-usb 模块采样的解决方案则是通过引入环形缓冲区,将传输缓冲区和运行时缓冲区分开。这种方式会引入固定的延迟,但在每次设备启动时,延迟的值都不同。虽然无法获取其他驱动的源码,但就测量结果而言,其他设备也应用了相同的机制。只要音频设备一直运行,测量延迟后保留数据流,就不会出问题。
在撰文时,在操作系统的音频 API 中,只有 OSX 的 CoreAudio 允许直接获取硬件延迟,而无需手动测量。然而,报告的延迟值依赖于使用的音频设备和驱动,对于外接的第三方设备而言,可能并不准确。
如果设备带有多个 I/O 流,例如带有 MIDI I/O 的声卡,则多个流通常不会对齐,每个流的系统延迟不尽相同。这一点的典型的例外是 IEEE 1394(火线),其数据流同步,允许将多个流并入同一个数据包中。
同样,当多个时钟同步的设备共用时,每个设备的系统延迟都可能不同。
排除已知量,从测量的回路延迟得知的标称延迟可用于量化系统开销。
关键结论在于,系统延迟是个需要在运行时测量并校准的变量。
大多数数字音频工作站都采用模块化架构。真正的信号处理部分不是由工作站本身进行的,而是由插件提供的。
这一概念可以追溯到 60 年代初的模块合成器,其各个模块不是内部直接布线,而是从外部互连,通常通过使用双端连接线或矩阵接线进行,从而实现复杂的连接方式。在数字系统中,这种单个模块的等价物便是插件:用于扩展环境,由工作站(插件的宿主)载入,额外的第三方软件。
通常而言,插件可以当作其基于硬件设备,如混响、压缩器、延迟效果器、合成器等设备的虚拟设备。这种方式使得宿主程序的开发商专注产品的效率,特定开发商则专注于数字信号处理的部分。“音频插件插件通常自己提供用户界面,其中通常包含 GUI 组件,用于控制和显示插件的音频参数。”
插件的处理算法可能会引入延迟。其原因多种多样,可能是效果器的内在特性,也可能是特定实现导致的。
由于插件的 DSP 是可选的,可能插入到信号流的任意位置,因此它正是延迟补偿复杂性的主要来源。
带延迟的插件分为两个大类:一类是延迟很短,无法直接感知,但是会引入相位效果的插件;另一类是延迟较长,会引入类似回声的插件。
前一类包含均衡器、失真效果器、导波效果器(waveguide)和类似的塑形效果器。虽然可以使用无延迟的数字信号处理实现这一类的多种效果器,很多模型都因为需要重新对齐信号的相位,或是使用 z^-1 反馈实现,需要引入少量音频采样的延迟。其中一例为线性相位均衡器,为对齐相位,需要为信号引入延迟。
后一类包含需要进行重型处理,用缓冲区换 CPU 占用的效果器,以及需要在处理前先进行滤波或分析音频的效果器。为包含低频内容,引入的延迟约为 20Hz(48kSPS 采样率下为 2400 采样)。
其中一种后一类的例子是带前视(lookahead)功能的限制器。这种效果器通常会用平滑的触发和释放曲线限制音频信号的峰值。如果效果器响应过快(即触发时间短),衰减信号太快,便会引入可闻的杂音。带前视的限制器会将一定时长的音频信号存入缓冲区,导致响应出现延迟,增益变化变得平滑。
此外,需要前后内容的效果器都会引入延迟。降噪效果器(de-noiser)、噪声过滤效果器(noise-filter)以及变调器(pitch-shifter)都归入这一类型。这些效果器都需要一块用于运算的内容。需要的数据大小通常是固定的,与处理缓冲区的大小无关。
很多进行矩阵乘法的效果器,例如,基于卷积的箱体效果器(cabinet)和音响模拟效果器(speaker simulator)的实现都会使用固定的卷积核。由于这一实现的本质,缓冲区大小固定,需要使用缓冲区与宿主程序的处理块大小对齐。这一点同样是实现上的取舍,因此大多数卷积效果器都会引入延迟。
延迟可能依赖于配置和参数,因此不是固定值。反馈快、前馈(feed-forward)短的插件,延迟通常是固定的采样数,而需要分析音频的效果器的延迟通常是采样率的整数分之一。延迟基于处理缓冲区大小的情况少之又少。
要进行延迟补偿的一种常见用途是平行压缩(parallel compression),这是一种混音用技巧,其中干信号与干信号经过极端压缩的湿信号混合起来。与一般的上行压缩不同,这种方式会保留干信号的的快速瞬态。然而压缩器可能位于总线上,并接受多个信号源的输入,其引入的延迟需要进行补偿。
由于历史和商业原因,如今有很多插件标准。从抽象的视角看,这些标准都很相似,提供的功能一样。
VST(Steinberg 开发)、Audio Unit(苹果开发,仅适用于 OSX)和 LV2(使用 ISC 许可)是最常见的几种插件标准。更为广泛的讨论不在本论文的范围之内。
上述标准均为插件提供向宿主报告延迟的方式,不过有些微的不同:
- Audio Unit 报告的延迟单位为秒(数据格式为双精度浮点)。可以在实例化插件后异步查询。插件可以使用回调(callback)通知宿主自身的延迟发生了改变。
- VST 允许实例化插件后将插件延迟初始化为定值(数据格式为 32 位整数)。此值名为
initialDelay
,单位为音频采样。 - 对于 LV2 而言,延迟是一个标准的控制输出端口,无需为定值。LV2 标准明确指定,用长度为零的缓冲区调用插件的处理函数视作延迟计算:“作为特殊情况,当
sample_count
为 0 时,插件应更新所有表示时刻的输出端口(如控制端口属于此类,音频端口不属于此类)。这一点对于有延迟的插件而言尤其有用,因为这些插件需要更新延迟输出端口,从而使宿主程序可以预滚(pre-roll)插件,从而计算延迟。”LV2 中,报告延迟的单位为音频采样,与采样率无关,能够以实时安全的方式获取。
无论哪种情况,都推荐宿主在启用插件后询问插件的延迟,因为实例化插件的参数可能会影响效果器的延迟。
当添加插件时,通常此插件会立刻引入延迟。信号经过插件,受到处理后输出需要时间,通常会使信号出现中断,或是引入可闻的杂音。
为缓解这些问题,LV2 的设计更进了一步。由于插件标准可以扩展,根据对应宿主程序的支持不同,添加了不引入声音中断的插入插件机制:插件以旁通模式加入。插件可以先行填充自身的缓冲区,在信号未经处理时于自身内部开始处理。之后解除插件的旁通,即可进行平滑的无中断过渡。插件本身提供这一机制是很重要的。宿主程序无从得知启用或解除 DSP 旁通的正确方式,有效的方法可能是进行交叉淡化,或是于软件内部以特定频率调高参数值,或是在内部状态之间进行切换。由于 LV2 插件将延迟信息作为普通的控制输出提供,这一机制可以优雅地抽象化,但这也使宿主处理潜在延迟更改的操作更为复杂。