2. Zone eines DENON-AVRs mit separatem AirPlay

DENON-AV-Receiver sind cool, denn sie klingen gut und man kann die netzwerkfähigen Geräte vollständig über eine HTTP-API steuern, welche DENON auch offenlegt.
Wie es sich für netzwerkfähige AV-Receiver gehört, machen die Modelle von DENEN auch AirPlay. Die Titelinformationen werden auf dem Display und ggf. auf dem Fernseher angezeigt, inklusive Album-Cover. Außerdem lässt sich die Receiver-Lautstärke vom iOS-/OS X-Gerät aus steuern, die Fernbedienung des Receivers ist auch vollständig nutzbar.

Das ganze hat leider nur einen kleinen Haken: viele DENON-Receiver besitzen zusätzliche Lautsprecheranschlüsse für eine zweite Zone, sodass man z.B. die Küche mit Radio beschallen kann, während im Wohnzimmer ein Film läuft.
Die Spotify-Connect-/Mediaserver- und AirPlay-Funktionalität des Receivers ist jedoch intern über einen einzigen Eingang mit dem Verstärker-Teil des Receivers verbunden, die separate Nutzung von zwei AirPlay-Instanzen oder Airplay und Spotify-Connect zusammen funktioniert also nicht; es wird auch nur ein AirPlay- und ein Spotify-Device im Netzwerk angekündigt.

Enter Raspberry Pi

Das AirPlay-Protokoll ist schon lange reverseengineert worden und es gibt einige OpenSource-Implementierungen. Die meines Erachtens beste ist Shairport-Sync. Shairport-Sync ist ein Fork des Originals, welches erstmals funktionierendes AirPlay aus dem reverseengineerten Protokoll umsetzte. Es unterstützt – im Gegensatz zum Original – auch den Teil des Protokolls, der für eine synchrone Wiedergabe auf mehreren Geräten sorgt. Zudem kann es die Metadaten vom Abspieldenden Gerät empfangen und hat auch sonst noch einige nützliche Funktionen unter der Haube.

Zunächst benötigt ihr einen Raspberry Pi mit aktualisierten Repositories. Und wir werden root. Weil ich zu faul bin, immer wieder sudo einzutippen.

$ sudo su
$ apt-get update
$ apt-get upgrade

Da ich in diesem Tutorial davon ausgehe, dass ihr Raspbian GNU/Linux 8.0 (jessie) oder neuer laufen habt, wollen wir nun erstmal schauen, ob das gegeben ist:

$ apt-get install lsb-release
$ lsb_release -a

Zunächst sollten wir mal unser Audio-Setup anschauen. Zuerst geben wir dem System aber noch ein Dummy-Adio-Device. Warum erfahrt ihr weiter unten.

modprobe snd-dummy

Damit der Pi das bei jedem Start automatisch macht, packen wir das ganze noch in die modules-Datei:

echo "snd-dummy" >> /etc/modules

Nun schauen wir, welche Audio-Devices es so im System gibt. Falls ihr eine USB-Soundkarte verwenden wollt, wäre jetzt ein guter Zeitpunkt, um sie anzustecken.

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]
  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: ALSA [bcm2835 ALSA], device 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: Dummy [Dummy], device 0: Dummy PCM [Dummy 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

Wie ihr seht, ist der analoge Part der Pi-onboard-Soundkarte hier card0 und device0; die Dummy-Soundkarte, die wir eben angelegt haben, ist card1 und device0.
Wir merken uns das für später.

Nun brauchen wir git, um shairport-sync clonen zu können und ein paar Pakete, mit denen wir es anschließend kompilieren:

$ apt-get install git screen autoconf libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev avahi-daemon libavahi-client-dev libssl-dev

Jetzt legen wir uns erstmal einen Order für das ganze Gedöns an und wechseln in diesen:

$ mkdir airplay/
$ cd airplay

Dann clonen wir den shit und gehen in dessen Ordner:

$ git clone https://github.com/mikebrady/shairport-sync.git
$ cd shairport-sync/

conf-File generieren und ausführen:

$ autoreconf -i -f
$ ./configure --with-alsa --with-avahi --with-ssl=openssl --with-systemd

Maken und – wenn keine Fehler aufgetreten sind – installen:

$ make
$ make install

Nun machen wir noch ein paar User-Dinge und sagen systemd, dass es Shairport-Sync beim Booten starten soll:

$ getent group shairport-sync &>/dev/null || groupadd -r shairport-sync >/dev/null
$ getent passwd shairport-sync &> /dev/null || useradd -r -M -g shairport-sync -s /usr/bin/nologin -G audio shairport-sync >/dev/null
$ systemctl enable shairport-sync

In der Shairport-Sync-Konfigurationsdatei (/etc/shairport-sync.conf) kann man nun alles Mögliche nach Belieben konfigurieren. Die Optionen sind alle in Kommentaren erklärt. Unter /etc/shairport-sync.conf.sample findet ihr eine Datei mit Beispieloptionen.
Zeile 44–51 der Konfigurationsdatei definiert ein paar Parameter zur Audio-Hardware. Hier kommen nun die Card- und Device-IDs zum Einsatz, die wir oben nachgeschaut haben:
In Zeile 46 definieren wir das Gerät, auf dem Shairport-Sync Audio ausgeben soll. Die beiden Nullen stehen dabei für die Card und das Device.

output_device = "hw:0,0";

In Zeile 47 und 48 geben wir nun aber einen abweichenden Mixer an: den der Dummy-Soundkarte. Hier reicht die Nummer der Card (1) und der Name des Mixer-Reglers (standardmäßig „Master“):

mixer_control_name = "Master";
mixer_device = "hw:1";

Nun einmal

$ reboot

…en und schauen, ob AirPlay schon funzt.

Was nun passieren sollte:
Shairport sollte auf dem internen Audio-Port des Raspberry Pi Audio abspielen. Wenn man die Lautstärke z.B. am iPhone verstellt, sollte sich die Lautstärke jedoch nicht verändern. Warum? Genau: Wir haben Shairport ja gesagt, dass es den Regler der Dummy-Soundkarte verschieben soll, wenn es Lautstärkeänderungen gibt.
Kontrollieren kann man das mit

$ alsamixer

Hier sieht man nun wahrscheinlich einen einzelnen Regler, den des Raspberry-Audioports. Mit [Up] und [Down] kann man nun die Lautstärke verstellen.
Die AirPlay-Lautstärke sollte diesen Regler nicht verstellen.

Mit [F6] kann man auf die Dummy-Soundkarte wechseln. Diese hat ein paar mehr Regler, aber uns interessiert nur der Master ganz links. Dieser sollte die AirPlay-Lautstärke widerspiegeln. Probiert's aus, es aktualisiert sich live.

Vielleicht fragt ihr euch, warum wir den ganzen Scheiß hier machen. Ganz einfach:
Wir wollen dreierlei Dinge erreichen:

  • Wir wollen, dass Shairport den Receiver bzw. dessen zweite Zone startet, sobald man beginnt, etwas abzuspielen. Außerdem sollte die zweite Zone noch auf den Eingang geschaltet werden, an dem der Raspberry Pi angeschlossen ist.
  • Wenn AirPlay endet (Playlist vorbei oder lang genug Pause), soll der Receiver wieder abgeschaltet werden.
  • Die Lautstärke, die man über das iGerät einstellt, soll nicht die Raspberry-Soundkarte regeln. Stattdessen sollen diese Lautstärkeeinstellungen an den DENON-Receiver weitergegeben werden.

Wenn Shairport funktioniert, legen wir erstmal einen Ordner für die Scripts an, die wir gleich verwenden werden:

$ mkdir /usr/local/scripts

In diesen Ordner zunächst folgendes Script:

/usr/local/scripts/airplaytrigger:

#!/bin/bash

ZONEURL='http://avr.local/MainZone/index.put.asp'

amixer -c 0 sset PCM 93%

if [[ $1 == 'start' ]]; then
    curl $ZONEURL --data 'cmd0=PutZone_OnOff%2FON&ZoneName=ZONE2' > /dev/null 2> /dev/null
    curl $ZONEURL --data 'cmd0=PutZone_InputFunction%2FGAME&ZoneName=ZONE2' > /dev/null 2> /dev/null
elif [[ $1 == 'stop' ]]; then
    curl $ZONEURL --data 'cmd0=PutZone_OnOff%2FOFF&ZoneName=ZONE2' > /dev/null 2> /dev/null
fi

(Auch ausführbar machen…)

$ chmod -x /usr/local/scripts/airplaytrigger

Die fettgedruckten Teile des Script müsst ihr ggf. ändern:

ZONEURL='http://avr.local/MainZone/index.put.asp'

Statt „avr.local“ tragt ihr hier die Adresse eures Receivers ein.

amixer -c 0 sset PCM 93%

-c 0“ repräsentiert hier die Card-ID der Soundkarte, auf der Shairport Audio abspielen soll (in meinem Fall die interne Soundkarte des Pi, die bei mir die Nummer 0 hat). Dieser Befehl setzt die Lautstärke des PCM-Reglers dieser Karte bei jeder AirPlay-Session auf 93%. 93% ist die prozentuale Lautstärke, die mir der alsamixer anzeigt, wenn der Regler auf 75/100 gesetzt ist. Für mein Setup ist das ein guter Wert, ggf. ist für euch 100% oder 50% besser.

curl $ZONEURL --data 'cmd0=PutZone_InputFunction%2FGAME&ZoneName=ZONE2' > /dev/null 2> /dev/null

GAME“ ist der Name des Inputs, den ihr am Receiver für den Raspberry Pi verwendet. Evtl. ist das ein analoger Eingang, wenn ihr eine fancyge Soundkarte benutzt, empfehle ich jedoch, eine S/PDIF oder Toslink-Verbindung zu nutzen. Im Webinterface des Receivers kann man die Eingänge an der Rückseite dem Entsprechenden Input zuordnen. Man kann die Inputs auch umbenennen. Quellen umbenennen im Webinterface Quellenzuordnung zu Eingängen

In den Bildern sieht man, dass ich den Input „Game“ in „ExtAirplay“ umbenannt und dann „ExtAirplay“ den 1. analogen Eingang zugeordnet habe.

Obwohl der Input jetzt „ExtAirplay“ heißt, muss in der HTTP-API der Originalname genutzt werden –> GAME.

Shairport Scripts aufrufen lassen

Nun sagen wir Shairport in seinen Einstellugnen unter /etc/shairport-sync.conf, dass es dieses Script aufrufen soll, wenn AirPlay beginnt und endet.
Zeile 33–39:

{
    run_this_before_play_begins = "/usr/local/scripts/airplaytrigger start";
    run_this_after_play_ends = "/usr/local/scripts/airplaytrigger stop";
    wait_for_completion = "yes";
    allow_session_interruption = "yes";
    session_timeout = 30;
};

An den Optionen

allow_session_interruption = "yes";

und

session_timeout = 30;

könnt ihr selbst mal ein bisschen rumspielen.

Nach einem

$ reboot

sollte sich der Receiver schon automatisch ein- und ausschalten.

Lautstärke an den Receiver übergeben

Hierfür legt ihr nun ein weiteres Script an:
/usr/local/scripts/airplayvolume:

#!/bin/bash

OLDVOL=0%

while : ; do
    NOWVOL=$(awk -F"[][]" '/dB/ { print $2 }' <(amixer -c 1 sget Master) | head -n 1)
    if [[ $NOWVOL != $OLDVOL ]]; then
        VOLUME=${NOWVOL::-1}
        VOLUME=$(expr $(expr 700 \* $VOLUME \* -1 + 80000) / 1000 \* -1)
        echo "Changing to ${NOWVOL} / ${VOLUME} dB"
        curl -m 0.5 "http://avr.local/MainZone/index.put.asp" --data "cmd0=PutMasterVolumeSet/${VOLUME}.0&ZoneName=ZONE2" > /dev/null 2> /dev/null
        OLDVOL=$NOWVOL
    fi
    sleep 0.1
done

(Auch ausführbar machen…)

$ chmod -x /usr/local/scripts/airplayvolume

In diesem Script müsst ihr folgendes anpassen:

NOWVOL=$(awk -F"[][]" '/dB/ { print $2 }' <(amixer -c 1 sget Master) | head -n 1)

-c 1“ ist die Nummer der Dummy-Soundkarte. Ihr habt vielleicht eine andere Nummer.
Master“ ist der Name des Reglers.

curl -m 0.5 "http://avr.local/MainZone/index.put.asp" --data "cmd0=PutMasterVolumeSet/${VOLUME}.0&ZoneName=ZONE2" > /dev/null 2> /dev/null

avr.local“ solltet ihr durch die Adresse eures Receivers ersetzen.

Beim Booten ausführen

Dieses Script muss nun beim Start ausgeführt werden. Dafür gibt es verschiedenste Wege, der schnellste und dreckigste ist, es in einem screen aus /etc/rc.local heraus zu starten.
Danach sollte diese Datei in etwa so aussehen:

#!/bin/sh -e

#[…]

screen -dmS airplayvolume /usr/local/scripts/airplayvolume

exit 0

(Das „exit 0“ muss ganz am Ende stehen…)

Ein

reboot

zum Schluss. Jetzt könnt ihr eure zweite Zone separat beschallen.

Yey. \o/