I have a whole-home audio
system that I cobbled together out of spare PCs and low-cost, low-power
plug-computers running a mostly-stock
Debian 6.0 with
MPD and PulseAudio,
and which works better than any of the prefabricated solutions that I could
find. Not only is it better, but it deployed cheaper, and it was
actually quicker to build it than it would have been to finish evaluating
the canned solutions and buy one. And, since the protocols (and software)
in use are all open standards and Open Source, there's no danger of
vendor-lock.
This setup uses multicast
RTP
with latency-matching across all nodes in the network; I have only
one `channel' right now (in the radio-tuner or input-switching sense,
not in the `mono vs. stereo' sense), but it's possible to define
multiple channels (or `sources', in proper home-audio vocabulary)
by giving them separate multicast addresses, and then a given receiver
can be siwtched to another channel just by changing the multicast address
on which that receiver listens.
Because of the latency-matching, multiple receiver-nodes (to the
extent that the ear can tell) all have their playback perfectly
time-synced such that there's no `echo' or `reverb' effect
when standing between two adjacent rooms with separate receivers.
I actually compared this against how well two different FM radios
in adjacent rooms sync to the same radio station, and the RTP system
did better. Really, multicast RTP over ethernet + dynamic
resampling-algorithms with latency-analysis on pentium-class CPUs
with network time-sync... provides quite a convincing emulation
of speaker-wire. Of course, it also provides a lot of dreamy features
that speaker-wire... can't even dream about.
That it's MPD-based means that I can have a single playback- and
playlist-control server for the entire house, accessible from anywhere
on the network; in other words, I have multiple `single points
of control': client UIs are available for desktop and laptop computers
of all OSes, Android devices, my friends' Apple iThings
(which can automagically discover the MPD server via
ZeroConf),
etc.--there's even an adaptor that
enables `dumb phones' with Java ME
to control MPD via bluetooth (or Wi-Fi, if they have it).
MPD manages playlists on the server, and can be told by the clients
to load them or to create and store new ones--and playlists
can include not just any of the tracks stored on the server,
but also anything available via an HTTP URL (e.g.: Internet radio,
or an audio-file shared by a laptop or other system on the LAN).
Of course, Using MPD also means that I can use all of the tools and plugins
that exist for MPD--like
last.fm/audioscrobbler support,
and my `mpdjay' autojockey script
(which elicited such a positive response from my wife--along the lines of
`OMG it's so awesome! Can you make me a portable version‽'--that we now
both have NanoNote units
running a pocket-sized setup similar to this, but without the multicast).
And MPD does gapless playback,
and it can use ReplayGain to
automatically adjust for differences in overall volume between
different tracks or albums--or even do dynamicrange compression when you want
background-music in a relatively noisy environment. And, on the subject
of volume-control: there's even a central volume-control--so that you can
turn the whole house's volume up or down, after you've got the individual
output-volumes set relative to each other.
We generally run GMPC
or Sonata on our desktop and laptop systems
(though I sometimes like ncmpc
for its extremem keyboard-friendliness--and it's what I run on my
NanoNote..., but that's mostly another story), my wife runs the
Remuco JME app on her Nokia (Symbian)
phone, we use PMix on our Android
tablet, and our iFriends use something called
"MPoD",
when they're visiting. And there are plenty of other clients
available.
How does it all work?
MPD runs on the machine hosting all of the audio-files, which is set-up
as an RTP sender--and, in my case, without any local speakers:
all of the `actual audio output' is done on `receiver' nodes
elsewhere on the network (my MPD server is a noisy old machine
that we keep down in the oubliette/basement).
In /etc/mpd.conf , MPD can be told to use PulseAudio for output with
a stanza like:
audio_output {
type "pulse"
name "MPD Stream"
sink "rtp"
description "what's playing on the stereo"
mixer_type "software"
}
... where "rtp" is the name of a sink defined in /etc/pulse/default.pa via:
load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100
load-module module-rtp-send source=rtp.monitor
That last line is the one that actually makes the RTP multicasting happen.
Now, keep all of the system clocks tightly synchronised so that PulseAudio
can actually determine the network latency (by comparing the source
timestamp on the RTP packets to the system time on the receiver), and
then it's just a matter of also running Pulseaudio on the other hosts--but
with those PulseAudio instances setup as receivers, which can be done
in the respective /etc/pulseaudio/default.pa files with...:
load-module module-rtp-recv
... or, if you have a GUI running on the receiver machine(s), you can
just toggle-on the `enable Multicast/RTP receiver' setting in paprefs .
Caveats
Actually, it can be a little more complicated, because:
-
PulseAudio defaults to doing something clever that should make
playback better
but which, as I understand it, tends to trigger bugs in ALSA; so any
"load-module module-udev-detect" directives in default.pa may
need to be modified to be "load-module module-udev-detect tsched=0"
on each host.
-
There were major problems in PulseAudio's latency-matching code
that weren't resolved until this past January; so you want PulseAudio 0.9.23 or later,
but that didn't actually exist yet when Debian 6.0 was released.
I just grabbed src/modules/rtp/module-rtp-recv.c from git
and spliced it
into the version of PulseAudio that Debian already had
(there were a couple of minor wrinkles, there, that I needed to
smooth out--nothing hard, though). Figuring out that this module
was just buggy and needed to be updated was probably the biggest
problem that I ran into.
-
The ARM-based plug-computers
that I'm using have no
floating-point units,
which means that PulseAudio needs a little bit of
additional configuration
before it will work entirely right on them and produce sound reliably
through the USB audio-adaptor.
-
If your RTP-transmitter machine has multiple network interfaces,
make sure that you're routing the multicast packets out over the
correct one--I
did run into that problem!
-
If you're using DHCP, the system may hiccough when the hosts
go through DHCP cycles, so you'll want to find a way
of setting your network up such that your audio-system
doesn't decide to renew its DHCP leases in the middle of a party.
Addenda
I initially installed PTPd for
super-accurate clock-sync between hosts, but ntpd should actually
provide sufficient precision. You can use PTP
to ensure that all of the nodes on a LAN are kept in very tight sync,
even if their collective idea of `what time is it?' isn't actually
accurate.
If your LAN is connected to the Internet, then it's probably sufficient
to just use NTP--which
will allow your clocks to also be accurate in addition to being
reasonably precise.
I also installed rtkit,
which PulseAudio can use to get realtime scheduling; in Debian, I had to
get this from Wheezy/testing,
but it installed fine on 6.0/Squeeze.
Under GNOME on my normal PCs, PulseAudio starts automatically;
on the plug-computers, I had to find another way to make PulseAudio run
automatically, and the most sensible thing was to hook into Debian's
`ifupdown' system by adding `up' and `down' options to the definition
of the plug's ethernet interface in /etc/network/interfaces , such that
the complete definition of eth0 looks like this:
# The primary network interface
allow-hotplug eth0
iface eth0 inet dhcp
up su pulse --login --shell /bin/sh \
--command 'pulseaudio --exit-idle-time=-1 --daemonize'
down killall pulseaudio
The day after I got everything working, the hard disk in one of my
`speaker-wire emulator' nodes died....
[Reply]
|