head	1.43;
access;
symbols;
locks
	greg:1.43; strict;
comment	@# @;


1.43
date	96.02.02.09.13.01;	author greg;	state Exp;
branches;
next	1.42;

1.42
date	95.12.09.07.48.50;	author greg;	state Exp;
branches;
next	1.41;

1.41
date	95.12.08.11.29.48;	author greg;	state Exp;
branches;
next	1.40;

1.40
date	95.12.05.10.36.20;	author greg;	state Exp;
branches;
next	1.39;

1.39
date	95.12.01.09.10.29;	author greg;	state Exp;
branches;
next	1.38;

1.38
date	95.11.09.09.37.12;	author greg;	state Exp;
branches;
next	1.37;

1.37
date	95.11.07.17.32.16;	author greg;	state Exp;
branches;
next	1.36;

1.36
date	95.11.07.17.31.50;	author greg;	state Exp;
branches;
next	1.35;

1.35
date	95.08.08.17.05.19;	author greg;	state Exp;
branches;
next	1.34;

1.34
date	95.07.24.17.11.08;	author greg;	state Exp;
branches;
next	1.33;

1.33
date	95.07.18.16.58.48;	author greg;	state Exp;
branches;
next	1.32;

1.32
date	95.07.17.22.37.22;	author greg;	state Exp;
branches;
next	1.31;

1.31
date	95.07.17.21.15.01;	author greg;	state Exp;
branches;
next	1.30;

1.30
date	95.07.17.18.48.07;	author greg;	state Exp;
branches;
next	1.29;

1.29
date	95.07.17.17.51.27;	author greg;	state Exp;
branches;
next	1.28;

1.28
date	95.07.14.15.45.41;	author greg;	state Exp;
branches;
next	1.27;

1.27
date	95.07.13.07.45.16;	author greg;	state Exp;
branches;
next	1.26;

1.26
date	95.07.13.03.05.12;	author greg;	state Exp;
branches;
next	1.25;

1.25
date	95.07.13.01.34.24;	author greg;	state Exp;
branches;
next	1.24;

1.24
date	95.07.12.18.18.30;	author greg;	state Exp;
branches;
next	1.23;

1.23
date	95.07.08.02.36.14;	author greg;	state Exp;
branches;
next	1.22;

1.22
date	95.07.07.21.52.50;	author greg;	state Exp;
branches;
next	1.21;

1.21
date	95.07.07.18.04.22;	author greg;	state Exp;
branches;
next	1.20;

1.20
date	95.07.07.02.22.58;	author greg;	state Exp;
branches;
next	1.19;

1.19
date	95.07.06.08.48.14;	author greg;	state Exp;
branches;
next	1.18;

1.18
date	95.07.02.06.58.48;	author greg;	state Exp;
branches;
next	1.17;

1.17
date	95.06.30.17.30.30;	author greg;	state Exp;
branches;
next	1.16;

1.16
date	95.06.30.08.18.03;	author greg;	state Exp;
branches;
next	1.15;

1.15
date	95.06.29.03.01.47;	author greg;	state Exp;
branches;
next	1.14;

1.14
date	95.06.28.23.01.31;	author greg;	state Exp;
branches;
next	1.13;

1.13
date	95.06.27.15.30.52;	author greg;	state Exp;
branches;
next	1.12;

1.12
date	95.06.27.11.25.12;	author greg;	state Exp;
branches;
next	1.11;

1.11
date	95.06.20.07.06.35;	author greg;	state Exp;
branches;
next	1.10;

1.10
date	95.06.14.18.02.44;	author greg;	state Exp;
branches;
next	1.9;

1.9
date	95.06.11.18.39.10;	author greg;	state Exp;
branches;
next	1.8;

1.8
date	95.06.11.03.10.49;	author greg;	state Exp;
branches;
next	1.7;

1.7
date	95.06.10.16.32.21;	author greg;	state Exp;
branches;
next	1.6;

1.6
date	95.06.08.20.30.22;	author greg;	state Exp;
branches;
next	1.5;

1.5
date	95.05.26.21.34.17;	author root;	state Exp;
branches;
next	1.4;

1.4
date	95.05.06.19.00.59;	author root;	state Exp;
branches;
next	1.3;

1.3
date	95.05.03.22.03.13;	author root;	state Exp;
branches;
next	1.2;

1.2
date	95.02.12.06.33.11;	author greg;	state Exp;
branches;
next	1.1;

1.1
date	95.02.11.20.01.27;	author greg;	state Exp;
branches;
next	;


desc
@MIDI sequencer which runs under Tcl/Tk using tclmidi extensions
@


1.43
log
@This is released as version 0.94
@
text
@#! /usr/local/bin/tkmidi -f
#
# TkSeq is a MIDI sequencer designed to work with Tcl/Tk and the tclmidi
# extensions. This is version 0.94d, Copyright (c) 1995  Greg Wolodkin
#
# This version requires tclmidi-3.0 or better.
# 
# email: greg@@eecs.berkeley.edu.  See the file COPYING for more info..
#
#    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.
#
# ---------------------------------------------------------------------
# Some notation:  ALL-CAPS are user-definable global variables.
#                 First letter capitalized indicates a global 
#                 state variable that is not user-defined.
#
# User Configuration begins here -- edit definitions if you want
# In C/C++, these will be #defines

proc userDefaults {} {

  global DDIVISION DQUANTIZE NUMTRACKS MTHRU MAXQUANT
  global TEMPO FMASK MEAN VARIANCE HAVE_TEX BITMAPS TEAROFF
  global SHOWPROG SHOWCHAN SHOWBARS SHOWBEAT SHOWQUAN SHOWMEAS SHOWMIDC
  global PIANOSCALE CHANNEL1 NUMDEV
  global HAVE_VOXWARE_GUS PATCH_DIR GM_PATS GUS_AUTO_LOAD GUS_AUTO_THRU

  # Default device is /dev/midi0.. look for no more than 4 devices
  set NUMDEV 4

  # 32 tracks is plenty for me.  Add more if you need 'em.
  set NUMTRACKS 32

  # file mask matches everything by default
  set FMASK ""

  # set BITMAPS to point to wherever you stash the bitmaps
  set BITMAPS /usr/include/X11/bitmaps

  # default variance for random perturbations moves events by just
  # a few SMF ticks
  set MEAN 0
  set VARIANCE 1.0 

  # default quantization is 1/16th notes, but nothing is quantized
  # unless you specifically ask for it.  MAXQUANT is for Piano Roll.
  set DQUANTIZE 16 
  set MAXQUANT  16

  # I assume you have midi2tex, tex, and xdvi.  Look for midi2tex
  # below to futz with it.  It's not reliable or fast ;-)
  set HAVE_TEX 1 

  # I have a GUS which is managed by the Linux sound driver.  Enable this if
  # you do too.  Comment GM_PATS out if you don't, to save some space.
  set HAVE_VOXWARE_GUS 1
  set GUS_AUTO_LOAD 1
  set GUS_AUTO_THRU 1
  set PATCH_DIR /dos/ultrasnd/midi
  set GM_PATS {acpiano britepno synpiano honktonk epiano1 epiano2 hrpschrd \
clavinet celeste glocken musicbox vibes marimba xylophon tubebell santur \
homeorg percorg rockorg church reedorg accordn harmonca concrtna nyguitar \
acguitar jazzgtr cleangtr mutegtr odguitar distgtr gtrharm acbass fngrbass \
pickbass fretless slapbas1 slapbas2 synbass1 synbass2 violin viola cello \
contraba marcato pizzcato harp timpani marcato slowstr synstr1 synstr2 \
choir doo voices orchhit trumpet trombone tuba mutetrum frenchrn hitbrass \
synbras1 synbras2 sprnosax altosax tenorsax barisax oboe englhorn bassoon \
clarinet piccolo flute recorder woodflut bottle shakazul whistle ocarina \
sqrwave sawwave calliope chiflead voxlead voxlead lead5th basslead fantasia \
warmpad polysyn ghostie bowglass metalpad halopad sweeper aurora soundtrk \
crystal atmosphr freshair unicorn sweeper startrak sitar banjo shamisen \
koto kalimba bagpipes fiddle shannai carillon agogo steeldrm woodblk taiko \
toms syntom revcym fx-fret fx-blow seashore jungle telephon helicptr \
applause ringwhsl}

  # these are used for the creation of new MIDI files,
  # but will be overridden when reading existing MIDI files
  set DDIVISION 120
  set TEMPO 120

  # Set this to 1 if you want tear-off menus
  set TEAROFF 0

  # Set this to one if you don't like channel 0
  set CHANNEL1 1

  # Set this to one to force MIDI THRU on by default when tkseq starts
  set MTHRU 1

  # Width (in pixels) of a single (white) piano key in Piano Roll view
  set PIANOSCALE 12.000000

  # Show channel assignments and patch changes by default
  # Set these to zero if you don't like the delay they introduce
  # Or you can disable them via the `View' menu..
  set SHOWMEAS 1
  set SHOWCHAN 1
  set SHOWPROG 1

  # For the piano roll -- these are a matter of taste.
  set SHOWMIDC 1
  set SHOWBARS 1
  set SHOWBEAT 0
  set SHOWQUAN 0

  if { ! [file exists ~/.tkseqopt] } {
    dialog .foo . "No options file found...\nCreating default version" \
      info 0 OK
    createDefaultOptions
  }
  option readfile ~/.tkseqopt
}

# -----------END of user configuration --------------------------------
#
# -----------Default Options-------------------------------------------
#
proc createDefaultOptions {} {

  if {[catch "set foo [open ~/.tkseqopt w]"]} {
    dialog .foo . "Couldn't create ~/.tkseqopt!" error 0 OK
    exit
  }

  puts $foo "! Here's some defaults to make things a little more"
  puts $foo "! peaceful.  Don't take it personal, if you really"
  puts $foo "! like pink and beige sliders that's OK by me ;-)"
  puts $foo ""
  puts $foo "! Tk's resources are so odd that it's almost impossible"
  puts $foo "! to use .Xdefaults *and* built-in options.  So I chose"
  puts $foo "! to have everything here in one place.  Remember that"
  puts $foo "! in Tk, order is important.. If things don't seem to be"
  puts $foo "! working as you'd expect then try again ;-)  You might"
  puts $foo "! want to check out Prof. Ousterhout's, pp.256-7"
  puts $foo ""
  puts $foo "! Main font"
  puts $foo "*font:		*-Times-Bold-R-Normal-*-14-*-*-*-*-*-*-*"
  puts $foo ""
  puts $foo "! Fonts used for certain widget classes (fixed width is best)"
  puts $foo \
    "*Listbox.font:	-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
  puts $foo \
    "*Text.font:	-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
  puts $foo \
    "*Entry.font:	-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
  puts $foo ""
  puts $foo "! Fonts used elsewhere in special cases.."
  puts $foo \
    "*subtitle.font:	-b&h-lucida-bold-i-normal-sans-12-*-*-*-*-*-iso8859-1"
  puts $foo \
    "*message.font:	-*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  puts $foo \
    "*text.font:	-*-Times-Medium-R-Normal-*-14-*-*-*-*-*-*-*"
  puts $foo ""
  puts $foo "! This just takes up space.."
  puts $foo "*highlightThickness:		0"
  puts $foo ""
  puts $foo "! Some default colors -- keep them simple"
  puts $foo "*background:			#b0b0b0"
  puts $foo "*selectBackground:		#989898"
  puts $foo "*activeBackground:		#989898"
  puts $foo ""
  puts $foo "*foreground:			#000000"
  puts $foo "*selectForeground:		#000000"
  puts $foo "*activeForeground:		#000000"
  puts $foo ""
  puts $foo "*subtitle.foreground:		#606060"
  puts $foo "*disabledForeground:		#909090"
  puts $foo "	"
  puts $foo "! Used for piano roll grid, and for note events"
  puts $foo "*event.foreground:		#a0a0a0"
  puts $foo "*grid.foreground:		#909090"
  puts $foo "*measure.foreground:		#0000c0"
  puts $foo "*beat.foreground:		#b02050"
  puts $foo "*quantum.foreground:		#909090"
  puts $foo ""
  puts $foo "! Coloring the keyboard"
  puts $foo "*whitekey.background:		#d0d0d0"
  puts $foo "*blackkey.background:		#000000"
  puts $foo "*activekey.background:		#80b0d0"
  puts $foo ""
  puts $foo "! Scrollbars and sliders"
  puts $foo "*scroll.troughColor:		#909090"
  puts $foo "*scale.troughColor:		#909090"
  puts $foo "*scroll.width:			10"
  puts $foo ""
  puts $foo "! Normal `control' buttons are blue.."
  puts $foo "*play.foreground:		#3050b0"
  puts $foo "*record.foreground:		#3050b0"
  puts $foo "*pause.foreground:		#3050b0"
  puts $foo "*ffwd.foreground:		#3050b0"
  puts $foo "*rewind.foreground:		#3050b0"
  puts $foo "*stop.foreground:		#3050b0"
  puts $foo ""
  puts $foo "! Active ones get brighter -- green for play, red for record"
  puts $foo "*play.activeForeground:		#60d040"
  puts $foo "*record.activeForeground:	#d04060"
  puts $foo "*pause.activeForeground:	#4060d0"
  puts $foo "*stop.activeForeground:		#4060d0"
  puts $foo "*ffwd.activeForeground:		#4060d0"
  puts $foo "*rewind.activeForeground:	#4060d0"
  close $foo
}

# -----------Tclmidi detection-----------------------------------------
#
if {[catch "mididevice"]} {
  proc mididevice {} {
    set HasTclMidi 0
    return 0
  }
  foreach i "config copy delete free get make merge move play put \
           read record rewind split stop time track version wait" {
    set TmpMsg "The midi$i command requires tclmidi."
    proc midi$i args "dialog .h . \"$TmpMsg\" error 0 OK; return 0"
  }
} else {
  set HasTclMidi 1
}

# -----------BEGIN subroutines used by top level window ---------------
#
# Initialize everything
#
proc midiCleanSlate {realclean} {
  global DDIVISION NUMTRACKS SHOWPROG SHOWCHAN CHANNEL1 TKS_VERSION
  global MTHRU NUMDEV DEVTAB
  global MidiState SmpteClk LabelNow Now CurDev MasterDev
  global Now MuteList SoloList Modified MeasView MidiThru
  global PlayFile RecFile TmpFile PlayName StopTime TimeScale
  global KeyYval KeyYtag MeterMap MetroData
  global Mtracks Mformat Mdivision
  global GusThruPid
  global PatchList PatchFile
  global PLoadList PLoadFile
  global RawPitch NoteOff

  # load user-defaults and `static' data
  if {$realclean} {
    loadMappedEventData
    userDefaults
    for {set i 0} {$i < 4} {incr i} {
      set SmpteClk($i) 0 
      set MidiThru($i) $MTHRU
    }
    set LabelNow "MIDI:"
    set CurDev 0
    set MasterDev 0
    set DEVTAB(0,dev)  ""; set DEVTAB(0,raw) "";
    set DEVTAB(0,name) ""; set DEVTAB(0,map) "";

  # Some UltraSound stuff
    set GusThruPid 0
    set PLoadList ""
    set PLoadFile ""
  }

  if {$PlayFile != ""} { 
    closeTrackInfo {}
    closePianoRoll {}
    midifree $PlayFile
    updateButtons 0 1 0 0 0 0
  }

  if {$RecFile != ""} { midifree $RecFile }
  if {$TmpFile != ""} { midifree $TmpFile }
  set PlayFile ""
  set RecFile ""
  set TmpFile ""

  set MeterMap ""
  set MidiState stopped
  set SoloList ""
  set MuteList ""
  set Modified 0
  set StopTime 0
  set MeasView 1

  set Mformat 1
  set Mtracks 1
  set Mdivision $DDIVISION

  if {$SmpteClk($CurDev) == 1} {
    set Now "No Sync"
  } else {
    set Now [tick2measure 0]
  }
  set TimeScale 25.000000

  # 12 notes
  set KeyYval(0)  0.00000; set KeyYtag(0)  {white "C"}
  set KeyYval(1)  0.09286; set KeyYtag(1)  {black "C sharp" "D flat"}
  set KeyYval(2)  0.14286; set KeyYtag(2)  {white "D"} 
  set KeyYval(3)  0.26429; set KeyYtag(3)  {black "E flat" "D sharp"} 
  set KeyYval(4)  0.28571; set KeyYtag(4)  {white "E"} 
  set KeyYval(5)  0.42857; set KeyYtag(5)  {white "F"}
  set KeyYval(6)  0.52143; set KeyYtag(6)  {black "F sharp" "G flat"}
  set KeyYval(7)  0.57143; set KeyYtag(7)  {white "G"}
  set KeyYval(8)  0.67856; set KeyYtag(8)  {black "A flat" "G sharp"}
  set KeyYval(9)  0.71429; set KeyYtag(9)  {white "A"}
  set KeyYval(10) 0.83571; set KeyYtag(10) {black "B flat" "A sharp"}
  set KeyYval(11) 0.85714; set KeyYtag(11) {white "B"}

  set MetroData(meas,start) 0
  set MetroData(meas,stop) 32
  set MetroData(chan,meas) [expr 9 + $CHANNEL1]
  set MetroData(chan,beat) [expr 9 + $CHANNEL1]
  set MetroData(patch,meas) 37
  set MetroData(patch,beat) 37
  set MetroData(vol,meas)  127
  set MetroData(vol,beat)   80
  set MetroData(dur,meas)  [expr $Mdivision / 4]
  set MetroData(dur,beat)  [expr $Mdivision / 4]

  # this wipes the main window clean
  if {[winfo exists .trkname.list]} { showTrackEverything {} }

  # This is the default noise to make
  set PatchList 0
  set PatchFile acpiano

  # These are just for safety
  set RawPitch 0
  set NoteOff "0 NoteOff 0 0 0"

  # open a default `empty' file
  if {! $realclean} {
    set PlayFile [midimake]
    midiconfig $PlayFile {tracks 1} {format 1} "division $DDIVISION"

    set PlayName "untitled.mid"
    wm iconname . $PlayName
    wm title . "tkseq $TKS_VERSION:  $PlayName"
  }
}

# ---------------------------------------------------------------------
# Generate the track channel list
#
proc showTrackEverything {tlist} {
  showTrackNames    $tlist
  showTrackPrograms $tlist
  showTrackChannels $tlist
  showTrackMuteList
  showTrackStatusLine
  # Erase what's there, but don't build the new one yet..
  if {$tlist == ""} {
    .trkmeas.list delete 0 end
     update
  }
  showTrackMeasures $tlist
}

proc showTrackStatusLine {} {
  global Mtracks Mdivision Mformat PlayFile
  global DDIVISION

  if {$PlayFile != ""} {
    scan [getConfig $PlayFile] "%d %d %d" Mdivision Mformat Mtracks
    set DDIVISION $Mdivision
  }
}

# ---------------------------------------------------------------------
# Write changed names to midi file in the form of MetaSequenceName
# events.  tlist is a list of lists, {number name} {number name} etc.
#
proc writeTrackNames {tlist} {
  global Modified PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  if {$tlist == ""} {
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist [lappend tlist $i]
    }
  }

  foreach i $tlist {
    midirewind $PlayFile $i
    set eventlist [midiget $PlayFile $i 0]
    foreach event $eventlist {
      if {[lindex $event 1] == "MetaSequenceName"} {
         mididelete $PlayFile $i $event
         set Modified 1
      }
    }
    set tmpname [.trkname.list get $i]
    if {$tmpname != ""} {
       midiput $PlayFile $i [list 0 MetaSequenceName "$tmpname"]
       set Modified 1
    }
    midirewind $PlayFile
  }
}

# --------------------------------------------------------------------
#
proc showTrackNames {tlist} {
  global NUMTRACKS
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  if {$tlist == ""} {
    .trkname.list delete 0 end
    for {set i 0} {$i < $NUMTRACKS} {incr i} {.trkname.list insert end ""}
    if {$PlayFile == ""} { return }
    for {set i 0} {$i < $mtrk} {incr i} { set tlist "$tlist $i" }
  }

  foreach i $tlist {
    if {$i < $mtrk} {
      if {$i == 0 && $mfmt == 1} {
        set tmpname "<meta>"
      } else {
        set tmpname "<untitled>"
      }
      midirewind $PlayFile $i

      # don't check every event -- it's too time consuming. 
      set eventlist [midiget $PlayFile $i 0]
      foreach event $eventlist {
        if {[lindex $event 1] == "MetaSequenceName"} {
          set tmpname [lindex $event 2]
          break
        }
      }
    } else {
      set tmpname ""
    }
    .trkname.list delete $i
    .trkname.list insert $i $tmpname
  }
}

# --------------------------------------------------------------------
#
proc showTrackPrograms {tlist} {
  global NUMTRACKS SHOWPROG DEVTAB CHANNEL1 GM_PATS
  global PlayFile CurDev MidiState
  global PatchList PatchFile

  set mf $PlayFile
  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  # Wipe the list clean if we don't want to see program changes
  if {$SHOWPROG == 0} {set mf ""; set tlist ""}

  if {$tlist == ""} {
    .trkprog.list delete 0 end
    for {set i 0} {$i < $NUMTRACKS} {incr i} {.trkprog.list insert end ""}
    if {$mf == ""} { return }
    for {set i 0} {$i < $mtrk} {incr i} { set tlist "$tlist $i" }
  }

  foreach i $tlist {
    if {$i < $mtrk} {
      set tmpprog " -- "
      set tmpcount 0
      midirewind $mf $i

      # don't check every event -- it's too time consuming. 
      # for now check no more than say the first 32 events..
      # Hmm.  I really do need to get all the program changes.

      while {[set event [midiget $mf $i next]] != "EOT"} {
        if {[lindex $event 1] == "Program"} {
          set tt [lindex $event 3]

	# Add this patch to the patch list
        # Might as well add the default GM binding for now
          if {[lsearch -exact $PatchList $tt] == -1} {
            set PatchList [lsort -integer [lappend PatchList $tt]]
            set index [lsearch -exact $PatchList $tt]
            set pname [lindex $GM_PATS $tt]
            set PatchFile [linsert $PatchFile $index $pname]
          }

          if {$tmpprog == " -- "} {

            # Might as well drop these into place now, assuming time 0.
            # FIX ME when miditime is working (at present, I can't be
            # guaranteed to get a zero initially..)

            if {$DEVTAB($CurDev,raw) != "" && $MidiState == "stopped"} {
                 set channel [string range [.trkchan.list get $i] 0 1]
                 midisend $DEVTAB($CurDev,raw) \
                    "0 Program [expr $channel - $CHANNEL1] $tt" 
            }

            if {$tt < 10} {
              set tmpprog "  $tt "
            } elseif {$tt < 100} {
              set tmpprog " $tt "
            } else {
              set tmpprog "$tt "
            }
          } else {
            if {[string first * $tmpprog] == -1} {
              if {[expr $tmpprog] != $tt} {
                set tmpprog [string trimright $tmpprog]*
              }
        #     break  Nope.. get them all
            } 
          }
        }
        # FIX ME
        # if {[incr tmpcount] > 32} { break }
      }
    } else {
      set tmpprog ""
    }
    .trkprog.list delete $i
    .trkprog.list insert $i $tmpprog
  }
}

# --------------------------------------------------------------------
#
proc showTrackChannels {tlist} {
  global CHANNEL1 SHOWCHAN NUMTRACKS
  global PlayFile

  set mf $PlayFile
  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  # Wipe the list clean if we don't want to see program changes
  if {$SHOWCHAN == 0} {set mf ""; set tlist ""}

  if {$tlist == ""} {
    .trkchan.list delete 0 end
    for {set i 0} {$i < $NUMTRACKS} {incr i} {.trkchan.list insert end ""}
    if {$mf == ""} { return }
    for {set i 0} {$i < $mtrk} {incr i} { set tlist "$tlist $i" }
  }

  foreach i $tlist {
    if {$i < $mtrk} {
      set tmpchan "--"
      set tmpcount 0
      midirewind $mf $i

      # don't check every event -- it's too time consuming. 
      # for now check no more than say the first 32 notes..
      while {[set event [midiget $mf $i next]] != "EOT"} {
        set etype [lindex $event 1]
        if {[string compare [string range $etype 0 3] "Note"] == 0} {
          set tt [expr [lindex $event 2] + $CHANNEL1]
          if {$tmpchan == "--"} {
            if {$tt < 10} {
              set tmpchan " $tt "
            } else {
              set tmpchan "$tt "
            }
          } else {
            if {[string trim $tmpchan] != $tt} {
              set tmpchan "[string trimright $tmpchan]*"
              break
            } 
          }
          # FIX ME
          if {[incr tmpcount] > 32} { break }
        }
      }
    } else {
      set tmpchan ""
    }
    .trkchan.list delete $i
    .trkchan.list insert $i $tmpchan
  }
}

# --------------------------------------------------------------------
#
proc showTrackMeasures {tlist} {
  global NUMTRACKS SHOWMEAS
  global PlayFile

  set mf $PlayFile

  # Wipe the list clean if we don't want to see program changes
  if {$SHOWMEAS == 0} {set mf ""; set tlist ""}

  set gotnull 0
  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  if {$tlist == ""} {
    .trkmeas.list delete 0 end
    for {set i 0} {$i < $NUMTRACKS} {incr i} {.trkmeas.list insert end ""}
    if {$mf == ""} { return }
    for {set i 0} {$i < $mtrk} {incr i} { set tlist "$tlist $i" }
  }

  foreach i $tlist {
    if {$i < $mtrk} {
      set mcount 0
      set tmplist ""
      set mtick [miditrack $mf $i end]
      scan [tick2measure $mtick] "%d:" mmeas; incr mmeas

      midirewind $mf $i
      for {set k 0} {$k < $mmeas} {incr k} {
        set tick  [measure2tick "$k:0:0"]
        set event [midiget $mf $i $tick]

        if {$event == ""} {
          if {$gotnull == 0} {
            dialog .foo . "midiget returned null: $i $tick" info 0 OK
            set gotnull 1
          }
          set event [midiget $mf $i next]
        }

        if {$event == "EOT"} {
          set k $mmeas
          break
        }
        set tick [lindex [lindex $event 0] 0]
        scan [tick2measure $tick] "%d" tmeas
        for {set j $k} {$j < $tmeas} {incr j} { set tmplist "$tmplist." }
        set tmplist "$tmplist\o"
        set k $tmeas
      }
    } else {
      set tmplist ""
    }
    .trkmeas.list delete $i
    .trkmeas.list insert $i "$tmplist"
  }
}

proc showTrackMuteList {} {
  global NUMTRACKS
  global MuteList SoloList PlayFile

  # erase whatever is there already
  .trkmute.list delete 0 end

  if {$PlayFile == ""} { return }

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  for {set i 0} {$i < $mtrk} {incr i} {
    .trkmute.list insert end "<play>"
  }
  for {set i $mtrk} {$i < $NUMTRACKS} {incr i} {
    .trkmute.list insert end ""
  }
  foreach i $MuteList {
    if {$i < $mtrk} {
      .trkmute.list delete $i
      .trkmute.list insert $i "<mute>"
    } else {
      set pos [lsearch -exact $MuteList $i]
      set MuteList [lreplace $MuteList $pos $pos]
    }
  }
  if {$SoloList != ""} {
    if {$SoloList < $mtrk} {
      .trkmute.list delete $SoloList
      .trkmute.list insert $SoloList "<solo>"
    } else {
      set SoloList ""
    }
  }
}

#  ---------------------------------------------------------------------
# Enable or disable play/record buttons based on sequencer state
# This works only if there is a MIDI device compiled into tkmidi.
#
proc updateButtons {play rec stop pause ffwd rewind} {
  global TEAROFF MidiStatus

  set plm [.mbar.realtime.menu index Play]
  set rcm [.mbar.realtime.menu index Record]
  set stm [.mbar.realtime.menu index Stop]
  set ffm [.mbar.realtime.menu index FFwd]
  set rwm [.mbar.realtime.menu index Rewind]
  set pam [.mbar.realtime.menu index Pause]

  switch -- [expr $play * $MidiStatus] {
    0 {
	.control.play configure -state disabled
	.mbar.realtime.menu entryconfigure $plm -state disabled
      }
    1 {
        .control.play configure -state normal 
    	.mbar.realtime.menu entryconfigure $plm -state normal
	bind .control.play <Any-Leave> {tkButtonLeave %W}
      }
    2 {
        .control.play configure -state active
	.mbar.realtime.menu entryconfigure $plm -state disabled
	bind .control.play <Any-Leave> {tkButtonEnter %W; break}
      }
  }
  switch [expr $rec * $MidiStatus] {
    0 {
        .control.record configure -state disabled
        .mbar.realtime.menu entryconfigure $rcm -state disabled
      }
    1 {
        .control.record configure -state normal
    	.mbar.realtime.menu entryconfigure $rcm -state normal
	bind .control.record <Any-Leave> {tkButtonLeave %W}
      }
    2 {
        .control.record configure -state active
        .mbar.realtime.menu entryconfigure $rcm -state disabled
	bind .control.record <Any-Leave> {tkButtonEnter %W; break}
      }
  }
  switch [expr $stop * $MidiStatus] {
    0 {
        .control.stop configure -state disabled 
    	.mbar.realtime.menu entryconfigure $stm -state disabled
      }
    1 { 
        .control.stop configure -state normal
        .mbar.realtime.menu entryconfigure $stm -state normal
      }
  }
  switch [expr $pause * $MidiStatus] {
    0 {
        .control.pause configure -state disabled
        .mbar.realtime.menu entryconfigure $pam -state disabled
      }
    1 { .control.pause configure -state normal
        .mbar.realtime.menu entryconfigure $pam -state normal
        bind .control.pause <Any-Leave> {tkButtonLeave %W}
      } 
    2 { .control.pause configure -state active
        .mbar.realtime.menu entryconfigure $pam -state disabled
	bind .control.pause <Any-Leave> {tkButtonEnter %W; break}
      }
  }
  switch [expr $ffwd * $MidiStatus] {
    0 {
        .control.ffwd configure -state disabled
        .mbar.realtime.menu entryconfigure $ffm -state disabled
    }
    1 {
        .control.ffwd configure -state normal
        .mbar.realtime.menu entryconfigure $ffm -state normal
        bind .control.ffwd <Any-Leave> {tkButtonLeave %W}
      }
    2 {
        .control.ffwd configure -state active
        .mbar.realtime.menu entryconfigure $ffm -state disabled
        bind .control.ffwd <Any-Leave> {tkButtonEnter %W; break}
      }
  }
  switch [expr $rewind * $MidiStatus] {
    0 {
        .control.rewind configure -state disabled
        .mbar.realtime.menu entryconfigure $rwm -state disabled
      }
    1 {
        .control.rewind configure -state normal
        .mbar.realtime.menu entryconfigure $rwm -state normal
	bind .control.rewind <Any-Leave> {tkButtonLeave %W}
      }
    2 {
        .control.rewind configure -state active
        .mbar.realtime.menu entryconfigure $rwm -state disabled
        bind .control.rewind <Any-Leave> {tkButtonEnter %W; break}
      }
  }
}

# ---------------------------------------------------------------------
# Generate the top level window
#
proc drawMainWindow {} {
  global DDIVISION DQUANTIZE HAVE_TEX BITMAPS NUMTRACKS MTHRU
  global TEAROFF SHOWPROG SHOWCHAN DEVTAB NUMDEV HAVE_VOXWARE_GUS
  global MidiThru SmpteClk LabelNow Now MidiStatus CurDev
  global Mtracks Mformat Mdivision PlayFile PlayName GusThruPid
  global PatchList PatchFile

  set MidiStatus [hasMidi]

  frame .mbar -relief raised -bd 2
  frame .trks

  menubutton .mbar.file     -text "File " -underline 0 \
  	-menu .mbar.file.menu 
  menubutton .mbar.realtime -text "Realtime " -underline 0 \
  	-menu .mbar.realtime.menu
  menubutton .mbar.settings -text "Settings " -underline 0 \
  	-menu .mbar.settings.menu 
  menubutton .mbar.view -text "View " -underline 0 \
  	-menu .mbar.view.menu
  menubutton .mbar.track -text "Track" -underline 0 \
  	-menu .mbar.track.menu

  menu .mbar.file.menu -tearoff $TEAROFF
  .mbar.file.menu add command -label "New" -underline 0 \
    -command fileNew
  .mbar.file.menu add command -label "Open" -underline 0 \
    -command fileOpenMidi
  .mbar.file.menu add separator
  .mbar.file.menu add command -label "Save" -underline 0 \
    -command fileSaveMidi
  .mbar.file.menu add command -label "Save as" -underline 5 \
    -command fileSaveMidiAs
  .mbar.file.menu add separator
  .mbar.file.menu add command -label "Exit" -underline 1 \
    -command fileQuit 

  menu .mbar.realtime.menu -tearoff $TEAROFF
  .mbar.realtime.menu add command -label "Play" -underline 0 \
    -command seqPlay -state disabled
  .mbar.realtime.menu add command -label "Record" -underline 0 \
    -command seqRecord -state disabled
  .mbar.realtime.menu add command -label "Stop" -underline 0 \
    -command seqStop -state disabled
  .mbar.realtime.menu add separator
  .mbar.realtime.menu add command -label "FFwd" -underline 0 \
    -command seqFFwd -state disabled
  .mbar.realtime.menu add command -label "Rewind" -underline 0 \
    -command seqRewind -state disabled
  .mbar.realtime.menu add command -label "Pause" -underline 0 \
    -command seqPause -state disabled

  menu .mbar.settings.menu -tearoff $TEAROFF

  # No need to choose a device if there is only one..
  if {$NUMDEV > 1} {
    .mbar.settings.menu add cascade -label "Device" -underline 0 -menu \
      .mbar.settings.menu.device
    menu .mbar.settings.menu.device -tearoff $TEAROFF
    for {set i 0} {$i < $NUMDEV} {incr i} {
      .mbar.settings.menu.device add radiobutton -label "$DEVTAB($i,name)" \
    	-underline 9 -variable CurDev -value $i
    }
  }
  .mbar.settings.menu add cascade -label "Clock" -underline 0 -menu \
    .mbar.settings.menu.clock
  .mbar.settings.menu add command -label "Enable Thru" -underline 7 \
    -command {setMidiThru $CurDev [expr ! $MidiThru($CurDev)]}

  .mbar.settings.menu add command -label "Channel Map" -underline 10 \
    -command {getChannelMap}

  if {$HAVE_VOXWARE_GUS} {
#    .mbar.settings.menu add command  -label "UltraSound" -underline 0 \
#	-command {configUltraSound}
    .mbar.settings.menu add cascade -label "UltraSound" -underline 0 \
	-menu .mbar.settings.menu.gus

    menu .mbar.settings.menu.gus -tearoff $TEAROFF
    .mbar.settings.menu.gus add command \
    -label "Enable GUS" -underline 0 \
      -command {setGusThru [expr ! $GusThruPid]}
    .mbar.settings.menu.gus add command -label "Patch Manager" \
       -command {patchManager} -underline 0
  }

  .mbar.settings.menu add separator
  .mbar.settings.menu add command -label "Metronome" -underline 1 \
    -command {windowMetronome}
  .mbar.settings.menu add cascade -label "Division" -underline 2 -menu \
    .mbar.settings.menu.div
  .mbar.settings.menu add cascade -label "Quantization" -underline 0 -menu \
    .mbar.settings.menu.quant
  .mbar.settings.menu add cascade -label "Randomization" -underline 0 \
    -menu .mbar.settings.menu.random
  .mbar.settings.menu add separator
  .mbar.settings.menu add command -label "Key" -underline 0 \
    -command {editMap Key}
  .mbar.settings.menu add command -label "Meter" -underline 0 \
    -command {editMap Meter}
  .mbar.settings.menu add command -label "Tempo" -underline 0 \
    -command {editMap Tempo}
  .mbar.settings.menu add command -label "SMPTE" -underline 0\
    -command {setSMPTEoffset}

  menu .mbar.settings.menu.clock -tearoff $TEAROFF
  .mbar.settings.menu.clock add radiobutton -label "Kernel" \
  	-underline 0 -variable SmpteClk(0) -value 0 \
  	-command updateDeviceInfo
  .mbar.settings.menu.clock add radiobutton -label "SMPTE" \
  	-underline 0 -variable SmpteClk(0) -value 1 \
  	-command updateDeviceInfo
  .mbar.settings.menu.clock add radiobutton -label "MPU-401" \
  	-underline 0 -variable SmpteClk(0) -value 2 \
  	-command updateDeviceInfo

# if {$SMPTEDEV == ""} 
  if {0} {
    .mbar.settings.menu entryconfigure \
       [.mbar.settings.menu index SMPTE] -state disabled
    .mbar.settings.menu.clock entryconfigure \
       [.mbar.settings.menu.clock index SMPTE/MTC] -state disabled
  }

  menu .mbar.settings.menu.div -tearoff $TEAROFF
  foreach i "48 72 96 120 144 168 192 216 240 360 480" {
    .mbar.settings.menu.div add radiobutton -label $i \
  	-variable DDIVISION -command newDivision -value $i
  }

  menu .mbar.settings.menu.quant -tearoff $TEAROFF
  .mbar.settings.menu.quant add radiobutton -label "whole notes" \
  	-variable DQUANTIZE -command newQuantization -value 1
  .mbar.settings.menu.quant add radiobutton -label "1/2 notes" \
  	-variable DQUANTIZE -command newQuantization -value 2
  .mbar.settings.menu.quant add radiobutton -label "1/4 notes" \
  	-variable DQUANTIZE -command newQuantization -value 4
  .mbar.settings.menu.quant add radiobutton -label "1/8 notes" \
  	-variable DQUANTIZE -command newQuantization -value 8
  .mbar.settings.menu.quant add radiobutton -label "1/4 triplets" \
  	-variable DQUANTIZE -command newQuantization -value 12
  .mbar.settings.menu.quant add radiobutton -label "1/16 notes" \
  	-variable DQUANTIZE -command newQuantization -value 16
  .mbar.settings.menu.quant add radiobutton -label "1/8 triplets" \
  	-variable DQUANTIZE -command newQuantization -value 24
  .mbar.settings.menu.quant add radiobutton -label "1/32 notes" \
  	-variable DQUANTIZE -command newQuantization -value 32
  .mbar.settings.menu.quant add radiobutton -label "1/16 triplets" \
  	-variable DQUANTIZE -command newQuantization -value 48
  .mbar.settings.menu.quant add radiobutton -label "1/64 notes" \
  	-variable DQUANTIZE -command newQuantization -value 64
  .mbar.settings.menu.quant add radiobutton -label "1/32 triplets" \
  	-variable DQUANTIZE -command newQuantization -value 96

  menu .mbar.settings.menu.random -tearoff $TEAROFF
  .mbar.settings.menu.random add command -label "Mean" \
	  -underline 0 -command setMean
  .mbar.settings.menu.random add command -label "Variance" \
	  -underline 0 -command setVariance

  menu .mbar.view.menu -tearoff $TEAROFF
  .mbar.view.menu add checkbutton -label "Channels" \
    -variable SHOWCHAN -underline 0 \
    -command {watchCursor .; showTrackChannels {}; normalCursor .}
  .mbar.view.menu add checkbutton -label "Program Changes" \
    -variable SHOWPROG -underline 0 \
    -command {watchCursor .; showTrackPrograms {}; normalCursor .}
  .mbar.view.menu add checkbutton -label "Measure Info" \
    -variable SHOWMEAS -underline 0 \
    -command {watchCursor .; showTrackMeasures {}; normalCursor .}

#  .mbar.view.menu add separator
#  .mbar.view.menu add command -label "Refresh" -underline 0 \
#    -command {showTrackEverything {}}

  menu .mbar.track.menu -tearoff $TEAROFF -postcommand trackMenu
  .mbar.track.menu add command -label "List" -underline 0 \
    -command {track Info}
  .mbar.track.menu add command -label "Mute" -underline 1 \
    -command {track Mute}
  .mbar.track.menu add command -label "Solo" -underline 1 \
    -command {track Solo}
  .mbar.track.menu add command -label "Name" -underline 0 \
    -command {track Name}
  .mbar.track.menu add command -label "View" -underline 0 \
    -command {track PianoRoll}
  if {$HAVE_TEX} {
    .mbar.track.menu add command -label "Score" -underline 0 \
      -command {track Score}
  }
  .mbar.track.menu add separator
  .mbar.track.menu add command -label "Channel" -underline 1 \
    -command {track ForceChannel}
  .mbar.track.menu add command -label "Program" -underline 0 \
    -command {track ProgramChange}
  .mbar.track.menu add command -label "Parameter" -underline 1 \
    -command {track ParameterSet}
  .mbar.track.menu add separator
  .mbar.track.menu add command -label "Copy" -underline 0 \
    -command {track Copy}
  .mbar.track.menu add command -label "Merge" -underline 0 \
    -command {track Merge}
  .mbar.track.menu add command -label "Remove" -underline 0 \
    -command {track Remove}
  .mbar.track.menu add separator
  .mbar.track.menu add command -label "Erase" -underline 0 \
    -command {track Erase}
  .mbar.track.menu add command -label "Offset" -underline 0 \
    -command {track Offset}
  .mbar.track.menu add command -label "Quantize" -underline 0 \
    -command {track Quantize}
  .mbar.track.menu add command -label "Randomize" -underline 3 \
    -command {track Randomize}
  .mbar.track.menu add command -label "Transpose" -underline 0 \
    -command {track Transpose}
  .mbar.track.menu add command -label "Volume" -underline 0 \
    -command {track Volume}

  button .mbar.help -text Help -relief flat \
     -command {displayText .h "Help" 64 20 \
      "Here is some info to get you started.\n\n\
       Mouse behavior is hopefully consistent within tkseq.. in general\
       you can left-click on something to select it, right-click to\
       unselect it, and double-left-click to invoke an action associated\
       with it (e.g. file selection).\n\n\
       The Track menu is pretty much complete.  Select a track (or\
       several) by name and then try some of the options in the \
       Track menu.\n\n\
       Recent additions include support for channel numbering to start\
       at one instead of zero.  This is *not* supported within the\
       Track->List window, as it would be too time-consuming there I\
       think.  So be careful. ;-)\n\n\
       Do you have any other useful tips I should insert here?  Bugs,\
       comments, and groovy modifications to greg@@eecs.berkeley.edu. \
       Thanks, and enjoy!"}

#    -command {dialog .h . "No help just now." info 0 OK}
#    -command {dialog .h . "[exec fortune -s]" info 0 OK}

# bitmap controls
  frame .stat -relief flat -bd 2
  frame .control -relief sunken -bd 2
  frame .control.buttons

  button .control.rewind -command seqRewind -width 1.2c \
    -bitmap "@@$BITMAPS/tks_rew" -state disabled
  button .control.stop -command seqStop -width 1.2c \
    -bitmap "@@$BITMAPS/tks_stop" -state disabled
  button .control.ffwd  -command seqFFwd -width 1.2c \
    -bitmap "@@$BITMAPS/tks_ffwd" -state disabled
  button .control.play  -command seqPlay -width 1.2c \
    -bitmap "@@$BITMAPS/tks_play" -state disabled
  button .control.record -command seqRecord -width 1.2c \
    -bitmap "@@$BITMAPS/tks_rec" -state disabled
  button .control.pause -command seqPause -width 1.2c \
    -bitmap "@@$BITMAPS/tks_paus" -state disabled

  frame .trknumb
  frame .trkname
  frame .trkmute
  frame .trkprog
  frame .trkchan
  frame .trkmeas 
  frame .trkscr 

  label .trknumb.subtitle -text "Trk" -relief raised -bd 1
  set subt [lindex [.trknumb.subtitle configure -foreground] 4]
  listbox .trknumb.list -foreground $subt \
    -yscrollcommand "trackScan {.trknumb.list}" \
    -width 5 -height 1 -relief flat

  label .trkname.subtitle -text "Description" -relief raised -bd 1
  listbox .trkname.list \
    -yscrollcommand "trackScan {.trkname.list}" \
    -xscrollcommand ".trkname.scroll set" \
    -width 18 -height 1 -relief sunken \
    -selectmode extended

  label .trkmute.subtitle -text "Mute" -relief raised -bd 1
  listbox .trkmute.list \
    -yscrollcommand "trackScan {.trkmute.list}" \
    -width 6 -height 1 -relief flat

  label .trkprog.subtitle -text " Pr" -relief raised -bd 1
  listbox .trkprog.list \
    -yscrollcommand "trackScan {.trkprog.list}" \
    -width 4 -height 1 -relief flat

  label .trkchan.subtitle -text "Ch" -relief raised -bd 1
  listbox .trkchan.list \
    -yscrollcommand "trackScan {.trkchan.list}" \
    -width 3 -height 1 -relief flat

  label .trkmeas.subtitle -text "Measure Info" -relief raised -bd 1
  listbox .trkmeas.list \
    -yscrollcommand "trackScan {.trkmeas.list}" \
    -xscrollcommand ".trkmeas.scroll set" -relief sunken \
    -height 2 -setgrid 1

  label .trkscr.subtitle -text " " -relief raised -bd 1

  scrollbar .trkscr.scroll -command "trackScrollUD"
  scrollbar .trkmeas.scroll -command ".trkmeas.list xview" \
    -orient horizontal
  scrollbar .trkname.scroll -command ".trkname.list xview" \
    -orient horizontal

  set basewidth [lindex [.trkname.scroll configure -width] 4]
  set bordwidth [lindex [.trkname.scroll configure -borderwidth] 4]
  set highwidth [lindex [.trkname.scroll configure -highlightthickness] 4]
  set dwidth [expr $basewidth + 2*$bordwidth + 2*$highwidth]

  frame .trkscr.dummy -width $dwidth -height $dwidth
  frame .trknumb.dummy -height $dwidth
  frame .trkmute.dummy -height $dwidth
  frame .trkprog.dummy -height $dwidth
  frame .trkchan.dummy -height $dwidth

  frame .stat.file
  frame .stat.trk
  frame .stat.fmt
  frame .stat.div
  frame .stat.now
  frame .stat.center

  label .stat.lfile -text "File:" -foreground $subt
  label .stat.vfile -textvariable PlayName -width 16
  label .stat.ltrk -text "Trk:" -foreground $subt
  label .stat.vtrk -width 3 -textvariable Mtracks 
  label .stat.lfmt -text "Fmt:" -foreground $subt
  label .stat.vfmt -textvariable Mformat
  label .stat.ldiv -text "Div:" -foreground $subt
  label .stat.vdiv -textvariable Mdivision
  label .stat.lnow -textvariable LabelNow -foreground $subt -width 8
  label .stat.vnow -textvariable Now -width 10 

  pack .stat.lfile .stat.vfile -in .stat.file -side left
  pack .stat.ltrk .stat.vtrk -in .stat.trk -side left
  pack .stat.lfmt .stat.vfmt -in .stat.fmt -side left
  pack .stat.ldiv .stat.vdiv -in .stat.div -side left
  pack .stat.vnow .stat.lnow -in .stat.now -side right

  pack .stat.trk .stat.fmt .stat.div -in .stat.center \
    -side left -expand 1 -fill x

  pack .stat.file .stat.center .stat.now \
       -in .stat -side left -padx 4m -expand 1 -fill x

  pack .mbar -side top -fill x
  pack .mbar.file .mbar.realtime .mbar.settings .mbar.view .mbar.track \
  	-side left
  pack .mbar.help -side right

  pack .trks -side top -expand 1 -fill both
  pack .stat -side bottom -fill x
  pack .control -side bottom -fill x

  pack .control.rewind .control.stop .control.ffwd \
       .control.play .control.pause .control.record \
       -in .control.buttons -side left -padx 1m
  pack .control.buttons -in .control 

  pack .trknumb.subtitle -in .trknumb -side top -fill x
  pack .trknumb.list -in .trknumb -side top -expand 1 -fill y
  pack .trknumb.dummy -in .trknumb -side bottom
  pack .trkname.subtitle -in .trkname -side top -fill x
  pack .trkname.list -in .trkname -side top -expand 1 -fill y
  pack .trkname.scroll -in .trkname -side bottom -fill x
  pack .trkmute.subtitle -in .trkmute -side top -fill x
  pack .trkmute.list -in .trkmute -side top -expand 1 -fill y
  pack .trkmute.dummy -in .trkmute -side bottom
  pack .trkprog.subtitle -in .trkprog -side top -fill x
  pack .trkprog.list -in .trkprog -side top -expand 1 -fill y
  pack .trkprog.dummy -in .trkprog -side bottom
  pack .trkchan.subtitle -in .trkchan -side top -fill x
  pack .trkchan.list -in .trkchan -side top -expand 1 -fill y
  pack .trkchan.dummy -in .trkchan -side bottom
  pack .trkmeas.subtitle -in .trkmeas -side top -fill x
  pack .trkmeas.list -in .trkmeas -side top -expand 1 -fill both
  pack .trkmeas.scroll -in .trkmeas -side bottom -fill x
  pack .trknumb .trkname .trkmute .trkprog .trkchan -in .trks \
    -side left -fill y
  pack .trkmeas -in .trks -side left -expand 1 -fill both
  pack .trkscr.subtitle -in .trkscr -side top -fill x
  pack .trkscr.scroll -in .trkscr -side top -expand 1 -fill y
  pack .trkscr.dummy -in .trkscr -side bottom
  pack .trkscr -in .trks -side right -fill y
  
  wm geometry . 40x12
  wm minsize . 18 2

  bind .mbar P seqPlay
  bind .mbar R seqRecord
  bind .mbar S seqStop
  bind .trkname.list <Double-1> { track Name }
  bind .trkname.list <Button-3> {
      selection clear .trkname.list
    }
  bind .trkmute.list <Double-1> {
      selection clear .trkname.list
      .trkname.list selection set [%W nearest %y]
      trackMuteSolo [.trkname.list curselection] 
    }
  bind .trkchan.list <Double-1> {
      selection clear .trkname.list
      .trkname.list selection set [%W nearest %y]
      trackForceChannel [.trkname.list curselection]
    }
  bind .trkprog.list <Double-1> {
      selection clear .trkname.list
      .trkname.list selection set [%W nearest %y]
      windowMappedEvent [.trkname.list curselection] Patch
    }
  bind .trkmeas.list <Double-1> {
      selection clear .trkname.list
      .trkname.list selection set [%W nearest %y]
      track PianoRoll
    }

  .trknumb.list delete 0 end
  for {set i 0} {$i < $NUMTRACKS} {incr i} {
    .trknumb.list insert end [format "%3d:" $i]
  }

  # disable real-time controls if there is no midi device
  tkwait visibility . 
  if {$MidiStatus} {
    mididevice $DEVTAB($CurDev,dev) "midithru $MidiThru($CurDev)"
  }
  updateButtons 0 1 0 0 0 0
  setMidiThru $CurDev $MTHRU

  # hack to "disable" selection in these listboxes
  foreach i {numb mute chan prog meas} { disableSelect .trk$i.list }

  # make selection a little more subtle in others
  # foreach i {mute chan prog meas} {
  #   set scol [lindex [.trk$i.list configure -background] 4]
  #   .trk$i.list configure -selectbackground $scol
  # }
}

# ---------------------------------------------------------------------
# Make the cursor into a watch when we do slow stuff
#
proc watchCursor {wlist} {
  foreach i $wlist {
    if {[lindex [$i configure -cursor] 4] != "watch"} {
      $i configure -cursor watch
      update idletasks
    }
  }
}

proc normalCursor {wlist} {
  foreach i $wlist {
    $i configure -cursor {}
  }
}

# -----------BEGIN File Menu procedures ------------------------------
#
# This module is a general-purpose file selector.
# It uses the global variables PlayName and FMASK.
# and makes calls to `dialog' for help functions
#
proc getFileName {text defname} {
  global FMASK

  # these aren't *really* global
  global GFNname waitname fdir

  set GFNname $defname

  set fdir [pwd]
  toplevel .file -cursor watch
  wm transient .file .
  set x [expr [winfo x .]+80]
  set y [expr [winfo y .]+60]
  wm geometry .file "+$x+$y"
  frame .file.t 
  frame .file.l	
  frame .file.r 
  frame .file.l.t 
  frame .file.l.b 
  frame .file.dir
  frame .file.mask
  frame .file.name
  frame .file.list
  frame .file.butt
  label .file.dir.label  -text "Directory:"
  entry .file.dir.entry -width 44 -relief sunken -bd 2 -textvariable fdir
  label .file.mask.label -text "File mask:"
  entry .file.mask.entry -width 20 -relief sunken -bd 2 -textvariable FMASK
  label .file.name.label -text "File name:"
  entry .file.name.entry -width 20 -relief sunken -bd 2 \
    -textvariable GFNname

  listbox .file.list.names -relief sunken -borderwidth 2 \
  	-yscrollcommand ".file.list.scroll set" \
  	-selectmode single
  scrollbar .file.list.scroll -command ".file.list.names yview"

  button .file.butt.ok -text OK \
    -command {if {$GFNname != ""} {set waitname $GFNname}}
  button .file.butt.delete -text Delete \
    -command {if {![catch {eval "set tt [selection get]"} msg]} {
                eval exec rm $tt;
                set GFNname "";
                listFiles
              }}
  button .file.butt.help -text Help \
    -command {dialog .h .file {Please select a filename.} info 0 OK}
  button .file.butt.cancel -text Cancel -command {set waitname ""}
  message .file.message -width 3i -text $text
  pack .file.t -side top -fill x -pady 1m
  pack .file.l -side left -fill y
  pack .file.r -side right -pady 1m
  pack .file.l.t -in .file.l -side top
  pack .file.l.b -in .file.l -side right
  pack .file.dir -in .file.t -side left -fill x -padx 1m
  pack .file.mask -in .file.l.t -side top -fill x -padx 1m -pady 2m
  pack .file.name -in .file.l.t -side top -fill x -padx 1m -pady 1m
  pack .file.butt -in .file.l.b -side right
  pack .file.message -in .file.l.b -side left -expand 1 -fill both \
    -padx 3m -pady 3m
  pack .file.list -in .file.r -side left -padx 2m -pady 1m
  pack .file.dir.label .file.dir.entry -in .file.dir \
 	-side left -padx 1m 
  pack .file.mask.label .file.mask.entry -in .file.mask \
 	-side left -padx 1m
  pack .file.name.label .file.name.entry -in .file.name \
  	-side left -padx 1m
  pack .file.list.scroll -in .file.list -side right -fill y
  pack .file.list.names -in .file.list -side left -fill x
  pack .file.butt.ok .file.butt.delete .file.butt.help \
  	.file.butt.cancel -in .file.butt \
  	-side top -fill x -padx 7m -pady 1m
  listFiles
  bind .file.name.entry <Return> {set waitname $GFNname}
  bind .file.mask.entry <Return> {listFiles}
  bind .file.dir.entry  <Return> {cd $fdir; set fdir [pwd]; listFiles}
  bind .file.list.names <Button-3> {
    selection clear  .file.list.names
    set GFNname ""
  }
  # in 4.0, local bindings fire first.. duplicate the class binding.
  bind .file.list.names <Button-1> {
    selection clear .file.list.names
    %W selection set [%W nearest %y]
    if {![file isdirectory [selection get]]} {
      set GFNname [selection get]
    }
  }
  bind .file.list.names <Double-1> { 
    %W selection set [%W nearest %y]
    if {[file isdirectory [selection get]]} { 
      cd [selection get]; set fdir [pwd]; listFiles 
    } else { 
      set GFNname [selection get]; set waitname $GFNname 
    }
  }

  normalCursor .file 
  tkwait visibility .file
  grab set .file
  focus .file.name.entry

  tkwait variable waitname
  destroy .file
  return $waitname
}

proc listFiles {} {
  global FMASK

  # clear existing data
  .file.list.names delete 0 end

  # show the parent
  if {[pwd] != "/"} {
    .file.list.names insert end ".."
  }

  # default mask of * -- I don't want to see it
  if {$FMASK == ""} {
    set realmask "*"
  } else {
    set realmask $FMASK
  }

  foreach i [lsort [glob "$realmask"]] {
    .file.list.names insert end $i
  }
}
# -------------------------------------------------------
# Clean out any existing MIDI files
#
proc fileNew {} {
  global Modified

  if {$Modified} {
    if {[dialog .h . "This will erase the\nexisting MIDI sequence!" \
       warning 1 {OK} {Cancel}] == 1} {
       return
     } 
  }
  midiCleanSlate 0
}

# -------------------------------------------------------
# Open a MIDI file
#
proc fileOpenMidi {} {
  global Modified PlayName

  if {$Modified} {
    if {[dialog .h . "This will overwrite the\nexisting MIDI sequence!" \
       warning 1 {OK} {Cancel}] == 1} {
       return
    } 
  }
  set tmpname $PlayName
  if {$tmpname == "untitled.mid"} {set tmpname ""}
  set PlayName [getFileName "Open a MIDI file" $tmpname]

  watchCursor .
  fileReadMidi
  normalCursor .
}

# -------------------------------------------------------
# Read a MIDI file
#
proc fileReadMidi {} {
  global TKS_VERSION
  global PlayFile MidiState PlayName Modified
  global HAVE_VOXWARE_GUS GUS_AUTO_LOAD PatchList PatchFile

  if {$PlayName == ""} { return }

  # if we already have a MIDI file open, blow it away
  if {$PlayFile != ""} {
    seqStop
    set tmpname $PlayName
    midiCleanSlate 0
    set PlayName $tmpname
  }
  if {[catch {eval "set ff [open $PlayName]"} msg]} {
    dialog .foops . "$msg" error 0 OK
    return
  }
  if {[catch {eval "set PlayFile [midiread $ff]"} msg]} {
    dialog .foops . "This doesn't look like a MIDI file." error 0 OK
    return
  }
  close $ff

  wm iconname . $PlayName
  wm title . "tkseq $TKS_VERSION:  $PlayName"

  buildMeterMap

  # need to update/display everything
  showTrackEverything {}
  set Modified 0

  if {$HAVE_VOXWARE_GUS && $GUS_AUTO_LOAD} {
    loadGusPatch $PatchList $PatchFile
  }

  updateButtons 1 1 0 0 0 0
}

# ------------------------------------------------------------------
# Quit
#
proc fileQuit {} {
  global NUMDEV DEVTAB HAVE_VOXWARE_GUS
  global MidiState Modified

  if {$Modified == 1} {
    if {[dialog .q . \
      "There may be unsaved work.\n           Quit anyway?" \
      warning 1 {OK} {Cancel}]} {
      return
    }
  } else {
    if {[dialog .q . "Really quit tkseq?" questhead 0 {OK} {Cancel}]} {
      return
    }
  }
  for {set i 0} {$i < $NUMDEV} {incr i} {
    midistop $DEVTAB($i,dev)
  }
 
  if {$HAVE_VOXWARE_GUS} {
    setGusThru 0
  }

  exit
}

# ------------------------------------------------------------------
# Save your work using the current filename
#
proc fileSaveMidi {} {
  global PlayFile PlayName Modified
  if {$PlayFile == ""} {
    dialog .h . "There is nothing to save." error 0 OK
    return
  }
  if {$PlayName == "" || $PlayName == "untitled.mid"} {
     fileSaveMidiAs
     return
  } else {
    writeTrackNames {}
    set ff [open $PlayName w]
    midiwrite $ff $PlayFile
    close $ff
    set Modified 0
  }
}
# ------------------------------------------------------------------
# Save your work under another name

proc fileSaveMidiAs {} {
  global PlayName Modified PlayFile
  if {$PlayFile == ""} {
    dialog .h . "There is nothing to save." error 0 OK
    return
  }
  set tmpname [getFileName "Save a MIDI file" $PlayName]
  if {$tmpname == ""} {
    return
  }

  if {[file exists $tmpname]} {
    if {[dialog .h . "File `$tmpname' exists." warning 1 Overwrite Cancel]} {
      return
    }
  }
  writeTrackNames {}
  set ff [open $tmpname w]
  set PlayName $tmpname
  midiwrite $ff $PlayFile
  close $ff
  set Modified 0
}

# -----------END File Menu procedures ------------------------------------
#
# -----------BEGIN Settings Menu procedures ------------------------------
# Most of these are handled just by cascade type menus
#
proc setVariance {} {
  global VARIANCE
  set VARIANCE [getLogScale .v . \
      "Randomization Variance" "" \
      {} $VARIANCE "helpVariance" ]
}

proc setMean {} {
  global MEAN
  set MEAN [getLinearScale .v . \
      "Randomization Mean" "" \
      {} $MEAN "helpVariance" ]
}

proc setSMPTEoffset {} {
  global SMPTEoffset

  dialog .d . \
    "You must insert MetaSMPTE\nevents manually for now." info 0 OK
}

proc editMap {type} {
  global PlayFile
  windowMappedEvent 0 $type
}

proc windowMetronome {} {
  global MeterMap MetroData PlayFile StopTime

  # some defaults should be done on the fly..
  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  getStopTime $PlayFile
  if {$StopTime > 0} {
    scan [tick2measure $StopTime] "%d:" MetroData(meas,stop)
  }

  # purely cosmetic..
  set MetroData(dur,meas)  [expr $mdiv / 4]
  set MetroData(dur,beat)  [expr $mdiv / 4]

  set win .metro
  toplevel $win -cursor watch
  wm transient $win .
  set x [expr [winfo x .]+320]
  set y [expr [winfo y .]+140]
  wm geometry $win "+$x+$y"

  frame $win.t  -relief raised -bd 2
  frame $win.m1 -relief raised -bd 2
  frame $win.m2 -relief raised -bd 2
  frame $win.b  -relief raised -bd 2

  label $win.message -text "Generate a Metronome Track"
  pack $win.message -in $win.t -expand 1 -fill x -ipady 3m

  frame $win.meas
  label $win.meas.subtitle -text "Duration in Measures" -anchor w
  label $win.meas.lstart -text " From " -anchor e
  entry $win.meas.start -width 4 -relief sunken -bd 2 \
    -textvariable MetroData(meas,start)
  label $win.meas.lfinish -text " to "
  entry $win.meas.finish -width 4 -relief sunken -bd 2 \
    -textvariable MetroData(meas,stop)
  pack $win.meas.subtitle -in $win.meas -side top -fill x -pady 1m 
  pack $win.meas.lstart $win.meas.start $win.meas.lfinish $win.meas.finish \
    -in $win.meas -side left -pady 1m
  pack $win.meas -in $win.m1 -side left -padx 2m

  frame $win.event
  label $win.event.subtitle -text "Event" -anchor w
  label $win.event.meas -text " Measure " -anchor e
  label $win.event.beet -text " Beat " -anchor e
  pack $win.event.subtitle $win.event.meas $win.event.beet -in $win.event \
    -side top -padx 1m -pady 1m

  frame $win.chan
  label $win.chan.subtitle -text "Ch"
  entry $win.chan.meas -width 3 -textvariable MetroData(chan,meas)
  entry $win.chan.beet -width 3 -textvariable MetroData(chan,beat)
  pack $win.chan.subtitle $win.chan.meas $win.chan.beet -in $win.chan \
    -side top -padx 1m -pady 1m

  frame $win.patch
  label $win.patch.subtitle -text "Pr"
  entry $win.patch.meas -width 3 -textvariable MetroData(patch,meas)
  entry $win.patch.beet -width 3 -textvariable MetroData(patch,beat)
  pack $win.patch.subtitle $win.patch.meas $win.patch.beet -in $win.patch \
    -side top -padx 1m -pady 1m

  frame $win.vol
  label $win.vol.subtitle -text "Vol"
  entry $win.vol.meas -width 3 -textvariable MetroData(vol,meas)
  entry $win.vol.beet -width 3 -textvariable MetroData(vol,beat)
  pack $win.vol.subtitle $win.vol.meas $win.vol.beet -in $win.vol \
    -side top -padx 1m -pady 1m

  frame $win.dur
  label $win.dur.subtitle -text "Dur" -anchor w
  entry $win.dur.meas -width 3 -textvariable MetroData(dur,meas)
  entry $win.dur.beet -width 3 -textvariable MetroData(dur,beat)
  pack $win.dur.subtitle $win.dur.meas $win.dur.beet -in $win.dur \
    -side top -padx 1m -pady 1m

  pack $win.event $win.chan $win.patch $win.vol $win.dur -in $win.m2 \
    -side left

  pack $win.t -in $win -side top -expand 1 -fill x
  pack $win.m1 -in $win -expand 1 -fill both -ipady 2m
  pack $win.m2 -in $win -expand 1 -fill both -ipadx 2m -ipady 2m 
  pack $win.b -in $win -expand 1 -fill x


  frame $win.butt 
  pack $win.butt -in $win.b -side top -expand 1 -fill x -padx 10m

  button $win.butt.ok -text "  OK  " \
    -command "watchCursor $win; makeMetronomeTrack; destroy $win"
  button $win.butt.cancel -text "Cancel" -command "destroy $win"

  pack $win.butt.ok $win.butt.cancel -in $win.butt -side left \
    -padx 4m -pady 1m -expand 1 -fill x

  normalCursor $win
  grab set $win
  focus $win.meas.finish

  tkwait window $win
}

proc makeMetronomeTrack {} {
  global MetroData PlayFile PlayName Modified
  global CHANNEL1

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  set otrk $mtrk
  set ntrk [expr $otrk + 1]
  midiconfig $PlayFile "tracks $ntrk"

  set evmeas "Note"
  set evbeat "Note"
  foreach i {chan patch vol dur} {
    set evmeas [lappend evmeas $MetroData($i,meas)]
    set evbeat [lappend evbeat $MetroData($i,beat)]
  }

  if {$CHANNEL1} {
    set evmeas [lreplace $evmeas 1 1 [expr $MetroData(chan,meas) - 1]]
    set evbeat [lreplace $evbeat 1 1 [expr $MetroData(chan,beat) - 1]]
  }
  midiput $PlayFile $otrk {0 MetaSequenceName "<metronome>"}

  for {set i $MetroData(meas,start)} {$i <= $MetroData(meas,stop)} {incr i} {
    midiput $PlayFile $otrk "[measure2tick $i:0:0] $evmeas"
    set num [lindex [getMeter $i:0:0] 2]
    for {set j 1} {$j < $num} {incr j} {
      midiput $PlayFile $otrk "[measure2tick $i:$j:0] $evbeat"
    }
  }
  addMetaEndOfTrack $PlayFile $otrk
  if {$PlayName == "untitled.mid" && $Modified == 0} {
    showTrackEverything 0
  }
  set Modified 1
  showTrackEverything $otrk
  updateButtons 1 1 0 0 0 0
}

# ------------------------------------------------------------------------ 
#
proc helpVariance {} {
  displayText .v.h "Help" 64 12 \
    "Tkseq is capable of adding subtle random perturbations\
     to the timing of your midi files.  These perturbations have normal\
     (or Gaussian) distribution with user-adjustable mean and variance.\n\n\
     Mean\
     corresponds to a deterministic offset in SMF ticks, e.g. if\
     you want to rush the snare a little, set the mean to be a small\
     negative number and randomize the track containing the snare drum.\n\n\
     Variance is a measure of how drastic the timing changes\
     will be -- larger values lead to bigger perturbations.\n\n\
     For starters, try using mean 0.0 and variance 1.0.  That\
     will move your events\
     by only a few ticks.  For more drastic effects, or to\
     simulate my brother after 27 beers, crank it up to 10.0\
     or higher.\n\n\
     Eventually the randomization routine may be\
     modified such that perturbations are normalized with\
     respect to the variable DIVISION, but for now everything\
     is in SMF ticks.\n"
}

proc helpPatchManager {} {
  global CHANNEL1

  displayText .pman.h "Help" 64 12 \
    "Choose AutoLoad to load patches based on the current MIDI song.\n\n\
     The >> and << buttons serve to move patches into RAM and out of RAM,\
     respectively.  Simply select the patch you are interested in, and\
     use one of these buttons to get it moving.  A double-click also serves\
     to move a patch from disk to RAM.\n\n\
     Audition is useful for checking out what a patch sounds like.  It puts\
     the patch into RAM if it's not already there, then drops in a program\
     change so it shows up on channel $CHANNEL1.  You can play notes via\
     your own controller, or using the mouse and the keyboard image built\
     into the patch manager window.\n\n\
     Clear does what you'd think.. it removes all patches from RAM.\n\n\
     Click on OK when you're done, and the patch manager will go away.\n"
}

proc setMidiThru {cdev val} {
  global DEVTAB MidiThru

  set MidiThru($cdev) $val

  if {[catch {eval "set ti [.mbar.settings.menu index {Enable Thru}]"}]} {
    set ti [.mbar.settings.menu index "Disable Thru"]
  }

  if {$val} {
    .mbar.settings.menu entryconfigure $ti \
      -label "Disable Thru" -underline 8
    mididevice $DEVTAB($cdev,dev) {midithru on}
  } else {
    .mbar.settings.menu entryconfigure $ti \
      -label "Enable Thru" -underline 7
    mididevice $DEVTAB($cdev,dev) {midithru off}
  }
}

proc setGusThru {val} {
  global GusThruPid

  if {[catch {eval "set ti [.mbar.settings.menu.gus index {Enable GUS}]"}]} {
    set ti [.mbar.settings.menu.gus index "Disable GUS"]
  }

  if {$val} {
    .mbar.settings.menu.gus entryconfigure $ti \
       -label "Disable GUS" -underline 0
    if {$GusThruPid == 0} {
      set GusThruPid [exec gusthru >& /dev/null &]
    }
  } else {
    .mbar.settings.menu.gus entryconfigure $ti \
      -label "Enable GUS" -underline 0
    if {$GusThruPid} {
      exec kill $GusThruPid
      set GusThruPid 0
    }
  }
  after 250
}

proc loadGusPatch {plist pfile} {
  global PATCH_DIR
  global GusThruPid PLoadList PLoadFile

  # be optimistic ;-)
  set success 1

  if {[winfo exists .pman]} {
    watchCursor .pman
  }

  set state $GusThruPid
  setGusThru 0

  if {$plist == "reset"} {
    exec gusload reset
    set PLoadList ""
    set PLoadFile ""
    if {[winfo exists .pman]} {
      normalCursor .pman
    }
    if {$state} {
      setGusThru 1
    }
    return
  }

# Fix ME I require all patches to be in the same directory

# build a list of patches we want loaded, i.e. those that are 
# already loaded plus the new ones.
  set tmpl $PLoadList; set tmpf $PLoadFile

  for {set i 0} {$i < [llength $plist]} {incr i} {
    set pnum [lindex $plist $i]; set pfil [lindex $pfile $i]

  # a new addition
    set lin [lsearch -exact $tmpl $pnum]
    if {$lin == -1} {
      set tmpl [lsort -integer [lappend tmpl $pnum]]
      set fin [lsearch -exact $tmpl $pnum]
      set tmpf [linsert $tmpf $fin $pfil]
    } else {
  # overwrite   
      set tmpl [lreplace $tmpl $lin $lin $pnum]
      set tmpf [lreplace $tmpf $lin $lin $pfil]
    }  
  }

  # Now we have the net result.. copy it to the GUS
  exec gusload reset
  for {set i 0} {$i < [llength $tmpl]} {incr i} {
    set pnum [lindex $tmpl $i]
    set pfil [lindex $tmpf $i]
    if {[catch "exec gusload $pnum $PATCH_DIR/$pfil.pat" msg]} {
      set success 0
      if {[string first "No space left" $msg] != -1} {
        dialog .err . \
    " Sample RAM is full.  Please reset\nand try again with fewer patches." \
          error 0 OK
      } else {
        dialog .err . "Error loading patch:\n$msg" error 0 OK
      }
      break
    }
  }
  if {$success} {
    set PLoadList $tmpl
    set PLoadFile $tmpf
  }
  if {$state} {
     setGusThru 1
  }
  if {[winfo exists .pman]} {
     normalCursor .pman
  }
}

proc patchManager {} {
  global PATCH_DIR PIANOSCALE PatchList PLoadList PLoadFile RawPitch
  global KeyYtag KeyYval BlackKey WhiteKey CurDev DEVTAB SHOWMIDC

# go to the patch directory, make sure it exists
  set olddir [pwd]
  if {[catch "cd $PATCH_DIR" msg]} {
     dialog .err . "Cannot find patch directory:\n$msg" error OK
     cd $olddir
     return
  }
  set w .pman
  toplevel $w -cursor watch
  set x [expr [winfo x .]+80]
  set y [expr [winfo y .]+60]
  wm title $w "Patch Manager"
  wm geometry $w "+$x+$y"

  label $w.whitekey; label $w.blackkey; label $w.activekey
  set WhiteKey [lindex [$w.whitekey configure -background] 4]
  set BlackKey [lindex [$w.blackkey configure -background] 4]
  set ActiveKey [lindex [$w.activekey configure -background] 4]

  frame $w.t -relief raised -bd 2
  frame $w.m -relief raised -bd 2
  frame $w.t.l
  frame $w.t.r
  frame $w.l	
  frame $w.c -relief sunken -bd 2
  frame $w.r 
  frame $w.b

  set octwidth [expr  7.000000 * $PIANOSCALE]
  set xnote    [expr  5.000000 * $octwidth]
  set y0       0
  set y1w      [expr  5.5 * $PIANOSCALE]
  set y1b      [expr  3.000000 * $y1w / 5.000000]

  canvas $w.keys -width $xnote -height $y1w

  message $w.t.l.message -width 2i -text " On Disk "
  listbox $w.l.list -relief sunken -borderwidth 2 \
  	-yscrollcommand "$w.l.scroll set" -width 14 -selectmode single
  scrollbar $w.l.scroll -command "$w.l.list yview"

  message $w.t.r.message -width 2i -text " In RAM "
  listbox $w.r.list -relief sunken -borderwidth 2 \
  	-yscrollcommand "$w.r.scroll set" -width 14 -selectmode single
  scrollbar $w.r.scroll -command "$w.r.list yview"

  button $w.autoload -text "Autoload" -underline 0\
    -command { loadGusPatch $PatchList $PatchFile; patchRefreshList }
  button $w.load -text ">>" -state disabled -command {
    if {[.pman.l.list curselection] != ""} {
      loadSinglePatch [selection get]
    }}
  button $w.clear -text "<<" -state disabled -command {
    if {[.pman.r.list curselection] != ""} {
      clearSinglePatch [selection get]
      .pman.load configure -state disabled
      .pman.clear configure -state disabled
      .pman.audition configure -state disabled
    }}
  button $w.audition -text "Audition" -state disabled -underline 1\
     -command { auditionPatch }
  button $w.allclear -text "Clear" -underline 0\
    -command "loadGusPatch reset {}; patchRefreshList"
  button $w.help -text "Help" -underline 0 -command { helpPatchManager }
  button $w.ok -text "OK" -underline 0 -command "destroy $w"

  pack $w.t -side top -fill x

  pack $w.t.l -in $w.t -side left  -fill x -expand 1
  pack $w.t.r -in $w.t -side right -fill x -expand 1
  pack $w.t.l.message -in $w.t.l -side left -ipadx 8m
  pack $w.t.r.message -in $w.t.r -side right -ipadx 8m

  pack $w.b -side bottom
  pack $w.keys -side bottom -in $w.b

  pack $w.m -side top -fill both
  pack $w.l -side left  -in $w.m -fill y
  pack $w.r -side right -in $w.m -fill y
  pack $w.c -side top   -in $w.m -fill x -ipady 0.3m

  pack $w.l.scroll -in $w.l -side right -fill y
  pack $w.l.list -in $w.l -side left -fill y -expand 1

  pack $w.r.scroll -in $w.r -side right -fill y
  pack $w.r.list -in $w.r -side left -fill y -expand 1

  pack $w.autoload $w.load $w.clear $w.audition $w.allclear $w.help $w.ok \
     -in $w.c -side top -fill x -expand 1 -ipady 0.5m

  for {set k 0} {$k < 5} {incr k} {
    for {set j 0} {$j < 12} {incr j} {
      if {[lindex $KeyYtag($j) 0] == "white"} {
        set width $PIANOSCALE;
        set fill $WhiteKey; set y1 $y1w
      } else {
        set width [expr $PIANOSCALE / 2.000000]
        set fill $BlackKey; set y1 $y1b
      }
      set x0 [expr ($k + $KeyYval($j)) * $octwidth]
      set x1 [expr $x0 + $width]

      # First note is C1, i.e. 36
      $w.keys create rectangle $x0 $y0 $x1 $y1 -fill $fill \
        -outline black -tags "n[expr 12*$k+$j+36] $KeyYtag($j)"
    }
  }
  $w.keys raise black white

  if {$SHOWMIDC} {
    $w.keys itemconfigure n60 -fill $ActiveKey
  }

  if {$DEVTAB($CurDev,raw) != ""} {
    bind $w.keys <Button-1> {
      set RawPitch \
        [string range [lindex [%W gettags [%W find withtag current]] 0] 1 end]
      midisend $DEVTAB($CurDev,raw) "0 NoteOn 0 $RawPitch 100"
    }
    bind $w.keys <B1-Enter> { 
      set RawPitch \
        [string range [lindex [%W gettags [%W find withtag current]] 0] 1 end]
      midisend $DEVTAB($CurDev,raw) "0 NoteOn 0 $RawPitch 100"
    }
    bind $w.keys <ButtonRelease-1> {
      midisend $DEVTAB($CurDev,raw) "0 NoteOff 0 $RawPitch 0"
    }
    bind $w.keys <B1-Leave> {
      midisend $DEVTAB($CurDev,raw) "0 NoteOff 0 $RawPitch 0"
    }
    bind $w.keys <B1-Motion> {
      set opitch $RawPitch
      set RawPitch \
        [string range [lindex [%W gettags [%W find withtag current]] 0] 1 end]
      if {$opitch != $RawPitch} {
         midisend $DEVTAB($CurDev,raw) "50 NoteOff 0 $opitch 0"
         midisend $DEVTAB($CurDev,raw) "0 NoteOn 0 $RawPitch 100"
      }
    }
  }

  bind $w.l.list <Button-3> {
     selection clear %W
     .pman.load configure -state disabled
     .pman.clear configure -state disabled
     .pman.audition configure -state disabled
  }
  bind $w.r.list <Button-3> {
     selection clear %W
     .pman.load configure -state disabled
     .pman.clear configure -state disabled
     .pman.audition configure -state disabled
  }

#  in 4.0, local bindings fire first.. duplicate the class binding.
  bind $w.l.list <Button-1> {
    selection clear %W
    %W selection set [%W nearest %y]
    .pman.load configure -state normal
    .pman.clear configure -state disabled
    .pman.audition configure -state normal
  }

  bind $w.r.list <Button-1> {
    selection clear %W
    %W selection set [%W nearest %y]
    .pman.load configure -state disabled
    .pman.clear configure -state normal
    .pman.audition configure -state normal
  }

  bind $w.l.list <Double-1> { 
    loadSinglePatch [selection get]
  }

  $w.l.list delete 0 end
  foreach i [lsort [glob "*.pat"]] {
    $w.l.list insert end [format " %12s" $i]
  }
  cd $olddir
  patchRefreshList
  normalCursor $w 
}

proc patchRefreshList {} {
  global PLoadList PLoadFile

  if {[winfo exists .pman]} {
    if {[.pman.r.list curselection] != ""} {
      set cf [string trim [string range [selection get] 4 end]]
    } else {
      set cf ""
    }
    .pman.r.list delete 0 end

    for {set i 0} {$i < [llength $PLoadList]} {incr i} {
      set pnum [lindex $PLoadList $i]
      set pfil [lindex $PLoadFile $i]
      .pman.r.list insert end [format "%3d: %8s" $pnum $pfil]
    }

    if {$cf != ""} {
      set cp [lsearch -exact $PLoadFile $cf]
      if {$cp != -1} {
        .pman.r.list selection set $cp
      }
    }
  }
}

proc loadSinglePatch {fname} {
  global GM_PATS PLoadFile

  set pname [string range $fname 0 [expr [string first . $fname] - 1]]
  set pname [string trim $pname]
  set index [lsearch -exact $GM_PATS $pname]

  # already loaded
  if {[lsearch -exact $PLoadFile $pname] != -1} {
    return
  }

  while {$index == -1} {
    set index \
      [getEntry .pman.et .pman "Loading Non-GM Patch" {} \
      "Patch Number:" "0" 10 140 100]
    if {$index < 0 | $index > 127} {
      dialog .pman.err "Patch value out of range." error 0 OK
      set index == -1
    }
    if {[expr $index - round($index)] != 0} {
      dialog .pman.err "Expecting an integer." error 0 OK
      set index == -1
    }   
  }  
  loadGusPatch $index $pname 
  patchRefreshList
}
 
proc clearSinglePatch {fstring} {
  global PLoadList

  # Because of limitations in gusload, we do this by brute force
  set pnum [string trim [string range $fstring 0 2]]
  set index [lsearch -exact $PLoadList $pnum]
  if {$index == -1} { return }
  set tmpl [lreplace $PLoadList $index $index]
  set tmpf [lreplace $PLoadFile $index $index]

  loadGusPatch reset {}
  loadGusPatch $tmpl $tmpf
  patchRefreshList
}

proc auditionPatch {} {
  global DEVTAB CHANNEL1
  global CurDev PLoadList PLoadFile

  if {[.pman.l.list curselection] != ""} {
    set fname [selection get]
    loadSinglePatch $fname
# I hate to work this hard.. :(
    set pname \
      [string trim [string range $fname 0 [expr [string first . $fname] - 1]]]
    set index [lsearch -exact $PLoadFile $pname]
    set audition [lindex $PLoadList $index]
  } elseif {[.pman.r.list curselection] != ""} {
    set audition [string trim [string range [selection get] 0 2]]
  } else {
    return
  }

  if {$DEVTAB($CurDev,raw) != ""} {
    midisend $DEVTAB($CurDev,raw) "0 Program 0 $audition" 
  }
}
 
# -------- END Settings Menu commands -------------------------------
#
# -------- BEGIN Track Menu commands --------------------------------
#
# Process track selection and perform desired action
#
proc trackMenu {} {
  set seltrklst [.trkname.list curselection]
  if {$seltrklst == ""} {
    set state disabled
  } else {
    set state normal
  }
  for {set i 0} {$i <= [.mbar.track.menu index last]} {incr i} {
    if {[.mbar.track.menu type $i] == "command"} {
      .mbar.track.menu entryconfigure $i -state $state
    }
  }
}

proc track {Command} {
  global Modified PlayFile

  set seltrklst [.trkname.list curselection]
  if {$seltrklst == ""} {
    dialog .r . "Select a track first." error 0 OK
    return
  }

  for {set k [expr [llength $seltrklst]-1]} {$k >=0} {incr k -1} {
    # an empty track has no name.  remove it from the list
    set seltrk [lindex $seltrklst $k]
    set i [lindex $seltrklst $k]
    set tmpname [.trkname.list get $i]
    if {$tmpname == ""} {
       set seltrklst [lreplace $seltrklst $k $k]
    }
  }  
  # are there any left?
  if {$seltrklst != ""} {
    set seltrklst [lsort -integer $seltrklst]
    track$Command $seltrklst
  } else {
    dialog .r . "Those tracks are empty." error 0 OK
  }
}
# ---------------------------------------------------------------------
# Make a copy of an existing track, storing it in the next open slot
#
proc trackCopy {tlist} {
  global Modified PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  set newlist ""
  set otrk $mtrk
  set ntrk [expr [llength $tlist]+$otrk]
  midiconfig $PlayFile "tracks $ntrk"

  watchCursor .
  for {set i $otrk} {$i < $ntrk} {incr i} {
    set k [lindex $tlist [expr $i-$otrk]]
    set newlist "$newlist $i"
    midicopy "$PlayFile $i" 0 "$PlayFile $k" 0 \
              [expr "[miditrack $PlayFile $k end] + 1"]
  }  
  normalCursor .

  showTrackEverything $newlist
  set Modified 1
}

# ---------------------------------------------------------------------
# remove all events, leaving only MetaSequenceName and MetaEndOfTrack
#
proc trackErase {tlist} {
  global Modified PlayFile

  watchCursor .
  foreach k $tlist {
    mididelete $PlayFile $k range 0 [miditrack $PlayFile $k end]
    writeTrackNames $k
    fillTrackInfo $k 0
  }  
  showTrackEverything $tlist
  normalCursor .
  set Modified 1
}

# ---------------------------------------------------------------------
# Put an event list in a pop-up window -- has some editing capabilities
#
proc trackInfo {tlist} {
  global TEAROFF

  foreach k $tlist {
    set selname [.trkname.list get $k]
    # open the info window only if it's not there already
    if {[winfo exists .ti$k]} {
      wm deiconify .ti$k
      fillTrackInfo $k 1
    } else {
      toplevel .ti$k -cursor watch
#      wm minsize .ti$k 1 1
      frame .ti$k.mbar -relief raised -bd 1
      pack .ti$k.mbar -side top -fill x
      listbox .ti$k.list -relief sunken -bd 1 \
        -yscrollcommand ".ti$k.scroll set" -width 40 -height 20 \
        -selectmode extended
      scrollbar .ti$k.scroll -command ".ti$k.list yview"
      pack .ti$k.scroll -side right -fill y
      pack .ti$k.list -side left -fill both -expand 1
      menubutton .ti$k.mbar.trk -text File -underline 0 \
        -menu .ti$k.mbar.trk.m 
      menubutton .ti$k.mbar.edit -text Edit -underline 0 \
        -menu .ti$k.mbar.edit.m 
      menubutton .ti$k.mbar.view -text View -underline 0 \
        -menu .ti$k.mbar.view.m 

      menu .ti$k.mbar.trk.m -tearoff $TEAROFF
      .ti$k.mbar.trk.m add command -label Reread -underline 0 \
        -command "remapTrackInfo $k $k"
      .ti$k.mbar.trk.m add command -label Dismiss -underline 0 \
         -command "destroy .ti$k" 
      menu .ti$k.mbar.edit.m -tearoff $TEAROFF
      .ti$k.mbar.edit.m add command -label Delete -underline 0 \
        -command "deleteEvents $k"
      .ti$k.mbar.edit.m add command -label Modify -underline 0 \
        -command "modifyEvents $k"
      .ti$k.mbar.edit.m add command -label Copy -underline 0 \
        -command "copyEvents $k"
      menu .ti$k.mbar.view.m -tearoff $TEAROFF
      .ti$k.mbar.view.m add radiobutton -label Measures -underline 0 \
        -variable MeasView -value 1 -command "remapTrackInfo $k $k"
      .ti$k.mbar.view.m add radiobutton -label Ticks -underline 0 \
        -variable MeasView -value 0 -command "remapTrackInfo $k $k"

      pack .ti$k.mbar.trk .ti$k.mbar.edit .ti$k.mbar.view -side left 
      bind .ti$k.list <Button-3> "selection clear .ti$k.t"
      wm title .ti$k "Track $k: $selname"
      wm iconname .ti$k "Track $k"
      fillTrackInfo $k 0
      normalCursor .ti$k
    }
  }
}
#
#------------------------------------------------------------------------
# Find the time of the last event in a MIDI file
#
proc getStopTime {mf} {
  global StopTime

  set eottime 0
  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  for {set i 0} {$i < $mtrk} {incr i} {
    set ticks [miditrack $mf $i end]
    if {$ticks > $eottime} { set eottime $ticks }
  }
  set StopTime $eottime
}

#----------------------------------------------------------------------
# Merge several tracks.  Be careful about multiple MetaEOTs.
#
proc trackMerge {tlist} {
  global Modified PlayFile

  set ntrk [llength $tlist]
  if {$ntrk == 1} { 
    dialog .d . "That was easy." info 0 OK
    return
  }

# get configuration of existing file..
  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

# make scratch space
  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "format $mfmt" "tracks 1" 
  set mlist "{$tmpf 0}"
  set dtrk [lindex $tlist 0]
  set strk [lrange $tlist 1 end]

  watchCursor .
  for {set i 0; set tname ""} {$i < $ntrk} {incr i} {
    set trk [lindex $tlist $i]
    set mlist "$mlist {$PlayFile $trk}"
    set tname [format "%s+%s" $tname [.trkname.list get $trk]]
  }
  if {[catch {eval "midimerge $mlist"} msg]} {
    # Oops.  Merge failed.
    dialog .d . "Merge failed! Mail greg@@eecs.berkeley.edu" error 0 OK
    normalCursor .
    midifree $tmpf
    return
  } else {
    # put everything in the lowest position -- the merge worked
    set eottime [expr [miditrack $tmpf 0 end] + 1]
    mididelete $PlayFile $dtrk range 0 $eottime
    midicopy "$PlayFile $dtrk" 0 "$tmpf 0" 0 $eottime
    fixMetaEndOfTrack $PlayFile $dtrk

    # name it after all the merged tracks
    .trkname.list delete $dtrk
    .trkname.list insert $dtrk "[string range $tname 1 end]"
    writeTrackNames $dtrk

    midiremove $PlayFile $strk 1
    midifree $tmpf
    normalCursor .
  }

  # did one of the merged tracks have an event list up?
  foreach i $tlist {
    if {[winfo exists .ti$i]} {
      remapTrackInfo $i $dtrk
      break
    }
  }
  # shuffle the rest down 
  set h [expr $dtrk + 1]
  for {set i $h; set k $h} {$i < $mtrk} {incr i} {
    if {[lsearch -exact $tlist $i] == -1} {
      remapTrackInfo $i $k
      incr k
    } else {
      closeTrackInfo $i
    }
  }

  showTrackEverything $tlist
  set Modified 1
}

#----------------------------------------------------------------------
#
proc remapTrackInfo {old new} {
  # there may be nothing to remap
  if {![winfo exists .ti$old]} { return }

  set mapped [winfo ismapped .ti$old]
  set start [lindex [.ti$old.scroll get] 0]
  set geom [wm geometry .ti$old]

  # if the window exists with the right name, just fix it up
  if {$new == $old} {
    set selname [.trkname.list get $old]
    wm title .ti$old "Track $old: $selname"
    watchCursor .ti$old
    fillTrackInfo $old 1
    normalCursor .ti$old

  # otherwise create it from scratch
  } else {
    destroy .ti$old

  # create the new one with same state
    trackInfo $new
    wm geometry .ti$new $geom 
    .ti$new.list yview moveto $start
    if {! $mapped} { wm iconify .ti$new }
  }
}

#----------------------------------------------------------------------
# Add tracks to the mute list.  Muted tracks are stripped out 
# later, just before playing.
#
proc trackMute {tlist} {
  global MuteList PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  # either add it if it's not there, or remove it if it is.
  foreach ltrk $tlist {
    set lpos [lsearch -exact $MuteList $ltrk]
    if {$lpos == -1} {
      if {$mfmt == 1 && $ltrk == 0} {
        dialog .muterr . "Format 1, Track 0.  Really?" error 0 Nahh.. 
        return
      } else {
        set MuteList [lsort -integer "$MuteList $ltrk"]
      }
    } else {
      set MuteList [lreplace $MuteList $lpos $lpos]
    }
  }
  showTrackMuteList
}

proc trackSolo {trk} {
  global SoloList PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  if {[llength $trk] != 1} {
    dialog .solo . "Ummm.. which one should I solo?" questhead 0 Nevermind
    return
  }

  if {$mfmt == 1 && $trk == 0} {
    dialog .muterr . "Format 1, Track 0.  Really?" error 0 Nahh..
    return
  }

  # either remove it if it is solo'd, or force it if it's not.
  set foo $SoloList
  if {$foo == ""} {
    set SoloList $trk
  } elseif {$foo != $trk} {
    set SoloList $trk
  } else {
    set SoloList ""
  }
  showTrackMuteList
}

# this only works for a single track -- bound to a mouse click
proc trackMuteSolo {trk} {
  global MuteList SoloList PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  if {$mfmt == 1 && $trk == 0} {
    dialog .muterr . "Format 1, Track 0.  Really?" error 0 Nahh..
    return
  }

  # Solo -> Mute -> Play -> Solo, etc..
  set mutepos [lsearch -exact $MuteList $trk]

  # case 1: Solo->Mute
  if {$SoloList == $trk} {
    set SoloList "" 
    if {$mutepos == -1} {
      set MuteList [lsort -integer "$MuteList $trk"]
    }

  # case 2: Mute->Play
  } elseif {$mutepos != -1} {
    set MuteList [lreplace $MuteList $mutepos $mutepos]

  # case 3: Play->Solo
  } else {
    set SoloList $trk
  }

  showTrackMuteList
}
# -------------------------------------------------------------------
#
proc trackForceChannel {tlist} {
  global CHANNEL1 SHOWCHAN
  global PlayFile

  if {$PlayFile == ""} { return } 

  set lost 0
  set tlist [fixTrackList $tlist]
  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  foreach i $tlist {
    set ochan [.trkchan.list get $i]
    if {$ochan == "--"} {
      dialog .d . "Track $i has no channel\nrelated messages." info 0 OK
      return
    }
    if {$ochan == "**"} {
      set ochan 99
    } else {
      set ochan [expr $ochan]
    }

    set nchan [getChannel $i $ochan]
    if {$nchan == 99} {return}

    set nchan [expr $nchan - $CHANNEL1]

    # now edit every event
    watchCursor .
    set tmpf [midimake]
    midiconfig $tmpf "division $mdiv" "tracks 1" "format 0"
    midirewind $PlayFile $i
    while {[set event [midiget $PlayFile $i next]] != "EOT"} {
      set etype [string range [lindex $event 1] 0 3]
      if {$etype != "Meta" && $etype != "Syst"} {
        set event [lreplace $event 2 2 $nchan]
      }
      if {[catch {midiput $tmpf 0 "$event"} msg]} {
        dialog .lost . "Unable to put event:\n $event" info 0 {OK}
        incr lost
      }
    }
    set eottime [expr [miditrack $PlayFile $i end] + 1]
    mididelete $PlayFile $i range 0 $eottime
    midicopy "$PlayFile $i" 0 "$tmpf 0" 0 $eottime
    midifree $tmpf

    remapTrackInfo $i $i

#    dialog .lost . "$lost events were lost\nas duplicates." info 0 OK
    normalCursor .
  }
  if {$SHOWCHAN} { showTrackChannels $tlist }
  set Modified 1
}

proc trackProgramChange {tlist} {
  windowMappedEvent $tlist Patch
}

proc trackParameterSet {tlist} {
  windowMappedEvent $tlist Parameter
}

#
#-----------------------------------------------------------------

proc loadMappedEventData {} {
  global Evnt Elist

  set Elist "Patch Parameter Tempo Meter Key"

#  Time Program Channel Patch
  set Evnt(Patch,N) 4
  set Evnt(Patch,T) 1
  set Evnt(Patch,H) "Program Changes for Track"
  set Evnt(Patch,0) Time
  set Evnt(Patch,1) Program
  set Evnt(Patch,2) Channel
  set Evnt(Patch,3) Patch

#  Time Parameter Channel Name Value
  set Evnt(Parameter,N) 5
  set Evnt(Parameter,T) 1
  set Evnt(Parameter,H) "Parameter Changes for Track"
  set Evnt(Parameter,0) Time
  set Evnt(Parameter,1) Parameter
  set Evnt(Parameter,2) Channel
  set Evnt(Parameter,3) Parameter
  set Evnt(Parameter,4) Value

#  Time MetaTempo Tempo
  set Evnt(Tempo,N) 3
  set Evnt(Tempo,T) 0
  set Evnt(Tempo,H) "Tempo Changes"
  set Evnt(Tempo,0) Time
  set Evnt(Tempo,1) MetaTempo
  set Evnt(Tempo,2) Tempo

#  Time MetaTime Num Den Clocks 32nds
  set Evnt(Meter,N) 6
  set Evnt(Meter,T) 0
  set Evnt(Meter,H) "Time Signature Changes"
  set Evnt(Meter,0) Measure
  set Evnt(Meter,1) MetaTime
  set Evnt(Meter,2) Numerator
  set Evnt(Meter,3) Denominator
  set Evnt(Meter,4) Clocks
  set Evnt(Meter,5) 32nds

#  Time MetaKey Pitch Maj/Min
  set Evnt(Key,N) 4
  set Evnt(Key,T) 0
  set Evnt(Key,H) "Key Signature Changes"
  set Evnt(Key,0) Measure
  set Evnt(Key,1) MetaKey
  set Evnt(Key,2) Key
  set Evnt(Key,3) Maj/Min
}

proc windowMappedEvent {tlist ix} {
  global SHOWPROG
  global Gmf Gtrk Gdat Evnt PlayFile PlayName Modified

  if {$PlayFile == ""} { 
    return
  } else {
    set Gmf($ix) $PlayFile
  }

  set win .[string tolower $ix]
  set tlist [fixTrackList $tlist]

  set track $Evnt($ix,T)
  set head  $Evnt($ix,H)
  if {$Evnt($ix,2) == "Channel"} { 
    set channel 1 
  } else {
    set channel 0
  }

  foreach trk $tlist {
    set Gtrk($ix) $trk
    if {$track} { set head "$Evnt($ix,H) $trk" }

    toplevel $win -cursor watch
    wm transient $win .
    set x [expr [winfo x .]+320]
    set y [expr [winfo y .]+140]
    wm geometry $win "+$x+$y"

    frame $win.t -relief raised -bd 2
    frame $win.b -relief raised -bd 2
    frame $win.l
    frame $win.r 
    frame $win.l.t 
    frame $win.l.b 

    for {set i 0} {$i < $Evnt($ix,N)} {incr i} { 
      if {$i != 1} {
        set Gdat($ix,$i) ""
        frame $win.dat$i
        label $win.dat$i.label -width 12 -text "$Evnt($ix,$i):" -anchor e
        entry $win.dat$i.entry -width 9 -relief sunken \
          -bd 2 -textvariable Gdat($ix,$i)
      }
    }

    frame $win.list
    frame $win.butt

    listbox $win.list.names -relief sunken -borderwidth 2 \
    	-yscrollcommand "$win.list.scroll set" \
    	-width 32 -selectmode single
    scrollbar $win.list.scroll -command "$win.list.names yview"

    button $win.butt.modify -text Apply -state disabled \
      -command "modifyMappedEvent $ix"
    button $win.butt.add -text Add -command "addMappedEvent $ix 0"
    button $win.butt.remove -text Remove -state disabled \
      -command "removeMappedEvent $ix"
    button $win.butt.ok -text OK -command "destroy $win"

    message $win.message -width 3.5i -text "$head"

    pack $win.t -side top -fill x
    pack $win.b -side bottom -fill y
    pack $win.l -in $win.b -side left -fill y
    pack $win.r -in $win.b -side right -fill y
    pack $win.l.t -in $win.l -side top -fill x
    pack $win.l.b -in $win.l -side right -fill x

    pack $win.butt -in $win.l.b -side right
    pack $win.message -in $win.t -fill x \
      -ipadx 1m -ipady 1m
    pack $win.list -in $win.r -side left -fill y

    for {set i 0} {$i < $Evnt($ix,N)} {incr i} {
      if {$i != 1} {
        pack $win.dat$i -in $win.l.t -side top -fill x -padx 0.5m
        pack $win.dat$i.label $win.dat$i.entry -in $win.dat$i \
	  -side left -padx 1m 
      }
    }

    pack $win.list.scroll -in $win.list -side right -fill y
    pack $win.list.names -in $win.list -side left -fill y
    pack $win.butt.modify $win.butt.add $win.butt.remove \
    	$win.butt.ok -in $win.butt \
    	-side top -fill both -pady 0.5m -padx 1m -expand y

    bind $win.list.names <Button-1> "fillMappedEvent $ix %y"
    bind $win.list.names <Button-3> "clearMappedEvent $ix"

    listMappedEvent $ix

    tkwait visibility $win

    if {$ix == "Key"} {
      windowKeyboardMap $win
    }

    normalCursor $win
    grab set $win
    focus $win.dat0.entry

    tkwait window $win

    if {$ix == "Patch"} {
      if {$SHOWPROG} { showTrackPrograms $trk }
    }
    remapTrackInfo $trk $trk
    showTrackEverything $trk
  }
}

proc windowKeyboardMap {win} {
  global DEVTAB CurDev
  global Gdat GetFlat TagList SelKey KeyYtag KeyYval 
  global WhiteKey BlackKey ActiveKey HotKey HotList
  global RawPitch

  set GetFlat 0
  set Gdat(Key,2) ""
  set Gdat(Key,3) ""
  set TagList ""
  set SelKey ""

  set w $win.board
  toplevel $w -class Dialog
  wm transient $w $win
  set x [expr [winfo x $win]-220]
  set y [expr [winfo y $win]+110]
  wm geometry $w "+$x+$y"

  # these are used to grab resources.. that's all.
  label $w.whitekey; label $w.blackkey; label $w.activekey
  set WhiteKey [lindex [$w.whitekey configure -background] 4]
  set BlackKey [lindex [$w.blackkey configure -background] 4]
  set ActiveKey [lindex [$w.activekey configure -background] 4]

  frame $w.l -relief raised -bd 2
  frame $w.r -relief raised -bd 2
  canvas $w.keys -width 7c -height 5c
  radiobutton $w.maj -text Major -variable Gdat(Key,3) -value "major" \
    -command "keyToggle" -relief raised -bd 2
  radiobutton $w.min -text Minor -variable Gdat(Key,3) -value "minor" \
    -command "keyToggle" -relief raised -bd 2
  button $w.flat -text "#/b" -state disabled \
    -command "incr GetFlat; keyToggle"

  pack $w.l $w.r -in $w -side left -padx 1m -pady 1m
  pack $w.keys -in $w.l -side left 

  pack $w.maj $w.min -in $w.r -side top -fill x -ipadx 2m -ipady 1m
  pack $w.flat -in $w.r -side top -fill x

  set pscale 30

  for {set j 0} {$j < 12} {incr j} {
    set keytype [lindex $KeyYtag($j) 0]
    if {$keytype == "white"} {
      set fill $WhiteKey
      set width $pscale; set y1 [expr 5.5 * $pscale]
    } else {
      set fill $BlackKey
      set width [expr $pscale / 2.000000]
      set y1 [expr 5.5 * $pscale * 0.6]
    }
    set y0 0
    set x0 [expr $KeyYval($j) * 7.000000 * $pscale]
    set x1 [expr $x0 + $width]

    $w.keys create rectangle $x0 $y0 $x1 $y1 -fill $fill \
      -outline $BlackKey -tags "n[expr $j+60] $KeyYtag($j)"
  }
  $w.keys raise black white

  bind $w.keys <Button-1> { 
    if {$SelKey != ""} {
      set keytype [lindex $TagList 1]
      if {$keytype == "white"} {
        %W itemconfigure $SelKey -fill $WhiteKey
      } else {
        %W itemconfigure $SelKey -fill $BlackKey
      }
    }
    set SelKey [%W find withtag current]
    %W itemconfigure $SelKey -fill $ActiveKey
    set TagList [%W gettags $SelKey]

    if {$DEVTAB($CurDev,raw) != ""} {
      set RawPitch [string range [lindex $TagList 0] 1 end]
      midisend $DEVTAB($CurDev,raw) "0 NoteOn 0 $RawPitch 100"
    }
    set GetFlat 0;
    keyToggle
  } 

  if {$DEVTAB($CurDev,raw) != ""} {
    bind $w.keys <ButtonRelease-1> {
      midisend $DEVTAB($CurDev,raw) "0 NoteOff 0 $RawPitch 0"
    }
    bind $w.keys <B1-Leave> {
      midisend $DEVTAB($CurDev,raw) "0 NoteOff 0 $RawPitch 0"
    }
  }
}

proc keyToggle {} {
  global Gdat GetFlat TagList

  if {$TagList == "" } {return}

  set GetFlat [expr $GetFlat % 2]
  if {[lindex $TagList 1] == "black"} {
    set foo [expr 2 + $GetFlat]
    .key.board.flat configure -state normal
  } else {
    set foo 2
    .key.board.flat configure -state disabled
  }
  set Gdat(Key,2) "[lindex $TagList $foo]"
}

#------------------------------------------------------------------
#
# These are general

proc listMappedEvent {type} {
  global CHANNEL1 Gmf Gtrk Evnt

  set win .[string tolower $type]
  set mf $Gmf($type); set trk $Gtrk($type)

  $win.list.names delete 0 end
  midirewind $mf $trk
  while {[set event [midiget $mf $trk next]] != "EOT"} {
    if {[lindex $event 1] == $Evnt($type,1)} {
      set ticks [lindex $event 0]
      set event [lreplace $event 0 0 [tick2measure $ticks]]
      if {$Evnt($type,2) == "Channel"} {
        set chan [lindex $event 2]
        set event [lreplace $event 2 2 [expr $chan + $CHANNEL1]]
      }
      $win.list.names insert end $event
    }
  }
}

proc removeMappedEvent {type} {
  set win .[string tolower $type]
  if { "[$win.list.names curselection]" != "" } {
    delMappedEvent $type [selection get]; clearMappedEvent $type
  }
}

proc modifyMappedEvent {type} {
  set win .[string tolower $type]
  if { "[$win.list.names curselection]" != "" } { addMappedEvent $type 1 }
}

proc clearMappedEvent {type} {
  global Evnt Gdat
  set win .[string tolower $type]
  selection clear $win.list.names
  $win.butt.modify configure -state disabled
  $win.butt.remove configure -state disabled
  for {set i 0} {$i < $Evnt($type,N)} {incr i} {set Gdat($type,$i) ""}
}

proc fillMappedEvent {type yval} {
  global Evnt Gdat

  # Hack to duplicate class binding
  set win .[string tolower $type]
  $win.list.names selection set [$win.list.names nearest $yval]

  set event [selection get]
  $win.butt.modify configure -state normal
  $win.butt.remove configure -state normal
  for {set i 0} {$i < $Evnt($type,N)} {incr i} {
    set Gdat($type,$i) [lindex $event $i]
  }
  if {$Evnt($type,0) == "Measure"} {
    scan [lindex $event 0] "%d:" Gdat($type,0)
  }
}

proc addMappedEvent {type old} {
  global CHANNEL1
  global Gmf Gtrk Gdat Evnt Modified
  set win .[string tolower $type]

  set Gdat($type,1) Evnt($type,1)

  set mf $Gmf($type)
  set trk $Gtrk($type)

  for {set i 0} {$i < $Evnt($type,N)} {incr i} {
    if {$Gdat($type,$i) == ""} { 
      dialog $win.err $win "Fill in all the blanks!" error 0 OK
      return 1
    }
  }

  if {$Evnt($type,0) == "Measure"} {
    scan $Gdat($type,0) "%d:" ttmp
    set ttmp "$ttmp:0:0"
  } else {
    set ttmp $Gdat($type,0)
  }

  set foobar [catch "set rtime [measure2tick $ttmp]"]
  if {$foobar} {
    dialog $win.pt $win "Invalid time specification.  Ex: 0:00:000" \
      error 0 OK
    return 1
  }

  set event ""
  if {$Evnt($type,2) == "Channel"} {
    set foobar [catch "set rchan [expr $Gdat($type,2) - $CHANNEL1]"]
    if {$foobar || $rchan < 0 || $rchan > 15} {
      dialog $win.pt $win "Invalid channel specification." error 0 OK
      return 1
    }
    set event [lappend event $rtime $Evnt($type,1) $rchan]; set i 3
  } else {
    set event [lappend event $rtime $Evnt($type,1)]; set i 2
  }

  for {} {$i < $Evnt($type,N)} {incr i} {
    set event [lappend event "$Gdat($type,$i)"] 
  }

  # remove the old event
  if {$old == 1} { delMappedEvent $type [selection get] }

  midiput $mf $trk $event
  clearMappedEvent $type; listMappedEvent $type

# make sure we didn't fuck up the MetaEndOfTrack..
  fixMetaEndOfTrack $mf $trk

  set Modified 1
  if {$type == "Meter"} { buildMeterMap }

  return 0
}

proc delMappedEvent {type event} {
  global CHANNEL1
  global Gmf Gtrk Evnt Modified

  set meas [lindex $event 0]
  set event [lreplace $event 0 0 [measure2tick $meas]]
  if {$Evnt($type,2) == "Channel"} {
    set chan [lindex $event 2]
    set event [lreplace $event 2 2 [expr $chan - $CHANNEL1]]
  }
  mididelete $Gmf($type) $Gtrk($type) $event
  listMappedEvent $type

  set Modified 1
}

# -------------------------------------------------------------------
#
proc fixTrackList {tlist} {
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  set newlist ""
  foreach trk $tlist {
    if {$trk >= 0 && $trk < $mtrk} {
      set newlist [lappend newlist $trk]
    }
  }

  return $newlist
}

# -------------------------------------------------------------------
#
proc trackName {tlist} {
  # hack to make the window pop up near the tracks we are naming
  set foo [lindex [.trkscr.scroll get] 0]
  set foobar [expr round(([lindex $tlist 0]-$foo)*6)]
  foreach trk $tlist {
    set tmpname \
      [getEntry .et . "New Track Name" {} "Track $trk:" \
      "[.trkname.list get $trk]" 18 140 $foobar]
    .trkname.list delete $trk
    .trkname.list insert $trk $tmpname
    writeTrackNames $trk
    remapTrackInfo $trk $trk
  } 
  set Modified 1
}
# -------------------------------------------------------------
#
proc trackQuantize {tlist} {
  global DQUANTIZE
  global Modified PlayFile

  watchCursor .
  set lost [midiquantize $PlayFile $tlist $DQUANTIZE]
  set Modified 1
  normalCursor .
  if {$lost != 0} {
    dialog .d . "quantize: $lost events lost\nas duplicates." info 0 OK 
  }
  foreach trk $tlist {
    remapTrackInfo $trk $trk
  }
}     

# --------------------------------------------------------------
# Randomize the timing of a group of tracks
#
proc trackRandomize {tlist} {
  global MEAN VARIANCE
  global Modified PlayFile
  watchCursor .
  set lost [midirandomize $PlayFile $tlist $MEAN $VARIANCE]
  set Modified 1
  normalCursor .
  if {$lost != 0} {
    dialog .d . "randomize: $lost events lost\nas duplicates." \
      info 0 OK
  }
  foreach trk $tlist {
    remapTrackInfo $trk $trk
  }
}

# -------------------------------------------------------------
#
proc trackOffset {tlist} {
  global Modified PlayFile
  global offset
  
  set lost 0
  set offset \
    [getEntry .et . "Timing offset" {} "in SMF ticks:" \
    "0" 10 140 100]
  watchCursor .
  midioffset $PlayFile $tlist [measure2tick $offset]
  normalCursor .
  set Modified 1
  if {$lost != 0} {
    dialog .d . "offset: $lost events lost\nas duplicates." \
      info 0 OK
  }
  foreach trk $tlist {
    remapTrackInfo $trk $trk
  }
}

# -------------------------------------------------------------
#
proc trackTranspose {tlist} {
  global Modified PlayFile

  # this isn't *really* global
  global halfsteps

  set lost 0
  set halfsteps \
    [getEntry .et . "Transpose up" {} "in semitones:" \
    "0" 4 140 100]
  watchCursor .
  miditranspose $PlayFile $tlist $halfsteps
  normalCursor .
  set Modified 1
  if {$lost != 0} {
    dialog .d . "transpose: $lost events lost\nas duplicates." \
      info 0 OK
  }
  foreach trk $tlist {
    remapTrackInfo $trk $trk
  }
}

proc trackVolume {tlist} {
  global Modified PlayFile

  # this isn't *really* global
  global scaleby

  set lost 0
  set scaleby \
    [getEntry .et . "Volume adjust" {} "scale by:" \
    "1.0" 4 140 100]
  watchCursor .
  midivolume $PlayFile $tlist $scaleby
  normalCursor .
  set Modified 1
  if {$lost != 0} {
    dialog .d . "volume: $lost events lost\nas duplicates." \
      info 0 OK
  }
  foreach trk $tlist {
    remapTrackInfo $trk $trk
  }
}

# -------------------------------------------------------------
# Completely remove selected tracks, shuffling the others down
# into the open slot
#
proc trackRemove {tlist} {
  global Modified PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  watchCursor .
  midiremove $PlayFile $tlist 1
  normalCursor .

  if {$mtrk == [llength $tlist]} {
    midiCleanSlate 0
  } else {
    # shuffle the rest down 
    set h [lindex $tlist 0]
    for {set i $h; set k $h} {$i < $mtrk} {incr i} {
      if {[lsearch -exact $tlist $i] == -1} {
        remapTrackInfo $i $k
        incr k
      } else {
        closeTrackInfo $i
      }
    }
    showTrackEverything {}
  }
  set Modified 1
}

# ---------------------------------------------------------------------
# An attempt at scoring -- not for the faint of heart.
# For this to work you will need midi2tex, musictex, TeX, and xdvi,
# not to mention a great deal of luck and patience.
# 
# Software can be found at any ctan site, e.g. ftp.cdrom.com, 
# /pub/tex/ctan/macros/musictex/software/midi2tex
#
proc trackScore {tlist} {
  global PlayFile

  # make sure we have track zero in there..
  if {[lsearch -exact $tlist 0] == -1} {
    set tlist "0 $tlist"
  }
  # now we have to invert the list, so we keep instead of remove
  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  for {set i 0; set klist ""} {$i < $mtrk} {incr i} {
    if {[lsearch -exact $tlist $i] == -1} {
      set klist "$klist $i"
    }
  }
  # generate a copy of the selected tracks
  set tmpf [midiremove $PlayFile $klist 0]
  set tt scoretmp 
  set scorecommand \
    [list "midi2tex $tt.mid" "tex $tt.TEX" "xdvi $tt"]
  set ff [open $tt.mid w]
  watchCursor .
  midiwrite $ff $tmpf
  normalCursor .
  close $ff
  midifree $tmpf
  shellCmd midi2tex 72 15 "$scorecommand"
}

# -----------BEGIN PianoRoll subroutines -----------------------
#

proc pianoRollScroll {w a b} {
  $w.note yview $a $b
  $w.keys yview $a $b
}

proc trackPianoRoll {tlist} {
  global TEAROFF PIANOSCALE SHOWMIDC DEVTAB CHANNEL1
  global SHOWBARS SHOWBEAT SHOWQUAN DQUANTIZE
  global TimeScale KeyYval KeyYtag CurDev
  global ShowBars ShowBeat ShowQuan ShowMidC PlayFile
  global WhiteKey BlackKey ActiveKey 

  watchCursor .

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  foreach trk $tlist {
    set w .pr$trk
    set ShowBars($trk) $SHOWBARS
    set ShowBeat($trk) $SHOWBEAT
    set ShowQuan($trk) $SHOWQUAN
    set ShowMidC($trk) $SHOWMIDC

    set octwidth [expr  7.000000 * $PIANOSCALE]
    set lastnote [miditrack $PlayFile $trk end]
    set xnote    [expr  ($lastnote + 1) * $TimeScale / $mdiv]
    if {$xnote < 508} { set xnote 508 }

    scan [tick2measure $lastnote] "%d:%d:%d" lastmeas bb tt
    if {$bb != 0 || $tt != 0} { incr lastmeas }

    set ynote    [expr  8.000000 * $octwidth]
    set x1       [expr  5.500000 * $PIANOSCALE]
    set x0b      [expr  2.000000 * $x1 / 5.000000]

    if {[winfo exists $w]} { destroy $w }
    toplevel $w -cursor watch
    wm title $w "Piano Roll: Track $trk"
    wm iconname $w "Track $trk"
    wm minsize $w 0 0
    wm maxsize $w 1600 1280
    wm geometry $w 600x400

    frame $w.r -relief raised -bd 2
    frame $w.l -relief raised -bd 2
    frame $w.c -relief sunken -bd 2
    frame $w.mbar -relief raised -bd 1

    canvas $w.keys -width $x1 -height $ynote \
      -scrollregion "0 0 $x1 $ynote" \
      -yscrollcommand "$w.r.scroll set"
    canvas $w.note -width $xnote -height $ynote \
      -scrollregion "0 0 $xnote $ynote" \
      -xscrollcommand "$w.c.scroll set" \
      -yscrollcommand "$w.r.scroll set"
    scrollbar $w.c.scroll -command "$w.note xview" -orient horizontal
    scrollbar $w.r.scroll -command "pianoRollScroll $w"

    # these are used to grab resources.. that's all.
    label $w.whitekey; label $w.blackkey; label $w.activekey
    set WhiteKey [lindex [$w.whitekey configure -background] 4]
    set BlackKey [lindex [$w.blackkey configure -background] 4]
    set ActiveKey [lindex [$w.activekey configure -background] 4]

    label $w.quantum; label $w.beat; label $w.measure
    set quantcolor [lindex [$w.quantum configure -foreground] 4]
    set beatcolor  [lindex [$w.beat configure -foreground] 4]
    set meascolor  [lindex [$w.measure configure -foreground] 4]

    label $w.grid; label $w.event;
    set backcolor  [lindex [$w.note configure -background] 4]
    set eventcolor [lindex [$w.event configure -foreground] 4]
    set gridcolor  [lindex [$w.grid configure -foreground] 4]

    pack  $w.mbar -side top -expand 1 -fill x
    menubutton $w.mbar.file -text File -underline 0 \
      -menu $w.mbar.file.m 
    menu $w.mbar.file.m -tearoff $TEAROFF
    $w.mbar.file.m add command -label Dismiss -underline 0 \
      -command "closePianoRoll $trk" 

    menubutton $w.mbar.view -text "View" -underline 0 \
  	-menu $w.mbar.view.menu
    menubutton $w.mbar.zoom -text "Zoom" -underline 0 \
  	-menu $w.mbar.zoom.menu

    menu $w.mbar.view.menu -tearoff $TEAROFF
    $w.mbar.view.menu add checkbutton -label "Middle C" -underline 7 \
      -variable ShowMidC($trk) -command "showMiddleC $trk" \
      -selectcolor $ActiveKey
    $w.mbar.view.menu add checkbutton -label "Measures" -underline 0 \
      -variable ShowBars($trk) -command "showMeasures $trk" \
      -selectcolor $meascolor
    $w.mbar.view.menu add checkbutton -label "Beats" -underline 0 \
      -variable ShowBeat($trk) -command "showBeats $trk" \
      -selectcolor $beatcolor
    $w.mbar.view.menu add checkbutton -label "Quantization" -underline 0 \
      -variable ShowQuan($trk) -command "showQuantization $trk" \
      -selectcolor $quantcolor

    menu $w.mbar.zoom.menu -tearoff $TEAROFF
    $w.mbar.zoom.menu add command -label "In    " -underline 0 \
      -command "pianoRollZoom $trk 2.000000"
    $w.mbar.zoom.menu add command -label "Out   " -underline 0 \
      -command "pianoRollZoom $trk 0.500000"

    pack $w.mbar.file $w.mbar.view $w.mbar.zoom -in $w.mbar -side left 

    set basewidth [lindex [$w.c.scroll configure -width] 4]
    set bordwidth [lindex [$w.c.scroll configure -borderwidth] 4]
    set highwidth [lindex [$w.c.scroll configure -highlightthickness] 4]
    set dwidth [expr $basewidth + 2*$bordwidth + 2*$highwidth]
    frame $w.dummy0 -width $dwidth -height $dwidth
    frame $w.dummy1 -height $dwidth

    pack $w.dummy0 -in $w.r -side bottom
    pack $w.r.scroll -in $w.r -side top -expand 1 -fill y
    pack $w.r -in $w -side right -expand 1 -fill both

    pack $w.dummy1 -in $w.l -side bottom
    pack $w.keys -in $w.l -side top -expand 1 -fill y
    pack $w.l -in $w -side left -expand 1 -fill both

    pack $w.c.scroll -in $w.c -side bottom -expand 1 -fill x
    pack $w.note -in $w.c -side top -expand 1 -fill both 
    pack $w.c -in $w -side top -expand 1 -fill both

    for {set k 0} {$k < 8} {incr k} {
      for {set j 0} {$j < 12} {incr j} {
        if {[lindex $KeyYtag($j) 0] == "white"} {
          set width $PIANOSCALE; set fill $WhiteKey; set x0 0
        } else {
          set width [expr $PIANOSCALE / 2.000000]
          set fill $BlackKey; set x0 $x0b
        }
        set y0 [expr ($k + $KeyYval($j)) * $octwidth]
        set y1 [expr $y0 + $width]

        # First note is C0 or 24
        $w.keys create rectangle $x0 $y0 $x1 $y1 -fill $fill \
          -outline black -tags "n[expr 12*$k+$j+24] $KeyYtag($j)"

        set yrule [expr $octwidth * (($k * 12.00000) + $j) / 12.000000]
        $w.note create line 0 $yrule $xnote $yrule \
          -fill $gridcolor -tags "zoom grid bm bb bq"
      }
    }
    $w.keys raise black white

    if {$DEVTAB($CurDev,raw) != ""} {
      bind $w.keys <Button-1> { 
        set pitch [string range \
          [lindex [%W gettags [%W find withtag current]] 0] 1 end]
        set track   [string range %W 3 [expr [string first .k %W] - 1]]
        set channel [string range [.trkchan.list get $track] 0 1]
        if {$channel == "--"} {
          set channel $CHANNEL1
        }
        set channel [expr $channel - $CHANNEL1]
        midisend $DEVTAB($CurDev,raw) "0 NoteOn $channel $pitch 100"
        set NoteOff "0 NoteOff $channel $pitch 0"
      }
      bind $w.keys <B1-Enter> { 
        set pitch [string range \
          [lindex [%W gettags [%W find withtag current]] 0] 1 end]
        set track   [string range %W 3 [expr [string first .k %W] - 1]]
        set channel [string range [.trkchan.list get $track] 0 1]
        if {$channel == "--"} {
          set channel $CHANNEL1
        }
        set channel [expr $channel - $CHANNEL1]
        midisend $DEVTAB($CurDev,raw) "0 NoteOn $channel $pitch 100"
        set NoteOff "0 NoteOff $channel $pitch 0"
      }
      bind $w.keys <ButtonRelease-1> {
        midisend $DEVTAB($CurDev,raw) $NoteOff
      }
      bind $w.keys <B1-Leave> {
        midisend $DEVTAB($CurDev,raw) $NoteOff
      }
      bind $w.keys <B1-Motion> {
        set opitch [lindex $NoteOff 3]
        set pitch [string range \
          [lindex [%W gettags [%W find withtag current]] 0] 1 end]
        if {$opitch != $pitch} {
           set channel [lindex $NoteOff 2]
           midisend $DEVTAB($CurDev,raw) $NoteOff
           midisend $DEVTAB($CurDev,raw) "0 NoteOn $channel $pitch 100"
           set NoteOff [lreplace $NoteOff 3 3 $pitch] 
        }
      }
    }

    # draw the grid, including measures, beats, and quantization
    $w.note create rectangle 100 100 100 100 -fill $backcolor \
      -tags "zoom marker" -outline $backcolor
    pianoRollGrid $trk

    # now fill in all the notes
    fillPianoRoll $trk 

    showMiddleC $trk
    normalCursor $w
  }
  normalCursor .
}

proc pianoRollGrid {trk} {
  global PIANOSCALE
  global TimeScale ShowMidC PlayFile Mquantize

  # we need this to be separate since changes in time signature
  # will futz with this grid..

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  set w .pr$trk

  # first delete the existing grid
  $w.note delete measure
  $w.note delete beat
  $w.note delete quantum

  # the finest quantization level
  set Mquantize 32

  # get the current magnification and scale back to 1, restoring later.
  set mag [lindex [$w.note coords marker] 0]
  $w.note scale zoom 0 0 [expr 100.0 / $mag] 1

  set ynote    [expr  56.000000 * $PIANOSCALE]
  set lastnote [miditrack $PlayFile $trk end]
  scan [tick2measure $lastnote] "%d:%d:%d" lastmeas bb tt
  if {$bb != 0 || $tt != 0} { incr lastmeas }

  set quantcolor [lindex [$w.quantum configure -foreground] 4]
  set beatcolor  [lindex [$w.beat configure -foreground] 4]
  set meascolor  [lindex [$w.measure configure -foreground] 4]
  set qpb [expr $Mquantize / 4]

  for {set i 0} {$i <= $lastmeas} {incr i} {
    set num [lindex [getMeter $i:0:0] 2]
    for {set j 0} {$j < $num} {incr j} {
      set xval [expr [measure2tick $i:$j:0] * $TimeScale / $mdiv]
      $w.note create line $xval 0 $xval $ynote -fill $beatcolor \
        -tags "beat zoom bm"
      for {set k 0} {$k < $qpb} {incr k} {
        set qval [expr $xval + $k * $TimeScale / $qpb]
        $w.note create line $qval 0 $qval $ynote -fill $quantcolor \
          -tags "quantum zoom bm bb"
      }
    }
    set xval [expr [measure2tick $i:0:0] * $TimeScale / $mdiv]
    $w.note create line $xval 0 $xval $ynote -fill $meascolor \
      -tags "measure zoom"
  }

  # restore zoom, as promised
  $w.note scale zoom 0 0 [expr $mag / 100.0] 1

  showQuantization $trk
  showBeats $trk
  showMeasures $trk
}

proc pianoRollZoom {trk xscale} {
  set screg [lindex [.pr$trk.note configure -scrollregion] 4]
  set xval  [lindex $screg 2]
  set newsr [lreplace $screg 2 2 [expr $xscale * $xval]]

  .pr$trk.note scale zoom 0 0 $xscale 1
  .pr$trk.note configure -scrollregion $newsr
}

proc showMiddleC {trk} {
  global ShowMidC ActiveKey WhiteKey

  if {$ShowMidC($trk)} {
    .pr$trk.keys itemconfigure n60 -fill $ActiveKey
  } else {
    .pr$trk.keys itemconfigure n60 -fill $WhiteKey
  }
}

proc showMeasures {trk} {
  global ShowBars

  if {$ShowBars($trk)} {
    set gridcolor [lindex [.pr$trk.measure configure -foreground] 4]
    .pr$trk.note itemconfigure measure -fill $gridcolor
    .pr$trk.note raise measure bm
  } else {
    set fillwith [lindex [.pr$trk.note configure -background] 4]
    .pr$trk.note itemconfigure measure -fill $fillwith 
    .pr$trk.note lower measure grid
  }
  .pr$trk.note raise event
}

proc showBeats {trk} {
  global ShowBeat

  if {$ShowBeat($trk)} {
    set gridcolor  [lindex [.pr$trk.beat configure -foreground] 4]
    .pr$trk.note itemconfigure beat -fill $gridcolor
    .pr$trk.note raise beat bb
  } else {
    set fillwith [lindex [.pr$trk.note configure -background] 4]
    .pr$trk.note itemconfigure beat -fill $fillwith 
    .pr$trk.note lower beat grid
  }
  showMeasures $trk
}

proc showQuantization {trk} {
  global DQUANTIZE MAXQUANT
  global PlayFile ShowQuan Mquantize

  if {$DQUANTIZE != $Mquantize} {
    set tmpQuant [expr {($MAXQUANT < $DQUANTIZE) ? $MAXQUANT : $DQUANTIZE}]
    set xscale [expr $Mquantize.0000 / $tmpQuant.0000] 
    .pr$trk.note scale quantum 0 0 $xscale 1
    set Mquantize $tmpQuant
  }

  if {$ShowQuan($trk)} {
    set quantcolor  [lindex [.pr$trk.quantum configure -foreground] 4]
    .pr$trk.note itemconfigure quantum -fill $quantcolor
    .pr$trk.note raise quantum bq
    if {$DQUANTIZE > $MAXQUANT} {
      dialog .pr$trk.h .pr$trk \
        "  Piano roll quantization is limited to 1/$MAXQUANT notes.\nChange MAXQUANT if you need finer resolution." \
        info 0 OK
    }
  } else {
    set fillwith [lindex [.pr$trk.note configure -background] 4]
    .pr$trk.note itemconfigure quantum -fill $fillwith 
    .pr$trk.note lower quantum grid
  }
  showBeats $trk
}

proc fillPianoRoll {tlist} {
  global PIANOSCALE 
  global TimeScale PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  # width of a piano key, sort-of
  set yscale [expr 7.000000 * $PIANOSCALE / 12.000000]

  foreach i $tlist {
    if {![winfo exists .pr$i]} { break }

    midirewind $PlayFile $i
    raise .pr$i

    # get the current magnification and scale back to 1, restoring later.
    set mag [lindex [.pr$i.note coords marker] 0]
    .pr$i.note scale zoom 0 0 [expr 100.0 / $mag] 1

    # erase whatever is there already
    .pr$i.note delete note

    set sum 0; set num 0; set start 0
    set gridcolor  [lindex [.pr$i.grid configure -foreground] 4]
    set eventcolor [lindex [.pr$i.event configure -foreground] 4]

    while {[set event [midiget $PlayFile $i next]] != "EOT"} {
      set etype [lindex $event 1]
      if {$etype == "Note"} {
        # Time Scale is adjustable
        set x0 [expr [lindex $event 0] * $TimeScale / $mdiv]
        set x1 [expr $x0 + [lindex $event 5] * $TimeScale / $mdiv]

	if {$num == 0} { set start $x0 }

        # Piano Roll goes from C0 to C8
        set y0 [expr ([lindex $event 3] - 24.000000) * $yscale] 
        set y1 [expr $y0 + $yscale]

        set sum [expr $sum + $y0]; incr num

        .pr$i.note create rectangle $x0 $y0 $x1 $y1 \
           -tags "event zoom" -fill $eventcolor
      }
    }
    set xsize [lindex [.pr$i.note configure -width] 4]
    set ysize [lindex [.pr$i.note configure -height] 4]

    tkwait visibility .pr$i.note

    if {$num != 0} {
      set center [expr $sum / $num]
      set visib  [winfo height .pr$i.note]
      set hidden [expr $center - $visib / 2.000000]
      set inthid [expr round($hidden / $yscale) * $yscale]
      pianoRollScroll .pr$i moveto [expr $inthid / $ysize]
    }
    if {$start != 0} {
      set visib  [winfo width .pr$i.note]
      scan [tick2measure [expr $start * $mdiv / $TimeScale]] "%d" tmeas
      set start [expr [measure2tick $tmeas:0:0] * $TimeScale / $mdiv]
      .pr$i.note xview moveto [expr $start / $xsize]
    }

    .pr$i.note scale zoom 0 0 [expr $mag / 100.0] 1
  }
}

# -----------BEGIN TrackInfo subroutines -----------------------
#
proc closeTrackInfo {tlist} {
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  # handle the cases where no tracks are specified
  if {$tlist == ""} {
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
  }
  foreach i $tlist {
    if {[winfo exists .ti$i]} { destroy .ti$i }
  }
}

proc closePianoRoll {tlist} {
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  # handle the cases where no tracks are specified
  if {$tlist == ""} {
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
  }
  foreach i $tlist {
    if {[winfo exists .pr$i]} {
       .pr$i.note delete all
       .pr$i.keys delete all
       destroy .pr$i
    } 
  }
}

# ---------------------------------------------------------------------
#
proc fillTrackInfo {tlist keep} {
  global MeasView PlayFile

  foreach i $tlist {
    if {![winfo exists .ti$i]} { break }

    midirewind $PlayFile $i
    raise .ti$i

    # erase whatever is there already
    .ti$i.list delete 0 end

    while {[set event [midiget $PlayFile $i next]] != "EOT"} {
      if {$MeasView} {
        set tt [lindex $event 0]
        set tt [tick2measure $tt]
        set event [lreplace $event 0 0 $tt]
      }
      .ti$i.list insert end $event
    }

    # keep current position if requested
    if {$keep} {
      .ti$i.list yview moveto [lindex [.ti$i.scroll get] 0]
    }
  }
}

proc tick2measure {tick} {
  global Mdivision 

  set tick [expr round($tick)]

  # get meter/tick mapping for this measure
  scan [getMeter $tick] "%d %d %d %d" btick bmeas num den

  set tpb  [expr $Mdivision * 4 / $den]
  set tick [expr $tick - $btick]

  set beat [expr $tick / $tpb]

  set meas [expr $bmeas + $beat / $num]
  set beat [expr $beat % $num]
  set frac [expr $tick % $tpb]

  set foo [format "%d:%2d:%3d" $meas $beat $frac]
  regsub -all " " $foo "0" foo
  return $foo
}

proc measure2tick {measure} {
  global Mdivision

  if {[scan $measure "%d:%d:%d" meas beat frac] == 1} {
    # already just a tick
    return $meas
  }
  # get meter/tick mapping for this measure
  scan [getMeter $measure] "%d %d %d %d" btick bmeas num den

  # Mdivision ticks per quarter note.  
  set tpb [expr $Mdivision * 4 / $den]
  set tpm [expr $tpb * $num]

  # Compute ticks 
  set foo [expr $btick + ($meas - $bmeas) * $tpm + $beat * $tpb + $frac]
  return $foo
}

proc getMeter {x} {
  global MeterMap

# x may be a tick or a measure m:b:t
  if {[scan $x "%d:%d:%d" meas beat frac] == 1} {
    set si 0
    set sx $x
  } else {
    set si 1
    set sx $meas
  }

# default is 4/4, if no meter events are found
  set meter {0 0 4 4}	

# brute force for now
  foreach m $MeterMap {
    if {[lindex $m $si] > $sx} { break }
    set meter $m
  }

  return $meter
}

proc buildMeterMap {} {
  global MeterMap PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  set MeterMap ""
  set btick 0
  set bmeas 0
  set num 4
  set den 4

  midirewind $PlayFile 0
  while {[set event [midiget $PlayFile 0 next]] != "EOT"} {
    set etype [lindex $event 1]
    if {$etype == "MetaTime"} {
      set tpb  [expr $mdiv * 4 / $den]
      set tock [expr [lindex $event 0] - $btick]
      set beat [expr $tock / $tpb]
      set bmeas [expr $bmeas + $beat / $num]

      scan $event "%d MetaTime %d %d" btick num den
      lappend MeterMap "$btick $bmeas $num $den"
    }
  }

  # update any piano rolls that might be open
  for {set i 0} {$i < $mtrk} {incr i} {
    if {[winfo exists .pr$i]} { pianoRollGrid $i }
  }
}

proc newQuantization {} {
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  for {set i 0} {$i < $mtrk} {incr i} {
    if {[winfo exists .pr$i]} { showQuantization $i }
  }
}

proc newDivision {} {
  global DDIVISION
  global PlayFile

  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  if {$mdiv == $DDIVISION} {
    return
  }

  if {$mdiv > $DDIVISION} {
    if {[dialog .ddiv . " Reducing DIVISION may\nadversely affect timing." \
      info 0 OK Cancel]} {
      set DDIVISION $mdiv
      return
    }
  }

  watchCursor .
  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  set tmpf [midimake]
  midiconfig $tmpf "division $DDIVISION" "format $mfmt" "tracks $mtrk"

  for {set i 0} {$i <  $mtrk} {incr i} {
    midicopy "$tmpf $i" 0 "$PlayFile $i" 0 \
      [expr "[miditrack $PlayFile $i end] + 1"]
  }
  midifree $PlayFile
  set PlayFile $tmpf

  normalCursor .
  showTrackStatusLine
}

proc getConfig {mf} {
  if {$mf == ""} {
    set mdiv 0
    set mfmt 0
    set mtrk 0
  } else {
    set config [midiconfig $mf division format tracks]
    set mdiv [lindex [lindex $config 0] 1]
    set mfmt [lindex [lindex $config 1] 1]
    set mtrk [lindex [lindex $config 2] 1]
  }
  return "$mdiv $mfmt $mtrk"
}

proc measureInfo {event} {
  global MeasView

  set tick [lindex $event 0]
  if {[scan $tick "%d:%d:%d" meas beat frac] == 1} {
    set tick [tick2measure $tick]
    scan $tick "%d:%d:%d" meas beat frac
  }
  return [list $meas $beat $frac]
}

# ---------------------------------------------------------------------
# this procedure deletes MIDI events via the track info screen.
#
proc deleteEvents {i} {
  global Modified PlayFile

  set elist [.ti$i.list curselection]
  if {$elist != ""} {
    foreach j $elist {
      set event [.ti$i.list get $j]
      set tick [lindex $event 0]
      set event [lreplace $event 0 0 [measure2tick $tick]]
      mididelete $PlayFile $i $event
    }
    # refresh the display
    showTrackEverything $i
    trackInfo $i
    set Modified 1
  }
}

# ---------------------------------------------------------------------
# this procedure edits a list of MIDI events selected via Track Info
#
proc modifyEvents {i} {
  global Modified MeasView PlayFile

  # make sure we own it before we modify it
  set elist [.ti$i.list curselection]
  if {$elist != ""} {
      foreach j $elist {
        set event [.ti$i.list get $j]
        set newevent \
          [getEntry .ti$i.ee .ti$i.list  "Modify Event" {} "Track $i:" \
            "$event" 40 {} {}]

        set tick [lindex $event 0]
        set event [lreplace $event 0 0 [measure2tick $tick]]
        set tick [lindex $newevent 0]
        set newevent [lreplace $newevent 0 0 [measure2tick $tick]]

        mididelete $PlayFile $i $event
        while {[catch {midiput $PlayFile $i "$newevent"} msg]} {
          if {[dialog .ti$i.di .ti$i "Unable to put event." warning \
            0 {Try again} {Keep original}]} {
            midiput $PlayFile $i $event
            break
          } else {
            set newevent \
              [getEntry .ti$i.ee .ti$i.list  "Modify Event" {} "Track $i:" \
              "$newevent" 40 {} {}]
          }
        }
      }
      # refresh the display
      showTrackEverything $i
      trackInfo $i
      set Modified 1
  }
}

# ---------------------------------------------------------------------
# this procedure copies a list of MIDI events selected via Track Info,
# for now pasting them to the end of the track
#
proc copyEvents {i} {
  global Modified MeasView PlayFile

  # make sure we own it before we modify it
  set elist [.ti$i.list curselection]
  if {$elist != ""} {

      # compute the number of the last measure
      set tt [expr [llength $elist] - 1]
      set first [lindex [measureInfo [.ti$i.list get [lindex $elist 0]]] 0]
      set last [lindex [measureInfo [.ti$i.list get [lindex $elist $tt]]] 0]
      set nummeasures [expr $last - $first + 1]

      dialog .ti$i.di .ti$i "I count $nummeasures measures" info 0 {OK}

      set numcopies \
      [getEntry .ti$i.et .ti$i "Copy Events" {} "number of copies:" 1 4 {} {}]

      # Take care of EOT
      midirewind $PlayFile $i
      set ticks [miditrack $PlayFile $i end]
      set pastepoint [expr [lindex [measureInfo $ticks] 0] + 1]
      dialog .ti$i.di .ti$i "Pastepoint at measure $pastepoint" info 0 {OK}

      foreach j $elist {
        set event [.ti$i.list get $j]
        set minfo [measureInfo $event]
        set meas [expr $pastepoint + [lindex $minfo 0] - $first]
        set beat [lindex $minfo 1]
        set frac [lindex $minfo 2]
        for {set k 0} {$k < $numcopies} {incr k} {
          set newm [expr $k * $nummeasures + $meas]
          set tick [measure2tick "$newm:$beat:$frac"]
          set event [lreplace $event 0 0 $tick]
          if {[catch {midiput $PlayFile $i "$event"} msg]} {
            dialog .ti$i.di .ti$i
               "Unable to put event $newm:$beat:$frac $event" info 0 {OK}
          }
        }
      }
      fixMetaEndOfTrack $PlayFile $i

      # refresh the display
      showTrackEverything $i
      trackInfo $i
      set Modified 1
  }
}
# ---------------------------------------------------------------------

proc trackScrollUD {args} {
  eval .trknumb.list yview $args
  eval .trkname.list yview $args
  eval .trkmute.list yview $args
  eval .trkprog.list yview $args
  eval .trkchan.list yview $args
  eval .trkmeas.list yview $args
}

# This is a hack.  General solution?
proc trackScan {a line fract} {
  foreach i {numb name mute chan prog meas} {
    if {[string compare .trk$i.list $a] != 0} {
      eval .trk$i.list yview moveto $line
    }
  }
#  foreach i {meas} {
#    if {[string compare .trk$i.text $a] != 0} {
#      eval .trk$i.text yview moveto $line
#    }
#  }
  eval .trkscr.scroll set $line $fract
}

# -------- END of track commands -----------------------------------
      
# -------- BEGIN sequencer control buttons -------------------------
# These should be self-explanatory
#
proc seqPause {} {
  dialog .d . "Pause has not been\nimplemented yet." info 0 OK
}
# ------------------------------------------------------------------
#
proc seqRewind {} {
  dialog .d . \
    "Rewind happens every time you\npress Stop (for now)." info 0 OK
}
# ------------------------------------------------------------------
#
proc seqFFwd {} {
  dialog .d . \
    "Fast forward has not been\nimplemented yet." info 0 OK
}

# ------------------------------------------------------------------
# This only starts recording.  The Stop function cleans up afterwards
# Maybe that should be cleaned up with a tkwait.
#
proc seqRecord {} {
  global TEMPO DEVTAB NUMDEV
  global MidiState PlayFile RecFile TmpFile LabelNow CurDev MasterDev
  global Modified MuteList SoloList Mdivision PlayF

  if {$MidiState == "stopped"} {
    # make sure we have a play file
    if {$PlayFile == ""} {
      set PlayFile [midimake]
      midiconfig $PlayFile "tracks 1" "division $Mdivision"
      midiput $PlayFile 0 "0 MetaTempo $TEMPO"
    }
    # create space for the new song
    set RecFile [midimake]
    set pconfig [midiconfig $PlayFile division]
    midiconfig $RecFile "tracks 1" [lindex $pconfig 0]
    watchCursor .
    if {$SoloList != ""} {
      set TmpFile [midikeep $PlayFile $SoloList 0]
      set foo $TmpFile
    } elseif {$MuteList != ""} {
      set TmpFile [midiremove $PlayFile $MuteList 0]
      set foo $TmpFile
    } else {
      set foo $PlayFile
    }
    getStopTime $foo

# Now strip off any channels we don't want, and play what's left
    for {set i 0} {$i < $NUMDEV} {incr i} {
      if {$DEVTAB($i,map) == {}} {
        set PlayF($i,pointer) $foo
        set PlayF($i,data) ""
      } else {
        dialog .h . "Midimap support doesn't work yet." info 0 OK
	set PlayF($i,pointer) $foo
	set PlayF($i,data) ""
#        set PlayF($i,data) [midimap $foo $DEVTAB($i,map)]
#        set PlayF($i,pointer) $PlayF($i,data)
      }
      if {$i != $MasterDev} {
        midiplay $DEVTAB($i,dev) $PlayF($i,pointer)
      }
    }
    normalCursor .

# The master must start last
    midirecord $DEVTAB($MasterDev,dev) $RecFile $PlayF($MasterDev,pointer)

    updateButtons 0 2 1 0 0 0
    set MidiState recording
    WaitForStop$LabelNow
  }
}
# ------------------------------------------------------------------
#
proc seqPlay {} {
  global DEVTAB NUMDEV
  global MidiState PlayFile LabelNow Modified TmpFile
  global MuteList SoloList CurDev MasterDev PlayF

# First strip off any tracks we don't want..

  watchCursor .

  if {$PlayFile != "" && $MidiState == "stopped"} {
    midistop $DEVTAB($CurDev,dev)
    if {$SoloList != ""} {
      set TmpFile [midikeep $PlayFile $SoloList 0]
      set foo $TmpFile
    } elseif {$MuteList != ""} {
      set TmpFile [midiremove $PlayFile $MuteList 0]
      set foo $TmpFile
    } else {
      set foo $PlayFile
    }
    getStopTime $foo

# Now strip off any channels we don't want, and play what's left
    for {set i 0} {$i < $NUMDEV} {incr i} {
      if {$DEVTAB($i,map) == {}} {
        set PlayF($i,pointer) $foo
        set PlayF($i,data) ""
      } else {
        dialog .h . "Midimap support doesn't work yet." info 0 OK
	set PlayF($i,pointer) $foo
	set PlayF($i,data) ""
#        set PlayF($i,data) [midimap $foo $DEVTAB($i,map)]
#        set PlayF($i,pointer) $PlayF($i,data)
      }
      if {$i != $MasterDev} {
        midiplay $DEVTAB($i,dev) $PlayF($i,pointer)
      }
    }
    normalCursor .

# The master must start last
    midiplay $DEVTAB($MasterDev,dev) $PlayF($MasterDev,pointer)

    updateButtons 2 0 1 0 0 0 
    set MidiState playing
    WaitForStop$LabelNow
  }
}
# --------------------------------------------------------------------
#
proc seqStop {} {
  global DEVTAB NUMDEV
  global MidiState PlayFile RecFile TmpFile Now CurDev
  global PlayName StopTime Modified PlayF

   scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk

  if {$MidiState != "stopped"} {
    set prev_state $MidiState
    set MidiState stopped
    set StopTime 0
    midistop $DEVTAB($CurDev,dev)  
    # free scratch space (created in seqRecord or seqPlay)
    if {$TmpFile != ""} {
      midifree $TmpFile
      set TmpFile ""
    }
    for {set i 0} {$i < $NUMDEV} {incr i} {
      if {$PlayF($i,data) != ""} {
        midifree $PlayF($i,data)
        set PlayF($i,pointer) ""
        set PlayF($i,data) ""
      }
    }
    if {$prev_state == "recording"} {
      midirewind $RecFile
      midirewind $PlayFile
      if {$PlayName == "untitled.mid" && $Modified == 0} {
	if {[dialog .h . "Keep track?" questhead 0 OK Discard]} {
          midiCleanSlate 0
        } else {
          # use format 1
          midiconfig $PlayFile "format 1" "tracks 2"
          set Modified 1
          # add EOT to new track and split it up
	  addMetaEndOfTrack $RecFile 0
          midisplit "$RecFile 0" "$PlayFile 0" "$PlayFile 1"
	  midifree $RecFile; set RecFile ""
          showTrackEverything "0 1"
          writeTrackNames {}
        }
      } else {
        if {[dialog .h . "Merge new track into $PlayName?" \
            questhead 0 OK Discard]} {
          midifree $RecFile
          set RecFile ""
        } else {
          # create a format one file consisting of the play file
          # plus the recorded track
          set newtrack $mtrk
          incr mtrk
	  set newfile [midimake]
	  midiconfig $newfile "format 1" "division $mdiv" "tracks $mtrk"
	  if {$mfmt} {
	    # copy each track to newfile
	    for {set i 0} {$i < $newtrack} {incr i} {
	      midicopy "$newfile $i" 0 "$PlayFile $i" \
	        0 [expr "[miditrack $PlayFile $i end] + 1"]
	    }
	    # including the new one
	    set newlist $newtrack
	    midicopy "$newfile $newtrack" 0 "$RecFile 0" \
	      0 [expr "[miditrack $RecFile 0 end] + 1"]
	  } else {
	    # split track 0 into newfile (adding yet another track)
            set newlist ""
            set newtrack $mtrk
            incr mtrk 2
	    midiconfig $newfile "tracks $mtrk"
	    midisplit "$PlayFile 0" "$newfile 0" "$newfile 1"
     	    midicopy "$newfile 2" 0 "$RecFile 0" 0 \
     	      [expr "[miditrack $RecFile 0 end] + 1"]
	  }
	  # add EOT to recorded track
	  addMetaEndOfTrack $newfile $newtrack
	  midifree $PlayFile
	  midifree $RecFile
	  set PlayFile $newfile
          set Modified 1
          showTrackEverything $newlist
          writeTrackNames $newlist
        }
      }
    }
    set RecFile ""
    if {$PlayFile == ""} {
      updateButtons 0 1 0 0 0 0
    } else {
      updateButtons 1 1 0 0 0 0
    }
  } 
}

# ------------------------------------------------------------------
# Make labels and data regarding time change when clock is changed
#
proc updateDeviceInfo {} {
  global DEVTAB
  global Now LabelNow SmpteClk CurDev

  # select device
  if {$SmpteClk($CurDev) == 1} {
    midifeature $DEVTAB($CurDev,dev) smpte_timing
    set LabelNow "SMPTE:"
    set foo [midifeature $DEVTAB($CurDev,dev) get_smpte]
    if {$foo == "NOSYNC" || $foo == "ERR"} {
      set Now "No Sync"
    } else {
      set Now [string range $foo 0 7]
    }
  } else {
    if {$SmpteClk($CurDev) == 2} {
      midifeature $DEVTAB($CurDev,dev) mpu401_timing
    } else {
      midifeature $DEVTAB($CurDev,dev) kernel_timing
    }
    set LabelNow "MIDI:"
    set foo [miditime $DEVTAB($CurDev,dev)]
    if {$foo == "ERR"} {
      set Now [tick2measure 0]
    } else {
      set Now [tick2measure $foo]
    }
  }
  update idletasks
}

# ------------------------------------------------------------------
# Called by play and record in MIDI mode to display MIDI time
#
proc WaitForStopMIDI: {} {
  global DEVTAB
  global Now StopTime MidiState CurDev

  # MIDI mode -- user intervention or when track playback is finished
  if {$MidiState != "stopped"} {
    set foo [miditime $DEVTAB($CurDev,dev)]
    if {$foo > $StopTime && $MidiState == "playing"} {
      seqStop; return
    }
    set Now [tick2measure $foo]
    update
    after 100 WaitForStopMIDI:
  }
}

proc WaitForStopSMPTE: {} {
  global DEVTAB
  global Now SmpteClk StopTime MidiState CurDev

  # SMPTE mode -- stop through user intervention only
  if {$MidiState != "stopped"} {
    set foo [midifeature $DEVTAB($CurDev,dev) get_smpte]
    if {$foo == "NOSYNC"} {
      set Now "No Sync"
    } else {
      set Now [string range $foo 0 7]
    }
    update
    after 500 WaitForStopSMPTE:
  }
}

# -------- END of sequencer control buttons -------------------------

# -------- BEGIN utilities which should be written in C++ -----------
# They use no globals (other than their return values, which may or
# may not really be necessary..)
#
#--------------------------------------------------------------------
# Use this one if all is well..
#
proc addMetaEndOfTrack {mf trk} {
  midiput $mf $trk "[miditrack $mf $trk end] MetaEndOfTrack"
}

# Use this one if things might be messed up..
#
proc fixMetaEndOfTrack {mf trk} {

  midirewind $mf $trk

  watchCursor .
  while {[set event [midiget $mf $trk next]] != "EOT"} {
    if {[lindex $event 1] == "MetaEndOfTrack"} {
      mididelete $mf $trk $event
    }
  }
  addMetaEndOfTrack $mf $trk
  normalCursor .
}

proc midiquantize {mf tlist quantize} {
  global lost

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  # quarter note gets mdiv ticks
  set iqdiv [expr 4*$mdiv/$quantize]
  set fqdiv [expr $iqdiv*1.0000]
  set lost 0

  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"

  foreach i $tlist {
    midirewind $mf $i
    midirewind $tmpf $i

    while {[set event [midiget $mf $i next]] != "EOT"} {
      set ticks [lindex $event 0]
      set newticks [expr round($ticks/$fqdiv)*$iqdiv]
      set newevent [lreplace $event 0 0 $newticks]
      if {[catch {midiput $tmpf 0 "$newevent"}]} {
        incr lost
      }
    }
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
    fixMetaEndOfTrack $mf $i
  }
  midifree $tmpf
  return $lost
}

#
proc midioffset {mf tlist offset} {
  global lost

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  set lost 0

  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"

  foreach i $tlist {
    midirewind $mf $i
    midirewind $tmpf $i

    while {[set event [midiget $mf $i next]] != "EOT"} {
      set ticks [lindex $event 0]
      set newticks [expr $ticks+$offset]
      if {$newticks < 0} { set newticks 0 }
      set newevent [lreplace $event 0 0 $newticks]
      if {[catch {midiput $tmpf 0 "$newevent"}]} {
        incr lost
      }
    }
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
    fixMetaEndOfTrack $mf $i
  }
  midifree $tmpf
  return $lost
}

#
proc miditranspose {mf tlist offset} {
  global lost

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  set lost 0

  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"

  foreach i $tlist {
    midirewind $mf $i
    midirewind $tmpf $i

    while {[set event [midiget $mf $i next]] != "EOT"} {
      set etype [string range [lindex $event 1] 0 3]
      if {$etype == "Note"} {
        set newevent [lreplace $event 3 3 [expr [lindex $event 3]+$offset]]
      } else {
        set newevent $event
      }
      if {[catch {midiput $tmpf 0 "$newevent"}]} {
        incr lost
      }
    }
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
    fixMetaEndOfTrack $mf $i
  }
  midifree $tmpf
  return $lost
}

proc midivolume {mf tlist scale} {
  global lost

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  set lost 0

  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"

  foreach i $tlist {
    midirewind $mf $i
    midirewind $tmpf $i

    while {[set event [midiget $mf $i next]] != "EOT"} {
      set etype [string range [lindex $event 1] 0 3]
      if {$etype == "Note"} {
        set oldvol [lindex $event 4]
        set newvol [expr round($oldvol*$scale)]
        set newvol [expr ($newvol < 127) ? $newvol : 127]
        set newevent [lreplace $event 4 4 $newvol]
      } else {
        set newevent $event
      }
      if {[catch {midiput $tmpf 0 "$newevent"}]} {
        incr lost
      }
    }
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
    fixMetaEndOfTrack $mf $i
  }
  midifree $tmpf
  return $lost
}

# ---------------------------------------------------------------------
# Requires that you have my rand extensions compiled into tcl
#
proc midirandomize {mf tlist mu var} {
  global lost 

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk

  set tmpf [midimake]
  midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"

  rand {dist normal} {type integer} "mean $mu" "variance $var" 
  rand "seed [exec date +%s]"
  set lost 0

  foreach i $tlist {
    # remove MetaEOT
    midirewind $mf $i
    while {[set event [midiget $mf $i next]] != "EOT"} {
      set ticks [lindex $event 0]
      set newticks [expr $ticks + [rand]]
      if {$newticks < 0} { set newticks 0 }
      set newevent "$newticks [lrange $event 1 end]"
      if {[catch {midiput $tmpf 0 "$newevent"}]} {
        incr lost
      }
    }
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
    fixMetaEndOfTrack $mf $i
  }
  midifree $tmpf
  return $lost
}

# ---------------------------------------------------------------------
# Strip selected tracks out of a MIDI file
#
# Initially I wrote this function to copy a midi file, omitting any 
# muted tracks.  Then I thought it was similar to what I wanted to do
# to remove tracks.  It is but it isn't.  Anyway, usage:
#
# midiremove [song] [tracklist] [overwrite] :: this command returns
# a midi song which looks just like [song] except it's missing the
# tracks specified in tracklist.  If [overwrite] is true, the new 
# song is the old song, i.e. tracks are deleted.  If [overwrite] is
# false, a copy of the song is made and the original is untouched.
#
proc midiremove {mf tlist overwrite} {
  global newfile

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  set newsize [expr $mtrk - [llength $tlist]]

  if {$overwrite} {
    set newfile $mf
    for {set i 0} {$i < $mtrk} {incr i} {
      # remove one indicated track
      if {[lsearch -exact $tlist $i] != -1} {
        mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
        # and find one above to fall into its place
        for {set k [expr $i+1]} {$k < $mtrk} {incr k} {
          if {[lsearch -exact $tlist $k] == -1} {
            midicopy "$mf $i" 0 "$mf $k" 0 \
              [expr "[miditrack $mf $k end] + 1"]
            # marking the old copy for deletion as well..
            set tlist "$tlist $k"
            break
          }
        }
      }
    }
    midiconfig $mf "tracks $newsize"
  } else {
    # I know I could just copy the entire song and use the above 
    # function but I want this to be fast for play w/ muted tracks
    set newfile [midimake]
    midiconfig $newfile "tracks $newsize" "division $mdiv" "format $mfmt"
    for {set i 0; set k 0} {$i < $mtrk} {incr i} {
      # copy the non-indicated tracks
      if {[lsearch -exact $tlist $i] == -1} {
        midicopy "$newfile $k" 0 "$mf $i" 0 \
          [expr "[miditrack $mf $i end] + 1"]
        incr k
      }
    }
  }
  return $newfile
}

proc midikeep {mf tlist overwrite} {

  scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  if {$mfmt == 1 && [lsearch -exact $tlist 0] == -1} {
    set tlist [lappend tlist 0]
  }
  for {set i 0; set rlist ""} {$i < $mtrk} {incr i} {
    if {[lsearch -exact $tlist $i] == -1} {
      set rlist [lappend rlist $i]
    }
  }
  set newfile [midiremove $mf $rlist $overwrite]
  return $newfile
}

# ---------------------------------------------------------------------
# Remove all events on channels listed by clist

proc midimap {mf clist} {

  # work with a copy of the original
  set newf [midiremove $mf {} 0]

  scan [getConfig $newf] "%d %d %d" mdiv mfmt mtrk
  for {set i 0} {$i < $mtrk} {incr i} {
    midirewind $newf $i

    while {[set event [midiget $mf $i next]] != "EOT"} {
      set etype [string range [lindex $event 1] 0 3]
      if {$etype != "Meta"} {
        if {[lsearch -exact $clist [lindex $event 2]] == -1} {
          mididelete $newf $i $event
        }
      }
    }
    fixMetaEndOfTrack $newf $i
  }
  return $newf
}

# ---------------------------------------------------------------------
# Requires that you have my rand extensions compiled into tcl
    
  


# --------------END stuff that should be coded into C++ ---------------

# --------------BEGIN (useful?) reusable dialog-box stuff ----------------

# See John Ousterhout's book -- that's where this came from
#
proc dialog {w parent text bitmap default args} {
  global button

  toplevel $w -class Dialog
  wm transient $w $parent
  set x [expr [winfo x $parent]+80]
  set y [expr [winfo y $parent]+60]
  wm geometry $w "+$x+$y"
  frame $w.top -relief raised -bd 1
  pack $w.top -side top -fill both
  frame $w.bot -relief raised -bd 1
  pack $w.bot -side bottom -fill both
  message $w.top.message -width 5i -text $text 
  pack $w.top.message -side right -expand 1 -fill both \
    -padx 3m -pady 3m
  if {$bitmap != ""} {
    label $w.top.bitmap -bitmap $bitmap
    pack $w.top.bitmap -side left -padx 3m -pady 3m
  }
  set i 0
  foreach but $args {
    button $w.bot.but$i -text $but -command "set button $i"
    if {$i == $default} {
      frame $w.bot.def -relief sunken -bd 1
      raise $w.bot.but$i
      pack $w.bot.def -side left -expand 1 -padx 3m -pady 2m
      pack $w.bot.but$i -in $w.bot.def -side left -padx 2m \
        -pady 2m -ipadx 2m -ipady 1m
    } else {
      pack $w.bot.but$i -side left -expand 1 -padx 3m -pady 3m \
        -ipadx 2m -ipady 1m
    }
    incr i
  }
  if {$default >= 0} {
    bind $w <Return> "$w.bot.but$default flash; set button $default"
    bind $w <Button-3> "$w.bot.but$default flash; set button $default"
  }

  tkwait visibility $w
  grab set $w
  focus $w.bot.but$default

  tkwait variable button
  destroy $w
  return $button
}

#-------------------------------------------------------------------
#
proc getChannel {trk ochan} {
  global CHANNEL1
  global getval foo

  set w .tc$trk
  set parent .

  toplevel $w -class Dialog
  wm transient $w .
  set x [expr [winfo x $parent]+320]
  set y [expr [winfo y $parent]+140]
  wm geometry $w "+$x+$y"

  frame $w.top -relief raised -bd 2
  pack $w.top -side top -fill both 
  frame $w.mid -bd 1
  pack $w.mid -side top -fill x -expand 1
  for {set i 0} {$i < 4} {incr i} {
    frame $w.mid.m$i
    pack $w.mid.m$i -in $w.mid -side left -fill x -expand 1
  }

  frame $w.bot -relief raised -bd 1
  pack $w.bot -side bottom -fill both
  message $w.top.message -width 3i -text "Channel for Track $trk"
  pack $w.top.message -side right -fill both -padx 1m -pady 1m

  button $w.bot.ok -text "OK" -command {if {$getval != 99} {set foo 1}}
  button $w.bot.cancel -text "Cancel" -command {set getval 99; set foo 1}

  for {set i 0} {$i < 4} {incr i} {
    for {set j 0} {$j < 4} {incr j} {
      set ch0 [expr 4 * $i + $j + $CHANNEL1]
      set ch1 $ch0
      if {$ch1 < 10} {
        set ch1 " $ch1"
      }
      radiobutton $w.mid.b$ch0 -text $ch1 -variable getval -value $ch1 \
        -command "$w.bot.ok configure -state normal"
      pack $w.mid.b$ch0 -in $w.mid.m$i -side top -expand 1 -fill both
    }
  }

  pack $w.bot.ok $w.bot.cancel -side left -padx 2m -pady 2m \
        -ipadx 1m -fill x -expand 1

  if {$ochan == 99} {
    $w.bot.ok configure -state disabled
  } else {
    $w.mid.b$ochan select
  }

  tkwait visibility $w
  grab set $w

  tkwait variable foo
  destroy $w

  return $getval
}

# ------------------------------------------------------------------
#
proc getChannelMap {} {
  global CHANNEL1 DEVTAB
  global CurDev ChanMap
  global foo

  set w .mm$CurDev
  set parent .

  set omap $DEVTAB($CurDev,map)
  for {set i 0} {$i < 16} {incr i} {
    if {[lsearch $omap $i] == -1} {
      set ChanMap($i) 1
    } else {
      set ChanMap($i) 0
    }
  } 

  toplevel $w -class Dialog
  wm transient $w .
  set x [expr [winfo x $parent]+320]
  set y [expr [winfo y $parent]+140]
  wm geometry $w "+$x+$y"

  frame $w.top -relief raised -bd 2
  pack $w.top -side top -fill both 
  frame $w.mid -bd 1
  pack $w.mid -side top -fill x -expand 1
  for {set i 0} {$i < 4} {incr i} {
    frame $w.mid.m$i
    pack $w.mid.m$i -in $w.mid -side left -fill x -expand 1
  }

  frame $w.bot -relief raised -bd 1
  pack $w.bot -side bottom -fill both
  message $w.top.message -width 3i \
    -text "Channel Map for $DEVTAB($CurDev,name)"
  pack $w.top.message -side right -fill both -padx 1m -pady 1m

  button $w.bot.ok -text "OK" -command {set foo 1}
  button $w.bot.cancel -text "Cancel" -command {set foo -1}

  for {set i 0} {$i < 4} {incr i} {
    for {set j 0} {$j < 4} {incr j} {
      set ch0 [expr 4 * $i + $j]
      set ch1 [expr 4 * $i + $j + $CHANNEL1]
      if {$ch1 < 10} {
        set ch1 " $ch1"
      }
      checkbutton $w.mid.b$ch0 -text $ch1 -variable ChanMap($ch0)
      pack $w.mid.b$ch0 -in $w.mid.m$i -side top -expand 1 -fill both
    }
  }

  pack $w.bot.ok $w.bot.cancel -side left -padx 2m -pady 2m \
        -ipadx 1m -fill x -expand 1

  tkwait visibility $w
  grab set $w

  tkwait variable foo

  if {$foo == 1} {
    set newmap ""
    for {set i 0} {$i < 16} {incr i} {
      if {$ChanMap($i) == 0} {
        lappend newmap $i
      }
    }
    set DEVTAB($CurDev,map) $newmap
  }

  destroy $w
}

# ------------------------------------------------------------------
# Similar but with a parameter rather than a button number.
# FIX ME I need a Cancel button too.
#
proc getEntry {w parent text bitmap varname default width xoffset yoffset} {
  global getval foo
  if {$default != ""} {
    set getval $default
  } else {
    set getval 1.0
  }
  if {$width == ""}   { set width 5 }
  if {$xoffset == ""} { set xoffset 0 }
  if {$yoffset == ""} { set yoffset 0 }
  toplevel $w -class Dialog
  wm transient $w $parent
  set x [expr [winfo x $parent]+80+$xoffset]
  set y [expr [winfo y $parent]+60+$yoffset]
  wm geometry $w "+$x+$y"
  frame $w.top -relief raised -bd 1
  pack $w.top -side top -fill both
  frame $w.bot -relief raised -bd 1
  pack $w.bot -side bottom -fill both
  message $w.top.message -width 3i -text $text
  pack $w.top.message -side right -expand 1 -fill both \
    -padx 3m -pady 3m
  if {$bitmap != ""} {
    label $w.top.bitmap -bitmap $bitmap
    pack $w.top.bitmap -side left -padx 3m -pady 3m
  }
  label $w.bot.lval -text "$varname"
  entry $w.bot.val -width $width -relief sunken -bd 1 -textvariable getval
  button $w.bot.ok -text " OK " -command "update idletasks; set foo 1"
  pack $w.bot.lval $w.bot.val $w.bot.ok -side left -padx 3m -pady 3m \
        -ipadx 2m -ipady 1m -expand 1
  bind $w.bot.val <Return> {set foo 1}

  tkwait visibility $w
  grab set $w
  focus $w.bot.val

  tkwait variable foo
  destroy $w
  return $getval
}

# ---------------------------------------------------------------------
# The best things in life vary on a log scale ;-)
#
proc getLogScale {w parent text bitmap varname default help} {
  global getval slideval foo
  if {$default != ""} {
    if {$default > 100.0} { set default 100.0 }
    if {$default < 0.010} { set default 0.010 }
    set getval $default
  } else {
    set getval 1.0
  }
  set slideval [expr round(log10($getval)*100)]
  toplevel $w -class Dialog
  wm transient $w $parent
  set x [expr [winfo x $parent]+80]
  set y [expr [winfo y $parent]+60]
  wm geometry $w "+$x+$y"
  frame $w.top -relief raised -bd 1
  frame $w.mid -relief sunken -bd 1
  frame $w.bot -relief raised -bd 1
  pack $w.top $w.mid $w.bot -side top -fill both
  message $w.top.message -width 3i -text $text
  pack $w.top.message -side right -expand 1 -fill both \
    -padx 3m -pady 3m
  if {$bitmap != ""} {
    label $w.top.bitmap -bitmap $bitmap
    pack $w.top.bitmap -side left -padx 3m -pady 3m
  }
  if {$varname == ""} {
    scale $w.mid.scale -showvalue 0 -orient horizontal \
      -from -200 -to 200 -command logScale
  } else {
    scale $w.mid.scale -showvalue 0 -orient horizontal \
      -from -200 -to 200 -command logScale -label $varname
  }
  entry $w.mid.val -width 3 -relief sunken -bd 2 -textvariable getval
  $w.mid.scale set $slideval
  button $w.bot.ok -text " OK " -command "update idletasks; set foo 1" 

  pack $w.mid.scale \
    -side left -padx 2m -pady 1m \
    -expand 1 -fill both
  pack $w.mid.val -side right -padx 2m -pady 2m -ipady 0.15m \
    -expand 1 -fill x
  if {$help != ""} {
    button $w.bot.help -text Help -command "$help"
    pack $w.bot.ok $w.bot.help -side left -padx 3m -pady 1m \
      -ipadx 2m -expand 1 -fill x
  } else {
    pack $w.bot.ok -padx 3m -pady 1m -ipadx 2m
  }
  bind $w.mid.val <Return> {set foo 1}

  tkwait visibility $w
  grab set $w
  focus $w.mid.val

  tkwait variable foo
  destroy $w
  return $getval
}
# ---------------------------------------------------------------------
# Linear is useful too
#
proc getLinearScale {w parent text bitmap varname default help} {
  global getval slideval foo
  if {$default != ""} {
    if {$default > 50} { set default 50 }
    if {$default < -50} { set default -50 }
    set getval $default
  } else {
    set getval 0
  }
  set slideval $getval
  toplevel $w -class Dialog
  wm transient $w $parent
  set x [expr [winfo x $parent]+80]
  set y [expr [winfo y $parent]+60]
  wm geometry $w "+$x+$y"
  frame $w.top -relief raised -bd 1
  frame $w.mid -relief sunken -bd 1
  frame $w.bot -relief raised -bd 1
  pack $w.top $w.mid $w.bot -side top -fill both
  message $w.top.message -width 3i -text $text
  pack $w.top.message -side right -expand 1 -fill both \
    -padx 3m -pady 3m
  if {$bitmap != ""} {
    label $w.top.bitmap -bitmap $bitmap
    pack $w.top.bitmap -side left -padx 3m -pady 3m
  }
  if {$varname == ""} {
    scale $w.mid.scale -showvalue 0 -orient horizontal \
      -from -50 -to 50 -command linearScale
  } else {
    scale $w.mid.scale -showvalue 0 -orient horizontal \
      -from -50 -to 50 -command linearScale -label $varname
  }
  entry $w.mid.val -width 3 -relief sunken -bd 2 -textvariable getval
  $w.mid.scale set $slideval
  button $w.bot.ok -text " OK " -command "update idletasks; set foo 1" 

  pack $w.mid.scale \
    -side left -padx 2m -pady 1m \
    -expand 1 -fill both
  pack $w.mid.val -side right -padx 2m -pady 2m -ipady 0.15m \
    -expand 1 -fill x
  if {$help != ""} {
    button $w.bot.help -text Help -command "$help"
    pack $w.bot.ok $w.bot.help -side left -padx 3m -pady 1m \
      -ipadx 2m -expand 1 -fill x
  } else {
    pack $w.bot.ok -padx 3m -pady 1m -ipadx 2m
  }
  bind $w.mid.val <Return> {set foo 1}

  tkwait visibility $w
  grab set $w
  focus $w.mid.val

  tkwait variable foo
  destroy $w
  return $getval
}

proc logScale {value} {
  global getval
  set getval [expr pow(10.0000,$value/100.0000)]
}

proc linearScale {value} {
  global getval
  set getval $value
}

# ---------------------------------------------------------------------
#
proc displayText {w title width height text} {
  global TEAROFF

  toplevel $w
  wm title $w $title
  wm minsize $w 1 1
  set x [expr [winfo x .]+200]
  set y [expr [winfo y .]-100] ; if {$y < 0} {set y 10}
  wm geometry $w "+$x+$y"
  frame $w.mbar -relief raised -bd 1
  pack $w.mbar -side top -fill x
  menubutton $w.mbar.file -text File -underline 0 \
    -menu $w.mbar.file.m
  pack $w.mbar.file -side left
  menu $w.mbar.file.m -tearoff $TEAROFF
  $w.mbar.file.m add command -label Dismiss -command "destroy $w"
  text $w.text -width $width -height $height -wrap word \
    -yscrollcommand "$w.scroll set" -setgrid 1
  scrollbar $w.scroll -command "$w.text yview"
  pack $w.text -side left -expand 1 -fill both
  pack $w.scroll -side right -fill y
  $w.text insert end $text
  $w.text configure -state disabled
}

# ------------------------------------------------------------------------ 
# FIX ME I'm not very general yet
#
proc shellCmd {title width height cmdlist} {
  global TEAROFF

  if {! [winfo exists .sh]} {
    toplevel .sh
    wm title .sh $title
    wm minsize .sh 1 1
    text .sh.text -setgrid 1 -yscrollcommand ".sh.scroll set" \
      -width $width -height $height
    scrollbar .sh.scroll -command ".sh.text yview"
    frame .sh.mbar -relief raised -bd 1
    pack .sh.mbar -side top -fill x
    menubutton .sh.mbar.file -text File -underline 0 \
      -menu .sh.mbar.file.m
    pack .sh.mbar.file -side left
    menu .sh.mbar.file.m -tearoff $TEAROFF
      .sh.mbar.file.m add command -label Dismiss -command "destroy .sh"
    pack .sh.text -side left 
    pack .sh.scroll -side right -fill y
  } else {
    .sh.text insert end "\n\n"
    .sh.text yview moveto 1.0
  }
  .sh.text insert end "Running midi2tex -> tex -> xdvi.. watch for errors.\n"
  .sh.text insert end "Be warned that this only works for small parts!\n"
  update idletasks

  foreach cmd $cmdlist {
     .sh.text insert end "[exec sh -c $cmd]"
     .sh.text yview moveto 1.0
  }
}

# ---------------------------------------------------------------------
# This is a real hack, but allows me to disable selection in a listbox
# without crafting a new widget.
#
proc disableSelect {t} {
  bindtags $t $t
}  

# The mididevice command is not enough.  Maybe the module isn't loaded
proc hasMidi {} {
  global NUMDEV DEVTAB MidiStatus

  set i 0
  set done 0
  set retry 0
  set NUMDEV 0

  # first check for old version of tclmidi.. this is a hack
  if {![catch {miditime smf}]} {
    dialog .h . \
    "You must upgrade to tclmidi-3.0 or above\n     in order to use this version of tkseq." \
     error 0 OK
    exit
  }

  # is MIDI support compiled into tclmidi?
  if {[mididevice]} {
    # Find all devices
    while {! $done} {
      set done [catch {set DEVTAB($i,dev) [mididevice /dev/midi$i]}]
      if {$done} {
        # we *DO* need at least one device available
        if {$i == 0} {
          set msg \
          "Cannot open device /dev/midi$i...\nRealtime controls may not work."
          set done [dialog .h . "$msg" info 0 Retry Disable]
        }
      } else {
        set DEVTAB($i,name) "/dev/midi$i"
        set DEVTAB($i,raw) [mididevice /dev/rmidi$i]
	set DEVTAB($i,map) {}
        incr i
      }
    }    
    set NUMDEV $i
  } else {
    if {$MIDIDEV != ""} {
      set msg "No MIDI hardware support.\nRealtime controls disabled."
      dialog .h . "$msg" error 0 OK
    }
  }
  return [expr $NUMDEV > 0]
}

# -------------BEGIN Main ---------------------------------------------
#
# first time through we need to initialize the play and record pointers
set TKS_VERSION 0.94d
set PlayFile ""
set RecFile ""
set TmpFile ""

if {$tk_version < 4.0} {
  dialog .h . "This version of TkSeq\nrequires Tk 4.0 or better." error 0 OK
  exit 0
}

midiCleanSlate 1
drawMainWindow
midiCleanSlate 0

watchCursor .

if {$argc == 1} {
  set PlayName $argv
  fileReadMidi
} else {
  if {$HAVE_VOXWARE_GUS && $GUS_AUTO_LOAD} {
    loadGusPatch $PatchList $PatchFile
  }
} 
if {$HAVE_VOXWARE_GUS && $GUS_AUTO_THRU} {
  setGusThru 1
}

normalCursor .

# -------------END Main -----------------------------------------------

@


1.42
log
@more small changes, mostly to patch manager/audition
@
text
@d4 1
a4 1
# extensions. This is version 0.94b, Copyright (c) 1995  Greg Wolodkin
d1390 1
a1390 1
    if {[dialog .h . {This will erase the\nexisting MIDI sequence!} \
d1757 2
a1758 2
  set ti [.mbar.settings.menu index "Enable Thru"]
  if {$ti == -1} {
d5077 1
a5077 1
set TKS_VERSION 0.94b
@


1.41
log
@Some changes in midisend stuff, cleaned up scrolling problems with
the "find closest" stuff.  Lost polyphony again, using "current"
tag.  The other option is to make a binding for each note, and
I don't like that idea much.
@
text
@d245 1
a245 1
  global GusThruPid LastPatch
a332 1
  set LastPatch 0
d950 1
a950 1
    -variable SHOWCHAN \
d953 1
a953 1
    -variable SHOWPROG \
d956 1
a956 1
    -variable SHOWMEAS \
d1799 1
a1799 1
  global GusThruPid PLoadList PLoadFile LastPatch
a1814 1
    set LastPatch 0
a1860 2
    } else {
      set LastPatch $pnum
d2090 1
a2090 1
  global GM_PATS LastPatch
d2096 5
d2119 1
a2119 1
  global PLoadList PLoadFile
d2135 1
a2135 1
  global LastPatch CurDev
d2138 7
a2144 2
    loadSinglePatch [selection get]
    set audition $LastPatch
@


1.40
log
@Getting really cool.  Now there is a patch manager
@
text
@d4 1
a4 1
# extensions. This is version 0.94, Copyright (c) 1995  Greg Wolodkin
d478 2
d513 5
a517 3
            if {[expr $tmpprog] != $tt} {
              set tmpprog [string trimright $tmpprog]*
              break
d522 1
a522 1
        if {[incr tmpcount] > 32} { break }
d1736 18
d1805 2
a1806 2
  if {[winfo exists .patch]} {
    watchCursor .patch
d1817 2
a1818 2
    if {[winfo exists .patch]} {
      normalCursor .patch
d1874 2
a1875 2
  if {[winfo exists .patch]} {
     normalCursor .patch
d1881 1
a1881 1
  global KeyYtag KeyYval BlackKey WhiteKey CurDev DEVTAB
d1890 1
a1890 1
  set w .patch
d1929 1
a1929 1
  button $w.autoload -text "Autoload" \
d1931 2
a1932 2
  button $w.load -text ">>" -command {
    if {[.patch.l.list curselection] != ""} {
d1935 2
a1936 2
  button $w.clear -text "<<" -command {
    if {[.patch.r.list curselection] != ""} {
d1938 3
d1942 3
a1944 1
  button $w.allclear -text "Clear" \
d1946 2
a1947 3
  button $w.audition -text "Audition" -command { auditionPatch }
  button $w.help -text "Help" -command { patchManagerHelp }
  button $w.ok -text "Dismiss" -command "destroy $w"
d1970 1
a1970 1
  pack $w.autoload $w.load $w.clear $w.allclear $w.audition $w.help $w.ok \
d1985 1
a1985 6
      # First note is C1
      set notenum [expr 12 * $k + $j + 36]

      if {$notenum == 60} {
        set fill $ActiveKey
      }
d1987 1
a1987 1
        -outline black -tags "$notenum $KeyYtag($j)"
d1992 4
d1998 2
a1999 1
      set RawPitch [lindex [%W gettags [%W find closest %x %y]] 0]
d2003 2
a2004 1
      set RawPitch [lindex [%W gettags [%W find closest %x %y]] 0]
d2015 2
a2016 1
      set RawPitch [lindex [%W gettags [%W find closest %x %y]] 0]
d2024 12
a2035 2
  bind $w.l.list <Button-3> { selection clear %W }
  bind $w.r.list <Button-3> { selection clear %W }
d2041 11
d2070 8
a2077 2
  if {[winfo exists .patch]} {
    .patch.r.list delete 0 end
d2081 8
a2088 1
      .patch.r.list insert end [format "%3d: %8s" $pnum $pfil]
d2102 1
a2102 1
      [getEntry .patch.et .patch "Loading Non-GM Patch" {} \
d2105 1
a2105 1
      dialog .patch.err "Patch value out of range." error 0 OK
d2109 1
a2109 1
      dialog .patch.err "Expecting an integer." error 0 OK
d2136 1
a2136 1
  if {[.patch.l.list curselection] != ""} {
d2139 1
a2139 1
  } elseif {[.patch.r.list curselection] != ""} {
d2796 1
a2796 1
      -outline $BlackKey -tags "[expr $j+48] $KeyYtag($j)"
d2809 1
a2809 1
    set SelKey [%W find closest %x %y]
d2814 1
a2814 1
      set RawPitch [lindex $TagList 0]
d2823 1
a2823 3
      if {$noteoff != ""} {
        midisend $DEVTAB($CurDev,raw) "0 NoteOff 0 $RawPitch 0"
      }
a2982 1
#  dialog .foo . "mididelete $Gmf($type) $Gtrk($type) $event" info 0 OK
a3211 1
  global noteoff
d3339 1
a3339 3
        # First note is C0
        set notenum [expr 12 * $k + $j + 24]

d3341 1
a3341 1
          -outline black -tags "$notenum $KeyYtag($j)"
d3352 2
a3353 1
        set pitch   [lindex [%W gettags [%W find closest %x %y]] 0]
d3364 2
a3365 1
        set pitch   [lindex [%W gettags [%W find closest %x %y]] 0]
d3383 2
a3384 1
        set pitch  [lindex [%W gettags [%W find closest %x %y]] 0]
d3478 1
a3478 1
    .pr$trk.keys itemconfigure 60 -fill $ActiveKey
d3480 1
a3480 1
    .pr$trk.keys itemconfigure 60 -fill $WhiteKey
d5071 1
a5071 1
set TKS_VERSION 0.94
d5085 2
a5088 1
  watchCursor .
a5089 1
  normalCursor .
d5098 2
@


1.39
log
@This is 0.93
@
text
@d4 1
a4 1
# extensions. This is version 0.93, Copyright (c) 1995  Greg Wolodkin
d38 1
d66 22
d99 2
a100 2
  # Set this to one to force MIDI THRU on by default
  set MTHRU 0
d245 4
d263 5
d330 9
d453 1
a453 1
  global NUMTRACKS SHOWPROG DEVTAB CHANNEL1
d455 1
d481 10
d786 2
a787 2
  global DDIVISION DQUANTIZE HAVE_TEX BITMAPS NUMTRACKS 
  global TEAROFF SHOWPROG SHOWCHAN DEVTAB NUMDEV
d789 2
a790 1
  global Mtracks Mformat Mdivision PlayFile PlayName
d838 11
a848 2
  .mbar.settings.menu add cascade -label "Device" -underline 0 -menu \
    .mbar.settings.menu.device
d851 4
a854 3
  .mbar.settings.menu add cascade -label "MIDI Thru" -underline 5 -menu \
    .mbar.settings.menu.thru
  .mbar.settings.menu add command -label "MIDI Map" -underline 7 \
d856 15
a877 1
  .mbar.settings.menu add separator
a879 2
  .mbar.settings.menu add command -label "SMPTE" -underline 0\
    -command {setSMPTEoffset}
a880 2
  .mbar.settings.menu add command -label "Tempo" -underline 0 \
    -command {editMap Tempo}
d885 4
a888 6

  menu .mbar.settings.menu.device -tearoff $TEAROFF
  for {set i 0} {$i < $NUMDEV} {incr i} {
    .mbar.settings.menu.device add radiobutton -label "$DEVTAB($i,name)" \
  	-underline 9 -variable CurDev -value $i
  }
d894 1
a894 1
  .mbar.settings.menu.clock add radiobutton -label "SMPTE/MTC" \
a900 8
  menu .mbar.settings.menu.thru -tearoff $TEAROFF
  .mbar.settings.menu.thru add radiobutton -label "On" \
  	-underline 1 -variable MidiThru($CurDev) -value 1 \
	-command {mididevice $DEVTAB($CurDev,dev) {midithru on}}
  .mbar.settings.menu.thru add radiobutton -label "Off" \
  	-underline 1 -variable MidiThru($CurDev) -value 0 \
	-command {mididevice $DEVTAB($CurDev,dev) {midithru off}}

d946 1
a946 7
  .mbar.view.menu add cascade -label "Options" -underline 0 -menu \
    .mbar.view.menu.options
  .mbar.view.menu add command -label "Refresh" -underline 0 \
    -command {showTrackEverything {}}

  menu .mbar.view.menu.options -tearoff $TEAROFF
  .mbar.view.menu.options add checkbutton -label "Channels" \
d949 1
a949 1
  .mbar.view.menu.options add checkbutton -label "Program Changes" \
d952 1
a952 1
  .mbar.view.menu.options add checkbutton -label "Measure Info" \
d956 4
d1215 1
d1276 1
a1276 1
  entry .file.dir.entry -width 45 -relief sunken -bd 2 -textvariable fdir
d1422 1
d1452 4
d1463 1
a1463 1
  global NUMDEV DEVTAB
d1480 5
d1732 356
d2680 1
a2680 1
  global noteoff
d2752 2
a2753 3
      set pitch [lindex $TagList 0]
      midisend $DEVTAB($CurDev,raw) "0 NoteOn 0 $pitch 100"
      set noteoff "0 NoteOff 0 $pitch 0"
a2754 1

a2758 1
  # How would you FIX ME to be polyphonic?  Ugly.
d2761 6
a2766 1
      midisend $DEVTAB($CurDev,raw) $noteoff
a3293 1
    # FIX ME to be polyphonic
d3304 12
a3315 1
        set noteoff "0 NoteOff $channel $pitch 0"
d3318 14
a3331 1
        midisend $DEVTAB($CurDev,raw) $noteoff
d5012 1
a5012 1
set TKS_VERSION 0.93
d5031 7
@


1.38
log
@Added midisend stuff to keyboard maps
@
text
@d4 1
a4 1
# extensions. This is version 0.92, Copyright (c) 1995  Greg Wolodkin
d263 1
a263 1
  if {$SmpteClk($CurDev)} {
d820 1
a820 1
  .mbar.settings.menu.clock add radiobutton -label "Internal" \
d826 3
d1858 1
a1858 1
    set eottime [expr [miditrack $PlayFile $dtrk end] + 1]
d3701 1
a3701 1
  if {$SmpteClk($CurDev)} {
d3711 5
a3715 1
    midifeature $DEVTAB($CurDev,dev) kernel_timing
d4559 1
a4559 1
set TKS_VERSION 0.92
@


1.37
log
@Version 0.91
ls
@
text
@d4 1
a4 1
# extensions. This is version 0.91, Copyright (c) 1995  Greg Wolodkin
d216 1
a216 1
  global MTHRU NUMDEV
d234 2
d412 2
a413 2
  global NUMTRACKS SHOWPROG
  global PlayFile
d430 1
a430 1
      set tmpprog " --"
d439 12
a450 1
          if {$tmpprog == " --"} {
d452 1
a452 1
              set tmpprog "  $tt"
d454 1
a454 1
              set tmpprog " $tt"
d456 1
a456 1
              set tmpprog $tt
d459 2
a460 2
            if {$tmpprog != $tt} {
              set tmpprog " **"
d509 1
a509 1
              set tmpchan " $tt"
d511 1
a511 1
              set tmpchan $tt
d514 2
a515 2
            if {$tmpchan != $tt} {
              set tmpchan "**"
d1007 1
a1007 1
    -width 3 -height 1 -relief flat
d1012 1
a1012 1
    -width 2 -height 1 -relief flat
d2246 1
d2248 2
a2249 1
  global WhiteKey BlackKey ActiveKey
d2303 1
a2303 1
      -outline $BlackKey -tags "$KeyYtag($j)"
d2309 1
a2309 1
      set keytype [lindex $TagList 0]
d2319 7
d2329 7
d2344 2
a2345 2
  if {[lindex $TagList 0] == "black"} {
    set foo [expr 1 + $GetFlat]
d2348 1
a2348 1
    set foo 1
d2715 1
a2715 1
  global TEAROFF PIANOSCALE SHOWMIDC
d2717 1
a2717 1
  global TimeScale KeyYval KeyYtag 
d2719 2
a2720 1
  global WhiteKey BlackKey ActiveKey
d2852 1
a2852 1
          -outline black -tags "$KeyYtag($j) note$notenum"
d2861 18
d2963 1
a2963 1
    .pr$trk.keys itemconfigure note60 -fill $ActiveKey
d2965 1
a2965 1
    .pr$trk.keys itemconfigure note60 -fill $WhiteKey
d4534 1
d4552 1
a4552 1
set TKS_VERSION 0.91
@


1.36
log
@Version 0.90
@
text
@d4 1
a4 1
# extensions. This is version 0.90, Copyright (c) 1995  Greg Wolodkin
d34 1
a34 1
  global DDIVISION DQUANTIZE NUMTRACKS MTHRU
d57 1
a57 1
  # unless you specifically ask for it.
d59 1
d85 1
a85 1
  set SHOWMEAS 0
d238 1
d316 5
a320 1
  if {$tlist == ""} { update }
d1036 1
a1036 1
  label .stat.vtrk -textvariable Mtracks 
d1102 24
a1125 8
  bind .trkname.list <Button-3> \
     { selection clear .trkname.list }
  bind .trkmute.list <Double-1> \
     { trackMuteSolo [.trkmute.list curselection] }
  bind .trkchan.list <Double-1> \
     { trackForceChannel [.trkchan.list curselection] }
  bind .trkprog.list <Double-1> \
     { windowMappedEvent [.trkprog.list curselection] Patch }
d1140 1
a1140 1
  foreach i {numb meas} { disableSelect .trk$i.list }
d1143 4
a1146 4
  foreach i {mute chan prog} {
    set scol [lindex [.trk$i.list configure -background] 4]
    .trk$i.list configure -selectbackground $scol
  }
d2759 1
a2759 1
      -command "destroy $w" 
d2861 1
a2861 1
  set Mquantize 96
d2952 1
a2952 1
  global DQUANTIZE
d2956 2
a2957 1
    set xscale [expr $Mquantize.0000 / $DQUANTIZE.0000] 
d2959 1
a2959 1
    set Mquantize $DQUANTIZE
d2966 5
d3065 20
d4503 1
a4503 1
set TKS_VERSION 0.90
d4519 1
d4521 1
@


1.35
log
@Changed the default color scheme to grey
@
text
@d4 3
a6 1
# extensions. This is version 0.6.6, Copyright (c) 1995  Greg Wolodkin
d34 1
a34 1
  global MIDIDEV SMPTEDEV DDIVISION DQUANTIZE NUMTRACKS MTHRU
d37 1
a37 1
  global PIANOSCALE CHANNEL1
d39 2
a40 7
  # these are some basic defaults
  set MIDIDEV "/dev/midi0"
  set SMPTEDEV "/dev/smpte0"

  # Use these if you don't have any such devices
  # set MIDIDEV ""
  # set SMPTEDEV ""
d66 1
a66 1
  set DDIVISION 240
a68 3
  # MIDI thru on by default (set to zero if you hate it..)
  set MTHRU 1

d75 3
d84 1
a84 1
  set SHOWMEAS 1
d214 3
a216 2
  global DDIVISION NUMTRACKS SHOWPROG SHOWCHAN CHANNEL1 TKS_VERSION MTHRU
  global MidiState SmpteClk LabelNow Now
d226 7
a248 1
  set SmpteClk 0
d254 1
a254 1
  set MidiThru $MTHRU
d259 5
a263 2
  set LabelNow "MIDI:"
  set Now [tick2measure 0]
d716 2
a717 2
  global MIDIDEV SMPTEDEV TEAROFF SHOWPROG SHOWCHAN
  global MidiThru SmpteClk LabelNow Now MidiStatus
d720 2
d766 2
d772 3
d777 1
a777 1
  .mbar.settings.menu add cascade -label "Division" -underline 0 -menu \
d794 6
d802 1
a802 1
  	-underline 0 -variable SmpteClk -value 0 \
d805 1
a805 1
  	-underline 0 -variable SmpteClk -value 1 \
d810 2
a811 2
  	-underline 1 -variable MidiThru -value 1 \
	-command {mididevice {midithru on}}
d813 2
a814 2
  	-underline 1 -variable MidiThru -value 0 \
	-command {mididevice {midithru off}}
d816 2
a817 1
  if {$SMPTEDEV == ""} {
d917 2
a1111 1
  set MidiStatus [hasMidi]
d1113 1
a1113 1
    mididevice "midithru $MidiThru"
d1336 4
a1339 1
  set PlayFile [midiread $ff]
d1358 1
d1360 1
d1372 3
a1374 1
  midistop
d2565 23
d2613 1
a2613 1
    showTrackEverything $tlist
d3396 3
a3398 3
  global MIDIDEV SMPTEDEV TEMPO
  global MidiState PlayFile RecFile TmpFile LabelNow
  global Modified MuteList SoloList Mdivision
d3411 1
a3412 1
      watchCursor .
d3414 1
a3414 3
      normalCursor .
      getStopTime $TmpFile
      midirecord $RecFile $TmpFile
a3415 1
      watchCursor .
d3417 1
a3417 3
      normalCursor .
      getStopTime $TmpFile
      midirecord $RecFile $TmpFile
d3419 19
a3437 2
      getStopTime $PlayFile
      midirecord $RecFile $PlayFile
d3439 5
d3452 7
a3458 2
  global MIDIDEV SMPTEDEV
  global MidiState PlayFile LabelNow Modified TmpFile MuteList SoloList
d3461 1
a3462 1
      watchCursor .
d3464 1
a3464 3
      normalCursor .
      getStopTime $TmpFile
      midiplay $TmpFile
a3465 1
      watchCursor .
d3467 1
a3467 3
      normalCursor .
      getStopTime $TmpFile
      midiplay $TmpFile
d3469 1
a3469 2
      getStopTime $PlayFile
      midiplay $PlayFile
d3471 23
d3502 3
a3504 2
  global MidiState PlayFile RecFile TmpFile Now
  global PlayName StopTime Modified
d3512 1
a3512 1
    midistop
d3518 7
d3598 2
a3599 2
  global SMPTEDEV MIDIDEV
  global Now LabelNow SmpteClk
d3602 2
a3603 2
  if {$SmpteClk == 1} {
    mididevice "name $SMPTEDEV"
d3605 1
a3605 1
    set foo [miditime smpte]
d3607 1
a3607 1
      set Now "Waiting.."
d3612 1
a3612 1
    mididevice "name $MIDIDEV"
d3614 1
a3614 1
    set foo [miditime smf]
d3618 1
a3618 1
      set Now [tick2measure foo]
d3628 2
a3629 1
  global Now StopTime MidiState
d3633 1
a3633 1
    set foo [miditime smf]
d3637 2
a3638 1
    set Now [tick2measure $foo]; update
d3644 2
a3645 1
  global Now SmpteClk StopTime MidiState
d3649 1
a3649 1
    set foo [miditime smpte]
d3651 1
a3651 1
      set Now "Waiting.."
d3655 1
a3655 1
    update idletasks
d3789 36
d3931 31
d4050 6
a4055 2
      set ch [expr 4 * $i + $j + $CHANNEL1]
      radiobutton $w.mid.b$ch -text $ch -variable getval -value $ch \
d4057 1
a4057 1
      pack $w.mid.b$ch -in $w.mid.m$i -side top -expand 1 -fill both
d4080 76
d4369 20
a4388 15
  toplevel .sh
  wm title .sh $title
  wm minsize .sh 1 1
  text .sh.text -setgrid 1 -yscrollcommand ".sh.scroll set" \
    -width $width -height $height
  scrollbar .sh.scroll -command ".sh.text yview"
  frame .sh.mbar -relief raised -bd 1
  pack .sh.mbar -side top -fill x
  menubutton .sh.mbar.file -text File -underline 0 \
    -menu .sh.mbar.file.m
  pack .sh.mbar.file -side left
  menu .sh.mbar.file.m -tearoff $TEAROFF
  .sh.mbar.file.m add command -label Dismiss -command "destroy .sh"
  pack .sh.text -side left 
  pack .sh.scroll -side right -fill y
d4392 1
a4393 1
#     dialog .ss . "sh -c $cmd" info 0 OK
d4395 1
d4409 1
a4409 1
  global MIDIDEV MidiStatus
d4411 11
a4421 6
  set retry 1

  if {$MIDIDEV != ""} {
    set mididev $MIDIDEV
  } else {
    set mididev "/dev/midi0"
d4425 18
a4442 13
  if {[mididevice]} {          
    set tmpfile [midimake]
    while {$retry} {
      mididevice "name $mididev"
      midistop
      set loaded [expr ![catch "midirecord $tmpfile" msg]]
      midistop
      if {$loaded} { return 1 }
      set msg \
        "Cannot open device $mididev...\nRealtime controls may not work."
      set retry [dialog .h . "$msg" info 0 Disable Retry]
    }
    midifree $tmpfile
d4449 1
a4449 1
  return 0
d4455 1
a4455 1
set TKS_VERSION 0.6.6
@


1.34
log
@0.6.5 -- removed hardcoded `entryconfigure' stuff using index.
Also disabled track menu if no tracks are selected.
@
text
@d4 1
a4 1
# extensions. This is version 0.6.5, Copyright (c) 1995  Greg Wolodkin
d35 1
a35 1
  global PIANOSCALE CHANNEL1 WHITEKEY BLACKKEY ACTIVKEY 
a96 5
  # Some colors I haven't figured out how to handle yet.
  set WHITEKEY #c0c0c0
  set BLACKKEY #000000
  set ACTIVKEY #4080a0

d128 1
a128 1
  puts $foo "*font: -*-Times-Bold-R-Normal-*-14-*-*-*-*-*-*-*"
d132 1
a132 1
   "*Listbox.font: -dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
d134 1
a134 1
   "*Text.font: -dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
d136 1
a136 1
   "*Entry.font: -dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
d140 1
a140 1
   "*subtitle.font: -b&h-lucida-bold-i-normal-sans-12-*-*-*-*-*-iso8859-1"
d142 1
a142 1
   "*message.font: -*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
d144 1
a144 6
   "*text.font: -*-Times-Medium-R-Normal-*-14-*-*-*-*-*-*-*"
  puts $foo ""
  puts $foo "*foreground: #a08880"
  puts $foo "*activeForeground: #a08880"
  puts $foo "*selectForeground: #a08880"
  puts $foo "*disabledForeground: #605050"
d146 2
a147 4
  puts $foo "*background: #483838"
  puts $foo "*activeBackground: #584848"
  puts $foo "*selectBackground: #405068"
  puts $foo "*selectColor: #407090"
d149 4
a152 3
  puts $foo "*highlightBackground: #584848"
  puts $foo "*highlightColor: #584848"
  puts $foo "*highlightThickness: 0"
d154 3
a156 2
  puts $foo "! Used for subtitles and suppressed text"
  puts $foo "*subtitle.foreground: #806860"
d158 3
d162 5
a166 5
  puts $foo "*event.foreground: #808080"
  puts $foo "*grid.foreground: #606060"
  puts $foo "*measure.foreground: #407090"
  puts $foo "*beat.foreground: #509070"
  puts $foo "*quantum.foreground: #907060"
d168 4
a171 4
  puts $foo "*scroll.background: #504040"
  puts $foo "*scroll.activeBackground: #584848"
  puts $foo "*scroll.troughColor: #403030"
  puts $foo "*scroll.width: 12"
d173 4
a176 3
  puts $foo "*scale.background: #483838"
  puts $foo "*scale.activeBackground: #584848"
  puts $foo "*scale.troughColor: #403030"
d178 7
a184 7
  puts $foo "! Normal buttons are blue.."
  puts $foo "*play.foreground: #305070"
  puts $foo "*record.foreground: #305070"
  puts $foo "*pause.foreground: #305070"
  puts $foo "*ffwd.foreground: #305070"
  puts $foo "*rewind.foreground: #305070"
  puts $foo "*stop.foreground: #305070"
d187 6
a192 6
  puts $foo "*play.activeForeground: #60c040"
  puts $foo "*record.activeForeground: #c04060"
  puts $foo "*pause.activeForeground: #4060c0"
  puts $foo "*stop.activeForeground: #4060c0"
  puts $foo "*ffwd.activeForeground: #4060c0"
  puts $foo "*rewind.activeForeground: #4060c0"
d1129 1
a1129 1
proc getFileName {text} {
d1133 3
a1135 1
  global waitname fdir
d1159 1
a1159 1
    -textvariable tmpname
d1167 1
a1167 1
    -command {if {$tmpname != ""} {set waitname $tmpname}}
d1171 1
a1171 1
                set tmpname "";
d1176 1
a1176 1
  button .file.butt.cancel -text Cancel -command {destroy .file}
d1202 1
a1202 1
  bind .file.name.entry <Return> {set waitname $tmpname}
d1207 1
a1207 1
    set tmpname ""
d1214 1
a1214 1
      set tmpname [selection get]
d1222 1
a1222 1
      set tmpname [selection get]; set waitname $tmpname 
d1265 1
a1265 1
    if {[dialog .h . {This will erase the existing MIDI sequence!} \
d1280 1
a1280 1
    if {[dialog .h . {This will overwrite the existing MIDI sequence!} \
d1285 3
a1287 1
  set PlayName [getFileName "Open a MIDI file"]
d1341 1
a1341 1
    if {[dialog .q . {Really quit tkseq?} questhead 0 {OK} {Cancel}]} {
d1355 1
a1355 1
    dialog .h . {There is nothing to save.} error 0 OK
d1358 3
a1360 2
  if {$PlayName == ""} {
    dialog .h . {You haven't specified a filename.} error 0 OK
d1375 1
a1375 1
    dialog .h . {There is nothing to save.} error 0 OK
d1378 5
a1382 1
  set tmpname [getFileName "Save a MIDI file"]
d1419 1
a1419 2
    {You will have to insert MetaSMPTE events manually for now.} \
    info 0 OK
d1958 1
a1958 1
      dialog .d . "Track $i has no channel related messages." info 0 OK
d1994 1
a1994 1
#    dialog .lost . "$lost events were lost as duplicates." info 0 OK
d2182 2
a2183 2
  global WHITEKEY BLACKKEY ACTIVKEY
  global Gdat GetFlat TagList SelKey KeyYtag KeyYval
d2197 7
d2225 1
a2225 1
      set fill $WHITEKEY
d2228 1
a2228 1
      set fill $BLACKKEY
d2237 1
a2237 1
      -outline $BLACKKEY -tags "$KeyYtag($j)"
d2245 1
a2245 1
        %W itemconfigure $SelKey -fill $WHITEKEY
d2247 1
a2247 1
        %W itemconfigure $SelKey -fill $BLACKKEY
d2251 1
a2251 1
    %W itemconfigure $SelKey -fill $ACTIVKEY
d2462 1
a2462 1
    dialog .d . "quantize: $lost events lost as duplicates." info 0 OK 
d2480 1
a2480 1
    dialog .d . "randomize: $lost events lost as duplicates." \
d2503 1
a2503 1
    dialog .d . "offset: $lost events lost as duplicates." \
d2528 1
a2528 1
    dialog .d . "transpose: $lost events lost as duplicates." \
d2612 1
a2612 1
  global TEAROFF PIANOSCALE WHITEKEY BLACKKEY ACTIVKEY SHOWMIDC
d2616 1
d2665 5
a2670 2
    label $w.grid; label $w.event;

d2675 1
d2695 1
a2695 1
      -selectcolor $ACTIVKEY
d2736 1
a2736 1
          set width $PIANOSCALE; set fill $WHITEKEY; set x0 0
d2739 1
a2739 1
          set fill $BLACKKEY; set x0 $x0b
d2838 1
a2838 2
  global WHITEKEY ACTIVKEY
  global ShowMidC
d2841 1
a2841 1
    .pr$trk.keys itemconfigure note60 -fill $ACTIVKEY
d2843 1
a2843 1
    .pr$trk.keys itemconfigure note60 -fill $WHITEKEY
d3130 1
a3130 1
    if {[dialog .ddiv . "Reducing DIVISION may adversely affect timing." \
d3222 1
a3222 1
          if {[dialog .ti$i.di .ti$i {Unable to put event.} warning \
d3324 1
a3324 1
  dialog .d . {Pause has not been implemented yet.} info 0 OK
d3330 1
a3330 1
    {Rewind happens every time you press Stop (for now).} info 0 OK
d3336 1
a3336 1
    {Fast forward has not been implemented yet.} info 0 OK
d4180 1
a4180 1
      set msg "No MIDI hardware support. Realtime controls disabled."
d4190 1
a4190 1
set TKS_VERSION 0.6.5
d4196 1
a4196 1
  dialog .h . "This version of TkSeq requires Tk 4.0 or better." error 0 OK
@


1.33
log
@Modified View menu, moving options into a separate cascade
Added an update before showTrackMeasures {}
This is 0.6.4 unfinished
@
text
@d4 1
a4 1
# extensions. This is version 0.6.4, Copyright (c) 1995  Greg Wolodkin
d613 7
d623 1
a623 2
	.mbar.realtime.menu entryconfigure [expr 0+$TEAROFF] \
    	     -state disabled
d627 1
a627 2
    	.mbar.realtime.menu entryconfigure [expr 0+$TEAROFF] \
      	   -state normal
d632 1
d639 1
a639 2
        .mbar.realtime.menu entryconfigure [expr 1+$TEAROFF] \
          -state disabled
d643 1
a643 2
    	.mbar.realtime.menu entryconfigure [expr 1+$TEAROFF] \
    	   -state normal
d648 1
d655 1
a655 2
    	.mbar.realtime.menu entryconfigure [expr 2+$TEAROFF] \
    	   -state disabled
d659 1
a659 2
        .mbar.realtime.menu entryconfigure [expr 2+$TEAROFF] \
          -state normal
d665 1
a665 2
        .mbar.realtime.menu entryconfigure [expr 6+$TEAROFF] \
           -state disabled
d668 1
a668 2
        .mbar.realtime.menu entryconfigure [expr 6+$TEAROFF] \
          -state normal
d672 1
d679 1
a679 2
        .mbar.realtime.menu entryconfigure [expr 4+$TEAROFF] \
           -state disabled
d683 1
a683 2
        .mbar.realtime.menu entryconfigure [expr 4+$TEAROFF] \
           -state normal
d688 1
d695 1
a695 2
        .mbar.realtime.menu entryconfigure [expr 5+$TEAROFF] \
           -state disabled
d699 1
a699 2
        .mbar.realtime.menu entryconfigure [expr 5+$TEAROFF] \
           -state normal
d704 1
d803 4
a806 4
    .mbar.settings.menu entryconfigure [expr 6+$TEAROFF] \
       -state disabled
    .mbar.settings.menu.clock entryconfigure [expr 1+$TEAROFF] \
       -state disabled
d862 1
a862 1
  menu .mbar.track.menu -tearoff $TEAROFF
d1079 2
a1080 1
  bind .trkname.list <Button-3> "selection clear .trkname.list"
d1597 14
d4177 1
a4177 1
set TKS_VERSION 0.6.4
@


1.32
log
@more fucking fixes.. this is 0.6.3
@
text
@d4 1
a4 1
# extensions. This is version 0.6.3, Copyright (c) 1995  Greg Wolodkin
a310 1
  showTrackMeasures $tlist
d313 2
d393 1
a393 1
      set tmpname "bogus"
d846 8
a853 1
  .mbar.view.menu add checkbutton -label "Channels" -variable SHOWCHAN \
d855 1
a855 1
  .mbar.view.menu add checkbutton -label "Program Changes" \
d858 1
a858 1
  .mbar.view.menu add checkbutton -label "Measure Info" \
a860 3
  .mbar.view.menu add separator
  .mbar.view.menu add command -label "Refresh" -underline 0 \
    -command {showTrackEverything {}}
d1011 1
a1011 1
  label .stat.vfile -textvariable PlayName -width 14
d1019 1
a1019 1
  label .stat.vnow -textvariable Now -width 8 
d4162 1
a4162 1
set TKS_VERSION 0.6.3
@


1.31
log
@Minor fixes, including handling of showTrackEverything when
adding the first track.
@
text
@a247 8
  # open a default `empty' file
  set PlayFile [midimake]
  midiconfig $PlayFile {tracks 1} {format 1} "division $DDIVISION"

  set PlayName "untitled.mid"
  wm iconname . $PlayName
  wm title . "tkseq $TKS_VERSION:  $PlayName"

a248 1

d292 10
d333 2
a335 1
    scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
d392 1
a392 1
      set tmpname ""
d853 3
d1161 1
a1161 1
  	-selectmode browse
d1209 1
d1211 2
a1212 3
    set tt [selection get] 
    if {![file isdirectory $tt]} {
      set tmpname $tt
d1217 2
a1218 3
    set tt [selection get]
    if {[file isdirectory $tt]} { 
      cd $tt; set fdir [pwd]; listFiles 
d1220 1
a1220 1
      set tmpname $tt; set waitname $tmpname 
d1341 1
a1341 4
  if {$MidiState != "stopped"} {
    set MidiState stopped
    midistop
  }
d1524 1
a1524 1
  global MetroData PlayFile
d1553 2
a1554 4
  if {$mtrk == 1} { 
    showTrackEverything {}
  } else {
    showTrackEverything $otrk
d1556 2
d2045 1
a2045 1
  global Gmf Gtrk Gdat Evnt PlayFile
a3342 1
    set Modified 1
d3379 1
a3379 1
  global PlayName StopTime
d3396 1
a3396 1
      if {$PlayName == "untitled.mid" && $mtrk == 1} {
d3402 1
d3407 1
a3407 1
          showTrackEverything {}
d3447 1
d4169 1
@


1.30
log
@small fixes for MTHRU and also width of MIDI/SMPTE field.
This is version 0.6.2
@
text
@d4 1
a4 1
# extensions. This is version 0.6.2, Copyright (c) 1995  Greg Wolodkin
d252 1
a252 1
  set PlayName untitled.mid
d1464 2
a1465 2
  label $win.event.beat -text " Beat " -anchor e
  pack $win.event.subtitle $win.event.meas $win.event.beat -in $win.event \
d1471 2
a1472 2
  entry $win.chan.beat -width 3 -textvariable MetroData(chan,beat)
  pack $win.chan.subtitle $win.chan.meas $win.chan.beat -in $win.chan \
d1478 2
a1479 2
  entry $win.patch.beat -width 3 -textvariable MetroData(patch,beat)
  pack $win.patch.subtitle $win.patch.meas $win.patch.beat -in $win.patch \
d1485 2
a1486 2
  entry $win.vol.beat -width 3 -textvariable MetroData(vol,beat)
  pack $win.vol.subtitle $win.vol.meas $win.vol.beat -in $win.vol \
d1492 2
a1493 2
  entry $win.dur.beat -width 3 -textvariable MetroData(dur,beat)
  pack $win.dur.subtitle $win.dur.meas $win.dur.beat -in $win.dur \
d1552 6
a1557 1
  showTrackEverything $otrk
d3381 2
d3396 1
a3396 1
      if {$PlayName == ""} {
a3405 1
          set PlayName untitled.mid
a3416 2

  	  scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
d4155 1
a4155 1
set TKS_VERSION 0.6.2
@


1.29
log
@Resizeable main window
@
text
@d4 1
a4 1
# extensions. This is version 0.6.1, Copyright (c) 1995  Greg Wolodkin
d32 1
a32 1
  global MIDIDEV SMPTEDEV DDIVISION DQUANTIZE NUMTRACKS 
d223 1
a223 1
  global DDIVISION NUMTRACKS SHOWPROG SHOWCHAN CHANNEL1 TKS_VERSION
d265 1
a265 1
  set MidiThru 0
d1009 1
a1009 1
  label .stat.vnow -textvariable Now
d4151 1
a4151 1
set TKS_VERSION 0.6.1
@


1.28
log
@fixed a typo in Track->Copy
@
text
@d4 1
a4 1
# extensions. This is version 0.5.0, Copyright (c) 1995  Greg Wolodkin
d32 1
a32 1
  global MIDIDEV SMPTEDEV DDIVISION DQUANTIZE NUMTRACKS VISTRACKS
d45 1
a45 2
  # allow 100 tracks, show 16 at a time. 
  # actually 32 is plenty for me.  Add more if you need 'em.
a46 1
  set VISTRACKS 16
d103 1
a103 1
    dialog .foo . "No options file found...  Creating default version" \
d149 1
a149 1
   "*text.font: -*-Lucida-Bold-R-Normal-*-*-120-*-*-*-*-*-*"
d151 4
a154 3
  puts $foo "*Text.spacing1:	0"
  puts $foo "*Text.spacing2:	0"
  puts $foo "*Text.spacing3:	3"
d156 4
a159 4
  puts $foo "*foreground:       	#a08880"
  puts $foo "*activeForeground: 	#a08880"
  puts $foo "*selectForeground:		#a08880"
  puts $foo "*disabledForeground:	#605050"
d161 3
a163 8
  puts $foo "*background:		#483838"
  puts $foo "*activeBackground:		#584848"
  puts $foo "*selectBackground:		#405068"
  puts $foo "*selectColor:		#4090a0"
  puts $foo ""
  puts $foo "*highlightBackground:	#584848"
  puts $foo "*highlightColor:		#584848"
  puts $foo "*highlightThickness:	0"
d166 1
a166 1
  puts $foo "*subtitle.foreground:	#806860"
d169 5
a173 5
  puts $foo "*event.foreground:		#808080"
  puts $foo "*grid.foreground:		#606060"
  puts $foo "*measure.foreground:	#4090a0"
  puts $foo "*beat.foreground:		#50a080"
  puts $foo "*quantum.foreground:	#907060"
d175 4
a178 4
  puts $foo "*scroll.background:	#504040"
  puts $foo "*scroll.activeBackground:	#584848"
  puts $foo "*scroll.troughColor:	#403030"
  puts $foo "*scroll.width:		10"
d180 3
a182 3
  puts $foo "*scale.background:		#483838"
  puts $foo "*scale.activeBackground:	#584848"
  puts $foo "*scale.troughColor:	#403030"
d185 6
a190 6
  puts $foo "*play.foreground:		#305070"
  puts $foo "*record.foreground:	#305070"
  puts $foo "*pause.foreground:		#305070"
  puts $foo "*ffwd.foreground:		#305070"
  puts $foo "*rewind.foreground:	#305070"
  puts $foo "*stop.foreground:		#305070"
d193 6
a198 6
  puts $foo "*play.activeForeground:		#60c040"
  puts $foo "*record.activeForeground:		#c04060"
  puts $foo "*pause.activeForeground:		#4060c0"
  puts $foo "*stop.activeForeground:		#4060c0"
  puts $foo "*ffwd.activeForeground:		#4060c0"
  puts $foo "*rewind.activeForeground:		#4060c0"
d223 1
a223 1
  global DDIVISION NUMTRACKS SHOWPROG SHOWCHAN CHANNEL1
d254 1
a254 1
  wm title . "tkseq:  $PlayName"
d296 2
a297 2
  set MetroData(dur,meas)  [expr $DDIVISION / 4]
  set MetroData(dur,beat)  [expr $DDIVISION / 4]
d299 2
a300 1
  if {[winfo exists .trkname.list]} { showTrackEverything }
d306 10
a315 1
proc showTrackEverything {} {
d317 1
d321 1
a322 5
  showTrackNames    {}
  showTrackPrograms {}
  showTrackChannels {}
  showTrackMeasures {}
  showTrackMuteList
d363 1
a363 3
  if {$PlayFile != ""} {
    scan [getConfig $PlayFile] "%d %d %d" mdiv mfmt mtrk
  }
a367 1

a368 1

d373 7
a379 6
    if {$i == 0 && $mfmt == 1} {
      set tmpname "<meta>"
    } else {
      set tmpname "<untitled>"
    }
    midirewind $PlayFile $i
d381 7
a387 6
    # don't check every event -- it's too time consuming. 
    set eventlist [midiget $PlayFile $i 0]
    foreach event $eventlist {
      if {[lindex $event 1] == "MetaSequenceName"} {
         set tmpname [lindex $event 2]
         break
d389 2
d404 1
a411 1

a412 2

    scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
d417 18
a434 14
    set tmpprog " --"
    set tmpcount 0
    midirewind $mf $i

    # don't check every event -- it's too time consuming. 
    # for now check no more than say the first 32 events..
    while {[set event [midiget $mf $i next]] != "EOT"} {
      if {[lindex $event 1] == "Program"} {
        set tt [lindex $event 3]
        if {$tmpprog == " --"} {
          if {$tt < 10} {
            set tmpprog "  $tt"
          } elseif {$tt < 100} {
            set tmpprog " $tt"
d436 4
a439 1
            set tmpprog $tt
a440 5
        } else {
          if {$tmpprog != $tt} {
            set tmpprog " **"
            break
          } 
d442 2
d445 2
a446 2
      # FIX ME
      if {[incr tmpcount] > 32} { break }
d460 1
a467 1

a468 2

    scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
d473 17
a489 13
    set tmpchan "--"
    set tmpcount 0
    midirewind $mf $i

    # don't check every event -- it's too time consuming. 
    # for now check no more than say the first 32 notes..
    while {[set event [midiget $mf $i next]] != "EOT"} {
      set etype [lindex $event 1]
      if {[string compare [string range $etype 0 3] "Note"] == 0} {
        set tt [expr [lindex $event 2] + $CHANNEL1]
        if {$tmpchan == "--"} {
          if {$tt < 10} {
            set tmpchan " $tt"
d491 4
a494 1
            set tmpchan $tt
d496 2
a497 5
        } else {
          if {$tmpchan != $tt} {
            set tmpchan "**"
            break
          } 
a498 2
        # FIX ME
        if {[incr tmpcount] > 32} { break }
d500 2
d520 1
a520 4

  if {$mf != ""} {
    scan [getConfig $mf] "%d %d %d" mdiv mfmt mtrk
  }
a524 1

a525 1

d530 18
a547 4
    set mcount 0
    set tmplist ""
    set mtick [miditrack $mf $i end]
    scan [tick2measure $mtick] "%d:" mmeas; incr mmeas
d549 3
a551 9
    midirewind $mf $i
    for {set k 0} {$k < $mmeas} {incr k} {
      set tick  [measure2tick "$k:0:0"]
      set event [midiget $mf $i $tick]

      if {$event == ""} {
        if {$gotnull == 0} {
          dialog .foo . "midiget returned null: $i $tick" info 0 OK
          set gotnull 1
d553 5
a557 1
        set event [midiget $mf $i next]
d559 2
a560 10

      if {$event == "EOT"} {
        set k $mmeas
        break
      }
      set tick [lindex [lindex $event 0] 0]
      scan [tick2measure $tick] "%d" tmeas
      for {set j $k} {$j < $tmeas} {incr j} { set tmplist "$tmplist." }
      set tmplist "$tmplist\o"
      set k $tmeas
d585 7
a591 2
    .trkmute.list delete $i
    .trkmute.list insert $i "<mute>"
d594 6
a599 2
    .trkmute.list delete $SoloList
    .trkmute.list insert $SoloList "<solo>"
d711 1
a711 1
  global DDIVISION DQUANTIZE HAVE_TEX BITMAPS NUMTRACKS VISTRACKS
d717 1
a718 2
  frame .trks 
  pack .mbar .trks -side top -fill x
a913 4
  pack .mbar.file .mbar.realtime .mbar.settings .mbar.view .mbar.track \
  	-side left
  pack .mbar.help -side right

a917 2
  pack .stat -side bottom -fill x
  pack .control -side bottom -expand 1 -fill x
a931 5
  pack .control.rewind .control.stop .control.ffwd \
       .control.play .control.pause .control.record \
       -in .control.buttons -side left -padx 1m
  pack .control.buttons -in .control -expand 1

d944 1
a944 1
    -width 5 -height $VISTRACKS -relief flat
d950 1
a950 1
    -width 18 -height $VISTRACKS -relief sunken \
d956 1
a956 1
    -width 6 -height $VISTRACKS -relief flat
d961 1
a961 1
    -width 3 -height $VISTRACKS -relief flat
d966 1
a966 10
    -width 2 -height $VISTRACKS -relief flat

  # make sure this text box has the same font as our listboxes..
#  set listfont [lindex [.trkname.list configure -font] 4]
#
#  text .trkmeas.text \
#    -yscrollcommand "trackScan {.trkmeas.text}" \
#    -xscrollcommand ".trkmeas.scroll set" \
#    -width 60 -height 0 -relief sunken -pady 0 \
#    -font $listfont -wrap none
d971 4
a974 2
    -xscrollcommand ".trkmeas.scroll set" \
    -width 60 -height $VISTRACKS -relief sunken 
d992 47
a1038 3
  
  pack .trknumb.subtitle -in .trknumb -side top -expand 1 -fill x
  pack .trknumb.list -in .trknumb -side top
d1040 2
a1041 2
  pack .trkname.subtitle -in .trkname -side top -expand 1 -fill x
  pack .trkname.list -in .trkname -side top
d1043 2
a1044 2
  pack .trkmute.subtitle -in .trkmute -side top -expand 1 -fill x
  pack .trkmute.list -in .trkmute -side top 
d1046 2
a1047 2
  pack .trkprog.subtitle -in .trkprog -side top -expand 1 -fill x
  pack .trkprog.list -in .trkprog -side top
d1049 2
a1050 2
  pack .trkchan.subtitle -in .trkchan -side top -expand 1 -fill x
  pack .trkchan.list -in .trkchan -side top
d1052 2
a1053 2
  pack .trkmeas.subtitle -in .trkmeas -side top -expand 1 -fill x
  pack .trkmeas.list -in .trkmeas -side top -fill y
d1055 5
a1059 2
  pack .trknumb .trkname .trkmute .trkprog .trkchan -in .trks -side left
  pack .trkmeas -in .trks -side left -fill y
a1060 1
  pack .trkscr.scroll -in .trkscr -side right -fill y
d1063 2
a1064 24
  frame .stat.left
  frame .stat.center
  frame .stat.right

  label .stat.lfile -text "File:" -width 5 -foreground $subt
  label .stat.file -textvariable PlayName -width 14
  label .stat.ltrk -text "  Tracks:" -foreground $subt
  label .stat.trk -textvariable Mtracks -width 3
  label .stat.lfmt -text "  Format:" -foreground $subt
  label .stat.fmt -textvariable Mformat -width 2
  label .stat.ldiv -text "  Division:" -foreground $subt
  label .stat.div -textvariable Mdivision -width 4
  label .stat.lnow -textvariable LabelNow -width 7 -foreground $subt
  label .stat.now -textvariable Now -width 10
  label .stat.dummy -text "   "

  pack .stat.lfile .stat.file -in .stat.left -side left \
    -fill x -anchor w
  pack .stat.ltrk .stat.trk .stat.lfmt .stat.fmt .stat.ldiv .stat.div \
       .stat.dummy -in .stat.center -side left -expand 1
  pack .stat.now .stat.lnow -in .stat.right -side right \
    -fill x -anchor e

  pack .stat.left .stat.center .stat.right -in .stat -side left -expand 1
a1076 2
  showTrackEverything

d1290 1
d1310 1
a1310 1
  wm title . "tkseq:  $PlayName"
d1314 2
a1315 1
  showTrackEverything
d1552 1
a1552 1
  showTrackEverything
d1618 1
d1626 1
d1632 1
a1632 1
  showTrackEverything
d1648 1
a1704 1
      update idletasks
d1796 1
a1796 1
  showTrackEverything
d1966 1
a1966 1
    dialog .lost . "$lost events were lost as duplicates." info 0 OK
d2149 1
d2526 1
a2526 1
    showTrackEverything
d2530 1
d3098 1
d3111 2
a3112 2
  # FIX ME
  showTrackEverything
d3116 10
a3125 4
  set config [midiconfig $mf division format tracks]
  set mdiv [lindex [lindex $config 0] 1]
  set mfmt [lindex [lindex $config 1] 1]
  set mtrk [lindex [lindex $config 2] 1]
d3155 1
a3155 1
    showTrackEverything
d3195 1
a3195 1
      showTrackEverything
d3248 1
a3248 1
      showTrackEverything
d3400 1
a3400 1
          showTrackEverything
d3417 1
a3417 1
	  if {$mfmt != 0} {
d3424 1
d3429 1
d3442 2
a3443 2
          showTrackEverything
          writeTrackNames {}
d4066 2
a4067 2
  text $w.text -setgrid 1 -width $width -height $height -wrap word \
    -yscrollcommand "$w.scroll set"
d4069 1
a4069 1
  pack $w.text -side left 
d4151 1
@


1.27
log
@Added <solo> and straightened up a little more with .stat
@
text
@d1608 1
a1608 1
              [expr "[miditrack $PlayFIle $k end] + 1"]
@


1.26
log
@New defaults are now in place
Status and control panels are somewhat resistant to font selection
@
text
@d231 1
a231 1
  global Now MuteList Modified MeasView MidiThru
d266 1
d565 1
a565 1
  global MuteList
a568 1
  for {set i 0} {$i < $NUMTRACKS} {incr i} { .trkmute.list insert end "" }
d570 10
d584 4
d845 2
d889 2
a890 1
       several) and then try some of the options in the Track menu.\n\n\
d955 1
a955 1
    -width 7 -height $VISTRACKS -relief flat
d1027 2
a1028 2
  label .stat.lfile -text "File:" -foreground $subt
  label .stat.file -textvariable PlayName
d1030 7
a1036 7
  label .stat.trk -textvariable Mtracks
  label .stat.lfmt -text "Format:" -foreground $subt
  label .stat.fmt -textvariable Mformat
  label .stat.ldiv -text "Division:" -foreground $subt
  label .stat.div -textvariable Mdivision
  label .stat.lnow -textvariable LabelNow -foreground $subt
  label .stat.now -textvariable Now
d1039 2
a1040 1
  pack .stat.lfile .stat.file -in .stat.left -side left -padx 1m 
d1043 2
a1044 1
  pack .stat.now .stat.lnow -in .stat.right -side right -padx 1m
d1046 1
a1046 2
  pack .stat.left .stat.center .stat.right -in .stat -side left \
    -expand 1 -fill x -padx 4m
d1052 2
d1075 1
a1075 1
  foreach i {numb mute} { disableSelect .trk$i.list }
d1078 1
a1078 1
  foreach i {chan prog} {
d1815 1
a1815 1
  global MuteList
d1817 4
a1820 3
  for {set i 0} {$i < [llength $tlist]} {incr i} {
    # either add it if it's not there, or remove it if it is.
    set ltrk [lindex $tlist $i]
d1823 6
a1828 1
      set MuteList [lsort -integer "$MuteList $ltrk"]
d1836 59
d3278 1
a3278 1
  global Modified MuteList Mdivision
d3291 7
a3297 1
    if {$MuteList != ""} {
d3317 1
a3317 1
  global MidiState PlayFile LabelNow Modified TmpFile MuteList
d3320 7
a3326 1
    if {$MuteList != ""} {
d3707 16
@


1.25
log
@option add version, before .tkseqopt
@
text
@d99 1
a99 1
  # Some colors I haven't figured out how to handle via .Xdefaults..
d103 7
d112 5
a116 71
# These are basically .Xresources or .Xdefaults..
proc userOptions {} {

  option add *font "-*-times-bold-r-*-*-14-*-*-*-*-*-*-*"

  option add *Listbox.font \
    "-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
  option add *Text.font \
    "-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"
  option add *Entry.font \
    "-dec-terminal-bold-r-normal--14-*-*-*-*-*-iso8859-1"

  option add *subtitle.font \
    "-b&h-lucida-bold-i-normal-sans-12-*-*-*-*-*-iso8859-1"
  option add *message.font \
    "-*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  option add *text.font \
    "-*-Lucida-Bold-R-Normal-*-*-120-*-*-*-*-*-*"

  option add *Text.spacing1 0
  option add *Text.spacing2 0
  option add *Text.spacing3 3

  option add *foreground		#a08880
  option add *activeForeground		#a08880
  option add *selectForeground		#a08880
  option add *disabledForeground	#605050

  option add *background		#483838
  option add *activeBackground		#584848
  option add *selectBackground		#405068
  option add *selectColor 		#506088

  option add *highlightBackground	#584848
  option add *highlightColor		#584848
  option add *highlightThickness	0

  # Used for subtitles/suppressed text
  option add *subtitle.foreground	#806860

  # Used for piano roll grid and such
  option add *grid.foreground		#606060
  option add *event.foreground		#808080
  option add *measure.foreground	#4080a0
  option add *beat.foreground		#509070
  option add *quantum.foreground	#807060

  option add *scroll.background		#504040
  option add *scroll.activeBackground	#584848
  option add *scroll.troughColor	#403030
  option add *scroll.width		10

  option add *scale.background		#483838
  option add *scale.activeBackground	#584848
  option add *scale.troughColor		#403030

  # Normal buttons are blue..
  option add *play.foreground		#305070
  option add *record.foreground		#305070
  option add *pause.foreground		#305070
  option add *ffwd.foreground		#305070
  option add *rewind.foreground		#305070
  option add *stop.foreground		#305070

  # Active ones get brighter -- green for play, red for record
  option add *play.activeForeground 	#60c040
  option add *record.activeForeground 	#c04060
  option add *pause.activeForeground 	#4060c0
  option add *stop.activeForeground 	#4060c0
  option add *ffwd.activeForeground 	#4060c0
  option add *rewind.activeForeground 	#4060c0
d118 88
a207 2
# -----------END of user configuration --------------------------------
#
d892 3
a894 1
  pack .stat .control -side bottom -fill x
d896 1
a896 2
  label .control.ldummy -text "" -width 25 -relief flat
  button .control.rewind -command seqRewind \
d898 1
a898 1
  button .control.stop -command seqStop \
d900 1
a900 1
  button .control.ffwd  -command seqFFwd \
d902 1
a902 1
  button .control.play  -command seqPlay \
d904 1
a904 1
  button .control.record -command seqRecord \
d906 1
a906 1
  button .control.pause -command seqPause \
a907 1
  label .control.rdummy -text "" -width 25
d909 4
a912 3
  pack .control.ldummy .control.rewind .control.stop .control.ffwd \
       .control.play .control.pause .control.record .control.rdummy \
       -side left -expand 1 -fill x -padx 2m 
d1006 23
a1028 17
  label .stat.lfile -width 6 -text " File:" -foreground $subt
  label .stat.file -width 15 -textvariable PlayName
  label .stat.ltrk -width 8 -text "  Tracks:" -foreground $subt
  label .stat.trk -width 2 -textvariable Mtracks
  label .stat.lfmt -width 8 -text " Format:" -foreground $subt
  label .stat.fmt -width 1 -textvariable Mformat
  label .stat.ldiv -width 10 -text " Division:" -foreground $subt
  label .stat.div -width 3 -textvariable Mdivision
  label .stat.dummy1 -width 2 -text ""
  label .stat.lnow -width 6 -textvariable LabelNow -foreground $subt
  label .stat.now -width 9 -textvariable Now
  label .stat.dummy2 -width 1 -text ""
  pack .stat.lfile .stat.file .stat.ltrk .stat.trk \
       .stat.lfmt .stat.fmt .stat.ldiv .stat.div \
       .stat.dummy1 -in .stat -side left -padx 1m -pady 1m
  pack .stat.dummy2 .stat.now .stat.lnow -in .stat \
       -side right -padx 1m -pady 1m
d1290 1
a1290 1
      {There may be unsaved work. Quit anyway?} \
d3628 1
a3628 1
  message $w.top.message -width 3i -text $text 
d3990 2
a3991 1
      set msg "Cannot open MIDI device $mididev..  Realtime controls may not work."
a4009 3

# this should happen before we put up any windows..
userOptions
@


1.24
log
@Lots of changes eliminating $mf and using the global PlayFile instead.
MetaEndOfTrack handling cleanup
Support for changing DIVISION in a MIDI file added
@
text
@d34 1
a34 1
  global SHOWPROG SHOWCHAN SHOWBEAT SHOWMEAS SHOWMIDC
d94 2
a95 1
  set SHOWMIDC 0
d97 1
d100 2
a101 2
  set WHITEKEY grey
  set BLACKKEY black
d105 74
d230 1
d232 3
d243 1
a243 1
  set MidiThru 1
d761 1
a761 1
  	-variable DQUANTIZE -value 1
d763 1
a763 1
  	-variable DQUANTIZE -value 2
d765 1
a765 1
  	-variable DQUANTIZE -value 4
d767 1
a767 1
  	-variable DQUANTIZE -value 8
d769 1
a769 1
  	-variable DQUANTIZE -value 12
d771 1
a771 1
  	-variable DQUANTIZE -value 16
d773 1
a773 1
  	-variable DQUANTIZE -value 24
d775 1
a775 1
  	-variable DQUANTIZE -value 32
d777 1
a777 1
  	-variable DQUANTIZE -value 48
d779 1
a779 1
  	-variable DQUANTIZE -value 64
d781 1
a781 1
  	-variable DQUANTIZE -value 96
d791 1
a791 1
    -command {watchCursor .; showTrackChannels $PlayFile {}; normalCursor .}
d794 1
a794 1
    -command {watchCursor .; showTrackPrograms $PlayFile {}; normalCursor .}
d797 1
a797 1
    -command {watchCursor .; showTrackMeasures $PlayFile {}; normalCursor .}
d1231 4
a1234 1
  set ff [open $PlayName]
d1238 3
d1628 5
a1633 5
    wm title .ti$k "Track $k: $selname"
    wm iconname .ti$k "Track $k"
    update idletasks
    fillTrackInfo $k 0
    normalCursor .ti$k
a1649 1
#  dialog .st . "Stop time is $eottime" info 0 OK
a1663 1
  set eottime 0
d1689 3
a1691 2
    mididelete $PlayFile $dtrk range 0 [expr $eottime+1]
    midicopy "$PlayFile $dtrk" 0 "$tmpf 0" 0 [expr $eottime + 1]
d1780 1
d1784 2
a1785 1
  set tlist [fixTrackList $PlayFile $tlist]
d1808 1
a1808 1
    midiconfig $tmpf "division $mdiv" "tracks 1" "format $mfmt"
d1812 6
a1817 4
      if {$etype == "Meta" || $etype == "Syst"} {
        midiput $tmpf 0 $event
      } else {
        midiput $tmpf 0 [lreplace $event 2 2 $nchan]
d1820 3
a1822 3
    mididelete $PlayFile $i range 0 [expr "[miditrack $PlayFile $i end] + 1"]
    midicopy "$PlayFile $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
    mididelete $tmpf 0 range 0 [expr "[miditrack $tmpf 0 end] + 1"]
d1826 2
d2436 4
a2439 2
  global TEAROFF PIANOSCALE WHITEKEY BLACKKEY ACTIVKEY SHOWMIDC SHOWBEAT
  global TimeScale KeyYval KeyYtag ShowBeat ShowMidC PlayFile
d2447 4
a2450 2

    if {[winfo exists $w]} { destroy $w }
d2464 1
d2472 15
d2488 8
a2495 1
    label $w.event; label $w.grid
a2498 5
    frame $w.r -relief raised -bd 2
    frame $w.l -relief raised -bd 2
    frame $w.c -relief sunken -bd 2

    frame $w.mbar -relief raised -bd 1
d2512 12
a2523 4
    $w.mbar.view.menu add checkbutton -label "Beats" \
      -variable ShowBeat($trk) -command "showBeats $trk"
    $w.mbar.view.menu add checkbutton -label "Middle C" \
      -variable ShowMidC($trk) -command "showMiddleC $trk"
d2526 1
a2526 1
    $w.mbar.zoom.menu add command -label "In    " \
d2528 1
a2528 1
    $w.mbar.zoom.menu add command -label "Out   " \
a2532 10
    canvas $w.keys -width $x1 -height $ynote \
      -scrollregion "0 0 $x1 $ynote" \
      -yscrollcommand "$w.r.scroll set"
    canvas $w.note -width $xnote -height $ynote \
      -scrollregion "0 0 $xnote $ynote" \
      -xscrollcommand "$w.c.scroll set" \
      -yscrollcommand "$w.r.scroll set"
    scrollbar $w.c.scroll -command "$w.note xview" -orient horizontal
    scrollbar $w.r.scroll -command "pianoRollScroll $w"

d2571 1
a2571 1
          -fill $gridcolor -tags zoom
d2574 1
d2576 4
a2579 15
    set measrule [lindex [.trkname.subtitle configure -foreground] 4]

    for {set i 0} {$i <= $lastmeas} {incr i} {
      set num [lindex [getMeter $i:0:0] 2]
      for {set j 1} {$j < $num} {incr j} {
        set xval [expr [measure2tick $i:$j:0] * $TimeScale / $mdiv]
        $w.note create line $xval 0 $xval $ynote -fill $gridcolor \
          -tags "beat zoom"
      }
      set xval [expr [measure2tick $i:0:0] * $TimeScale / $mdiv]
      $w.note create line $xval 0 $xval $ynote -fill $measrule \
        -tags "measure zoom"
    }

    $w.keys raise black white
d2584 1
a2584 2
    set ShowBeat($trk) $SHOWBEAT; showBeats $trk
    set ShowMidC($trk) $SHOWMIDC; showMiddleC $trk
d2590 57
d2667 15
d2686 1
a2686 1
    set gridcolor  [lindex [.pr$trk.grid configure -foreground] 4]
d2688 1
a2688 1
    .pr$trk.note raise beat
d2692 13
a2704 1
    .pr$trk.note lower beat
d2706 11
a2716 1
  .pr$trk.note raise event
d2734 4
d2782 2
d2924 14
d3373 1
a3373 1
  set eottime [miditrack $mf $trk end]
d3976 3
d3986 5
@


1.23
log
@Whew.. this is version 0.5.  Changes in watchCursor, as well as
some other little things which I forget.
@
text
@d127 1
a127 1
  global MidiState SmpteClk LabelNow Now Mtracks Mformat Mdivision
d131 1
d140 1
a140 1
    closeTrackInfo $PlayFile {}
a154 3
  set Mtracks 1
  set Mformat 1
  set Mdivision $DDIVISION
d164 3
d194 2
a195 2
  set MetroData(dur,meas)  [expr $Mdivision / 4]
  set MetroData(dur,beat)  [expr $Mdivision / 4]
d197 1
a197 1
  if {[winfo exists .trkname.list]} "showTrackEverything $PlayFile"
d203 10
a212 5
proc showTrackEverything {mf} {
  showTrackNames    $mf {}
  showTrackPrograms $mf {}
  showTrackChannels $mf {}
  showTrackMeasures $mf {}
d220 2
a221 2
proc writeTrackNames {mf tlist} {
  global Modified
d224 1
a224 1
    set mtrk [lindex [lindex [midiconfig $mf tracks] 0] 1]
d231 2
a232 2
    midirewind $mf $i
    set eventlist [midiget $mf $i 0]
d235 1
a235 1
         mididelete $mf $i $event
d241 1
a241 1
       midiput $mf $i [list 0 MetaSequenceName "$tmpname"]
d244 1
a244 1
    midirewind $mf
d250 1
a250 1
proc showTrackNames {mf tlist} {
d252 5
d262 1
a262 1
    if {$mf == ""} { return }
a263 1
    set mtrk [lindex [lindex [midiconfig $mf tracks] 0] 1]
a266 2
  set mfmt [lindex [lindex [midiconfig $mf format] 0] 1]

d273 1
a273 1
    midirewind $mf $i
d276 1
a276 1
    set eventlist [midiget $mf $i 0]
d290 1
a290 1
proc showTrackPrograms {mf tlist} {
d292 3
d305 1
a305 1
    set mtrk [lindex [lindex [midiconfig $mf tracks] 0] 1]
d344 1
a344 1
proc showTrackChannels {mf tlist} {
d346 3
d359 1
a359 1
    set mtrk [lindex [lindex [midiconfig $mf tracks] 0] 1]
d397 1
a397 1
proc showTrackMeasures {mf tlist} {
d399 3
d409 1
a409 3
    set config [midiconfig $mf tracks division]
    set mtrk [lindex [lindex $config 0] 1]
    set mdiv [lindex [lindex $config 1] 1]
a578 1
  global Mformat Mdivision PlayFile PlayName
d580 1
d644 1
a644 1
    -command {editMap $PlayFile Tempo}
d646 1
a646 1
    -command {editMap $PlayFile Key}
d648 1
a648 1
    -command {editMap $PlayFile Meter}
d674 4
a677 22
  .mbar.settings.menu.div add radiobutton -label 48 \
  	-variable DDIVISION -value 48 
  .mbar.settings.menu.div add radiobutton -label 72 \
  	-variable DDIVISION -value 72
  .mbar.settings.menu.div add radiobutton -label 96 \
  	-variable DDIVISION -value 96 
  .mbar.settings.menu.div add radiobutton -label 120 \
  	-variable DDIVISION -value 120 
  .mbar.settings.menu.div add radiobutton -label 144 \
  	-variable DDIVISION -value 144 
  .mbar.settings.menu.div add radiobutton -label 168 \
  	-variable DDIVISION -value 168 
  .mbar.settings.menu.div add radiobutton -label 192 \
  	-variable DDIVISION -value 192 
  .mbar.settings.menu.div add radiobutton -label 216 \
  	-variable DDIVISION -value 216 
  .mbar.settings.menu.div add radiobutton -label 240 \
  	-variable DDIVISION -value 240 
  .mbar.settings.menu.div add radiobutton -label 360 \
  	-variable DDIVISION -value 360 
  .mbar.settings.menu.div add radiobutton -label 480 \
  	-variable DDIVISION -value 480 
d721 1
a721 1
    -command {track Info $PlayFile}
d723 1
a723 1
    -command {track Mute $PlayFile}
d725 1
a725 1
    -command {track Name $PlayFile}
d727 1
a727 1
    -command {track PianoRoll $PlayFile}
d730 1
a730 1
      -command {track Score $PlayFile}
d734 1
a734 1
    -command {track ForceChannel $PlayFile}
d736 1
a736 1
    -command {track ProgramChange $PlayFile}
d738 1
a738 1
    -command {track ParameterSet $PlayFile}
d741 1
a741 1
    -command {track Copy $PlayFile}
d743 1
a743 1
    -command {track Merge $PlayFile}
d745 1
a745 1
    -command {track Remove $PlayFile}
d748 1
a748 1
    -command {track Erase $PlayFile}
d750 1
a750 1
    -command {track Offset $PlayFile}
d752 1
a752 1
    -command {track Quantize $PlayFile}
d754 1
a754 1
    -command {track Randomize $PlayFile}
d756 1
a756 1
    -command {track Transpose $PlayFile}
d921 1
a921 1
     { trackForceChannel $PlayFile [.trkchan.list curselection] }
d923 1
a923 1
     { windowMappedEvent $PlayFile [.trkprog.list curselection] Patch }
d925 1
a925 1
  showTrackEverything $PlayFile
d1140 1
a1140 1
  global PlayFile MidiState PlayName Modified Mtracks Mdivision Mformat
a1154 5
  set config [midiconfig $PlayFile tracks division format]
  set Mtracks [lindex [lindex $config 0] 1]
  set Mdivision [lindex [lindex $config 1] 1]
  set Mformat [lindex [lindex $config 2] 1]

d1157 1
a1157 1
  showTrackEverything $PlayFile
d1198 1
a1198 1
    writeTrackNames $PlayFile {}
d1220 1
a1220 1
  writeTrackNames $PlayFile {}
d1255 3
a1257 3
proc editMap {mf type} {
  if {$mf == ""} { return }
  windowMappedEvent $mf 0 $type
d1261 1
a1261 1
  global MeterMap MetroData PlayFile StopTime Mdivision
d1264 2
d1270 1
d1272 2
a1273 2
  set MetroData(dur,meas)  [expr $Mdivision / 4]
  set MetroData(dur,beat)  [expr $Mdivision / 4]
d1368 2
a1369 2
  set config [midiconfig $PlayFile tracks division format]
  set otrk [lindex [lindex $config 0] 1]
d1393 2
a1394 1
  showTrackEverything $PlayFile
d1427 2
a1428 7
proc track {Command mf} {
  global Modified 

  if {$mf == ""} {
    dialog .r . {All tracks are empty.} error 0 OK
    return
  }
d1448 1
a1448 1
    track$Command $mf $seltrklst
d1456 2
a1457 2
proc trackCopy {mf tlist} {
  global Modified
d1459 2
a1460 2
  set config [midiconfig $mf tracks division format]
  set otrk [lindex [lindex $config 0] 1]
d1462 1
a1462 1
  midiconfig $mf "tracks $ntrk"
d1467 2
a1468 2
    midicopy "$mf $i" 0 "$mf $k" 0 \
              [expr "[miditrack $mf $k end] + 1"]
d1472 1
a1472 1
  showTrackEverything $mf
d1479 2
a1480 2
proc trackErase {mf tlist} {
  global Modified 
d1484 3
a1486 3
    mididelete $mf $k range 0 [miditrack $mf $k end]
    writeTrackNames $mf $k
    fillTrackInfo $mf $k 0
d1495 1
a1495 1
proc trackInfo {mf tlist} {
d1503 1
a1503 1
      fillTrackInfo $mf $k 1
d1524 1
a1524 1
        -command "remapTrackInfo $mf $k $k"
d1529 1
a1529 1
        -command "deleteEvents $mf $k"
d1531 1
a1531 1
        -command "modifyEvents $mf $k"
d1533 1
a1533 1
        -command "copyEvents $mf $k"
d1536 1
a1536 1
        -variable MeasView -value 1 -command "remapTrackInfo $mf $k $k"
d1538 1
a1538 1
        -variable MeasView -value 0 -command "remapTrackInfo $mf $k $k"
d1546 1
a1546 1
    fillTrackInfo $mf $k 0
d1558 1
a1558 1
  set mtrk [lindex [lindex [midiconfig $mf tracks] 0] 1]
d1571 2
a1572 2
proc trackMerge {mf tlist} {
  global Modified Mtracks
d1581 3
a1585 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 0]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d1594 1
a1594 1
    set mlist "$mlist {$mf $trk}"
a1595 8
    # remove EOT but keep track of the biggest one
    midirewind $mf $trk
    set lastevent($i) [midiget $mf $trk prev]
    if {[lindex $lastevent($i) 1] == "MetaEndOfTrack"} {
	mididelete $mf $trk $lastevent($i)
        set ticks [lindex $lastevent($i) 0]
        if {$ticks > $eottime} { set eottime $ticks }
    }
a1596 2
  # put a MetaEndOfTrack on the destination track
  midiput $tmpf 0 "$eottime MetaEndOfTrack"
d1598 2
a1599 9
    # Oops.  Merge failed.  Try to patch things back up
    dialog .d . \
      "Merge failed! Mail greg@@eecs.berkeley.edu" error 0 OK
    for {set i 0} {$i < $ntrk} {incr i} {
      set trk [lindex $tlist $i]
      if {[lindex $lastevent($i) 1] == "MetaEndOfTrack"} {
        midiput $mf $trk $lastevent($i)
      }
    }
d1605 3
a1607 2
    mididelete $mf $dtrk range 0 [expr $eottime+1]
    midicopy "$mf $dtrk" 0 "$tmpf 0" 0 [expr $eottime+1]
d1612 1
a1612 1
    writeTrackNames $mf $dtrk
d1614 1
a1614 1
    midiremove $mf $strk 1
d1622 1
a1622 1
      remapTrackInfo $mf $i $dtrk
d1628 1
a1628 1
  for {set i $h; set k $h} {$i < $Mtracks} {incr i} {
d1630 1
a1630 1
      remapTrackInfo $mf $i $k
d1633 1
a1633 1
      closeTrackInfo $mf $i
d1637 1
a1637 5
  # update the number of tracks, as they probably changed
  set config [midiconfig $mf tracks division format]
  set Mtracks [lindex [lindex $config 0] 1]

  showTrackEverything $mf
d1643 1
a1643 2
proc remapTrackInfo {mf old new} {

d1656 1
a1656 1
    fillTrackInfo $mf $old 1
d1664 1
a1664 1
    trackInfo $mf $new
d1675 2
a1676 2
proc trackMute {mf tlist} {
  global MuteList 
d1693 1
a1693 1
proc trackForceChannel {mf tlist} {
d1696 1
a1696 3
  if {$mf == ""} { return } 

  set tlist [fixTrackList $mf $tlist]
d1698 2
a1699 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d1722 2
a1723 2
    midirewind $mf $i
    while {[set event [midiget $mf $i next]] != "EOT"} {
d1731 2
a1732 2
    mididelete $mf $i range 0 [expr "[miditrack $mf $i end] + 1"]
    midicopy "$mf $i" 0 "$tmpf 0" 0 [expr "[miditrack $tmpf 0 end] + 1"]
d1736 1
a1736 1
    remapTrackInfo $mf $i $i
d1739 1
a1739 1
  if {$SHOWCHAN} { showTrackChannels $mf $tlist }
d1743 2
a1744 2
proc trackProgramChange {mf tlist} {
  windowMappedEvent $mf $tlist Patch
d1747 2
a1748 2
proc trackParameterSet {mf tlist} {
  windowMappedEvent $mf $tlist Parameter
d1807 1
a1807 1
proc windowMappedEvent {mf tlist ix} {
d1809 1
a1809 1
  global Gmf Gtrk Gdat Evnt
d1811 1
a1811 1
  if {$mf == ""} { 
d1814 1
a1814 1
    set Gmf($ix) $mf
d1818 1
a1818 1
  set tlist [fixTrackList $mf $tlist]
d1916 1
a1916 1
      if {$SHOWPROG} { showTrackPrograms $mf $trk }
d1918 1
a1918 1
    remapTrackInfo $mf $trk $trk
d2126 1
a2126 2
  set eottime [miditrack $mf $trk end]
  midirewind $mf $trk
a2127 9
  watchCursor .
  while {[set event [midiget $mf $trk next]] != "EOT"} {
    if {[lindex $event 1] == "MetaEndOfTrack"} {
      mididelete $mf $trk $event
    }
  }
  midiput $mf $trk "$eottime MetaEndOfTrack"
  normalCursor .
  
d2153 2
a2154 1
proc fixTrackList {mf tlist} {
d2156 1
a2156 2
  set config [midiconfig $mf tracks]
  set mtrk [lindex [lindex $config 0] 1]
d2170 1
a2170 2
proc trackName {mf tlist} {

d2180 2
a2181 2
    writeTrackNames $mf $trk
    remapTrackInfo $mf $trk $trk
d2187 1
a2187 1
proc trackQuantize {mf tlist} {
d2189 1
a2189 1
  global Modified
d2192 1
a2192 1
  set lost [midiquantize $mf $tlist $DQUANTIZE]
d2196 4
a2199 2
    dialog .d . "quantize: $lost events lost as duplicates." \
      info 0 OK 
d2206 1
a2206 1
proc trackRandomize {mf tlist} {
d2208 1
a2208 1
  global Modified
d2210 1
a2210 1
  set lost [midirandomize $mf $tlist $MEAN $VARIANCE]
d2217 3
d2224 2
a2225 2
proc trackOffset {mf tlist} {
  global Modified
d2233 1
a2233 1
  midioffset $mf $tlist [measure2tick $offset]
d2240 3
d2247 2
a2248 2
proc trackTranspose {mf tlist} {
  global Modified 
d2258 1
a2258 1
  miditranspose $mf $tlist $halfsteps
d2265 3
d2274 2
a2275 2
proc trackRemove {mf tlist} {
  global Modified Mtracks
d2277 1
d2279 1
a2279 1
  midiremove $mf $tlist 1
d2282 1
a2282 1
  if {$Mtracks == [llength $tlist]} {
d2287 1
a2287 1
    for {set i $h; set k $h} {$i < $Mtracks} {incr i} {
d2289 1
a2289 1
        remapTrackInfo $mf $i $k
d2292 1
a2292 1
        closeTrackInfo $mf $i
d2295 1
a2295 5
    # update the number of tracks, as they probably changed
    set config [midiconfig $mf tracks division format]
    set Mtracks [lindex [lindex $config 0] 1]

    showTrackEverything $mf
d2307 3
a2309 1
proc trackScore {mf tlist} {
d2315 2
a2316 2
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
d2323 1
a2323 1
  set tmpf [midiremove $mf $klist 0]
d2344 1
a2344 1
proc trackPianoRoll {mf tlist} {
d2346 1
a2346 1
  global TimeScale KeyYval KeyYtag ShowBeat ShowMidC 
d2350 2
a2351 2
  set config [midiconfig $mf division]
  set mdiv [lindex [lindex $config 0] 1]
d2358 1
a2358 1
    set lastnote [miditrack $mf $trk end]
d2481 1
a2481 1
    fillPianoRoll $mf $trk 
d2525 1
a2525 1
proc fillPianoRoll {mf tlist} {
d2527 1
a2527 1
  global TimeScale
d2529 1
a2529 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d2537 1
a2537 1
    midirewind $mf $i
d2547 1
a2547 1
    while {[set event [midiget $mf $i next]] != "EOT"} {
d2589 4
a2592 2
proc closeTrackInfo {mf tlist} {
  global Mtracks    
d2596 1
a2596 1
    for {set i 0} {$i < $Mtracks} {incr i} {
d2607 2
a2608 2
proc fillTrackInfo {mf tlist keep} {
  global MeasView
d2613 1
a2613 1
    midirewind $mf $i
d2619 1
a2619 1
    while {[set event [midiget $mf $i next]] != "EOT"} {
d2636 1
a2636 1
  global Mdivision
d2701 3
a2703 1
  global MeterMap PlayFile Mdivision
d2715 1
a2715 1
      set tpb  [expr $Mdivision * 4 / $den]
d2726 41
d2781 2
a2782 2
proc deleteEvents {mf i} {
  global Modified
d2790 1
a2790 1
      mididelete $mf $i $event
d2793 2
a2794 2
    showTrackEverything $mf
    trackInfo $mf $i
d2802 2
a2803 2
proc modifyEvents {mf i} {
  global Modified MeasView
d2819 2
a2820 2
        mididelete $mf $i $event
        while {[catch {midiput $mf $i "$newevent"} msg]} {
d2823 1
a2823 1
            midiput $mf $i $event
d2833 2
a2834 2
      showTrackEverything $mf
      trackInfo $mf $i
d2843 2
a2844 2
proc copyEvents {mf i} {
  global Modified MeasView
d2862 2
a2863 8
      midirewind $mf $i
      set lastevent [midiget $mf $i prev]
      set ticks [lindex $lastevent 0]
      if {[lindex $lastevent 1] == "MetaEndOfTrack"} {
        mididelete $mf $i $lastevent
        set lastevent [midiget $mf $i prev]
        set ticks [lindex $lastevent 0]
      }
d2877 1
a2877 1
          if {[catch {midiput $mf $i "$event"} msg]} {
d2883 2
d2886 2
a2887 2
      showTrackEverything $mf
      trackInfo $mf $i
d2945 1
a2945 1
  global Modified Mdivision MuteList
d3000 1
a3000 1
  global Mdivision Mtracks Mformat PlayName StopTime
a3019 1
          set Mformat 1; set Mtracks 2
d3022 1
a3022 1
	  midiput $RecFile 0 "[miditrack $RecFile 0 end] MetaEndOfTrack"
d3026 2
a3027 2
          showTrackEverything $PlayFile
          writeTrackNames $PlayFile {}
d3037 4
a3040 2
          set newtrack $Mtracks
          set Mtracks [expr $Mtracks+1]
d3042 2
a3043 3
	  midiconfig $newfile \
	    "format 1" "division $Mdivision" "tracks $Mtracks"
	  if {$Mformat != 0} {
d3054 3
a3056 4
            set Mformat 1
            set newtrack $Mtracks
            set Mtracks [expr $Mtracks+2]
	    midiconfig $newfile "tracks $Mtracks"
d3062 1
a3062 2
	  midiput $newfile $newtrack \
	    "[miditrack $newfile $newtrack end] MetaEndOfTrack"
d3066 2
a3067 2
          showTrackEverything $PlayFile
          writeTrackNames $PlayFile {}
d3149 23
d3175 1
a3175 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
a3185 1
    # first quantize EOT forward so as not to truncate anything.
a3187 6
    set lastevent [midiget $mf $i prev]
    set ticks [lindex $lastevent 0]
    if {[lindex $lastevent 1] == "MetaEndOfTrack"} {
      mididelete $mf $i $lastevent
    }
    set lastick [expr round($ticks/$fqdiv+1.0000)*$iqdiv]
a3188 1
    # now quantize the rest
d3192 1
a3192 1
      set newevent "$newticks [lrange $event 1 end]"
a3195 1
#      dialog .d . "$ticks->$newevent" {} 0 OK
d3200 1
a3200 1
    midiput $mf $i "[miditrack $mf $i end] MetaEndOfTrack"
d3210 1
a3210 5
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]

a3218 5
    set lastevent [midiget $mf $i prev]
    set ticks [lindex $lastevent 0]
    if {[lindex $lastevent 1] == "MetaEndOfTrack"} {
      mididelete $mf $i $lastevent
    }
d3224 1
a3224 1
      set newevent "$newticks [lrange $event 1 end]"
d3232 1
a3232 1
    midiput $mf $i "[miditrack $mf $i end] MetaEndOfTrack"
d3242 1
a3242 5
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]

a3250 5
    set lastevent [midiget $mf $i prev]
    set ticks [lindex $lastevent 0]
    if {[lindex $lastevent 1] == "MetaEndOfTrack"} {
      mididelete $mf $i $lastevent
    }
d3266 1
a3266 1
    midiput $mf $i "[miditrack $mf $i end] MetaEndOfTrack"
d3278 1
a3278 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
a3289 5
    set event [midiget $mf $i prev]
    if {[lindex $event 1] == "MetaEndOfTrack"} {
      mididelete $mf $i $event
    }
    # now randomize the rest
d3302 1
a3302 1
    midiput $mf $i "[miditrack $mf $i end] MetaEndOfTrack"
d3324 1
a3324 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
@


1.22
log
@Fixed measure2tick using MeterMap
Added `remapTrackInfo' in lots of places, to keep it current
@
text
@d34 2
a35 2
  global PIANOSCALE CHANNEL1 SHOWPROG SHOWCHAN SHOWBEAT SHOWMIDC
  global WHITEKEY BLACKKEY ACTIVKEY 
d89 1
d126 1
a126 1
  global DDIVISION NUMTRACKS SHOWPROG SHOWCHAN
d130 1
a130 1
  global KeyYval KeyYtag MeterMap
d185 11
d270 1
a270 2
      set etype [lindex $event 1]
      if {$etype == "MetaSequenceName"} {
d384 4
a387 1
  global NUMTRACKS
d616 2
d713 2
a714 2
  .mbar.view.menu add checkbutton -label "Channels" \
    -variable SHOWCHAN -command {showTrackChannels $PlayFile {}}
d716 5
a720 1
    -variable SHOWPROG -command {showTrackPrograms $PlayFile {}}
d956 7
a962 3
proc watchCursor {} {
  . configure -cursor watch
  update idletasks
d965 4
a968 3
proc normalCursor {} {
  . configure -cursor {}
  update idletasks
d984 1
a984 1
  toplevel .file
d1073 1
d1133 2
d1136 1
d1268 133
d1473 1
a1473 1
  watchCursor
d1479 1
a1479 1
  normalCursor
d1491 1
a1491 1
  watchCursor
d1497 1
a1497 1
  normalCursor
d1514 2
a1515 2
      toplevel .ti$k
      wm minsize .ti$k 1 1
d1554 1
d1556 1
d1601 1
a1601 1
  watchCursor
d1627 1
a1627 1
    normalCursor
d1642 1
a1642 1
    normalCursor
d1686 1
d1688 1
d1754 1
a1754 1
    watchCursor
d1772 1
a1772 1
    normalCursor
d1867 1
a1867 1
    toplevel $win
d1944 1
d2114 3
d2157 1
a2157 2
#  dialog .foo . "midiput $Gmf($type) $Gtrk($type) $event" info 0 OK
  midiput $Gmf($type) $Gtrk($type) $event
d2160 13
d2237 1
a2237 1
  watchCursor
d2240 1
a2240 1
  normalCursor
d2253 1
a2253 1
  watchCursor
d2256 1
a2256 1
  normalCursor
d2273 1
a2273 1
  watchCursor
d2275 1
a2275 1
  normalCursor
d2295 1
a2295 1
  watchCursor
d2297 1
a2297 1
  normalCursor
d2312 1
a2312 1
  watchCursor
d2314 1
a2314 1
  normalCursor
d2364 1
d2366 1
d2384 2
d2405 1
a2405 1
    toplevel $w
d2521 1
d2523 1
d2530 1
d2957 1
a2957 1
      watchCursor
d2959 1
a2959 1
      normalCursor
d2980 1
a2980 1
      watchCursor
d2982 1
a2982 1
      normalCursor
@


1.21
log
@Added zoom to piano roll
Added/cleaned up some Xresources stuff
@
text
@d4 1
a4 1
# extensions. This is version 0.4.9, Copyright (c) 1995  Greg Wolodkin
d34 2
a35 2
  global PIANOSCALE CHANNEL1 SHOWPROG SHOWCHAN
  global WHITEKEY BLACKKEY ACTIVKEY
d92 4
d127 1
a127 1
  global Now MuteList Modified MeterNum MeterDen MeasView MidiThru
d129 1
a129 1
  global KeyYval KeyYtag
d156 1
a162 3

  set MeterNum 4
  set MeterDen 4
a373 1
  global MeterNum
d396 1
a396 1
    set mmeas [expr $mtick / $mdiv / $MeterNum + 1];
d667 4
d1133 2
a1134 1
  getTimeSignature $PlayFile 0
a1141 16
proc getTimeSignature {mf tick} {
  global MeterNum MeterDen

  # default time signature in case we don't find anything
  set MeterNum 4
  set MeterDen 4

  midirewind $mf 0
  while {[set event [midiget $mf 0 next]] != "EOT"} {
    if {[lindex $event 0] > $tick} { break }
    if {[lindex $event 1] == "MetaTime"} {
      scan $event "%d MetaTime %d %d" tt MeterNum MeterDen
    }
  }
}

d1511 3
a1513 10
# sometimes the title will change..
# even if they are the same window
#  if {$new == $old} { return }

  # there is no window to speak of
  if {![winfo exists .ti$old]} {
    closeTrackInfo $mf $new
    return
  }
  
d1517 10
a1526 1
  destroy .ti$old
d1529 5
a1533 4
  trackInfo $mf $new
  wm geometry .ti$new $geom 
  .ti$new.list yview moveto $start
  if {! $mapped} { wm iconify .ti$new }
d1604 2
d1626 1
a1626 1
  set Elist "Patch Parameter Tempo Time"
d1659 1
a1659 1
  set Evnt(Meter,0) Time
d1670 1
a1670 1
  set Evnt(Key,0) Time
a1781 1
  }
d1783 4
a1786 2
  if {$ix == "Patch"} {
    if {$SHOWPROG} { showTrackPrograms $mf $tlist }
d1825 3
a1827 2
    set fill [lindex $KeyYtag($j) 0]
    if {$fill == "white"} {
d1830 1
d1935 3
d1954 8
a1961 1
  set foobar [catch "set rtime [measure2tick $Gdat($type,0)]"]
d1992 2
d2044 2
a2046 1
  writeTrackNames $mf $tlist
d2197 2
a2198 2
  global TEAROFF PIANOSCALE WHITEKEY BLACKKEY ACTIVKEY
  global TimeScale KeyYval KeyYtag ShowBeat ShowMidC MeterNum
d2310 1
a2310 1
          -fill $gridcolor -tag zoom
d2317 2
a2318 1
      for {set j 1} {$j < $MeterNum} {incr j} {
d2333 2
a2334 2
    set ShowBeat($trk) 0; showBeats $trk
    set ShowMidC($trk) 1; showMiddleC $trk
d2369 1
d2394 1
d2425 2
a2426 1
      pianoRollScroll .pr$i moveto [expr $hidden / $ysize]
d2484 1
a2484 1
  global MeterNum MeterDen Mdivision
d2488 5
a2492 5
  set tpbt [expr $Mdivision * 4 / $MeterDen]
  set beat [expr $tick / $tpbt]
  set frac [expr $tick % $tpbt]
  set meas [expr $beat / $MeterNum]
  set note [expr $beat % $MeterNum]
d2494 7
a2500 1
  set foo [format "%d:%2d:%3d" $meas $note $frac]
d2506 1
a2506 1
  global MeterNum MeterDen Mdivision
d2509 1
d2512 9
a2520 2
  set tpbt [expr $Mdivision * 4 / $MeterDen]
  set foo [expr $frac + ($beat + $MeterNum * $meas) * $tpbt]
d2522 48
@


1.20
log
@more small changes -- almost 0.5.0
@
text
@d1841 1
a1841 1
      -outline $BLACKKEY -tags $KeyYtag($j)
d2215 5
d2232 1
a2232 1
    menubutton $w.mbar.view -text "View " -underline 0 \
d2234 2
d2243 7
a2249 1
    pack $w.mbar.file $w.mbar.view -in $w.mbar -side left 
a2279 2
    set rule #707070

d2298 2
a2299 1
        $w.note create line 0 $yrule $xnote $yrule -fill $rule
d2303 2
a2304 1
    set darkrule #505060
d2308 2
a2309 1
        $w.note create line $xval 0 $xval $ynote -fill $darkrule -tag beat
d2312 2
a2313 1
      $w.note create line $xval 0 $xval $ynote -fill $rule -tag measure
d2326 8
a2347 1
  set darkrule #505060
d2349 2
a2350 1
    .pr$trk.note itemconfigure beat -fill $darkrule
d2381 1
d2398 2
a2399 1
        .pr$i.note create rectangle $x0 $y0 $x1 $y1 -fill darkgrey
@


1.19
log
@This is 0.4.9, just prior to 0.5.  Needs a little more cleanup.
@
text
@d760 1
a763 17
  label .stat.lfile -width 6 -text " File:" 
  label .stat.file -width 15 -textvariable PlayName
  label .stat.ltrk -width 8 -text "  Tracks:"
  label .stat.trk -width 2 -textvariable Mtracks
  label .stat.lfmt -width 8 -text " Format:"
  label .stat.fmt -width 1 -textvariable Mformat
  label .stat.ldiv -width 10 -text " Division:"
  label .stat.div -width 3 -textvariable Mdivision
  label .stat.dummy1 -width 2 -text ""
  label .stat.lnow -width 6 -textvariable LabelNow
  label .stat.now -width 9 -textvariable Now
  label .stat.dummy2 -width 1 -text ""
  pack .stat.lfile .stat.file .stat.ltrk .stat.trk .stat.lfmt \
    .stat.fmt .stat.ldiv .stat.div .stat.dummy1 -in .stat \
    -side left -padx 1m -pady 1m
  pack .stat.dummy2 .stat.now .stat.lnow -in .stat \
    -side right -padx 1m -pady 1m
a764 1
# bitmap controls
d793 2
a794 1
  listbox .trknumb.list \
d875 18
d2666 1
a2666 1
      .trkname.list configure -cursor watch ; update idletasks
d2668 1
a2668 1
      .trkname.list configure -cursor {} ; update idletasks
d2689 1
a2689 1
      .trkname.list configure -cursor watch ; update idletasks
d2691 1
a2691 1
      .trkname.list configure -cursor {} ; update idletasks
d2825 1
a2826 1
    # MIDI mode -- user intervention or when track playback is finished
d2831 1
a2831 2
    set Now [tick2measure $foo]
    update idletasks
d2839 1
a2840 1
    # SMPTE mode -- stop through user intervention only
@


1.18
log
@Seems to work now under tk4.0 non-beta..
@
text
@d4 1
a4 1
# extensions. This is version 0.3.1, Copyright (c) 1995  Greg Wolodkin
d141 11
a154 2
  set Mtracks 0
  set Mformat 0
a156 4
  set PlayName ""
  set PlayFile ""
  set RecFile ""
  set TmpFile ""
a158 1
  set Mdivision $DDIVISION
d182 1
a182 1
  if {[winfo exists .trkname.list]} { showTrackEverything {} }
d201 1
d216 1
d222 1
d374 2
d403 4
a406 1
        dialog .foo . "midiget returned null" info 0 OK
d460 1
a460 1
	bind .control.play <Any-Leave> {tkButtonEnter %W}
d477 1
a477 1
	bind .control.record <Any-Leave> {tkButtonEnter %W}
d504 1
a504 1
	bind .control.pause <Any-Leave> {tkButtonEnter %W}
d521 1
a521 1
        bind .control.ffwd <Any-Leave> {tkButtonEnter %W}
d538 1
a538 1
        bind .control.rewind <Any-Leave> {tkButtonEnter %W}
d765 1
a765 1
  label .stat.ltrk -width 8 -text " Tracks:"
d771 1
a771 1
  label .stat.dummy1 -width 3 -text ""
d809 1
d813 2
d820 2
d825 2
d830 2
d845 1
d868 1
d871 1
d874 1
d877 1
d880 1
d883 2
a885 1
  pack .trkmeas.list -in .trkmeas -side left -fill y
d897 1
a897 1
     {trackForceChannel $PlayFile [.trkchan.list curselection]}
d899 1
a899 1
     {windowMappedEvent $PlayFile [.trkprog.list curselection] Patch }
d991 1
a991 2
  message .file.msg -width 3i -text $text \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
d1001 1
a1001 1
  pack .file.msg -in .file.l.b -side left -expand 1 -fill both \
a1360 2
      wm title .ti$k "Track $k: $selname"
      wm iconname .ti$k "Track $k"
d1398 2
a1534 1
  readTrackNames $mf $new
d1742 1
a1742 2
    message $win.msg -width 3.5i -text "$head" \
      -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*" 
d1752 1
a1752 1
    pack $win.msg -in $win.t -fill x \
d1939 1
a1939 1
  global Gmf Gtrk Gdat Evnt
d1980 2
d1987 1
a1987 1
  global Gmf Gtrk Evnt
d1998 2
d2196 5
a2200 1
    scan [tick2measure $lastnote] "%d" lastmeas
a2201 1
    set xnote    [expr  ($lastnote + 1) * $TimeScale / $mdiv]
a2493 1
    fillTrackInfo $mf $i 1
d2495 1
a2533 1
      fillTrackInfo $mf $i 1
d2535 1
a2590 1
      fillTrackInfo $mf $i 1
d2592 1
d2725 1
a2725 1
          set Mformat 1; set Mtracks 2; set Modified 1
a2774 1
          set Modified 1
d3106 2
a3107 3
  message $w.top.msg -width 3i -text $text \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  pack $w.top.msg -side right -expand 1 -fill both \
d3168 2
a3169 3
  message $w.top.msg -width 3i -text "Channel for Track $trk" \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  pack $w.top.msg -side right -fill both -padx 1m -pady 1m
d3224 2
a3225 3
  message $w.top.msg -width 3i -text $text \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  pack $w.top.msg -side right -expand 1 -fill both \
d3269 2
a3270 3
  message $w.top.msg -width 3i -text $text \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  pack $w.top.msg -side right -expand 1 -fill both \
d3331 2
a3332 3
  message $w.top.msg -width 3i -text $text \
    -font "*-Lucida-Bold-R-Normal-*-*-140-*-*-*-*-*-*"
  pack $w.top.msg -side right -expand 1 -fill both \
d3400 1
a3400 2
  text $w.tt -setgrid 1 -width $width -height $height -wrap word \
    -font "-b&h-lucida-bold-r-normal-sans-12-*-*-*-*-*-iso8859-1" \
d3402 2
a3403 2
  scrollbar $w.scroll -command "$w.tt yview"
  pack $w.tt -side left 
d3405 2
a3406 2
  $w.tt insert end $text
  $w.tt configure -state disabled
@


1.17
log
@added beat lines to piano roll
@
text
@d393 5
d996 1
d998 1
d1005 1
d1745 1
a1745 1
    bind $win.list.names <Button-1> "fillMappedEvent $ix"
d1897 1
a1897 1
proc fillMappedEvent {type} {
d1899 2
d1902 3
d1908 1
a1908 1
    set Gdat($type,$i) [lindex [selection get] $i]
@


1.16
log
@Middle C is blue.  Start piano scroll at a measure bar.
@
text
@a392 8
      # Tclmidi should return EOT but it doesn't -- hmmm.
      # This is probably fixed by now..
      if {$event == ""} {
#        dialog .foo . \
#          "midiget failed! Mail greg@@eecs.berkeley.edu" info 0 OK
        set event [midiget $mf $i next]
      }

d1091 1
d1099 16
d2143 1
a2143 1
  global TimeScale KeyYval KeyYtag ShowBeat ShowMidC
d2185 1
a2185 1
      -variable ShowBeat($w) -command "showBeats $w"
d2220 1
a2220 1
    set rule #606060
d2244 1
d2246 4
d2275 1
a2275 1
proc showBeats {w} {
d2278 9
d2369 1
a2369 1
  global MeterNum MeterDen Mdivision MeasView
d2401 3
a2403 2
  set beat [expr $tick / $Mdivision]
  set frac [expr $tick % $Mdivision]
d2418 2
a2419 1
  set foo [expr $frac + ($beat + $MeterNum * $meas) * $Mdivision]
@


1.15
log
@added measure bars to piano roll
@
text
@d35 1
d91 5
d394 1
d396 2
d400 1
d1378 1
a1378 5
# Merge several tracks.  Be careful about multiple MetaEOTs and other
# duplicate event mishaps.  Maybe midimerge should be sturdier?  Or is
# there a good reason for having multiple MetaEOTs?  COMMENT: it would
# be nice if midimerge had a flag which would allow merging with the
# removal of duplicate events
d1420 1
a1420 1
      "Merge failed. Check for duplicate events." error 0 OK
d1751 1
d1797 1
a1797 1
      -outline black -tags $KeyYtag($j)
d1803 6
a1808 1
      %W itemconfigure $SelKey -fill [lindex $TagList 0]
d1811 1
a1811 1
    %W itemconfigure $SelKey -fill #306090
d2133 2
a2134 2
  global TEAROFF PIANOSCALE
  global TimeScale KeyYval KeyYtag
a2137 1

d2171 10
a2180 1
    pack $w.mbar.file -in $w.mbar -side left 
d2216 1
a2216 1
          set width $PIANOSCALE; set fill grey; set x0 0
d2219 1
a2219 1
          set fill black; set x0 $x0b
d2224 3
d2228 1
a2228 1
          -outline black -tags $KeyYtag($j)
a2240 1
    tkwait visibility $w.note
d2244 3
d2250 16
d2310 2
d2320 2
d2375 2
@


1.14
log
@First cut at piano roll stuff.. this is version 0.4
@
text
@d2133 4
a2137 1
    set xnote    [expr  [miditrack $mf $trk end] * $TimeScale / $mdiv]
a2194 1

d2211 6
@


1.13
log
@Key selection via graphical keyboard
@
text
@d34 1
a34 1
  global CHANNEL1 SHOWPROG SHOWCHAN
d82 3
d118 2
a119 1
  global PlayFile RecFile TmpFile PlayName StopTime
d156 15
d685 2
d688 1
a688 1
    .mbar.track.menu add command -label "View" -underline 0 \
d1745 1
a1745 1
  global Gdat GetFlat TagList SelKey
d1761 1
a1761 1
  canvas $w.c -width 7c -height 5c
d1767 1
a1767 1
    -command "set GetFlat [expr ! $GetFlat]; keyToggle"
d1770 1
a1770 1
  pack $w.c -in $w.l -side left 
d1775 18
a1792 25
  $w.c create rectangle 0c 0c 1c 5c \
    -fill grey -outline black -tags {grey C}
  $w.c create rectangle 0.65c 0c 1.15c 3c \
    -fill black -outline black -tags {black {C sharp} {D flat}}
  $w.c create rectangle 1c 0c 2c 5c \
    -fill grey -outline black -tags {grey D}
  $w.c create rectangle 1.85c 0c 2.45c 3c \
    -fill black -outline black -tags {black {E flat} {D sharp}}
  $w.c create rectangle 2c 0c 3c 5c \
    -fill grey -outline black -tags {grey E}
  $w.c create rectangle 3c 0c 4c 5c \
    -fill grey -outline black -tags {grey F}
  $w.c create rectangle 3.65c 0c 4.15c 3c \
    -fill black -outline black -tags {black {F sharp} {G flat}}
  $w.c create rectangle 4c 0c 5c 5c \
    -fill grey -outline black -tags {grey G}
  $w.c create rectangle 4.75c 0c 5.25c 3c \
    -fill black -outline black -tags {black {A flat} {G sharp}}
  $w.c create rectangle 5c 0c 6c 5c \
    -fill grey -outline black -tags {grey A}
  $w.c create rectangle 5.85c 0c 6.35c 3c \
    -fill black -outline black -tags {black {B flat} {A sharp}}
  $w.c create rectangle 6c 0c 7c 5c \
    -fill grey -outline black -tags {grey B}
  $w.c raise black grey
d1794 1
a1794 1
  bind $w.c <Button-1> { 
d1811 1
d2110 162
@


1.12
log
@Big additions in terms of Tempo, Key, Meter, Parameter, Program,
and Channel maps.  Mostly unified, although Channel is still unique.
I think the Key signature should be unique as well, since there are
not many keys to consider.  Maybe a piano octave widget.. ;-)

You still need to consider the effect of changing meter..
@
text
@a1167 2
  global CHANNEL1

a1168 1

a1169 1
#  dialog .d . {These maps are not supported yet.} info 0 OK
d1177 3
a1179 2
     to the timing of your midi files.  Perturbations have normal\
     distribution with user-adjustable mean and variance.  Mean\
d1182 1
a1182 1
     negative number and randomize the track containing the snare drum. \
d1184 1
a1184 1
     will be -- larger values lead to bigger perturbations. \
d1189 2
a1190 1
     or higher.  Eventually the randomization routine may be\
d1193 1
a1193 1
     is in SMF ticks."
d1611 1
a1611 1
  global Gmf Gtrk Evnt
d1648 7
a1654 5
      set Gdat($ix,i) ""
      frame $win.dat$i
      label $win.dat$i.label -width 12 -text "$Evnt($ix,$i):" -anchor e
      entry $win.dat$i.entry -width 9 -relief sunken \
        -bd 2 -textvariable Gdat($ix,$i)
d1707 5
d1723 84
d1884 1
d1891 1
a1891 1
    set event "$rtime $Evnt($type,1) $rchan"; set i 3
d1893 1
a1893 1
    set event "$rtime $Evnt($type,1)"; set i 2
d1897 1
a1897 1
    set event "$event $Gdat($type,$i)" 
d3105 1
a3105 1
    -font "-adobe-helvetica-medium-r-*-*-*-140-*-*-*-*-*-*" \
@


1.11
log
@Coolness.  Removed all arrays TrkName, TrkMeas, etc.  All data is
stored in listboxes, since we need to show it there anyway..  I think
things are much faster, but it's hard to tell given recent hardware
upgrades ;-)
@
text
@d117 1
a117 1
  # load user-defaults
d119 1
d211 2
d214 5
a218 1
    set tmpname "<untitled>"
d574 1
a574 1
    -command {editMap tempo}
d576 1
a576 1
    -command {editMap key}
d578 1
a578 1
    -command {editMap meter}
d671 1
a671 1
  .mbar.track.menu add command -label "Force Channel" -underline 0 \
d673 1
a673 1
  .mbar.track.menu add command -label "Program Change" -underline 0 \
d675 2
d691 1
a691 1
  .mbar.track.menu add command -label "Randomize" -underline 1 \
d843 1
a843 1
     {trackProgramChange $PlayFile [.trkprog.list curselection]}
d1167 4
a1170 1
proc editMap {type} {
d1172 2
a1173 1
  dialog .d . {These maps are not supported yet.} info 0 OK
d1498 4
d1548 64
d1613 18
a1630 1
  global PCmf PCtrk PCtime PCchan PCnum
d1633 2
a1634 3
    set PCmf $mf
    set PCtrk $trk
    set PCtime ""; set PCchan ""; set PCnum  ""
d1636 2
a1637 2
    toplevel .patch
    wm transient .patch .
d1640 31
a1670 45
    wm geometry .patch "+$x+$y"
    frame .patch.t
    frame .patch.l	
    frame .patch.r 
    frame .patch.l.t 
    frame .patch.l.b 
    frame .patch.time
    frame .patch.chan
    frame .patch.num
    frame .patch.list
    frame .patch.butt
    label .patch.time.label  -text "   Time:"
    entry .patch.time.entry -width 9 -relief sunken -bd 2 -textvariable PCtime
    label .patch.chan.label -text  "Channel:"
    entry .patch.chan.entry -width 9 -relief sunken -bd 2 -textvariable PCchan
    label .patch.num.label -text   "  Patch:"
    entry .patch.num.entry -width 9 -relief sunken -bd 2 -textvariable PCnum

    listbox .patch.list.names -relief sunken -borderwidth 2 \
    	-yscrollcommand ".patch.list.scroll set" \
    	-width 32 -selectmode browse
    scrollbar .patch.list.scroll -command ".patch.list.names yview"

    button .patch.butt.modify -text Apply -state disabled -command { \
      if {[.patch.list.names curselection] != ""} { \
        delProgramEvent $PCmf $PCtrk [selection get]; \
        addProgramEvent $PCmf $PCtrk $PCtime $PCchan $PCnum; \
        set PCtime ""; set PCchan ""; set PCnum "" \
      } \
    }
    button .patch.butt.add -text Add -command { \
      if {$PCtime != "" && $PCchan != "" && $PCnum != ""} { \
        addProgramEvent $PCmf $PCtrk $PCtime $PCchan $PCnum; \
        set PCtime ""; set PCchan ""; set PCnum "" \
      } else { \
        dialog .patch.err .patch "Fill in all of the blanks." error 0 OK \
      } \
    }
    button .patch.butt.remove -text Remove -state disabled -command { \
      if {[.patch.list.names curselection] != ""} { \
        delProgramEvent $PCmf $PCtrk [selection get]; \
        set PCtime ""; set PCchan ""; set PCnum "" \
      } \
    }
    button .patch.butt.ok -text OK -command {destroy .patch}
d1672 1
a1672 1
    message .patch.msg -width 3.5i -text "Program Changes for Track $trk" \
d1675 9
a1683 9
    pack .patch.t -side top -fill x
    pack .patch.l -side left -fill y
    pack .patch.r -side right 
    pack .patch.l.t -in .patch.l -side top -fill x
    pack .patch.l.b -in .patch.l -side right -fill x
    pack .patch.time .patch.chan .patch.num \
      -in .patch.l.t -side top -fill x -padx 0.5m
    pack .patch.butt -in .patch.l.b -side right
    pack .patch.msg -in .patch.t -fill x \
d1685 1
a1685 12
    pack .patch.list -in .patch.r -side left -fill y
    pack .patch.time.label .patch.time.entry -in .patch.time \
   	-side left -padx 1m 
    pack .patch.chan.label .patch.chan.entry -in .patch.chan \
   	-side left -padx 1m
    pack .patch.num.label .patch.num.entry -in .patch.num \
    	-side left -padx 1m
    pack .patch.list.scroll -in .patch.list -side right -fill y
    pack .patch.list.names -in .patch.list -side left -fill y
    pack .patch.butt.modify .patch.butt.add .patch.butt.remove \
    	.patch.butt.ok -in .patch.butt \
    	-side top -fill x -pady 0.5m -padx 1m
d1687 6
a1692 7
    bind .patch.list.names <Button-3> { 
      selection clear .patch.list.names
      .patch.butt.modify configure -state disabled
      .patch.butt.remove configure -state disabled
      set PCtime ""
      set PCchan ""
      set PCnum  ""
d1695 8
a1702 7
    bind .patch.list.names <Button-1> {
      set PCtime [lindex [selection get] 0]
      set PCchan [lindex [selection get] 2]
      set PCnum  [lindex [selection get] 3]
      .patch.butt.modify configure -state normal
      .patch.butt.remove configure -state normal
    }
d1704 1
a1704 1
    listProgramChanges $mf $trk
d1706 3
a1708 3
    tkwait visibility .patch
    grab set .patch
    focus .patch.time.entry
d1710 1
a1710 1
    tkwait window .patch
d1713 3
a1715 1
  if {$SHOWPROG} { showTrackPrograms $mf $tlist }
d1718 9
a1726 2
proc listProgramChanges {mf trk} {
  global CHANNEL1
d1728 1
a1728 3
  .patch.list.names delete 0 end
  .patch.butt.modify configure -state disabled
  .patch.butt.remove configure -state disabled 
d1731 1
a1731 1
    if {[lindex $event 1] == "Program"} {
d1734 1
a1734 1
      if {$CHANNEL1} {
d1736 1
a1736 1
        set event [lreplace $event 2 2 [expr $chan + 1]]
d1738 1
a1738 1
      .patch.list.names insert end $event
d1743 11
a1753 2
proc delProgramEvent {mf trk event} {
  global CHANNEL1
d1755 8
a1762 5
  set meas [lindex $event 0]
  set event [lreplace $event 0 0 [measure2tick $meas]]
  set chan [lindex $event 2]
  set event [lreplace $event 2 2 [expr $chan - $CHANNEL1]]
  mididelete $mf $trk $event
d1764 8
a1771 1
  listProgramChanges $mf $trk
d1774 1
a1774 1
proc addProgramEvent {mf trk ptime pchan pnum} {
d1776 4
d1781 8
a1788 1
  set foobar [catch "set rtime [measure2tick $ptime]"]
d1790 1
a1790 1
    dialog .patch.pt .patch "Invalid time specification.  Ex: 0:00:000" \
d1792 12
a1803 1
    return
d1805 3
a1807 5
  set foobar [catch "set rchan [expr $pchan - $CHANNEL1]"]
  if {$foobar || $rchan < 0 || $rchan > 15} {
    dialog .patch.pt .patch "Invalid channel specification.  Must be 0-15" \
      error 0 OK
    return
d1809 37
a1845 5
  set foobar [catch "set rnum [expr $pnum]"]
  if {$foobar || $rnum < 0 || $rnum > 127} {
    dialog .patch.pt .patch "Invalid patch specification.  Must be 0-127" \
      error 0 OK
    return
a1846 2
  set event "$rtime Program $rchan $rnum"
  midiput $mf $trk $event
d1848 1
a1848 1
  listProgramChanges $mf $trk
d2265 1
a2265 1
      midiput $PlayFile 0 "0 metatempo $TEMPO"
d2275 1
a2275 1
     getStopTime $TmpFile
d2329 1
a2329 2
	if {[dialog .h . "Keep track?" \
          questhead 0 OK Discard]} {
d2332 7
a2338 1
	      set PlayFile $RecFile
d2340 2
a2341 3
          set Mtracks 1
          set Modified 1
          midiput $PlayFile 0 "[miditrack $PlayFile 0 end] MetaEndOfTrack"
d2345 1
a2345 1
          questhead 0 OK Discard]} {
d2354 2
a2355 2
	  midiconfig $newfile "format 1" "division $Mdivision" \
	    "tracks $Mtracks"
d2369 1
a2369 1
            set Mtracks [expr $Mtracks+1]
d2373 1
a2373 1
	      [expr "[miditrack $RecFile 0 end] + 1"]
d2382 1
@


1.10
log
@Minor changes.  Help is fleshed out.
Button-3 binding in Program select clears entries.
This is version 1.3.1, and I'm sending it to Adrian
@
text
@d114 1
a115 2
  global Now MuteList TrkName TrkMeas Modified
  global MeterNum MeterDen MeasView MidiThru
d127 1
a142 5
  for {set i 0} {$i < $NUMTRACKS} {incr i} {
    set TrkName($i) ""
    set TrkMeas($i) ""
  }

d152 1
a152 5
  if {[winfo exists .trklist.list]} {
    if {$SHOWPROG} { showTrackPrograms {} {} }
    if {$SHOWCHAN} { showTrackChannels {} {} }
    refreshTrackDisplay
  }
d159 5
a163 16
  global NUMTRACKS SHOWCHAN SHOWPROG
  global Modified TrkName TrkMeas

  for {set i 0} {$i < $NUMTRACKS} {incr i} {
    set TrkName($i) ""
    set TrkMeas($i) ""
  }

  if {$mf != ""} {
    readTrackNames $mf {}
    readTrackMeasures $mf {}
  }

  if {$SHOWPROG} { showTrackPrograms $mf {} }
  if {$SHOWCHAN} { showTrackChannels $mf {} }
  refreshTrackDisplay
a170 6
  global TrkName

  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
a171 1
  # handle the cases where no tracks are specified
d173 1
d187 3
a189 2
    if {$TrkName($i) != ""} {
       midiput $mf $i [list 0 MetaSequenceName "$TrkName($i)"]
d197 2
a198 2
proc readTrackNames {mf tlist} {
  global TrkName
d200 5
a204 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d206 2
a207 5
  # handle the cases where no tracks are specified
  if {$tlist == ""} {
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
d211 1
a211 1
    set TrkName($i) "<untitled>"
d213 2
d219 1
a219 1
         set TrkName($i) [lindex $event 2]
d223 2
d242 1
a242 5
    set config [midiconfig $mf tracks division format]
    set mtrk [lindex [lindex $config 0] 1]
    set mdiv [lindex [lindex $config 1] 1]
    set mfmt [lindex [lindex $config 2] 1]

d282 4
a285 1
  global CHANNEL1 NUMTRACKS
d293 1
a293 5
    set config [midiconfig $mf tracks division format]
    set mtrk [lindex [lindex $config 0] 1]
    set mdiv [lindex [lindex $config 1] 1]
    set mfmt [lindex [lindex $config 2] 1]

d331 3
a333 2
proc readTrackMeasures {mf tlist} {
  global TrkMeas MeterNum
d335 5
a339 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
a340 1
  # handle the cases where no tracks are specified
d342 6
a347 3
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
d375 2
a376 1
    set TrkMeas($i) "$tmplist"
d380 1
a380 5
# ---------------------------------------------------------------------
# Refresh the on-screen track list -- don't update anything in memory..
# just update the display
#
proc refreshTrackDisplay {} {
d382 2
a383 1
  global TrkName TrkMeas MuteList 
d386 5
a390 15
  .trklist.list delete 0 end
  .trkmeas.text delete 1.0 end
  for {set i 0} {$i < $NUMTRACKS} {incr i} {
    .trklist.list insert end "$TrkName($i)"
    if {[lsearch -exact $MuteList $i] == -1} {
      .trkmute.list insert end ""
    } else {
      .trkmute.list insert end "<mute>"
    }
    # this is a hack so that we have only $NUMTRACKS entries
    if {$i < [expr $NUMTRACKS - 1]} {
      .trkmeas.text insert end "$TrkMeas($i)\n"
    } else {
      .trkmeas.text insert end "$TrkMeas($i)"
    }
d753 1
a753 1
  frame .trklist
d763 3
a765 3
  listbox .trklist.list \
    -yscrollcommand "trackScan {.trklist.list}" \
    -xscrollcommand ".trklist.scroll set" \
d779 7
a785 1
  set listfont [lindex [.trklist.list configure -font] 4]
d787 2
a788 2
  text .trkmeas.text \
    -yscrollcommand "trackScan {.trkmeas.text}" \
d790 1
a790 2
    -width 60 -height 0 -relief sunken -pady 0 \
    -font $listfont -wrap none
d793 1
a793 1
  scrollbar .trkmeas.scroll -command ".trkmeas.text xview" \
d795 1
a795 1
  scrollbar .trklist.scroll -command ".trklist.list xview" \
d798 3
a800 3
  set basewidth [lindex [.trklist.scroll configure -width] 4]
  set bordwidth [lindex [.trklist.scroll configure -borderwidth] 4]
  set highwidth [lindex [.trklist.scroll configure -highlightthickness] 4]
d811 2
a812 2
  pack .trklist.list -in .trklist -side top
  pack .trklist.scroll -in .trklist -side bottom -fill x
d820 2
a821 2
  pack .trkmeas.text -in .trkmeas -side left -fill y
  pack .trknumb .trklist .trkmute .trkprog .trkchan -in .trks -side left
d830 1
a830 1
  bind .trklist.list <Button-3> "selection clear .trklist.list"
d865 1
a865 2
  .trklist.list configure -cursor watch
  .mbar configure -cursor watch
d870 1
a870 2
  .trklist.list configure -cursor {}
  .mbar configure -cursor {}
d920 1
d1054 1
d1192 1
a1192 1
  global Modified TrkName
d1199 1
a1199 1
  set seltrklst [.trklist.list curselection]
d1209 2
a1210 1
    if {$TrkName($i) == ""} {
d1249 1
a1249 1
  global Modified TrkName
a1257 1

d1260 1
a1265 1
  global TrkName
d1268 1
a1268 1
    set selname "$TrkName($k)"
d1343 1
a1343 1
  global Modified TrkName Mtracks
d1367 1
a1367 1
    set tname [format "%s+%s" $tname $TrkName($trk)]
d1398 2
a1399 1
    set TrkName($dtrk) "[string range $tname 1 end]"
d1477 1
a1477 1
  refreshTrackDisplay
a1483 1
  global TrkName 
a1705 1
  global TrkName
d1711 1
a1711 1
    set TrkName($trk) \
d1713 3
a1715 1
      "$TrkName($trk)" 18 140 $foobar]
d1717 1
a1717 2
  writeTrackNames $mf {}
  refreshTrackDisplay
a1954 1

a1994 1
  
a2051 1
  
d2060 1
a2060 1
  eval .trklist.list yview $args
d2064 1
a2064 1
  eval .trkmeas.text yview $args
d2069 1
a2069 1
  foreach i {numb list mute chan} {
d2074 5
a2078 5
  foreach i {meas} {
    if {[string compare .trk$i.text $a] != 0} {
      eval .trk$i.text yview moveto $line
    }
  }
d2124 1
a2124 1
      .trklist.list configure -cursor watch ; update idletasks
d2126 1
a2126 1
      .trklist.list configure -cursor {} ; update idletasks
d2147 1
a2147 1
      .trklist.list configure -cursor watch ; update idletasks
d2149 1
a2149 1
      .trklist.list configure -cursor {} ; update idletasks
d2313 1
a2313 1
proc midiquantize {mf trklist quantize} {
d2329 1
a2329 1
  foreach i $trklist {
d2360 1
a2360 1
proc midioffset {mf trklist offset} {
d2373 1
a2373 1
  foreach i $trklist {
d2401 1
a2401 1
proc miditranspose {mf trklist offset} {
d2414 1
a2414 1
  foreach i $trklist {
d2446 1
a2446 1
proc midirandomize {mf trklist mu var} {
d2461 1
a2461 1
  foreach i $trklist {
@


1.9
log
@Eliminated arrays associated with TrkChan, TrkProg.  Need to do the
same for TrkMeas and TrkName.  (The info is already stored in the
listbox.. no need to keep a separate copy!)

Also made minor mods to file selector -- bind <Button-1> and <Button-3>
@
text
@d4 1
a4 1
# extensions. This is version 0.1, Copyright (c) 1995  Greg Wolodkin
d724 17
a740 1
     -command {dialog .h . "No help just now." info 0 OK}
d1197 1
a1197 1
  displayText .v.h "Help" 64 8 \
d1650 3
d2896 1
a2896 1
    -font "-adobe-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*" \
@


1.8
log
@Program Changes are fixed, except you can only do one track at a time
This may be it for a while.. as there's too much other stuff happening.
@
text
@d33 2
a34 1
  global TEMPO FMASK MEAN VARIANCE HAVE_TEX BITMAPS TEAROFF CHANNEL1
d81 6
d112 1
a112 1
  global DDIVISION NUMTRACKS
d115 2
a116 2
  global Now MuteList TrkName TrkProg TrkChan TrkMeas Modified
  global MeterNum MeterDen MeasView MidiThru ShowChan ShowProg
a144 2
    set TrkProg($i) ""
    set TrkChan($i) ""
a151 2
  set ShowChan 1
  set ShowProg 1
d158 2
a166 25
proc showTrackPrograms {mf} {
  global NUMTRACKS 
  global TrkProg Modified ShowProg

  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkProg($i) "" }
  if {$mf != "" && $ShowProg} {
    readTrackPrograms $mf {}
  }
  refreshTrackDisplay
}

# ---------------------------------------------------------------------
# Generate the track channel list
#
proc showTrackChannels {mf} {
  global NUMTRACKS 
  global TrkChan Modified ShowChan

  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkChan($i) "" }
  if {$mf != "" && $ShowChan} {
    readTrackChannels $mf {}
  }
  refreshTrackDisplay
}

d168 2
a169 2
  global NUMTRACKS
  global Modified ShowChan ShowProg TrkName TrkProg TrkChan TrkMeas
a172 2
    set TrkProg($i) ""
    set TrkChan($i) ""
a178 2
    if {$ShowProg} { readTrackPrograms $mf {} }
    if {$ShowChan} { readTrackChannels $mf {} }
d181 2
d253 2
a254 2
proc readTrackPrograms {mf tlist} {
  global TrkProg
d256 2
a257 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
a258 1
  # handle the cases where no tracks are specified
d260 11
a270 3
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
d274 1
a274 1
    set tmpprog "---"
d279 1
a279 1
    # for now check no more than say the first 32 notes..
d283 1
a283 1
        if {$tmpprog == "---"} {
d293 1
a293 1
            set tmpprog "***"
a296 4
        incr tmpcount
        if {$tmpcount > 32} {
          break
        }
d298 2
d301 2
a302 1
    set TrkProg($i) $tmpprog
d308 8
a315 3
proc readTrackChannels {mf tlist} {
  global CHANNEL1
  global TrkChan
d317 4
a320 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d322 1
a322 5
  # handle the cases where no tracks are specified
  if {$tlist == ""} {
    for {set i 0} {$i < $mtrk} {incr i} {
      set tlist "$tlist $i"
    }
d348 2
a349 4
        incr tmpcount
        if {$tmpcount > 32} {
          break
        }
d352 2
a353 1
    set TrkChan($i) $tmpchan
d409 1
a409 1
  global TrkName TrkProg TrkChan TrkMeas MuteList 
a411 2
  .trkprog.list delete 0 end
  .trkchan.list delete 0 end
a420 7
    if {$TrkName($i) != ""} {
      .trkprog.list insert end $TrkProg($i)
      .trkchan.list insert end $TrkChan($i)
    } else {
      .trkprog.list insert end ""
      .trkchan.list insert end ""
    }
d539 2
a540 2
  global MIDIDEV SMPTEDEV TEAROFF
  global Mformat Mdivision PlayFile PlayName ShowChan ShowProg
d684 1
a684 1
    -variable ShowChan -command {showTrackChannels $PlayFile}
d686 1
a686 1
    -variable ShowProg -command {showTrackPrograms $PlayFile}
d971 10
d982 5
a986 6
    if {![catch {eval "set tt [selection get]"} msg]} {
      if {[file isdirectory $tt]} { 
        cd $tt; set fdir [pwd]; listFiles 
      } else { 
        set tmpname $tt; set waitname $tmpname 
      }
d1497 2
a1498 2
  global CHANNEL1
  global TrkName TrkChan
d1506 1
a1506 1
    set ochan $TrkChan($i)
d1541 1
a1541 2
  readTrackChannels $mf $tlist
  refreshTrackDisplay
d1546 1
a1546 1
  global ShowProg
d1630 4
a1633 4
    bind .patch.list.names <Button-3> { \
      selection clear .patch.list.names; \
      .patch.butt.modify configure -state disabled; \
      .patch.butt.remove configure -state disabled \
d1636 6
a1641 6
    bind .patch.list.names <Button-1> { \
      set PCtime [lindex [selection get] 0]; \
      set PCchan [lindex [selection get] 2]; \
      set PCnum  [lindex [selection get] 3]; \
      .patch.butt.modify configure -state normal; \
      .patch.butt.remove configure -state normal \
d1653 1
a1653 1
  if {$ShowProg} { showTrackPrograms $mf }
@


1.7
log
@Channel forcing is really groovy now.
I'm about to make major changes in the way info is collected
in an effort to speed things up.
@
text
@d28 1
d81 1
d99 1
d136 7
a142 4
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkName($i) "" }
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkProg($i) "" }
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkChan($i) "" }
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkMeas($i) "" }
a157 14
# ---------------------------------------------------------------------
# Generate and display the track list, based on info
# contained in MetaSequenceName events
#
proc showTrackNames {mf} {
  global NUMTRACKS
  global TrkName Modified

  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkName($i) "" }
  if {$mf != ""} {
    readTrackNames $mf {}
  }
  refreshTrackDisplay
}
d187 1
a187 4
# ---------------------------------------------------------------------
# Generate and display the track measure list
#
proc showTrackMeasures {mf} {
d189 8
a196 1
  global TrkMeas Modified
a197 1
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set TrkMeas($i) "" }
d199 1
d201 2
d204 1
d456 1
d876 1
a876 1
     {trackProgramChange $PlayFile [.trkchan.list curselection]}
d878 1
a878 4
  showTrackNames $PlayFile
  showTrackPrograms $PlayFile
  showTrackChannels $PlayFile
  showTrackMeasures $PlayFile
d1092 2
d1095 1
a1095 4
  showTrackNames $PlayFile
  showTrackPrograms $PlayFile
  showTrackChannels $PlayFile
  showTrackMeasures $PlayFile
d1163 1
d1199 1
a1199 1
  displayText .v.h "Help" 64 12  \
d1217 1
d1273 1
a1273 5
  showTrackNames $mf
  showTrackPrograms $mf
  showTrackChannels $mf
  showTrackMeasures $mf

d1358 1
a1358 4
  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]
d1361 1
a1361 2
    midirewind $mf $i
    set ticks [lindex [midiget $mf $i prev] 0]
d1364 1
d1388 1
a1388 1
  set mtrk [lindex [lindex $config 0] 1]
d1461 1
a1461 5
  showTrackNames $mf
  showTrackPrograms $mf
  showTrackChannels $mf
  showTrackMeasures $mf

a1510 15
# -------------------------------------------------------------------
#
proc trackProgramChange {mf tlist} {
  global CHANNEL1
  global TrkName TrkProg TrkChan

  set config [midiconfig $mf tracks division format]
  set mtrk [lindex [lindex $config 0] 1]
  set mdiv [lindex [lindex $config 1] 1]
  set mfmt [lindex [lindex $config 2] 1]

#  foreach i $tlist {
#  }
   dialog .pc . "Not yet implemented." info 0 OK
}
d1534 4
a1537 1
    if [catch "set nchan [getChannel $i $ochan]"] {return}
d1564 170
d1853 1
a1853 5
    showTrackNames $mf
    showTrackPrograms $mf
    showTrackChannels $mf
    showTrackMeasures $mf

d1859 1
a1859 1
# For this to work you will need midi2tex, TeX, and xdvi,
d1861 3
a1972 1
# note that `selection get' doesn't work in 4.0 anymore.. :(
d1978 8
a1985 7
      foreach j $elist {
        set event [.ti$i.list get $j]
        set tick [lindex $event 0]
        set event [lreplace $event 0 0 [measure2tick $tick]]
        mididelete $mf $i $event
      }
      set Modified 1
d1987 3
a1989 8
      # refresh the display
      fillTrackInfo $mf $i 1

      showTrackNames $mf
      showTrackPrograms $mf
      showTrackChannels $mf
      showTrackMeasures $mf
    }
a2024 1
      set Modified 1
d2028 2
a2029 4
      showTrackNames $mf
      showTrackPrograms $mf
      showTrackChannels $mf
      showTrackMeasures $mf
d2032 1
a2082 1
      set Modified 1
d2086 2
a2087 4
      showTrackNames $mf
      showTrackPrograms $mf
      showTrackChannels $mf
      showTrackMeasures $mf
d2115 1
d2263 1
a2263 4
          showTrackNames $PlayFile
          showTrackPrograms $PlayFile
          showTrackChannels $PlayFile
          showTrackMeasures $PlayFile
d2308 1
a2308 1
# Called by play and record in SMPTE mode to display SMPTE time
d2314 1
a2314 1
    # MIDI mode -- user intervention or when track is finished
d2316 1
a2316 1
    if {$foo > $StopTime} {
d2664 1
a2664 1
  button $w.bot.cancel -text "Cancel" -command "destroy $w"
d2689 1
d2896 5
a2900 2
    -font "-adobe-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*" 
  pack $w.tt -side left -padx 2m -pady 2m
@


1.6
log
@Added CHANNEL1 feature so channel numbers appears to start at one.
Added autostop for MIDI device, and display of SMF time
@
text
@d43 2
a44 1
  set NUMTRACKS 100
d105 2
a106 2
  global Now MuteList TrkName TrkChan TrkMeas Modified
  global MeterNum MeterDen MeasView MidiThru
d134 1
d141 2
a142 1
  set ShowChan 0
d170 14
d276 51
d355 5
a359 1
          set tmpchan $tt
d372 1
a372 5
    if {[string length $tmpchan] == 1} {
      set TrkChan($i) 0$tmpchan
    } else {
      set TrkChan($i) $tmpchan
    }
d428 1
a428 1
  global TrkName TrkChan TrkMeas MuteList 
d431 1
d443 1
d446 1
d566 2
a567 2
  global SHOWCHAN MIDIDEV SMPTEDEV TEAROFF
  global Mformat Mdivision PlayFile PlayName
a571 2
#  focus .mbar

d712 2
d727 5
a757 1
#  focus .mbar
d802 1
d818 3
d823 1
a823 1
    -width 3 -height $VISTRACKS -relief flat
d848 1
d857 2
d863 1
a863 1
  pack .trknumb .trklist .trkmute .trkchan -in .trks -side left
d873 4
d879 1
d897 7
a903 1
  foreach i {numb mute chan} { disableSelect .trk$i.list }
d1010 2
a1011 1
#  set oldFocus [focus]
d1014 1
a1015 1
#  grab release .file
a1016 1
#  focus $oldFocus
d1097 1
d1202 1
a1202 1
  displayText .v.h "Help" 64 8  \
d1236 1
a1236 1
    dialog .r . "Select a track or two and try again." error 0 OK
d1276 1
d1471 1
d1526 65
d1597 1
a1597 2
  for {set i 0} {$i < [llength $tlist]} {incr i} {
    set trk [lindex $tlist $i]
d1647 1
a1647 1
    "0" 18 140 100]
d1649 1
a1649 1
  midioffset $mf $tlist $offset
d1668 2
a1669 2
    [getEntry .et . "Transpose up" {} "in halfsteps:" \
    "0" 18 140 100]
d1709 1
d1847 1
d1891 1
d1951 1
d1962 1
d2026 1
a2026 1
      getStopTime $TmpFile
d2044 1
a2044 1
  if {$PlayFile != ""} {
d2129 1
d2491 2
a2492 1
#  set oldFocus [focus]
d2494 2
a2495 1
  focus $w
a2496 1
#  grab release $w
a2497 1
#  focus $oldFocus
d2500 60
d2597 2
a2598 1
#  set oldFocus [focus]
d2601 1
a2603 1
#  focus $oldFocus
d2606 1
d2661 2
a2662 1
#  set oldFocus [focus]
d2664 2
a2665 1
  focus $w.mid.scale
a2667 1
#  focus $oldFocus
d2724 2
a2725 1
#  set oldFocus [focus]
d2727 2
a2728 1
  focus $w.mid.scale
a2730 1
#  focus $oldFocus
a2766 1
#  focus $w.mbar
a2787 1
#  focus .sh.mbar
@


1.5
log
@fixed focus on entry boxes
added time-based seed to randomization
@
text
@d32 1
a32 1
  global TEMPO FMASK MEAN VARIANCE HAVE_TEX BITMAPS TEAROFF
d75 3
d102 2
a103 2
  global MidiState SmpteClk Lsmpte Mtracks Mformat Mdivision
  global PlayFile RecFile TmpFile PlayName
a121 1
  set Lsmpte ""
a124 1
  set Now ""
d130 2
d142 3
d260 1
d285 1
a285 1
        set tt [lindex $event 2]
d497 1
a497 1
  global MidiThru SmpteClk Lsmpte Now MidiStatus
d569 2
a570 1
  	-underline 0 -variable SmpteClk -value 0
d572 2
a573 1
  	-underline 0 -variable SmpteClk -value 1 
d695 2
a696 2
  label .stat.lsmpte -width 6 -textvariable Lsmpte
  label .stat.smpte -width 9 -textvariable Now
d701 1
a701 1
  pack .stat.dummy2 .stat.smpte .stat.lsmpte -in .stat \
d1261 21
d1843 1
a1843 1
  global MidiState PlayFile RecFile TmpFile SmpteClk Lsmpte
a1846 8
    # select device
    if {$SmpteClk == 1} {
      mididevice "name $SMPTEDEV"
      set Lsmpte "SMPTE:"
    } else {
      mididevice "name $MIDIDEV"
      set Lsmpte ""
    }
d1861 1
d1864 1
d1870 1
a1870 1
    stime
d1877 1
a1877 1
  global MidiState PlayFile Lsmpte SmpteClk Modified TmpFile MuteList
a1879 7
    if {$SmpteClk} {
      mididevice "name $SMPTEDEV"
      set Lsmpte "SMPTE:"
    } else {
      mididevice "name $MIDIDEV"
      set Lsmpte ""
    }
d1884 1
d1887 1
d1892 1
a1892 1
    stime
d1898 3
a1900 2
  global MidiState PlayFile RecFile TmpFile 
  global Mdivision Mtracks Mformat PlayName
d1904 1
d1980 30
d2012 18
a2029 2
proc stime {} {
  global Now SmpteClk MidiState
d2031 4
a2034 9
    if {$SmpteClk} {
      set foo [miditime smpte]
      if {$foo == "NOSYNC"} {
        set Now "Waiting.."
      } else {
        set Now [string range $foo 0 7]
      }
      update idletasks
      after 500 stime
d2036 1
a2036 2
      set Lsmpte ""
      set Now ""
d2038 2
d2042 1
@


1.4
log
@Improved checking for existence of MIDI hardware
@
text
@d78 15
d384 1
a384 1
  global TEAROFF
d386 4
a389 22
  if {[mididevice] == 1} {
    switch -- $play {
      0 {
          .control.play configure -state disabled
      	  .mbar.realtime.menu entryconfigure [expr 0+$TEAROFF] \
      	     -state disabled
      	}
      1 {
          .control.play configure -state normal 
      	  .mbar.realtime.menu entryconfigure [expr 0+$TEAROFF] \
      	     -state normal
	  bind .control.play <Any-Leave> {tkButtonLeave %W}
	}
      2 {
          .control.play configure -state active
	  bind .control.play <Any-Leave> {tkButtonEnter %W}
        }
    }
    switch $rec {
      0 {
          .control.record configure -state disabled
    	  .mbar.realtime.menu entryconfigure [expr 1+$TEAROFF] \
d391 89
a479 75
        }
      1 {
          .control.record configure -state normal
    	  .mbar.realtime.menu entryconfigure [expr 1+$TEAROFF] \
    	     -state normal
	  bind .control.record <Any-Leave> {tkButtonLeave %W}
	}
      2 {
          .control.record configure -state active
	  bind .control.record <Any-Leave> {tkButtonEnter %W}
        }
    }
    switch $stop {
      0 {
          .control.stop configure -state disabled 
    	  .mbar.realtime.menu entryconfigure [expr 2+$TEAROFF] \
    	     -state disabled
        }
      1 { 
          .control.stop configure -state normal
    	  .mbar.realtime.menu entryconfigure [expr 2+$TEAROFF] \
    	     -state normal
        }
    }
    switch $pause {
      0 {
          .control.pause configure -state disabled
          .mbar.realtime.menu entryconfigure [expr 6+$TEAROFF] \
             -state disabled
        }
      1 {
          .control.pause configure -state normal
          .mbar.realtime.menu entryconfigure [expr 6+$TEAROFF] \
             -state normal
	  bind .control.pause <Any-Leave> {tkButtonLeave %W}
	}
      2 {
          .control.pause configure -state active
	  bind .control.pause <Any-Leave> {tkButtonEnter %W}
	}
    }
    switch $ffwd {
      0 {
          .control.ffwd configure -state disabled
          .mbar.realtime.menu entryconfigure [expr 4+$TEAROFF] \
             -state disabled
	}
      1 {
          .control.ffwd configure -state normal
          .mbar.realtime.menu entryconfigure [expr 4+$TEAROFF] \
             -state normal
 	  bind .control.ffwd <Any-Leave> {tkButtonLeave %W}
	}
      2 {
          .control.ffwd configure -state active
	  bind .control.ffwd <Any-Leave> {tkButtonEnter %W}
	}
    }
    switch $rewind {
      0 {
          .control.rewind configure -state disabled
          .mbar.realtime.menu entryconfigure [expr 5+$TEAROFF] \
             -state disabled
        }
      1 {
          .control.rewind configure -state normal
          .mbar.realtime.menu entryconfigure [expr 5+$TEAROFF] \
             -state normal
	  bind .control.rewind <Any-Leave> {tkButtonLeave %W}
	}
      2 {
          .control.rewind configure -state active
	  bind .control.rewind <Any-Leave> {tkButtonEnter %W}
	}
    }
d482 1
d490 1
a490 1
  global MidiThru SmpteClk Lsmpte Now
d525 1
a525 1
    -command seqPlay
d527 1
a527 1
    -command seqRecord
d529 1
a529 1
    -command seqStop
d532 1
a532 1
    -command seqFFwd
d534 1
a534 1
    -command seqRewind
d536 1
a536 1
    -command seqPause
d697 12
a708 6
  button .control.rewind -command seqRewind -bitmap "@@$BITMAPS/tks_rew"
  button .control.stop -command seqStop -bitmap "@@$BITMAPS/tks_stop"
  button .control.ffwd  -command seqFFwd -bitmap "@@$BITMAPS/tks_ffwd"
  button .control.play  -command seqPlay -bitmap "@@$BITMAPS/tks_play"
  button .control.record -command seqRecord -bitmap "@@$BITMAPS/tks_rec"
  button .control.pause -command seqPause -bitmap "@@$BITMAPS/tks_paus"
a785 1
  updateButtons 0 1 0 0 0 0
d798 1
d910 1
a910 1
#  focus .file.name.entry
d2130 1
a2132 4
# FIX ME
#  set foo [lindex [time date] 0]
#  rand {seed [lindex $foo 0]}

d2263 1
a2263 1
#  focus $w
d2309 1
a2309 1
#  focus $w.bot.val
d2371 1
a2371 1
#  focus $w.mid.scale
d2433 1
a2433 1
#  focus $w.mid.scale
d2517 1
a2517 1
  global MIDIDEV
a2545 2
  updateButtons 0 0 0 0 0 0
  .mbar.realtime configure -state disabled
@


1.3
log
@Recovering from bonehead error.  10 versions lost.. doesn't
matter much I guess.  Anyway this is the latest version, and
it *requires* Tcl7.4b3/Tk4.0b3 or above.
@
text
@d776 3
a778 5
  if {[mididevice] == 0 && $MIDIDEV != ""} {
    dialog .d . "No MIDI device found.  Realtime controls disabled." info 0 OK
    updateButtons {0 0 0 0 0 0}
    .mbar.realtime configure -state disabled
  } else {
d2499 36
@


1.2
log
@Cleaned up global variable names
@
text
@d32 1
a32 1
  global TEMPO FMASK MEAN VARIANCE HAVE_TEX BITMAPS
d38 5
a42 1
  # allow 100 tracks, show 16 at a time
d69 6
d86 2
a87 1
  global Now Mutelist TrkName Modified
d97 1
d107 1
a107 1
  set Mutelist ""
d115 2
d118 5
d124 2
a125 2
  if {[winfo exists .trklist]} {
    refreshTrackNames
d140 29
a168 1
  refreshTrackNames
d236 96
d333 2
a334 1
# Refresh the on-screen track list
d336 1
a336 1
proc refreshTrackNames {} {
d338 1
a338 1
  global TrkName Mutelist
d340 4
a343 2
  .trklist delete 0 end
  .trkmute delete 0 end
d345 8
a352 3
    .trklist insert end "$TrkName($i)"
    if {[lsearch -exact $Mutelist $i] == -1} {
      .trkmute insert end "      "
d354 7
a360 1
      .trkmute insert end "<mute>"
d366 1
d368 101
a468 29
proc updateButtons {play rec stop pause fastfwd rewind} {
  switch -- $play {
    0 {.cont.play configure -state disabled}
    1 {.cont.play configure -state normal -foreground #303090}
    2 {.cont.play configure -state normal -foreground #10d010}
  }
  switch $rec {
    0 {.cont.record configure -state disabled}
    1 {.cont.record configure -state normal -foreground #303090}
    2 {.cont.record configure -state active -foreground #d01010}
  }
  switch $stop {
    0 {.cont.stop configure -state disabled}
    1 {.cont.stop configure -state normal}
  }
  switch $pause {
    0 {.cont.pause configure -state disabled}
    1 {.cont.pause configure -state normal -foreground #303090}
    2 {.cont.pause configure -state active -foreground #1010a0}
  }
  switch $fastfwd {
    0 {.cont.fastfwd configure -state disabled}
    1 {.cont.fastfwd configure -state normal -foreground #303090}
    2 {.cont.fastfwd configure -state active -foreground #1010a0}
  }
  switch $rewind {
    0 {.cont.rewind configure -state disabled}
    1 {.cont.rewind configure -state normal -foreground #303090}
    2 {.cont.rewind configure -state active -foreground #1010a0}
d476 3
a478 1
  global Mformat Mdivision Lsmpte Now SmpteClk PlayFile PlayName
d481 2
a482 1
  focus default .mbar
d492 2
d497 1
a497 1
  menu .mbar.file.menu
d511 1
a511 1
  menu .mbar.realtime.menu
d519 2
a520 2
  .mbar.realtime.menu add command -label "FastFwd" -underline 0 \
    -command seqFastFwd
d526 1
a526 1
  menu .mbar.settings.menu
d529 2
d548 1
a548 1
  menu .mbar.settings.menu.clock
d554 16
a569 1
  menu .mbar.settings.menu.div
d593 1
a593 1
  menu .mbar.settings.menu.quant
d598 2
d602 2
d606 2
d610 2
d613 1
a613 1
  menu .mbar.settings.menu.random
d615 1
a615 1
  -underline 0 -command setMean
d617 5
a621 1
  -underline 0 -command setVariance
d623 1
a623 1
  menu .mbar.track.menu
d657 2
a658 1
  pack .mbar.file .mbar.realtime .mbar.settings .mbar.track -side left
d661 7
a667 8
  tk_menuBar .mbar .mbar.file .mbar.realtime .mbar.settings .mbar.track 
  focus .mbar
  frame .stat -relief raised -bd 2
  frame .cont -relief sunken -bd 2
  pack .stat .cont -side bottom -fill x
  label .stat.lfile -width 4 -text "File:" 
  label .stat.file -width 20 -textvariable PlayName
  label .stat.ltrk -width 5 -text "Tracks:"
d669 1
a669 1
  label .stat.lfmt -width 6 -text "Format:"
d671 1
a671 1
  label .stat.ldiv -width 8 -text "Division:"
d673 1
a673 1
  label .stat.dummy1 -width 4 -text ""
d675 2
a676 2
  label .stat.smpte -width 8 -textvariable Now
  label .stat.dummy2 -width 2 -text ""
d683 9
a691 14
  label .cont.ldummy -text "" -width 20 -relief flat
  button .cont.rewind -command seqRewind \
    -bitmap "@@$BITMAPS/tks_rew"
  button .cont.stop -command seqStop \
    -bitmap "@@$BITMAPS/tks_stop"
  button .cont.fastfwd  -command seqFastFwd \
    -bitmap "@@$BITMAPS/tks_ffwd"
  button .cont.play  -command seqPlay \
    -bitmap "@@$BITMAPS/tks_play"
  button .cont.record -command seqRecord \
    -bitmap "@@$BITMAPS/tks_rec"
  button .cont.pause -command seqPause \
    -bitmap "@@$BITMAPS/tks_paus"
  label .cont.rdummy -text "" -width 20 
d693 2
a694 2
  pack .cont.ldummy .cont.rewind .cont.stop .cont.fastfwd \
       .cont.play .cont.pause .cont.record .cont.rdummy \
d696 64
a759 13
  listbox .trknumb -yscrollcommand "trackScan" \
    -geometry "5x$VISTRACKS"
  listbox .trklist -yscrollcommand "trackScan" \
    -geometry "18x$VISTRACKS" -relief sunken -bd 1
  listbox .trkmute -yscrollcommand "trackScan" \
    -geometry "7x$VISTRACKS"
  # hack to "disable" selection in this listbox
  set foo [lindex [.trknumb configure -background] 4]
  .trknumb configure -selectbackground $foo
  .trkmute configure -selectbackground $foo
  scrollbar .trkscroll -command "trackScroll"
  pack .trknumb .trklist .trkmute -in .trks -side left
  pack .trkscroll -in .trks -side right -fill y
d763 1
a763 1
  bind .trklist <Button-3> "selection clear .trklist"
d766 2
d770 1
d772 1
a772 1
    .trknumb insert end [format "%3d:" $i]
d774 12
d792 1
a792 1
  .trklist configure -cursor watch
d798 1
a798 1
  .trklist configure -cursor {}
d818 2
a819 2
  set x [expr [winfo x .]+40]
  set y [expr [winfo y .]+30]
d840 2
a841 1
  	-yscrollcommand ".file.list.scroll set"
d847 4
a850 1
    -command {exec rm [selection get]; listFiles}
d883 8
a890 7
  bind .file.list.names <Double-1> { \
    set tt [selection get]; \
    if {[file isdirectory $tt]} { \
      cd $tt; set fdir [pwd]; listFiles \
    } else { \
      set tmpname $tt; set waitname $tmpname \
    } \
d892 1
a892 1
  set oldFocus [focus]
d894 1
a894 1
  focus .file.name.entry
d896 1
d898 1
a898 1
  focus $oldFocus
a977 1
#  .mbar.settings.menu.div configure -state disable
d979 2
d1115 1
a1115 1
  set seltrklst [.trklist curselection]
d1157 3
d1183 1
d1199 6
a1204 5
      listbox .ti$k.t -relief sunken -bd 1 \
        -yscrollcommand ".ti$k.s set" -geometry 30x20
      scrollbar .ti$k.s -command ".ti$k.t yview"
      pack .ti$k.s -side right -fill y
      pack .ti$k.t -side left -fill both -expand 1
d1209 4
a1212 1
      menu .ti$k.mbar.trk.m
d1217 1
a1217 1
      menu .ti$k.mbar.edit.m
d1222 7
a1228 1
      pack .ti$k.mbar.trk .ti$k.mbar.edit -side left 
d1230 2
a1231 2
      tk_menuBar .ti$k.mbar .ti$k.mbar.trk .ti$k.mbar.edit
      bind .ti$k.t <Button-3> "selection clear .ti$k.t"
d1291 1
d1309 1
a1309 1
    if {[winfo exist .ti$i]} {
d1328 1
d1330 3
d1351 2
a1352 2
  set start  [lindex [.ti$old.s get] 2]
  set geom   [wm geometry .ti$old]
d1359 1
a1359 1
  .ti$new.t yview $start
d1368 1
a1368 1
  global Mutelist 
d1373 1
a1373 1
    set lpos [lsearch -exact $Mutelist $ltrk]
d1375 1
a1375 1
      set Mutelist [lsort -integer "$Mutelist $ltrk"]
d1377 1
a1377 1
      set Mutelist [lreplace $Mutelist $lpos $lpos]
d1380 1
a1380 1
  refreshTrackNames
d1388 2
a1389 2
  set foo [lindex [.trkscroll get] 2]
  set foobar [expr ([lindex $tlist 0]-$foo)*6]
d1397 1
a1397 1
  refreshTrackNames
a1435 2

  # this isn't *really* global
d1437 1
a1437 1

d1501 1
d1503 3
d1558 1
d1567 1
a1567 1
    .ti$i.t delete 0 end
d1570 6
a1575 1
      .ti$i.t insert end "$event"
d1580 1
a1580 1
      .ti$i.t yview [lindex [.ti$i.s get] 2]
d1585 34
d1622 1
d1625 17
a1641 4
  if {[.ti$i.t curselection] != ""} {
    set eventlist [selection get]
    foreach event $eventlist {
      mididelete $mf $i $event
a1642 4
    set Modified 1
    # refresh the display
    fillTrackInfo $mf $i 1
  }
d1649 1
a1649 1
  global Modified
d1652 24
a1675 16
  if {[.ti$i.t curselection] != ""} {
    set eventlist [selection get]
    foreach event $eventlist {
      set newevent \
        [getEntry .ti$i.ee .ti$i.t  "Modify Event" {} "Track $i:" \
          "$event" 30 {} {}]
      mididelete $mf $i $event
      while {[catch {midiput $mf $i "$newevent"} msg]} {
        if {[dialog .ti$i.di .ti$i {Unable to put event.} warning \
          0 {Try again} {Keep original}]} {
          midiput $mf $i $event
          break
        } else {
          set newevent \
            [getEntry .ti$i.ee .ti$i.t  "Modify Event" {} "Track $i:" \
            "$newevent" 30 {} {}]
d1678 7
a1684 5
    }
    set Modified 1
    # refresh the display
    fillTrackInfo $mf $i 1
    showTrackNames $mf
d1687 10
d1698 48
d1748 6
a1753 4
proc trackScroll {val} {
 .trknumb yview $val
 .trklist yview $val
 .trkmute yview $val
d1756 13
a1768 3
proc trackScan {a b c d} {
  .trkmute yview $c
  .trkscroll set $a $b $c $d
d1786 1
a1786 1
proc seqFastFwd {} {
d1798 1
a1798 1
  global Modified Mdivision Mutelist
d1819 4
a1822 4
    if {$Mutelist != ""} {
      .trklist configure -cursor watch ; update idletasks
      set TmpFile [midiremove $PlayFile $Mutelist 0]
      .trklist configure -cursor {} ; update idletasks
d1837 1
a1837 1
  global MidiState PlayFile Lsmpte SmpteClk Modified TmpFile Mutelist
d1847 4
a1850 4
    if {$Mutelist != ""} {
      .trklist configure -cursor watch ; update idletasks
      set TmpFile [midiremove $PlayFile $Mutelist 0]
      .trklist configure -cursor {} ; update idletasks
d1927 2
a1948 2
#      set foo [lindex [mididevice {time smpte}] 0]
#      set foo [lindex $foo 2]
d1977 1
a1977 1
  # FIX ME so I work with arbitrary meters
d2206 1
a2206 1
#
d2214 2
a2215 2
  set x [expr [winfo x $parent]+40]
  set y [expr [winfo y $parent]+30]
d2248 1
a2248 1
  set oldFocus [focus]
d2250 1
a2250 1
  focus $w
d2252 1
d2254 1
a2254 1
  focus $oldFocus
d2273 2
a2274 2
  set x [expr [winfo x $parent]+40+$xoffset]
  set y [expr [winfo y $parent]+30+$yoffset]
d2294 1
a2294 1
  set oldFocus [focus]
d2296 1
a2296 1
  focus $w.bot.val
d2299 1
a2299 1
  focus $oldFocus
d2317 2
a2318 2
  set x [expr [winfo x $parent]+40]
  set y [expr [winfo y $parent]+30]
d2356 1
a2356 1
  set oldFocus [focus]
d2358 1
a2358 1
  focus $w.mid.scale
d2361 1
a2361 1
  focus $oldFocus
d2379 2
a2380 2
  set x [expr [winfo x $parent]+40]
  set y [expr [winfo y $parent]+30]
d2418 1
a2418 1
  set oldFocus [focus]
d2420 1
a2420 1
  focus $w.mid.scale
d2423 1
a2423 1
  focus $oldFocus
d2440 2
d2453 1
a2453 1
  menu $w.mbar.file.m 
d2460 1
a2460 2
  tk_menuBar $w.mbar $w.mbar.file 
  focus $w.mbar
d2467 1
d2480 1
a2480 1
  menu .sh.mbar.file.m 
d2482 1
a2482 2
  tk_menuBar .sh.mbar .sh.mbar.file 
  focus .sh.mbar
d2493 9
d2508 5
@


1.1
log
@Initial revision
@
text
@d23 3
a25 1
# User Configuration begins here -- edit these if you want
d27 2
d30 1
a30 1
  # ALL-CAPS are user-definable global variables
d43 2
a45 1
  set FMASK ""
d53 2
a54 1
  # default quantization is 1/16th notes 
d57 1
a57 1
  # I assume you have midi2tex, tex, and xdvi.  grep for midi2tex
d74 3
a76 3
  global Midi_state Smpteclk Lsmpte Mtracks Mformat Mdivision
  global Now Mutelist Trkname Modified
  global pf rf tf pname rname
d83 3
a85 3
  if {$pf != ""} { 
    closeTrackInfo $pf {}
    midifree $pf
d87 2
a88 2
  if {$rf != ""} { midifree $rf }
  if {$tf != ""} { midifree $tf }
d90 2
a91 2
  set Midi_state stopped
  set Smpteclk 0
d98 5
a102 6
  set pname ""
  set rname ""
  set pf ""
  set rf ""
  set tf ""
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set Trkname($i) "" }
d115 1
a115 1
  global Trkname Modified
d117 1
a117 1
  for {set i 0} {$i < $NUMTRACKS} {incr i} { set Trkname($i) "" }
d129 1
a129 1
  global Trkname
d151 2
a152 2
    if {$Trkname($i) != ""} {
       midiput $mf $i [list 0 MetaSequenceName "$Trkname($i)"]
d161 1
a161 1
  global Trkname
d176 1
a176 1
    set Trkname($i) "<untitled>"
d182 1
a182 1
         set Trkname($i) [lindex $event 2]
d194 1
a194 1
  global Trkname Mutelist
d199 1
a199 1
    .trklist insert end "$Trkname($i)"
d246 1
a246 1
  global Mformat Mdivision Lsmpte Now Smpteclk pf pname
d313 1
a313 1
  	-underline 0 -variable Smpteclk -value 0
d315 1
a315 1
  	-underline 0 -variable Smpteclk -value 1 
d361 1
a361 1
    -command {track Info $pf}
d363 1
a363 1
    -command {track Mute $pf}
d365 1
a365 1
    -command {track Name $pf}
d368 1
a368 1
      -command {track Score $pf}
d372 1
a372 1
    -command {track Copy $pf}
d374 1
a374 1
    -command {track Merge $pf}
d376 1
a376 1
    -command {track Remove $pf}
d379 1
a379 1
    -command {track Erase $pf}
d381 1
a381 1
    -command {track Offset $pf}
d383 1
a383 1
    -command {track Quantize $pf}
d385 1
a385 1
    -command {track Randomize $pf}
d387 1
a387 1
    -command {track Transpose $pf}
d402 1
a402 1
  label .stat.file -width 20 -textvariable pname
d455 1
a455 1
  showTrackNames $pf
d481 1
a481 1
# It uses the global variables pname, FMASK, and fdir
d486 2
d612 1
a612 1
  global Modified pname
d620 1
a620 1
  set pname [getFileName "Open a MIDI file"]
d628 1
a628 1
  global pf Midi_state pname Modified Mtracks Mdivision Mformat
d630 1
a630 1
  if {$pname == ""} { return }
d633 1
a633 1
  if {$pf != ""} {
d635 1
a635 1
    set tmpname $pname
d637 1
a637 1
    set pname $tmpname
d639 2
a640 2
  set ff [open $pname]
  set pf [midiread $ff]
d642 1
a642 1
  set config [midiconfig $pf tracks division format]
d648 1
a648 1
  showTrackNames $pf
d656 1
a656 1
  global Midi_state Modified
d668 2
a669 2
  if {$Midi_state != "stopped"} {
    set Midi_state stopped
d679 2
a680 2
  global pf pname Modified
  if {$pf == ""} {
d684 1
a684 1
  if {$pname == ""} {
d687 3
a689 3
    writeTrackNames $pf {}
    set ff [open $pname w]
    midiwrite $ff $pf
d698 2
a699 2
  global pname Modified pf
  if {$pf == ""} {
d709 1
a709 1
  writeTrackNames $pf {}
d711 2
a712 2
  set pname $tmpname
  midiwrite $ff $pf
d776 1
a776 1
  global Modified Trkname
d793 1
a793 1
    if {$Trkname($i) == ""} {
d832 1
a832 1
  global Modified Trkname
d848 1
a848 1
  global Trkname
d851 1
a851 1
    set selname "$Trkname($k)"
d898 1
a898 1
  global Modified Trkname Mtracks
d922 1
a922 1
    set tname [format "%s+%s" $tname $Trkname($trk)]
d952 1
a952 1
    set Trkname($dtrk) "[string range $tname 1 end]"
d1034 1
a1034 1
  global Trkname
d1041 1
a1041 1
    set Trkname($trk) \
d1043 1
a1043 1
      "$Trkname($trk)" 18 140 $foobar]
d1052 2
a1053 1
  global Modified DQUANTIZE
d1069 2
a1070 1
  global Modified MEAN VARIANCE
d1084 4
a1087 1
  global Modified offset
d1106 4
a1109 1
  global Modified halfsteps
d1313 3
a1315 2
  global Midi_state pf rf tf MIDIDEV SMPTEDEV Smpteclk Lsmpte
  global Modified Mdivision TEMPO Mutelist
d1317 1
a1317 1
  if {$Midi_state == "stopped"} {
d1319 1
a1319 1
    if {$Smpteclk == 1} {
d1327 4
a1330 4
    if {$pf == ""} {
      set pf [midimake]
      midiconfig $pf "tracks 1" "division $Mdivision"
      midiput $pf 0 "0 metatempo $TEMPO"
d1333 3
a1335 3
    set rf [midimake]
    set pconfig [midiconfig $pf division]
    midiconfig $rf "tracks 1" [lindex $pconfig 0]
d1338 1
a1338 1
      set tf [midiremove $pf $Mutelist 0]
d1340 1
a1340 1
      midirecord $rf $tf
d1342 1
a1342 1
      midirecord $rf $pf
d1346 1
a1346 1
    set Midi_state recording
d1353 2
a1354 2
  global Midi_state pf MIDIDEV SMPTEDEV Lsmpte Smpteclk Modified
  global tf Mutelist
d1356 2
a1357 2
  if {$pf != ""} {
    if {$Smpteclk} {
d1366 1
a1366 1
      set tf [midiremove $pf $Mutelist 0]
d1368 1
a1368 1
      midiplay $tf
d1370 1
a1370 1
      midiplay $pf
d1373 1
a1373 1
    set Midi_state playing
d1380 5
a1384 4
  global Midi_state pf rf tf Mdivision pname Mtracks Mformat
  if {$Midi_state != "stopped"} {
    set prev_state $Midi_state
    set Midi_state stopped
d1387 3
a1389 3
    if {$tf != ""} {
      midifree $tf
      set tf ""
d1392 3
a1394 3
      midirewind $rf
      midirewind $pf
      if {$pname == ""} {
d1399 2
a1400 2
	      set pf $rf
          set pname untitled.mid
d1403 1
a1403 1
          midiput $pf 0 "[miditrack $pf 0 end] MetaEndOfTrack"
d1406 1
a1406 1
        if {[dialog .h . "Merge new track into $pname?" \
d1408 2
a1409 2
          midifree $rf
          set rf ""
d1421 2
a1422 2
	      midicopy "$newfile $i" 0 "$pf $i" \
	        0 [expr "[miditrack $pf $i end] + 1"]
d1425 2
a1426 2
	    midicopy "$newfile $newtrack" 0 "$rf 0" \
	      0 [expr "[miditrack $rf 0 end] + 1"]
d1433 3
a1435 3
	    midisplit "$pf 0" "$newfile 0" "$newfile 1"
     	    midicopy "$newfile 2" 0 "$rf 0" 0 \
	      [expr "[miditrack $rf 0 end] + 1"]
d1440 4
a1443 4
	  midifree $pf
	  midifree $rf
	  set pf $newfile
          showTrackNames $pf
d1448 2
a1449 2
    set rf ""
    if {$pf == ""} {
d1461 3
a1463 3
  global Now Smpteclk Midi_state
  if {$Midi_state != "stopped"} {
    if {$Smpteclk} {
d2011 3
a2013 3
set pf ""
set rf ""
set tf ""
@
