I finally got my hands on a Raspberry Pi and my first project has been to make a good quality music player. I’ve not had a speaker setup to listen to music on for many years – it’s almost exclusively been on headphones, mostly on my phone, for far too many years now.
There are a lot of people out there doing similar things, and thanks to all for providing various hints and tips to get me going:
Most of these setups are focussing on a single audio source. I was after a more complex setup.
My goal for this project was to be able to chuck audio at the Raspberry Pi from:
- Android phones
- iOS devices
- MPD for standalone playback
Using PulseAudio has allowed me to achieve all of these goals as well as providing a few additional benefits.
USB Dac
The on-board sound out of the Raspberry Pi is not intended to be high quality and for me wasn’t even close to reasonable for a HiFi experience. The Raspberry Pi has two USB ports and Raspbian has USB audio support available in the kernel out of the box.
There are a lot of USB DACs available to suit all budgets and quality requirements. I ended up with a HiFimeDIY Sabre.
I wasn’t in the market for something audiophile, but I can certainly hear the difference in audio quality compared to the audio jack on my laptop.
Testing the DAC
The first step after plugging the DAC in is to try and play something. You can list the available audio devices:
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: DAC [HiFimeDIY DAC], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: DAC [HiFimeDIY DAC], device 1: USB Audio [USB Audio #1]
Subdevices: 1/1
Subdevice #0: subdevice #0
Playing something through the DAC is simply a case of telling aplay
which device to use:
aplay -D plughw:1,0 /usr/share/sounds/alsa/Front_Left.wav
aplay -D plughw:1,0 /usr/share/sounds/alsa/Front_Right.wav
aplay -D plughw:1,0 /usr/share/sounds/alsa/Front_Center.wav
Low Latency nrpacks=1
There is a kernel module option to reduce the latency and help increase the quality of the USB audio output. I’ve enabled it to no noticeable effect other than to remove some error messages that were showing up rather frequently in the kernel log.
The guys over at The Raspyfi Project have made a rather extensive list of optimisations they’ve made to the stock Raspbian image.
ALSA
Using ALSA directly is nice and simple and should simply work everywhere if aplay
worked above.
Unfortunately only a single device can use ALSA at a time, so for the audio nirvana we’re aiming for we need to make use of a sound server. There have been a lot of different sound servers over the years in Linux, one of the more recent ones is PulseAudio. It has its fans and detractors, but is generally well supported in modern distributions with Raspbian being no exception.
PulseAudio
PulseAudio as with everything else sound related in Linux seems to be sporadically documented and there is also a lot of out of date information floating around. Thankfully the distribution maintainers do a generally fantastic job of pulling everything together.
System Wide PulseAudio
The only way I could get clean, glitch free playback with PulseAudio with the USB DAC was to run the PulseAudio daemon as root. The documentation for PulseAudio goes to great lengths to explain why you shouldn’t run a system wide instance of PulseAudio.
Running PulseAudio as the pi user resulted in continual and very painful to listen to clicking in the output.
For this project I am running the Raspberry Pi headless with no X11 running so this is the only sane way to get PulseAudio up and running and we do fall under the one acceptable use case of running system wide PulseAudio – that of an embedded system with no real users.
Raspbian ships with an init script already supplied and to enable it you need to edit /etc/default/pulseaudio
and change the appropriate line:
PULSEAUDIO_SYSTEM_START=1
I also added the pi and mpd users to the pulse-access group to allow them access.
root@raspberrypi:~# adduser pi pulse-access
root@raspberrypi:~# adduser mpd pulse-access
root@raspberrypi:~# groups mpd
mpd : audio pulse-access
root@raspberrypi:~# groups pi
pi : pi adm dialout cdrom sudo audio video plugdev games users netdev input indiecity pulse-access
Pulse Resampling
Older versions of pulse used a fixed sampling rate and would re-sample on the fly. With the release of v2.0 this has been changed. If the output device supports multiple sample rates then they will be switched on the fly.
You can get a list of available sample rates:
pi@raspberrypi ~ $ cat /proc/asound/card1/stream0
HiFimeDIY Audio HiFimeDIY DAC at usb-bcm2708_usb-1.3, full speed : USB Audio
Playback:
Status: Stop
Interface 3
Altset 1
Format: S16_LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 8000, 16000, 32000, 44100, 48000, 96000
Interface 3
Altset 2
Format: S24_3LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 8000, 16000, 32000, 44100, 48000, 96000
...
You can verify that PulseAudio is indeed switching on the fly with a couple of simple steps. Play a file with a sample rate of say 44.1 Khz and whilst it is playing:
pi@raspberrypi ~ $ cat /proc/asound/card1/stream0
HiFimeDIY Audio HiFimeDIY DAC at usb-bcm2708_usb-1.3, full speed : USB Audio
Playback:
Status: Running
Interface = 3
Altset = 1
Packet Size = 388
Momentary freq = 44100 Hz (0x2c.199a)
Interface 3
Altset 1
Format: S16_LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 8000, 16000, 32000, 44100, 48000, 96000
Interface 3
Altset 2
Format: S24_3LE
Channels: 2
Endpoint: 3 OUT (ADAPTIVE)
Rates: 8000, 16000, 32000, 44100, 48000, 96000
...
In the first few lines (Momentary freq =
) you can see the sample rate that is actually being used by the hardware.
There are almost certainly other ways to verify this and query all the properties of audio devices, but this seems to do the job!
Wireless
To cut down on the wires going to the system I opted to get a WiFi USB dongle. It’s a low profile, low power adapter the same size as the micro Bluetooth dongles that have been around for a while now.
The chipset is a RTL8188CUS and with the latest Raspbian image simply worked out of the box.
pi@raspberrypi ~ $ sudo lsusb | grep Real
Bus 001 Device 004: ID 0bda:8176 Realtek Semiconductor Corp. RTL8188CUS 802.11n WLAN Adapter
There are plenty of guides on the web on how to configure wireless USB dongles for the Raspberry Pi.
AutoFS and NFS
My audio is all stored on a NAS which exposes it via Samba and NFS.
I use AutoFS to make mounting the networked filesystem nice and easy. A few packages are needed nfs-common, rpcbind and autofs5.
Edit /etc/audo.master
and uncomment the /net
line:
/net -hosts
After starting up the autofs daemon you should be able to simply browse your network shares under /net/machine
for example mine is under /net/plug.local
.
MPD
MPD was a requirement for this project as it allows music to be queued up from a variety of sources and simply left to play.
Getting MPD up and running was easy and simply a case of telling it to use PulseAudio. Edit /etc/mpd.conf
and set your audio_output
section like so:
audio_output {
type "pulse"
name "MPD PulseAudio Output"
}
Setting the music_directory
option to point to my AutoFS NAS mount directory was the only other change necessary to /etc/mpd.conf
.
UPnP
With the basic audio output working the next step is a UPnP renderer so that I can get my Android phone in on the audio action. There are a few different UPnP renderer options available for Linux, but the easiest to get up and running I found to be gmediarender-ressurect. A few other people have got this up and going on the Raspberry Pi.
I’ll hand over to the official documentation:
And a couple of useful posts:
gmediarender-ressurect uses GStreamer to output it’s audio, which doesn’t default to using PulseAudio. Thankfully it allows the audio output sink to be controlled from the command line:
/usr/local/bin/gmediarender -f upnp-pi --gstout-audiosink
Automatic Startup of gmediarender-ressurect
UPDATE – solved, see Musical Pi Follow Up
Using PulseAudio made getting automatic startup working a bit tricky. I had a couple of attempts at creating an init.d script, which were partially successful. gmediarender would start successfully, but would insist on routing audio out via the onboard audio of the Raspberry Pi rather than the USB DAC.
My solution was to simply start it up via a line in /etc/rc.local
:
su -c "/usr/local/bin/gmediarender -f upnp-pi -d --gstout-audiosink pulsesink" pi
Simply, easy and does the trick.
GStreamer Default Sink
You can set GStreamer to use PulseAudio as its default sink with a quick GConf change:
gconftool-2 -t string --set /system/gstreamer/0.10/default/audiosink pulsesink
As previously mentioned though, this wasn’t enough for me to get everything working on boot. The /etc/rc.local
approach seems to be the most reliable so far.
AirPlay
As there are also fruity products around in our household AirPlay support was definitely on the list of things to get working. Enter ShairPort!
Again I’ll hand over to others at this point:
Automatic Startup of ShairPort
UPDATE – solved, see Musical Pi Follow Up
Again I had a few problems getting the supplied init.d script to work. It is almost certainly something to do with using PulseAudio, but again a simple one liner in /etc/rc.local
does the job:
su -c "/usr/local/bin/shairport.pl --a air-pi -d --ao_driver=pulse" pi
Networked PulseAudio Speakers
One benefit of using PulseAudio is that you can make use of PulseAudio network streaming for next to no effort. This allows me to route all the audio output from my laptop to a decent set of speakers via the Raspberry Pi, great for things like Spotify or anything else that can’t be run directly on the Raspberry Pi.
There are a few bits and pieces necessary to get this working.
Enable receiving and advertising networked audio on the Raspberry Pi by adding the following to /etc/pulse/system.pa
:
load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;192.168.0.0/16
load-module module-zeroconf-publish
Obviously change the ip address ranges to suit and you’ll need to install pulseaudio-module-zeroconf
.
Finally you need to allow PulseAudio to make use of the networked speakers on your other machines. Fire up paprefs on a machine with PulseAudio installed and check the “Make discoverable PulseAudio sound devices available locally”. You probably have to log out and back in again to have this option take affect.
I had to install pulseaudio-utils
to get the paprefs
application.
On my Ubuntu 12.04 laptop in the “Sound Settings” applet I now get the option to choose “HiFimeDIY DAC Analog Stereo on pulse@raspberrypi.local” as a destination for my sound. Lovely!
Issues
I get occasional niggles with the wireless – it feels like the dongle goes to sleep sometimes and takes a while to respond after periods of inactivity. That and occasional signal problems make me think it may be worth considering going down the wired ethernet route. That said the problems seem transient and infrequent enough to be ignored and in general the bandwidth is stable enough to stream FLAC and networked PulseAudio without issue.
Thoughts
One final thing I’d love to get working is getting audio from Windows through to the Raspberry Pi in the same way that PulseAudio lets me do with Linux. There are a few options floating around there on the web including AirFoil which works with the AirPlay protocol, but I’ve not experimented yet.
All in all though it’s been a fun project and I’m enjoying having my music out in the open and not trapped in my headphones!