Shell script to set alsa default device by name

Optimize your system for ultimate performance.

Moderators: MattKingUSA, khz

Post Reply
Pheeble
Posts: 2
Joined: Wed Aug 05, 2020 6:07 am

Shell script to set alsa default device by name

Post by Pheeble »

I'm running Linux Mint 20 XFCE without pulseaudio, just alsa and jackd.

I configured the alsa default device via /etc/asound.conf, which requires specifying the index number of the device as defined in /proc/asound/cards.

The problem is that the index assigned to a given device may change at boot, which makes the configuration incorrect, so alsa no longer uses the correct device. Contrary to some online advice, specifying the name of the device in /etc/asound.conf does not work.

I ended up writing a dash shell script that creates a user-specific alsa configuration file at /home/$USER/.asoundrc that over-rides the system configuration.

The script accepts arguments to specify the alsa default device name, and optionally to redirect output through jackd.

I have the script run at each login to set the default alsa device. I also execute the script with appropriate arguments at startup and shutdown of jack using the setup options in QjackCtl:
QjackCtl - Run shell script
QjackCtl - Run shell script
QjackCtl-options.png (39.64 KiB) Viewed 620 times

I'm posting the complete (voluminously commented) script here in case anyone finds it useful.

Code: Select all

#!/bin/dash

########################################################################
#                                                                      #
#                   set-alsa-default-device.sh                         #
#                                                                      #
#   This script is designed to set the default alsa audio device by    #
#   referring to it by name instead of index number.                   #
#                                                                      #
#   The index numbers may change as devices are added or removed,      #
#   or randomly at each reboot, so specifying the alsa default device  #
#   via the index number can be problematic.                           #
#                                                                      #
#   For example, this output indicates that the device                 #
#   named 'PCH' has the index number '2':                              #
#                                                                      #
#    cat /proc/asound/cards                                            #
#      0 [HDMI    ]: HDA-Intel - HDA Intel HDMI                        #
#                    HDA Intel HDMI at 0xf7914000 irq 34               #
#      1 [CH345   ]: USB-Audio - CH345                                 #
#                    QinHeng CH345 at usb-0000:00:14.0-14, full speed  #
#      2 [PCH     ]: HDA-Intel - HDA Intel PCH                         #
#                    HDA Intel PCH at 0xf7910000 irq 35                #
#      3 [NVidia  ]: HDA-Intel - HDA NVidia                            #
#                    HDA NVidia at 0xf7080000 irq 17                   #
#                                                                      #
#   ... but on the next boot, 'PCH' had become index number '1':       #
#                                                                      #
#    cat /proc/asound/cards                                            #
#      0 [HDMI   ]: HDA-Intel - HDA Intel HDMI                         #
#                   HDA Intel HDMI at 0xf7914000 irq 34                #
#      1 [PCH    ]: HDA-Intel - HDA Intel PCH                          #
#                   HDA Intel PCH at 0xf7910000 irq 35                 #
#      2 [NVidia ]: HDA-Intel - HDA NVidia                             #
#                   HDA NVidia at 0xf7080000 irq 17                    #
#      3 [CH345  ]: USB-Audio - CH345                                  #
#                   QinHeng CH345 at usb-0000:00:14.0-14, full speed   #
#                                                                      #
#   The script sets the default alsa device by writing a user-specific #
#   alsa configuration file at '/home/$USER/.asoundrc'.                #
#                                                                      #
#   Note: Any existing file will be over-written! Back up as required. #
#                                                                      #
#   The entries 'defaults.pcm.card' (output) and 'defaults.ctl.card'   #
#   (input) are created, referencing the device's name and index       #
#   number as listed in /proc/asound/cards.                            #
#                                                                      #
#   Specify the audio device to set as default by supplying the        #
#   device name as a case-sensitive command argument, eg.              #
#    'set-alsa-default-device.sh PCH' to set PCH as alsa default       #
#                                                                      #
#   If no such device name is supplied, the value of the script        #
#   variable 'devdef' will be used. Edit as required.                  #
#                                                                      #
#   Optionally send all alsa output to the jackd audio server by       #
#   adding the keyword 'jack' as a command argument, eg.               #
#    'set-alsa-default-device.sh PCH jack' to use PCH + jack           #
#   or                                                                 #
#    'set-alsa-default-device.sh jack' to use 'devdef' value + jack    #
#                                                                      #
#                                                                      #
#   Supplying more than two command line arguments, or an incorrect    #
#   audio device name, will result in the script terminating with      #
#   an error message and a return value of '1'.                        #
#                                                                      #
########################################################################

# Path to user's alsa configuration file
rc_path="/home/$USER/.asoundrc"

# Device name to use if none supplied as script argument
devdef='PCH'

# Variable to indicate if redirection to jackd server is required.
# Redirection occurs only if the variable 'jack' is set to 'true'.
jack=0

# Get the script arguments, if any. Maximum of two arguments.
# The arguments are optional, and could be comprised of:
#   - one alsa audio device name as listed in /proc/asound/cards, 
#   and/or
#   - the keyword 'jack' to indicate that the output should be
#     redirected to jackd.
# If no arguments are supplied, the value of the script variable
# 'devdef' is used.
if [ "$#" -eq '0' ]
then
    d="$devdef"
elif [ "$#" -eq '1' ]
then
    if [ "$1" = 'jack' ]
    then
        jack='true'
        d="$devdef"
    else
        d="$1"
    fi
elif [ "$#" -eq '2' ]
then
    if [ "$1" = 'jack' ]
    then
        jack='true'
        d="$2"
    elif [ "$2" = 'jack' ]
    then
        jack='true'
        d="$1"
    else
        echo "Error: when supplying two arguments, one of the arguments must be the keyword 'jack' to indicate redirection via the jackd audio server."
        exit 1
    fi
else
    echo 'Error: Maximum of two arguments.'
    exit 1
fi

# Get the contents of '/proc/asound/cards''
cards="$(cat /proc/asound/cards)"

# Get the index number of the device specified in the variable 'd'
device="$(printf '%s' "$cards" | grep "\[$d")"

# Check that the return string is not zero-length
if [ -z "$device" ] 
then
    echo "Device '$d' not found in list of devices in '/proc/asound/cards'"
    exit 1
fi

# Get the first field of the output, ignoring leading spaces
i="$(printf '%s' "$device" | awk '{print $1}')"

# Check that i is an integer
if [ -n "$i" ] && [ "$i" -eq "$i" ] 2>/dev/null
then
    # Prepend comment char to each line of 'cards' variable
    cards="$(printf '%s' "$cards" | sed 's/^/#/')"
cat << EOF1 > "$rc_path"
# Alsa default device set to '$d' as index '$i' from /proc/asound/cards:

$cards

# Output:
defaults.pcm.card $i

# Input:
defaults.ctl.card $i
EOF1

    # Append jackd redirection if required
    if [ "$jack" = 'true' ]
    then
cat << 'EOF2' >> "$rc_path"

# Send all alsa output through jackd.
# Note: jack must be running or alsa applications will fail.
pcm.!default {
    type plug
    slave.pcm "jack"
    hint.description "Jack Audio"
}
EOF2
    fi
else
    echo "Failed to get index of device '$d' from '/proc/asound/cards'"
    exit 1
fi
User avatar
LAM
Established Member
Posts: 992
Joined: Thu Oct 08, 2020 3:16 pm
Has thanked: 141 times
Been thanked: 348 times

Re: Shell script to set alsa default device by name

Post by LAM »

Thank you, i solved the problem in a different way, but is always nice to have an alternative in case things break again. :D

This is the solution that worked for me:

/etc/modprobe.d/alsa-base.conf

Code: Select all

options snd cards_limit=2 
options snd-hda-intel index=1,0 id=PCH,HDMI

in mix, nobody can hear your screen

Post Reply