layout: page |
title: "pi-audio" |
date: 2014-03-24 21:29 |
comments: true |
sharing: true |
footer: true |
I want to be able to stream audio from any mobile device (laptop, iDevice, Android-, guest's phones) to any romo in the house. I also want to be able to take the podcast I'm listening to upstairs and "move" it downstairs, or to the other room as I move around.
I started with a vanilla Raspbian install on the Pi.
There are several methods for transmitting audio over a network:
shairport
package emulates an AirPlay audio endpoint, which can be driven from iOS devices.I want to support as many as possible, reaching any or multiple endpoints, and without contention (mixing if multiple devices want to stream to the same speakers.)
Nothing worked. Playing with ALSA made Pulse hang and Pulse tried to send things back to ALSA and it was a 100% CPU util and zero. Struggled, mightily, until I uninstalled PulseAudio and magically ALSA started working. See a page of frustrated notes
(Note, this page, interestingly, seems to reccommend a OSS v4. I might come back to that when I have a low latency application.
List ALSA playback devices:
pi@raspberrypi:~/blog/source/pi-audio$ aplay --list-devices
**** List of PLAYBACK Hardware Devices ****
card 0: system_1 [iMic USB audio system], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: system [iMic USB audio system], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 2: 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
Not sure what all the subdevices for the bcm2835 are. Analong, HDMO, in, out?
It's also somewhat disturbing that the cards are numbered with the USB devices first Udev is supposed to stop that from happening. How can I refer to a USB device stably so that living room stays living room through restarts? Ideally refer by device serial number?
USB devices, in general, will not get assigned to the same places between restarts, so I need some way to make sure that if I play to the bedroom I'll get the bedroom.
The Alsa-project.org wiki had a page showing how to assign device names based on the path to the particular port the device is plugged in to.
Unplugging and replugging for some reason does not show up on udevadm monitor
. So I rebooted with the audio cards unplugged from USB, then ran udevadm monitor
This got me:
KERNEL[101.908473] change /devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.4/1-1.3.4:1.0/sound/card1 (sound)
KERNEL[112.194239] change /devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.3/1-1.3.3:1.0/sound/card2 (sound)
NOTE this did not work for another brand of USB audio device, even booting with things unplugged. Anohter thing I did was extract the device paths from the output of udevadmin info --export-db
.
So I added these lines to /etc/udev/rules.d/39-usb-alsa.rules
:
SUBSYSTEM!="sound", GOTO="my_usb_audio_end"
ACTION!="add", GOTO="my_usb_audio_end"
DEVPATH=="/devices/platform/bcm2835_AUD0.0/sound/card?", ATTR{id}="builtin"
DEVPATH=="/devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.4/1-1.3.4:1.0/sound/card?", ATTR{id}="living_room"
DEVPATH=="/devices/platform/bcm2708_usb/usb1/1-1/1-1.3/1-1.3.3/1-1.3.3:1.0/sound/card?", ATTR{id}="office"
LABEL="my_usb_audio_end"
Saving that and rebooting, I find that ALSA now has names for my audio cards:
pi@raspberrypi:~/blog/source/pi-audio$ aplay --list-devices
**** List of PLAYBACK Hardware Devices ****
card 0: living_room [iMic USB audio system], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: office [iMic USB audio system], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
However, it did not manage to change the "id" on the builtin. No matter, as it is terrible and I won't be using it.
And ALSA can play using those names;
aplay -D default:living_room /usr/share/scratch/Media/Sounds/Animal/HorseGallop.wav
asound.conf
The native sample rate on these things is 48000 Hz, if I remember the spec sheet right, so let's make ALSA resample in software.
I also want some virtual devices for "everywhere," "upstairs," etc.
Here's the etc/asound.conf
I wrote (note it gets more complicated down below with mixing:
pcm.raw_living_room {
type hw
card living_room
rate 48000
}
pcm.raw_office {
type hw
card office
rate 48000
}
pcm.living_room {
type plug
slave { pcm "raw_living_room" }
}
ctl.living_room {
type hw
card "living_room"
}
pcm.office {
type plug
slave { pcm "raw_office" }
}
ctl.office {
type hw
card "office"
}
pcm.!default {
type plug
slave { pcm "raw_living_room" }
}
ctl.!default {
type hw
card "living_room"
}
pcm.everywhere {
type plug;
slave.pcm {
type route;
slave.pcm {
type multi;
slaves.a.pcm raw_living_room;
slaves.a.channels 2;
slaves.b.pcm raw_office;
slaves.b.channels 2;
bindings.0.slave a;
bindings.0.channel 0;
bindings.1.slave a;
bindings.1.channel 1;
bindings.2.slave b;
bindings.2.channel 0;
bindings.3.slave b;
bindings.3.channel 1;
}
ttable.0.0 1;
ttable.1.1 1;
ttable.0.2 1;
ttable.1.3 1;
}
}
pcm.pulse { type pulse }
ctl.pulse { type pulse }
I found I could aplay -D everywhere
and get sound out of both speakers (with some popping).
Note that you may often get warnings on the ALSA site about ganging audio cards together, that their clocks might drift. The nice thing about USB audio devices is that they (or at least some of them, like the original iMics) derive their 48000Hz clock off of the 1Khz frame clock on the USB bus. So two devices on the same bus will stay in relative synch indefinitely. As long as they are fed properly by the audio drivers...
I ran rpi-update
since audio and USB concention have been worked on and this Pi is quite old. No change that I saw (yet).
Actually, no. The only thing that Pulseaudio ever did was prevent sound from being played (even though ALSA, already worked, what), and hog the CPU. So, make sure to uninstall pulseaudio. See a second page of frustrated notes.
After uninstalling PulseAudio, ALSA magically started working again.
So I decided to set up software mixing and multi-clients in ALSA. See this page.
Eventually my /etc/asound.conf looks like this. Note the numeric paramters, whcih I copied from somewhere without really experimenting, but they seemed to take care of popping.
And it allows mixing of multiple streams from here, there, everywhere:
cd /usr/share/scratch/Media/Sounds/Vocals
mpg321 -o alsa -a everywhere Oooo-badada.mp3 & mpg321 -o alsa -a office Sing-me-a-song.mp3
Now, I get some dropouts when playing three mp3 streams at once, but that seems to be mpg321's inefficiencies.
Clone the repo from github:
pi@raspberrypi:~$ git clone https://github.com/abrasive/shairport.git
Install build dependencies:
sudo apt-get install libssl-dev libavahi-client-dev libasound2-dev libao-dev libpulse-dev
Compile:
cd ~/.shairport && ./configure && make
Test:
./shairport -a "Living room" -p 5002 -- -d living_room &
./shairport -a "Office" -p 5003 -- -d office &
./shairport -a "Everywhere" -p 5004 -- -d everywhere &
That, remarkably, worked. Multiple endpoints can even play simultaneously (even endpoints that share the same physical speakers). It doesn't allow multiple connections per endpoint, though its contention behavior is better than the airport (it boots off old connections instead of letting them hog)
Then the thing is to make the shairport
s run at startup as daemons. I mashed together some example init.d scripts found on the web and made this which I placed at /etc/init.d/shairport
. Note the script makes the daemons run with realtime priority. Then I ran:
sudo update-rc.d shairport defaults
to enable the script at startup.
Note: I think I want the daemon to run at realtime priority only when it is streaming, but not when waiting for connections? Can that be done?
Popping and clicking was worse with the Audio-Technica dongles than with the iMics. I found a page about a branch of the Raspberry pi kernel that fixes the USB lossage.
But this fix has already been merged and I've been using it! However, there are some tweaks. Putting
dwc_otg.fiq_split_enable=0
in /boot/cmdline.txt
made crackling much worse! But adding it at the beginning of the command line had better luck -- crackle when starting a strem which settles down, a bit worse (occasional crackles and skips) when 'everywhere' -- but then the crackles get horrible after a half hour or so. Really, bright, destroy-your-tweeters electric cracks, as opposed to the popcorn glitches I get when the option is off. (Doesn't depend on whether I'm using multiple devices, either.
Another thing to try is:
dwc_otg.speed=1
which eliminated crackles, or at least slowed them down to once or twice in a half hour.. This option forces USB1.1 speeds. The disadvantage is that more than two endpoints will refuse to play because of insufficient USB bandwidth. That's what I'm sticking for for the moment.
Other options:
dwc_otg.microframe_schedule=0
made USB fail to start up at all because of failures to allocate enough periodic transfers.
See this thread and this thread and this thread for some hints and other things to try.
Nothing satisfactory yet so the next thing is COMPILE A KERNEL, from the last link.
Linux always comes to this...
I cross-compiled the kernel mentioned in one of the previous threads, and described that process on a separate page.
Short story, it didn't work. Crackles/pops just as bad, and forcing USB 1.1 speeds made the Ethernet drop packets.
Argh, ALWAYS try the simple things first. I swapped back the two Griffin iMics for the C-Media based other devices Mics and that almost eliminated glitches with fiq_split_enable=1
. This is with the original kernel too (no dropping Ether packets when loaded.)
Now when I try to play simultaneously through two iMics and an ATR2USB, the ATR drifts out of sync while the iMics stay in sync.... THAT TELLS ME that they might be differing in terms of whether they operate in sync/async/adaptive mode (and this might explain pops and crackle.) How to tell which endpoints are in use. or configure the particular endpoints to use?
DLNA is the competing protocol to AirPlay. Actually not really competing, in that they don't do much of the same tasks at all, (AirPlay decompresses on the client and streams audio to the server; DLNA gets a media player, media library server, and remote control to work together.) But ler's add a DLNA service as well.
A recommended DLNA audio renderer is gmrender-resurrect
It downloads and builds well enough:
git clone https://github.com/hzeller/gmrender-resurrect.git
sudo apt-get install libupnp-dev gstreamer0.10-alsa gstreamer0.10-plugins-base libgstreamer-plugins-base0.10 libgstreamer-plugins-base0.10-dev libgstreamer0.10-dev
./autoconf
./configure
make
Let's try and find a DLNA player to test it out.
Then set it up with an init script...