Published on 2021-03-31 by Kenneth Flak
Back to Tech Research
May you live in interesting times. Or, alternatively: Good luck getting professional audio properly configured on Linux. At least, this used to be the predicament. Getting ALSA to work together with Jack to work together with PulseAudio is an esoteric business worthy of the deepest forms of black magic. Now, however, Wim Taymans at Red Hat is working on something that might just bring us from the dark ages into an era of enlightenment, at least as far as the end user is concerned: pipewire.
The pipewire
promise is one of easy, plug-and-play configuration of
all your audio devices. No more worrying about whether your application
supports jack, PulseAudio or both. Now you should be able to just run
your application, and sound will magically appear from your speakers.
Just like that. Last week I decided it was time to check out this
fabulous new invention, and so I set about making it work for me. Here's
a preliminary tour of what I found out:
I wanted the full integration, so I ran:
paru -S pipewire pipewire-{alsa,jack,wireplumber,pulse}
EDIT 2023-07-03: media-session
is now deprecated. Use wireplumber
instead.
All the packages except pipewire-jack-dropin
are in the extra
repo. pipewire-jack-dropin
is in AUR. However, if you prefer, you could simply remove your existing jack/jack2 packages instead of installing the dropin. Replace paru
with your preferred AUR helper.
If you, like me, run jack on login, then make sure to disable whatever script you're currently running. Reboot and you should be good to go.
In short: this is very, very promising. pipewire
emulates jack
whenever a jack
client is launched. No need to manually start a
server. Also: For the first time since I abandoned macOS for Linux my
computer automatically swapped sound card when I plugged in my Fireface
UCX. No fiddling around with custom-made scripts to switch between
internal and external interfaces.
I might have gotten a bit more xruns than I would normally gotten, but not show-stoppingly so.
EDIT 2023-07-03: xruns are no longer an issue for me when running with a vanilla kernel.
There is very little need for any specific configuration, which
was almost shocking for a long-time Linux musician. Currently
the samplerate of pipewire is fixed globally, and can be set in
/etc/pipewire/pipewire.conf
. Uncomment this line:
default.clock.rate = 48000
and set it to whatever you want it to be, provided your hardware supports it.
Slightly confusingly, you can also set this for jack in
/etc/pipewire/jack.conf
, in the jack.properties
section.
jack.properties = {
node.latency = 256/48000
...
}
This will give you a default buffer size of 256 samples at a samplerate of 48khz. If your audio interface is not stellar, you'd probably want to go for 512 or even 1024 samples to avoid xruns at the expense of higher latency. If your interface is excellent you might go lower, into the warp speed area of 64 or even 32 samples.
The buffer size can also be set in the client itself. In ardour
I
was able to set this in the settings. Alternatively you can set an
environment variable before running your application:
PIPEWIRE_LATENCY=256/48000 your_application
Notice that this will only change the ratio between buffer size and
samplerate. It is not (yet) possible to change samplerate at runtime, so
if you try something like this while the jack.properties node.latency
is set to 48000:
PIPEWIRE_LATENCY=256/96000 your_application
... you will end up with a buffer size of 128 and a samplerate of 48k. Ratios, ratios, ratios.
Pipewire will resample audio as required to get PulseAudio, jack and ALSA working together.
So, in short: set the samplerate globally, mess around with buffer sizes per application. More details on configuration can be found [here](https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Configur ation)
EDIT 2023-07-03: It is possible to set the global samplerate during runtime by issuing the command:
pw-metadata -n settings 0 clock.rate <value>
The default configuration is of the consumer kind, which means stereo
in and stereo out. If you invested big money in 18 channels I/O this
seems a bit sad. The remedy is to activate the pro
profile. I
haven't figured out how to do this on the command line yet, but it's
very easy to do this in PulseAudio Volume Control (PAVU). Go to the
Configuration
tab, click on the Profile
drop-down menu under your
card, select Pro Audio
and you should be good to go. All the channels
should now be available.
For some reason all my jack applications insisted on playing back on the
internal soundcard by default. The solution was, yet again, found in the
PAVU application. Go to the Output Devices
tab, find the interface you
want to use as default, click on the green checkbox on the right side
to make this your default output. Repeat the same steps in the Input Devices
.
The Fireface UCX is a brilliant soundcard in many ways, but on Linux you
will only get the class-compliant experience, which means zero control
over levels of individual input and output channels on the software
side. To set the levels you have to use the rotary encoder in the front
of the soundcard, an encoder that has died on me more than once. A very
unfortunate weakness, in other words. Now, however, this problem can be
mitigated through PAVU. Next to the previously mentioned green checkbox
in the Output
and Input Devices
there is a padlock button. Click
on this, and voila! All your input and output stream levels become
individually accessible! Of course, it is possible to pull off this kind
of trick in any number of jack mixing applications, but the option to do
this directly in a central configuration application makes life just a
bit easier.
I haven't yet found an easy way to check the xrun count, but pipewire helpfully posts any information about this to the systemd logs. So, to keep an eye on the state of your pipewire instance in real time, you could simple run this in a separate terminal:
journalctl -f | grep pipewire
This will give you a running commentary on the state of your audio stack. An xrun will look something like this:
Apr 01 10:44:04 t480s pipewire[10997]: (alsa_output.usb-RME_Fireface_UCX__23815246__F9C767BDDD2EEC8-00.pro-output-0-49) client too slow! rate:256/48000 pos:294748928 status:triggered
Apr 01 10:44:04 t480s pipewire[10997]: (alsa_output.usb-RME_Fireface_UCX__23815246__F9C767BDDD2EEC8-00.pro-output-0-49) XRun! rate:256/48000 count:19 time:23386594654 delay:22444 max:22444
EDIT on 4 April 2021: after publishing this, I was made aware of an
extremely handy tool that comes bundled with pipewire
: pw-top
. This
lets you monitor all your sinks and sources; their samplerates and
buffer sizes, xrun counts, process ids and more. Very useful! Next on my
todo-list is to write an xrun-counter for my sway/i3 status bar.
EDIT 2023-07-03: I am currently using waybar, which has a very handy
jack
module. This one flashes red on any xruns. My config looks like
this:
"jack": {
"format": " DSP {load}% / {latency} ms / {bufsize} ",
"format-xrun": "{xruns} xruns",
"format-disconnected": "DSP off",
"realtime": true,
"on-click": "qjackctl"
},
Zoom seems to work without issues or further configuration. I had some trouble getting Jitsi up and running in Firefox. In the Chromium-base Qutebrowser I could make it work after a restart and a segfault in pipewire.
It is still early days of development, and hickups are to be expected. However, my overall feeling is that this is a massive improvement on the user experience over what used to be the labyrinthine tangle that professional audio Linux users have had to suffer over the years. I am looking forward to pipewire-native ways of setting samplerates, buffer sizes and so on at runtime, preferably using the command line.
EDIT 2023-07-03: I am now fully back on Pipewire after having had some troubles with xruns, and am very, very happy with how it works. I also no longer need to run a realtime kernel, a vanilla kernel is more than sufficient to get lower latencies than I have ever experienced before, as the regular kernel is running PREEMPT by default.