Airplay to Squeezebox Touch (via Linux)




This post focuses on playing music from iTunes or iOS devices to a Logitech Squeezebox using AirPlay. The concept is to stream music from iTunes or iOS devices > Ubuntu server > Squeezebox. This requires three key tools Shairport, WaveInput, ecasound and some patience.

Shairport authored by James Laird in 2011 emulates an AirPort Express for the purpose of streaming music from iTunes and compatible iPods. It implements a server for the Apple RAOP protocol, it does not support AirPlay v2 (video and photo streaming). It is a reverse engineer of the RAOP protocol and relies upon the ALAC decoder released by David Hammerton, which is reproduced within Shairport. The project can be found at https://github.com/albertz/shairport

WaveInput authored by BPA is a Logitech Squeezebox plugin that enables PC audio to be captured and played through a Squeezebox. There are version of Windows and Linux and the project can be found here http://code.google.com/p/bpaplugins/

ecasound is a multitrack processing tool for playback, recording, format conversions, effects and mixing. Its use here is to transcode the raw stream from Airplay/Shairport into a format the Squeezebox can understand. I use it to introduce some LAME MP3 compression to the stream to help with my network bandwidth and to reduce playback stutter.


 This article relies heavily upon the excellent work of Stuart Shelton and this post on the Logitech forums. There are two main methods for transferring the sound from shairport to the squeezebox:

The first relies upon piping the shairport stream to a raw data pipe which is then encoded to the Logitech Media server; and
The second uses a loopback device whereby shairport outputs audio to a loopback device which then feeds into the encoder for Logitech media server.

I'll discuss both methods here but will start with the raw pipe method since it is a lot easier to set up (although I am experiencing some performance issues with this approach on my Atom based server.

Shairplay to Raw pipe approach
The flow of music is:

iTunes / iOS
> via Airplay
> Shairport on Ubuntu
> raw date pipe
> ecasound on Ubuntu (to transcode the raw Airplay stream to compressed FLAC, MP3 or raw PCM)
> PCM stream (my server was not powerful enough for real time FLAC / MP3 encoding)
> WaveInput plugin on the Logitech Media Server (running on Ubuntu)
> via wi-fi or ethernet
> Squeezebox
> speakers

I hope the above helps someone, it certainly helped me.

Note Shairport requires that Bonjour / Avahi service that advertises the Airplay service to iTunes / the iOS device is already setup and configured on your Ubuntu server. It was already configured on my server from a previous effort at setting up an Apple Time Machine server you can read about setting up Avahi here from post 6 onwards. I won't be setting up Avahi in this post.


Here's how I got Airplay working:


Logitech Media Server
1. Login into the Logitech Media Server ("LMS" or squeezeboxserver) via a browser at http://serverIP:9000/

2. Goto Settings > Plugins > At the bottom under "Additional repositories" add http://bpaplugins.googlecode.com/svn/trunk/repo.xml

3. Once the repository has been added under the 3rd party plugin section above enable "WaveInput (v1.04)" by bpa

4. Add a new favourite to the Squeezebox via LMS
Title: Squeezebox Airplay
URL: wavin:default


On the Ubuntu server (running 10.04 LTS)
1. Install the prerequisites for Shairport and WaveInput
apt-get install build-essential libssl-dev libcrypt-openssl-rsa-perl libao-dev libio-socket-inet6-perl libwww-perl avahi-utils pkg-config ecasound

2. Install the additional required perl packages
sudo perl -MCPAN -e 'install Crypt::OpenSSL::RSA'
sudo perl -MCPAN -e 'install HTTP::Message'
sudo perl -MCPAN -e 'install IO::Socket::INET6'
sudo perl -MCPAN -e 'install IO::Socket::SSL'
sudo perl -MCPAN -e 'install Net::SDP'

3. Install Shairport to the bin directory
cd /usr/bin

4. Download the source code
sudo git clone https://github.com/albertz/shairport.git

5. Change directory to the source code directory
cd shairport

6. Build the Shairport perl executable
sudo make

7. Create the pipe for shairport to pipe the music to WaveInput
sudo mkfifo /var/lib/squeezeboxserver/airplay-fifo.raw

8. Make the pipe accessible by the squeezeboxserver user (which will run shairport and LMS)
sudo chown squeezeboxserver /var/lib/squeezeboxserver/airplay-fifo.raw
sudo chmod 755 /var/lib/squeezeboxserver/airplay-fifo.raw

9. Adjust the firewall, forgive me I use ufw
sudo ufw allow from any to any port 5000:5005 proto tcp
sudo ufw allow from any to any port 6000:6005 proto udp

10. Move the WaveInput plugin settings from the cache to a more permanent place
sudo mv /var/lib/squeezeboxserver/cache/InstalledPlugins/Plugins/WaveInput /var/lib/squeezeboxserver/Plugins/WaveInput

11. Backup the default 'arecord' configuration for WaveInput
cd /var/lib/squeezeboxserver/Plugins/WaveInput
sudo mv custom-convert.conf custom-convert.conf.bak

12. Create a new configuration for WaveInput using ecasound, 
sudo touch custom-convert.conf
sudo gedit custom-convert.conf

The config is as follows, the tab indentation is important:
#
# wavin 
#
wavin pcm * * 
        # R
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -i /var/lib/squeezeboxserver/airplay-fifo.raw -o stdout
wavin mp3 * *
        # RB:{BITRATE=-B %B}
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -i /var/lib/squeezeboxserver/airplay-fifo.raw -o stdout | [lame] --silent -r -q $QUALITY$ -v $BITRATE$ - -
wavin flc * * 
        # R
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -i /var/lib/squeezeboxserver/airplay-fifo.raw -o stdout | [flac] -cs --totally-silent --endian=little --channels=2 --sign=signed --bps=16 --sample-rate=44100 --compression-level-0 -

Save the config!

13. In Squeezebox Server’s web user-interface, create a new Favourite named “AirPlay” with URL “wavin:default” (note the spelling i.e. no 'e').

14. Restart Logitech Media Server
sudo /etc/init.d/logitechmediaserver restart

15. Add the squeezeboxserver to the audio user group, this can be helpful for debugging the config
sudo usermod -a -G audio squeezeboxserver

16. Start the Shairport server:
sudo -u squeezeboxserver perl shairport.pl -v -a "Squeezebox Airplay" --pipe=/var/lib/squeezeboxserver/airplay-fifo.raw

17. On iTunes or iOS select the "Squeezebox Airplay" option and start playing your music.

If you cannot connect it is likely a firewall issue try disabling the firewall for debugging with sudo ufw disable - don't forget to re-enable it and don't do this if you connect direct to the internet without a router inbetween you and the internet.

18. In the Shairport terminal window you should see activity as the music stream is received, it's usual to see lots of errors since you are streaming most likely over wi-fi there are bound to be sync errors, such as:

"requesting resend on 1 packets"
"Suspected resync request packet received. Initiating resync."
"missing frame" etc.

Ignore them!

19. On LMS or the Squeezebox start playing the "Squeezebox Airplay" favourite after a couple of seconds you should have audio from your Squeezebox.

20. By default Shairport will stream FLAC to the Squeezebox, unfortunately my Ubuntu server doesn't seem to have the power (Atom D330) to encode the Airplay stream to FLAC for the Squeezebox on the fly in real time. This results in a very choppy sound experience with my audio playing for a couple of seconds then drops out for 30sec to a couple of minutes before playing another couple of seconds and the cycle repeats. I isolated this by testing the Squeezebox on wired ethernet so the only bottleneck could be the Ubuntu server. To combat this I disabled both FLAC and MP3 streams on the Squeezebox and used the raw PCM signal which does not require the Airplay stream to be transcoded.

Disable the FLAC and MP3 streams via LMS > Settings > Advanced > File Types > WavInput and set both eacsound/FLAC and eacsound/MP3 to disabled. Note do not touch WAV or Wavpack they are unrelated. 

21. Restart LMS for the change to take affect 
sudo /etc/init.d/logitechmediaserver restart

And start the Squeezebox playing back the "Airplay Squeezebox favourite"

This lead to a stable stream to my Squeezbeox using wired ethernet. Note it takes around 1 minute for the stream to settle down after play is hit on the Squeezebox there is some buffering/stuttering in the first 1 minute or so. It's also worth noting , the stream also has around a 30 second delay from the Airplay source.

22. Once I swapped back to w-fi I was plagued with choppy playback again. The only cause for this can be that my 54g wireless network does not have the bandwidth to both receive the Airplay music over wi-fi and stream it back to the Squeezebox over wi-fi. To combat this enabled the MP3 stream on the Squeezebox which takes preference over PCM. 

Enable MP3 via LMS > Settings > Advanced > File Types > WavInput and set eacsound/MP3 to enabled. Again note not to touch WAV or Wavpack.

23. Restart LMS for the change to take affect 
sudo /etc/init.d/logitechmediaserver restart

And start the Squeezebox playing back the "Airplay Squeezebox favourite". 

Once again this lead to the a stable stream to my Squeezbeox with the same caveat that it takes around 1 minute for the stream to settle down after hitting play, there is still some buffering/stuttering in the first 1 minute or so; and the stream still has around a 30 second delay from the Airplay source.

Troubleshooting ideas

1. IPv6 If you experience issues try disabling IPv6 this did not effect me but to disable it edit /etc/sysctl.conf and add the following lines:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

2. Test shairport: Test the shairport connection to the Ubuntu server, this helps to isolate ecasound and the Squeezebox from the equation. Turn on your speakers and check they work by playing an audio file locally from Banshee or Rhythmbox or alike. Then fire up shairport  using the command belw to output to alsa. This should give you sound once you play the Airplay source to shairport - if not you have problems with shairport.
sudo perl shairport.pl -v -a "Squeezebox Airplay" --ao_driver alsa

3. Test the pipe and ecasound: Ecasound transcodes the raw Airplay stream to FLAC, MP3 or PCM for the Squeezebox. The format used depends on the filetype enabled in the LMS settings. To test that the pipe is accessible (i.e. not a privilege issue) and that ecasound is working you can ask ecasound to output to the Ubuntu server's alsa interface to check for audio. Again test your Ubuntu servers speakers are working first with Rythmbox or Banshee or alike. Note your hardware interface may not be 0:1, I tried several combinations before I hit on the right interface.
sudo ecasound -i /var/lib/squeezeboxserver/airplay-fifo.raw -o alsa,hw,0:1


If you don't have sound on the server you can pipe the signal to xxd, whereby you should see the signal as an ascii stream in the terminal. If nothing is moving and there's no activity then there's a problem with the pipe (check the permissions) or ecasound.

sudo xxd /var/lib/squeezeboxserver/airplay-fifo.raw



Shairplay to ALSA loopback approach
In my experience, this approach leads to a much lower latency/delay from source to speaker. It requires the ALSA loopback device, I discuss setting up the ALSA loopback device here.


Loopback describes ways of routing electronic signals / digital data streams from their originating facility back to the source without intentional processing or modification. This is primarily a means of testing the transmission. It is used here to loop the sound from Shairplay into the Logitech Media Server via and encoder.


The flow of music under this approach is:

iTunes / iOS
> via Airplay
> Shairport on Ubuntu
> ALSA loopback device input

> ALSA loopback device output
> ecasound on Ubuntu (to transcode the raw Airplay stream to compressed FLAC, MP3 or raw PCM)
> PCM stream (the lame MP3 stream using this approach is noisy and PCM performs well
 - note you can easily swap between them)
> WaveInput plugin on the Logitech Media Server (running on Ubuntu)
> via wi-fi or ethernet
> Squeezebox
> speakers


Create the alsa loopback device
See here.

There are now two options of encoder you can use:
arecord: which I found easier to set up but has issues related to the amount of data it can encode leading to the stream cutting out after 2-3 hours; or
ecasound: which should perform better as it introduces a buffer.





Running with an alsa loopback device using arecord
The loopback input used by shairport is hw:Loopback,0 or for me hw:2,0 
The loopback output fed to arecord is hw:Loopback,1 or for me  hw:2,1


The LMS Favourite on the Squeezebox should be  as wavin:hw:Loopback,1

Shairport runs assudo perl /usr/bin/shairport/shairport.pl -v -a "Squeezebox Airplay" --ao_driver alsa --ao_devicename=hw:Loopback,0


Custom-convert.conf (default by BPA with an update for pcm streaming) should be edited to:
#
# wavin 
#
wavin pcm * * 
         # R
         [arecord] -d0 -c2 -f S16_LE -r 44100 -twav -D $FILE$ 
wavin mp3 * *
         # RB:{BITRATE=-B %B}
         [arecord] -d0 -c2 -f S16_LE -r 44100 -twav -D $FILE$ | [lame] --silent -r -x -q $QUALITY$ -b $BITRATE$ - -
wavin flc * * 
         # R
         [arecord] -d0 -c2 -f S16_LE -r 44100 -twav -D $FILE$ | [flac] -cs --totally-silent --endian=little --channels=2 --sign=signed --bps=16 --sample-rate=44100 --compression-level-0 -

Remember to restart the LMS. 



Running with an alsa loopbackdevice using ecasound

The loopback input used by shairport is hw:Loopback,0 or for me hw:2,0 
The loopback output fed to ecasound for is the device number hw:2,1 using the loopback device name didn't seem to work.



The LMS Favourite on the Squeezebox should be wavin:hw,2,1   note carefully the difference in syntax with a comma not colon after hw.

Shairport runs assudo perl /usr/bin/shairport/shairport.pl -v -a "Squeezebox Airplay" --ao_driver alsa --ao_devicename=hw:Loopback,0

Custom-convert.conf (default by BPA with an update for pcm streaming) should be edited to:
#
#wavin
#
wavin pcm * *
        # R
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -i:alsahw,2,1 -o stdout
wavin mp3 * *
        # RB:{BITRATE=-B %B}
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -eadb:-60 -i:alsahw,2,1 -o stdout | [lame] --silent -r -x -q $QUALITY$ -b $BITRATE$ - -
wavin flc * *
        # R
        [ecasound] -q -z:db -b:4096 -f:16,2,44100 -eadb:-60 -i:alsahw,2,1 -o stdout | [flac] -cs --totally-silent --endian=little --channels=2 --sign=signed --bps=16 --sample-rate=44100 --compression-level-0 -

Remember to restart the LMS. 


Notes:
1. Attenuation (i.e. eadb) to reduce the stream volume in the custom-convert.conf 
does not appear to work on the pcm stream.

2. ecasound can be debugged to error log by adding -D -dd "$@" 2>/tmp/ecasound.log to the ecasound commands in the custom-convert.conf. The error log can then be read with  sudo tail -f -n 20 /tmp/ecasound.log

3. I have some hiss on the ecasound and arecord streams when using the loopback approach and lame. This is despite all other pulse and alsa input interfaces being muted. When the music stream is stopped on shairplay the hiss disappears and attenuation makes no difference. When using the raw pipe approach with lame there is no hiss at all the downside is the huge delay I am experiencing of c. 30 seconds when using the pipe. Now that I have all the options working I'll see if I can improve the latency on the pipe.

4. The lame compression used in conjunction with the loopback appraoch appears to be introducing the hiss since the pcm stream is clean with no hiss at all that I can discern. The pcm stream also only has a 6 second delay.

Summary
My Atom D330 server with Ubuntu 10.04 can send Airplay to the Squeezebox Touch as follows:
- via alsa loopback with ecasound to lame on the fly but this seems to introduce noise and a 8 second delay
- via alsa loopback with ecasound to pcm on the fly without noise and a 6 second delay
- via raw pipe with ecasound to lame on the fly without noise and a 30 second delay

It cannot:
- under my Ubuntu 10.04 setup to convert to FLAC on the fly via alsa loopback or the pipe (CPU not powerful enough?!)
- pass the raw pipe to the squeezebox via PCM, this leads to huge delays and lots of stuttering. I thought this was bandwidth related but it cannot be if the pcm stream plays using the alsaloop back mechanism.

This is tested with iTunes and iOS on an iPhone 3GS. So for the time being I will be sticking with alsa loopback, ecasound and pcm until I can reduce the delay on the pipe.


Outstanding
- Set up Shairplay as a service with stop and start commands to run at boot.
- Reduce the delay on the raw pipe.


Sources:
https://github.com/albertz/shairport
http://code.google.com/p/bpaplugins/
http://blog.stuart.shelton.me/archives/762
http://forums.slimdevices.com/showthread.php?49584-Announce-WaveInput-for-Linux

Credit to BPA and Ralphy on the Logitech forums for their assistance.

21 comments:

  1. I came across your instructions above and have almost an identical set-up (Ubuntu 12.04). Everything seems to have worked, but I'm not hearing any sound from my player: my iPhone recognized and sent audio to the LMS and I saw normal messages at the CLI.

    I think my problem is to do with the LMS plugin. With the favourite I created, when I click on the associated "Play" button, nothing appears to happen.

    Can I ask what happens when you click on the "Play" button of the web interface?

    I've posted the output of the LMS log here: http://forums.slimdevices.com/showthread.php?49584-Announce-WaveInput-for-Linux&p=717487&viewfull=1#post717487

    Any insight would be appreciated.

    cheers,
    S.

    ReplyDelete
  2. Found my problem... seems WaveInput didn't install properly when I first enabled it. Now, everything is working as it should--thanks for the how-to!

    Just wondering if you got any further on creating a service wrapper? If not, I'm going to start to take a stab at it.

    cheers,
    S.

    ReplyDelete
    Replies
    1. Glad to hear that. Sadly I haven't had the opportunity to work on a service script yet. Stuarts blog has a sevice script but it doesn't work as is on Ubuntu it may help. If you crack it I'd gladly test and host a script. Unfortunately my shairplay seems to conflict with my airport express on the same network which I need to sort first before I refine the shairplay service.

      Delete
    2. Turns out to be VERY simple! I only needed to modify the included init script:

      juggler@server:~$ sudo cp /usr/bin/shairport/shairport.init.sample /etc/init.d/shairport
      juggler@server:~$ sudo vi /etc/init.d/shairport
      change the following two lines
      DAEMON="/usr/bin/shairport.pl"
      DAEMON_ARGS="-a Squeezebox -w $PIDFILE --pipe=/var/lib/squeezeboxserver/airplay-fifo.raw"
      juggler@server:~$ sudo update-rc.d shairport defaults

      Delete
    3. Do you have an actual Airport Express as well?

      Delete
    4. I do (older version, wall-wart style). Also have a Raspberry Pi running OpenElec (XBMC distribution)--this also includes Airplay support. Both are working even with Shairport running.

      My router is an ASUS running Tomato; currently I have not enabled any IPv6 settings on the router.

      Delete
    5. Thanks similar set up with an RPi, latest AirportExpress and a DD-WRT router without IPv6 here still not got to the bottom of this.

      Delete
  3. Frustrating. I'm running Ubuntu Server 12.04. There were some updates yesterday including Linux headers etc. After updating, I'm no longer able to connect my iDevices--when I attempt to, I get the following error from Shairport before it crashes:

    Odd number of elements in hash assignment at /usr/bin/shairport.pl line 636.
    Use of uninitialized value in subroutine entry at /usr/bin/shairport.pl line 637.
    no AESIV at /usr/bin/shairport.pl line 637.

    Any insights?

    ReplyDelete
    Replies
    1. I've not experienced that issue but I am running on 10.04. Are you connecting from iOS6 or OSX 10.8 Lion...
      I see you've also logged a bug over at GitHub.

      I am still trying to resolve my AirportExpress conflict do you specify a MAC address in your shaiport command line?

      Delete
  4. Well that's embarassing... your comment twigged me to the fact that it is actually iOS6 that has broken Shairport and not any Ubuntu package upgrades (confirmed on another iDevice that hasn't yet upgraded to iOS6).

    I'm using the exact command line you provided above... no MAC specified.

    ReplyDelete
  5. Hi, I have done this whole installation on a Pi, with Squeezeplug, everything is working, but I hardly get and audio its mostly hiss, any suggestions.

    ReplyDelete
    Replies
    1. Off the top of my head I cannot remember, sometimes it was permissions related.

      If you have hiss the easiest option is to use the pipe method.

      Although from testing on my own RPi I don't think the RPi has sufficient power to transcode the streams for AirPlay.

      Delete
  6. Thanks for a great tutorial.

    I have some issues installing this on a pi. I cant get the following command to work:

    sudo perl -MCPAN -e 'install IO::Socket::INET6'

    Did you do anything special? Im running squeezeplug on my rPi.

    ReplyDelete
    Replies
    1. I think on the current RPi raspbian builds this is no longer required.

      Delete
  7. But it seems like i dont need it, now it is working on my setup.

    I used the first method and have the buffering issues

    I will try the second method to see if it solves the issues.

    ReplyDelete
    Replies
    1. On my RPi when trying this under either method I experienced unusable lag, I don't think the RPi has enough grunt to do this. That set up was using the RPi as a client only and not using it as a server.

      Delete
  8. Hi, I’ve finaly made something equivalent for Windows Squeezebox server. You can find it here :
    http://forums.slimdevices.com/showthread.php?100048

    ReplyDelete
  9. Many thanks, great tutorial, works like a charm! (Ubuntu 12.04, iPhone 4S, iOS 7)

    I used the raw pipe method. There's indeed a delay, but on my HTPC (Celeron 1.2GHz) it's only oa couple of seconds, I can live with that.

    ReplyDelete
  10. Awesome stuff you've blogged about here. I consider myself the lucky owner of SB Touch but am now falling in love with the simplicity and elegance of Plex. So my dream is to combine the best of both worlds and make my SB Touch a valid player for Plex. I'm hoping to be able to use your instructions for this (since Plex can use AirPlay targets), but my SB server is running 14.04 and is virtualized. Will this make a lot of difference?

    ReplyDelete
    Replies
    1. Your best bet is shairport2 SBT plugin. It will work with a virtual server but you need to ensure that the networking on the VM is bridged to the host as airplay requires dynamic assigned ports.

      Delete
  11. For automatically starting the airplay input, I've done this:

    In shairport config:
    sessioncontrol =
    {
    run_this_before_play_begins = "/opt/scripts/play-airplay.sh";
    };

    And the script contains:
    #!/bin/bash
    echo favorites playlist play item_id:c7bdbce1.6 | nc -q 1 192.168.2.21 9090 >/dev/null


    You'll need to modify the item_id to whatever it is on your setup of course.

    ReplyDelete