install snapserver - fcorthay/RPi-multiroom-audio GitHub Wiki
This documentation bases on the Snapcast documentation.
Multiroom audio is implemented through Snapcast.
In order to stream the music, install the Snap server:
sudo apt install -y snapserver sudo chgrp users /etc/snapserver.conf sudo chmod 664 /etc/snapserver.conf
Allow streaming to both IPV4 and IPV6:
ORIGINAL='#bind_to_address = 0.0.0.0' REPLACEMENT="$ORIGINAL\nbind_to_address = ::" sudo sed -i "s/$ORIGINAL/$REPLACEMENT/" /etc/snapserver.conf
The basic source for snapserver
is a FIFO.
However we will be using the capture from ALSA,
using the loopback
device.
The reason for this is that one cannot mix mopidy together with the snapcast receiver, snapclient,
onto the same soundcard, using the ALSA dmix
mechanism (at least not to my knowledge).
The solution to this is to disconnect mopidy from the soundcard,
connect it to the ALSA Loopback
device, subdevice SNAPSERVER_LOOPBACK_SUBDEVICE
,
have snapserver stream this audio to the Ethernet port, and have snapclient receive it and send it to the soundcard.
This audio path also allows other devices on the same Ethernet network to receive and play the same music as the RPi.
If we consider to later also redirect the snapclient output via the Loopback
,
in order to have some signal processing right before the loudspeakers,
the subdevice SNAPSERVER_LOOPBACK_SUBDEVICE
, defined in configuration.bash
,
should not be 0 as snapclient only uses subdevice 0 (at least as to my knowledge).
Hence the choice of the subdevice 1.
With this, we can change the audio stream source in the snapserver configuration:
source ~/Documents/RPi-multiroom-audio/configuration.bash ORIGINAL='source = pipe:///tmp/snapfifo?name=default' REPLACEMENT="#$ORIGINAL\nsource = alsa:///?name=Loopback&device=hw:$ALSA_LOOPBACK_PLAYBACK_DEVICE,$SNAPSERVER_LOOPBACK_SUBDEVICE" ORIGINAL=${ORIGINAL////\\/} REPLACEMENT=${REPLACEMENT//&/\\&} REPLACEMENT=${REPLACEMENT////\\/} sudo sed -i "s/$ORIGINAL/$REPLACEMENT/" /etc/snapserver.conf
Change the audio sample format to math the one in the configuration:
source ~/Documents/RPi-multiroom-audio/configuration.bash ORIGINAL='#sampleformat = 48000:16:2' REPLACEMENT="$ORIGINAL\nsampleformat = $AUDIO_RATE:$AUDIO_BIT_NB:2" sudo sed -i "s/$ORIGINAL/$REPLACEMENT/" /etc/snapserver.conf
Add the snapserver
user to the audio
group:
SNAPSERVER_USER=`cat /lib/systemd/system/snapserver.service | grep ^User | cut -d '=' -f 2` sudo usermod -a -G audio $SNAPSERVER_USER cat /etc/group | grep ^audio
Restart the service:
sudo service snapserver restart
Check how it is working:
ps aux | grep snap | grep -v grep sudo service snapserver status | cat
Connect the mopidy output to the Loopback
device:
source ~/Documents/RPi-multiroom-audio/configuration.bash ORIGINAL="output = alsasink device=dmix:$AMPLIFIER_SOUNDCARD" REPLACEMENT="#$ORIGINAL\noutput = audioresample ! audioconvert" REPLACEMENT+=" ! audio\/x-raw,rate=$AUDIO_RATE,channels=2,format=S${AUDIO_BIT_NB}LE" REPLACEMENT+=" ! alsasink device=dmix:$ALSA_LOOPBACK_CAPTURE_DEVICE,$SNAPSERVER_LOOPBACK_SUBDEVICE" sudo sed -i "s/$ORIGINAL/$REPLACEMENT/" /etc/mopidy/mopidy.conf sudo service mopidy restart
With this, the mopidy audio should now pay on the audio receiver device.
For a quick test, install Snapcast on Android or Snapcast on iOS.
Play a file on the Loopback device
:
alias play2loop="SDL_AUDIODRIVER='alsa' AUDIODEV='dmix:$ALSA_LOOPBACK_CAPTURE_DEVICE,$SNAPSERVER_LOOPBACK_SUBDEVICE' ffplay -ar $AUDIO_RATE -autoexit -nodisp -loglevel quiet" play2loop ~/Music/someFile.flac
Check the RPi's address:
ifconfig | grep -A 2 eth0
Specify the snapcast server on the mobile device accordingly and play what is being streamed.
Snapcast
can be controlled per JavaScript Object Notation - Remote Procedure Call (JSON-RPC).
For complex objects to be displayed in a terminal, install jq:
sudo apt install -y jq
Making a JSON-RPC request can be done using curl in a terminal.
The Snapcast Application Programming Interface (API) is documented in the GitHub documentation.
Getting the server status allows, among others, to get the client ids:
curl -s -d '{"jsonrpc":"2.0", "id":1, "method":"Server.GetStatus"}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc | jq
The client ids seem to be their MAC address.
The client locally playing has "id": "00:00:00:00:00:00"
.
Get a client's volume:
curl -s -d '{"jsonrpc":"2.0", "id":1, "method":"Client.GetStatus", "params":{"id":"00:00:00:00:00:00"}}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc | jq | grep -A 3 volume
Set a client's volume and mute status:
curl -sw "\n" -d '{"jsonrpc":"2.0", "id":1, "method":"Client.SetVolume", "params":{"id":"00:00:00:00:00:00", "volume":{"muted":false, "percent":75}}}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc
Set a client's volume only:
curl -sw "\n" -d '{"jsonrpc":"2.0", "id":1, "method":"Client.SetVolume", "params":{"id":"00:00:00:00:00:00", "volume":{"percent":50}}}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc
Mute a client's sound (adapt id):
curl -sw "\n" -d '{"jsonrpc":"2.0", "id":1, "method":"Client.SetVolume", "params":{"id":"00:00:00:00:00:00", "volume":{"muted":true}}}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc
Remove a client (adapt id):
curl -sw "\n" -d '{"jsonrpc":"2.0", "id":1, "method":"Server.DeleteClient", "params":{"id":"00:00:00:00:00:00"}}' -H 'Content-Type: application/json' http://localhost:1780/jsonrpc
Here a Python example for a Snapcast request:
#!/usr/bin/env python3 import json import requests SNAP_SERVER_NAME='localhost' SNAP_SERVER_PORT=1780 url = "http://%s:%d/jsonrpc" % (SNAP_SERVER_NAME, SNAP_SERVER_PORT) request_id = 0 payload = {'jsonrpc': '2.0', 'id': request_id} payload['method'] = 'Server.GetStatus' response = requests.post(url, data=json.dumps(payload)).json() request_id = request_id + 1 groups_info = response['result']['server']['groups'] for group_info in groups_info : for client_info in group_info['clients'] : client_name = client_info['host']['name'] client_id = client_info['id'] print("%s : %s" % (client_name, client_id))
In the repository:
-
Snapcast/getStatus.bash
performs the above request with additional bells and whistles -
Snapcast/setVolume.bash
allows to change the volume of one of the snapserver's clients
~/.bash_aliases
:
source ~/Documents/RPi-multiroom-audio/configuration.bash alias getsnapserver='SNAPCAST_SERVER=`cat /etc/default/snapclient | grep ^SNAPCLIENT_OPTS | sed "s/.*--host\s*//" | sed "s/[ \"].*//"`' alias volume='getsnapserver && $AUDIO_BASE_DIR/Snapcast/setVolume.py -s $SNAPCAST_SERVER $(hostname)' alias snapstatus='getsnapserver && $AUDIO_BASE_DIR/Snapcast/getStatus.py -s $SNAPCAST_SERVER'
Check the audio path:
source ~/Documents/RPi-multiroom-audio/configuration.bash $AUDIO_BASE_DIR/Management/showInstallation.bash
See the devices served:
source ~/Documents/RPi-multiroom-audio/configuration.bash $AUDIO_BASE_DIR/Snapcast/getStatus.py
Change the volume on one of the devices:
source ~/Documents/RPi-multiroom-audio/configuration.bash $AUDIO_BASE_DIR/Snapcast/setVolume.py AudioAmp 50
The present installation is presented hereafter:
Mopidy reads music from the disk and sends it to the the loopback device. Snapserver reads music from the loopback device and sends it to the Ethernet port. And indeed : there is no more sound from the RPi, yet one can listen to the music on another device.
Continue the installation process.