PipeWire/示例

出自 ArchWiki

环绕声系统

分离前/后声道

使用 PipeWire 作为 PulseAudio/JACK 的替代品时,你可以设置 PipeWire 来复制 Pulseaudio 中分离前/后声道的示例。这样做允许你使用 Pulseaudio 将音频流发送到单独的输出端,用于扬声器或耳机。

将扬声器连接到线路输出端口,耳机连接到后置端口。在 pavucontrol 中,将声卡设置为模拟环绕 4.0 输出。然后使用以下命令,为扬声器和耳机创建新的输出端,将扬声器连接到前置声道,将耳机连接到后置声道

 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,其中 IDpactl load-module 命令的输出。目前不支持通过 pactl unload-module 卸载单个模块 [1]。但是,你可以使用它来卸载所有 module-null-sink 模块,方法是使用 pactl unload-module module-null-sink

使用 jack_connect,将新输出端的监视器连接到声卡的播放端口。通过运行 pw-link -iol[2] 找出声道的名称。

 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 替换为你的声卡名称。在脚本执行前添加延迟也可能使事情运行更顺畅。
注意: Jack/Pipewire 输出端的名称似乎会不时更改。在上面的示例中,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)来控制前置和后置/环绕(alsa 命名)声道。一个根据你当前默认 pulseaudio 输出端自动执行此操作的脚本可以在这里找到。

回声消除

PipeWire 可以实时消除扬声器声音在麦克风中的回声,这使得即使在其他应用程序正在播放音频时,也可以无需使用耳机参加音频聊天。

通常,语音聊天应用程序确实会消除反馈,但它们只知道通过它们的音频。例如,如果另一个语音聊天参与者通过你的扬声器讲话,聊天应用程序“知道”这一点,并能够有选择地从你的麦克风中消除这种噪音,否则这种噪音会作为令人讨厌的回声重复返回到语音聊天中。当其他应用程序正在你的扬声器上播放时,这种方法的问题往往开始出现,因为语音聊天应用程序不知道这些音频,其他参与者可能会听到并抱怨。示例情况:

  • 在玩在线视频游戏时使用单独的语音聊天应用程序
  • 在使用语音聊天应用程序的同时,使用同步视频播放解决方案,例如 Jellyfin SyncPlay 或 Watch2Gether

这就是系统级回声消除解决的问题;与其让语音聊天应用程序抑制回声——并在上述情况下失败——不如让 PipeWire 来做,它天生“知道”所有在扬声器上播放的音频。

假设 PipeWire 配置为空白,可以通过在 /etc/pipewire/pipewire.conf.d/ 中创建一个世界可读的配置文件来启用系统级回声消除,该文件的名称以“.conf”结尾,例如 60-echo-cancel.conf

“aec.args”的默认值可以在这里找到,只需在“aec-webrtc.cpp”中搜索“webrtc.”。

 context.modules = [
 
     # Echo cancellation
     {   name = libpipewire-module-echo-cancel
         args = {
             # Monitor mode: Instead of creating a virtual sink into which all
             # applications must play, in PipeWire the echo cancellation module can read
             # the audio that should be cancelled directly from the current fallback
             # audio output
             monitor.mode = true
             # The audio source / microphone wherein the echo should be cancelled is not
             # specified explicitly; the module follows the fallback audio source setting
             source.props = {
                 # Name and description of the virtual source where you get the audio
                 # without echoed speaker output
                 node.name = "source_ec"
                 node.description = "Echo-cancelled source"
             }
             aec.args = {
                 # Settings for the WebRTC echo cancellation engine
                 webrtc.gain_control = true
                 webrtc.extended_filter = false
                 # Other WebRTC echo cancellation settings which may or may not exist
                 # Documentation for the WebRTC echo cancellation library is difficult
                 # to find
                 #webrtc.analog_gain_control = false
                 #webrtc.digital_gain_control = true
                 #webrtc.experimental_agc = true
                 #webrtc.noise_suppression = true
             }
         }
     }
 ]

诸如此类的配置更改需要 PipeWire 重启(即 pipewire.servicepipewire-pulse.service 用户单元)才能生效。

将额外的音频混合到麦克风音频中

上面的回声消除示例可以扩展为提供一个虚拟输出端,将音频复制到你的麦克风中。

它是 PulseAudio/Examples#Mixing additional audio into the microphone's audio 的重新创建,并解决了相同的用例。

为了实现这一点,你还需要加载两个“Combine stream”模块的实例,如下所示。

目前,在每次重启或 PipeWire 重启后,设置需要用户手动操作,例如在 Helvum 中才能完成;请参阅配置示例中的“TODO”注释。

 context.modules = [
 
     # (Configuration for system-wide echo cancellation, see above)
 
     # Audio effects sink (stereo)
     {   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 = {
                 # If you have an upmix configuration in client.conf.d, set the same
                 # parameters here, or else your sound effects application will not
                 # be upmixed in your local audio output
                 #channelmix.upmix = true  # (...)
                 # Possible alternative: Poor man's stereo upmix, i.e. mirroring front
                 # to rear speakers
                 #combine.audio.position = [ FL FR FL FR ]
                 #audio.position = [ FL FR RL RR ]
             }
         }
     }
 
     # Main source
     # Virtual source that supplies these sources mixed together:
     #  - source_ec (Echo-cancelled source)
     #  - sink_fx.monitor (Monitor of the audio effects sink)
     {   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 Block with matches= and actions= that matches the monitor of
                 # sink_fx and hooks it up to source_main
                 # No PipeWire configuration known yet that automates this
                 # See this PipeWire issue for news:
                 # https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/3710
                 # For the time being, add the required connections manually in Helvum,
                 # i.e. connect these points:
                 #  - sink_fx.monitor_FL -> source_main.output.input_FL
                 #  - sink_fx.monitor_FR -> source_main.output.input_FR
             ]
         }
     }
 ]