Multi-room-audio via Raspberry Pi en bijvoorbeeld Chromecast Audio

Whirlwind volgt de ontwikkelingen van home automation met interesse en schrijft uit eigen ervaring zo nu en dan een artikel over dit onderwerp. In dit artikel maken we middels de Snapcast-software op een Raspberry Pi met HiFiBerry-geluidskaart een multi-room-audiosysteem dat gevoed wordt door een Chromecast Audio (of een ander apparaat dat op de line-in van de geluidskaart kan worden aangesloten). De Snapcast-software en de Chromecast Audio kunnen beide door de home automation software Home Assistant bediend worden.

Stap 1: Snapcast installeren

Snapcast bestaat uit een server en client. Snapserver stuurt het geluid naar snapclients binnen het netwerk, waarna zij het geluid synchroon afspelen. Het is mogelijk om op dezelfde Raspberry Pi zowel snapserver als snapclient te installeren.

Op de Raspberry Pi maken we gebruik van het besturingssysteem Raspbian. Aangezien Raspbian gebaseerd is op de Linux-distributie Debian, kunnen we als volgt het Debian package van Snapcast downloaden. Op het moment van schrijven is Snapcast 0.16 de nieuwste versie. Zie https://github.com/badaix/snapcast/releases voor de actuele versie.

wget https://github.com/badaix/snapcast/releases/download/v0.16.0/snapserver_0.16.0_armhf.deb

Pak het Debian package als volgt uit en installeer het vervolgens:

sudo dpkg -i snapserver_0.16.0_armhf.deb

sudo apt-get -f install

Test snapserver als volgt. De tekst active (running) geeft aan dat snapserver draait.

sudo systemctl status snapserver

Herhaal de bovenstaande stappen voor de snapclient.

Stap 2a: Snapclient configureren

We gebruiken de HiFiBerry DAC+ ADC geluidskaart. De input van deze geluidskaart gebruiken we om de muziek van bijvoorbeeld een Chromecast Audio op te nemen en de output gebruiken we om de muziek af te spelen. Als volgt koppelen we snapclient aan de output van de geluidskaart. Met het onderstaande commando zoeken we eerst de naam van de geluidskaart op:

snapclient -l

Een lijst met geluidskaarten verschijnt. We lezen af dat default:CARD=sndrpihifiberry de naam van de geluidskaart is. Open het configuratiebestand van snapclient om de geluidskaart te koppelen:

sudo nano /etc/default/snapclient

Wijzig de waarde van SNAPCLIENT_OPTS als volgt:

SNAPCLIENT_OPTS="--soundcard default:CARD=sndrpihifiberry"

Stap 2b: Snapserver configureren

Standaard leest snapserver het FIFO-bestand /tmp/snapfifo uit. De muziekstream bevindt zich in dit bestand. Een FIFO-bestand (first in, first out) is een speciaal bestand waarbij geen data in het bestandssysteem wordt opgeslagen. Een FIFO-bestand is zodoende 0 bytes groot.

Normaal gesproken maakt snapserver het FIFO-bestand /tmp/snapfifo aan. Open het configuratiebestand om in te stellen dat snapserver enkel het FIFO-bestand dient te lezen; niet aanmaken.

sudo nano /etc/snapserver.conf

Wijzig de stream-instelling als volgt. Dankzij mode=read zal snapserver niet zelf het FIFO-bestand aanmaken.

stream = pipe:///tmp/snapfifo?name=default&mode=read

Stap 3: geluidsopname met arecord

Sluit een Chromecast Audio of een ander apparaat met een 3,5 mm jack-aansluiting aan op de line-in (stereo input) van de HiFiBerry DAC+ ADC geluidskaart en sluit een speaker aan op de output. Speel bijvoorbeeld Spotify af op de Chromecast Audio. Start de opname op de Raspberry Pi als volgt:

arecord --device=plughw:1,0 --format=S16_LE --rate=48000 --channels=2 /tmp/snapfifo

Bovenstaand commando neemt het geluid van de Chromecast Audio op en stuurt deze naar /tmp/snapfifo. Snapserver krijgt het geluid binnen en stuurt dit naar de snapclients. Mocht het volume te laag zijn, gebruik dan alsamixer om het volume in te stellen. Het programma arecord kan beëindigd worden met Ctrl-C.

  • De device-parameter bepaalt welke geluidskaart opgenomen wordt. Het nummer van de kaart kan gevonden worden via arecord -l. De geluidskaart heeft bijvoorbeeld nummer 1 en het device-nummer is 0.
  • De format-parameter bepaalt het sample format (16 bit).
  • De rate-parameter bepaalt de sampling rate in Hertz. Snapcast werkt standaard met 48000 Hertz.
  • De channels-parameter bepaalt het aantal kanalen: mono (1 kanaal) of stereo (2 kanalen).

Tip: schakel middels de apparaatinstellingen van de Chromecast Audio in de Google Home app de optie Volledig dynamisch bereik in. Deze instelling is bedoeld voor hifi-systemen en verbetert de geluidskwaliteit.

Stap 4: geluidsopname automatisch starten

Maak als volgt een service aan indien je de geluidsopname automatisch wilt starten bij het opstarten van de Raspberry Pi.

sudo nano /etc/systemd/system/arecord.service

Plaats het volgende in arecord.service:

[Unit]
Description=arecord
After=network-online.target snapserver.service
Requires=network-online.target snapserver.service

[Service]
User=pi
Group=pi
ExecStartPre=/home/pi/snapcast/arecord.sh
ExecStart=/usr/bin/arecord --device=plughw:1,0 --format=S16_LE --rate=48000 --channels=2 --fatal-errors /tmp/snapfifo
Restart=always

[Install]
WantedBy=multi-user.target

In de bovenstaande service is bij ExecStartPre vermeld dat het shell script arecord.sh gedraaid moet worden, voordat arecord wordt gestart (zie ExecStart). Het shell script doet het volgende:

  • arecord stoppen indien het actief is
  • bestanden in /tmp verwijderen waarvan de bestandsnaam met snap begint, omdat mogelijk snapfifo-01, snapfifo-02, enzovoort aangemaakt zijn
  • FIFO-bestand /tmp/snapfifo aanmaken

De inhoud van het shell script is als volgt:

#!/bin/sh

# arecord stoppen
killall -q arecord

# Oude FIFO-bestanden verwijderen
find /tmp -maxdepth 1 -type p -name "snap*" -delete

# Oude bestanden verwijderen
find /tmp -maxdepth 1 -type f -name "snap*" -delete

# Nieuwe snapfifo aanmaken
mkfifo /tmp/snapfifo

Maak het shell script uitvoerbaar met chmod a+rx arecord.sh.

Zoals hierboven vermeld kan het gebeuren dat snapfifo-01, snapfifo-02, enzovoort in /tmp aangemaakt worden. Indien dit gebeurt, willen we dat de arecord service automatisch herstart wordt. De ExecStartPre van de arecord service zal dan namelijk het shell script arecord.sh aanroepen, dat de ongewenste bestanden verwijdert.

Middels de onderstaande path unit monitoren we de aanwezigheid van bestanden in /tmp waarvan de bestandsnaam met snapfifo- begint. Maak deze path unit aan in /etc/systemd/system/arecord-watcher.path.

[Unit]
Description=arecord-watcher

[Path]
PathExistsGlob=/tmp/snapfifo-*

[Install]
WantedBy=multi-user.target

Maak /etc/systemd/system/arecord-watcher.service aan. Deze service wordt gestart door de bovenstaande path unit indien bijvoorbeeld /tmp/snapfifo-01 gedetecteerd is. Overigens is het van belang dat de naam van de path unit en service hetzelfde zijn, vandaar de namen arecord-watcher.path en arecord-watcher.service.

[Unit]
Description=arecord-watcher

[Service]
Type=oneshot
ExecStart=systemctl restart arecord.service

[Install]
WantedBy=multi-user.target

Herlaad als volgt systemd en stel in dat arecord.service en arecord-watcher.path gestart moeten worden bij het opstarten van de Raspberry Pi:

sudo systemctl daemon-reload

sudo systemctl enable arecord.service

sudo systemctl enable arecord-watcher.path

Bekijk de status van de services en de path unit met:

sudo systemctl status arecord.service

sudo systemctl status arecord-watcher.service

sudo systemctl status arecord-watcher.path

Stap 5: Snapcast-component in Home Assistant inschakelen

Als je de Snapcast-clients wilt beheren in Home Assistant, schakel je als volgt de Snapcast-component in. Open het configuratiebestand van Home Assistant en plaats daarin het volgende:

media_player:
  - platform: snapcast
    host: 192.168.178.22

Vul het IP-adres van de Raspberry Pi in waarop snapserver draait. Na de herstart van Home Assistant verschijnt een media player waarmee het volume van de snapclients ingesteld kan worden.

Tip: gebruik in Home Assistant ook de Google Cast-integratie. Hiermee kan de Chromecast gepauzeerd worden en naar het vorige en volgende nummer gesprongen worden. Verder is het volume instelbaar en wordt de cover getoond.

Stap 6: Spotcast custom component

Met de Spotcast custom component is het mogelijk om Spotify te starten op een idle Chromecast device. Dit maakt het mogelijk om een Spotify playlist te starten zonder gebruik te maken van de Spotify app, bijvoorbeeld na een druk op een knop.

Onderstaand een voorbeeld van een script dat Spotify via Spotcast start op een Chromecast:

spotify:
  alias: Spotify
  sequence:
    - service: spotcast.start
      data:
        entity_id: media_player.chromecast
        uri: 'spotify:playlist:21oVzNKpKHwtFsAYSVMkx9'
        random_song: true