ALSA sound device sharing

While I was working on a new project (an Internet Radio and Airplay server) with my Raspberry Pi I ran into an interesting sound issue.  My original development on the project was done using the built in audio from the headphone jack and all was great except that the Raspberry Pi doesn't produce the best quality analog audio.  I wanted something better so I ordered a Ti PCM2704 USB DAC card.  The card was very cheap but should produce better sound than I was getting from the Raspberry Pi's headphone jack.  My new DAC card arrived and I plugged it into my project.

 

The Default Audio Device

The first thing that I noticed is that all my audio was still coming out of the Raspberry Pi's headphone jack.  A but of searching on the net and a quick fix was discovered.  I just needed to change the order (index) that ALSA used to disover and list the sound cards.  you can check this by using the aplay command to list your sound devices. Card 0 will be your default card and mine was still pointing to the Raspberry's bcm2835 device.

# aplay -l

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 [USB Audio DAC], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

 
The fix for this is to edit the file /etc/modprobe.d/alsa-base.conf comment out this line:

options snd-usb-audio index=-2

and add this line in right after it:

options snd-usb-audio nrpacks=1

 
Now the device order should look like this and your devices should play out the USB DAC.
 

# aplay -l

**** List of PLAYBACK Hardware Devices ****
card 0: DAC [USB Audio DAC], 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: 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

Sharing the Sound Device

 
Now that I had the Pi playing audio out the USB DAC, I still had one problem, sharing the audio device.  My project used MPD to play the internet radio stations and Shairport to play any streaming AirPlay content.  I start both of these program at startup and tell them to share the same audio device. This worked great for the built in audio but gave me the following error when I tried to use it with the USB DAC.  The first service to start (usually mpd) would work but the second service (usually Shairport) would not be able to open the audio device.
 

pcm_dmix.c:1018:(snd_pcm_dmix_open) unable to open slave

 
The fix to this issue was a lot harder to find than I expected.  I had to study up on ALSA way more than I wanted to and soon relized that ALSA is a very complicated audio system with very cryptic documentation.  The solution was easy to impliment but it took way more time than I expected. This is the reason why I am writing this article, so I will remember and others may benifit. 
 
Here is what worked for me....  Create a new files called /root/.asoundrc and place the following information in it.
 
pcm.dmix0 {
    type dmix
    ipc_key 673138
    ipc_key_add_uid false   # let multiple users share
    ipc_perm 0666           # IPC permissions for multi-user sharing (octal, default 0600)
    slave {
        pcm "hw:0,0"
        rate 48000
        period_time 80000
        buffer_time 320000
        period_size 4096
        buffer_size 16384
    }
    bindings {
        0 0
        1 1
    }
}
# 'dsp0' is espected by OSS emulation etc.
pcm.dsp0 {
    type plug
    slave.pcm "dmix0"
}
ctl.dsp0 {
    type hw
    card 0
}
pcm.!default {
    type plug
    slave.pcm "dmix"
}
ctl.!default {
    type hw
    card 0
 
 
Reboot and give it a try.  This seemed to work for me.