Amixer

Getting Volume Keys to work

If you can't get the XF86 Key Events for your volume keys to work, the following script provides a good workaround. (Tested with amixer version 1.0.28)

#!/bin/bash
# $HOME/bin/volctrl

COMMAND=$1
case $COMMAND in
   'tmute')
       amixer set Master toggle
       ;;
   'up')
       amixer -M set Master 10%+ 
       ;;
   'down')
       amixer -M set Master 10%- 
       ;;
   *)
       echo "Invalid argument $COMMAND"
       exit -1
       ;;
esac

You can increase the volume with

volctrl up

decrease it with

volctrl down

and toggle mute with

volctrl tmute

All you need to do now is mapping these commands to the keyboard shortcuts you want.

Older Amixer versions

The -M flag for non-linear scaling isn't available in older Amixer version. The below version of the script addresses this to a certain extent.

#!/bin/bash
# $HOME/bin/volctrl
CONTROL_NAME="Master"
VOLUME_RANGE_STR=$(amixer get $CONTROL_NAME | grep 'Limits: Playback' | sed -e 's/.*Playback//g' | sed -e 's/ \- /:/g')
MIN_VOLUME=$(echo $VOLUME_RANGE_STR | cut -d':' -f 1)
MAX_VOLUME=$(echo $VOLUME_RANGE_STR | cut -d':' -f 2)
VOL_RANGE=$[ $MAX_VOLUME - $MIN_VOLUME ]

function get_mute_state(){
    amixer get $CONTROL_NAME | tail -n 1 | egrep -o  "\[o[fn]f?\]" | tr -d [\[\]]
}

function get_volume(){
    amixer get $CONTROL_NAME | tail -n 1 | egrep -o \(\-\|\)[0-9]* | head -n 1
}

function volum_abs_to_rel(){
    local abs_val=$1
    local norm_val=$(echo "100 * ((( $abs_val - $MIN_VOLUME ) / $VOL_RANGE ) ^4)" | bc -l)
    stripped_val=$(echo ${norm_val} | sed -e 's/\..*//g')
    echo ${stripped_val:-0}
}

function get_volume_percent(){
    echo $(volum_abs_to_rel $(get_volume))
}

function set_volume_percent(){
    local target=$1
    [ $target -gt 100 ] && target=100
    [ $target -lt 0 ] && target=0
    local target_abs=$(echo "$MIN_VOLUME + $VOL_RANGE * sqrt(sqrt( $target / 100))" | bc -l)
    amixer -- set $CONTROL_NAME ${target_abs%.*}
}

function volume_change_percent(){
    local delta=$1
    local current=$(get_volume_percent)
    local new=$[ $current + $delta ] 
    set_volume_percent $new
}

COMMAND=$1
case $COMMAND in
    'tmute')
        amixer set $CONTROL_NAME toggle
        ;;
    'up')
        volume_change_percent 10
        ;;
    'down')
        volume_change_percent -10
        ;;
    'get')
        if [ "off" == "$(get_mute_state)" ];then
            echo "M";
        else 
            echo $(get_volume_percent)"%"
        fi
        ;; 
     *)
        echo "Invalid argument $COMMAND"
        exit -1
        ;;
esac