Last active
March 9, 2023 16:12
-
-
Save neogeographica/a46b693a1a80acca0170d5bdf69245ea to your computer and use it in GitHub Desktop.
switch audio output (PulseAudio)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# For PulseAudio. | |
# FOR THIS TO WORK, I needed to edit /etc/pulse/default.pa so that this line: | |
# load-module module-stream-restore | |
# is instead like this: | |
# load-module module-stream-restore restore_device=false | |
# | |
# I believe this will also undermine the ability to set some app to use a | |
# non-default sink, and have it stay that way across app restarts, but my | |
# amount of care about that usecase is zero. | |
# The grepping/sed-ing patterns for various bits of command output may need | |
# to be tweaked to work with other systems or audio devices. | |
# We're going to be dealing with an list of sink indexes stored in a bash | |
# array. To avoid confusion (maybe) the sink indexes will be called "IDs" in | |
# the code/comments below, and the array index where an ID is stored will be | |
# called its index. | |
# Create an array of sink IDs. | |
sink_ids=() | |
mapfile -t sink_ids < <(pacmd list-sinks | grep -oP '^([[:space:]]|\*)*index:[[:space:]]\K[[:digit:]]+') | |
# Get the ID of the default sink (marked with asterisk). | |
default_sink_id=$(pacmd list-sinks | grep -oP '^[[:space:]]*\*[[:space:]]*index:[[:space:]]\K[[:digit:]]+') | |
# Get the index of the default sink. | |
for index in "${!sink_ids[@]}"; do | |
if [[ "${sink_ids[$index]}" == "$default_sink_id" ]]; then | |
default_sink_index=$index | |
fi | |
done | |
# Get the last index in the array. | |
final_sink_index=$((${#sink_ids[@]} - 1)) | |
# Create a list of the sink descriptions. | |
sink_descriptions=() | |
mapfile -t sink_descriptions < <(pacmd list-sinks | sed -n -e 's/.*\(alsa.name\|bluez.alias\)[[:space:]]=[[:space:]]"\(.*\)"/\2/p') | |
# OK we want to cycle to a new sink ID 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="HDMI 0" | |
# Start our search for next-sink-to-use at the current default sink. | |
next_sink_index=$default_sink_index | |
# 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_id=${sink_ids[$next_sink_index]} | |
# Terminate the search if we've come back to our original starting point. | |
if [[ "$next_sink_index" == "$default_sink_index" ]] ; 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. | |
pacmd set-default-sink $next_sink_id | |
# Move all current inputs to the new default sink. | |
for input_id in $(pacmd list-sink-inputs | grep -oP '^[[:space:]]*index:[[:space:]]\K[[:digit:]]+'); | |
do | |
pacmd move-sink-input $input_id $next_sink_id | |
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