使用PipeWire取代PulseAudio

renyuneyun 2021年06月25日(周五) 1 mins

几周前在这篇博文中偶然发现PipeWire这个声音系统,其中对Carla的展示让我眼前一亮——我之前就想要是有这么个图形化交互,那么配置PA时候会直观且省事很多。

稍微多看了看,发现它是Red Hat所开发(PulseAudio似乎也是RH开发的),(号称)用来取代PulseAudio和Jack这两个声音系统的软件——(号称)目标是提供兼顾最终(普通)用户和高级/底延迟需求的用户的一套系统。另外,它也不只支持音频,也支持图像,似乎是为了在Wayland上共享屏幕而这么考虑的。

最近花了一点时间,用它取代自己一直在用的PulseAudio,发现还挺简单的。更换后有许多优点,也有(如果处理不妥善的话)个别软件的兼容问题。正巧我想起来自己以前在配置声音系统上遇到许多坑,所以这里稍微讨论一下Linux上的声音系统(我用过的几个),以及我切换到PipeWire和玩耍的过程。

Linux的声音系统

由于各种各样的历史和许可证原因,Linux上有许多和声音相关的部件,包括但不限于ALSAOSSPulseAudioJack。虽然断断续续用过一年多的Ubuntu,但我刚接触Arch的时候对这些东西还是一头雾水,看到它们只有一个念头:我就是想让电脑出个声,怎么就这么难。

本来我以为它们只是一层搭一层或并列的关系,就好像大部分部件一样。但我花了一个小时,最后了解到它们不是这样,而是互相取代互相竞争但又不可互相取代的关系。基本可以被这张当时发现的老图概括:

Linux声音系统关系图

不过这么些年过来,我现在重新去看的时候,还是理清了大部分的头绪的:

  • ALSA和OSS是内核层级的组件,负责直接和声音设备通信,并提供接口以便上层使用。
    因为声音设备是设备,所以ALSA/OSS它们必须存在;因为声音设备是设备,对设备的使用是独占的,所以需要更上层的组件来收集所有的应用需求,以和本层组件进行通信。 之所以存在两个,是历史和许可证/商业原因:原本几乎都用OSS,但OSS 4闭源了,所以社区转向了ALSA,结果后来OSS 4又开源了。
  • PulseAudio处于应用层的底层,负责和内核层级的ALSA和OSS通信,并提供接口以便上层使用。
    蓝牙之类的外部设备,也一般都通过PulseAudio进行连接处理。
  • Jack和PulseAudio所处层级有些类似,但一般认为更专注于高阶使用,尤其是实时、低延迟需求的场景。

上面这是整体情况,在一些细节上可能需要更仔细考量。比如说,OSS 4其实提供了非独占使用的能力;又比如Jack其实一般不去取代PulseAudio而是和PulseAudio共存——实际上Jack比PulseAudio稍微低一点,但还是比ALSA/OSS高。

Note

另外,我偶然间还看到了PulseAudio的论文,里面说PulseAudio本身就没考虑取代Jack,而是考虑到「支持Jack」(也是实情)。但最有意思的是该论文说PulseAudio设计中考量了低延迟的通信,这句话如果成立,那么我很好奇为什么现在Jack仍然有大量用户。

对于大部分情况(或者说对于几乎所有的普通用户),使用ALSA+PulseAudio是最佳的选择,不出意外可以开箱即用。下图比较好地展示了PulseAudio所处的层级:

PulseAudio架构

于是,ALSA提供声音设备的驱动支持以及低级调用;PulseAudio允许你分应用程序调整输入输出(音量、输出设备、平衡等),支持蓝牙音频设备,可以调整声音效果(噪声消除、合并输出等);大部分软件应当可以自动通过PulseAudio输入/输出音频。当然,如果你不使用DE,那么需要注意在用户环境手动启动PulseAudio的daemon,或者使用systemd服务。

Note

PulseAudio还有更高级的用法,比如远程(网络)输出,多用户共享一个PulseAudio daemon之类,或许需要手动配置。但普通情况下用不到这些。

所以……PulseAudio不好在哪?

既然我前面说ALSA+PulseAudio是大部分情况下的最佳选择,而且我也一直在这么用,那么我为什么要切换到PipeWire呢?

我之前的需求主要有这么几项:

# 噪声消除 # 监听麦克风 # 分别控制不同程序的输出 # (同一程序)输出到多个设备 # 蓝牙

这些都可以通过PulseAudio完成,但有的有些麻烦。比如监听麦克风需要载入PulseAudio模块,但很显然我不会始终都需要监听,所以这么模块不能像噪声消除一样添加到配置文件中;我用过的所有GUI控制程序又不直接提供这个选项(虽然按说没有难度,只是没人加),所以需要每次手动执行命令。而噪声消除会创建更多的设备,设备名又往往很长,不总是好找。而且噪声消除的模块叫做echo cancel(回声消除),我一直没找到关于它到底是回声消除还是噪声消除的解释。

而我最近,因为打算开会时候播放视频,想要同时共享音频,这个新需求通过PA不好完成——虽然有方法,但实在是好麻烦

于是就想起来了PipeWire那个patchbay的程序,觉得可以考虑切换一下试试——虽然Carla是Jack上的,但切换到Jack也是切换,我干嘛不直接换到一个打算取代PulseAudio和Jack而且已经比较成熟的软件呢?

切换到PipeWire

因为我用的是Arch,官方仓库已经包含了PipeWire的主要包,所以直接安装就行:

# pacman -S pipewire pipewire-pulse

这里的pipewire-pulse是PipeWire对PulseAudioAPI的兼容层,目测作用是使得外部程序可以将PipeWire当作PulseAudio来使用。 许多人也会推荐顺便安装Jack的兼容层,也就是加上pipewire-jack;也可以额外安装pipewire-alsa

然后,理所当然地,我看到了pipewire-pulsepulseaudio冲突。于是果断先N,停下安装,先关掉并禁用pulseaudio的服务和socket(不关闭socket的话我的KDE会自动重新启动pulseaudio的服务)。

$ systemctl --user stop pulseaudio.{service,socket}
$ systemctl --user disable pulseaudio.{service,socket}

Note

其实如果不装pipewire-pulse的话,PipeWire是可以和PulseAudio共存的。

当然你也完全可以不先清理pulseaudio,但那样的话要么手动处理(而非通过systemd的命令),要么就得登出再登入了。

然后重新执行安装,安装完后启动PipeWire的相关服务:

$ systemctl --user start pipewire pipewire-pulse pipewire-media-session

Note

pipewire-pulse是对PulseAudio的兼容层服务,不启动的话不会被当成PulseAudio使用。我的KDE似乎只认PulseAudio。 pipewire-media-session是管理会话/授权的服务。不启动的话我的KDE无法探测到声音设备。

这时候,我的KDE就会自动探测到新的声音设备了:

Plasma上显示的声音设备

测试了一下,输出正常。

玩耍

Helvum——尝试patchbay

既然输出正常,那么就要测试输入。既然如此,不如直接安装一个patchbay来处理。

于是去AUR装了helvum,一个专门为PipeWire设计的patchbay软件:

yay -S helvum

我顺便放了个音乐,打开helvum看看:

Helvum的patchbay界面

能直接拖拽连线还是很方便的,想要监听麦克风那就直接把麦克风和音频输出连起来就好:

Helvum上监听麦克风

我的截图中设备名称比较好认,但那其实是我调整后的结果。我修改了~/.config/pipewire/media-session.d/alsa-monitor.conf这个文件,在里边针对我常用的设备调整了一下它的名称:

rules = [
  {
        matches = [
          {
                node.name = "~^alsa_input.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi___ucm0001.hw_sofhdadsp_6__source$"
          }
        ]
        actions = {
          update-props = {
                node.nick = "Laptop Mic"
          }
        }
  },
  {
        matches = [
          {
                node.name = "~^alsa_input.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi___ucm0001.hw_sofhdadsp__source$"
          }
        ]
        actions = {
          update-props = {
                node.nick = "Headphone Mic"
          }
        }
  },
  {
        matches = [
          {
                node.name = "~^alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi___ucm0001.hw_sofhdadsp__sink$"
          }
        ]
        actions = {
          update-props = {
                node.nick = "Laptop"
          }
        }
  }
]

这个文件的路径我是参考这个例子的,但我不确定是否这是最佳选项,但至少在我这里工作正常。细节另外可以参考官方wiki,且有一些关于文件的额外说明。

wireplumber——另一个会话管理器

然后我试了一下在官方仓库(community)中的wireplumber,用来取代默认的pipewire-media-session.service

它提供了似乎更好的对音频输出的分类,自动创建了「通知」、「音乐」等多个类别,且似乎会自动识别播放声音的软件和对应的类别。这样的设计更贴近移动设备常用的分类,省去了分应用每次重新调整大小的麻烦,我挺喜欢的。新版本似乎移除了这个特性,所以我又用回了默认的服务。

按前面博文 所说,pipewire-media-session是官方实现,Wireplumber是另一个人开发的(第三方实现?),但官方在考虑长期的未来切换到全面使用Wireplumber。

pulseeffects——噪声消除等

虽然名称叫pulseeffects,但它其实是搭配PipeWire的。我稍微看了一眼历史,似乎以前确实是为了PulseAudio设计的,但在v5版本后就转向支持PipeWire了。

这个软件的使用很直观,打开之后直接用就行。我主要需要噪声消除,于是切换到麦克风标签,然后将噪声消除打开(顺便切换了一下顺序,不过不太要紧)。

试了一下,效果还不错,而且还可以调级别。看它的说法,似乎噪声消除是基于RNNoise这个东西,我并不熟悉。不过从名称猜大概是基于RNN的噪声消除?

Pulseeffects的界面

它下面还有个WebRTC的项目,我试了一下,但暂时还没有完全理解。

总结与残留问题

整体而言,我觉得切换到PipeWire是比较简单的过程(但我由于缺少说明走了一点弯路),而且切换过后也没有什么难题(除了还没测试的蓝牙)(蓝牙自动可用,只要打开pipewire-pulse.service)。而切换过后带来的便利(尤其是patchbay)却是实打实的。

除去下面提到的没能解决的问题外,我会倾向于使用PipeWire来取代|PA|的——虽然现在我必须用Teams所以不得不暂时换回|PA|。(Teams问题已解决。)

我并非什么追求音质的人,手上按售价算最贵的耳机是别人送我的AKG 451,所以在这个方面没有太多发言权。但至少我测试下来的听感上,切换前后没有什么差别,也理应没有什么差别——声音系统不过是将音频数据的数字信号转换为发送给耳机的相应数字或模拟信号,难道不应该一模一样么(或比如蓝牙进行压缩)?

当然,切换过来也有个别暂时没能解决的问题的。

比如说,我的笔记本内置麦克风有时会有「喀喀」的声音,大概三四秒一次,但有时候就又完全没有(如果有就会一直有,直到我停止监听麦克风)。但好在耳机的麦克风没有问题,于是对我影响不大,毕竟笔记本麦克风对外界噪音的采集更大一些,我本来就不太用。

另外,我用Teams的时候,在使用pipewire-pulse的情况下,Teams可以检测到麦克风,但无法检测到扬声器,这就很奇怪了。而更有意思的是,Teams的通知、拨号音完全正常,只是通话有问题。而如果使用pulseeffects创建虚拟的输出,这时候我这边Teams会无法顺利建立通话,而对方则一切正常,可以看到我(但听不到)。按Electron的这个issue中提到的信息,这是因为Teams自己包进去一个不知什么版本的Electron。目前我没找到解法,只能观察着|PipeWireIssue|。PipeWire的这个issue中,我找到了临时解法:在前面提到的alsa-monitor.conf中对应设备内增加这么两行设置:

node.pause-on-idle = false
audio.format = "S16LE"

其中最重要的是第二行,第一行似乎可以去掉(不过我还是留下了)。加上之后,重启服务,Teams就可以正常侦测到设备了,也可以正常使用了。不过使用pulseeffects需要特别注意一下,就是要在Teams建立通话以后打开pulseeffects,且不要将Teams的输入输出设备换成pulseeffects的。虽然比较麻烦和诡异,但至少可以达到预期目的,也就暂时接受了。

再比如一个不算是「异常」,只是问题的事:在Helvum中看,pulseeffects会生成新的输入输出「设备」,但似乎是每次新增一个输出源(比如新开了个播放器)都会生成新的。我还没理解究竟是怎么回事。


Related posts:

您可以在Hypothesis上的該群組內進行評論,或使用下面的Disqus評論。