Using Pipewire with HDMI

This was harder to find out how to do than I'd expected, which may, of course, be due to my lack of web skills, but at any rate, I'm putting this up so I don't have to look again.

This was done on a minimal Fedora install, a small Arch install, a Voidlinux install, and FreeBSD. All used openbox as the window manager, I suspect that it may be even simpler in desktop environments like Gnome.

My use case is occasionally connecting a laptop, through an HDMI cable, to a large TV. I wanted it to automatically use the TV's speaker when connected and the laptop's speakers when not. I only connect it to the TV infrquently, say for a maximum of an hour or so, a few times a week.

First, you need pipewire and wireplumber. Once you have these installed, you can hook the laptop up to the TV and type, as the user who will be doing this
wpctl status |more

This will give output that will contain, among other things, something like
Audio
 	Devices:
       40. Built-in Audio                      [alsa]
       41. Built-in Audio                      [alsa]

  Sinks:
      44. Built-in Audio Digital Stereo (HDMI) [vol: 0.74]
     * 45. Built-in Audio Analog Stereo        [vol: 0.72]

You'll want to pipe it through more or less as I did in the example as there will be a lot of output. Anyway, in that example, you see that the HDMI sink is number 44. Now set it as default.
wpctl set-default 44

It will now use the HDMI when it is plugged in. If it's not attached to the TV it will use the laptop speakers. In my experience, once I unplug it, I have to get out of X and restart it, for the sound to change. But for my needs, this isn't a big deal.

Voidlinux

With Voidlinux, and probably others that don't use systemd, it's a little more complicated, but not terribly so. Aside from pipewire and wireplumber, you'll need to install rtkit. You also have to set the $XDG_RUNTIME_DIR. If you don't do these two things, pipewire will fail to start.

On Voidlinux, you can install elogind, which will automatically set the $XDG_RUNTIME_DIR variable without any further action on your part. However, many who use Void prefer to not use systemd associated programs. So, if you don't wish to use elogind, just set the path in your $HOME/.bash_profile. Add to the end of your .bash_profile
unset XDG_RUNTIME_DIR
export XDG_RUNTIME_DIR=$(mktemp -d /tmp/$(id -u)-runtime-dir.XXX)

Note that that is exactly what you type, dir.XXX. When it's created it will take a specific name but your .bash_profile should have the letters XXX as shown.

There are further instructions in the VoidLinux handbook on using pipewire with alsa here. Install alsa-pipewire, then
mkdir -p /etc/alsa/conf.d
 ln -s /usr/share/alsa/alsa.conf.d/50-pipewire.conf /etc/alsa/conf.d
 ln -s /usr/share/alsa/alsa.conf.d/99-pipewire-default.conf /etc/alsa/conf.d

If you do this and install rtkit with sudo xbps-install -Sy rtkit, pipewire will start. It may give some messages, but start it with pipewire &, and it will run happily in the background. Another option, and what I use, is dmenu which I run in openbox. I open dmenu, type pipewire, and I can then run the wpctl status command mentioned earlier.

As of early June, 2023, this has changed. See the VoidLinux news article. I now have to, after running pipewire, run wireplumber. Again, I use dmenu. First I run pipewire, then wireplumber. The pipewire command has to be run first. For my use, when I don't have it hooked to the TV, I don't even need pipewire. So, I can, if just running the laptop, not even use the pipewire command and get sound through the speakers. Therefore, I only run pipewire, then wireplumber, (both through dmenu) when hooking it up to the TV. Though I don't use it often enough to make it worthwhile, one can add
pipewire &
sleep 3;
wireplumber &

to $HOME/.xinitrc, above the line calling the window manager. (I've found the sleep 3 line to be necessary or wireplumber may not always start) As I only need it on occasion, I don't do it that way, preferring to use dmenu, but for someone using it frequently with their TV, that's another way to do it.

FreeBSD

FreeBSD does have pipewire, but doesn't have wireplumber which provides the wpctl command. To switch between laptop and HDMI speakers, one can cat /dev/sndstat to see what you have. For example, my T495 Thinkpad's /dev/sndstat is
Installed devices:
pcm0: <ATI R6xx (HDMI)> (play)
pcm1: <ATI R6xx (HDMI)> (play)
pcm2: <ATI R6xx (HDMI)> (play)
pcm3: <Realtek ALC257 (Analog 2.0+HP/2.0)> (play/rec) default
pcm4: <Realtek ALC257 (Right Analog Mic)> (rec)
No devices installed from userspace.

Note that the default is pcm3, which refers to the laptop speakers. To get it to use HDMI, I can edit it on the fly with
sysctl hw.snd.default_unit=1

This switches the default to HDMI. Once disconnected from the TV, I can go back to the laptop speakers with
sysctl hw.snd.default_unit=3

If you use this frequently, it might be worth making a shell script.

Vermaden's incredibly useful blog has a section on using pactl (pulse audio control) to switch output on the fly. I haven't had a chance to try it yet, but it seems as if it may be a good solution.

I hope that this helps the reader to quickly and easily use pipewire with HDMI speakers. Some of the docs that I found were lacking in information while others seemed to overcomplicate it. I hope this strikes a happy medium.