This script will cycle to the next available audio output device. It can be tied to a hotkey to easily be triggered. This is handy, for example, for swapping between speakers and headphones.
This script will work on systems running PulseAudio or Pipewire services.
Tools/commands used in this script
declare
- declares a new variable, allows for setting 'types'pacmd
- provides information about PulseAudio audio devicespactl
- provides information about PipeWire audio devicesnotify-send
- sends pop-up notifications to user (may need to be installed withsudo apt install libnotify-bin
)- Icons
-i
can be found in the following locations:/usr/share/icons/gnome/32x32
/usr/share/notify-osd/icons/
- Icons
grep
- the ever useful search command- Here, used with Character Classes
sed
- the inline editing toolmapfile
- the recommended way to populate arrays from grep results- Run through ShellCheck to verify script.
Complete Script
#!/bin/bash
# Check which sound server is runningif pgrep pulseaudio >/dev/null; then sound_server="pulseaudio"elif pgrep pipewire >/dev/null; then sound_server="pipewire"else echo "Neither PulseAudio nor PipeWire is running." exit 1fi
# Grab a count of how many audio sinks we haveif [[ "$sound_server" == "pulseaudio" ]]; then sink_count=$(pacmd list-sinks | grep -c "index:[[:space:]][[:digit:]]") # Create an array of the actual sink IDs sinks=() mapfile -t sinks < <(pacmd list-sinks | grep 'index:[[:space:]][[:digit:]]' | sed -n -e 's/.*index:[[:space:]]\([[:digit:]]\)/\1/p') # Get the ID of the active sink active_sink=$(pacmd list-sinks | sed -n -e 's/[[:space:]]*\*[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p')
elif [[ "$sound_server" == "pipewire" ]]; then sink_count=$(pactl list sinks | grep -c "Sink #[[:digit:]]") # Create an array of the actual sink IDs sinks=() mapfile -t sinks < <(pactl list sinks | grep 'Sink #[[:digit:]]' | sed -n -e 's/.*Sink #\([[:digit:]]\)/\1/p') # Get the ID of the active sink active_sink_name=$(pactl info | grep 'Default Sink:' | sed -n -e 's/.*Default Sink:[[:space:]]\+\(.*\)/\1/p') active_sink=$(pactl list sinks | grep -B 2 "$active_sink_name" | sed -n -e 's/Sink #\([[:digit:]]\)/\1/p' | head -n 1)fi
# Get the ID of the last sink in the arrayfinal_sink=${sinks[$((sink_count - 1))]}
# Find the index of the active sinkfor index in "${!sinks[@]}"; do if [[ "${sinks[$index]}" == "$active_sink" ]]; then active_sink_index=$index fidone
# Default to the first sink in the listnext_sink=${sinks[0]}next_sink_index=0
# If we're not at the end of the list, move up the listif [[ $active_sink -ne $final_sink ]]; then next_sink_index=$((active_sink_index + 1)) next_sink=${sinks[$next_sink_index]}fi
#change the default sinkif [[ "$sound_server" == "pulseaudio" ]]; then pacmd "set-default-sink ${next_sink}"elif [[ "$sound_server" == "pipewire" ]]; then # Get the name of the next sink next_sink_name=$(pactl list sinks | grep -C 2 "Sink #$next_sink" | sed -n -e 's/.*Name:[[:space:]]\+\(.*\)/\1/p' | head -n 1) pactl set-default-sink "$next_sink_name"fi
#move all inputs to the new sinkif [[ "$sound_server" == "pulseaudio" ]]; then for app in $(pacmd list-sink-inputs | sed -n -e 's/index:[[:space:]]\([[:digit:]]\)/\1/p'); do pacmd "move-sink-input $app $next_sink" doneelif [[ "$sound_server" == "pipewire" ]]; then for app in $(pactl list sink-inputs | sed -n -e 's/.*Sink Input #\([[:digit:]]\)/\1/p'); do pactl "move-sink-input $app $next_sink" donefi
# Create a list of the sink descriptionssink_descriptions=()if [[ "$sound_server" == "pulseaudio" ]]; then mapfile -t sink_descriptions < <(pacmd list-sinks | sed -n -e 's/.*alsa.name[[:space:]]=[[:space:]]"\(.*\)"/\1/p')elif [[ "$sound_server" == "pipewire" ]]; then mapfile -t sink_descriptions < <(pactl list sinks | sed -n -e 's/.*Description:[[:space:]]\+\(.*\)/\1/p')fi
# Find the index that matches our new active sinkfor sink_index in "${!sink_descriptions[@]}"; do if [[ "$sink_index" == "$next_sink_index" ]]; then notify-send -i audio-volume-high "Sound output switched to ${sink_descriptions[$sink_index]}" exit fidone
Install
- Create the file
audio-device-switch.sh
, paste in the script, and place it in/usr/local/bin
. - Make the script executable:
sudo chmod 755 /usr/local/bin/audio-device-switch.sh
. - Open the Keyboard Shortcuts settings page, add a new shortcut, tell it to execute
audio-device-switch.sh
, and set up your shortcut! - Install the
notify-send
library if you want to see a popup notification when the audio device switches:sudo apt install libnotify-bin
.
Customizations
Feel free to modify this script and make it your own. Some ideas for customization:
Different icon in the notification
Line 83 of the script calls notify-send
with the -i
flag which defines which icon is displayed. Stock icons are found in:
/usr/share/icons/gnome/32x32
/usr/share/notify-osd/icons/
Acknowledgements
This is a more modern, robust rewrite of tsvetan's solution on the Ubuntu forums.