Created
May 26, 2023 20:04
-
-
Save NivenT/738473a117407f435d88ea389b622e20 to your computer and use it in GitHub Desktop.
bash script for setting up HDMI audio on Ubuntu (based on https://wiki.archlinux.org/title/PulseAudio/Examples#HDMI_output_configuration and https://askubuntu.com/a/1415109/1606121)
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 | |
####### 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