PulseAudio sound forwarding across a network

Here is a command-line-only way to forward audio over a network in Ubuntu 8.10+. Ubuntu uses PulseAudio as a sound server. It can be configured (esp. using the padevchooser package) to do this “properly”, but I wanted something fast that didn’t require configuration (and, uhm, I didn’t know about padevchooser).

The first step is to establish a secure connection between the machines using SSH to forward a local TCP port (the one with the sound application) to a TCP port on the remote machine (the one with the speakers):

(on the computer with the sound application)
ssh -L4000:localhost:4000 remotehost

PulseAudio is a server that listens for connections on a Unix domain socket by default, which means 1) it can’t be accessed remotely, and 2) ssh can’t redirect a TCP port to it directly. One could configure PulseAudio to use a TCP port instead, but then you have to worry about security. So instead, use the socat tool (you might need to install the socat package) on the remote machine to forward the remote machine’s local port 4000 to its PulseAudio Unix socket:

(on the computer with the speakers, e.g. inside the SSH session)
socat TCP-LISTEN:4000,fork UNIX-CONNECT:/tmp/pulse-$USER/native

socat will accept only local connections by default, which is why we need to SSH to the remote machine to connect — that’s a good thing if you like security.

The “,fork” option has socat listen for multiple connections. Otherwise it’ll quit after the first connection (it’ll play one sound file and exit). You can run socat once you’ve logged in with SSH. You might think you can do it all on one line (because ssh takes a second command line argument for a command to run), but it doesn’t work well with this “,fork” option because when you CTRL+C SSH to end the session, socat keeps running (which might be fine, I guess, but it will prevent you from running it a second time since something will already be listening on port 4000).

Now we can have PulseAudio-enabled programs play sound remotely by specifying to put the sound on the local port 4000, rather than to where it normally puts it for local sound. The paplay program plays a wav file:

paplay soundfile.wav (plays it locally)
paplay -s localhost:4000 soundfile.wav (plays it remotely)

Or equivalently by setting an environment variable:
PULSE_SERVER=localhost:4000 paplay soundfile.wav

The environment variable method should work for any other program that plays sound with PulseAudio.

The tricks begin when programs don’t support PulseAudio and instead use OSS or Alsa to play sounds. In principle, the padsp command is able to redirect the use of /dev/dsp (i.e. OSS output) to PulseAudio. Likewise, for programs that output sound using ALSA, you can redirect the output to PulseAudio by setting the environment variable ALSA_PCM_NAME=pulse.

PULSE_SERVER=localhost:4000 padsp application_using_oss
PULSE_SERVER=localhost:4000 ALSA_PCM_NAME=pulse application_using_alsa

I wanted OSS support for running Festival, the speech synthesis program, but this method doesn’t work in the versions of everything I have – Festival segfauls if you use padsp on it. So instead there are more tricks. You can override how Festival plays sounds with some Festival commands. Here’s how to have it output with the PulseAudio sound player (and with the command to redirect the output to the SSH forwarded port):

(Parameter.set ‘Audio_Method ‘Audio_Command)
(Parameter.set ‘Audio_Required_Format ‘wav)
(Parameter.set ‘Audio_Command “paplay -s localhost:4000 $FILE”)

I also wanted this to work with the Praat phonetics program, which currently outputs using ALSA, but it did not recognize the ALSA environment variable setting as described above, so it may not be possible to redirect its sound output this way.

4 Responses to “PulseAudio sound forwarding across a network”

  1. pwn says:

    This didn’t work. It said connection refused.

  2. pwn says:

    Nevermind, it worked now. :)

  3. Mark says:

    Thanks for this. Didn’t quite work for me. The use of socat to connect to the local filesystem socket won’t work in the version of pulseaudio I have because the directory name in /tmp is randomised.

    Instead, I have done

    socat TCP-LISTEN:4000,fork TCP:localhost:4713

    and enabled tcp access in pulseaudio /etc/pulseaudio/default.conf

    load-module module-esound-protocol-tcp auth-anonymous=1
    load-module module-native-protocol-tcp auth-anonymous=1

  4. g says:

    thank you! on my box (libpulse0 1:0.9.22~0.9.21+stable-queue-32-g8478-0ubuntu14) it’s UNIX-CONNECT:/tmp/pulse-GVI2xrVn23oQ/native

Leave a Reply