4、操作系统音频栈:从驱动到 API 的延迟真相

各位好,我是老周。今天咱们聊聊操作系统音频栈。说白了,就是你的音频数据从 DAW 出来,到声卡真正发声,中间到底走了哪些路。这条路的长短,直接决定了延迟的高低。

我见过太多人,买了几万块的声卡,结果延迟还是下不去。为什么?因为问题根本不在声卡上,而在你选的音频 API 上。今天我就把这几个主流系统的音频栈给你拆开看看。

核心观点:音频延迟的瓶颈,90% 不在硬件,而在软件栈的缓冲策略和调度方式。选对 API,比换声卡更管用。

4.1 Windows:WASAPI 与 WDM-KS

Windows 的音频栈,说实话,历史包袱很重。从最早的 MME 到 DirectSound,再到现在的 WASAPI,每一层都是为了兼容性而堆叠的。

WASAPI(Windows Audio Session API) 是 Vista 之后主推的音频 API。它分两种模式:共享模式和独占模式。

  • 共享模式:所有应用的声音混在一起,由系统音频引擎处理。延迟通常在 30-100ms,不适合专业用途。
  • 独占模式:应用直接接管声卡,绕过系统混音器。延迟可以降到 10ms 以下。

WDM-KS(Kernel Streaming) 是更底层的方案。它直接通过内核驱动与声卡通信,绕过了所有用户态的处理。我个人的习惯是,在 Windows 上做录音时,优先选 WDM-KS。

我的经验:有一次帮一个直播团队调延迟,他们用的 WASAPI 共享模式,延迟一直在 50ms 上下。我让他们切到 WDM-KS,延迟直接掉到 6ms。主播当场就愣住了。

这里有个坑要注意:WDM-KS 不支持多应用同时使用声卡。你开了 DAW,就不能出声了。所以日常使用还是 WASAPI 共享模式方便,干活的时候再切。

API 延迟范围 适用场景 多应用支持
WASAPI 共享 30-100ms 日常播放、视频通话 是
WASAPI 独占 5-20ms 录音、编曲 否
WDM-KS 3-10ms 专业录音、低延迟监听 否

4.2 macOS:CoreAudio 的优雅与限制

macOS 的 CoreAudio,说实话,是我用过最舒服的音频框架。它从底层就设计好了低延迟路径,不需要像 Windows 那样绕来绕去。

CoreAudio 的核心是 Audio Unit 和 Audio Queue。前者用于实时处理,后者用于非实时播放。做音频开发的人,基本都跟 Audio Unit 打交道。

我记得有一次在 Mac 上做多轨录音,CoreAudio 默认的缓冲大小是 512 帧,延迟大概 12ms。我把缓冲调到 64 帧,延迟降到 3ms,系统依然稳定。这在 Windows 上,同样的缓冲大小,早就爆音了。

注意:CoreAudio 的 kAudioUnitSubType_HALOutput 是低延迟的关键。如果你用 Audio Queue,延迟会高不少。做实时监听,一定要走 Audio Unit 路径。

macOS 还有一个好东西叫 Aggregate Device,可以把多个声卡合并成一个虚拟设备。我曾在项目中用这个功能,把一台 Apogee 和一台 RME 合并,实现了 16 通道的同步录音。延迟控制在 5ms 以内。

4.3 Linux:ALSA 与 JACK 的硬核选择

Linux 的音频栈,嗯,怎么说呢,强大但门槛高。ALSA 是内核层的音频驱动,JACK 是用户态的实时音频服务器。

ALSA(Advanced Linux Sound Architecture) 直接操作硬件,延迟可以做到极低。但它的 API 比较底层,写起来费劲。我一般只在嵌入式设备上直接调 ALSA。

JACK(JACK Audio Connection Kit) 才是 Linux 上专业音频的标配。它提供了一套图钉式的音频路由系统,你可以把任意应用的输出接到任意应用的输入。

我曾经在树莓派上搭过一个 JACK 系统,做网络音频传输。缓冲设到 128 帧,延迟 5ms,跑了一周没出问题。Linux 的实时内核(RT Kernel)在这里起了大作用。

避坑指南:我曾经在 Ubuntu 上装 JACK,默认的 ALSA 后端延迟很高。后来发现是 PulseAudio 在中间插了一脚。解决方案是:用 pasuspender 暂停 PulseAudio,或者直接卸载它。嗯,Linux 就是这样,要么全要,要么全不要。

组件 层级 延迟 特点
ALSA 内核驱动 1-5ms 底层、稳定、配置复杂
JACK 用户态服务 3-10ms 灵活路由、实时调度
PulseAudio 用户态服务 20-50ms 易用、功能多、延迟高

4.4 Android:AAudio 的现代方案

Android 的音频栈,以前是个灾难。老的 AudioTrack 和 OpenSL ES 延迟高得离谱。直到 Android 8.0 引入了 AAudio,情况才好转。

AAudio 是专门为低延迟音频设计的 C API。它直接操作音频驱动,绕过了 Java 层的很多开销。我测试过,在 Pixel 手机上,AAudio 的延迟可以做到 10ms 左右。

但有个问题:AAudio 只在 Android 8.0 以上支持,而且需要硬件配合。很多中低端手机的音频芯片本身延迟就高,软件再优化也没用。

我的建议:做 Android 音频开发,优先用 AAudio。如果必须兼容老版本,用 OpenSL ES,但要做好延迟 30ms+ 的心理准备。另外,记得在 Manifest 里声明 android:hardwareAccelerated="true",能减少一些延迟。

4.5 iOS:AVAudioSession 的封闭生态

iOS 的音频栈,是封闭生态的典型代表。你没法像在 Windows 上那样直接操作驱动,只能通过 AVAudioSession 来配置音频行为。

AVAudioSession 的核心是 Category 和 Mode。Category 决定了音频的行为(播放、录音、混合等),Mode 决定了延迟和路由策略。

  • Playback:延迟较高,适合音乐播放。
  • Record:延迟较低,适合录音。
  • PlayAndRecord:兼顾播放和录音,延迟中等。
  • Measurement:最低延迟模式,用于专业测量。

我做过一个 iOS 的实时效果器 App,用 Measurement 模式,缓冲设到 256 帧,延迟大概 8ms。再低就会爆音了。iOS 的音频调度优先级很高,但缓冲大小还是受限于硬件。

注意:AVAudioSession 的 setPreferredIOBufferDuration 只是建议值,系统不一定会采纳。实际延迟取决于设备型号和当前负载。别指望它能像 CoreAudio 那样精确控制。

4.6 各系统音频栈对比

说了这么多,咱们用一张表总结一下。你想想看,不同系统的设计哲学完全不同,但目标是一样的:在低延迟和稳定性之间找平衡。

系统 推荐 API 最低延迟 易用性 灵活性
Windows WDM-KS 3ms 低 高
macOS CoreAudio (Audio Unit) 3ms 高 中
Linux JACK 3ms 低 极高
Android AAudio 10ms 中 低
iOS AVAudioSession 8ms 高 低

下面这张图,是我自己画的音频栈结构图。你可以看到,从应用层到硬件,每一层都在增加延迟。我们的目标,就是尽量走最短的路径。

操作系统音频栈结构图 应用层 (DAW / 播放器) 音频 API (WASAPI / CoreAudio / ALSA / AAudio / AVAudioSession) 音频缓冲 (Buffer / Ring Buffer) 音频驱动 (WDM / CoreAudio Driver / ALSA Driver) 硬件 (声卡 / DAC / ADC) 延迟来源 缓冲大小 调度延迟 优化方向 减小缓冲 实时优先级

从这张图你能看到,数据从应用层到硬件,每一层都可能成为瓶颈。我个人习惯是,先确定目标延迟,然后从硬件层往上推,看哪一层是短板。

举个例子,如果你想要 5ms 的延迟,硬件和驱动层大概占 2ms,缓冲层占 2ms,API 和应用层占 1ms。如果缓冲层占了 5ms,那你就得调小缓冲大小。但如果驱动层不支持小缓冲,那换 API 也没用。

总结一下:选音频 API 不是选最好的,而是选最适合你设备和场景的。Windows 上优先 WDM-KS,macOS 上走 Audio Unit,Linux 上上 JACK,移动端用 AAudio 或 AVAudioSession。记住,延迟是堆出来的,不是调出来的。