Tue 2019-12-31 20:57

  • Note
  • Posted:
  • Edited:

I've used PulseAudio on many Linux laptops and workstations over the years, and it usually works so well, so consistently that I've never felt the need to dig around under the covers. It was basically this magical software that I installed and my computer had sound.

Over the holiday break, I had some spare time to tinker and I thought it might be nice to finally put together some kind of audio streaming solution for the apartment. Sort of a "whole house audio" system - without the wiring. I setup a music server running MPD on an older Intel NUC I had sitting in my parts drawer, and NFS-mounted my music library from the NAS. Then I enlisted a couple of old Raspberry Pi 2B's I had sitting around collecting dust, added a USB WiFi adapter to each, and made them my audio receivers to connect to speakers.

The question then was, how to get music from the MPD server to the RPis?

It turns out the answer was pretty simple and - as you've probably guessed already - it involved PulseAudio. Did you know that PulseAudio has a network transport? I didn't. It was time for me to learn some new stuff about an old tool.

First off, because no one would be logged into a desktop session on these devices I had to set PulseAudio on the music server and the RPis to run in system mode. Some tweaking was involved to accomplish this (see the linked docs) and all of the config lines I used below go in /etc/pulse/ rather than /etc/pulse/

For strangers to the innards of PulseAudio, first grasp the basic concept that PulseAudio defines inputs and outputs as "sources" and "sinks". My first step was to create a new fake sink (a "null" sink) to receive the system audio, and transport the audio sent to that null sink (captured by the "monitor" of that sink) via RTP stream to my RPis.

On the server:

load-module module-null-sink sink_name=rtp 
load-module module-rtp-send source=rtp.monitor mtu=1408 destination_ip=[remote IP 1] 
load-module module-rtp-send source=rtp.monitor mtu=1408 destination_ip=[remote IP 2]

On the RPis:

load-module module-rtp-recv sap_address=[local IP]

Well, that was actually pretty simple. I configured MPD to use PulseAudio as an output, restarted both PulseAudio and MPD, and started playing a song in MPD. BINGO! Synchronous streaming audio to the RPis and their connected speakers, and the apartment was filled with music in every room.

I played with this setup for a day or so: adding music playlists to MPD, setting up M.A.L.P. on my phone to remote control MPD, etc. Then I thought, wouldn't it be cool if I could take the output from my SiriusXM receiver and stream that audio to every room, also? It turns out that yes - it is cool. But it was a task not without some challenges to accomplish.

For starters, my NUC didn't have a stereo line-in port so I picked up a no-name $15 USB sound card that did. Then I thought it would be a simple matter of plugging in the stereo out from the SiriusXM receiver to the line-in on the USB card, but nothing is ever that simple. I had to go back to the PulseAudio docs and this time learn about the loopback module, which acts like a virtual mixer.

First, I had to create an additional null sink dedicated for MPD:

load-module module-null-sink sink_name=mpd_out

Next, I had to tell MPD to send its output to that sink (in /etc/mpd.conf):

audio_output {
    type    "pulse"
    name    "Pulse Output"
    sink    "mpd_out" 

Then I had to use pactl list sources to find the cryptic ass name PulseAudio had given to the USB sound card interface, which turned out to be:


Say that five times fast.

Finally, I had to mux the monitor of the MPD sink and the line-in source together by sending them both through the loopback module to the RTP stream sink:

load-module module-loopback source=mpd_out.monitor sink=rtp
load-module module-loopback source=alsa_input.usb-0d8c_USB_Sound_Device-00.analog-stereo sink=rtp

I turned on the SiriusXM receiver and... nothing. The USB sound card has both a mono mic port and a stereo line-in port, and for whatever reason the mic port has a higher default priority and the system preferred it as the capture device. I had to tell PulseAudio to prefer the line-in port instead. While I was at it, I set the volume on the line-in port to normalize it with the MPD output, and made sure the port was not muted:

set-source-port 1 analog-input-linein
set-source-volume 1 15000
set-source-mute 1 0

I restarted PulseAudio and the SiriusXM output started piping to all of the speakers in the house. So cool. Now I have a whole house audio system with a choice of either playlists from my music library or live SiriusXM satellite radio. I think my next project will be to setup an IR receiver/repeater system so I can change the radio station while sitting on the couch in the other room.

Submit a webmention

You may reply, like, repost, or bookmark this page by submitting a webmention.