Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

I have a dual screen setup on my laptop (using 12.04 LTS) using a HDMI connected display. Everything works fine, but everytime I connect/disconnect the cable I have to go to Sound preferences and change the sound output device manually.

Is there any way to change the sound output device on connect/disconnection of cable, so when I connect my display the sound output is set to HDMI and when I disconnect it the sound goes back to laptop speakers?

share|improve this question
    
Same problem here. –  ThiagoPonte Mar 4 '13 at 10:47
    
Check if my answer works with you. –  Salem Mar 8 '13 at 16:19

4 Answers 4

For the benefit of people who stumble upon this question - Salem's solution almost worked for me in 13.04, I ended up gathering bits and pieces from all around the web, I think the deal breaker for me was the lack of the environment variable PULSE_SERVER

Here is my full solution, which is basically repeating Salem's solution with the few missing pieces. I also redid it as a shell script (despite my love for Python) because I was afraid at first that my Python script is running into import path issues:


(same as Salem's answer) Create a file /etc/udev/rules.d/hdmi_sound.rules as root with the content:

SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/hdmi_sound_toggle"

Create a file /usr/local/bin/hdmi_sound_toggle as root with the content:

#!/bin/sh
USER_NAME=`who | grep "(:0)" | cut -f 1 -d ' '`
USER_ID=`id -u $USER_NAME`
HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`

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

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

Then make it executable with chmod 0755 /usr/local/bin/hdmi_sound_toggle

I tried to make this script as generic as possible, but you still might need to change some lines, such as the HDMI_STATUS file path or the profiles used. You can see a list of profiles by running pactl list cards and looking under Profiles.

Note that the script failed for me when I removed the keyword "export" when setting PULSE_SERVER, I think pactl is looking for the env variable

Don't forget to reload your udev rules: sudo udevadm control --reload-rules

Update this script is updated for 14.04. Before that, you would use USER_NAME instead of USER_ID everywhere

share|improve this answer
1  
Note that I needed to append | uniq to the USER_NAME command. Also note that the sounds settings in Ubuntu can be deceiving. I wasn't seeing the changes in the sound settings panel, but once I was playing something the audio would switch back and forth smoothly I also noticed that after a few times of plugging and unplugging the HDMI, the script stopped being run (maybe some sort of bug in udev). –  FuegoFro Jul 7 at 1:24
1  
I added | sort -u to the username because it was saying extra option elemer elemer --- My username. Same as FuegoFro's solution at the end of the day. –  elemer82 Oct 7 at 22:46
up vote 3 down vote accepted

I finally managed to make this work using udev. So if someone wants the same behavior here are the steps:

First we need to create a file /etc/udev/rules.d/hdmi_sound.rules with the following content:

    SUBSYSTEM=="drm", ACTION=="change", RUN+="/usr/local/bin/hdmi_sound_toggle"

this will make udev execute the script hdmi_sound_toggle every time there is a change in HDMI connection. That script must have execution permission and the contents are as follows:

#!/usr/bin/env python

import subprocess
from syslog import syslog

def output(cmd):
    return subprocess.check_output(cmd, shell=True)

# the following variables may need some modification.
user = "my_username"
card = "/sys/class/drm/card0"
dev_speaker = "output:analog-stereo+input:analog-stereo"
dev_hdmi = "output:hdmi-stereo+input:analog-stereo"
#

interfaces = output("ls {0}".format(card), ).split("\n")

vga = filter(lambda x: "VGA" in x, interfaces)[0]
hdmi = filter(lambda x: "HDMI" in x, interfaces)[0]

syslog("HDMI connection was changed!")

hdmi_connected = output("cat {0}/{1}/status".format(card,hdmi)).startswith("connected")
title = "HDMI was {0}".format("connected" if hdmi_connected else "disconnected")
message = "Audio output has changed to {opt}.".format(opt = "HDMI" if hdmi_connected else "built-in speakers")

cmd = "sudo -u " + user + " /usr/bin/pactl set-card-profile 0 " + (dev_hdmi if hdmi_connected else dev_speaker)

syslog("HDMI was connected." if hdmi_connected else "HDMI was disconnected.")
try:
    a = output(cmd)
    output("sudo -u {0} notify-send \"{1}\" \"{2}\"".format(user, title, message))
    syslog("Audio output changed.")
except Exception as ex:
    syslog("Error changing output device: " + str(ex))

Probably this can be easily made in bash, but as my main language is python I used it. Everything works except the notification: it doesn't show up, I really don't know why. If someone knows how to fix it please say something.

Note: the names of script/udev rule can be changed, but you need to use the full path.

share|improve this answer

This is the first time I've come here for any help on this... I'm in the same boat on 12.10. It's annoying. It's been this way since 10.10 in my experience. There has to be a config somewhere that can handle this.

EDIT: http://voices.canonical.com/david.henningsson/2012/04/14/audio-over-hdmi-and-displayport-in-ubuntu-12-04/

It's set that way on purpose. That's horrible.

share|improve this answer

Based on Salem's answer and daniel's answer

I took Salem's answer and daniel's answer and made some necessary changes, their solution didn't worked for me out of the box:

(similar as Salem's answer).

Create a file /etc/udev/rules.d/hdmi_sound.rules as root with the content:

SUBSYSTEM=="drm", RUN+="/usr/local/bin/hdmi_sound_toggle"

Note ACTION=="change", is missing !

Create a file /usr/local/bin/hdmi_sound_toggle as root with the content:

#!/bin/sh
USER_NAME=`who | grep "(:0)" | cut -f 1 -d ' '| sort -u`
USER_ID=`id -u $USER_NAME`
HDMI_STATUS=`cat /sys/class/drm/card0/*HDMI*/status`

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

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

Note USER_NAME=who | grep "(:0)" | cut -f 1 -d ' '| sort -u I added | sort -u because otherwise it came back with elemer elemer elemer --my username 3 times.

Then make it executable with chmod 0755 /usr/local/bin/hdmi_sound_toggle

Don't forget to reload your udev rules: sudo udevadm control --reload-rules

Important this script is updated for 14.04. Before that, you would use USER_NAME instead of USER_ID everywhere

Credits: Salem and daniel.

share|improve this answer

protected by Community Aug 23 at 16:46

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.