PipeWire/示例
環繞聲系統
分離前置與後置聲道
當使用 PipeWire 替代 PulseAudio/JACK 時,你可以配置 PipeWire 來複製 PulseAudio 的前置/後置分離示例。這樣一來,你就可以將使用 PulseAudio 的音頻流發送到揚聲器或耳機的獨立輸出端(Sink)。
將揚聲器連接到線路輸出(line-out)埠,耳機連接到後置埠。在 pavucontrol 中將所使用的音效卡設置為 Analog Surround 4.0 Output。
使用 PipeWire 配置文件
創建額外的 PipeWire 配置文件 ~/.config/pipewire/pipewire.conf.d/speakers-headphones.conf 或 /etc/pipewire/pipewire.conf.d/speakers-headphones.conf,寫入以下內容並根據實際情況修改 target.object 行:
context.modules = [
{ name = libpipewire-module-loopback
args = {
node.name = "speakers"
node.description = "Speakers"
capture.props = {
media.class = "Audio/Sink"
audio.position = [ FL FR ]
}
playback.props = {
target.object = "alsa_output.pci-0000_00_14.2.analog-surround-40"
stream.dont-remix = true
node.passive = true
audio.position = [ FL FR ]
}
}
}
{ name = libpipewire-module-loopback
args = {
node.name = "headphones"
node.description = "Headphones"
capture.props = {
media.class = "Audio/Sink"
audio.position = [ FL FR ]
}
playback.props = {
target.object = "alsa_output.pci-0000_00_14.2.analog-surround-40"
stream.dont-remix = true
node.passive = true
audio.position = [ RL RR ]
}
}
}
]
重啟 PipeWire 後,你應該會在 pavucontrol 中看到兩個新的輸出。類似的示例可以在 [[1](https://docs.pipewire.org/page_module_loopback.html) PipeWire Loopback 模塊文檔]中找到。
如果你使用的是 pipewire-pulse包,你可能還需要通過編輯 ~/.config/pipewire/pipewire-pulse.conf 或在 /etc/pipewire/pipewire-pulse.conf.d/ 中創建配置文件來啟用上混音(upmixing):
stream.properties = {
channelmix.upmix = true
channelmix.upmix-method = psd # none, simple
channelmix.lfe-cutoff = 150
channelmix.fc-cutoff = 12000
channelmix.rear-delay = 12.0
}
這些選項及更多的解釋可以通過以下命令查看:
$ man pipewire-props
使用 pactl 和 pw-link
使用以下命令為揚聲器和耳機創建新的輸出端,並將揚聲器連結到前置聲道、將耳機連結到後置聲道:
pactl load-module module-null-sink sink_name=speakers object.linger=1 media.class=Audio/Sink channel_map=FL,FR pactl load-module module-null-sink sink_name=headphones object.linger=1 media.class=Audio/Sink channel_map=RL,RR
object.linger=1 可以使輸出端在創建它的客戶端斷開連接後保持存活。你可以將 sink_name 命名為任意名稱。
要卸載模塊,你可以使用 pw-cli destroy ID,其中 ID 是 pactl load-module 命令的輸出。目前暫不支持通過 pactl unload-module 單獨卸載模塊 [[2](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/709)]。不過,你可以使用 pactl unload-module module-null-sink 來卸載所有 module-null-sink 模塊。
使用 jack_connect,將新輸出端的監聽器連接到音效卡的播放埠。通過運行 pw-link -iol 來查明聲道的名稱 [[3](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Migrate-JACK)]。
pw-link speakers:monitor_1 alsa_output.pci-0000_00_14.2.analog-surround-40:playback_FL pw-link speakers:monitor_2 alsa_output.pci-0000_00_14.2.analog-surround-40:playback_FR pw-link headphones:monitor_1 alsa_output.pci-0000_00_14.2.analog-surround-40:playback_RL pw-link headphones:monitor_2 alsa_output.pci-0000_00_14.2.analog-surround-40:playback_RR
alsa_output.pci-0000_00_14.2.analog-surround-40 替換為你音效卡的名稱。在腳本執行前添加延遲也可能會使運行更加平穩。alsa_output.pci-0000_00_14.2.analog-surround-40:playback_FL
有時會變成 Built-in\ Audio\ Analog\ Surround\ 4.0:playback_FL。作為臨時變通方法,你可以在自動啟動腳本中添加第二套使用備用名稱的 pw-jack 命令。要單獨控制音量,一種選擇是使用 ALSA 工具(例如 amixer(1))來控制 Front(前置)和 Rear/Surround(後置/環繞,ALSA 命名)聲道。可以在[[4](https://gist.github.com/nicman23/a2a80200e1d5d1f65b75846c5f2387b7) 此處]找到一個根據當前默認 PulseAudio 輸出端自動執行該操作的腳本。
回聲消除
PipeWire 可以實時從麥克風中去除揚聲器的聲音,這使得在不使用耳機的情況下參與語音聊天成為可能,甚至在其他應用程式正在播放音頻時也是如此。
通常,語音聊天應用程式確實會消除回音,但它們只能識別經過自身的音頻。例如,如果另一個語音聊天參與者通過你的揚聲器講話,聊天應用程式會「感知」到這一點,並能夠選擇性地從你的麥克風中擦除這種噪聲,否則該噪聲會作為煩人的回聲被重複發送回語音聊天中。這種方法的弊端通常在於其他應用程式通過你的揚聲器播放音頻時——因為語音聊天無法感知這些音頻,其他參與者可能會聽到並抱怨。示例如下:
- 在使用獨立語音聊天應用程式的同時玩網絡視頻遊戲
- 在使用語音聊天應用程式的同時使用同步視頻播放解決方案,例如 Jellyfin SyncPlay 或 Watch2Gether
這就是系統級回聲消除所要解決的問題;你不再讓語音聊天應用去抑制回聲(且在上述情況下會失效),而是讓 PipeWire 來完成,它能夠天然地「感知」揚聲器上播放的所有音頻。
假設 PipeWire 採用默認配置,可以通過在 /etc/pipewire/pipewire.conf.d/ 中創建一個以「.conf」結尾且世界可讀的配置文件來啟用系統級回聲消除,例如 60-echo-cancel.conf:
「aec.args」的默認值可以在 [[5](https://gitlab.freedesktop.org/pipewire/pipewire/-/blob/master/spa/plugins/aec/aec-webrtc.cpp) 此處]找到,只需在 「aec-webrtc.cpp」 中搜索 「webrtc.」 即可。
context.modules = [
# 回声消除
{ name = libpipewire-module-echo-cancel
args = {
# 监听模式:PipeWire 的回声消除模块不创建所有应用程序都必须播放的虚拟输出端,
# 而是直接从当前的默认(fallback)音频输出读取需要消除的音频
monitor.mode = true
capture.props = {
node.name = "ec_capture"
node.description = "Echo-cancellation capture"
media.name = "Echo-cancellation capture"
# 要从中消除回声的音频源/麦克风
# 可以通过以下命令查找:pw-cli list-objects Node | grep node.name
# 可选;如果未指定,模块将使用/跟随默认音频源
#node.target = "alsa_input.usb-UGREEN_Camera_UGREEN_Camera_SN0001-02.analog-stereo"
# 被动节点:当此捕获空闲时,不要使麦克风保持活动状态
node.passive = true
# 强制图中的输入流量子(quantum)
# 如果遇到语音失真/爆音,可以调整此项
# 默认:0/未设置
#node.force-quantum = 256
}
# 要从输入中过滤掉的输出端
# 可以通过以下命令查找:pw-cli list-objects Node | grep node.name
# 可选;如果未指定,模块将使用/跟随默认音频源
#sink.props = {
# node.target = "alsa_output.pci-0000_0f_00.4.analog-stereo"
#}
source.props = {
# 提供消除回声后的麦克风音频的虚拟音频源
node.name = "source_ec"
node.description = "Echo-cancellation source"
media.name = "Echo-cancellation source"
}
aec.args = {
# WebRTC 回声消除引擎的设置
# 增益控制:动态麦克风音频归一化
# 默认:false
# 注意,PipeWire WebRTC 源代码建议不要启用它:
# > 注意:AGC 似乎会干扰无关延迟检测(Agnostic Delay Detection),特别是
# > 对于语音,会导致非常差的性能,默认禁用
#webrtc.gain_control = true
# 扩展滤波器:放宽音频延迟窗口(?)
# 默认:true
# 引用已废弃的 Mozilla Positron 项目(2016年)的旧源码:
# > 扩展滤波器模式使我们能够灵活地忽略系统报告的延迟。
# > 我们对那些我们认为其提供结果与 AEC 期望不符的平台这样做。
# 建议:除非必要,否则将其关闭
webrtc.extended_filter = false
}
}
}
]
諸如此類的配置更改需要 重啟 PipeWire(即 pipewire.service 和 pipewire-pulse.service 用戶單元)才能生效。
語音處理
通過在濾波器鏈(filter chain)模塊中使用均衡器(EQ)、高通濾波器、除嘶聲器(de-esser)和壓縮器,可以提高電容麥克風的語音清晰度。
安裝 lsp-plugins-lv2包 和 calf包。在 ~/.config/pipewire/pipewire.conf.d/ 中創建一個 .conf 文件(或者修改你存放濾波器鏈模塊的配置文件),使用以下模板:
context.modules = [{
name = libpipewire-module-filter-chain
args = {
filter.graph = {
nodes = [
# 高通濾波器 - 消除約 80Hz 以下的噴麥低頻雜音
{
type = builtin
name = hp
label = bq_highpass
control = {
"Freq" = 90
"Q" = 0.707
}
}
# 除嘶聲器 (LSP)
{
type = lv2
plugin = "[6](http://calf.sourceforge.net/plugins/Deesser)"
name = deesser
control = {
detection = 0 # 0=RMS, 1=Peak
mode = 1 # 0=Wide, 1=Split
threshold = 0.18 # ~ -15 dB 線性值
ratio = 4.0
laxity = 15
f1_freq = 6500.0 # "Split" 分頻點
f2_freq = 6500.0 # "Peak" 檢測中心
}
}
# 語音均衡器 (Calf)
{
type = lv2
plugin = "[7](http://calf.sourceforge.net/plugins/Equalizer5Band)"
name = eq
control = {
ls_active = 1
ls_freq = 100.0
ls_level = 0.668 # -3 dB,減少低頻渾濁感
p1_active = 1
p1_freq = 300.0
p1_q = 1.0
p1_level = 0.794 # -2 dB,減少箱聲/盒音
p2_active = 1
p2_freq = 3000.0
p2_q = 1.0
p2_level = 1.259 # +2 dB,增加臨場感
hs_active = 1
hs_freq = 9000.0
hs_level = 0.708 # -3 dB,修飾高頻刺耳聲
}
}
# 壓縮器 (Calf)
{
type = lv2
plugin = "[8](http://calf.sourceforge.net/plugins/Compressor)"
name = comp
control = {
threshold = 0.125 # ~ -18 dB 線性值
ratio = 3.0
attack = 10.0
release = 100.0
makeup = 1.585 # ~ +4 dB 線性值
}
}
]
# 這可以與 PipeWire wiki 頁面上詳細介紹的降噪插件結合使用,
# 只需取消下面 links[] 行的注釋並更改輸入即可
links = [
#{ output = "rnnoise:Output" input = "gate:Input" }
#{ output = "gate:Output" input = "hp:In" }
{ output = "hp:Out" input = "deesser:in_l" }
{ output = "deesser:out_l" input = "eq:in_l" }
{ output = "eq:out_l" input = "comp:in_l" }
]
#inputs = [ "rnnoise:Input" ]
inputs = [ "hp:In" ]
outputs = [ "comp:out_l" ]
}
capture.props = {
node.name = "capture.voice_processing"
node.description = "Voice processing capture"
media.name = "Voice processing capture"
node.passive = true
audio.rate = 48000
audio.position = [ FL ]
# 如果與上面的降噪示例結合使用,請使用
# node.target 來指定輸入,而不是使用定位到輸入默認源的 stream.capture.sink
#node.target = "source_ec"
stream.capture.sink = true
}
playback.props = {
node.name = "voice_processing_source"
node.description = "Voice processing source"
media.name = "Voice processing source"
media.class = "Audio/Source"
audio.rate = 48000
audio.position = [ FL ]
}
}
}]
要調整各種插件的數值,請使用電壓/振幅分貝(dB)公式:linear = 10^(dB / 20)
將附加音頻混入麥克風音頻中
上面的回聲消除示例可以進行擴展,以提供一個將音頻複製到你的麥克風中的虛擬輸出端。
這是對 PulseAudio/Examples#Mixing additional audio into the microphone's audio 的重新實現,並解決了相同的用例。
為實現這一點,你需要額外加載兩個「Combine stream」(合併流)模塊實例,如下所示。
目前,在每次重啟或 PipeWire 重啟後,該設置[[9](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3710) 需要用戶在例如 Helvum 中手動操作]才能完成;請參閱配置示例中的 「TODO」 注釋。
context.modules = [
# (系統級回聲消除配置,見上文)
# 音頻效果輸出端(立體聲)
{ name = libpipewire-module-combine-stream
args = {
combine.mode = sink
node.name = sink_fx
node.description = "Effects sink (play shared audio here)"
combine.props = {
audio.position = [ FL FR ]
}
stream.props = {
# 如果你在 client.conf.d 中有上混音配置,請在此處設置相同的
# 參數,否則你的音效應用程式將不會在本地音頻輸出中被上混音
#channelmix.upmix = true # (...)
# 可能的替代方案:窮人的立體聲上混音,即把前置聲道鏡像到後置揚聲器
#combine.audio.position = [ FL FR FL FR ]
#audio.position = [ FL FR RL RR ]
}
}
}
# 主源(Main source)
# 提供將以下音源混合在一起的虛擬源:
# - source_ec(回聲消除後的源)
# - sink_fx.monitor(音頻效果輸出端的監聽器)
{ name = libpipewire-module-combine-stream
args = {
combine.mode = source
node.name = source_main
node.description = "Main source (record from here)"
#combine.latency-compensate = false
combine.props = {
audio.position = [ FL FR ]
}
stream.rules = [
{ matches = [
{
node.name = "source_ec"
media.class = "Audio/Source"
}
]
actions = {
create-stream {
}
}
}
# TODO 包含匹配和動作的塊,用於匹配 sink_fx 的監聽器並將其連接到 source_main
# 目前尚無自動化此操作的已知 PipeWire 配置
# 有關最新消息,請參閱此 PipeWire 問題:
# [10](https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3710)
# 目前,請在 Helvum 中手動添加所需的連接,即連接這些點:
# - sink_fx.monitor_FL -> source_main.output.input_FL
# - sink_fx.monitor_FR -> source_main.output.input_FR
]
}
}
]
將音頻流傳輸到 Android 設備
可在 Android 上使用的 [[11](https://github.com/kaytat/SimpleProtocolPlayer) Simple Protocol Player] 應用程式可以通過 [[12](https://docs.pipewire.org/page_module_protocol_simple.html) PipeWire 簡單協議]接收音頻流。這允許將主機上的音頻播放通過 TCP 路由到 Android 設備。
創建一個 PipeWire 配置文件 ~/.config/pipewire/pipewire.conf.d/android-streaming.conf,內容如下:
context.modules = [
{
name = libpipewire-module-protocol-simple
args = {
# 启用捕获模式(向外发送音频)
capture = true
playback = false
# 音频格式设置
audio.rate = 48000
audio.channels = 2
# TCP 地址和端口
# 替换为 Android 设备的 IP 地址
server.address = [
"tcp:12345"
]
capture.props = {
# 替换为你的输出设备名称
# 可通过以下命令查找:pw-cli list-objects Node | grep node.name
target.object = "alsa_output.usb-Creative_Technology_Ltd_SB_X-Fi_Surround_5.1_Pro_0000007E-00.analog-stereo"
stream.capture.sink = true
}
}
}
]
重啟 PipeWire 以應用配置。在 Android 設備上,打開 Simple Protocol Player 應用並連接到配置埠上的主機 IP 地址。