使用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: