Skip to content

Instantly share code, notes, and snippets.

@BuriedStPatrick
Last active January 14, 2025 16:40
Show Gist options
  • Save BuriedStPatrick/8ab954dd4ee651d498328b1f00d1932d to your computer and use it in GitHub Desktop.
Save BuriedStPatrick/8ab954dd4ee651d498328b1f00d1932d to your computer and use it in GitHub Desktop.
Audio on Arch Linux

This guide is mostly for my own sanity and help me troubleshoot down-the-line. Feel free to read along, though. Some things may be misconceptions, I'm just trying as best I can.

Introduction

Pre-requisites:

  • This is Arch, btw.
  • SystemD-based system. You wouldn't be reading this if you knew how to run anything else.

The goals for my system:

  1. Full control over how applications route audio between each other and other hardware devices.
  2. Ability to switch between routing "profiles" so that different workflows can quickly be adapted to.
  3. Full migration away from Windows to Linux, config files safely stored in Git, updates when I choose to.
  4. More reliance on FOSS, less proprietary software with DRM schemes that make them unreliable.

My initial problems:

  1. Rudimentary knowledge of general Linux, but audio is an entirely different beast.
  2. Poor knowledge of how devices are managed under Linux.
  3. Poor understanding of pipewire, pulseaudio, JACK & ALSA.
  4. Most of my used VST plugins have no Linux version or require iLok.
  5. Rudimentary knowledge of Wine.

Installing Pipewire

We're going to use pipewire to manage all our audio. It is the thing that enables us to do what we need to. Pipewire has implementations for PulseAudio, JACK and ALSA. As I understand it, PulseAudio used to be what most people used and it's still actively supported. But it's also very limited in what it can do. No crazy routings like what we need.

We'll install the following packages:

pipewire-pulse
pipewire-alsa
pipewire-jack
pipewire-libcamera
pipewire-v4l2
wireplumber
pavucontrol

Then we're going to enable and start the pipewire-pulse service which is responsible for being a pipewire-based implementation of PulseAudio:

systemctl --user --now enable pipewire-pulse

After which we're going to enable and start the wireplumber service which is responsible for configuring audio devices and such with pipewire. We'll configure it in a bit:

systemctl --user --now enable wireplumber

Let's check the systemd logs while we're at it:

journalctl --follow

Does anything look weird? Probably. It's likely fine, though, I don't know your system. But take note of it at least, in case something goes wrong in the future. And use this command to debug problems with pipewire and wireplumber.

Managing audio devices with Wireplumber

Oh lord, was this tedious to figure out. All the tutorials use .lua-based configuration files, but they're deprecated as of wireplumber v0.5 and onwards. It uses .conf files now, haven't ya heard? Well, if you looked in the logs with journalctl it would shout it from the rooftops. Ya doofus.

So how do we configure stuff now? Well, there's some documentation if that's your cup of tea. It isn't mine. Yes, I'm a stupid and bad person. To be clear, in general I really like documentation, I would just prefer it easily explained from an end-user's perspective, but this is heavy with implied domain-specific language that makes my head spin. Brainy no workey.

The first thing we should be doing is filtering out all the crap. If you're using Linux as a DAW then I assume you're not going to be using your on-board sound card. Let's first check what devices we have with pw-cli that came with the pipewire install:

pw-cli list-objects Device

You probably see a lot of devices with the name alsa_card.pci<something>. These are typically on-board audio cards or graphics cards (yes, graphics cards produce audio through the HDMI connection). I think the "PCI" here means it's running through PCI on your PC. It's a pretty decent assumption we don't need these clogging up our patch bay. Let's get rid of them from ever confusing us with our precious limited attention span. We'll "filter" them out by adding a config file:

# ~/.config/wireplumber/wireplumber.conf.d/50-generic-card-disable.conf
# Disable all generic PCI sound cards
# (On-board audio & graphics card audio)
monitor.alsa.rules = [
  {
    matches = [
      {
        device.name = "~alsa_card.pci-*"
      }
    ]
    actions = {
      update-props = {
        device.disabled = true
      }
    }
  }
]

This config format is SPA JSON. Do you care? Probably not. But the documentation is very invested in letting you know that, so I'll note this here as well. But how is this supposed to work? Well it doesn't work yet, because we need to restart wireplumber to reload the config:

systemctl --user restart wireplumber

Then wait a few extra seconds for it to get going and let's use pw-cli list-objects Device | grep -i alsa_card.pci to check that the devices are no longer present in the output (should be empty).

Okay, but HOW DOES IT WORK!?!?!

Wireplumber has this concept of API's (Application Programming Interfaces) that function sort of like modules in what it can hook into. So one is the ALSA module, the low-level Linux audio... thingey. That's what really manages our audio devices at the lowest level. So the monitors.alsa.rules propery on the first line adds a rule to the ALSA module. Given some predicate, do something to whichever device matched the predicate.

So we're telling it "Match any device whose name matches the ~alsa_card.pci-* pattern. The ~, I think, tells it to start from anywhere, not just the first character, the * just indicates a wildcard for any string match. So, if you needed to only match a certain subset of PCI-cards, you could be more specific in your matches or just write the name out entirely if you're looking to filter out a specific device. We got the name from the result of pw-cli list-objects Device using the device.name property.

Then in the actions property, we can define some stuff to do to the device. Like disabling it, changing the name, adding a better description, setting the device profile, etc.

I'm using a Focusrite Scarlett 18i20 card as my main soundcard for audio production and playback, let's see what I've done to it:

# ~/.config/wireplumber/wireplumber.conf.d/50-scarlett-18i20.conf
# Rename Focusrite card to something more sane
monitor.alsa.rules = [
  {
    matches = [
      {
        device.name = "~alsa_card.usb-Focusrite_Scarlett_*"
      }
    ]
    actions = {
      update-props = {
        device.profile = "pro-audio"
        device.name = "focusrite-18i20-audio"
        device.description = "Focusrite 18i20 (Audio)"
      }
    }
  }
]

This is kinda' stupid because I just say any Focusrite Scarlett card should be called an 18i20. But I also don't plug in anything else so this works for me. If you have multiple sound cards, then you should be more specific here. Now why would I bother with this? Because I want to make sure the name is consistently set to the same thing always. There should be no question anywhere where I create a patchbay just what this device should be identified as. This will keep my system consistent and not randomly re-assign stuff I don't want and I'm in control of what my device means to my system, not the other way around.

This is the extent of what I've done with my audio devices using wireplumber. Can you believe that's all I needed? And can you believe just HOW LONG it took for me to get there? As I said, I am a stupid and bad person.

Managing video devices with Wireplumber

I have an Elgato 4K Cam Link (who doesn't) connected through USB at all times. I want the snoopers to always have a good view when I'm doing business. To make sure things are running as smoothly as possible, I've done a few unspeakable things to my video devices. But this time, we're OBVIOUSLY not using the ALSA API. Be honest, did you think we were gonna' use THAT for video devices? ... because I may have for too many hours of my life...

We're instead going to configure Video4Linux and libcamera. Why both? I don't know anything about these technologies, they just showed up and I'm doing my best, okay?!

So Video4Linux is called v4l2 (I guess it's version 2?) and libcamera is just libcamera. I know this because I was smart enough to run pw-list list-objects Device and note their device.api properties. That means the first line when we define a rule in wireplumber, instead of looking like monitor.alsa.rules, it'll be more like monitor.v4l2.rules or monitor.libcamera.rules if you prefer. I won't go into details with these ones, it's the same as the audio config stuff. Add some "matches" predicates and perform some "actions" to modify or disable them.

Setting up a patch bay

I'm currently bouncing between the following packages:

  • qjackctl - Doesn't support video.
  • qpwgraph - Doesn't support MIDI.

I really like qjackctl as it feels more feature complete with a bunch of options for various stuff I barely understand. And it seems to respect dark mode on my systems. If it JUST supported video, that'd be swell. qpwgraph is what you'll see recommended most places. And it is really nice, less bloated, and supports video routing as well. But lack of MIDI routing (as far as I can tell) really is a bother, and I haven't had a good experience with its take on a patch bay.

As for now, I set up patch bays with qjackctl and use qpwgraph for the rare occasion I need to route video.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment