Wednesday, February 23, 2011

PulseAudio: streaming iTunes from the mac to linux

After beating my head against a wall for the past three days, I finally overcame my mental block and was able to get audio streaming from iTunes to my Linux box. This was to help facilitate the production of a video that will be the subject of my next post.

For this project though, I had to jump through a good number of hoops to get the streaming going. The side benefit was that the project allowed me to educate myself on the benefits of PulseAudio, which I had previously believed to be a piece of crap that only served to bollucks your workstation's audio. Now that I have a better understanding of PulseAudio, I've changed my mind.

Here's an article on why you might want to care about PulseAudio.

Still, the implementation of audio streaming over the network using PulseAudio is somewhat programmer-centric and thus, it is not easy. The faint of heart might choose to simplify their lives and buy one of these Apple AirPort Express with Air Tunes.

This post will give an overview of the tools used to stream audio from a Mac running the Enlightment Sound Daemon (ESD) sound server to any PC running PulseAudio. I'll include links to the more detailed resources, rather than repeating information already available on the web.

Here's an overly simplified signal chain of what's happening to make this work.

On the Mac
iTunes -> Soundflower audio driver -> eSound daemon -> Linux box

On the Linux box
audio stream from Mac -> PulseAudio -> ALSA -> PC's audio interface -> speakers

It's a bit of a funky setup, over-explained semi-coherently here:
and here:

The Process
On the Mac
Prerequisites: iTunes and esound macport
1) install Soundflower, the audio driver that ESD will use
2) setup an ESD sound server using ESD and open a network connection for the Linux box
3) format and forward the sound onto the Linux box

On the Linux box
Prerequisites: pulseaudio, paprefs(pa prefs), pavucontrol(pa volume control), pulseaudio-utils
4) make sure paprefs is setup properly
5) start pulseaudio
6) verify that data is being sent over the network

Detailed Steps
On the Mac
1) install Soundflower and select Soundflower 2ch as your System Prefs -> Sound -> Output and Input
Best explained here:

For debugging purposes, enter the commands below in separate terminals. In both cases, if the commands execute correctly, the command prompt should not return to you.

2) setup an audio server using the Enlightment Sound daemon (ESD) and open a port for your Linux box to connect to. To do this, open a terminal and run a command similar to this:
mac:~ sodo$ esd -tcp -bind [mac]:16001 -public -promiscuous -trust
- accepting connections on port 16001

3) format and forward the sound to the Linux box (make sure PA is running on the Linux box first):
esdrec -s [mac]:16001 esdcat -s [linux]

On the Linux box
4) make sure paprefs is setup properly
There's more detail at this link, but here are the prefs you want to set:

5) start (or kill and restart) pulseaudio
[sodo@ogre ~]$ pulseaudio -k
[sodo@ogre ~]$ pulseaudio --start
[sodo@ogre ~]$ ps -ef grep pulse
sodo 3860 1 0 00:06 ? 00:00:01 /usr/bin/pulseaudio --start --log-target=syslog
sodo 3862 3860 0 00:06 ? 00:00:00 /usr/libexec/pulse/gconf-helper
sodo 5298 5045 0 00:09 pts/4 00:00:00 grep pulse

Here are some other notes regarding pacmd, the command line interface to PulseAudio.

pacmd list-modules
>>> [sodo@ogre ~]$ pacmd list-modules
Welcome to PulseAudio! Use "help" for usage information.
>>> 23 module(s) loaded.
index: 4
used: 1
load once: no
properties: = "Lennart Poettering"
module.description = "ALSA Card"
module.version = "0.9.21"

pacmd list-sinks
[sodo@ogre ~]$ pacmd list-sinks
Welcome to PulseAudio! Use "help" for usage information.
>>> 1 sink(s) available.
* index: 0
state: RUNNING

Once you've verified the daemon is up, look for a sound source in the PulseAudio Volume Control app, Playback tab:

I'm not sure why the stream from the mac is listed as null.

6) verify that data is being sent over the network
First, I look at netstat. Netstat is telling me that my Linux box (.8) on port 16001 has an established connection to my Mac (.12) on port 62356:
[sodo@ogre ~]$ netstat -na grep \.12

Most likely, the Linux box initiated the connection from 16001 and the Mac responded back by opening port 62356. I am not sure how ESD works in this regard either, as the ports identified on the ESD command line would suggest that the Mac should have port 16001 open and not the Linux box. Weirdness.

And make sure access to port 16001 is not disabled on the bloody firewall, either local on each box or a remote firewall between the machines!  I use an exclamation point here because this got me twice when I was setting this whole thing up:

Also, I use iptraf to show me the volume of my network traffic. Here we see the lion's share of data is coming from .12 (my Mac) and .8 (my Linux box) is shuttling a lot less data. Observed over time, I see that the Linux box sends about 1% of the audio stream's data quantity simply to manage the connection:

Lastly, you could take a look at the files PulseAudio keeps open by looking at TCP port 16001:
[sodo@ogre ~]$ lsof -i TCP:16001
pulseaudi 3860 sfrase 30u IPv6 48581 0t0 TCP *:16001 (LISTEN)
pulseaudi 3860 sfrase 31u IPv4 48582 0t0 TCP *:16001 (LISTEN)
pulseaudi 3860 sfrase 36u IPv4 49374 0t0 TCP ogre:16001->mac:50314 (ESTABLISHED)

PulseAudio is quite cool. I think it works a bit more consistently if you have two machines with PulseAudio on both ends. I've tried this and it works very well. Here's an article on how to stream music between two computers using only PulseAudio:

Of course, I needed to stream from the Mac and there is no port of PulseAudio for the Mac. Still good stuff this PulseAudio, once you understand more about it.

Example of this working

Update 2012/12/03
After upgrading both my Fedora box from F15 to F17 and upgrading my Mac from Leopard to Lion, pulseaudio streaming broke.  I had to do the following:
1) on the mac, download and install the latest version of SoundFlower (reboot necessary)
2) on the linux box, download and install pulseaudio-module-zeroconf (above and beyond the normal pulseaudio installation)
3) on the linux box, make sure iptables is turned off (just for testing purposes..reenable with an exception for port 16001).  On Fedora 17, this would be:
systemctl stop iptables.service

Updated Process
Also, I streamlined my process for getting this to work.  Try the below steps in this same order:
1) on the mac, make sure both input and output is set to SoundFlower (2CH)
2) on the mac, stream eSound to a public port, like so:
esd -tcp -bind ::0
3) on linux, make sure pulseaudio is running and that paprefs is set (as above)
4) on the mac, open an eSound stream to the Linux box
esdrec -s ::0 | esdcat -s
5) you should then see a nice volume control named "null" appear under the Playback tab in the PulseAudio volume control (pavucontrol)

Good luck!

Other References
PulseAudio Sink, Streams, Clients

How to: AirTunes without AirPort

network audio between mac and linux 1&2

Remote sound playback through a Nokia 770

Using the PulseAudio sink

PulseAudio Wiki

Linux: exporting audio and video


Jim Finn said...

If you add a -n flag to esdcat, it will give a name to your connection on the Linux side. For example...

esdcat -n "Macintosh" -s ::1

Would cause your connection to display as "Macintosh" in the pulse audio playback devices .. instead of 'null'.

Cacasodo said...

Thanks for the useful tip!

Feel free to drop me a line or ask me a question.