PipeWire ist ein fortschrittliches Open-Source-Audio-Server-Projekt, das sich darauf konzentriert, die Audioverwaltung auf Linux-Systemen zu verbessern. Mit seinem flexiblen und leistungsstarken Ansatz bietet PipeWire eine Reihe von Funktionen, darunter die Möglichkeit, virtuelle Sinks zu erstellen. Virtuelle Sinks sind virtuelle Audiogeräte, die es ermöglichen, Audio von verschiedenen Anwendungen zu einem einzigen Ziel zu leiten. Diese Funktion bietet eine effiziente Möglichkeit, Audioquellen zu kombinieren und zu steuern, was besonders nützlich ist, wenn es um komplexe Audio-Routing-Szenarien geht. In diesem Zusammenhang bietet PipeWire eine solide Grundlage für die Verwaltung von Audio-Ressourcen auf Linux-Plattformen und ermöglicht eine verbesserte Flexibilität und Kontrolle über das Audio-Setup.
Inhalt
1. Einleitung
Die Problemstellung war es, ein professionelles Audiointerface an Linux zu betreiben. In diesem Beispiel geht es um ein Motu M4, welches per USB angeschlossen wird. Das Device benötigt selber keine speziellen Treiber, um auch unter Linux genutzt zu werden. Da das Device aber mehrere Audio Ein- und Ausgänge hat, wird dieses Gerät unter PipeWire als Surround-4.0 Gerät erkannt, sowohl was die Eingänge, als auch die Ausgänge angeht.
Es wurden also alle Ein- und Ausgänge zusammen gefasst und als Surround Device angezeigt. Zwei Lautsprecher vorne und zwei Lautsprecher hinten. Das Problem ist nun, wenn man einfach nur Stereo Audio ausgeben möchte, wird dieses Signal über FL & FR (Also Front-Left und Front-Right ausgegeben). Und es gibt keine einfache Möglichkeit, hier umzuschalten, wie es zum Beispiel bei Windows der Fall ist.
Natürlich kann man über Jack, bzw. QJack oder WirePlumber die Kanäle umgeroutet werden. Aber das ist alles wesentlich aufwendiger, als wenn man in der Taskleiste über PavuControl oder ähnlichen Tools einfach die Quelle umschalten kann.
2. Virtual Sinks
Das Stichwort lautet “Virtual Sinks”.
Virtuelle Sinks sind so etwas wie virtuelle Audio-Ausgangssteckplätze auf einem Computer. Die muss man sich vorstellen wie verschiedene “Stöpsel”, in denen Audio von verschiedenen Programmen gesteckt werden können. Diese virtuellen Sinks sammeln dann alle diese Audio-Signale an einem Ort.
Ein einfaches Beispiel könnte ein Video-Chat sein, bei dem sowohl Musik von einem Musikplayer als auch Sprache aus dem Video-Chat-Programm gehört werden soll. Anstatt die beiden getrennt zu hören, könnte man virtuelle Sinks verwenden, um beide Audiosignale an einem Ort zu mischen. Das bedeutet, dass es nur einen “Lautstärkeregler” für beide Quellen existieren würden und die Audioausgabe besser kontrollierbar wäre.
Insgesamt ermöglichen virtuelle Sinks eine flexiblere Handhabung verschiedener Audioquellen auf einem Computer, indem sie sie virtuell an einem Ort zusammenführen.
Aber man kann auch aus einzelnen Kanälen eigene virtuelle “Soundkarten” erstellen. Und genau darum geht es in diesem Beispiel.
3. Virtual Sinks anlegen
Als erstes müssen ein paar Daten über das Audiodevice abgefragt werden, welches aufgeteilt werden soll:
$ pactl list sinks
Je nach Hardware, bzw. deren Ausstattung wird eine recht lange Liste ausgegeben. In dieser Liste muss man sich das entsprechende Audiogerät heraussuchen. Bei meinem Moto M4 sähe die Ausgabe wie folgt aus:
Sink #55
State: RUNNING
Name: alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0
Description: M4 Pro
Driver: PipeWire
Sample Specification: s32le 4ch 48000Hz
Channel Map: aux0,aux1,aux2,aux3
Owner Module: 4294967295
Mute: no
Volume: aux0: 41943 / 64% / -11,63 dB, aux1: 41943 / 64% / -11,63 dB, aux2: 41943 / 64% / -11,63 dB, aux3: 41943 / 64% / -11,63 dB
balance 0,00
Base Volume: 65536 / 100% / 0,00 dB
Monitor Source: alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0.monitor
Latency: 0 usec, configured 0 usec
Flags: HARDWARE DECIBEL_VOLUME LATENCY
Properties:
alsa.card = "0"
alsa.card_name = "M4"
alsa.class = "generic"
alsa.device = "0"
alsa.driver_name = "snd_usb_audio"
alsa.id = "USB Audio"
alsa.long_card_name = "MOTU M4 at usb-0000:11:00.3-1, high speed"
alsa.name = "USB Audio"
alsa.resolution_bits = "32"
alsa.subclass = "generic-mix"
alsa.subdevice = "0"
alsa.subdevice_name = "subdevice #0"
api.alsa.auto-link = "true"
api.alsa.card.longname = "MOTU M4 at usb-0000:11:00.3-1, high speed"
api.alsa.card.name = "M4"
api.alsa.disable-tsched = "true"
api.alsa.path = "hw:0,0"
api.alsa.pcm.card = "0"
api.alsa.pcm.stream = "playback"
audio.channels = "4"
audio.position = "AUX0,AUX1,AUX2,AUX3"
card.profile.device = "0"
clock.name = "api.alsa.0"
device.api = "alsa"
device.class = "sound"
device.id = "53"
device.profile.description = "Pro"
device.profile.name = "pro-output-0"
device.profile.pro = "true"
device.routes = "0"
factory.name = "api.alsa.pcm.sink"
media.class = "Audio/Sink"
device.description = "M4"
node.group = "pro-audio-0"
node.link-group = "pro-audio-0"
node.name = "alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0"
node.nick = "M4"
node.pause-on-idle = "false"
object.path = "alsa:pcm:0:hw:0,0:playback"
priority.driver = "1500"
priority.session = "1500"
factory.id = "18"
clock.quantum-limit = "8192"
client.id = "34"
node.driver = "true"
factory.mode = "merge"
audio.adapt.follower = ""
library.name = "audioconvert/libspa-audioconvert"
object.id = "55"
object.serial = "55"
node.max-latency = "16384/48000"
api.alsa.period-size = "1024"
api.alsa.period-num = "32"
api.alsa.headroom = "0"
api.acp.auto-port = "false"
api.acp.auto-profile = "false"
api.alsa.card = "0"
api.alsa.use-acp = "true"
api.dbus.ReserveDevice1 = "Audio0"
device.bus = "usb"
device.bus-id = "usb-MOTU_M4_M4AE035F64-00"
device.bus_path = "pci-0000:11:00.3-usb-0:1:1.0"
device.enum.api = "udev"
device.icon_name = "audio-card-analog-usb"
device.name = "alsa_card.usb-MOTU_M4_M4AE035F64-00"
device.nick = "M4"
device.plugged.usec = "3641826113"
device.product.id = "0x000b"
device.product.name = "M4"
device.serial = "MOTU_M4_M4AE035F64"
device.subsystem = "sound"
sysfs.path = "/devices/pci0000:00/0000:00:08.1/0000:11:00.3/usb5/5-1/5-1:1.0/sound/card0"
device.vendor.id = "0x07fd"
device.vendor.name = "Mark of the Unicorn"
device.string = "0"
Formats:
pcmZwei Zeilen sind davon jetzt von Interesse:
... audio.position = "AUX0,AUX1,AUX2,AUX3" ... node.name = "alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0" ...
Unter <audio.position> finden sich die Namen der einzelnen Kanälen. In diesem Fall sind die AUX0 bis AUX3. Es könnten dort aber auch so Werte wie FL, FR, FC, LFE, FL und FR stehen. Das kommt immer auf das verwendete Audiointerface an. Und unter <node.name> das entsprechende Device.
In diesem Fall ist AUX0 & AUX1 (Kanal 1 & 2 beim Motu M4) der erste Stereo Ausgang und AUX2 & AUX3 (Kanel 3 & 4) der zweite. Und jeweils jeder Stereo Ausgang soll eine eigene Virtuelle “Soundkarte” werden.
Dazu editiert man die Datei <pipewire-pulse.conf> im Konfigurationsverzeichnis des eigenes Benutzers. Sofern diese Datei noch nicht vorhanden ist, wird diese neu angelegt:
$ vi ~/.config/pipewire/pipewire-pulse.conf
Sofern die Datei schon existiert, nach dem Eintrag “context.modules = [” suchen. In diesem Block wird ein zusätzlicher Eintrag hinzugefügt. Die einzelnen Sektionen starten immer mit der Art, also in diesem Fall contect.modules =, gefolgt von der eckigen Klammer [. Und beendet wird dieser Block mit der Klammer ].
Am besten trägt man vor der ] Klammer folgenden Eintrag ein:
{ name = libpipewire-module-loopback
args = {
node.description = "Motu M4 Channel 1+2"
capture.props = {
node.name = "M4_stereo_pair_1"
media.class = "Audio/Sink"
audio.position = [ FL FR ]
}
playback.props = {
node.name = "playback.M4_stereo_pair_1"
audio.position = [ AUX0 AUX1 ]
target.object = "alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0"
stream.dont-remix = true
node.passive = true
}
}
}
{ name = libpipewire-module-loopback
args = {
node.description = "Motu M4 Channel 3+4"
capture.props = {
node.name = "M4_stereo_pair_2"
media.class = "Audio/Sink"
audio.position = [ FL FR ]
}
playback.props = {
node.name = "playback.M4_stereo_pair_2"
audio.position = [ AUX2 AUX3 ]
target.object = "alsa_output.usb-MOTU_M4_M4AE035F64-00.pro-output-0"
stream.dont-remix = true
node.passive = true
}
}
}Sollte die Datei neu angelegt worden sein, dann müsste dieser Block natürlich noch entsprechend umfasst werden mit:
context.modules = [ ... Hier den obigen Block einfügen ... ]
Mit dieser Konfiguration werden zwei weitere virtuelle “Soundkarten” angelegt, die mit Motu M4 Channel 1+2 und Motu M4 Channel 3+4 dann in den Sound Einstellungen zu finden sein werden.
Damit die Änderung wirksam wird, muss PipeWire neu gestartet werden:
$ systemctl --user restart pipewire pipewire-pulse
Und direkt danach kann die Audioquelle entsprechend umgeschaltet werden:

4. Fazit
Zusammenfassend ermöglichen virtuelle Sinks in Verbindung mit PipeWire eine verbesserte Verwaltung von Audio-Ressourcen auf Linux-Systemen. Durch die Schaffung virtueller Audio-Ausgangssteckplätze bietet PipeWire eine flexible Lösung, um verschiedene Audioquellen effizient zu kombinieren und zu kontrollieren. Dies ist besonders nützlich in Szenarien, in denen eine präzise Steuerung der Audioausgabe erforderlich ist, wie beispielsweise bei der gleichzeitigen Nutzung von Musik, Video-Chats und anderen Anwendungen. Die Integration von virtuellen Sinks erleichtert die Handhabung von komplexen Audio-Routing-Anforderungen und bietet eine verbesserte Benutzerfreundlichkeit für die Audioverwaltung auf Linux-Plattformen.
Quellen:
https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices#examples
https://pipewire.org/
