Shairport simulates an Airport Express. A Synology NAS box can output through use of a USB audio adapter (I am using a TerraTec AureonDualUSB stick). Since a NAS tends to be running 24/7 anyway, using it as an AirPlay bridge is an obvious choice.
Fair warning though: This is indeed not for the faint-hearted. It turned out to require quite a bit more fiddling and I had expected it to be.
First, you need to get Perl 5.10 running on the NAS. I’ve put up a separate entry on how I did this. Alternatively, you can try to fix the C version for shairport, or try to get my attempted 5.8 port to work).
shairport may require a number of libraries via optware – for example libao. I don’t remember what exactly I had to install.
The next problem is avahi. Synology includes avahi-daemon and uses it internally, but it doesn’t include the avahi-publish client, which shairport uses to announce itself. Further, the included avahi-daemon is compiled without dbus support, so we can’t send commands to it anyway. There are two options at this junction:
- Statically define a service for shairport in /usr/syno/avahi/services/
- Replace the builtin avahi-daemon with the optware package.
Both of those are imperfect solutions and both will probably need to be re-applied after a DSM upgrade. The first will not involve any editing of system scripts, but would then announce shairport even if it isn’t running, so I went with the second solution.
First, let’s install avahi and dbus. If we create the groups and users that are required up-front, we will have less problems later:
$ addgroup netdev
$ adduser avahi (errors can be ignored)
$ ipkg install avahi
$ ipkg install dbus
Possible error messages by adduser about lack of a home directory may be ignored. To test, shutdown the builtin avahi-daemon (/usr/syno/etc/rc.d/S99avahi.sh stop), and start the optware one: /opt/sbin/avahi-daemon . “avahi-browse -a” should show sensible information now. If it doesn’t, make sure dbus is running. Possibly, you need to delete the pid file (rm /opt/var/run/dbus/pid), then try starting it again (/opt/etc/init.d/S20dbus start).
To replace the builtin avahi-daemon, what I did was edit the file /usr/syno/etc/rc.d/S99avahi.sh to make the DAEMON variable point to /opt/sbin/avahi-daemon, and then I symlinked /opt/etc/init.d/S20dbus into /usr/syno/etc/rc.d/, so that dbus will start before avahi.
Another problem: shairport, at the time of this writing, uses 5000 as a default port, which will already be in use on your Synology NAS. You need to edit shairport.pl and choose a different port.
At this point, you should be able to run shairport, and see it appear as an AirPlay target in iTunes. (As an aside, the command line I have to use is: $ LD_LIBRARY_PATH=/opt/lib /usr/local/bin/perl /path/to/shairport.pl –
the full path to perl ensures that the right version is used, and /opt/lib contains required libraries and is not searched by the default).
Actually playing any audio probably doesn’t work yet. shairport is going to complain “could not open ao device”. On my device, I can play audio via /dev/dsp4. I identified that device by using “lsof | grep mplayer | grep dev” (lsof shows open files and can be installed via optware). You can also use the preinstalled /usr/syno/bin/mplayer to test your audio out from the console. Using “mplayer -v” will make mplayer show the device is is using.
We can tell shairport this by calling it like so:
$ LD_LIBRARY_PATH=/opt/lib /usr/local/bin/perl /path/to/shairport.pl --ao_driver=oss --ao_devicename=/dev/dsp4
It shairport still complains about not being able to open the device, the problem might be that have an older version of libao. You then need to edit shairport’s hairtunes.c and modify one of the calls to ao_append_option() so that the –ao_devicename option is applied using the key “dsp”, rather than “dev”.
To start shairport at boot, I’m using the following the following script, saved as /opt/etc/init.de/S90shairport:
#!/bin/sh
APNAME="Sonos: Bei Michael"
PIDFILE=/opt/var/run/shairport.pid
SHAIRPORT="/usr/local/bin/perl /opt/local/shairport/shairport.pl"
shairport_start() {
if [ "$(ps | grep shairport\.pl |grep -v grep)" > /dev/null ]; then
echo "shairport already running, use restart instead"
else
echo "Starting shairport..."
export PATH=/opt/bin:$PATH # for shairport to find the avahi-publish-service executable
echo LD_LIBRARY_PATH=/opt/lib $SHAIRPORT --ao_driver=oss --ao_devicename=/dev/dsp4 --apname=\"$APNAME\" -d --writepid=\"$PIDFILE\" | at now + 1 minutes
fi
}
shairport_stop() {
echo "Stopping shairport..."
kill `cat $PIDFILE`
rm $PIDFILE
}
shairport_restart() {
shairport_stop
sleep 1
shairport_start
}
case "$1" in
'start')
shairport_start
;;
'stop')
shairport_stop
;;
'restart')
shairport_restart
;;
*)
echo "Usage: start, stop, restart"
esac
Unfortunately, this script is full of hacks and problems. When running the script from a SSH session, when you exit, the audio stops playing (even thought the shairplay.pl process continues to run). I’m not sure why. If shairport is run as part of the boot process, this is not a problem.
Also note that I am piping the command into the at-daemon, to delay the start by a minute. Unfortunately, optware’s /opt/etc/init.d/* scripts seem to run *before* the stuff in /usr/syno/etc/rc.d/, so this is to ensure that avahi-daemon is already running by the time shairport starts.
Also, dbus makes trouble: Because it never clears out it’s pidfile on shutdown, after a reboot, dbus will not come up. I fixed this by strategically placing a rm -f $PIDFILE in /opt/etc/init.d/S20dbus. The correct way of course would be check whether the pidfile is stale before deleting it, but I fail at shell.
So there it is. You may just want to go and buy an Airport Express.