Changing output source on the fly

Posted on October 13, 2020 by abhinav

Changing sound output of application to use Bluetooth headphoes

So with Tiling window manager(XMonad) I don’t have a systray setup. By that, I don’t have any tray applet widgets. One of the disadvantage of that is that when I have firefox or any application which plays sound by default it uses my laptop speaker.

When I connect my Bose QC II it doesn’t change the audio automatically to it. Up until now I used to close and then reopen Firefox or any application that still played the audio using my Laptop’s speakers.

More recently when I was dabbling with multiple Desktop Environments(XFCE, Gnome, KDE, LXDE) I noticed the same behavior. Was expecting DE’s to handle this behavior rather seamlessly but I noticed that I had to change to my bluetooth through the systray applet. This got me thinking that there should be a way for me to achieve this through commandline.

Enter commandline

First we need to get the list of audio sinks available:

$ pactl list sinks short
0       alsa_output.pci-0000_00_1f.3.analog-stereo      module-alsa-card.c      s16le 2ch 48000Hz       SUSPENDED
3       bluez_sink.28_11_A5_71_61_49.a2dp_sink  module-bluez5-device.c  s16le 2ch 44100Hz       SUSPENDED
4       alsa_output.pci-0000_01_00.1.hdmi-stereo        module-alsa-card.c      s16le 2ch 44100Hz       SUSPENDED

Hopefully it is obvious from the output that 3(in my case) is what we want as sink for my bluetooth headphones.

If you want a more verbose list you could use pacmd

$ pacmd list-sinks

The above command spits a lot more info along with the product name(in case you have multiple Bluetooth devices and want to narrow down on the right one.

If you want to change the default output sink you could just type this command in

pacmd set-default-sink 1

But in my case I wanted to change the stream for firefox to my Bose headphones.

$ pacmd list-sink-inputs
1 sink input(s) available.
    index: 0
        driver: <protocol-native.c>
        flags: START_CORKED 
        state: CORKED
        sink: 3 <bluez_sink.28_11_A5_71_61_49.a2dp_sink>
        volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        muted: no
        current latency: 154.69 ms
        requested latency: 45.32 ms
        sample spec: float32le 2ch 48000Hz
        channel map: front-left,front-right
                     Stereo
        resample method: speex-float-1
        module: 12
        client: 100 <Firefox>
        properties:
                media.name = "AudioStream"
                application.name = "Firefox"
                native-protocol.peer = "UNIX socket client"
                native-protocol.version = "34"
                application.process.id = "1531"
                application.process.user = "abhinav"
                application.process.host = "archpad"
                application.process.binary = "firefox"
                application.language = "en_US.UTF-8"
                window.x11.display = ":0"
                application.process.machine_id = "b0cbd25c457647ca86dadf6e87280db1"
                application.process.session_id = "1"
                application.icon_name = "firefox"
                module-stream-restore.id = "sink-input-by-application-name:Firefox"

From above you can see that the sink input index is 0 for Firefox. To move the stream to bluetooth headphones, we need to run

pacmd move-sink-input 0 3

In the above command 0 is the index of the input stream and 3 index of the sink, which is the Bose headphones.

Voila, now my input stream is routed to the headphones without having to restart any application.