#!/bin/sh

# ALSA configurator
# in 1998-2001 by Jan ONDREJ (SAL) <ondrejj@salstar.sk>
# Many fixes and improvements by Bernd Kaindl, SuSE

version=0.4.4a

#  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., 675 Mass Ave, Cambridge, MA 02139, USA.

# Example:
# export CARD_0x#="snd-module_name Nicnames_of_cards [...]\
#  @IOs IRQs DMAs ...\
#  @available_IOs avail_IRQs ...\
#  default_IO default_IRQ ..."
# Don't forget add to CARDS!

# Check for root privileges
if [ `id -u` -ne 0 ]; then
  echo "You must be root to use this script."
  exit 1
fi

echo "Initializing ... please wait ..."

# Exit function
acex() {
  test -n $TMP && test -f $TMP && rm -f $TMP
  clear
  echo "$2"
  exit $1
}

# Find module configuration (and make it if none)
if [ -f /etc/modules.conf ]; then
  export cfgfile="/etc/modules.conf"
elif [ -d /etc/modutils ]; then
  export cfgfile="/etc/modutils/alsa"
elif [ -f /etc/conf.modules ]; then
  export cfgfile="/etc/conf.modules"
else
  export cfgfile="/etc/modules.conf"
fi
test -f $cfgfile || touch $cfgfile

# Check for alsasound rc script
if [ -f /etc/init.d/alsasound ]; then
  # RedHat >=7.0
  export rcalsasound=/etc/init.d/alsasound
elif [ -f /etc/rc.d/init.d/alsasound ]; then
  # RedHat <7.0
  export rcalsasound=/etc/rc.d/init.d/alsasound
elif [ -f /sbin/init.d/alsasound ]; then
  # SuSe
  export rcalsasound=/sbin/init.d/alsasound
elif [ -f /etc/init.d/alsa ]; then
  # Debian
  export rcalsasound=/etc/init.d/alsa
fi

# Check for dialog, whiptail, gdialog, awk, ... ?
if which dialog > /dev/null; then
  DIALOG=dialog
else
  if which whiptail > /dev/null; then
    whiptail_wrapper() {
      X1="$1"
      X2="$2"
      if [ $1 = --yesno ]; then
        X3=`expr $3 + 3`
      elif [ $1 = --menu ]; then
        X3=`expr $3 + 1`
      else
        X3=$3
      fi
      shift 3
      whiptail "$X1" "$X2" $X3 "$@"
    }
    DIALOG=whiptail_wrapper
  else
    acex 1 "Error, dialog or whiptail not found."
  fi
fi
if which awk > /dev/null; then :
else
  acex 1 "Error, awk not found. Can't continue."
fi

# Check for existence of group "audio".
if grep -q ^audio: /etc/group; then
  SND_MODE=0660
  SND_GID=`grep ^audio: /etc/group | cut -d: -f3`
  SND_UID=0
else
  SND_MODE=0666
  SND_GID=0
  SND_UID=0
fi

# Read mode, gid, uid from conf.modules
eval `grep "^options snd " $cfgfile | cut -d " " -f3-`
if [ "$snd_device_mode" ]; then
  SND_MODE="$snd_device_mode"
fi
if [ "$snd_device_gid" ]; then
  SND_GID="$snd_device_gid"
fi
if [ "$snd_device_uid" ]; then
  SND_UID="$snd_device_uid"
fi

# Get the ALSA version
/sbin/modprobe snd
ALSAVER=`cat /proc/asound/version | awk '/Advanced Linux Sound Architecture/ {print $7}'`
AV_MAJOR=`echo $ALSAVER | cut -d. -f1`
AV_MINOR=`echo $ALSAVER | cut -d. -f2`
AV_FULL=`expr $AV_MAJOR \* 1000 + $AV_MINOR`

# Stripnum
stripnum() {
  SEQ=":`seq -s : $1 $2`:"
  shift 2
  for i in $@; do
    SEQ=`echo $SEQ | sed s/:$i:/:/`
  done
  echo $SEQ
}

# Set variables
MODINFO=/sbin/modinfo
MODPATH="/lib/modules/`uname -r`/misc"
TMP=/tmp/alsaconf.$$
USED_IRQS=`grep "^ *[0-9]*:" /proc/interrupts | cut -d: -f1 | tr -d " "`
FREE_IRQS=`stripnum 0 15 $USED_IRQS`
USED_DMAS=`grep "^ *[0-9]*:" /proc/dma | cut -d: -f1 | tr -d " "`
FREE_DMAS=`stripnum 0 7 $USED_DMAS`

# INTRO
intro() {
$DIALOG --msgbox "
                    ALSA  CONFIGURATOR
                      version $version

                      (c)  1998-2001
            Jan ONDREJ (SAL), <ondrejj@salstarsk>
      Many fixes and improvements by Bernd Kaindl, SuSE

  This script is a configurator for Advanced Linux Sound
  Architecture driver.
  If alsa is running, you should close all sound apps
  now and run \"$rcalsasound stop\"
  to remove the drivers. alsaconf will try to do this,
  but it cannot kill your apps.
  You need ALSA 0.5.0 or higher now.

               (Detected ALSA version: $ALSAVER)
" 21 67 || acex 0 "Aborted."
}

# FAREWELL
farewell() {
$DIALOG --msgbox "

                $*

                   ALSA  CONFIGURATOR
                     version $version

           will prepare the card for playing now.

 Now I'll run '$rcalsasound start',
 then use I'll use amixer to raise the 'Master' and 'PCM'
 volumes. If you want to use the commandline-based mixer
 amixer also, read the man page: 'man amixer'.

" 18 62 || acex 0 "Aborted."
}

# Find cards
load_cards() {
  MENU_CARDS='no_card No_more_cards'
  echo -n "Srarching for cards ."
  for i in $MODPATH/snd-card-*; do
    echo -n "."
    CARD_DRIVER=`echo $i | sed 's/^.*snd-card-\(.*\)\.o$/\1/'`
    CI=`$MODINFO -d $i | grep "^Card: " | cut -d" " -f2- | tr " " "_"`
    if [ -z "$CI" ]; then # if no Card string
      CI=`$MODINFO -d $i | grep "^Driver: " | cut -d" " -f2- | tr " " "_"`
    fi
    for c in $CI; do
      MENU_CARDS="$MENU_CARDS $CARD_DRIVER `echo $c | head -c 50`"
    done
  done
  echo ""
}

# Select card
select_card() {
  $DIALOG --menu "Select card" 20 80 13 $MENU_CARDS 2> $TMP || acex 0 "Aborted."
  SELECTED_CARD=snd-card-`cat $TMP`
}

# Enter a number or text.
enter_data() {
  $DIALOG --inputbox "$1\n$2" 9 70 "$DEFOPT" 2> $TMP || acex 0 "Aborted."
  RETURN=`cat $TMP`
}

# Disabled or Enabled ?
enter_dis_ena() {
  $DIALOG --menu "$1" 9 70 2 "0" "Disable" "1" "Enable" 2> $TMP || acex 0 "Aborted."
  RETURN=`cat $TMP`
}

# Add option
add_option() {
  if [ "$2" ]; then
    CARD_OPT="$CARD_OPT $1=$2"
  fi
}

# Get card options from conf.modules
get_card_options() {
  # Clear all ALSACONF_snd_ variables
  ALSACONF_snd_temp=0
  `set | grep "^ALSACONF_snd_" | cut -d"=" -f1 \
    | sed -e 's/^/export /g' -e 's/$/=/g'`

  # Read conf.modules
  O=`grep "options * $1 * snd_index=$2 " $cfgfile`
  if [ "$O" ]; then
    # single card conf. detected
    `echo $O | cut -d" " -f3- | tr " " "\n" | sed 's/^/export ALSACONF_/g'`
  else
    O=`grep "options * $1 * snd_index=$2," $cfgfile`
    if [ -z "$O" ]; then
      O=`grep "options * $1 * snd_index=[0-9,]*,$2[, ]" $cfgfile`
    fi
    if [ "$O" ]; then
      # now is multicard conf. detected. Try, what card is it.
      X=`echo $O | cut -d" " -f3 | cut -d"=" -f2 | tr "," " "`
      I=2
      for x in $X; do
        [ $x -eq $2 ] && break
        I=`expr $I + 1`
      done
      export I
      # now $I-1 is number of card on options line, $O is options line
      `echo $O | cut -d" " -f3- | tr " " "\n" | sed 's/=/=,/g' \
         | awk 'BEGIN -F, {I=ENVIRON["I"]} {print $1$(I)}' \
         | sed 's/^/export ALSACONF_/g'`
    fi
  fi
}

# Read options for card
read_card_options() {
  # Get options for this driver
  CARD_OPT="$CARD_DRIVER snd_index=$CARD_NUM"
  OPTNUM="0"
  CARD_GETOPT=`$MODINFO -p $MODPATH/$CARD_DRIVER.o \
    | grep -v "^snd_index " | cut -d" " -f1`
  for OPT in $CARD_GETOPT; do
    OPTNUM=`expr $OPTNUM + 1`
    INF=`$MODINFO -p $MODPATH/$CARD_DRIVER.o \
      | grep "^$OPT " | cut -d'"' -f2`
    INF1=`echo "$INF" | cut -d"[" -f1`
    INF2=`echo "$INF" | cut -d"[" -f2 | tr -d "]"`
    eval DEFOPT="\$ALSACONF_$OPT"
    case $INF2 in
      BOOL)
        enter_dis_ena "$INF1"
        ;;
      list=*)
        MAINOPT=`echo "$INF2" | cut -d"=" -f2`
        enter_data "$INF1" "$MAINOPT"
        ;;
      DMA*_SIZE)
        enter_data "$INF1" "4-128"
        ;;
      *)
        enter_data "$INF1" ""
        ;;
    esac
    add_option $OPT $RETURN
  done
}

# MAIN
load_cards
intro
if [ -d /proc/asound ]; then
  $rcalsasound stop >/dev/null 2>&1
fi
CARD_NUM=0
while true; do
  select_card
  if [ "$SELECTED_CARD" = "snd-card-no_card" ]; then
    break
  fi
  CARD_DRIVER=$SELECTED_CARD
  get_card_options $CARD_DRIVER $CARD_NUM
  read_card_options

  eval CARD_OPT_$CARD_NUM="\$CARD_OPT"
  CARD_NUM=`expr $CARD_NUM + 1`
done

# If no cards configured, then exit
if [ "$CARD_NUM" = "0" ]; then
  acex 0 "No cards configured."
fi

# Configuration done
$DIALOG --yesno "Do you want to modify ${cfgfile}?" 5 50 || acex 0 "Aborted."
rm -f $TMP
clear

# Copy conf.modules and make changes.
export ACB="# --- BEGIN: Generated by ALSACONF, do not edit. ---"
export ACE="# --- END: Generated by ALSACONF, do not edit. ---"

add_to_modules_conf() {
  awk '
    BEGIN {
      ACB=ENVIRON["ACB"]
      ACE=ENVIRON["ACE"]
      WOAC=0
      do {
        EOF=getline CMF <ENVIRON["cfgfile"]
        if (EOF <= 0) {
          print ""
          print ACB
          WOAC=1
          break
        }
        print CMF
      } while (CMF != ACB);
    }
    // {
      print
    }
    END {
      if (WOAC == 1) {
        print ACE
      } else {
        do {
          EOF=getline CMF <ENVIRON["cfgfile"]
        } while ((CMF != ACE) && (EOF > 0));
        print CMF
        while (EOF > 0) {
          EOF=getline CMF <ENVIRON["cfgfile"]
          if (EOF>0)
            print CMF
        }
      }
    }
  '
}

join_cards() {
  # Join cards with same driver to one line.
  N1=0
  while [ $N1 -lt $CARD_NUM ]; do
    N1=`expr $N1 + 1`
    CO1=`eval echo \\$CARD_OPT_$N1 | cut -d" " -f1`
    N2=1
    while [ $N2 -lt $N1 ]; do
      CO2=`eval echo \\$CARD_OPT_$N2 | cut -d" " -f1`
      if [ "$CO1" = "$CO2" ]; then
        O1=`eval echo \\$CARD_OPT_$N1`
        O2=`eval echo \\$CARD_OPT_$N2`
        OO=`echo "$O2 $O1" | tr " " "\n" | awk -F= '
          BEGIN {
            x=0
          }
          /.*=.*/ {
            if (A[$1]=="") {
              N[x++]=$1
              A[$1]=$2
            } else {
              A[$1]=A[$1]","$2
            }
            next
          }
          {
            drv=$1
          }
          END {
            printf drv
            for(i=0;i<x;i++) {
              n=N[i]
              printf " %s=%s",n,A[n]
            }
          }
        '`
        eval CARD_OPT_$N2=\"$OO\"
        unset CARD_OPT_$N1
        break
      fi
      N2=`expr $N2 + 1`
    done
  done
}

# Detect 2.2.X kernel
KVER=`uname -r | tr ".-" "  "`
KVER1=`echo $KVER | cut -d" " -f1`
KVER2=`echo $KVER | cut -d" " -f2`
if [ $KVER1 -ge 2 ] && [ $KVER2 -ge 2 ]; then
  SOUND_CORE="soundcore"
else
  SOUND_CORE="snd"
fi

if [ "$CARD_NUM" = "1" ]; then
# Single card.
echo "# --- ALSACONF verion $version ---
alias char-major-116 snd
alias snd-card-0 $CARD_DRIVER
alias char-major-14 $SOUND_CORE
alias sound-slot-0 snd-card-0
alias sound-service-0-0 snd-mixer-oss
alias sound-service-0-1 snd-seq-oss
alias sound-service-0-3 snd-pcm-oss
alias sound-service-0-8 snd-seq-oss
alias sound-service-0-11 snd-mixer-oss
alias sound-service-0-12 snd-pcm-oss
options snd snd_major=116 snd_cards_limit=1\
 snd_device_mode=$SND_MODE snd_device_gid=$SND_GID snd_device_uid=$SND_UID
options $CARD_OPT" | add_to_modules_conf > $TMP
else
# More cards.
(echo "# --- ALSACONF verion $version ---
alias char-major-116 snd
alias char-major-14 $SOUND_CORE"
N=0
while [ $N -lt $CARD_NUM ]; do
  echo -n "alias snd-card-$N "
  eval echo \$CARD_OPT_$N | cut -d" " -f1
  echo "alias sound-slot-$N snd-card-$N"
  echo "alias sound-service-$N-0 snd-mixer-oss"
  echo "alias sound-service-$N-1 snd-seq-oss"
  echo "alias sound-service-$N-3 snd-pcm-oss"
  echo "alias sound-service-$N-8 snd-seq-oss"
  echo "alias sound-service-$N-11 snd-mixer-oss"
  echo "alias sound-service-$N-12 snd-pcm-oss"
  N=`expr $N + 1`
done
echo "options snd snd_major=116 snd_cards_limit=$CARD_NUM\
 snd_device_mode=$SND_MODE snd_device_gid=$SND_GID snd_device_uid=$SND_UID"
join_cards
N=0
while [ $N -lt $CARD_NUM ]; do
  echo -n "options "
  eval echo \$CARD_OPT_$N
  N=`expr $N + 1`
done
) | add_to_modules_conf > $TMP
fi

if cp -f $cfgfile $cfgfile.old; then
  mv -f $TMP $cfgfile
  # Update /etc/modules.conf on Debian systems newer than 2.2
  test -f /etc/debian_version -a ! -f /etc/conf.modules -a -x /sbin/update-modules \
    && /sbin/update-modules >/dev/null 2>&1
  depmod -a 2>/dev/null
  farewell "OK, $CARD_NUM card(s) configured."
  echo ""
  echo Loading driver:
  $rcalsasound start
  echo Setting the PCM volume to 100% and the Master output volume to 50%
  if amixer set PCM 100% unmute
  then
    :
  else
    echo "Could not initialize the mixer, the card was probably"
    echo "not detected correctly."
    exit
  fi
  amixer set Master 100% unmute
  # ESS 1969 chipset has 2 PCM channels
  amixer set PCM,1 100% unmute                                  2>/dev/null
  # Trident chipsets have also Wave group on digital path:
  amixer set Wave 100% unmute					2>/dev/null
  # CS4237B chipset:
  amixer set 'Master Digital' 80% unmute			2>/dev/null
  # for some GUS soundcards
  amixer set Synth 50% unmute					2>/dev/null

  # Start alsamixer dialog
  if $DIALOG --yesno "
alsamixer is a interactive program to set volume bars
on your sound card. Left & right arrow keys are used to
select the channel (or device, depending on your preferred
terminology). You can also use n (next) and p (previous).

Up/down arrows control the volume for the currently selected
device. Both the left & right signals are controlled.
You can also use "+" or "-" to turn volumes up or down.

"M" toggles muting for the current channel (both left and right).
You can mute left and right independently by using , and . resp.

You can exit with ALT + Q, or by hitting ESC.

Do you need to set your miexer settings with alsamixer?
" 21 70
  then
        clear
        alsamixer
  fi

  # Play sample dialog
  if $DIALOG --yesno "
 PCM and Master channels should now be open for playing.
      Shall I try to play a sound sample now?

                           NOTE:
If you have a big amplifier, lower your volumes or say no.
    Otherwise check that your speaker volume is open,
           and look if you can hear Linus.
" 13 65
  then
	clear
	aplay /usr/share/sndconfig/sample.au
  fi
  if [ ! -r /etc/asound.conf ]; then
     echo "Saving the mixer setup used for this in /etc/asound.conf."
     echo "It will be restored when you load alsa with \"$rcalsasound start\"."
     echo "If you save your own mixer settings to this file, run \"alsactl store\"."
     /usr/sbin/alsactl store
  fi
cat <<END
===============================================================================

 You can use aplay to play audio files with ALSA, and almost
 any OSS Sound tool and Games out there should also work.

 When you run '$rcalsasound stop' your mixer settings will
 be saved to /etc/asound.conf.
 They will be restored on the next '$rcalsasound start'
 which is used to load ALSA.

 To use the alsa mixers, start X and call "xamixer2" or "gamix",
 press the Buttons below the 'Master' and 'PCM' bars if they
 are not already pressed to unmute the channels and raise the
 volumes of these settings if not done yet.

END
else
  echo "ERROR! $cfgfile could not be saved."
  echo "Configured ALSA is in $TMP."
fi
