Multiroom Audio with Librespot and Snapcast
Snapcast is an open-source synchronous multiroom client-server audio player and Librespot is an open source client library for Spotify - a perfect combination to create a free multiroom audio solution. Librespot works as a spotify connect receiver and can be controlled by regular Spotify apps, while Snapcast serves time synchronized audio to snapcast clients over the network (wifi).
While Librespot and Snapcast support several backends and sources, respectively, this article focuses on using Alsa backend.(1)
Though
pipe
backend works, snapcast appears to intermittently crash when usingpipe
Virtual Sound Card¶
As shown in Snapcast's documentation, we'll use the snd-aloop
kernel module to create a virtual sound card.
To load this kernel module at boot, add the snd-aloop
to /etc/modules
The following output shows the sound cards before loading the module.
red@avani:~$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC283 Analog [ALC283 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 7: HDMI 1 [HDMI 1]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 9: HDMI 3 [HDMI 3]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 10: HDMI 4 [HDMI 4]
Subdevices: 1/1
Subdevice #0: subdevice #0
Output of aplay -l
after rebooting the host
red@avani:~$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 0: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM]
Subdevices: 8/8
Subdevice #0: subdevice #0
Subdevice #1: subdevice #1
Subdevice #2: subdevice #2
Subdevice #3: subdevice #3
Subdevice #4: subdevice #4
Subdevice #5: subdevice #5
Subdevice #6: subdevice #6
Subdevice #7: subdevice #7
card 1: PCH [HDA Intel PCH], device 0: ALC283 Analog [ALC283 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 7: HDMI 1 [HDMI 1]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 8: HDMI 2 [HDMI 2]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 9: HDMI 3 [HDMI 3]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 10: HDMI 4 [HDMI 4]
Subdevices: 1/1
Subdevice #0: subdevice #0
red@avani:~$
Note
The Loopback card might show up with a different id when loading the kernel moduel using the modprobe snd-aloop
command as opposed to having the module load at boot.
Notice the card number, device number and the sub-device numbers on the entries labelled Loopback. They form the hardware id in the format cardnumber,devicenumber,subdevicenumber, (Ex. hw:0,0,5)
. Card number indicates the hardware ID the OS recognized, and the two devices (with id 0
and 1
) form a loop. Audio sent to device 0
on a sub-device (Ex. hw:0,0,5)
will be delivered (looped) on device 1
on the same sub-device (Ex. hw:0,1,5)
.
Librespot¶
Though linux package managers include librespot, they are usually not up to date. Librepot can be compiled with required features. Install the OS related dependecies as mentioned in librespot github as well as Rust to compile the software.
Check Version
Config¶
The Alsa sound card will be passed device to librespot along with a few other parameters such as player name, bitrate, etc. Following are some common parameters passed. Store these parameters in a config file /etc/librespot/librespot.conf
cat <<EOF | sudo tee /etc/librespot/librespot.conf
name=Home-Multiroom #Name of the spotify player
backend=alsa #Since we are using alsa in this example
device=hw:0,0,0 # Id of the cardnumber,devicenumber,subdevicenumber
bitrate=320
initial_volume=80
device_type=avr #Device type indicator when it shows up in Spotify players list
cache_dir=/tmp/spotify/
EOF
Librespot systemd service file¶
Setup librespot to run as a systemd service
cat <<EOF | sudo tee /etc/systemd/system/librespot.service
[Unit]
Description=Librespot Spotify Client
After=network.target
Wants=snapserver.service
[Service]
EnvironmentFile=/etc/librespot/librespot.conf
ExecStart=/usr/bin/librespot \
--name ${name} \
--backend ${backend} \
--device ${device} \
--bitrate ${bitrate} \
--system-cache ${cache_dir} \
--cache ${cache_dir} \
--initial-volume ${initial_volume} \
--device-type ${device_type} \
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
Run librespot¶
Run sudo systemctl daemon-reload
to load the new service file and then start the service with sudo systemctl enable librespot --now
. View the status of librespot using systemctl status librespot
and use journalctl -u librespot
to view the librespot's systemd service logs, if needed.
Snapserver¶
Download and install snapserver packages for the distribution from releases page.
Config¶
The Snapcast server receives audio on the other device of the virtual sound card. Similar to librespot, snapserver will take the hardware id, hw:0,1,0
in this case.
cat <<EOF | sudo tee /etc/snapserver.conf
[stream]
source = alsa:///?name=multiroom&device=hw:0,1,0&sampleformat=44100:16:2
[http]
enabled = true
bind_to_address = 0.0.0.0
port = 1780
doc_root = /usr/share/snapserver/snapweb
[tcp]
enabled = true
bind_to_address = 0.0.0.0
port = 1705
EOF
In addition, the builtin snapweb can be reached using port 1780 (http) or 1788 (https)
.
Run snapserver¶
Start the service with sudo systemctl enable snapserver --now
. View the status of librespot using systemctl status snapserver
and use journalctl -u snapserver
to view the snapserver's systemd service logs, if needed.