Skip to content

Instantly share code, notes, and snippets.

@neogeographica
Created March 9, 2023 16:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neogeographica/bd6279a8c9562b04263a5812df3448b3 to your computer and use it in GitHub Desktop.
Save neogeographica/bd6279a8c9562b04263a5812df3448b3 to your computer and use it in GitHub Desktop.
switch audio output (PipeWire... and maybe PulseAudio too?)
#!/bin/bash
# For PipeWire. Based on my previous script for PulseAudio. This one might
# also work with PulseAudio since it only uses pactl... haven't tested that.
# Unlike on my older PulseAudio system, I didn't need to edit any config file
# to disable per-application persistence of sink assignments.
# The grepping/sed-ing patterns for various bits of command output may need
# to be tweaked to work with other systems or audio devices.
# Create an array of sink names.
sink_names=()
mapfile -t sink_names < <(pactl list short sinks | cut -f2)
# Get the name of the default sink.
default_sink_name=$(pactl info | grep -oP '^Default Sink:[[:space:]]*\K.+')
# Get the last index in the array.
final_sink_index=$((${#sink_names[@]} - 1))
# Create a list of the sink descriptions.
sink_descriptions=()
mapfile -t sink_descriptions < <(pactl list sinks | sed -n -e 's/.*device\.description[[:space:]]=[[:space:]]"\(.*\)"/\1/p')
# OK we want to cycle to a new sink that should become the default sink. We
# don't want to just "pick the next one" because some sinks are not hooked up
# to anything in reality.
# Ideally I'd support a list of sinks-to-skip here, but in practice right now
# there's only one that I need to skip.
blacklisted_sink_desc="HDA NVidia"
# Start our search for next-sink-to-use at the current default sink.
for index in "${!sink_names[@]}"; do
if [[ "${sink_names[$index]}" == "$default_sink_name" ]]; then
next_sink_index=$index
fi
done
# Begin the search.
looping=true
while [[ $looping == true ]] ; do
# Choose the next sink in the array. Wrap around to the beginning if needed.
if [[ $next_sink_index -ne $final_sink_index ]] ; then
next_sink_index=$((next_sink_index + 1))
else
next_sink_index=0
fi
next_sink_name=${sink_names[$next_sink_index]}
# Terminate the search if we've come back to our original starting point.
if [[ "$next_sink_name" == "$default_sink_name" ]] ; then
looping=false
fi
# Terminate the search if we found a non-blacklisted sink.
if [[ "${sink_descriptions[$next_sink_index]}" != "$blacklisted_sink_desc" ]] ; then
looping=false
fi
done
# Change the default sink.
pactl set-default-sink $next_sink_name
# Move all current inputs to the new default sink.
for input_id in $(pactl list short sink-inputs | cut -f1);
do
pactl move-sink-input $input_id $next_sink_name
done
# And notify!
notify-send -i audio-volume-high "Sound output switched to ${sink_descriptions[$next_sink_index]}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment