#!/bin/bash # volume # # Copyright 2007, Michael T. Dean (mtdean at thirdcontact dt com) # # 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 St, Fifth Floor, Boston, MA 02110-1301 USA # # Description: # This script can be used to adjust the volume of any control provided by your # sound card and can optionally display a message using either xosd or # mythtvosd. The script is useful to MythTV users whose sound cards require # changing some volume control other than PCM or Master (the only two choices # MythTV supports) or who wish to modify multiple controls; or to anyone # wishing to add a low-dependency volume control with OSD to any system using, # for example, multimedia keys (i.e. XF86AudioRaiseVolume, # XF86AudioLowerVolume, XF86AudioMute) or LIRC. # # Version: 0.2 # # Revision History: # 0.2 (20070310): complete rewrite; added support for xosd; added volume # control override; added mute toggle; changed script to use # control limits (rather than percents--which pose rounding # problems); each control is modified relative to current # value (instead of setting all to the same value); removed # usage of linefeed IFS; added error checking and defaults # for robustness; put some code in functions # 0.1 (20060123): initial version # # Quickstart: # Verify your amixer reports sane values as described in the first paragraph # of "Usage" below. Then, run the script as shown below: # ./volume up PCM # ./volume down PCM # ./volume mute PCM # ./volume unmute PCM # ./volume toggle PCM # # Usage: # volume {up|down|mute|unmute|toggle} [control1 [control2 [...]]] # # First, verify that amixer reports sane values for the control limits and # current volume. Control limits must be specified in the format " Limits: # Playback 0 - 31" in any line of output. Current volume must be specified # in the format ": Playback 28 [90%]" in any line of output. # Please test that your version of amixer does this for each control you've # specified in VOLUME_CONTROLS or you may blow out your speakers. # # Then, modify the default values specified below to allow usage of the # script in most cases without listing control names in the command line # (i.e. ./volume {up|down|mute|unmute|toggle}). # # To use with multimedia keys or other keyboard controls, map the appropriate # commands to the desired keys in your window manager's or some other key # handler's keybindings. # # To use with LIRC, you must run irexec (i.e. add the lines, "killall irexec" # and "irexec -d" (no quotes)--and in the order given)--to your X start # script (i.e. ~/.xinitrc). Then, add the following lines to your LIRC # configuration (i.e. ~/.lircrc) and adjust button names and config path as # appropriate: # # begin # prog = irexec # button = volume-up # config = /path/to/volume up & # repeat = 2 # end # begin # prog = irexec # button = volume-down # config = /path/to/volume down & # repeat = 2 # end # begin # prog = irexec # button = mute # config = /path/to/volume mute & # config = /path/to/volume unmute & # repeat = 0 # end # # Modify the following default values as appropriate for your system. # Set the VOLUME_CONTROLS to the array of volume controls you want the script # to adjust. Place spaces (no commas) between elements of the array. Use # single quotes around elements with spaces, and optionally on elements without # spaces). # The list of volume controls may be overriden by specifying the names of # controls to modify at the end of the command-line. When overriding the # volume controls, each control will be adjusted by only 1 unit. See, also, # MIXER_ADJ_AMOUNT, below. #VOLUME_CONTROLS=('PCM' 'Surround' 'Center' 'LFE' 'Analog Front') #VOLUME_CONTROLS=('PCM') VOLUME_CONTROLS=('Master') # Set the MIXER_ADJ_AMOUNT to the array of the desired number of units by which # you want to adjust the volume of each control specified in VOLUME_CONTROLS. # You should specify the same number of adjustments as you specified controls # in VOLUME_CONTROLS. # The units used are specific to the sound card control. The range of # available values is reported by "amixer sget " on the line that # starts with " Limits:" and may differ for the controls you've specified. # By choosing appropriate values, you can ensure all controls change by the # same relative volume. # If not specified, all controls will be adjusted by 1 unit. #MIXER_ADJ_AMOUNT=(1 1 1 1 1) #MIXER_ADJ_AMOUNT=(1) # Set the OSD_VOLUME_CONTROL to the name of the ALSA control whose volume # should be reported when using the OSD. # This control must be included in VOLUME_CONTROLS above or the volume will # always be set to the same value; # If not specified, the first value in VOLUME_CONTROLS will be used. #OSD_VOLUME_CONTROL='Center' #OSD_VOLUME_CONTROL='PCM' #OSD_VOLUME_CONTROL='Master' # Display the current volume using the specified OSD application (Currently, # only 'xosd' and 'mythtvosd' are supported). Use '' or comment out if you do # not want to display the volume # Note that if using mythtvosd, the volume message will only be displayed when # using Myth's internal player to play back videos. When using xosd, the # volume message will be displayed whether playing back videos or in the # frontend menus (or anywhere else in X). USE_OSD='xosd' #USE_OSD='mythtvosd' #USE_OSD='' # OSD application executables. # Provide the full path to the executable if it is not in your PATH. XOSD='osd_cat' MYTHTVOSD='mythtvosd' # Arguments to pass to xosd (do not specify -b, -P, and -T). # This is the location at which to specify font, position, alignment, offset, # delay, lines, shadow, and color XOSD_ARGS='-f -adobe-helvetica-bold-r-normal-*-*-240-100-100-p-*-iso8859-1 -p bottom -A center -o 80 -d 1 -l 1 -s 2 -c Green' # xosd barmode to use--either percentage or slider XOSD_BARMODE=percentage #XOSD_BARMODE=slider # Set the BROADCAST_ADDRESS to the address of the frontend on which you want to # display the volume OSD when using mythtvosd. # Using 127.0.0.1 will generally work. BROADCAST_ADDRESS='127.0.0.1' # No changes should be required below this line. ACTION=$1 shift if [ $# -gt 0 ]; then unset VOLUME_CONTROLS MIXER_ADJ_AMOUNT OSD_VOLUME_CONTROL for INDEX in `seq 0 $(($# - 1))`; do VOLUME_CONTROLS[${INDEX}]="$1" shift done fi if [ "x${OSD_VOLUME_CONTROL}" = "x" ]; then OSD_VOLUME_CONTROL=${VOLUME_CONTROLS[0]} fi NUM_CONTROLS=$((${#VOLUME_CONTROLS[*]} - 1)) set_volume() { for INDEX in `seq 0 ${NUM_CONTROLS}`; do unset CONTROL RANGE VOLUME ADJUST_AMOUNT CONTROL=${VOLUME_CONTROLS[$INDEX]} if [ "x${CONTROL}" = "x" ]; then continue fi # Relies on amixer reporting limits in the format # " Limits: Playback 0 - 31" in any line of output. Please test that # your version of amixer does this for each control you've specified in # VOLUME_CONTROLS or you may blow out your speakers. RANGE=(`amixer sget "${CONTROL}" | awk '/Limits/ {split($0,a); print a[3] " " a[5]; exit }'`) if [ "x${RANGE[0]}" = "x" -o "x${RANGE[1]}" = "x" ]; then # Couldn't get the range. Skip this control continue fi # Relies on amixer reporting volume in the format # ": Playback 28 [90%]" in any line of output. Please test # that your version of amixer does this for each control you've specified # in VOLUME_CONTROLS or you may blow out your speakers. VOLUME=`amixer sget "${CONTROL}" | awk '/[%]/ {split($0,a,":"); split(a[2],a); print a[2]; exit}'` if [ "x${VOLUME}" = "x" ]; then # Couldn't get the volume. Skip this control continue fi ADJUST_AMOUNT=${MIXER_ADJ_AMOUNT[$INDEX]} if [ "x${ADJUST_AMOUNT}" = "x" ]; then ADJUST_AMOUNT=1 fi if [ "x$1" != "xup" ]; then ADJUST_AMOUNT=-${ADJUST_AMOUNT} fi NEW_VOLUME="$((${VOLUME} + ${ADJUST_AMOUNT}))" if [ ${NEW_VOLUME} -lt ${RANGE[0]} ]; then NEW_VOLUME=${RANGE[0]} elif [ ${NEW_VOLUME} -gt ${RANGE[1]} ]; then NEW_VOLUME=${RANGE[1]} fi amixer -q set ${CONTROL} ${NEW_VOLUME} & done } simple_message() { MESSAGE=$1 if [ "x${MESSAGE}" = "x" ]; then return fi if [ "x${USE_OSD}" = "xxosd" ]; then if [ "x${XOSD}" != "x" -a "x${DISPLAY}" != "x" ]; then # Kill any running xosd processes (to clear the previous message) killall -q ${XOSD} > /dev/null 2>&1 echo ${MESSAGE} | ${XOSD} ${XOSD_ARGS} > /dev/null 2>&1 & fi elif [ "x${USE_OSD}" = "xmythtvosd" ]; then if [ "x${MYTHTVOSD}" != "x" ]; then ${MYTHTVOSD} --bcastaddr="${BROADCAST_ADDRESS}" \ --template='alert' \ --alert_text="${MESSAGE}" > /dev/null 2>&1 & fi fi } show_percent() { if [ "x${USE_OSD}" = "xxosd" ]; then # Relies on amixer reporting volume in the format "[90%]" in any line of # output. NEW_VOLUME_PCT=`amixer sget "${OSD_VOLUME_CONTROL}" | awk '/[%]/ {split($0,a,"["); split(a[2],a,"%"); print a[1]; exit}'` if [ "x${NEW_VOLUME_PCT}" = "x" ]; then # Couldn't get the volume. Do not display the OSD. return fi if [ "x${XOSD}" != "x" -a "x${DISPLAY}" != "x" ]; then # Kill any running xosd processes (to clear the previous message) killall -q ${XOSD} > /dev/null 2>&1 ${XOSD} -b ${XOSD_BARMODE} -P ${NEW_VOLUME_PCT} \ -T "${OSD_VOLUME_CONTROL} Volume: ${NEW_VOLUME_PCT}%" ${XOSD_ARGS} > /dev/null 2>&1 & fi elif [ "x${USE_OSD}" = "xmythtvosd" ]; then if [ "x${MYTHTVOSD}" != "x" ]; then ${MYTHTVOSD} --bcastaddr="${BROADCAST_ADDRESS}" \ --template='alert' \ --alert_text="${OSD_VOLUME_CONTROL} Volume: ${NEW_VOLUME_PCT}%" \ > /dev/null 2>&1 & fi fi } case "${ACTION}" in up|down) set_volume "${ACTION}" show_percent ;; mute|unmute) for INDEX in `seq 0 ${NUM_CONTROLS}`; do unset CONTROL CONTROL=${VOLUME_CONTROLS[$INDEX]} amixer -q set ${CONTROL} ${ACTION} & done MESSAGE=`echo "${ACTION}" | tr [:lower:] [:upper:]` > /dev/null 2>&1 simple_message ${MESSAGE} ;; toggle) for INDEX in `seq 0 ${NUM_CONTROLS}`; do unset CONTROL CURRENT_STATE CONTROL=${VOLUME_CONTROLS[$INDEX]} # Relies on amixer reporting mute state in the format # "[90%] [7.50dB] [on]" in any line of output. Please test # that your version of amixer does this for each control you've specified # in VOLUME_CONTROLS. CURRENT_STATE=`amixer sget "${CONTROL}" | awk '/[%]/ {split($0,a,"["); split(a[4],a,"]"); print a[1]; exit}'` if [ "x${CURRENT_STATE}" = "x" ]; then # Couldn't get the mute state. Skip this control. continue fi if [ "x${CURRENT_STATE}" = "xoff" ]; then MUTE=unmute else MUTE=mute fi amixer -q set ${CONTROL} ${MUTE} & done # Assume the mute state of all controls is the same as that of the # last successfully set control. Using toggle with multiple controls # in different states doesn't make much sense. MESSAGE=`echo "${MUTE}" | tr [:lower:] [:upper:]` > /dev/null 2>&1 simple_message ${MESSAGE} ;; *) echo "Usage: $0 {up|down|mute|unmute|toggle} [control1 [control2 [...]]]" exit 1 ;; esac