Switch between internal and HDMI speakers automatically

10,884

Background

I had a similar problem during suspend/resume when sound would switch from HDMI TV to laptop speakers. Upon resume sound would stay on Laptop speakers and I would have to manually reset output device to TV in System Settings -> Sound.

This was one of my first annoying experiences with upgrade from Ubuntu 14.04 to 16.04 and the root was upgrade to PulseAudio 8 that comes with 16.04 LTS

After much searching I created a script called TV-sound. Although I don't plug and unplug the TV like yourself, I did some google searches and create a variation of the script to work in your situation. I've tested it and it works.

Step 1: Create script to switch audio between connected devices

We'll create a script called hotplugtv which udev calls. This same script can be called in many places though. For example, during testing I used it in lock-screen-timer where sound reverted back to Laptop during screen lock.

cd /usr/local/bin
sudo touch hotplugtv
sudo chmod +x hotplugtv
gksu gedit hotplugtv

When the editor opens with a blank screen, copy and paste the following into it:

#! /bin/bash

# NAME: hotplugtv
# PATH: /usr/local/bin
# DESC: Update pulseaudio output device when HDMI TV plugged / unplugged
# CALL: called from /etc/udev/rules.d/99-monitor-hotplug.rules
# DATE: Created Nov 26, 2016.
# NOTE: logs output using log-file
# UPDT: Dec 14, 2016 - Sometimes /sys/class/drm/card0 & sometimes /sys/class/drm/card1
#       so use /sys/class/dmcard* instead.

if [[ $(cat /sys/class/drm/card*-HDMI-A-1/status | grep -Ec "^connected") -eq 1 ]]; then
#        log-file "HDMI TV connected" ~/bin/log-hotplugtv;
        /bin/sleep 2;
        export PULSE_RUNTIME_PATH="/run/user/1000/pulse/";
        sudo -u rick -E pacmd set-card-profile 0 output:hdmi-stereo;
else
#        log-file "HDMI TV disconnected" ~/bin/log-hotplugtv;
        export PULSE_RUNTIME_PATH="/run/user/1000/pulse/";
        sudo -u rick -E pacmd set-card-profile 0 output:analog-stereo;
fi

exit 0

You will need to replace the two occurrences of rick with your own user id, ie UTF-8, etc.

I know this can be more professional with user name automatically set to a bash variable but I'm not that skilled yet :( Anyway, save the file and exit gedit.

Step 2: Create udev rules

udev monitors hotplug events when you plug in and unplug your HDMI monitor. Type the following to create a new rule.

cd /etc/udev/rules.d
sudo cp 70-persistent-net.rules 99-hotplugtv.rules
gksu gedit 99-hotplugtv.rules

NOTE: If the file 70-persistent-net.rules doesn't exist in your directory copy any other file there. We don't need the file contents, just the file permissions to ensure ours are the same.

The editor will show a bunch of irrelevant text, highlight it and delete it. Then highlight the code below and paste it into the editor:

# NAME: 99-hotplugtv.rules
# PATH: /etc/udev/rules.d
# DESC: Update pulseaudio output device when HDMI TV plugged / unplugged
# CALL: automatically called on system events
# DATE: Created Nov 26, 2016.
# NOTE: in future may requre systemd service hooks

ACTION=="change", SUBSYSTEM=="drm", ENV{HOTPLUG}=="1", RUN+="/usr/local/bin/hotplugtv"

Save the file and exit.

To enable the rule (without rebooting) we need to reload udev:

sudo udevadm control --reload-rules

Now you can plug and unplug your HDMI monitor / TV and the sound switches appropriately.

Caveat

On my system the sound automatically reverts to the Laptop speakers when HDMI is unplugged. On your system it did not. Further enhancements to the code may be required if sound doesn't go to your Laptop speakers when HDMI is unplugged. Please reply via comment below how things work / don't work out.

Quick testing in CLI

You can quickly test the code at the terminal by using:

    export PULSE_RUNTIME_PATH="/run/user/1000/pulse/";
  • Switch to HDMI / TV:

        sudo -u rick -E pacmd set-card-profile 0 output:hdmi-stereo;
    
  • Switch back to built in speakers:

        sudo -u rick -E pacmd set-card-profile 0 output:analog-stereo;
    
  • Remember to replace rick with your user name.

  • The initial export line probably isn't necessary but I've included it just to be safe.
  • If you have multiple sound cards, or something other than hdmi-stereo tweaks are obviously needed to find out the correct parameters before writing your script.

Deciphering your device name within PulseAudio

The code below uses the same command twice. Once when the sound is set to external HDMI TV. A second time when the sound is set to the Laptop's built in speakers. Each time you see the name PulseAudio uses:

$ pacmd list-sinks | grep -e 'name:' -e 'index'
  * index: 28
    name: <alsa_output.pci-0000_00_1b.0.hdmi-stereo>
───────────────────────────────────────────────────────────────────────────────
$ pacmd list-sinks | grep -e 'name:' -e 'index'
  * index: 30
    name: <alsa_output.pci-0000_00_1b.0.analog-stereo>

When you have multiple sound cards

Use the command aplay -l to see if you have cards greater than number 0. If so you will need to use appropriate card number in your scripts. For example:

$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: 92HD91BXX Analog [92HD91BXX Analog]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 3: HDMI 0 [HDMI 0]
  Subdevices: 0/1
  Subdevice #0: subdevice #0

In the above example all card numbers are 0 with different output sources. If you have USB speakers they can have a different card number than 0.

Edit December 2, 2016

For some unknown reason the script was broken today. Above code used to read: "$(cat /sys/class/drm/card0-HDMI-A-1/status" but I had to change card0 to card1 and the code above has been revised as such. I can't explain what changed on my system other than regular Ubuntu updates since November 26, 2016.

Edit December 14, 2016

Above code needed to be switched again back to: "$(cat /sys/class/drm/card0-HDMI-A-1/status". Instead of revising code between card0 and card1 depending on boot, revise program to reference card* to capture both scenarios.

Share:
10,884

Related videos on Youtube

UTF-8
Author by

UTF-8

Updated on September 18, 2022

Comments

  • UTF-8
    UTF-8 over 1 year

    You know how when you plug headphones into your laptop's aux jack, the internal speakers are automatically muted and the sounds comes out of your headphones? I want exactly that for HDMI.

    I connect a monitor with built-in speakers to my laptop to watch videos, sometimes. It's very weird when the video is on the monitor (which is placed 3 m away from my desk) but the sound comes out of my laptop's internal speakers (laptop sits on the desk).

    Of course, I can just go to the sound settings and switch the output channel to HDMI. But then, later on, when I removed the HDMI cable hours ago and might even be in a totally different location, the sound won't play because the sound server still tries to play it via HDMI but there isn't anything connected to it.

    Automatically switching back to the internal speakers when no device is connected via HDMI is more important but switching to sound output via HDMI when a device is connected would still be very nice.

    This apparently worked with 11.10 but it doesn't work with 16.04.

  • UTF-8
    UTF-8 over 7 years
    I can't copy 70-persistent-net.rules because it doesn't exist. /etc/udev/rules.d only contains a single file called 39-smfp_samsung.rules.
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    Oh well copy 30-smfp_samsung.rules, we dont need the file contents only to make sure we have the right permissions for our new file. Copying an existing file is the easiest way (for me anyways).
  • UTF-8
    UTF-8 over 7 years
    It doesn't work on my system. @ Person who already upvoted this answer: Does it work on your? | I first tried it without rebooting but it didn't work. It doesn't worker after rebooting, either. I had HDMI disconnected and set the sound to the internal speakers before rebooting. After rebooting, I played some music and connected my HDMI cable. The music still (only) came out of the built-in speakers. Do you have to change anything other than the 2 occurrences of the username? cat /sys/class/drm/card0-HDMI-A-1/status shows the correct status (I tried several times).
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    @UTF-8 I've added a new section where you can test switching at the command line. Can you run this and see if errors come out please?
  • UTF-8
    UTF-8 over 7 years
    There seems to be no output:analog-stereo: pastebin.com/n5bHyhCm This makes it even weirder because then I'd expect the sound to always be played via HDMI. Screenshots of my sound settings when HDMI is connected and when HDMI isn't connected. Edit: I already tried this but it took me a few more minutes to post this comment because I had to upload the screenshots.
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    @UTF-8 I've added a new section to decipher what names PulseAudio is using for your output sources.
  • UTF-8
    UTF-8 over 7 years
    They seem to have the same names as on your system. Or at least, that's what I'm reading out of it: pastebin.com/eqmRyW03
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    Looks like you have two different cards. One on 03 and one on 1b. Mine has one card named 1b. It's weird.... You can play with changing card 0 to card 1 in the terminal to see if that switches properly (using the commands in second last section of answer) Also command lspci to see all PCI devices might shed clues.
  • UTF-8
    UTF-8 over 7 years
    output:hdmi-stereo only exists for profile 0 and output:analog-stereo only exists for profile 1. (pacmd set-card-profile 0 output:hdmi-stereo, pacmd set-card-profile 1 output:analog-stereo run without error but changing the number in either command to 1 or 0 respectively results in a "No such profile" error.). The selected sink (pacmd list-sinks) changes when I execute these commands but it doesn't change where the sound is played.
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    I updated the answer a few minutes ago with a new section using aplay -l to list the different cards. Try that for correcting syntax.
  • UTF-8
    UTF-8 over 7 years
    Output. I only have cards 0 and 1 according to this output. I don't have USB speakers. The only USB device connected to my laptop right now is a mouse. The only places where the card number is used are after set-card-profile, aren't they?
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    USB speakers was added for everyone reading the answer... To me it sounds like you need to say card 1 with analog and card 0 with HDMI. Can you try this in command line using sudo... command as shown in answer section on testing in CLI?
  • UTF-8
    UTF-8 over 7 years
    I think that's what I already tried. I did it again, though: Output.
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    In your output you are doing the reverse of what you want... ie sound is already coming from laptop and you are using sudo to force it to the laptop. Then sound is coming from HDMI and you are using sudo to force it to HDMI... The point of the test in CLI is to do the opposite to switch from one device to the other.
  • UTF-8
    UTF-8 over 7 years
    Oops, I messed that up. But it doesn't work either if I do it correctly.
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    Can you show the output (with HDMI TV plugged in and card 0 for HDMI and Card 1 for builtin speakers) using sudo to switch back and forth between the two... the error messages could be helpful.
  • UTF-8
    UTF-8 over 7 years
    Do you mean like that?
  • WinEunuuchs2Unix
    WinEunuuchs2Unix over 7 years
    No... use card 0 in place of card 1 and card 1 in place of card 0. Leave the rest of the commands the same and try them with everything plugged in. They should switch between HDMI and built in speakers. If not there should be an error message. ie use the EXACT same commands as last time, but now use them at the right time for testing.
  • UTF-8
    UTF-8 over 7 years