Hi-Fi Audio via AirPlay on Raspberry Pi

KRK Rokit with Raspberry Pi and turntable

Originally published January 29, 2013. Now updated with latest Shairport libs, depedencies, and installation instructions.

After getting my Raspberry Pi last week, I set out to make it do something useful in the interim before a more compelling creative lightning bolt hits me. Luckily, nerds & neckbeards with smarts beyond the scope of my own ability have done the heavily lifting necessary to turn a Raspberry Pi into a cheap AirPlay receiver. In the footnotes of Jordan Burgess' fantastic piece on the topic, he writes that sound quality out of the Pi's 3.5mm stereo jack suffers because the Pi's hardware uses pulse wave modulation to output a signal to the jack. He was right. The sound quality is far from Hi-Fi and is shameful to run through my KRK monitors. To add insult to injury, the stereo jack would often output deafeningly-loud white noise if you were unlucky when stopping a track. I suspected the software at fault was shairport, an open-source reverse engineering of Apple's AirPlay codec. I would venture to guess that shairport doesn't clean up and silence the audio signal when playback is interrupted because the authors didn't care to accomodate a shitty PWM-based DAC. I cannot point the finger at the amazing contributors to shairport though, as they've reverse-engineered something that I would take me weeks to wrap my brain around.

Since the culprit is suspected to be a "dirty" PWM, I thought that the option of using an external USB sound card would amend the white noise problem and offer me CD-quality audio to boot. I had an M-Audio Mobile Pre laying around that was acting as a dummy mixer for the KRK's anyway, so I decided to dive in and setup wireless Hi-Fi audio on my Raspberry Pi.

Assumptions

  • You have a Raspberry Pi and compatible USB soundcard
  • Networking is setup and working fine on your Pi
  • You're comfortable opening up a terminal shell on your Raspberry Pi via ssh

What I'll Be Covering

  • Setup Shairport on your Raspberry Pi as a daemon
  • Get linux to recognize, prefer, and use the USB sound card

Shairport Daemon Setup

This section is a blend of Trouch & Burgess' write-ups.

Make sure your Raspberry Pi is up-to-date

This is always smart to run with new linux systems.

pi@raspberrypi ~ $ sudo apt-get update  
pi@raspberrypi ~ $ sudo apt-get upgrade

Install Shairport prerequisites

Shairport requires perl and a handful of other packages from the repo in order to install successfully.

pi@raspberrypi ~ $ sudo apt-get install git libao-dev libssl-dev libcrypt-openssl-rsa-perl libio-socket-inet6-perl libwww-perl avahi-utils libmodule-build-perl libasound2-dev libpulse-dev

Install Perl Net-SDP

Apparently a change in IOS 6 requires this module to be installed or else shairport will crash.

pi@raspberrypi ~ $ git clone https://github.com/njh/perl-net-sdp.git perl-net-sdp  
pi@raspberrypi ~ $ cd perl-net-sdp  
pi@raspberrypi ~/perl-net-sdp $ perl Build.PL  
pi@raspberrypi ~/perl-net-sdp $ sudo ./Build  
pi@raspberrypi ~/perl-net-sdp $ sudo ./Build test  
pi@raspberrypi ~/perl-net-sdp $ sudo ./Build install  
pi@raspberrypi ~/perl-net-sdp $ cd ..

Build & install Shairport "as root" and have it launch on system startup

This will install shairport into /usr/sbin and make it available to the system / root user.

pi@raspberrypi ~ $ git clone https://github.com/abrasive/shairport.git  
pi@raspberrypi ~ $ cd shairport  
pi@raspberrypi ~ $ ./configure  
Configuring Shairport  
OpenSSL found  
libao found  
PulseAudio found  
ALSA found  
Avahi client found  
getopt.h found  
CFLAGS: -D_REENTRANT -I/usr/include/alsa -D_REENTRANT  
LDFLAGS: -lm -lpthread -lssl -lcrypto -lao -lpulse-simple -lpulse -lasound -lavahi-common -lavahi-client  
Configure successful. You may now build with 'make'  
pi@raspberrypi ~/shairport $ make  
pi@raspberrypi ~/shairport $ sudo make install  
pi@raspberrypi ~/shairport $ sudo cp scripts/debian/init.d/shairport /etc/init.d/shairport  
pi@raspberrypi ~/shairport $ cd /etc/init.d  
pi@raspberrypi ~/shairport $ sudo chmod a+x shairport  
pi@raspberrypi ~/shairport $ sudo update-rc.d shairport defaults  
pi@raspberrypi ~/shairport $ sudo useradd -g audio shairport # add shairport user, then add user to audio group

For instance, if we wanted to name it HiFiPi, we'd make the AP_NAME line look like this (by default, it's set to the Raspberry Pi's hostname):

pi@raspberrypi ~/shairport $ sudo nano shairport  
AP_NAME=HiFiPi # look for this line in shairport file

Make sure that the installation was successful by starting shairport

pi@raspberrypi ~/shairport $ sudo /etc/init.d/shairport start

Ok, so now we've basically installed shairport and it will launch everytime your Raspberry Pi boots.

USB Soundcard Setup

Check to see if linux recognizes your USB sound card

Make sure your soundcard is plugged in before booting up the Raspberry Pi. You can check to see if your system detects it with aplay -l

pi@raspberrypi ~ $ 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 1: MobilePre [MobilePre], device 0: USB Audio [USB Audio]  
  Subdevices: 0/1
  Subdevice #0: subdevice #0

Make the Raspberry Pi prefer the USB sound card

Your Pi will prefer it's own hardware audio on boot-up, we need to change this by editing alsa-base.conf

pi@raspberrypi ~ $ sudo nano /etc/modprobe.d/alsa-base.conf

Find the following line:

options snd-usb-audio index=-2

Comment out the line and add the following after:

#options snd-usb-audio index=-2  
options snd-usb-audio nrpacks=1

Find your sound card's alias by listing them with aplay -L (note the capital L)

pi@raspberrypi ~ $ aplay -L  
null  
    Discard all samples (playback) or generate zero samples (capture)
pulse  
    Playback/recording through the PulseAudio sound server
sysdefault:CARD=ALSA  
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device
front:CARD=MobilePre,DEV=0  
    MobilePre, USB Audio
    Front speakers
iec958:CARD=MobilePre,DEV=0  
    MobilePre, USB Audio
    IEC958 (S/PDIF) Digital Audio Output
sysdefault:CARD=ALSA  
    bcm2835 ALSA, bcm2835 ALSA
    Default Audio Device

According to the output above, it's called front:MobilePre, yours may be different.

Tell ALSA to use the USB hardware as it's default sound device.

Edit /etc/asound.conf to look like the following with front:MobilePre replaced with your own device identifier.

pi@raspberrypi ~ $ sudo nano /etc/asound.conf
It should look something like the following:
pcm.mmap0 {  
    type mmap_emul;
    slave {
      pcm "hw:0,0";
    }
}

pcm.!default front:MobilePre
Reboot your Pi.
pi@raspberrypi ~ $ sudo reboot

Test an audio file

After restarting the machine, try playing an audio file out of it. mpg123 can play any mp3 stored on the machine. Lets transfer an mp3 over scp to the Pi

drew@mymachine~ $ scp Cake.mp3 pi@10.0.1.9:~/

Install mpg123 and see if the audio file plays

pi@raspberrypi ~ $ sudo apt-get install mpg123  
pi@raspberrypi ~ $ mpg123 Cake.mp3

Hear something? Awesome.

Test Shairport via an Apple Device

Connect headphones to your USB sound card and make sure the shairport streaming works. If it's all good, then we're done! No clicks, no pops, just delicious hi-fi audio.

Shairport device on AirPlay chooser

Troubleshooting

Don't hear anything? Let's try to debug this. See what hardware device and subdevice the USB soundcard is listed at. At this point, it should be card 0, subdevice 0 as it is the default device.

pi@raspberrypi ~ $ aplay -l  
**** List of PLAYBACK Hardware Devices ****
card 0: MobilePre [MobilePre], device 0: USB Audio [USB Audio]  
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 1: ALSA [bcm2835 ALSA], device 0: bcm2835 ALSA [bcm2835 ALSA]  
  Subdevices: 7/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
Turn the mp3 into a wav file and force a test on the hardware where the 0,0 corresponds to <card device number>,<subdevice number>
pi@raspberrypi ~ $ mpg123 -w test.wav Cake.mp3  
pi@raspberrypi ~ $ sudo aplay -Dhw:0,0 test.wav

If you hear nothing, perhaps your USB sound card is not compatible with the Pi. If you hear something, then the sound card simply is not being properly set as the default audio device. Double-check your alsa-base.conf and asound.conf files to try to spot a typo.

Extra

Bumping up the volume

The 'max volume' for your USB sound card is often fixed to some value. You can control the volume dynamically via anything that can stream to AirPlay, but the volume will still be capped at a limit. Let's change this. Start playing audio on your Raspberry Pi. Use alsamixer to pull up a GUI for editing sound device volume. Press F6 and select your device. Edit the levels and then hit ESC when you're ready to exit.

pi@raspberrypi ~ $ alsamixer

After hitting F6 and selecting my device, it looks like this:


Store those values as defaults.

pi@raspberrypi ~ $ sudo alsactl store 0

References