Transitioning to Pipewire on Arch Linux


Published on 2021-03-31 by Kenneth Flak

Back to Tech Research


Table of Contents

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:

Installation

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.

First Impressions

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 soundcard 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.

Configuration

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>

Access All Your I/Os

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.

Setting External Audio Interface as Default

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.

Individual Control of Channel Levels

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.

Checking For Xruns

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"
        },

Video Conference

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.

Advanced Configuration

Added sectoin 2024-01-01: wireplumber gives you the possibility to customize your configuration from here to eternity. This comes in very handy when you want to set different latencies for different soundcards. If, for example, you would like your shitty, internal card to have a nice, big hardware buffer with a correspondingly big latency, and your shiny, beautiful external soundcard to have a smaller hardware buffer, you can set that up in ~/.config/wireplumber/main.lua.d. This is a directory with lua scripts that are run by wireplumber, and that overrides the defaults you set elsewhere. The files should be named 50-or-larger-somethingsomething.lua. You could use 51-internal.lua to set a higher alsa buffer size than the default, and change the nick that shows up when querying the status of the soundcard:


rule = {
    matches = {
        {
            {"node.name", "equals", "alsa_card.pci-0000_00_1f.2"}
        }
    },
    apply_properties = {
        -- ["device.disabled"] = true
        -- ["api.alsa.period-size"]   = 2,
        ["api.alsa.period-num"]    = 4096,
        ["node.nick"] = "ALC257",
    }
}

table.insert(alsa_monitor.rules, rule)

Verdict

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.

EDIT 2024-01-01: Now that Pipewire has reached the 1.0 milestone, there's really no reason not to use it any more...

Resources

Pipewire wiki
Arch Linux wikipage on pipewire