#!/bin/sh
# the next line restarts using wishx \
exec wishx "$0" "$@"

#########################################################################
#
# tk3play 1.00 last edit: 04/22/97
#  
#  Copyright (C) 1997 Brian Foutz (bef2@cornell.edu)
#
#  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.
#
#
########################################################################

if {[info tclversion] < 7.6} {
   puts "You must have Tcl version 7.6 installed or"
   puts "try the statically linked version of tk3play."
   exit
}

if {[lsearch [info commands] pipe] == -1} {
   puts "You must have TclX version 7.6 installed or"
   puts "try the statically linked version of tk3play."
   exit
}

#
# Configuration Defaults
#
# You must set TK3DIR to the directory which contains the bitmaps,
# system.tk3playrc etc...
#

set TK3DIR /usr/lib/tk3play
source $TK3DIR/playedit.tcl

#########################################################################
#
# Start User Configurable Variables
#
#########################################################################
#
# Location of executables
#

set BINDIR /usr/bin

#
# tk3play will search this directory for playlists and mp3s if if can't
# find them in the current directory.  This also the directory the where the
# playlist editor starts.
#

set PLAYDIR ~/.tk3play

#
# If you have l3dec it will decode lower bitrate streams (64 kbit/s, 
# 56 kbit/s, 32 kbit/s, ...).  mpg123 will not.
# Set PLAYER to the one you want to use.  Tk3play's version 
# of mpg123 has been modified so its output is similar to l3dec.
# Other decoders won't work here unless they've been 
# modified as well.  (See proc player_parser{} for details)
# The last command in PLAYER should specify the flag which specifies
# the starting frame.
#

#set PLAYER "$BINDIR/l3dec -sto -ign -fb"
set PLAYER "$BINDIR/mpg123-m -v -s -k"

#
# Set the command to use to get information about a MP3
# For mpg123: -n 0 would be better but it doesn't output the 
# file info in this case.
#

#set INFOCOMMAND "$BINDIR/l3dec -fn 0"
set INFOCOMMAND "$BINDIR/mpg123-m -v -n 1"

# Show subsecs

set display(show_subsecs) 0

# Set buffer memory for bwavplay in bytes

set bufmem 500000

# Set how often bwavplay updates current time and buffer status
# in hundredth of a second.  Minimum resolution is around 3 hundredths
# since this how often the audio device gets written (at 44.1 kHz 16 bit).
# Lowering this number increases CPU usage.

set output_interval 50

# Pause which occurs when there is no other pause specified 
# (in the playlist).  This insures the buffer will contain 
# at least bgpause seconds of data (more if your machine is faster).
# Also, pauses less than bgpause will not show the negative time
# countdown.  (bgpause in specified in 1/100th of a second)

set state(bgpause) 50

#
# Autoplay on startup or auto-open playlist editor if no files are
# given on the command-line
# 1 - yes
# 0 - no
set state(autoplay) 0

# 
# Resize button sizes - add more or change these
# I've set this for my version of afterstep (pre5).  Other
# versions and other window managers may need different sizes here
#
set display(resize_list) {156x82 300x171}

# Fonts:
# sfont - the smallest font
# bfont - font for track and time
# rfont - bold font for frame/buffer numbers and pause
# mfont - for subsecs

set display(sfont) "-adobe-helvetica-medium-r-normal-*-10-*-*-*-*-*-*-*"
set display(rfont) "-adobe-helvetica-bold-r-*-*-12-*-*-*-*-*-*-*"
set display(mfont) "-adobe-times-medium-r-normal-*-24-*-*-*-*-*-*-*"
set display(bfont) "-adobe-times-medium-r-normal-*-34-*-*-*-*-*-*-*"

# if you're looking for a bigger display try these:

#set display(sfont) "-adobe-helvetica-medium-r-normal-*-14-*-*-*-*-*-*-*"
#set display(rfont) "-adobe-helvetica-bold-r-*-*-18-*-*-*-*-*-*-*"
#set display(mfont) "-adobe-times-medium-r-normal-*-24-*-*-*-*-*-*-*"
#set display(bfont) "-adobe-utopia-medium-r-normal-*-44-*-*-*-*-*-*-*"

# Colors:

set display(timecolor)   green
set display(stereocolor) red
set display(offcolor)    gray50
set display(oncolor)     orange
set display(songcolor)   yellow
set display(scalecolor)  gray65
set display(buttoncolor) gray85
set display(bgcolor)     black

#########################################################################
#
# End User Configurable section
#
#########################################################################

#
# Now read system and user .tk3playrc to change the above options
#

set file $TK3DIR/system.tk3playrc
if {[file isfile $file]} {
   source $file
}

set file ~/.tk3playrc
if {[file isfile $file]} {
   source $file
}

#
# Create the users default playlist directory if it doesn't exist
#

if {![file exists $PLAYDIR]} {
   mkdir $PLAYDIR
}
set PLAYDIR [glob $PLAYDIR]

##########################################################################
#
# Variables
# 
# The following tcl arrays are used to store the state of the player
#
# state( )
#  pipe_open, playing, no_update, current_song, bgpause, autoplay
#  playedit_open, csecs
# 
# display ( )
#  show_subsecs, sfont, rfont, mfont, bfont, timecolor, stereocolor
#  offcolor, oncolor, songcolor, scalecolor, buttoncolor, bgcolor
#  track, layer, outrate, inrate, mode, time, subsec, remain,
#  total, repeat, framenum, song, pl_name, buffer
#  scalenum, mouse_on_scale, resize_list, resize_number
#
# playlist( )
#  songlen_list, playlist_len, layer_list, outrate_list, inrate_list
#  mode_list, playlist, pause_list, filename
#
# fileinfo( )
#  valid, layer, outrate, inrate, mode
#
# other global variables:
#  TK3DIR, BINDIR, PLAYDIR, bufmem, output_interval, fwavplay, fplayer_r
#  fplayer_w, player_inbuf, starting_secs, bpipe, cdir, infopipe_r,
#  infopipe_w
#
##########################################################################
#
# Section 1. : Subprocess Control
#
# The procs in this section handle starting and stopping the 
# "maplay | bwavplay" pipeline and parse output from the pipeline
# to keep the display updated.
#
##########################################################################

#
# Parses the output from bwavplay
# which is used to update the buffer info and time. 
#

proc wavplay_handler { } {
   global fwavplay starting_secs .p2.scale
   upvar #0 state s
   upvar #0 display d

   if {![eof $fwavplay]} {
      set buf [gets $fwavplay]
      if {[string length $buf] != 0} {
         if {[string first "Error" $buf] != -1} {
            puts $buf
            exit
         }
         set i [string first "|" $buf]
         set wavsecs [string range $buf 0 [expr $i - 1]]
         set bufperc [string range $buf [expr $i + 1] [expr $i + 4]]
         if {$s(no_update) == 0} {
            set total_secs [expr $wavsecs + $starting_secs]
            set s(csecs) $total_secs
            update_time
            set d(buffer) "$bufperc%"
            .p2.scale set [secs2frames $total_secs]
         }
      }
   } else {
      set s(pipe_open) 0
      catch {close $fwavplay}
      if {$s(playing)} {goto_next}
   }
   return
}

#
# Parses the output from decoder and updates the current frame number
# being decoded.
#

proc player_parser { } {
   global fplayer_r .p2.scale player_inbuf
   upvar #0 state s
   upvar #0 display d

   if {![eof $fplayer_r]} {
      set buf [read $fplayer_r]
      set len [string length $buf]
      while {$len != 0} {
         append player_inbuf $buf

         # First see if we've read in the header
         set index [string last "frame no:" $player_inbuf]
         if {$index != -1} {
            # finished reading header, so remove from player_inbuf
            set player_inbuf [string range $player_inbuf [expr $index + 9] end]
         }

         # See if there is some frame info
         set index [string last "\b\b\b\b\b" $player_inbuf]
         if {$index != -1} {
            set sframe [expr $index - 5]
            set eframe [expr $index - 1]
            set fnum [string range $player_inbuf $sframe $eframe]
            if {!$s(no_update)} {
               set d(framenum) $fnum
            }
            set player_inbuf [string range $player_inbuf [expr $index + 5] end]
         }

         # Check for EOF message
         set index [string last "end" $player_inbuf]
         if {$index == -1} {
            set index1 [string last "EOF" $player_inbuf]
         }
         if {$index != -1} {
            set player_inbuf [string range $player_inbuf [expr $index + 4] end]
            # set framenum "EOF"
         }

         # Read some more data
         # we pause here to keep tk3play from updating the display
         # after reading in each frame during a forward or backward
         # jump.
         after 50
         set buf [read $fplayer_r]
         set len [string length $buf]
      }
   } else {
      # close the pipe, this resets the filehandler as well
      close $fplayer_r
      set s(pipe_open) 0
      puts "EOF on decoder"
      # if {$s(playing)} {goto_next}
   }
   return
}

#
# Plays the current song starting at frame $fstart
#

proc play {fstart {pause -1}} { 
   global .p2.scale BINDIR fwavplay fplayer_r fplayer_w PLAYDIR
   global starting_secs bufmem output_interval PLAYER
   upvar #0 state s
   upvar #0 display d
   upvar #0 playlist p

   if {$s(current_song) == -1} return
   if {$pause == -1} {set pause $s(bgpause)}
   if {$s(pipe_open) == 1} {
      return
   } else {
      set s(playing) 1
      set filename [lindex $p(playlist) $s(current_song)]
      set play_speed [lindex $d(outrate) 0]
      set play_speed [expr $play_speed * 1000]
      set soption "-S"
      if {$d(mode) == "Mono"} {set soption " "}
         
      set s(no_update) 0
      set s(pipe_open) 1
      set starting_secs [frames2secs $fstart]

      # If the filename has spaces we create a symlink
      # (with no spaces in the name) so l3dec can handle it. (Ha!)
      if {[string first " " \"$filename\"] != -1} {
         exec ln -sf $filename $PLAYDIR/tmplink
         set filename $PLAYDIR/tmplink
      }

      # Play a mp3 file
      set fwavplay [open "| echo 'n' \
         | $PLAYER $fstart \"$filename\" 2>@$fplayer_w \
         | $BINDIR/bwavplay -q -r -b 16 $soption -s $play_speed \
            -m $bufmem -o $output_interval -p $pause" r]
      fconfigure $fwavplay -blocking 0
      fileevent $fwavplay readable {wavplay_handler}
   }
   return
}

#
# Close down the pipeline, when it returns bwavplay as been
# killed and pipe_open is set to false
#

proc stop { } {
   global fwavplay
   upvar #0 state s

   if {$s(pipe_open) == 0} {
      return
   } else {
      set s(playing) 0
      # This kills bwavplay directly and instantly stops the sound.
      exec kill [lindex [pid $fwavplay] 2]
      # wait for bwavplay to stop playing
      # which means the eof has triggered the handler to close
      if {$s(pipe_open)} {
         vwait state(pipe_open)
      }
   }
   return
}

##########################################################################
#
# Section 2. : Utilities 
#
##########################################################################

#
# Updates the time on the display
#

proc update_time { } {
   upvar #0 state s
   upvar #0 display d
   upvar #0 playlist p

   set songlen [lindex $p(songlen_list) $s(current_song)]
   set in_songs 0
   if {$d(total)} {
      for {set i 0} {$i < $s(current_song)} {incr i} {
         set in_songs [expr $in_songs + [lindex $p(songlen_list) $i]]
      }
   }

   if {$d(remain)} {
      if {$d(total)} {
         set allsecs [expr $p(playlist_len) - $in_songs - $s(csecs)]
      } else {
         set allsecs [expr $songlen - $s(csecs)]
      }
   } else {
      if {$d(total)} {
         set allsecs [expr $in_songs + $s(csecs)]
      } else {
         set allsecs $s(csecs)
      }
   }

   set i [string last "." $allsecs]
   set d(subsec) [string range $allsecs [expr $i + 1] [expr $i + 1]]
   set total_sec [string range $allsecs 0 [expr $i - 1]]
   if {$allsecs < 0} {
      set negative 1
      set total_sec [expr - $allsecs + 0.99]
   } elseif {$allsecs == 0} {
      set negative 0
      set total_sec [expr - $allsecs + 0.99]
   } else {
      set negative 0
   }
   set mins [expr floor( $total_sec / 60 )]
   set secs [expr $total_sec - $mins * 60]
   set min [expr round($mins)]
   set sec [expr round(floor($secs))]
   if {$sec < 10} {
      set sec 0$sec
   }
   if {$negative} {
      set curr_pause [lindex $p(pause_list) $s(current_song)]
      if {$curr_pause > $s(bgpause)} {
         set d(time) -$min:$sec
      } else {
         set d(time) "0:00"
      }
   } else {
      set d(time) $min:$sec
   }
}

#
# Initializes the display based on current_song info
#

proc init_display { } {
   global .p2.scale
   global .p1.display.right.mode.first.mode
   upvar #0 state s
   upvar #0 display d
   upvar #0 playlist p

   set t .p1.display.right.mode.first.mode
   set d(track) [expr $s(current_song) + 1]
   set tmp [lindex $p(playlist) $s(current_song)]
   set d(song) [string toupper [file tail $tmp]]
   set d(buffer) "0.00%"
   set d(layer) [lindex $p(layer_list) $s(current_song)]
   set d(outrate) [lindex $p(outrate_list) $s(current_song)]
   set d(inrate) [lindex $p(inrate_list) $s(current_song)]
   set d(mode) [lindex $p(mode_list) $s(current_song)]
   if {$d(mode) == "Stereo"} {
     $t config -fg $d(stereocolor)
   } else {
     $t config -fg $d(timecolor)
   }
   set allsecs [lindex $p(songlen_list) $s(current_song)]
   set d(framenum) [secs2frames $allsecs]
   .p2.scale config -to $d(framenum)
   .p2.scale set 0
   set s(csecs) 0.0
   update_time
}

proc at_playlist_end {} {
   upvar #0 state s
   upvar #0 playlist p

   if {$s(current_song) == [expr [llength $p(playlist)] - 1]} {
      return 1
   } else {
      return 0
   }
}

proc at_playlist_start {} {
   upvar #0 state s

   if {$s(current_song)} {
      return 0
   } else {
      return 1
   }
}

#
# Decides what song to play next when the player has reached the end
# of the current song; then plays it.
#

proc goto_next {} {
   upvar #0 state s
   upvar #0 playlist p
   upvar #0 display d

   set finished 0
   # select the next song based on the state of d(repeat)
   # Normal: goto the next song
   # Repeat All: repeat the playlist
   # Repeat Track: means repeat current song.
   # Shuffle: randomly select the song

   switch $d(repeat) {
      "Normal" {
        if {[at_playlist_end]} {
            set finished 1
         } else {
            set s(current_song) [expr $s(current_song) + 1]
         }
      }
      "Repeat All" {
         if {[at_playlist_end]} {
            set s(current_song) 0
         } else {
            set s(current_song) [expr $s(current_song) + 1]
         }
      }
      "Shuffle" { set_rand_song }
      "Repeat Track" { }
      "default" { error "Error in switch" }
   }

   if {$s(playing)} {
      if {$s(pipe_open)} {stop}
      if {!$finished} {
         init_display
         set pause [lindex $p(pause_list) $s(current_song)] 
         play 0 $pause
      } else {
         set s(current_song) 0
         set s(playing) 0
         init_display
      }
   }
   return
}

#
# Runs maplay decoding 0 frames and parses the output to
# determine the type of mp3.
#

proc get_song_info {filename} {
   global infopipe_w infopipe_r INFOCOMMAND PLAYDIR
   upvar #0 fileinfo f

   # Get mpeg info
   set extension [file extension $filename]
   if {[string last "mp" $extension] == -1 &&
       [string last "MP" $extension] == -1} {
      set f(valid) 0
      return
   }

   set temp [lindex [glob -nocomplain $filename] 0]

   # If the filename has spaces we create a symlink
   # (with no spaces in the name) so l3dec can handle it. (Ha!)
   if {[string first " " \"$temp\"] != -1} {
      exec ln -sf $temp $PLAYDIR/tmplink
      set temp $PLAYDIR/tmplink
   }

   catch {
      eval exec echo 'n' | $INFOCOMMAND \"$temp\" 2>@$infopipe_w >/dev/null
   }

   set buf [gets $infopipe_r]
   set len [string length $buf]
   set index -1
   while {($index == -1) && (![eof $infopipe_r])} {
      append inbuf $buf
#      puts $buf
      # First see if we've read in the header
      set index [string last "frame no:" $inbuf]
      if {$index != -1} {
         set f(valid) 1
         set istart [string first "Now decoding" $inbuf]
         set blist [string range $inbuf $istart end]
         set word [lindex $blist 3]
         set f(layer) [string range $word 0 [expr [string length $word] - 2]]
         set word "[lindex $blist 4] [lindex $blist 5]"
         set f(outrate) [string range $word 0 [expr [string length $word] - 2]]
         set word "[lindex $blist 6] [lindex $blist 7]"
         set f(inrate) [string range $word 0 [expr [string length $word] - 2]]
         set word "[lindex $blist 8] [lindex $blist 9]"
         
         if {[string match "*tereo*" $word]} {
            set f(mode) "Stereo"
         } elseif {[string match "*dual*" $word]} {
            set f(mode) "Stereo"
         } else {
            set f(mode) "Mono"
         }
      } else {
         set index1 [string last "no header" $inbuf]
         if {$index1 == -1} {set index1 [string last "Internal error:" $inbuf]}
         if {$index1 == -1} {set index1 [string last "not supported" $inbuf]}
         if {$index1 == -1} {set index1 [string last "finished" $inbuf]}
         if {$index1 == -1} {set index1 [string last "EOF" $inbuf]}
         if {$index1 != -1} {
            set f(valid) 0
            return
         }
      }
      if {$index == -1} {
         set buf [gets $infopipe_r]
         # set buf [read $infopipe_r 10]
         set len [string length $buf]
      }
   }
   if {$index == -1} {
      set f(valid) 0
   } else {
      set f(valid) 1
   }
   return
}


#
# Adds file to the player playlist
# Normally add file to the regular playlist, but when building a 
# new playlist build a temporary one first.
#

proc playlist_add {file pause {pl playlist}} {
   upvar #0 state s
   upvar #0 $pl p
   upvar #0 fileinfo f

   get_song_info "$file"
   if {$f(valid)} {
      lappend p(playlist) "$file"
      lappend p(layer_list) $f(layer)   
      lappend p(outrate_list) $f(outrate)
      lappend p(inrate_list) $f(inrate)
      lappend p(mode_list) $f(mode)
      lappend p(pause_list) $pause
      set len [file size $file]
      set kbitrate [lindex $f(inrate) 0]
      set byterate [expr $kbitrate * 125]
      set song_len [expr $len / $byterate]
      lappend p(songlen_list) $song_len
      if {[info exists p(playlist_len)]} {
         set p(playlist_len) [expr $p(playlist_len) + $song_len]
      } else {
         set p(playlist_len) $song_len
         if {$pl == "playlist"} {
            set s(current_song) 0
            init_display
         }
      }
   } else {
      puts "$file appears to be an invalid mpeg, skipping..."
   }
}

#
# Determines the layer type, encoding rate, output rate, song length, 
# and whether the file is stereo for each song in the playlist.
#

proc process_playlist {listfile} {
   upvar #0 state s
   upvar #0 playlist p
   upvar #0 display d
   upvar #0 temp t

   set t(playlist_len) 0 
   set t(playlist) ""
   set filespec ""
   set fp [open $listfile r]
   set good 1
   while {![eof $fp] && $good} {
      set playline [gets $fp]
      set length [llength $playline]
      if {$length > 1} {
         if {[scan "$playline" "%d" pause] == 1} {
	    # Pause value present
	    # Scan again to find the starting index of the filename
            if {[scan $playline "%d %s" spause string1] != 2} {
               puts "error in playlist $listfile : line = $playline"
               set good 0
            }
            set starti [string first $string1 "$playline"]
	    set filespec [string range "$playline" $starti end]
         } else {
	    # Set filespec = entire line (w/ spaces)
 	    set filespec "$playline"
            set pause $s(bgpause)
         }
      } elseif {$length == 1} {
         # Set filespec = entire line (no spaces)
         set filespec "$playline"
         set pause $s(bgpause)
      } else {
         # length = 0  -blank line -skip
         continue
      }

      # OK should have a valid filespec and pause value

      if {$good} {
         set filelist [lsort [glob -nocomplain "$filespec"]]
         if {[llength $filelist] == 0} {
            puts "error in playlist $listfile: line =  $playline"
            set good 0
         }

         foreach filename $filelist {
            if {[file isfile "$filename"]} {
                playlist_add "$filename" $pause temp
            } else {
#               puts "in else filename = $filename"
               # I'm not sure why its "." ?
               if {$filename != "."} {
                  puts "tk3play: Playlist error - $filename is not valid"
                  exit
               }
            }
         }
      }
   }
   close $fp
   if {$t(playlist_len) != 0} {
      if {$s(pipe_open)} {stop}
      # Copy the temp playlist into the real one.
      set p(songlen_list) $t(songlen_list)
      set p(playlist_len) $t(playlist_len)
      set p(layer_list) $t(layer_list)
      set p(outrate_list) $t(outrate_list)
      set p(inrate_list) $t(inrate_list)
      set p(mode_list) $t(mode_list)
      set p(playlist) $t(playlist)
      set p(pause_list) $t(pause_list)
      # Clear temp playlist
      unset t(songlen_list)
      unset t(playlist_len)
      unset t(layer_list)
      unset t(outrate_list)
      unset t(inrate_list)
      unset t(mode_list)
      unset t(playlist)
      unset t(pause_list)
      set d(pl_name) [string toupper [file tail $listfile]]
      set s(current_song) 0
      init_display
   } else {
      puts "Playlist $listfile contained invalid entries"
   }
}

proc open_file {file} {
   global PLAYDIR
   upvar #0 state s
   upvar #0 display d
   upvar #0 playlist p
   upvar #0 fileinfo f

   if {![file isfile "$file"]} {
      if {![file isfile "$PLAYDIR/$file"]} {
         puts "tk3play: File $file not found"
         puts {Usage: tk3play [file]}
         exit
      } else {
         set file "$PLAYDIR/$file"
      }
   }

   get_song_info "$file"
   if {$f(valid)} {
      playlist_add "$file" $s(bgpause)
   } else {
      process_playlist "$file"
      set p(filename) $file
   }
}

#
# Utility to convert from frame no. (as output by maplay) to seconds.
# 

proc frames2secs { frames } {
   upvar #0 display d

   if {[lindex $d(inrate) 0] < 48} {
      set secs [expr $frames / 19.14]
   } else {
      set secs [expr $frames / 38.28]
   }
   return $secs
}

#
# Utility to convert from seconds to frame no.
#

proc secs2frames { secs } {
   upvar #0 display d

   if {[lindex $d(inrate) 0] < 48} {
      set frames [expr $secs * 19.14]
      set frames [expr round(floor($frames))]
   } else {
      set frames [expr $secs * 38.28]
      set frames [expr round(floor($frames))]
   }
   return $frames
}

#
# Select a song from the playlist at random (for shuffle play)
#

proc set_rand_song { } {
   upvar #0 state s
   upvar #0 playlist p

   set len [llength $p(playlist)]
   set s(current_song) [random $len]
   return
}


##########################################################################
#
# Section 3. : Callbacks
#
# Procs called when the user does something (presses a button, slides
# the scale, etc...) 
#
##########################################################################


#
# When the user presses the "next" or "prev" buttons, 
# a 1 second alarm is set to allow the user to hit the button again
# before actually jumping to the song.  This proc is called when
# the alarm is rung to play the newly selected song.
# 

proc alarm_handler { } {
   upvar #0 state s

   if {$s(pipe_open)} {stop}
   play 0
   set s(no_update) 0
   return
}

proc blink_time { color1 color2 } {
   global .p1.display.left.time.time
   upvar #0 state s
   upvar #0 display d

   set w .p1.display.left.time.time
   if {$s(paused)} {
      $w config -fg $color1
      after 500 [list blink_time $color2 $color1]
   } else {
      $w config -fg $d(timecolor)
   }
}
   
proc push_next { } {
   upvar #0 state s

   if {$s(current_song) == -1} {return}
   if {![at_playlist_end]} {
      set s(current_song) [expr $s(current_song) + 1]
      init_display
      if {$s(playing)} {
         alarm 1
         set s(no_update) 1
      }
   }
   return
}

proc push_prev { } {
   upvar #0 state s

   if {$s(current_song) == -1} {return}
   if {![at_playlist_start]} {
      set s(current_song) [expr $s(current_song) - 1]
      init_display
      if {$s(playing)} {
         alarm 1
         set s(no_update) 1
      }
   }
   return
}

proc push_play { } {
   global .p2.scale
   upvar #0 state s

   if {$s(playing)} {return}
   if {$s(current_song) != -1} {play [.p2.scale get]}
}

proc push_stop { } {
   upvar #0 state s
   if {$s(current_song) != -1} {
      stop
      init_display
   }
}

proc scale_release { } {
   upvar #0 state s
   upvar #0 display d

   set s(no_update) 0
   if {$d(mouse_on_scale) && $s(playing)} {
      stop
      play $d(scalenum)
   }
   return
}

proc toggle_pause { } {
   global fwavplay
   upvar #0 state s
   upvar #0 display d
   if {!$s(pipe_open)} {return}
   if {$s(paused)} {
      exec kill -s 18 [lindex [pid $fwavplay] 2]
      set s(paused) 0
   } else {
      exec kill -s 19 [lindex [pid $fwavplay] 2]
      set s(paused) 1
      blink_time $d(timecolor) $d(bgcolor)
   }
}

proc toggle_resize { } {
   upvar #0 display d

   set d(resize_number) [expr $d(resize_number) + 1]
   if {$d(resize_number) == [llength $d(resize_list)]} {
      set d(resize_number) 0
   }

   
   set geom [lindex $d(resize_list) $d(resize_number)]
   set curr_geom [wm geometry .]
   set sindex1 [string first + $curr_geom]
   set sindex2 [string first - $curr_geom]
   if {$sindex1 == -1} {set sindex $sindex2} \
   elseif {$sindex2 == -1} {set sindex $sindex1} \
   elseif {$sindex1 < $sindex2} {set sindex $sindex1} \
   else {set sindex $sindex2}
   set pos [string range $curr_geom $sindex end]
   wm geometry . $geom$pos

   return
}

proc toggle_repeat { } {
   global .p1.display.right.mode.third.repeat
   upvar #0 display d

   set t ".p1.display.right.mode.third.repeat"
   switch $d(repeat) {
      "Normal" {set d(repeat) "Repeat All"}
      "Repeat All" {set d(repeat) "Repeat Track"}
      "Repeat Track" {set d(repeat) "Shuffle"}
      "Shuffle" {set d(repeat) "Normal"}
   }
   return
}

proc toggle_remain { } {
   global .p1.display.left.time.bottom.remain
   upvar #0 display d

   set t ".p1.display.left.time.bottom.remain"
   if {$d(remain)} {
      $t config -fg gray50
      $t config -activeforeground gray50
      set d(remain) 0
   } else {
      $t config -fg orange
      $t config -activeforeground orange
      set d(remain) 1
   }
   update_time
}

proc toggle_total { } {
   global .p1.display.left.time.bottom.total
   upvar #0 display d

   set t ".p1.display.left.time.bottom.total"
   if {$d(total)} {
      $t config -fg gray50
      $t config -activeforeground gray50
      set d(total) 0
   } else {
      $t config -fg orange
      $t config -activeforeground orange
      set d(total) 1
   }
   update_time
}

proc update_scale { fnum } {
   upvar #0 state s
   upvar #0 display d

   if {$s(pipe_open)} {
      set d(scalenum) $fnum
   }
   if {$s(no_update) && $s(current_song) != -1} {
      set d(scalenum) $fnum
      set d(framenum) $fnum
      set s(csecs) [frames2secs $fnum]
      update_time
   }
   return
}

proc about { } {
   global TK3DIR version

   toplevel .a
   wm title .a "About Tk3Play"
   label .a.l1 -bitmap @$TK3DIR/title.xbm
   label .a.l2 -text "Copyright: Brian Foutz, 1997"
   label .a.l3 -text "Version $version"
   button .a.l4 -text "OK" -command "set button .a.13"
   pack .a.l1 .a.l2 .a.l3 .a.l4 -padx 10 -pady 5 -side top

   bind .a <Return> ".a.l3 flash; set button .a.l3"
   set oldFocus [focus]
   grab set .a
   focus .a
   tkwait variable button
   destroy .a
   focus $oldFocus
   return
}

proc say_goodbye { } {
#   puts "CYA!"
   exit 0
}

#######################################################################
#
# Section 4. : Display Definition
#
#######################################################################
# Main display window

proc pack_display { } {
   global TK3DIR PlayList
   upvar #0 state s
   upvar #0 display d

   frame .p1
   frame .p1.display -bg $d(bgcolor)
   label .p1.song -bg $d(bgcolor) -fg $d(songcolor) \
      -textvariable display(song) \
      -anchor w -font $d(sfont)
   label .p1.playlist -bg $d(bgcolor) -fg $d(songcolor) \
      -textvariable display(pl_name) -anchor w -font $d(sfont)
   pack .p1 -side top -fill x
   pack .p1.display -side top -fill both -expand 1
   pack .p1.playlist .p1.song -side bottom -ipadx 1m -fill x -expand 1

   # Do left part of display
   set t .p1.display.left
   frame $t -bg $d(bgcolor)
   pack $t -side left -fill y -expand 1

   # Do Track
   set t .p1.display.left.track
   frame $t -bg $d(bgcolor)
   label $t.top -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -text "TRACK"
   label $t.number -bg $d(bgcolor) -fg $d(timecolor) -font $d(bfont) \
      -textvariable display(track) -width 3 -anchor e
   button $t.resize -text "Resize" -bg $d(bgcolor) \
      -fg $d(oncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(oncolor) -relief flat \
      -highlightbackground $d(bgcolor) -bd 0 -padx 0 \
      -font $d(sfont) -command {toggle_resize}

   pack $t -side left -fill y -expand 1
   pack $t.top -side top
   pack $t.number -side top
   pack $t.resize -side bottom

   # Do Time
   set t .p1.display.left.time
   frame $t -bg $d(bgcolor)
   frame $t.bottom -bg $d(bgcolor)
   label $t.top -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -text "       MIN       SEC "
   if {$d(show_subsecs)} {
      label $t.subsec -bg $d(bgcolor) -fg $d(timecolor) -font $d(mfont) \
          -textvariable display(subsec) -anchor sw -width 1
   }
   label $t.time -bg $d(bgcolor) -fg $d(timecolor) -font $d(bfont) \
      -textvariable display(time) -anchor se -width 6
   button $t.bottom.remain -text "Remaining" -bg $d(bgcolor) \
      -fg $d(offcolor) -activebackground $d(bgcolor) \
      -activeforeground $d(offcolor) -relief flat \
      -highlightbackground $d(bgcolor) -bd 0 -padx 0 \
      -font $d(sfont) -command {toggle_remain}
   button $t.bottom.total -text "Total" -bg $d(bgcolor) -fg $d(offcolor) \
      -activebackground $d(bgcolor) -activeforeground $d(offcolor) \
      -relief flat -highlightbackground $d(bgcolor) -bd 0 -padx 0 \
      -font $d(sfont) -command {toggle_total}
   pack $t -side left -fill both -expand 1
   pack $t.top -side top -fill x -expand 1
   if {$d(show_subsecs)} {
      pack $t.subsec -side right -fill y -expand 1
   }
   pack $t.time -side top -fill x -expand 1
   pack $t.bottom -side bottom -fill x -expand 1
   pack $t.bottom.remain $t.bottom.total -side right

   # Do Right part of display
   set t .p1.display.right
   frame $t -bg black
   pack $t -side right -fill y -expand 1

   # Do Frame and Buffer info
   set t .p1.display.right.fb
   frame $t -bg $d(bgcolor)
   label $t.fl -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -text "FRAME" -width 7 -anchor e
   label $t.frame -bg $d(bgcolor) -fg $d(timecolor) -font $d(rfont) \
      -textvariable display(framenum) -width 7 -anchor e
   label $t.bl -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -text "BUFFER" -width 7 -anchor e
   label $t.buffer -bg $d(bgcolor) -fg $d(timecolor) -font $d(rfont) \
      -textvariable display(buffer) -width 7 -anchor e
   pack $t -side left -fill y -expand 1
   pack $t.fl $t.frame $t.bl $t.buffer -side top -fill x -expand 1

   # Do mode info
   set t .p1.display.right.mode
   frame $t -bg $d(bgcolor)
   label $t.top -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -text "MODE" -width 14
   frame $t.first -bg $d(bgcolor)
   frame $t.second -bg $d(bgcolor)
   frame $t.third -bg $d(bgcolor)
   label $t.first.outrate -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -textvariable display(outrate)
   label $t.first.mode -bg $d(bgcolor) -fg $d(stereocolor) \
      -font $d(sfont) -textvariable display(mode)
   label $t.second.inrate -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -textvariable display(inrate)
   label $t.second.layer -bg $d(bgcolor) -fg $d(timecolor) -font $d(sfont) \
      -textvariable display(layer)
   button $t.third.repeat -textvariable display(repeat) -bg $d(bgcolor) \
      -fg $d(oncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(oncolor) -relief flat \
      -highlightbackground $d(bgcolor) -bd 0 -padx 0 \
      -font $d(sfont) -command {toggle_repeat}
   pack $t -side right -fill y -expand 1
   pack $t.top -side top -fill x -expand 1
   pack $t.first -side top -fill x -expand 1
   pack $t.second -side top -fill x -expand 1
   pack $t.third -side top -fill x -expand 1
   pack $t.first.outrate -side left
   pack $t.first.mode -side right
   pack $t.second.inrate -side left
   pack $t.second.layer -side right -fill x -expand 1
   pack $t.third.repeat -ipadx 0 -padx 0 -fill x -expand 1 -side left

   frame .p2 -bg $d(bgcolor)
   scale .p2.scale -orient horizontal -from 0 -to 1000 -bg $d(scalecolor) \
      -activebackground $d(buttoncolor) -troughcolor $d(bgcolor) \
      -relief flat -highlightbackground $d(bgcolor) -width 12
   .p2.scale config -showvalue 0
   .p2.scale config -command {update_scale}
   bind .p2.scale <ButtonPress-1> {set state(no_update) 1}
   bind .p2.scale <ButtonRelease-1> {scale_release}
   bind .p2.scale <Enter> {set display(mouse_on_scale) 1}
   bind .p2.scale <Leave> {set display(mouse_on_scale) 0}
   pack .p2 -fill x -expand 1
   pack .p2.scale -side left -fill x -expand 1

   frame .p3 -bg $d(bgcolor)
   button .p3.prev -bitmap @$TK3DIR/prev.xbm -bg $d(bgcolor) \
      -fg $d(buttoncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(buttoncolor) -relief flat \
      -highlightbackground $d(bgcolor) -command {push_prev}
   button .p3.stop -bitmap @$TK3DIR/close.xbm -bg $d(bgcolor) \
      -fg $d(buttoncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(buttoncolor) -relief flat \
      -highlightbackground $d(bgcolor) -command {push_stop}
   button .p3.pause -bitmap @$TK3DIR/pause.xbm -bg $d(bgcolor) \
      -fg $d(buttoncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(buttoncolor) -relief flat \
      -highlightbackground $d(bgcolor) -command {toggle_pause}
   button .p3.play -bitmap @$TK3DIR/play.xbm -bg $d(bgcolor) \
      -fg $d(buttoncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(buttoncolor) -relief flat \
      -highlightbackground $d(bgcolor) -command {push_play}
   button .p3.next -bitmap @$TK3DIR/next.xbm -bg $d(bgcolor) \
      -fg $d(buttoncolor) -activebackground $d(bgcolor) \
      -activeforeground $d(buttoncolor) -relief flat \
      -highlightbackground $d(bgcolor) -command {push_next}
   button .p3.shuffle -text "Quit" -bg $d(bgcolor) -fg $d(buttoncolor) \
      -activebackground $d(bgcolor) -activeforeground $d(buttoncolor) \
      -relief flat -highlightbackground $d(bgcolor) \
      -command {say_goodbye}
   button .p3.playlist -text "Playlist" -bg $d(bgcolor) -fg $d(buttoncolor) \
      -activebackground $d(bgcolor) -activeforeground $d(buttoncolor) \
      -relief flat -highlightbackground $d(bgcolor) \
      -command {PlayList(playedit)}
   pack .p3 -side bottom
   pack .p3.prev .p3.stop .p3.pause .p3.play .p3.next -side left
   pack .p3.shuffle .p3.playlist -side right
}

wm title . "tk3play"
wm iconname . "tk3play"
. config -bg $display(bgcolor)

########################################################################
#
# Section 5. : Initialization
#
# Don't change this unless you know what you're doing.
#
########################################################################

pipe infopipe_r infopipe_w
pipe fplayer_r fplayer_w
fconfigure $fplayer_r -blocking 0
fileevent $fplayer_r readable {player_parser}
signal trap SIGALRM alarm_handler

pack_display

set version 0.97
set state(playing) 0
set state(pipe_open) 0
set state(no_update) 0
set state(current_song) -1
set state(paused) 0
set state(playedit_open) 0
set display(total) 0
set display(remain) 0
set display(repeat) "Normal"
set display(resize_number) 0
random seed

toggle_resize

foreach i $argv {
   open_file $i
}

if {$state(autoplay)} {
   if {[info exists playlist(playlist)]} {
      play 0
   } else {
      PlayList(playedit)
   }
}
