USB sound in Ubuntu – automatically selecting a USB audio device when it is plugged in


Ubuntu has started to support USB audio.    When I plug in a USB speaker or 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.

I have included the text of the script below.  To start using it:

  1. 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
  2. As a super user, run the script with the --install flag.  This will install the play command if you don’t have it, setup the udev rule, restart the udev system, and install the pm-utils script.
    • sudo /opt/usb-audio-select.sh --install
  3. 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
  4. 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 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.

if [ "$1" = "--install" ]
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\", SUBSYSTEM==\"usb\", DRIVERS==\"snd-usb-audio\", RUN+=\"$script\"" > $rulefile
        service udev restart
    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
    fi
    exit 0
fi

if [ "$1" = "--sleep" ]
then
    sleep 1
fi

if [ "$UID" == "0" ]
then
    # Check process table for users running PulseAudio
    for user in `ps axc -o user,command | grep pulseaudio | cut -f1 -d' ' | sort | uniq`
    do
        # Fork and relaunch this script as each pulseaudio user
        # tell it to sleep for a second to let pulseaudio install the usb device
        su $user -c "bash $0 --sleep" &
    done
else
    # Use grep to figure out the name of the usb speaker
    speaker=`pacmd list-sinks | grep 'name:' | grep usb | sed 's/.*<//g;s/>.*//g;' | head -n 1`
    # Use grep to figure out the name of the usb microphone
    mic=`pacmd list-sources | grep 'name:' | grep input | grep usb | sed 's/.*<//g;s/>.*//g;' | head -n 1`

    if [ "z$speaker" != "z" ]
    then
        # use this speaker
        pacmd  set-default-sink "$speaker" | grep -vE 'Welcome|>>> $'
        # unmute
        pacmd  set-sink-mute "$speaker" 0 | grep -vE 'Welcome|>>> $'
        # Set the volume.  20000 is 20%
        pacmd  set-sink-volume "$speaker" 20000 | grep -vE 'Welcome|>>> $'
    fi

    if [ "z$mic" != "z" ]
    then
        # use this microphone
        pacmd  set-default-source "$mic" | grep -vE 'Welcome|>>> $'
        # unmute
        pacmd  set-source-mute "$mic" 0 | grep -vE 'Welcome|>>> $'
        # Set the volume.  80000 is 80%
        pacmd  set-source-volume "$mic" 80000 | grep -vE 'Welcome|>>> $'
    fi

    #play a sound to let you know that it was plugged in
    play /usr/share/sounds/speech-dispatcher/test.wav 2> /dev/null
fi

exit 0

This script is licensed under the GNU General Public License 2


Leave a comment

Your email address will not be published. Required fields are marked *

11 thoughts on “USB sound in Ubuntu – automatically selecting a USB audio device when it is plugged in

  • ignacy

    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

    • Stephen Post author

      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.

  • Justyn

    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?

  • Guilherme

    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 !

  • Alex

    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

  • Yannick

    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!

  • foobar

    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

  • Peter van Heusden

    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.