My monitor has an integrated KVM switch. It’s a nice feature, when I change the video input source from my desktop to my laptop it also moves my mouse and keyboard as well. Because HDMI can also carry audio, I have both the desktop and laptop use HDMI as the preferred audio output device. In this way when I switch from desktop to laptop my mouse, keyboard, video, and audio all make the switch.

Unfortunately the laptop needs to be told to start using the HDMI source as the preferred output device if I boot it up without being initially connected to the monitor. This makes sense, because the laptop doesn’t yet see the monitor, and isn’t aware that HDMI will be an option. A similar thing happens with the desktop when it goes to sleep. When the desktop wakes to find the monitor sleeping, it seems to be uncertain of the audio output capabilities available, and defaults to the onboard sound.

I wrote a quick script to solve both of these issues. It will look for audio output devices (“sinks”) and select the HDMI one if it isn’t already selected. If you have multiple HDMI devices connected you’ll want to modify this script a little bit to look for specific devices.

The command line utility pacmd does all the real work.

pacmd list-sinks shows available audio sinks here is an example of the output (edited for brevity):

2 sink(s) available.
  * index: 1
    name: <alsa_output.pci-0000_0b_00.1.hdmi-stereo-extra1>
    driver: <module-alsa-card.c>
    state: RUNNING
    monitor source: 1
    properties:
        alsa.resolution_bits = "16"
        device.api = "alsa"
        device.class = "sound"
        alsa.name = "HDMI 1"
    ports:
        hdmi-output-1: HDMI / DisplayPort 2
            properties:
                device.icon_name = "video-display"
                device.product.name = "DELL U3417W
    active port: <hdmi-output-1>
    index: 2
    name: <alsa_output.pci-0000_0d_00.4.iec958-stereo>
    driver: <module-alsa-card.c>
    state: SUSPENDED
    monitor source: 2
    properties:
        device.product.name = "HD Audio Controller"
        device.icon_name = "audio-card-pci"
    ports:
        iec958-stereo-output: Digital Output (S/PDIF) (priority 0, latency offset 0 usec, available:                active port: <iec958-stereo-output>

Here you can see two sinks, index 1: is the HDMI, and index 2: is the onboard audio. The asterisk in front of “index 1:” indicates the HDMI device in the output above is currently active.

The script reads through this output line by line. When it sees an index it keeps track of the number and determines if the asterisk character is there indicating that the index is the active device. It continues reading line by line to figure out which device it has just read the index for. When it gets to the HDMI device it looks to see if the current index number (the HDMI) is the active device index number. If it isn’t it sets it using:

pacmd set-default-sink $index

Here is the script:

#!/bin/bash
pacmd list-sinks |while read line
do
    if [ -z "$devices" ]; then
        devices="${line//' sink(s) available.'}"
        devices="${devices//[^0-9]}"
        if [ -z "$devices" ]; then
            echo "Couldn't read devices count: $line" >&2
            exit 1
        fi
        if [ "$devices" -lt "2" ]; then
            echo "Only one device detected" >&2
            exit 1
        fi
    fi

        # Figure out which device index we are on and if it is
        # the currently active device
    if [ "${line//'index:'}" != "$line" ]; then
        index="${line//[^0-9]}"
        if [ -n "$index" ] && [ "${line//'*'}" != "$line" ]; then
            active_index="$index"
        fi
    fi

        # Use the HDMI output device if it isn't active already
    if [ "${line//'<hdmi-output-'}" != "$line" ]; then
        if [ "$active_index" != "$index" ]; then
            # Looks like HDMI output isn't active
            echo "HDMI (index: $index) not active, active index: $active_index"
            echo "Setting HDMI output active"
            pacmd set-default-sink "$index"

        fi
    fi

done

I use a modified version of this script (one that produces no output or non-zero exit codes) to automatically make this change when my computer resumes from sleep.

In the directory /usr/lib/pm-utils/sleep.d are a set of scripts to be ran when suspend/resume or hibernate/thaw actions occur.

$ cd /usr/lib/pm-utils/sleep.d
/usr/lib/pm-utils/sleep.d$ ls
000record-status  40inputattach  60_wpa_supplicant  95hdparm-apm

If you want your script to only run in when entering “suspend” and “hibernate” look for those values in the first argument passed to the script. Likewise look for “resume” or “thaw” when leaving those states. The 60_wpa_supplicant script is a good example of handling either. Since you’ll likely want to do the same things for sleep or hibernate, or resume and thaw, you can group those together like so:

case "$1" in
    suspend|hibernate)
        $WPACLI suspend
        ;;
    resume|thaw)
        $WPACLI resume
        ;;
esac

Scripts in the directory are ran in the order they appear numerically and alphabetically. The script 000record-status should be ran first, that’s why it starts with so many 0s. If you want to make sure your script runs last maybe call it 99something. I’ve named mine 99hdmi-sink:

/usr/lib/pm-utils/sleep.d$ cat 99hdmi-sink
#!/bin/bash
if [ "$1" != "thaw" ] && [ "$1" != "resume" ]; then
    exit
fi
pacmd list-sinks |while read line
do
    if [ -z "$devices" ]; then
        devices="${line//' sink(s) available.'}"
        devices="${devices//[^0-9]}"
        if [ -z "$devices" ]; then
            exit
        fi
        if [ "$devices" -lt "2" ]; then
            exit
        fi
    fi
    if [ "${line//'index:'}" != "$line" ]; then
        index="${line//[^0-9]}"
        if [ -n "$index" ] && [ "${line//'*'}" != "$line" ]; then
            active_index="$index"
        fi
    fi
    if [ "${line//'<hdmi-output-'}" != "$line" ]; then
        if [ "$active_index" != "$index" ]; then
            pacmd set-default-sink "$index"
        fi
    fi
done

Since my script won’t make any action if HDMI is already the preferred output device, I don’t really have to bother looking to see what state I’m changing into or out of, I can just run the script and let it sort out the situation, but I decided to exit early if it doesn’t seem to be a waking event.

Categories:General Nonsense
Published on :Posted on

Post your comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.