Skip to content

Instantly share code, notes, and snippets.

@NivenT
Created May 26, 2023 20:04
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 NivenT/738473a117407f435d88ea389b622e20 to your computer and use it in GitHub Desktop.
Save NivenT/738473a117407f435d88ea389b622e20 to your computer and use it in GitHub Desktop.
#!/bin/bash
####### Constants ############
USER=$(who | grep -o "^[^ ]\+" | head -n 1)
USER_ID=$(id -u $USER)
HDMI_FLDR="/sys/class/drm/"
PULSE_SERVER="unix:/run/user/$USER_ID/pulse/native"
# Use different lock files depending on if this is run by udev or manually from the terminal
# this avoids permission issues, I think
if [ $USER == $(whoami) ]; then
LOCK_FILE="/tmp/hdmi_sound.lock"
else
LOCK_FILE="/var/lock/hdmi_sound.lock"
fi
ENABLE_DESKTOP_NOTIFICATIONS=0 # Change this to a 1 to have script produce notifcations when run
NOTIFY_ICON="/usr/share/icons/gnome/256x256/status/audio-volume-high.png"
## These are needed to get the notification working
export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=:0
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$USER_ID/bus"
####### Functions ############
function alert() {
echo $1
if [ $ENABLE_DESKTOP_NOTIFICATIONS == 0 ]; then
return
fi
if [ $USER == $(whoami) ]; then
notify-send --icon=$NOTIFY_ICON "$1"
else
su $USER -c "notify-send --icon=$NOTIFY_ICON \"$1\""
fi
}
function has_detected_hdmi_audio() {
sudo -u $USER pactl --server $PULSE_SERVER list sinks | grep -iq -e "HDMI" -e "High Definition Audio Controller"
}
function detect_hdmi_audio() {
readarray -t DEVICES <<< $(aplay -l | grep "card.*device.*HDMI")
for DEVICE in "${DEVICES[@]}"; do
CARD_STR=($(grep -o "card [0-9]\+" <<< $DEVICE))
CARD=${CARD_STR[1]}
DEVICE_STR=($(grep -o "device [0-9]\+" <<< $DEVICE))
DEVICE_NUM=${DEVICE_STR[1]}
echo Testing card $CARD, device $DEVICE_NUM...
if ! $(speaker-test --channels 2 --test wav --nloops 1 --device hw:$CARD,$DEVICE_NUM | grep -qi -e left -e right); then
continue
fi
echo Adding HDMI to PulseAudio...
sudo -u $USER pactl --server $PULSE_SERVER load-module module-alsa-sink device=hw:$CARD,$DEVICE_NUM sink_name="HDMI"
break
done
}
function get_hdmi_sink_idx() {
echo Searching for index of HDMI sink...
IDX=0
readarray -t NAMES <<< $(sudo -u $USER pactl --server $PULSE_SERVER list sinks | grep "alsa.name")
for NAME in "${NAMES[@]}"; do
if $(grep -qi "HDMI" <<< $NAME); then
break
fi
IDX=$(( $IDX + 1 ))
done
if [ $IDX -ge ${#NAMES[@]} ]; then
echo Failed to find index. Exiting
exit
fi
readarray -t SINKS <<< $(sudo -u $USER pactl --server $PULSE_SERVER list sinks | grep "Sink #")
eval "$1=$(grep -o '[0-9]\+$' <<< ${SINKS[$IDX]})"
}
function setup_hdmi_audio() {
if ! has_detected_hdmi_audio; then
alert "Detecting HDMI"
echo PulseAudio has not detected any HDMI yet, so first finding device...
detect_hdmi_audio
fi
get_hdmi_sink_idx IDX
echo Switching audio to sink with index $IDX...
sudo -u $USER pactl --server $PULSE_SERVER set-default-sink $IDX
}
####### Script ############
OLD_STATUS=$(cat $LOCK_FILE)
# open $LOCK_FILE (for writing/reading) and assign it file descriptor 8 (the 8 is arbitrary)
exec 8<>$LOCK_FILE
#alert "$(ls /proc/self/fd)" # check to see if fd 8 was created
# attempt to obtain an exclusive (-x) lock to file descriptor 8, and return immediately (-n) if you fail
flock -x -n 8 || exit 1
# have the lock, valid for the lifetime of the file descriptor (i.e. until the end of the script)
# This assumes there is a single HDMI port that ever gets used
for DEVICE in $(ls "$HDMI_FLDR" | grep "card*"); do
if ! $(grep -q "HDMI" <<< "$DEVICE"); then
continue
fi
STATUS_FILE="$HDMI_FLDR/$DEVICE/status"
if ! [ -f "$STATUS_FILE" ]; then
continue
fi
STATUS=$(cat "$HDMI_FLDR/$DEVICE/status")
# flock alone didn't seem to stop multiple instances from running at once with udev
# so also use $LOCK_FILE to keep track of the old status
if [ $STATUS == $OLD_STATUS ]; then
break
else
alert "Running custom hdmi script"
fi
echo $STATUS > $LOCK_FILE
if [ $STATUS != "connected" ]; then
sudo -u $USER pactl --server $PULSE_SERVER set-default-sink 1
else
alert "setting up HDMI"
setup_hdmi_audio
fi
break
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment