概览 Linux 音频系统

论 Linux 是如何将音乐文件转换成空气的震动.

Information Circle
info

本文于 2022 年 2 月 25 日更新过,添加了 PipeWire 相关介绍。

你很有可能从未仔细研究过 Linux 的音频系统. 在大多数情况下, 在装好图形界面后(或者像 ubuntu 或 openSUSE 这种装好就带桌面环境的发行版里),音频就“自然而然”地能用了。的确,对很多人来说,自带的音频设置已经够好了。

然而,隐藏在这“够好”下面的是一套颇为复杂的音频系统。如果你好奇心大发,想要了解 Linux 是怎么将你的音乐文件变成空气的震动的,抑或遇到了一点关于声音的问题,想要大概了解一下音频系统的构成以便定位问题来源,这里是一篇简短的介绍。

俯瞰音频系统

要让你的喇叭或者耳机动起来,我们需要经历以下的步骤:

首先,我们将音频文件 解码 成未压缩的音频流 (waveform)。现在,为了节省空间,大部分音频文件都是压缩过的,比如无损压缩格式 flac, ape 和有损压缩格式 mp3, aac, ogg 等。只有经过解码这个步骤,才能将这些音频文件变成声卡可以认识的音频流。

然后,我们将这些音频流输出到一个 音频服务器 (sound server) 里。比如,如果你想一边看视频一边参加一个视频会议,你就需要使用一个音频服务器把这两个音频流混合到一起以便让声卡播放。

最终,我们将混制好的音频流送到 声卡驱动 那里。由于所有音轨已经被音频服务器汇成一条,驱动仅需将音频流发送给声卡做最终的数模转换即可。

Linux 下的具体实现

架构已经明晰,那么 Linux 下是哪些部件组成了这一架构的呢?

ALSA: 声卡驱动

严格来说,ALSA 是一套完整的音频处理系统(毕竟它的全名是 高级 Linux 音频架构 )。但在现在的大多数使用情况下,我们只是将它作为一个声卡驱动用。这里主要介绍实际的使用场景。

ALSA 负责直接与声卡通讯。为了方便应用程序开发,ALSA 提供了一套与硬件无关的 API 以便程序能够轻松地设置输出参数并发送音频流。

然而,直接使用 ALSA 有很多不方便。为了使 ALSA 能够同时播放多条音频,用户必须做一些设置 。因此,大部分现代的桌面环境使用音频服务器解决这类问题。

PulseAudio: 你或许正在用的音频服务器实现

如果你正在使用某种桌面环境的话,你大概已经用过位于托盘上面的音量控制控件。这通常就是控制 PulseAudio 服务器的一个前端。

为了将多个程序发来的音频流按照指定的音量混音并发往声卡驱动(通常是 ALSA),PulseAudio 拥有一个内置的混音器。PulseAudio 还提供了一套很适合用户界面的 API(例如各大桌面环境的音量控件)。

然而,由于 PulseAudio 主要针对没什么专业需求的 Linux 桌面用户,它使用一套检测系统来发现系统上可用的音频硬件,并自动决定应该如何配置这些硬件。然而,这些配置在很多情况下并不理想。更糟糕的是,为了省电或节省系统资源,内置的混音器往往不能提供最佳的质量,且会引入相当大的内部处理延迟。

简单得说,对于懒得操心细化的配置且并不需要多么高的音频质量的普通用户来说,PuseAudio 应该已经足够好了。

JACK: 创造者与专业用户的音频服务器

PulseAudio 也许对普通用户来说已经够好,那么对于专业用户呢?

答案是 JACK!从设计角度上来说,JACK 并不是用来当作通用的音频服务器使用的。它是一个 “音频连接工具”(正如它的名字,"JACK Audio Connection Kit" 所言)。在 JACK 的世界里面,每个音频节点(可能是个音频软件,也有可能是一个抽象硬件,比如一个 MIDI 键盘)都可以拥有多个输入源和输出源。举个例子,你可以将你的 MIDI 的键盘的 MIDI 输出连接到 FluidSynth (一个 MIDI 电子合成器)的输入源上,这样在 FluidSynth 的输出源上就会输出合成好的声音。然后将 FluidSynth 的输出源同时连接到一个录音软件(比如 Audacity)和主音频输出上。这样。你就可以一边监听你的演奏,一边将它们录制到音频文件当中去。

而且,由于 JACK 是被设计成用作专业音乐制作的,它内置的混音器的质量极佳。因此,有很多 JACK 用户并不用它制作音乐,而是把 JACK 当作一个质量更好且用起来更灵活的音频服务器使用。想让视频会议上的同事们听听你正在听的音乐?没问题!只要将音乐播放器的输出源接到视频会议软件的输入源上即可。

然而,JACK 没能统治 Linux 桌面是有原因的。相对于 PulseAudio 来说,JACK 需要配置一些参数(例如声卡的比特率,缓冲区大小等等)才能正常工作。如果参数不正确,最终的声音会出现莫名其妙的卡顿和损坏。在此之外,JACK 的灵活性有些时候反而是劣势,毕竟为了达成这种灵活性,JACK 有些情况下需要用户手动配置音频节点的连接方式。

PipeWire: 新希望

严格来说,PipeWire 不仅仅是一个音频服务器,不过它的音频服务器功能十分卓越。相比于 PulseAudio,PipeWire 的 CPU 占用更低,混音器质量更好且处理延迟极低。对于桌面用户来说,PipeWire 对蓝牙音频设备的支持也更加可靠。对于专业用户来说,PipeWire 的音频内部处理延迟已经接近 JACK 了,足以支撑专业音频处理。

更棒的是,PipeWire 完全实现了 PulseAudio 的应用接口。因此,理论上你只需要装上 PipeWire(发行版的包管理器应该会自动卸载 PulseAudio 以防止冲突),所有的程序和控制面板应该都可以完美工作。目前为止(2022年2月,0.3 以上版本)PipeWire 应该已经很稳定了,但是 PipeWire 毕竟是很新的项目,可能还会有一些兼容性问题。

我应该用什么?

如果你一般只听听音乐什么的话,PulseAudio 应该足够好了。如果你遇到了任何音质上面的问题,很有可能只是因为 PulseAudio 没有使用最佳的配置选项,或者有可能是因为你的声卡的驱动有 bug。看一看 ArchWiki 上面的 PulseAudio/Troubleshooting,大多数问题应该很好解决。

如果你想做点音乐的话,JACK 值得一试。你需要一点配置(比如给 JACK 实时权限,找到适合你声卡的配置等等),但配好之后 JACK 的灵活性是无可匹敌的。

如果你觉得 PipeWire 解决了你的痛点的话,PipeWire 值得一试!PipeWire 已经在我的主力机上跑了一年多了,体感没有什么稳定性问题,而且蓝牙连接过程相对 PulseAudio 更可靠。

对于某些特殊情况,比如你只需要运行一个播放音频的软件且需要最小的延迟(a.k.a. 音游),那么你可以试着让应用程序直接使用 ALSA,毕竟这样的话音频将被直接发往声卡,省略了所有中间处理的步骤。或者也可以试试 JACK,毕竟在 JACK 里声音延迟是相对较小且可控的。

发表于 2020-07-21
JS
Arrow Up