Switching PulseAudio output device

25.06.2012 13:27

I have my desktop computer connected to a hi-fi and a television screen through HDMI. This means that I'm often switching video and audio outputs from my monitor and speakers on the desk to the TV and vice-versa. Changing video output via xrandr from a shell script is trivial. Convincing PulseAudio daemon to switch audio outputs is a completely different can of worms.

In GNOME you switch the audio output from the user-friendly Sound Preferences window (internally called gnome-volume-control). This requires several clicks of the mouse and doesn't have a command-line interface to it as far as I know. Its effect is instantaneous and even works reliably with applications that are playing audio while switching outputs.

PulseAudio itself comes with two basic command line tools, called pacmd and pactl. Former offers a set-default-sink command, but that doesn't do what you expect from it. It turns out that, at least on my system, PulseAudio daemon keeps persistent settings for each application that ever connected to it. This means that the default sink only gets used for applications that the daemon hasn't seen yet.

Digging through the source code of gnome-volume-control revealed that it walks through the stored persistent settings and modifies them each time you change the sound output device in that dialog window.

So what I did is copy the way gnome-volume-control does it and package it into a simple command-line utility, paswitch.

You can get it here:

$ git clone https://www.tablix.org/~avian/git/paswitch.git

Once you compile it by calling make, the usage is straightforward:

$ paswitch alsa_output.pci-0000_01_00.1.hdmi-stereo

To obtain the name of the sink, you can run pactl:

$ pactl list
...
Sink #0
        State: SUSPENDED
        Name: alsa_output.pci-0000_01_00.1.hdmi-stereo
...

By the way, this answer offers a different way of doing it. However it requires turning off persistent settings via restore_device=false in PulseAudio configuration.

Posted by Tomaž | Categories: Code

Comments

You are a genius, thanks for this.

I had the same feeling, too many click, and i couldn't switch with pacmd then i found your page

Hi - your solution looks really promising, though I'm getting link errors on the compilation.
I'm on Ubuntu 12.04.

It picks up the headers just fine (I can compile an object), but it seems it isn't finding the required library ...

ah! had to change the compile/link line to:
gcc -Wall -o $@ $^ -lpulse
(gcc -Wall -o paswitch main.c -lpulse)

ld docs state that it won't research libraries to resolve symbols in object that appear later on the command line

and now, to try out the code ;-)

Sweet! works just as advertised! :-)

I'd recommend running this command to find your sinks ... much easier to parse:
pactl list short sinks

Thank you very much.

Sean, thanks for the suggestion. I just pushed a couple of commits to the git repository that should also fix the linking problem.

Posted by Tomaž

Thanks so much for these hints. I have now set it up so that I switch from PC to TV using CTRL Left and CTRL Right (TV is right from my PC) and volume up and down with CTRL Up and CTRL Down. I have used xbindkeys for this (xbindkeys-config allows you to set it all up easily)

Just in case someone is interested, these are my four dirty scripts:

audioTV.sh:
$HOME/bin/paswitch/paswitch alsa_output.pci-0000_01_00.1.hdmi-stereo
play /usr/share/sounds/freedesktop/stereo/audio-volume-change.oga

audioPC.sh:
$HOME/bin/paswitch/paswitch alsa_output.pci-0000_00_1b.0.analog-stereo
play /usr/share/sounds/freedesktop/stereo/audio-volume-change.oga

volumeUP.sh
echo "5*(`cat $HOME/bin/volume.txt`<=95)+`cat $HOME/bin/volume.txt`" | bc -l > $HOME/bin/volume.tmp
mv $HOME/bin/volume.tmp $HOME/bin/volume.txt
pactl set-sink-volume 0 `cat $HOME/bin/volume.txt`%
pactl set-sink-volume 1 `cat $HOME/bin/volume.txt`%
play /usr/share/sounds/freedesktop/stereo/audio-volume-change.oga

volumeDOWN.sh
echo "(`cat $HOME/bin/volume.txt`-5)*(`cat $HOME/bin/volume.txt`>=5)" | bc -l > $HOME/bin/volume.tmp
mv $HOME/bin/volume.tmp $HOME/bin/volume.txt
pactl set-sink-volume 0 `cat $HOME/bin/volume.txt`%
pactl set-sink-volume 1 `cat $HOME/bin/volume.txt`%
play /usr/share/sounds/freedesktop/stereo/audio-volume-change.oga

Switching audio is now a pleasure!

Hi

Thank you for this. I tried this out and it works perfectly!

I have created a package for it and added it to the Arch Linux User Repository (AUR)

See it here: https://aur.archlinux.org/packages/paswitch/

Posted by Andy Pieters

This is exactly what I was looking for. Thanks!

Also thanks to Andy for the AUR package. Made it super simple.

Posted by Ben

Man, thanks so much!

Posted by calexandre

Just wrote this script which will switch to the first idle sink, rather than hardcoding the sink names into my script:

#!/bin/bash

# Here we first need to work out the first idle sink, then switch to it, if we only have one sink (i.e. no headphones) then we should be good
pactl list short sinks | while read SINKLINE
do
# first extract the sink status, if it is idle, extract the name and switch to it, using paswitch
sinkStatus=$(echo $SINKLINE | cut -d' ' -f7)
if [[ ${sinkStatus} == "IDLE" ]]; then
sinkName=$(echo $SINKLINE | cut -d' ' -f2)
paswitch ${sinkName}
fi
done

Add a new comment


(No HTML tags allowed. Separate paragraphs with a blank line.)