Intro

So, you’ve got various computers on your network running PipeWire or PulseAudio, and you want to play audio through some of these machines from your desktop. Well, it’s not that hard to do, read on to find out how. This guide is geared towards Ubuntu/Debian, but should be adaptable to other distros.

I have a Raspberry Pi downstairs hooked up to my stereo. I like to send audio from my upstairs Ubuntu Desktop so I can have the same music synced throughout the house. I had some trouble getting it setup reliable over the years, so finally decided to write a blog about it.

Prerequisites

You will need:

  • Two or more computers running PulseAudio or PipeWire
  • pavucontrol - apt-get install pavucontrol
  • pactl - included in the pulseaudio-utils package on Ubuntu/Debian

Overview

So, once you’ve got all the prerequisites, here’s how it works. You have your source computer (your desktop) that you play music from, and the target computers (satellite, remote computers in other rooms) that you will stream music to from your source computer.

Target Setup

It’s probably best to setup the target computers first. We’ll need to load and configure some modules.

First, we need to allow other machines to connect to the target and use its outputs. We do that by loading module-native-protocol-tcp:

pactl load-module module-native-protocol-tcp auth-anonymous=1

Note: This configuration allows anyone to connect.

Next, we need to tell the network about the outputs we have. For that, we load module-zeroconf-publish:

pactl load-module module-zeroconf-publish

Source Setup

Now, let’s setup the source machine. First, we need to load module-zeroconf-discover. This will discover the outputs published by the target computers:

pactl load-module module-zeroconf-discover

If you open pavucontrol, you should see network outputs in the “Output Devices” tab.

The next step is optional, you can now select the various network outputs via pavucontrol’s “Playback” tab and pipe sound to them. If you’d like to actually “synchronize” and play the same thing on both source and target at once, you need to load the module-combine-sink module. It does require some parameters:

pactl load-module module-combine-sink sink_name=HouseRadio slaves="alsa_output.stereo,tunnel.remotehost.local.alsa_output.stereo"

With the above, we create a new Output Device called “HouseRadio” that plays to all of the interfaces passed to slaves with comma separation.

Find the Sink Names

The only tricky part is finding the sink names, but it’s not too bad:

pactl list sinks | grep Name:

This will show you the names of your sinks. If you’re using PulseAudio, the card names will sometimes be surrounded by ‘<’ and ‘>’ characters (PipeWire seems to omit them, thankfully). Leave these out of the pactl commands when referring to those sinks.

Surviving Reboot

While it’s pretty trivial to run the handful of pactl commands on the various hosts when you need it, it’s also easy to just set it up permanently.

PipeWire

If you’re using PipeWire, copy /usr/share/pipewire/pipewire-pulse.conf to /etc/pipewire/pipewire-pulse.conf.

Edit the file and find the context.exec section. Add the relevant lines for target and source hosts:

Target:
{ path = "pactl"	args = "load-module module-native-protocol-tcp auth-anonymous=1" }
{ path = "pactl"	args = "load-module module-zeroconf-publish" }
Source:
{ path = "pactl"	args = "load-module module-zeroconf-discover"

Technically, you can add a line on the source machine to load module-combine-sink, but I like to control this with a script when I need to “broadcast”. It just unloads the module (in case it was previously loaded), then reloads it with my preferred settings. This way, I can also be sure PipeWire doesn’t try to setup the combined sink before the network sinks are actually available.

PulseAudio

If you’re using pulseaudio, edit /etc/pulse/default.pa and add these two lines:

Target:
load-module module-native-protocol-tcp
load-module module-zeroconf-publish
Source:
load-module module-zeroconf-discover

# You might also need to explicitly load this
load-module module-native-protocol-tcp

Gotchas

System Mode / Ensuring a PA/PW Session is Running

PipeWire and PulseAudio generally require a user to be logged in before this will actually work. If you don’t want to deal with having to login to remote machines everytime you want to combine audio with them, you can configure PulseAudio in system mode.

PulseAudio does not recommend this, but I do it on my pi for convenience. Setting that up is beyond the scope of this guide, but I will try to post a guide for it and link it here.

Simply SSHing into the target machines should be enough to get it working. You can leave a running tmux session as a workaround as well.

Stuttering Audio on Pause

I’ve since moved everything to PipeWire, but when I was connecting from PipeWire to PulseAudio, I would get stuttering/repeated audio segments when pausing. It would play the last few milliseconds of the audio buffer (seemingly) another 5 times. Pretty annoying and potentially embarassing depending on where you pause.

Make sure you’re running similar/same version of PulseAudio or PipeWire on both ends.

Restarting to Reload Configuration Changes

To quickly reload PipeWire after config file changes, run systemctl --user restart pipewire.

To quickly reload PulseAudio after changing the configuration, run pulseaudio -k as the user running pulseaudio (you if normal user mode, root if system mode).

Wrap Up

Well, there you have it. You should be able to adapt this your needs fairly easily. I recommend playing around with pactl and getting it working in a proof of concept before changing the config files. The modules can be unloaded and loaded willy nilly without restarting the whole process.

You can even wrap this stuff into scripts and just call it when you need it. I don’t always have my source desktop connected to my pi because I’m afraid I’ll accidentally send it some audio I’d rather not have blasting through the house.

Good luck, drop me a line at [email protected] if you run into difficulties and I’ll try and help you and and/or get the guide updated. Thanks for reading!