Keep audio settings when plugged into dock | Crash course on Udev rules - jacobmoroni/ubuntu_hacks GitHub Wiki
Setting up audio stuff for Ubuntu 20
I am trying to setup my computer to use default speaker and microphone when plugged into my dock because it doesnt have speakers and it drives me crazy to set it each time.
One simple solution is to edit the /etc/pulse/default.pa
file and comment out the lines that make it automatically switch
to whatever is plugged in. These are the lines to comment out (lines 36-38 for me)
.ifexists module-switch-on-connect.so
load-module module-switch-on-connect
.endif
The problem is this disables it always, but I only want it disabled for my dock
So here is another attempt:
To see current speaker options:
pactl list short sinks
Will return something like this:
0 alsa_output.pci-0000_01_00.1.hdmi-stereo-extra4 module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
1 alsa_output.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
2 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 48000Hz SUSPENDED
To set the speaker option (This is an example to set it to internal speaker) will likely differ per computer:
pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo
To see current microphone options:
pactl list short sources
Will return something like this:
0 alsa_output.pci-0000_01_00.1.hdmi-stereo-extra4.monitor module-alsa-card.c s16le 2ch 44100HzSUSPENDED
1 alsa_output.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
2 alsa_input.usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00.mono-fallback module-alsa-card.c s16le 1ch 44100Hz SUSPENDED
3 alsa_output.pci-0000_00_1f.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 48000HzSUSPENDED
4 alsa_input.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 48000Hz SUSPENDED
To set microphone option (This is an example to set it to internal microphone) will likely differ per computer:
pactl set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo
Then lets move to udev stuff.
First we need to figure out what we want to capture.
running udevadm monitor --udev --property
will capture udev events and print them to the screen and you can go through and see what happened so you can capture it.
I wanted to capture a sound card event so I searched through the print out after running it for sound and found this event
UDEV [2081.044657] add /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:04.0/0000:2d:00.0/0000:2e:02.0/0000:2f:00.0/usb7/7-2/7-2.1/7-2.1.1/7-2.1.1.4/7-2.1.1.4:1.0/sound/card2/pcmC2D0c (sound)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:04.0/0000:2d:00.0/0000:2e:02.0/0000:2f:00.0/usb7/7-2/7-2.1/7-2.1.1/7-2.1.1.4/7-2.1.1.4:1.0/sound/card2/pcmC2D0c
SUBSYSTEM=sound
DEVNAME=/dev/snd/pcmC2D0c
DEVTYPE=pcm
SEQNUM=7239
USEC_INITIALIZED=2081044536
MAJOR=116
MINOR=16
TAGS=:uaccess:
I did a bit more research and wanted to specifically capture the ThinkPad Dock so I searched for that and found this event:
UDEV [2081.102542] add /devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:04.0/0000:2d:00.0/0000:2e:02.0/0000:2f:00.0/usb7/7-2/7-2.1/7-2.1.1/7-2.1.1.4/7-2.1.1.4:1.0/sound/card2/controlC2 (sound)
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1c.0/0000:04:00.0/0000:05:04.0/0000:2d:00.0/0000:2e:02.0/0000:2f:00.0/usb7/7-2/7-2.1/7-2.1.1/7-2.1.1.4/7-2.1.1.4:1.0/sound/card2/controlC2
SUBSYSTEM=sound
DEVNAME=/dev/snd/controlC2
SEQNUM=7240
USEC_INITIALIZED=2081045442
ID_VENDOR=Lenovo
ID_VENDOR_ENC=Lenovo
ID_VENDOR_ID=17ef
ID_MODEL=ThinkPad_Thunderbolt_3_Dock_USB_Audio
ID_MODEL_ENC=ThinkPad\x20Thunderbolt\x203\x20Dock\x20USB\x20Audio
ID_MODEL_ID=3083
ID_REVISION=0083
ID_SERIAL=Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000
ID_SERIAL_SHORT=000000000000
ID_TYPE=audio
ID_BUS=usb
ID_USB_INTERFACES=:010100:010200:030000:
ID_USB_INTERFACE_NUM=00
ID_USB_DRIVER=snd-usb-audio
ID_PATH=pci-0000:2f:00.0-usb-0:2.1.1.4:1.0
ID_PATH_TAG=pci-0000_2f_00_0-usb-0_2_1_1_4_1_0
MAJOR=116
MINOR=17
DEVLINKS=/dev/snd/by-id/usb-Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio_000000000000-00 /dev/snd/by-path/pci-0000:2f:00.0-usb-0:2.1.1.4:1.0
TAGS=:uaccess:
These provide the necessary information to create a udev rule to capture that event
create a rules file /etc/rules.d/95-dock-connect.rules
-the number at the beginning of the file sets the order of the rules running from what I gather. I think the higher it is, the later rule to run.) I set it to 95 because the pulseaudio rule is a 90 and we need this to run after that
-the actual name (between the number and the .rules) doesnt matter so name it what you want
-probably the .rules does matter
in that file you can add some rules. Here are a couple examples:
SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", RUN+="/bin/device_added.sh"
SUBSYSTEM=="usb", ACTION=="remove", ENV{DEVTYPE}=="usb_device", RUN+="/bin/device_removed.sh"
SUBSYSTEM=="sound", ACTION=="add", ENV{DEVTYPE}=="pcm", RUN+="/bin/sound_added.sh"
KERNEL=="controlC2", SUBSYSTEM=="sound", ACTION=="add", RUN+="/bin/thinkpad_dock_added.sh"
So those are capturing the event that happens with SUBSYSTEM ACTION and DEVTYPE and then when that triggers it runs the script after the RUN+
The first 2 are examples of a USB plugging and unplugging, the 3rd one is capturing the addition of a "pcm" devtype in the sound subsystem. And the last one is capturing the DEVNAME of the sound card controlC2 which is where the thinkpad sound was showing up. (KERNEL== matches with DEVNAME) So that is the trigger I wanted.
after changing a rule you can reload all the rules with this command sudo udevadm control --reload
Then we just need to write the script to handle the event:
above we said that it will be in /bin/thinkpad_dock_added.sh
a quick and dirty way to test if you have it right is to put something like this in the file
#!/bin/bash
echo "thinkpad device added at $(date)" >>/home/jacobolson/testing.log
Then plug it in and see if that echo statement shows up in testing.log (or whatever you want to name it)
After verifying that it was correct, I just want to set up the sound how I want it when this is plugged in:
final version of the script file
/bin/thinkpad_dock_added.sh
#!/bin/bash
sleep 30
if [[ $(pactl list short sinks | grep "Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio") ]]; then
eval $(pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo)
eval $(pactl set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo)
fi
And the final version of the rules file:
/etc/udev/rules.d/95-dock-connected.rules
KERNEL=="controlC2", SUBSYSTEM=="sound", ACTION=="add", RUN+="/bin/thinkpad_dock_added.sh"
Once that file is written, dont forget to make it executable and reload the udev rules
bada-bing bada-boom!
Here are some of the sites I used to make this. Here for a deeper dive if needed
- Microphone fixed source
- thread debugging udev kernel issues
- basic udev rules
- device detection management
- laptop lid and dock scripts
- pulse audio examples
- call a service with udev
So... Update on this. It didnt work. Turns out 2 things were wrong.
-
pactl has to be run as a user not sudo, and the udev rules are run as sudo from what I gather. there is a work-around for that. you just have to change the command from
pactl <command>
to
sudo -u <username> pactl --server "unix:/run/user/$(id -u <username>)/pulse/native" <command>
and replace the
<username>
with your username and<command>
with the command you want to run for the sake of not feeling like this was a complete waste of time. here are the scripts I ended up on with that:
/etc/udev/rules.d/99-dock-connected.rules
#KERNEL=="controlC2", SUBSYSTEM=="sound", ACTION=="add", RUN+="/bin/thinkpad_dock_added.sh"
SUBSYSTEM=="sound", ACTION=="change", RUN+="/bin/thinkpad_dock_added.sh"
/bin/thinkpad_dock_added.sh
#!/bin/bash
echo "running this audio change script $(date)" >> /home/jacobolson/testing.log &
{
#sleep 10
if [[ $(sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" info | grep "Lenovo_ThinkPad_Thunderbolt_3_Dock_USB_Audio") ]]; then
#eval $(pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo)
#eval $(pactl set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo)
eval $(sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo)
eval $(sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo)
echo "Set default audio $(date)" >> /home/jacobolson/testing.log
else
result=$(sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" list short sinks)
echo $result >> /home/jacobolson/testing.log
result2=$(pactl list short sources)
echo $result2 >> /home/jacobolson/testing.log
echo "this did not find the dock $(date)" >> /home/jacobolson/testing.log
fi
}
#sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo
#sudo -u jacobolson pactl --server "unix:/run/user/1000/pulse/native" set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo
there is a similar example on this site. But it is a bit different. So maybe a slightly different implementation could have worked.
- The event I was trying to catch for the udev rule seems to be right before the audio settings changed, so I couldnt get it to actually work because it would wait until after the script was run to change my audio settings to the dock. I never figured this out but I found a few ways around it.
- theoretically I could have called a systemd.service from the udev rule and that could have set the setting, but that was another rabbit hole that I didnt feel like going down
- I created an alias that ran the following to let me at least switch to internal audio quickly when needed
pactl set-default-sink alsa_output.pci-0000_00_1f.3.analog-stereo pactl set-default-source alsa_input.pci-0000_00_1f.3.analog-stereo
- I created a script with the above lines and mapped that to a keyboard shortcut Shift + F1 to make it even faster
Then I stumbled across the easy solution that I should have just done from the beginning. I installed pavucontrol
(sudo apt install) and then in there you can just turn off an audio device and it wont switch to that here is the link to the answer, but this is what it says:
run pavucontrol
in a terminal or via your desktop.
- First select the Output Devices tab and make sure the drop down in the lower right corner is set to All output devices.
- Then select the Configuration tab.
- Find the device you want Pulse to ignore and select off in the profile drop-down.