15

I like to switch the sound output from Speaker to USB headphone with a Shortcut. Is there a way to accomplish this?

user.dz
  • 47,137
  • 13
  • 140
  • 258
Evenbit GmbH
  • 4,598
  • 4
  • 23
  • 33
  • 1
    Closely related: http://askubuntu.com/questions/41858/shortcut-to-switch-between-analog-stereo-output-hdmi-audio-output – Takkat Jun 28 '12 at 06:16

8 Answers8

8

Automated solution https://ubuntuforums.org/showthread.php?t=1370383 It works on Ubuntu 18.04

  1. Open the terminal and type:

     sudoedit /usr/local/bin/audio-device-switch.sh
    
  2. Copy and paste the below code in nano editor

  3. Save it and close nano editor.

  4. sudo chmod 755 /usr/local/bin/audio-device-switch.sh

  5. System -> Preferences -> Keyboard Shortcuts

  6. Press Add and enter Switch between audio devices as name and audio-device-switch.sh as command and press Apply.

  7. Select the newly added shortcut row and click on the shortcut column. 8. Choose a shortcut combination – e.g. Win + F12.

  8. That's all - now you can plug in your plug in your HDMI device and switch the audio output by pressing the chosen shortcut combination.

Code:

#!/bin/bash

declare -i sinks_count=`pacmd list-sinks | grep -c index:[[:space:]][[:digit:]]`
declare -i active_sink_index=`pacmd list-sinks | sed -n -e 's/\*[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p'`
declare -i major_sink_index=$sinks_count-1
declare -i next_sink_index=0

if [ $active_sink_index -ne $major_sink_index ] ; then
    next_sink_index=active_sink_index+1
fi

#change the default sink
pacmd "set-default-sink ${next_sink_index}"

#move all inputs to the new sink
for app in $(pacmd list-sink-inputs | sed -n -e 's/index:[[:space:]]\([[:digit:]]\)/\1/p');
do
    pacmd "move-sink-input $app $next_sink_index"
done

#display notification
declare -i ndx=0
pacmd list-sinks | sed -n -e 's/device.description[[:space:]]=[[:space:]]"\(.*\)"/\1/p' | while read line;
do
    if [ $next_sink_index -eq $ndx ] ; then
        notify-send -i notification-audio-volume-high "Sound output switched to" "$line"
        exit
    fi
done
matreshkin
  • 181
  • 1
  • 2
  • great work! don't try to run the script as root (e.g. with sudo), it will not work. just run as normal user – sotix Nov 30 '20 at 16:44
  • I had to fix the last if and do to match the bash syntax. The script works perfectly!. – Gor Stepanyan Dec 22 '20 at 14:39
  • I just realized, that the indices of sinks are not necessarily counted from 0 to COUNT-1, so you have to get list of indices and then move to the next one from the list – fairtrax Jan 02 '21 at 09:53
8
  1. Check for port names pactl list sinks (I remove non needed sinks output):

    Sink #1
        State: RUNNING
        Name: alsa_output.pci-0000_00_1b.0.analog-stereo
        Description: Built-in Audio Analog Stereo
        Driver: module-alsa-card.c
    ...
        Ports:
            analog-output-speaker: Speakers (priority: 10000, not available)
            analog-output-headphones: Headphones (priority: 9000, available)
        Active Port: analog-output-headphones
        Formats:
            pcm
    
  2. Set sink port using pactl set-sink-port:

     pactl set-sink-port 1 analog-output-speaker
    

    or

     pactl set-sink-port 1 analog-output-headphones
    

    If you are using a removable device (Example: USB devices), it's better to use sink name instead of id. For example:

     pactl set-sink-port alsa_output.pci-0000_00_1b.0.analog-stereo analog-output-headphones
    

Reference: man pactl

user.dz
  • 47,137
  • 13
  • 140
  • 258
1

I have Ubuntu 20, and realized, that the indices of devices are not counted from 0 to COUNT-1. So I had to modify the script. This one works now:

#!/bin/bash

declare -i sinks_count=`pacmd list-sinks | grep -c index:[[:space:]][[:digit:]]`

if [ $sinks_count -eq 0 ] ; then
    exit
fi

declare -i active_sink_index=`pacmd list-sinks | sed -n -e 's/\*[[:space:]]index:    [[:space:]]\([[:digit:]]\)/\1/p'`

active_index_position_found=0
let next_sink_index=-1
while read index ;
do
    declare -i ind=($(echo $index | tr -dc '[0-9]+'))
    if [ $next_sink_index -lt 0 ] ; then
        export next_sink_index=$ind
    fi
    if [ $active_index_position_found -eq 1 ] ; then
        export next_sink_index=$ind
        break;
    fi
    if [ $active_sink_index -eq $ind ] ; then
        export active_index_position_found=1
    fi
done < <(pacmd list-sinks | grep index:[[:space:]][[:digit:]])

#change the default sink
pacmd "set-default-sink ${next_sink_index}"

#move all inputs to the new sink
for app in $(pacmd list-sink-inputs | sed -n -e 's/index:[[:space:]]\([[:digit:]]    \)/\1/p');
do
    pacmd "move-sink-input $app $next_sink_index"
done

#display notification
declare -i ndx=0
pacmd list-sinks | sed -n -e 's/device.description[[:space:]]=[[:space:]]"\(.*\)"    /\1/p' | while read line;
do
    if [ $next_sink_index -eq $ndx ] ; then
    notify-send -i notification-audio-volume-high "Sound output switched to"     "$line"
        exit
    fi
    ndx+=1
done;
fairtrax
  • 111
  • 1
  • This still didn't work for me. I got the following error when I tried to run the script. `Sink 2945029504090482540 does not exist` – Heisenberg Sep 20 '21 at 13:52
1

It was not working with two digit indices. In my case Nvidia HDMI sink was with index 23. Here is a working solution :)

#!/bin/bash    
    
declare -i sinks_count=`pacmd list-sinks | grep -Pc 'index:\s+\d+'`    
    
if [ $sinks_count -eq 0 ] ; then    
    exit    
fi    
    
declare -i active_sink_index=`pacmd list-sinks | grep -Po '\*\s+index:\s+\K\d+'`    
    
active_index_position_found=0    
let next_sink_index=-1    
while read index ;    
do    
    declare -i ind=($(echo $index | tr -dc '[0-9]+'))    
    if [ $next_sink_index -lt 0 ] ; then    
        export next_sink_index=$ind    
    fi    
    if [ $active_index_position_found -eq 1 ] ; then    
        export next_sink_index=$ind    
        break;    
    fi    
    if [ $active_sink_index -eq $ind ] ; then    
        export active_index_position_found=1    
    fi    
done < <(pacmd list-sinks | grep -Po 'index:\s+\K\d+')    
    
#change the default sink    
pacmd "set-default-sink ${next_sink_index}"    
    
#move all inputs to the new sink
for app in $(pacmd list-sink-inputs | grep -Po 'index:\s+\K\d+');
do
    pacmd "move-sink-input $app $next_sink_index"
done
1

Since everyone's been adding their solutions, here's mine.

#!/bin/sh

currentline=$(pactl list short sinks | grep -n "$(pactl get-default-sink)" | cut -d: -f 1)
lastline=$(pactl list short sinks | wc -l)
nextline=$(($currentline % $lastline + 1))
nextsink=$(pactl list short sinks | head "-n$nextline" | tail -1 | cut -f 1)

pactl set-default-sink $nextsink

for sinkinput in $(pactl list short sink-inputs | cut -f 1); do
  pactl move-sink-input $sinkinput "@DEFAULT_SINK@"
done
j2L4e
  • 190
  • 1
  • 7
0

The only script version that worked for me was thew one @rosetta-stoned shared above. Scripts from other comments did not. [OS: Ubuntu Mate 22.04 LTS (Jammy Jellyfish) 64-bit]

I further extended the script with a line to play a sound. This way you can hear sound in the devices as you keep switching output devices. Hearing sound in the desired device will mean you don't need to swap around anymore.

#!/bin/bash

declare -i sinks_count=`pacmd list-sinks | grep -Pc 'index:\s+\d+'`

if [ $sinks_count -eq 0 ] ; then
    exit
fi

declare -i active_sink_index=`pacmd list-sinks | grep -Po '\*\s+index:\s+\K\d+'`

active_index_position_found=0
let next_sink_index=-1
while read index ;
do
    declare -i ind=($(echo $index | tr -dc '[0-9]+'))
    if [ $next_sink_index -lt 0 ] ; then
        export next_sink_index=$ind
    fi
    if [ $active_index_position_found -eq 1 ] ; then
        export next_sink_index=$ind
        break;
    fi
    if [ $active_sink_index -eq $ind ] ; then
        export active_index_position_found=1
    fi
done < <(pacmd list-sinks | grep -Po 'index:\s+\K\d+')

#change the default sink
pacmd "set-default-sink ${next_sink_index}"

#move all inputs to the new sink
for app in $(pacmd list-sink-inputs | grep -Po 'index:\s+\K\d+');
do
    pacmd "move-sink-input $app $next_sink_index"
done
paplay /usr/share/sounds/mate/default/alerts/sonar.ogg
Netsmile
  • 1
  • 1
0

I added support for "prev" and "next" arguments, since I have quite a few devices to choose from. Just bind 2 keys.

#!/bin/bash

declare direction="$1"

declare -i sinks_count=`pacmd list-sinks | grep -Pc 'index:\s+\d+'`

if [ $sinks_count -eq 0 ] ; then
    exit
fi

declare -i active_sink_index=`pacmd list-sinks | grep -Po '\*\s+index:\s+\K\d+'`

readarray -t indexes < <(pacmd list-sinks | grep -Po 'index:\s+\K\d+')

declare indexes_count=${#indexes[@]}

declare active_index=-1

for i in "${!indexes[@]}"; do
    if [[ "${indexes[$i]}" = "${active_sink_index}" ]]; then
        active_index=$i;
    fi
done

declare next_index=$((( $active_index + 1 ) % $indexes_count))
declare prev_index=$((( $active_index - 1 ) % $indexes_count))

declare next_sink_index=${indexes[$next_index]}
declare prev_sink_index=${indexes[$prev_index]}

declare sink_to_use="${next_sink_index}"

if [ "$direction" = "prev" ] ; then
    sink_to_use="${prev_sink_index}"
fi

# Change the default sink
pacmd "set-default-sink ${sink_to_use}"

# Move all inputs to the new sink
for app in $(pacmd list-sink-inputs | grep -Po 'index:\s+\K\d+');
do
    pacmd "move-sink-input $app $sink_to_use"
done
RanzQ
  • 1
-1

Do this in 2 steps:

  1. Find a command line setting to change back/forth between these settings.

  2. Add these to some key combinations. Systems Settings >> Keyboard >> Shortcuts

david6
  • 14,449
  • 4
  • 36
  • 46
  • How can one find out, what command line setting is needed to switch the sound output? Is there a way to trace what happens, when I do it with the GUI? – Evenbit GmbH Jun 28 '12 at 05:43
  • No, you need to use **amixer** (command line) and/or **alsamixer** (char-mode) in a terminal. These are old-school, and require some effort to master. Start by adding output from **amixer -c 0** to your question .. – david6 Jun 28 '12 at 07:16
  • 1
    I find that command and successfully run a shortcut to switch between analog and HDMI output, see [my answer here](http://askubuntu.com/a/898638/349837). – Pablo Bianchi Mar 30 '17 at 22:40