Ubuntu has started to support USB audio. When I plug in a USB speaker or headset (such as my Pyle USB gaming headset), it gets recognized and installed automatically. However there are some bugs:
- The USB sound device does not get selected automatically. This is especially annoying for headphones. When you plug in analog headphones, the sound automatically switches to them and the main speakers turn off.
- The volume settings are turned way up for the device. Ideally it would remember the volume settings from the last time the device was plugged in.
- When the computer goes to sleep, the volume of usb devices gets turned way up on wake.
I discovered that I could address these shortcomings by creating a script.
- PulseAudio provides command line control through
pacmd
. I can use that to Query for USB audio devices (both speakers and microphones) that got installed automatically - Select default devices (both speakers and microphones)
- Set volumes for devices (both speakers and microphones)
- The
udev
system provides a mechanism for running a script when a USB sound device is plugged in< - The pm-utils system provides a mechanism for running a script on wake.
- The
I have included the text of the script below. To start using it:
- Copy and paste the script to a file in a permanent location (for example into
/opt/usb-audio-select.sh
)sudo editor /opt-usb-audio-select.sh
- Copy, paste, save, then exit
- Make the script executable
sudo chmod a+rx /opt/usb-audio-select.sh
- As a super user, run the script with the
--install
flag. This will install theplay
command if you don't have it, setup theudev
rule, restart theudev
system, and install the pm-utils script.sudo /opt/usb-audio-select.sh --install
- If you want to test the script without fully installing (or run it manually for any reason) you may do so.
/opt/usb-audio-select.sh
- The script sets volume levels that are appropriate for my headset. You might need to edit the script to adjust the volume levels to your preferences.
#!/bin/bash
#
# Automatically select USB sound devices
# Copyright (C) 2013-2020 Stephen Ostermiller
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# To install:
# Put this script somewhere permanent like /opt/usb-audio-use.sh
# then run:
# sudo /opt/usb-audio-use.sh --install
# To uninstall run:
# sudo rm -f /lib/udev/rules.d/99-usb-audio-auto-select.rules /usr/lib/pm-utils/sleep.d/99usbaudio
# The name of any device you want to give priority over other devices
# A case-insensitive substring from your cards will work. For your
# options run:
# pacmd list-cards | grep 'name:'
priority="Media_Electronics"
# Volume at which to set the speakers
# (1 to 100000, 20000 is 20%)
# comment this line out not to touch the volume
speakervolume=20000
# Volume at which to set the microphone
# (1 to 100000, 80000 is 80%)
# comment this line out not to touch the volume
micvolume=80000
#####################################################################
install=0
sleep=0
verbose=0
for i in "$@"
do
case $i in
--install)
install=1
;;
--sleep)
sleep=1
;;
--verbose)
verbose=1
;;
*)
echo "Unknown option $i"
exit 1;
;;
esac
done
if [ $install == 1 ]
then
if [ $EUID -ne 0 ]
then
echo "Error you are not the root user."
echo "Run this install as root (or use sudo)"
exit 1
fi
if [ ! `which play` ]
then
apt-get -y install sox
fi
script=`readlink -f $0`
rulefile=/lib/udev/rules.d/99-usb-audio-auto-select.rules
if [ -e $rulefile ]
then
echo "udev rule already exists: $rulefile"
else
echo "Creating udev rule: $rulefile"
echo "ACTION==\"add|remove\", ENV{ID_TYPE}==\"audio\", RUN+=\"$script\"" > $rulefile
service udev restart
echo "Installed udev rule"
fi
rulefile=/usr/lib/pm-utils/sleep.d/99usbaudio
if [ -e $rulefile ]
then
echo "pm-utils sleep/wake rule already exists: $rulefile"
else
echo "Creating pm-utils sleep/wake rule: $rulefile"
echo -e "#!/bin/sh \n case \"\$1\" in \n 'resume' | 'thaw') \n $script \n ;; \n esac" > $rulefile
chmod a+x $rulefile
echo "Installed pm-utils sleep/wake rule"
fi
exit 0
fi
if [ $sleep == 1 ]
then
if [ $verbose == 1 ]
then
echo "Sleeping 1 second"
fi
sleep 1
fi
if [ "$UID" == "0" ]
then
if [ $verbose == 1 ]
then
echo "Checking process table for users running PulseAudio"
fi
for user in `ps axc -o user,command | grep pulseaudio | cut -f1 -d' ' | sort | uniq`
do
verbosearg=""
if [ $verbose == 1 ]
then
echo "Forking to run as PulseAudio user: $user"
verbosearg="--verbose"
fi
# tell it to sleep for a second to let pulseaudio install the usb device
su $user -c "bash $0 --sleep $verbosearg" &
done
else
export PULSE_RUNTIME_PATH="/run/user/$UID/pulse/"
if pacmd list-cards 2>&1 | grep 'No PulseAudio daemon running'
then
echo "For user: `whoami`"
exit 1
fi
# List the sound cards, put USB sound cards first
cards=`pacmd list-cards | grep 'name:' | sed -r "s/.*<//g;s/>.*//g;s/.*usb.*/1-\0/gi;s/.*($priority).*/0-\0/gi" | sort | sed -r 's/^([01]-)*//g'`
# The first card
card=`echo "$cards" | head -n 1`
# Find a profile for it, preferrably something with output and input, but fall back to just output
profile=`pacmd list-cards | sed -n "/$card/,/^\s*Index:/p" | sed -n '/^\s*profiles:/,/^\s*off/p' | tail -n+2 | grep -v 'available: no' | sed -r 's/^\s+//g;s/: .*//g;s/.*output.*input.*/0-\0/g;s/.*output.*/0-\0/g' | sort | sed -r 's/^(0-)+//g' | head -n 1`
if [ $verbose == 1 ]
then
echo "Enabling: $card $profile"
fi
pacmd set-card-profile "$card" "$profile" | grep -vE 'Welcome|>>> $'
# For each of the other cards
echo "$cards" | tail -n+2 | while read card
do
if [ $verbose == 1 ]
then
echo "Disabling: $card"
fi
pacmd set-card-profile "$card" off | grep -vE 'Welcome|>>> $'
done
# List the speakers, put USB speakers first
speakers=`pacmd list-sinks | grep 'name:' | sed -r "s/.*<//g;s/>.*//g;s/.*usb.*/1-\0/gi;s/.*($priority).*/0-\0/gi" | sort | sed -r 's/^([01]-)*//g'`
# The first speaker
speaker=`echo "$speakers" | head -n 1`
if [ $verbose == 1 ]
then
echo "Setting default speaker: $speaker"
fi
pacmd set-default-sink "$speaker" | grep -vE 'Welcome|>>> $'
if [ $verbose == 1 ]
then
echo "Unmuting speaker: $speaker"
fi
pacmd set-sink-mute "$speaker" 0 | grep -vE 'Welcome|>>> $'
if [ "z$speakervolume" != "z" ]
then
if [ $verbose == 1 ]
then
let volume=$speakervolume/1000
echo "Setting $volume% volume: $speaker"
fi
pacmd set-sink-volume "$speaker" $speakervolume | grep -vE 'Welcome|>>> $'
fi
# For each of the other speakers
echo "$speakers" | tail -n+2 | while read speaker
do
if [ $verbose == 1 ]
then
echo "Muting speaker: $speaker"
fi
pacmd set-sink-mute "$speaker" 1 | grep -vE 'Welcome|>>> $'
done
mics=`pacmd list-sources | grep 'name:' | grep input | sed -r "s/.*<//g;s/>.*//g;s/.*usb.*/1-\0/gi;s/.*($priority).*/0-\0/gi" | sort | sed -r 's/^([01]-)*//g'`
# The first mic
mic=`echo "$mics" | head -n 1`
if [ $verbose == 1 ]
then
echo "Setting default source: $mic"
fi
pacmd set-default-source "$mic" | grep -vE 'Welcome|>>> $'
if [ $verbose == 1 ]
then
echo "Unmuting mic: $mic"
fi
pacmd set-source-mute "$mic" 0 | grep -vE 'Welcome|>>> $'
if [ "z$micvolume" != "z" ]
then
if [ $verbose == 1 ]
then
let volume=$micvolume/1000
echo "Setting volume to $volume%: $mic"
fi
pacmd set-source-volume "$mic" $micvolume | grep -vE 'Welcome|>>> $'
fi
# For each of the other mics
echo "$mics" | tail -n+2 | while read mic
do
if [ $verbose == 1 ]
then
echo "Muting mic: $mic"
fi
pacmd set-source-mute "$mic" 1 | grep -vE 'Welcome|>>> $'
done
if [ -e "/usr/lib/libreoffice/share/gallery/sounds/train.wav" ]
then
if [ $verbose == 1 ]
then
echo "Playing sound: /usr/lib/libreoffice/share/gallery/sounds/train.wav"
fi
play /usr/lib/libreoffice/share/gallery/sounds/train.wav 2> /dev/null
else
echo "Sound file does not exist: /usr/lib/libreoffice/share/gallery/sounds/train.wav"
exit 1
fi
fi
exit 0
This script is licensed under the GNU General Public License 2
14 thoughts on “USB sound in Ubuntu – automatically selecting a USB audio device when it is plugged in”
Thanks for nice method, however it was not working for me “out of box”. Few observations:
1. lines where the parameter speaker and mic are set resulted in empty variables, indeed there is ” sed ‘s/.*.*//g;’ ” which substitutes whole line with empty string. And I’ve prefered to use index instead of name (index is also used later), so my lines look like:
speaker=`pacmd list-sinks | grep -B 1 ‘name:.*usb’ | grep index | cut -d”:” -f2`
mic=`pacmd list-sources | grep -B 1 ‘name:.*usb’ | grep -B 1 input | grep index | cut -d”:” -f2`
2. setting the default sink by “pacmd set-default-sink $speaker” was not enough to actually change the device (during playback), I’ve added “pacmd move-sink-input”, so two lines added after the “pacmd set-default-sink $speaker”
sink=`pacmd list-sink-inputs | grep index | cut -d”:” -f2`
pacmd move-sink-input $sink $speaker | grep -vE ‘Welcome|>>> $’
3. small detail already mentioned in the post, I need to set volume to 25000 to hear anything.
Maybe there is something else to do with mic, but I don’t need this at hte moment and haven’t tested.
In case of questions/comments don’t hesitate to contact me at ignacykrasicki@interia.pl
When I pasted the script into this blog I didn’t realize that less than and greater than signs would need to be escaped. I have updated the post with corrections. The script is now appearing like I intended.
Wow – this was so helpful! Ran the script as is and it works perfectly. Thanks! Lubuntu 13.04
Hi Stephen,
This is a really convenient script, thanks!
I know it’s small, but would you consider putting on github or somewhere like that with an open source license so people can fork it?
I have licensed it under the GPL 2. Feel free to copy it, modify it, or post it to Github yourself.
Hi
Thanks a lot for your scripts.
I just needed to add ‘.*//g;’ | head -n 1`
to this
speaker=`pacmd list-sinks | grep ‘name:’ | grep usb | sed ‘s/.*.*//g;’ | head -n 1`
and then you got the speaker name properly
Thank you again !
How do you uninstall? It doesn’t work for me and I’d like to uninstall the installer.
Thank you.
Thanks a lot!
I was about to make the same, but found this solution.
To fit to my setup, I just slightly modified this script, because grep ‘usb’ gave me build-in mic in my usb camera as a default usb input device.
Use grep to figure out the name of the usb microphone
mic=
pacmd list-sources | grep 'name:' | grep input | grep USB_Headset | sed 's/.*<//g;s/>.*//g;' | head -n 1
Thanks so much for the script, it’s working great for the output on the headphones however it’s not working for the microphone. Can anyone help me figure this out: When I plug in my Logitech headset, and then open the sound settings, the output switched to the headset but the input stayed on the internal microphone, HOWEVER, if I happen to have the Sound settings window open, then both output and input switch to the headset! I reproduce this everytime, it’s only works when the sound setting window open. Can anyone explain why this is happening and what I could do to fix it? Thank you!
Thanks! Worked great for me.
To the user who was asking how to uninstall the changes made by this, I think you just have to remove these 2 files:
rm /lib/udev/rules.d/99-usb-audio-auto-select.rules /usr/lib/pm-utils/sleep.d/99usbaudio
Hi there
Firstly, if you just want to switch to a device when it is plugged in, you can use pulseaudio’s module-switch-on-connect for that:
cd ~/.config/pulse
cp /etc/pulse/default.pa .
and then to the end of default.pa add:
.if exists module-switch-on-connect.so
.nofail
load-module module-switch-on-connect
.fail
.endif
That doesn’t satisfy my requirements right now though because I have a broken USB microphone so want to switch only the output device, so I did something like what you suggested:
1) As per your instructions I added a udev rule as /etc/udev/rules.d/91-switch-to-usb-headset.rules:
SUBSYSTEM==”usb”, ACTION==”add”, DRIVERS==”snd-usb-audio”, RUN+=”/usr/local/bin/select_usb_headset_as_pulseaudio_user.sh”
2) This rule runs this little script as the root user:
#!/bin/bash
PULSEAUDIO_USER=$(find /run/user -type d -name pulse -exec stat -c ‘%U’ {} \;)
if [ “z$PULSEAUDIO_USER” != “z” ] ; then
# do this in the background so that we move on and let pulseaudio know about the new device
su -c /usr/local/bin/select_usb_headset.sh $PULSEAUDIO_USER &
fi
It finds the user running pulseaudio by examining /run/user for a directory named pulse and then runs the script that actually does the switch, but in the background. It does this because pulseaudio doesn’t activate the new audio sink until this rule has been processed, so backgrounding the script allows the rule to finish processing and things to move on. I’m not sure when and how exactly pulseaudio’s audio sink list is updated.
3) I’ve put the script that is run to do the actual switch here: https://gist.github.com/pvanheus/b236cae92dee77719467 – it sleeps for a second before doing the switch to ensure that the new audio sink is available.
The simplest solution for this, without scripting, is shown in response: https://askubuntu.com/a/158250/75657
Could it be adapted for HDMI instead of USB? I’ve an HDMI monitor with sound, and it’d be great if sound settings were automatically changed to “Digital Stereo (HDMI) Output when it’s plugged in.
Thanks
I’m not sure how to adapt it for HDMI. There is likely a different mechanism for listening for plugging and unplugging HDMI devices compared to USB devices.