#! /bin/sh
#
# dream-machine.sh
# A kind of alarm clock that can wake you up with an MP3 playlist, a CD or a
# set of MIDI files - Put this in your crontab !
#
# TODO : migrer tous les modules vers le nouveau système "status file"
# TODO : Infos (TV ? Stream MP3 ?)
#
# Copyright (c) 2000 Raphaël HALIMI <raph@captainblood.org>
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA

VERSION="1.1"

# First, some variables to make configuration easier - it is HIGHLY recommended
# to change these to suit your system ;-)

# Length of the music time. If you enable the "BUZZER" option below, the
# buzzer will start as soon as the play time is elapsed. In minutes.
MUSIC_TIME=60

# The duration of the "snooze" command. In minutes.
SNOOZE_TIME=10

# "MP3" for MP3s, "CD" for CDs and... "MIDI" for MIDIs :-)
MEDIA=MP3

# The command you use to reset the mixer. If your box plays music and
# the volume is set at 0, you'll have some problems to wake up :-)
MIXER_RESET="aumix -v 25"

# Your MP3 player.
MP3_PLAYER="mplayer"

# MP3 player options. Insert here the options you want or need
# for your MP3 player
MP3_PLAYER_OPTIONS='-really-quiet -nocache'

# It's a command use to produce a buzzer sound. Strongly recommended.
# If you don't want a buzzer, simply comment out this line.
# Download one at http://www.captainblood.org/scripts/buzzer.mp3
BUZZER_COMMAND="play /usr/share/sounds/buzzer.mp3"

# Your favourite MP3 playlist
# MP3_FILES="/mnt/mp3/00_Playlists/Big_Playlist.m3u"
MP3_FILES="http://vip2.yacast.fr/encoderfranceinfo"

# CD player configuration
CD_PLAYER=cdplay		# Command to play a CD
CD_PLAYER_OPTIONS=''		# CD player options
CD_STOPPER=cdstop		# Command to stop a CD

# MIDI player configuration
MIDI_PLAYER=drvmidi		# Your MIDI player
MIDI_PLAYER_OPTIONS='-i d'	# MIDI player options
MIDI_FILES="/home/raph/123.mid"	# MIDI file(s)

# You don't want to change these.
SOUND_PID_FILE=/tmp/dm_sound
SLEEP_FILE=/tmp/dm_sleep
LOCK_FILE=/tmp/dm_lock
DIALOG_TMPFILE=/tmp/dm_manager
LOG_FILE=/tmp/dm_log
STATUS_FILE=/tmp/dm_status
MAIN_OPTION="$1"
SEC_OPTION="$2"

# Last : we try to find the interface...
#if [ -n "$DISPLAY" -a -n `which Xdialog` ] ; then
#  DIALOG=Xdialog
#else
  DIALOG=dialog
#fi

# END OF OPTIONS

# Fire it up baby
echo "[$$] `date` : Invoked as $0 $*" >> $LOG_FILE


# Modules

# Print help
function print_help {
  echo "dream-machine.sh v$VERSION"
  echo "Usage : dream-machine.sh [option]"
  echo "Options (not compatible between each other ; first one is used) :"
  echo "  -p [minutes] : Mixer reset, play [minutes] then play buzzer"
  echo "  -m [minutes] : Same than \"-p\" but without buzzer (music only)"
  echo "  -l [minutes] : Same than \"-l\" but without mixer reset (sleeping mode)"
  echo "  -b : Buzzer only"
  echo "  -s : Stop"
  echo "  -n : Snooze $SNOOZE_TIME minutes"
  echo "  -h : Print this help message and exit"
  echo "  -d : Print built-in defaults and exit"
}

# Print defaults
function print_defaults {
  echo "Built-in defaults (edit script to change) :"
  echo "Playing time :		$MUSIC_TIME minute(s)"
  echo "Snooze time :		$SNOOZE_TIME minute(s)"
  echo "Media :			$MEDIA"
  echo "MP3 player :		$MP3_PLAYER"
  echo "Midi player :		$MIDI_PLAYER"
  echo "CD player :		$CD_PLAYER"
  echo "Mixer reset :		$MIXER_RESET"
  echo "MP3 Playlist :		$MP3_FILES"
  echo "Buzzer command :	$BUZZER_COMMAND"
}

# Waking up (the purpose of the script) is an ABSOLUTE PRIORITY. So, we want
# the sound device free by any means necessary
function free_device {
  MyIsUsed="`fuser /dev/dsp`"
  if [ "$MyIsUsed" ] ; then
    echo "Audio device busy, KILLING APPS"
    echo "Processes to kill :"
    MyKillPID=`echo $MyIsUsed | sed "s/^\/dev\/dsp: *//"`
    for I in $MyKillPID ; do
      MyLine=`ps fax | grep "^ *$I"`
      if [ "$MyLine" ] ; then
        echo -n $MyLine
        kill $I > /dev/null && echo " killed successfully" || echo " NOT KILLED"
      else
        echo "PID $I disappeared"
      fi
    done
  else
    echo "Audio device was free"
  fi
}

# The modules that plays music (MP3, CD or MIDI)
function dm_play {
  if [ $MEDIA = MP3 ] ; then
    $MP3_PLAYER $MP3_PLAYER_OPTIONS "$MP3_FILES" & > /dev/null 2>&1
    echo "$!" > $SOUND_PID_FILE
    echo "playing $MEDIA." > $STATUS_FILE
  elif [ $MEDIA = CD ] ; then
    $CD_PLAYER $CD_PLAYER_OPTIONS &
    echo "playing $MEDIA." > $STATUS_FILE
  elif [ $MEDIA = MIDI ] ; then
    $MIDI_PLAYER $MIDI_PLAYER_OPTIONS "$MIDI_FILES" &
    echo "$!" > $SOUND_PID_FILE
    echo "playing $MEDIA." > $STATUS_FILE
  fi
  echo "[$$] `date` : Starting music ($MEDIA)" >> $LOG_FILE
}

# The buzzer
function dm_buzzer {
  STATUS=`cat $STATUS_FILE`
    if [ "$BUZZER_COMMAND" ] ; then
      if [ "$STATUS" != "manually stopped." ] ; then
        dm_mixer_reset
        while true ; do
          $BUZZER_COMMAND
        done &
        echo "$!" > $SOUND_PID_FILE
        echo "playing buzzer." > $STATUS_FILE
      fi
    fi
echo "[$$] `date` : Starting buzzer" >> $LOG_FILE
}

# Kills music or buzzer
function dm_kill_sound {
  if [ -e $SOUND_PID_FILE ] ; then
    SOUND_PID="`cat $SOUND_PID_FILE`"
    if [ -d /proc/$SOUND_PID ] ; then
      kill $SOUND_PID
    fi
    rm -f $SOUND_PID_FILE
  fi
  STATUS=`cat $STATUS_FILE`
  if [ "$STATUS" = "playing buzzer." ] ; then
    echo "[$$] `date` : Stopped buzzer" >> $LOG_FILE
  else
    echo "[$$] `date` : Stopped music" >> $LOG_FILE
  fi
  echo "waiting for orders..." > $STATUS_FILE
}

# Handles the last one nicely
function dm_stop {
  STATUS=`cat $STATUS_FILE`
  if [ "$STATUS" = "playing buzzer." ] ; then
    dm_kill_sound
  elif [ "$STATUS" = "playing MP3." ] ; then
    dm_kill_sound
  elif [ "$STATUS" = "playing CD." ] ; then
    $CD_STOPPER
  elif [ "$STATUS" = "playing MIDI." ] ; then
    dm_kill_sound
  fi
}

# The snooze function
function dm_snooze {
  SOUND_PID="`cat $SOUND_PID_FILE`"
  if [ -d /proc/$SOUND_PID ] ; then
    kill -19 $SOUND_PID
  fi
  SNOOZE_TIME_SEC=$[$SNOOZE_TIME * 60]
  echo "[$$] `date` : Snooze on" >> $LOG_FILE
}

# Wake up after a snooze
function dm_wakeup {
  SOUND_PID="`cat $SOUND_PID_FILE`"
  if [ -d /proc/$SOUND_PID ] ; then
    kill -18 $SOUND_PID
  fi
  echo "[$$] `date` : Snooze off" >> $LOG_FILE
}

# Kill the script (used only when launched with --stop or --manager)
function dm_kill_running {
  if [ -e $LOCK_FILE ] ; then
    SCRIPT_PID=`cat $LOCK_FILE`
    if [ -d /proc/$SCRIPT_PID ] ; then
      kill -14 $SCRIPT_PID
    fi
  fi
  echo "[$$] `date` : Killed another instance" >> $LOG_FILE
}

# Resets the mixer
function dm_mixer_reset {
  $MIXER_RESET > /dev/null
}

# This is a sleep loop
function dm_wait {
  if [ "$MAIN_OPTION" ] ; then
    if [ "$SEC_OPTION" ] ; then
      MUSIC_TIME_SEC=$[$SEC_OPTION * 60]
    else
      MUSIC_TIME_SEC=$[$MUSIC_TIME * 60]
    fi
  else
    MUSIC_TIME_SEC=$[$MUSIC_TIME * 60]
  fi
  echo "[$$] `date` : Sleeping for $MUSIC_TIME_SEC seconds" >> $LOG_FILE
  sleep $MUSIC_TIME_SEC
  echo "[$$] `date` : Back to work" >> $LOG_FILE
}

# Tests presence of a lock file
function dm_testlock {
  if [ -f $LOCK_FILE ] ; then
    echo "Already running (pid `cat $LOCK_FILE`). Aborting."
    echo "[$$] `date` : Lock file detected (pid `cat $LOCK_FILE`)" >> $LOG_FILE
    echo "[$$] `date` : Quit (already running ?)" >> $LOG_FILE
    exit 1
  fi
}

# Creates lock file
function dm_lock {
  echo $$ > $LOCK_FILE
}

# Removes the lock file
function dm_unlock {
  rm -f $LOCK_FILE
}

# A normal playing function
function dm_complete_play {
  dm_testlock
  dm_lock
  dm_mixer_reset
  dm_play
  dm_wait
  dm_stop
  dm_unlock
}

# The same, with no mixer resetting and with some checks for the manager
function dm_sleep {
  dm_testlock
  dm_lock
  touch $SLEEP_FILE
  dm_play
  dm_wait
  dm_stop
  dm_unlock
  rm -f $SLEEP_FILE
}

# A normal stopping function
function dm_complete_stop {
  dm_stop
  dm_unlock
  if [ ! -e $SLEEP_FILE ] ; then
    dm_kill_running
  else
    rm -f $SLEEP_FILE
  fi
  echo "manually stopped." > $STATUS_FILE
}

# The manager dialog
function dm_manager {
  if [ ! -e $STATUS_FILE ] ; then
    echo "waiting for orders..." > $STATUS_FILE
  fi
  while true ; do
    sleep 1		# Dirty, but necessary :-/
    $DIALOG --clear --title "Dream Machine v$VERSION" \
      --backtitle "Captain Blood's Scripts : http://www.captainblood.org" \
      --menu "\nDream Machine currently `cat $STATUS_FILE`\nWhat do you want to do ?" 0 0 0 \
      "Sleep" "Play (no buzzer, don't reset mixer)" \
      "Stop" "Stop the Dream Machine" \
      "Snooze" "Sleep $SNOOZE_TIME more minute(s)" \
      "Mixer" "Audio mixer (volume)" \
      "Quit" "Quit the Dream Machine Manager" \
      2> $DIALOG_TMPFILE

  RETVAL=$?
  CHOICE=`cat $DIALOG_TMPFILE`

  case "$RETVAL" in
    0)
    case "$CHOICE" in
      "Sleep")
        dm_sleep
        ;;
      "Stop")
        dm_complete_stop
        ;;
      "Snooze")	# HELP : NOT WORKING WITH Xdialog
        dm_snooze
        $DIALOG --sleep $SNOOZE_TIME_SEC \
        --title "Snooze..." \
        --infobox "Sleep well..." 0 0
        dm_wakeup
        ;;
      "Mixer")
        aumix
        ;;
      "Quit")
        break
        ;;
    esac
    ;;
    1|255)
      break
    ;;
  esac
  done
  rm $DIALOG_TMPFILE
}

# Clean exit
function clean_exit {
  echo "[$$] `date` : Quit" >> $LOG_FILE
  exit $1
}

# This is the main program...
while getopts "plbsmnhdi" MyOption ; do
  case "$MyOption" in
    p)	# To wake up. Launch it from cron.
      free_device
      dm_complete_play
      dm_buzzer
      clean_exit
      ;;
    l) 	# To sleep : no mixer reset, no buzzer.
      dm_sleep
      clean_exit
      ;;
    m)	# Music only
      free_device
      dm_complete_play
      clean_exit
      ;;
    b)	# Buzzer only
      free_device
      dm_buzzer
      clean_exit
      ;;
    s)	# Stops an already started script
      dm_complete_stop
      clean_exit
      ;;
    n)	# Stops the music/buzzer for a while
      dm_snooze
      sleep $SNOOZE_TIME_SEC
      dm_wakeup
      clean_exit
      ;;
    h)	# Help 
      print_help
      clean_exit
      ;;
    d)
      print_defaults
      clean_exit
      ;;
    i)
      dm_manager
      clean_exit
      ;;
    ?)
      print_help
      clean_exit 1
      ;;
  esac
done

dm_manager
clean_exit 0
