This collection is being used to maintain (at least) three quite different multiroom audio setups based on Raspberry Pi and high quality audio HATs. We use hifiberry, but this works with any supported audio HAT. We are happy to hear from anyone else being successful using these roles.
The roles support setting up an audio multiroom system from a collection of Raspberry Pis. We try to make available all audio sources, so you can
As a side product, it is also possible with these roles to create a standalone mpd music box that can be used offline. If you are crazy enough, you could run snapcast
there too :smile:.
Hardware audio sources and sinks need to be driven individually, so that we can receive audio from the analog input, digitize it, stream it synchronously over the network to (all other clients and) the local client that sends the audio to the analog output. This will have some delay, but exactly the same delay as all other networked clients. On the other hand, a soundcard has one global digital clock for all inputs/outputs. Therefore, we use only one digital audio format in all components and set that format explicitly wherever possible. This also minimizes the CPU load (and audio quality loss) for unwanted format conversions.
ALSA dmix devices can be used to mix multiple streams together (provided that all streams have the same digital audio format) without any conversion.
dsnoop devices can be used to consume the same audio stream by multiple recorders. dmix
/dsnoop
devices proved to be very useful, but they are
apparently not usable for alsaloop
as sinks/sources.
In order to support multiple audio inputs and mix them with a dmix
device, we use the snd_aloop
kernel module. This creates a virtual soundcard and whatever you stream to dmix:CARD=Loopback,DEV=0
comes out of device=dsnoop:CARD=Loopback,DEV=1
(and 1->2 and 2->3 etc). To make that work without any audio stuttering or other issues, the default audio format for dmix
/dsnoop
has to be set
in /etc/asound.conf
.
Implementation on Pi
Ansible implementation
inventory
mapping Pi hosts to group_vars
playbook
mapping roles to hosts or host groupshost_vars
/group_vars
base (TODO rename)
dmix
or dsnoop
devices. That means always.Multiroom (snapcast)
1780
) to snapserver that allows to control the snapclient volumes with a browser.Connecting the ALSA streams
snd_aloop
kernel module if you need an alsa loopback devicearec | aplay
commands with configurable variables (e.g. an alsaloop
that can use dmix
/dsnoop
devices)bluetooth_sink
is also mapped to that node.Bluetooth
acable
.acable
to accept an audio stream via bluetooth A2DP:
bluetoothd
drives the hardwarea2dp-agent.py
configures a systemd unitrfkill
) if not usedrfkill
. Needed as separate role to handle devices without acable
at all.MPD
Spotify
Standalone Music Box
accesspoint
role to be executed beforefirewalld
and udev
based management of all other network interfaces (on top of the Raspbian default dhcpcd
). As soon as e.g. the builtin ethernet or an additional WLAN USB dongle has internet connectivity, the accesspoint
clients will have too.Ansible host:
community.general
collection for community.general.modprobe
, install with ansible-galaxy collection install community.general
.t2d.raspotify
role as base for the raspotify role here, install with ansible-galaxy install t2d.raspotify
.ansible.posix
for the usbmount
role.Pi:
pi
touch /boot/ssh
/boot/wpa_supplicant.conf
when you need WLAN from the start/boot/config.txt
: # dtparam=audio=on
/boot/config.txt
. You should see it with aplay -L
sed -iBAK -e "s/raspberrypi/NEWHOSTNAME/g" /etc/hostname /etc/hosts
rm -v /etc/ssh/ssh_host_*
and dpkg-reconfigure openssh-server
rm /etc/machine-id
, systemd-machine-id-setup
rm /var/lib/dhcpcd/*.lease*
, systemctl restart dhcpcd
pi
(and not root
) append -u pi
to the command line.Your Environment:
hifiberry
AMP module) or a stereo equipment and a hifiberry
DAC/ADC module.amsel_small_server
setup) correctly.1) Populate the variables in the host_vars/$HOSTGROUPNAME/main.yml
files with your settings. Checkout roles/*/defaults/main.yml
to see all variables available.
2) Populate the inventory
file with a [snapclient]
and [snapservers]
hostgroup and list all snapclients/servers you want to target
3) Write a playbook
A working example uses the following files (TODO change this before going to ansible galaxy):
multiroom-amsel.yml
inventory_amsel
group_vars/amsel*
(TODO change this before going to ansible galaxy)
In the docroot of this repo, do the following (in check mode).
ansible-playbook multiroom-amsel.yml --check --diff -u pi -i inventory_amsel
--check
from the command line. root
on the Pi (and not pi
), remove -u pi
from the command line