12

I'm trying to get my laptop to switch the audio to HDMI when it's plugged in, and back to standard analog when it's removed.

I set up a udev rule to trigger a script to perform the switching, however I'm having problems getting it to work:

  • It appears that until I select the HDMI Audio output in the Sound Settings, it is not recognised as a available sink, also once the HDMI Audio output is selected, the analog output is no longer listed.

See the below:

# HDMI plugged in, not selected in Sound settings.
$ pactl list short sinks 
5   alsa_output.pci-0000_00_1b.0.analog-stereo  module-alsa-card.c  s16le 2ch 44100Hz   SUSPENDED
kingamajick@kingamajick-laptop: ~

# HDMI selected in sound settings    
$ pactl list short sinks 
7   alsa_output.pci-0000_00_1b.0.hdmi-stereo    module-alsa-card.c  s16le 2ch 48000Hz   SUSPENDED
kingamajick@kingamajick-laptop: ~

# HDMI removed
$ pactl list short sinks 
9   alsa_output.pci-0000_00_1b.0.analog-stereo  module-alsa-card.c  s16le 2ch 48000Hz   SUSPENDED

The script I'm using the toggle the output is as follows:

#!/bin/bash
HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`
INPUTS=($(pacmd list-sink-inputs | grep index | awk '{print $2}'))

if [ $HDMI_STATUS = "connected" ]
then
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.hdmi-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.hdmi-stereo  &> /dev/null; done
else
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.analog-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.analog-stereo  &> /dev/null; done-server $PULSE_SERVER set-card-profile 0 output:analog-stereo+input:analog-stereo
fi

The script gives an error about no such sink when I trigger it before I select HDMI in the sound settings.

1

7 Answers 7

11

Based in this link and this link ...i tested and WORKS FINE ubuntu 14.04

you should create a rule in /etc/udev/rules.d/100-hdmi_sound.rules

whith this content

SUBSYSTEM=="drm", ACTION=="change", RUN+="/lib/udev/hdmi_sound_toggle.sh"

then create

sudo -H gedit /lib/udev/hdmi_sound_toggle.sh

whith your content

#!/bin/bash

HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`
INPUTS=($(pacmd list-sink-inputs | grep index | awk '{print $2}'))
if [ $HDMI_STATUS = "connected" ]
then
    pactl set-card-profile 0 output:hdmi-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.hdmi-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.hdmi-stereo  &> /dev/null; done
else
    pactl set-card-profile 0 output:analog-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.analog-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.analog-stereo  &> /dev/null; done
fi

I Hope can help ..sorry for my english i spoke spanish

9
+100
  1. Check for available cards & profiles

    pactl list cards
    
  2. Add following command to the script to change profile before setting default sink & moving streams

    pactl [options] set-card-profile  CARD PROFILE
    

(So no need to switch it manually from Sound Settings)

Reference:

pactl -h & man pactl

0
4

I had a bit of trouble following this. I copied the original script and modified it as suggested and now it works.

I am including it here to make it easier for someone else to use.

#!/bin/bash
## From: https://askubuntu.com/questions/458194/switching-to-hdmi-audio-when-hdmi-is-plugged-into-a-laptop-14-04
## Script needs to be triggered by a udev rule
## Works manually now

##source ${HOME}/bin/bash_trace
HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`
INPUTS=($(pacmd list-sink-inputs | grep index | awk '{print $2}'))

if [ $HDMI_STATUS = "connected" ]
then
    pactl set-card-profile 0 output:hdmi-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.hdmi-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.hdmi-stereo  &> /dev/null; done
else
    pactl set-card-profile 0 output:analog-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.analog-stereo
    for i in ${INPUTS[*]}; do pacmd move-sink-input $i alsa_output.pci-0000_00_1b.0.analog-stereo  &> /dev/null; done
fi

Now, I'd just like to trigger it with udev, but I'm clueless as to how to do that.

1
  • 1
    This doesn't work in 16.04. I'm working on it again.
    – Joe
    Dec 1, 2016 at 10:30
3

The provided scripts didn't work for me with udev out of the box on Ubuntu 14.04, I had to add:

export DISPLAY=:0

at the beginning, convert the two pacmd commands to pactl and finally run the script using sudo -s -u (user) from the udev rule.

#!/bin/bash
## From: https://askubuntu.com/questions/458194/switching-to-hdmi-audio-when-hdmi-is-plugged-into-a-laptop-14-04
## Script needs to be triggered by a udev rule
## Works manually now

export DISPLAY=:0

##source ${HOME}/bin/bash_trace
HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`
INPUTS=($(pactl list sink-inputs | grep Input | awk '{print $3}' | sed -r 's/^.{1}//')

if [ $HDMI_STATUS = "connected" ]
then
    pactl set-card-profile 0 output:hdmi-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.hdmi-stereo
    for i in ${INPUTS[*]}; do pactl move-sink-input $i alsa_output.pci-0000_00_1b.0.hdmi-stereo  &> /dev/null; done
else
    pactl set-card-profile 0 output:analog-stereo
    pactl set-default-sink alsa_output.pci-0000_00_1b.0.analog-stereo
    for i in ${INPUTS[*]}; do pactl move-sink-input $i alsa_output.pci-0000_00_1b.0.analog-stereo  &> /dev/null; done
fi
2

Well, all the provided scripts didn't work for me (Ubuntu 16.04), but I found this app indicator which allows you to change sound output in just one click:

https://github.com/lkettenb/sound-output-switcher

(note: you need to install the appindicator package first: sudo apt-get install python-appindicator )

Just thought I'd leave this here for others who might be in the same situation as me, even though it's an alternative solution... but it makes it pretty easy and it's discrete ;-)

0

Again another solution (tested working in 16.04) based on previous but with some improvements that works using proper pactl pointing to the right server (it can be improved to automatically get names of the cards and profiles, refer to pactl list or pactl info for now).

#!/bin/bash

USER_NAME=$(who | grep tty$(sudo fgconsole) | cut -f1 -d' ')
USER_ID=$(id -u "$USER_NAME")
HDMI_STATUS=$(</sys/class/drm/card0/*HDMI*/status)

HDMI_CARD="pci-0000_00_03.0"
HDMI_PROFILE="hdmi-stereo-extra1"

INTERNAL_CARD="pci-0000_00_1b.0"
INTERNAL_PROFILE="analog-stereo"

export LANG=C
export PULSE_SERVER="unix:/run/user/$USER_ID/pulse/native"

function pactl_cmd() {
  sudo -u $USER_NAME pactl --server $PULSE_SERVER $*
}

if [ "$HDMI_STATUS" = "connected" ]; then
  CARD=$HDMI_CARD
  OUTPUT=$HDMI_PROFILE
else
  CARD=$INTERNAL_CARD
  OUTPUT=$INTERNAL_PROFILE
fi

pactl_cmd set-default-sink alsa_output.$CARD.$OUTPUT
pactl_cmd set-card-profile alsa_card.$CARD output:$OUTPUT

INPUTS=($(pactl_cmd list sink-inputs | grep "^Sink Input #"|cut -d# -f2))
for i in ${INPUTS[*]}; do
  pactl_cmd move-sink-input $i alsa_output.$CARD.$OUTPUT
done

Related gist is here.

1
  • I had similar script that worked using card0 but one day it switched to card1 so I changed the script then the next day it changed back to card0. In the end I changed the code to card* and all is well since then. Just a heads up if something similar happens to you. Feb 8, 2017 at 0:06
0

Udev scripts run as root, but you also have to know which display manager you are using. This scripts should work, but you must authorize ths display manager to do it.

Find the DM you are using:

pgrep -a Xorg

See the -auth option, it will show you the display manager. It could be sddm ou lightdm in the lastest Ubuntu releases.

This line must be exported as XAUTHORITY variable, like this:

ls /car/run/sddm*

for instance, this could be an valid output:

/var/lib/mdm/:0.Xauth (This will be used by the script)

This is how your script must look like:

#!/bin/bash
export DISPLAY=:0
export XAUTHORITY=`ls /var/run/sddm*`
HDMI_STATUS="$(cat /sys/class/drm/card0-HDMI-A-1/status)"
USER=`whoami`
export XAUTHORITY=/home/$USER/.Xauthority
export DISPLAY=:0

if [ "$HDMI_STATUS" = connected ];
then
    sudo -u $USER pactl set-card-profile 0 output:hdmi-stereo+input:analog-stereo
else
    sudo -u $USER pactl set-card-profile 0 output:analog-stereo+input:analog-stereo
fi
exit 0

This will allow your display manager to change the HDMI output. Follow the insctuctions to create the udev rules files and to make the script executable.

1
  • I followed the advice, the script is called but pactl will not be executed (if typed in at shell it works). My guess: It takes some time to switch on HDMI and sound setting is not possible at that time. I added a sleep 5 before the sudo, but that increases also the time for screen to be switched to HDMI. How to change order ?
    – Arno
    Oct 23, 2020 at 7:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .