Share a sound system between multiple devices using a Raspberry Pi

Ever had a moment where you want to use your sound system between multiple devices? Well, I definitely did. I often switch the system between my desktop (where it's used most of the time), laptop and phone. Switching between my desktop and laptop was easy enough, but having to replug it every time got annoying quickly. And since it is just a dumb system, it has no wireless connections for my phone to use. So I figured out a solution in which I use my Raspberry Pi as an intermediary which plays all sound send to it through the speakers. I figured I write down how I've done it both for myself in the future where I might want to reproduce this setup, or for anyone else reading this.

Requirements

For this setup I use a Raspberry Pi 3B. Slower variants might work, but in my experience doing anything else other than playing sound makes the audio stutter already, so I don't recommend it.

For my speakers I use a Logitech Z906 5.1 system. I've had it for a few years and am loving it. It plays 5.1 surround sound, has a strong bass and individual volume controls for front, back, central and bass speaker. However, since Jack is the only connection both the Raspberry Pi and speakers support, which has absolute horrific audio quality on the RPi, I use a converter which converts any digital optic or 3x jack connection to USB. This way I can just plug it into any device with an USB port and keep the ability to play surround sound.

On the software side I use Raspbian OS, together with PulseAudio. The OS however doesn't matter as long as it supports PulseAudio, and I might personally switch to Alpine Linux for it in the future.

Setup

To connect your devices to the system, you can use either Bluetooth or a direct network connection to PulseAudio, or both. I personally use the latter option for both my laptop and desktop (which are running PulseAudio already), and Bluetooth for my current Android phone.

In my case I also had to tell Pulse to use the speakers as 5.1 surround rather than stereo by adding set-card-profile 0 output:analog-surround-51 to /etc/pulse/system.pa. This might differ depending on your setup.

Bluetooth

PulseAudio supports playing audio as an A2DP source. This way the RPi will show up as a sound system to any Bluetooth device connecting to it.

Install PulseAudio and the Bluetooth modules:

# apt install bluez pulseaudio pulseaudio-module-bluetooth

First configure Bluetooth. Set AutoEnable under [Policy] to true in /etc/bluetooth/main.conf so all found Bluetooth adapters are enabled at boot, and Class under [General] to 0x00041C so it advertises itself as a Bluetooth speaker.

Create a new file /etc/bluetooth/audio.conf with the following contents:

[General]
Enable=Source,Sink,Media,Socket

This will setup the device as an audio gateway.

Now start the service and make it launch on boot:

# systemctl start bluetooth
# systemctl enable bluetooth

To start PulseAudio on boot without a desktop environment, I wrote my own systemd unit file. This starts PulseAudio in system mode which is normally recommended against unless you're in an embedded system. Although this system is technically not embedded, I do however think it fits the purpose and I have not encountered any issues while doing so.

Place the following in pulseaudio.service in /etc/systemd/system:

[Unit]
Description=PulseAudio Daemon
After=sound.target

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
PrivateTmp=true
Restart=always
ExecStart=/usr/bin/pulseaudio --system --realtime --disallow-exit --no-cpu-limit

PulseAudio has to be told to act as a Bluetooth receiver, so add the following to /etc/pulse/system.pa:

load-module module-bluetooth-policy
load-module module-bluetooth-discover

Also make sure the user running the daemon (usually pulse) is part of the lp group. Then start the service and make it launch on boot:

# systemctl start pulseaudio
# systemctl enable pulseaudio

If everything went well, the RPi should now appears as a audio receiver for Bluetooth devices. You can make the adapter either be discoverable and pairable permanently by editing /etc/bluetooth/main.conf entries, or connect devices on a case by case basis using bluetoothctl.

To connect from a Bluetooth device to your Raspberry Pi:

# bluetoothctl
[bluetooth]# discoverable on
[bluetooth]# pairable on

To connect from your Raspberry Pi to a device:

# bluetoothctl
[bluetooth]# scan on
[bluetooth]# connect <mac address of device found in previous step>

Network

PulseAudio also supports playing audio via the local network, using the PulseAudio instance installed on your local machine.

Install PulseAudio and the zeroconf module:

# apt install pulseaudio pulseaudio-module-zeroconf

In this case we only need to configure PulseAudio. Add the following to /etc/pulse/system.pa:

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.1.0/16
load-module module-zeroconf-publish

Replace 192.168.1.0/16 with your own network range. This tells PulseAudio to trust any device from the local network. You can also use this to only allow specific IP addresses.

Also make sure the user running the daemon (usually pulse) is part of the lp group. Then start the service and make it launch on boot:

# systemctl start pulseaudio
# systemctl enable pulseaudio

Lastly on your local machine add the following to /etc/pulse/default.pa:

load-module module-zeroconf-discover

Now restart PulseAudio, pulseaudio --kill. The speakers should now show up as a network device between your regular PulseAudio devices.