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 TrouchBurgess' 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

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 ..

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/hendrikw82/shairport.git
pi@raspberrypi ~ $ cd shairport 
pi@raspberrypi ~/shairport $ sudo make
pi@raspberrypi ~/shairport $ sudo make install
pi@raspberrypi ~/shairport $ sudo cp shairport.init.sample /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
For instance, if we wanted to name it HiFiPi, we'd make the DAEMON_ARGS line look like this:
DAEMON_ARGS="-w $PIDFILE -a HiFiPi"
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.


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