1#!/bin/sh
2# the next line restarts using wish \
3exec wish8.4 "$0" "$@"
4
5package require -exact snack 2.2
6# Try to load optional file format handlers
7catch { package require snacksphere }
8catch { package require snackogg }
9package require http
10
11set debug 0
12snack::debug $debug
13snack::sound snd -debug $debug
14snack::sound cbs -debug $debug
15
16set tcl_precision 7
17set f(prog) [info script]
18set f(labfile) ""
19set f(sndfile) ""
20set f(lpath)   ""
21set f(header)  ""
22set mexhome "~/snack/"
23catch {source $mexhome/ipa_tmh.tcl}
24set f(ipapath) $mexhome/ipa_xbm
25set local 0
26if $local {
27    set v(labfmt) TIMIT
28    set v(smpfmt) WAV
29    set v(ashost) ior.speech.kth.se
30} else {
31    set v(labfmt) TIMIT
32    set v(smpfmt) WAV
33    set v(ashost) localhost
34}
35set labels {}
36set undo {}
37set v(labchanged) 0
38set v(smpchanged) 0
39set v(width) 600
40set v(toth) 286
41set v(msg) "Press right mouse button for menu"
42set v(timeh) 20
43set v(yaxisw) 40
44set v(labelh) 20
45set v(psfilet) {tmp$N.ps}
46set v(psfile)  ""
47set v(vchan)   -1
48#set v(offset) 0
49#set v(zerolabs) 0
50set v(startsmp) 0
51set v(lastmoved) -1
52set v(p_version) 2.2
53set v(s_version) 2.2
54set v(plugins) {}
55set v(scroll) 1
56set v(rate) 16000
57set v(sfmt) Lin16
58set v(chan) 1
59set v(topfr) 8000
60set v(rp_sock) ""
61#set v(propflag) 0
62set v(pause) 0
63set v(recording) 1
64set v(activerec) 0
65set v(cmap) grey
66set v(grey) " "
67#set v(color1) {#000 #006 #00B #00F #03F #07F #0BF #0FF #0FB #0F7 \
68	      #0F0 #3F0 #7F0 #BF0 #FF0 #FB0 #F70 #F30 #F00}
69set v(color1) {#000 #004 #006 #00A #00F \
70 	       #02F #04F #06F #08F #0AF #0CF #0FF #0FE \
71	       #0FC #0FA #0F8 #0F6 #0F4 #0F2 #0F0 #2F0 \
72	       #4F0 #6F0 #8F0 #AF0 #CF0 #FE0 #FC0 #FA0 \
73	       #F80 #F60 #F40 #F20 #F00}
74set v(color2) {#FFF #BBF #77F #33F #00F #07F #0BF #0FF #0FB #0F7 \
75	      #0F0 #3F0 #7F0 #BF0 #FF0 #FB0 #F70 #F30 #F00}
76set v(contrast) 0
77set v(brightness) 0
78set v(showspeg) 0
79set v(remspegh) 200
80set v(remote) 0
81set v(asport) 23654
82set v(handle) ""
83set v(s0) 0
84
85set z(zoomwinh) 200
86set z(zoomwinw) 600
87set z(zoomwinx) 200
88set z(zoomwiny) 200
89set z(zoomwavh) 0
90set z(zoomwavw) 0
91set z(f) 1
92
93set s(sectwinh) 400
94set s(sectwinw) 400
95set s(sectwinx) 200
96set s(sectwiny) 200
97set s(secth) 400
98set s(sectw) 400
99set s(rx) -1
100
101proc SetDefaultVars {} {
102    global f v s local
103
104    set v(waveh) 50
105    set v(spegh) 0
106    set v(scrw) 32767
107    set v(pps) 400
108    set v(opps) 400
109    set v(fftlen) 256
110    set v(winlen) 128
111    set v(anabw) 125
112    set v(preemph) 0.97
113    set v(ipa) 0
114    set v(autoload) 0
115    set v(ch) 0
116    set v(slink) 0
117    set v(mlink) 0
118    if {$::tcl_platform(platform) == "unix"} {
119	set v(printcmd)  {lpr $FILE}
120	set v(gvcmd)     {ghostview $FILE}
121	set v(psfilecmd) {cp -f _xspr$n.ps $v(psfile)}
122	if $local {
123	    set v(pluginfiles) {~/snack/xsplug/dataplot.plg ~/snack/xsplug/generator.plg ~/snack/xsplug/transcribe.plg ~/snack/xsplug/cutter.plg ~/snack/xsplug/pitch.plg}
124	} else {
125	    set v(pluginfiles) [glob -nocomplain *.plg]
126	}
127#	set v(browser) "netscape"
128	if {$::tcl_platform(os) == "HP-UX"} {
129	    option add *font {Helvetica 10 bold}
130	} else {
131	    option add *font {Helvetica 12 bold}
132	}
133    } else {
134	set v(printcmd)  {C:/gs/gs6.50/bin/gswin32 "-IC:\gs\gs6.50;C:\gs\gs6.50\fonts" -sDEVICE=laserjet -dNOPAUSE $FILE -c quit}
135	set v(gvcmd)     {C:/ghostgum/gsview/gsview32 $FILE}
136	set v(psfilecmd) {command.com /c copy _xspr$n.ps $v(psfile)}
137	if $local {
138#	    set v(pluginfiles) {H:/tcl/mexd/dataplot.plg H:/tcl/mexd/generator.plg H:/tcl/mexd/pitch.plg}
139            set v(pluginfiles) {}
140	} else {
141	    set v(pluginfiles) [glob -nocomplain *.plg]
142	}
143#	set v(browser) "c:/program files/netscape/communicator/program/netscape.exe"
144    }
145    set v(ipafmt) TMH
146    set v(labalign) w
147    set v(fg) black
148    set v(bg) [. cget -bg]
149    if {[string match macintosh $::tcl_platform(platform)] || \
150	    [string match Darwin $::tcl_platform(os)]} {
151	set v(fillmark) 0
152    } else {
153	set v(fillmark) 1
154    }
155    set v(font)  {Courier 10}
156    if {[string match unix $::tcl_platform(platform)] } {
157     set v(sfont) {Helvetica 10 bold}
158    } else {
159     set v(sfont) {Helvetica 8 bold}
160    }
161    set v(gridfspacing) 0
162    set v(gridtspacing) 0
163    set v(gridcolor) red
164    set v(cmap) grey
165    set v(showspeg) 0
166    set v(remspegh) 200
167    set v(linkfile) 0
168    set f(skip) 0
169    set f(byteOrder) ""
170    set f(ipath) ""
171    set f(ihttp) "http://www.speech.kth.se/~kare/ex1.wav"
172    #"http://www.speech.kth.se/cgi-bin/TransAll?this_is_an_example+am"
173
174    set s(fftlen) 512
175    set s(anabw)  31.25
176    set s(ref)    -110.0
177    set s(range)  110.0
178    set s(wintype) Hamming
179    set s(atype) FFT
180    set s(lpcorder) 20
181
182    if {[info exists snack::snackogg]} {
183      set ::ogg(nombr) 128000
184      set ::ogg(maxbr) -1
185      set ::ogg(minbr) -1
186      set ::ogg(com)   ""
187      set ::ogg(query) 1
188    }
189}
190
191SetDefaultVars
192catch { source [file join ~ .xsrc] }
193catch { source [file join ~ .xsrf] }
194
195snd config -rate $v(rate)
196snd config -encoding $v(sfmt)
197snd config -channels $v(chan)
198
199set filt(f) [snack::filter map 0.0]
200
201set echo(f) [snack::filter echo 0.6 0.6 30 0.4]
202set echo(n) 1
203set echo(drain) 1
204set echo(iGain) 60
205set echo(oGain) 60
206
207set mix(f) [snack::filter map 0.0]
208
209set amplify(f) [snack::filter map 1.0]
210set amplify(v) 100.0
211set amplify(db) 0
212
213set normalize(f) [snack::filter map 1.0]
214set normalize(v) 100.0
215set normalize(db) 0
216set normalize(allEqual) 1
217
218set remdc(f) [snack::filter iir -numerator "0.99 -0.99" -denominator "1 -0.99"]
219
220set f(spath) $f(ipath)
221set f(http) $f(ihttp)
222set f(urlToken) ""
223
224if {$v(p_version) != $v(s_version)} {
225     set v(msg) "Warning, you have saved settings from an older version of xs!"
226    SetDefaultVars
227}
228
229# Put custom settings between the lines below
230# Custom settings start here
231# Custom settings end here
232
233snack::menuInit
234snack::menuPane File
235snack::menuCommand File {Open...} GetOpenFileName
236snack::menuBind . o File {Open...}
237snack::menuCommand File {Get URL...} OpenGetURLWindow
238snack::menuCommand File Save Save
239snack::menuBind . s File Save
240snack::menuCommand File {Save As...} SaveAs
241snack::menuCommand File Close Close
242snack::menuSeparator File
243snack::menuCommand File Print... {Print .cf.fc.c -1}
244snack::menuCommand File Info {set v(msg) [InfoStr nopath]}
245snack::menuSeparator File
246if [info exists recentFiles] {
247    foreach e $recentFiles {
248	snack::menuCommand File $e [list OpenFiles $e]
249    }
250    snack::menuSeparator File
251}
252snack::menuCommand File Exit Exit
253
254snack::menuPane Edit 0 ConfigEditMenu
255snack::menuCommand Edit Undo Undo
256snack::menuEntryOff Edit Undo
257snack::menuSeparator Edit
258snack::menuCommand Edit Cut Cut
259snack::menuBind . x Edit Cut
260snack::menuCommand Edit Copy Copy
261snack::menuBind . c Edit Copy
262snack::menuCommand Edit Paste Paste
263snack::menuBind . v Edit Paste
264snack::menuCommand Edit Crop Crop
265snack::menuCommand Edit {Mark All} MarkAll
266snack::menuCommand Edit {Zero Cross Adjust} ZeroXAdjust
267
268set n [snack::menuPane Audio]
269bind $n <<MenuSelect>> { snack::mixer update }
270snack::menuCommand Audio {Play range} PlayMark
271snack::menuCommand Audio {Play All} PlayAll
272snack::menuBind . p Audio {Play All}
273snack::menuCommand Audio {Stop Play} StopPlay
274#snack::menuCommand Audio {Gain Control...} {snack::gainBox rp}
275snack::menuCommand Audio Mixer... snack::mixerDialog
276#if {[snack::mixer inputs] != ""} {
277#    snack::menuCascade Audio Input
278#    foreach jack [snack::mixer inputs] {
279#	snack::mixer input $jack v(in$jack)
280#	snack::menuCheck Input $jack v(in$jack)
281#    }
282#}
283#if {[snack::mixer outputs] != ""} {
284#    snack::menuCascade Audio Output
285#    foreach jack [snack::mixer outputs] {
286#	snack::mixer output $jack v(out$jack)
287#	snack::menuCheck Output $jack v(out$jack)
288#    }
289#}
290snack::menuCascade Audio {Audio Settings}
291snack::menuCascade {Audio Settings} {Set Sample Rate}
292set rateList [snack::audio rates]
293if {$rateList == ""} {
294    set rateList {11025 22050 44100}
295}
296foreach fr $rateList {
297    snack::menuRadio {Set Sample Rate} $fr v(rate) $fr SetRaw
298}
299snack::menuCascade {Audio Settings} {Set Encoding}
300foreach fo [snack::audio encodings] {
301    snack::menuRadio {Set Encoding} $fo v(sfmt) $fo SetRaw
302}
303snack::menuCascade {Audio Settings} {Set Channels}
304snack::menuRadio {Set Channels} Mono   v(chan) 1 SetRaw
305snack::menuRadio {Set Channels} Stereo v(chan) 2 SetRaw
306
307snack::menuPane Transform 0 ConfigTransformMenu
308snack::menuCascade Transform Conversions
309snack::menuCascade Conversions {Convert Sample Rate}
310foreach fr $rateList {
311    snack::menuCommand {Convert Sample Rate} $fr "Convert {} $fr {}"
312}
313snack::menuCascade Conversions {Convert Encoding}
314foreach fo [snack::audio encodings] {
315    snack::menuCommand {Convert Encoding} $fo "Convert $fo {} {}"
316}
317snack::menuCascade Conversions {Convert Channels}
318snack::menuCommand {Convert Channels} Mono   "Convert {} {} Mono"
319snack::menuCommand {Convert Channels} Stereo "Convert {} {} Stereo"
320snack::menuCommand Transform Amplify... Amplify
321snack::menuCommand Transform Normalize... Normalize
322#snack::menuCommand Transform Normalize... Normalize
323snack::menuCommand Transform Echo... Echo
324snack::menuCommand Transform {Mix Channels...} MixChan
325snack::menuCommand Transform Invert Invert
326snack::menuCommand Transform Reverse Reverse
327snack::menuCommand Transform Silence Silence
328snack::menuCommand Transform {Remove DC} RemoveDC
329
330snack::menuPane Tools
331
332snack::menuPane Options 0 ConfigOptionsMenu
333snack::menuCommand Options Settings... Settings
334if {[info exists snack::snackogg]} {
335  snack::menuCommand Options "Ogg Vorbis..." [list OggSettings Close]
336}
337snack::menuCommand Options Plug-ins... Plugins
338snack::menuCascade Options {Label File Format}
339snack::menuRadio {Label File Format} TIMIT v(labfmt) TIMIT {Redraw quick}
340snack::menuRadio {Label File Format} HTK v(labfmt) HTK {Redraw quick}
341snack::menuRadio {Label File Format} WAVES v(labfmt) WAVES {Redraw quick}
342snack::menuRadio {Label File Format} MIX v(labfmt) MIX {Redraw quick}
343if $local {
344    snack::menuCascade Options {IPA Translation}
345    snack::menuRadio {IPA Translation} TMH v(ipafmt) TMH {source $mexhome/ipa_tmh.tcl;Redraw quick}
346    snack::menuRadio {IPA Translation} CMU v(ipafmt) CMU {source $mexhome/ipa_cmu.tcl;Redraw quick}
347}
348snack::menuCascade Options {Label Alignment}
349snack::menuRadio {Label Alignment} left v(labalign)   w {Redraw quick}
350snack::menuRadio {Label Alignment} center v(labalign) c {Redraw quick}
351snack::menuRadio {Label Alignment} right v(labalign)  e {Redraw quick}
352snack::menuCascade Options {View Channel}
353snack::menuRadio {View Channel} both v(vchan) -1 { Redraw;DrawZoom 1;DrawSect }
354snack::menuRadio {View Channel} left v(vchan) 0  { Redraw;DrawZoom 1;DrawSect }
355snack::menuRadio {View Channel} right v(vchan) 1 { Redraw;DrawZoom 1;DrawSect }
356snack::menuSeparator Options
357if $local {
358    snack::menuCheck Options {IPA Transcription} v(ipa) {Redraw quick}
359}
360snack::menuCheck Options {Record Button} v(recording) ToggleRecording
361snack::menuCheck Options {Show Spectrogram} v(showspeg) ToggleSpeg
362snack::menuCheck Options {Auto Load} v(autoload)
363snack::menuCheck Options {Cross Hairs} v(ch) DrawCrossHairs
364snack::menuCheck Options {Fill Between Marks} v(fillmark) {$c coords mfill -1 -1 -1 -1 ; Redraw quick}
365snack::menuCheck Options {Link to Disk File} v(linkfile) Link2File
366if {$tcl_platform(platform) == "unix"} {
367    snack::menuCheck Options {Link Scroll} v(slink)
368    snack::menuCheck Options {Link Marks} v(mlink)
369}
370#snack::menuCheck Options {Align x-axis/first label} v(offset) {Redraw quick}
371#snack::menuCheck Options {Show zero length labels} v(zerolabs) {Redraw quick}
372snack::menuSeparator Options
373snack::menuCommand Options {Set default options} {SetDefaultVars ; Redraw}
374snack::menuCommand Options {Save options} SaveSettings
375
376snack::menuPane Window
377snack::menuCommand Window {New Window} NewWin
378snack::menuBind . n Window {New Window}
379snack::menuCommand Window Refresh Redraw
380snack::menuBind . r Window Refresh
381snack::menuCommand Window {Waveform Zoom} OpenZoomWindow
382snack::menuCommand Window {Spectrum Section} OpenSectWindow
383#snack::menuCommand Window {WaveSurfer} WS
384
385snack::menuPane Help
386snack::menuCommand Help Version Version
387snack::menuCommand Help Manual  {Help http://www.speech.kth.se/snack/xs.html}
388
389# Put custom menus between the lines below
390# Custom menus start here
391# Custom menus end here
392
393#bind Menu <<MenuSelect>> {
394#    global v
395#    if {[catch {%W entrycget active -label} label]} {
396#	set label ""
397#    }
398#    set v(msg) $label
399#    update idletasks
400#}
401
402if {$tcl_platform(platform) == "windows"} {
403    set border 1
404} else {
405    set border 0
406}
407
408snack::createIcons
409pack [frame .tb -highlightthickness 1] -anchor w
410pack [button .tb.open -command GetOpenFileName -image snackOpen -highlightthickness 0 -border $border] -side left
411
412pack [button .tb.save -command Save -image snackSave -highlightthickness 0 -border $border] -side left
413pack [button .tb.print -command {Print .cf.fc.c -1} -image snackPrint -highlightthickness 0 -border $border] -side left
414
415pack [frame .tb.f1 -width 1 -height 20 -highlightth 1] -side left -padx 5
416pack [button .tb.cut -command Cut -image snackCut -highlightthickness 0 -border $border] -side left
417pack [button .tb.copy -command Copy -image snackCopy -highlightthickness 0 -border $border] -side left
418pack [button .tb.paste -command Paste -image snackPaste -highlightthickness 0 -border $border] -side left
419
420pack [frame .tb.f2 -width 1 -height 20 -highlightth 1] -side left -padx 5
421pack [button .tb.undo -command Undo -image snackUndo -highlightthickness 0 -border $border -state disabled] -side left
422
423pack [frame .tb.f3 -width 1 -height 20 -highlightth 1] -side left -padx 5
424pack [button .tb.play -command PlayMark -bitmap snackPlay -fg blue3 -highlightthickness 0 -border $border] -side left
425bind .tb.play <Enter> {SetMsg "Play mark"}
426pack [button .tb.pause -command PausePlay -bitmap snackPause -fg blue3 -highlightthickness 0 -border $border] -side left
427bind .tb.pause <Enter> {SetMsg "Pause"}
428pack [button .tb.stop -command StopPlay -bitmap snackStop -fg blue3 -highlightthickness 0 -border $border] -side left
429bind .tb.stop <Enter> {SetMsg "Stop"}
430pack [button .tb.rec -command Record -bitmap snackRecord -fg red -highlightthickness 0 -border $border] -side left
431bind .tb.rec <Enter> {SetMsg "Record"}
432#pack [button .tb.gain -command {snack::gainBox rp} -image snackGain -highlightthickness 0 -border $border] -side left
433pack [button .tb.gain -command snack::mixerDialog -image snackGain -highlightthickness 0 -border $border] -side left
434bind .tb.gain <Enter> {SetMsg "Open gain control panel"}
435
436pack [frame .tb.f4 -width 1 -height 20 -highlightth 1] -side left -padx 5
437pack [button .tb.zoom -command OpenZoomWindow -image snackZoom -highlightthickness 0 -border $border] -side left
438bind .tb.zoom <Enter> {SetMsg "Open zoom window"}
439
440frame .of
441pack [canvas .of.c -width $v(width) -height 30 -bg $v(bg)] -fill x -expand true
442pack [scrollbar .of.xscroll -orient horizontal -command ScrollCmd] -fill x -expand true
443bind .of.xscroll <ButtonPress-1> { set v(scroll) 1 }
444bind .of.xscroll <ButtonRelease-1> RePos
445bind .of.c <1> {OverPlay %x}
446
447pack [ frame .bf] -side bottom -fill x
448entry .bf.lab -textvar v(msg) -width 1 -relief sunken -bd 1 -state disabled
449pack .bf.lab -side left -expand yes -fill x
450
451set v(toth) [expr $v(waveh) + $v(spegh) + $v(timeh)+ $v(labelh)]
452pack [ frame .cf] -fill both -expand true
453pack [ frame .cf.fyc] -side left -anchor n
454canvas .cf.fyc.yc2 -height 0 -width $v(yaxisw) -highlightthickness 0
455pack [ canvas .cf.fyc.yc -width $v(yaxisw) -height $v(toth) -highlightthickness 0 -bg $v(bg)]
456
457pack [ frame .cf.fc] -side left -fill both -expand true
458set c [canvas .cf.fc.c -width $v(width) -height $v(toth) -xscrollcommand [list .cf.fc.xscroll set] -yscrollcommand [list .cf.fc.yscroll set] -closeenough 5 -highlightthickness 0 -bg $v(bg)]
459scrollbar .cf.fc.xscroll -orient horizontal -command [list $c xview]
460scrollbar .cf.fc.yscroll -orient vertical -command yScroll
461#pack .cf.fc.xscroll -side bottom -fill x
462#pack .cf.fc.yscroll -side right -fill y
463pack $c -side left -fill both -expand true
464
465proc yScroll {args} {
466    global c
467
468    eval .cf.fyc.yc yview $args
469    eval $c yview $args
470}
471
472$c create rect -1 -1 -1 -1 -tags mfill -fill yellow -stipple gray25
473$c create line -1 0 -1 $v(toth) -width 1 -tags [list mark [expr 0 * $v(rate)/$v(pps)] m1] -fill $v(fg)
474$c create line -1 0 -1 $v(toth) -width 1 -tags [list mark [expr 0 * $v(rate)/$v(pps)] m2] -fill $v(fg)
475
476bind all <Control-l> {
477    set n 0
478    if {$labels == {}} return
479    while {[lindex [$c coords lab$n] 0] < [expr $v(width) * [lindex [$c xview] 0]]} { incr n }
480
481    $c focus lab$n
482    focus $c
483    $c icursor lab$n 0
484    set i 0
485    SetMsg [lindex $labels $i] $i
486    SetUndo $labels
487}
488
489$c bind text <Control-p> {
490    set __x [lindex [%W coords [%W focus]] 0]
491    set __y [lindex [%W coords [%W focus]] 1]
492    set __n [lindex [$c gettags [$c find closest $__x $__y]] 0]
493    PlayNthLab $__n
494    break
495}
496
497$c bind text <Button-1> {
498    %W focus current
499    %W icursor current @[$c canvasx %x],[$c canvasy %y]
500    set i [lindex [$c gettags [%W focus]] 0]
501    SetMsg [lindex $labels $i] $i
502    SetUndo $labels
503}
504
505event add <<Delete>> <Delete>
506catch {event add <<Delete>> <hpDeleteChar>}
507
508$c bind text <<Delete>> {
509    if {[%W focus] != {}} {
510	%W dchars [%W focus] insert
511	SetLabelText [lindex [$c gettags [%W focus]] 0] [$c itemcget [%W focus] -text]
512	set i [lindex [$c gettags [%W focus]] 0]
513	SetMsg [lindex $labels $i] $i
514    }
515}
516
517$c bind text <BackSpace> {
518    if {[%W focus] != {}} {
519	set _tmp [%W focus]
520	set _ind [expr [%W index $_tmp insert]-1]
521	if {$_ind >= 0} {
522	    %W icursor $_tmp $_ind
523	    %W dchars $_tmp insert
524	    SetLabelText [lindex [$c gettags [%W focus]] 0] [$c itemcget [%W focus] -text]
525	    set i [lindex [$c gettags [%W focus]] 0]
526	    SetMsg [lindex $labels $i] $i
527	}
528	unset _tmp _ind
529    }
530}
531
532$c bind text <Return> {
533    %W insert current insert ""
534    %W focus {}
535}
536
537$c bind text <Enter> {
538    %W insert current insert ""
539    %W focus {}
540}
541
542$c bind text <Control-Any-Key> { break }
543
544$c bind text <Any-Key> {
545    if {[%W focus] != {}} {
546	%W insert [%W focus] insert %A
547	SetLabelText [lindex [$c gettags [%W focus]] 0] [$c itemcget [%W focus] -text]
548	set i [lindex [$c gettags [%W focus]] 0]
549	SetMsg [lindex $labels $i] $i
550    }
551    set v(labchanged) 1
552}
553
554$c bind text <space> {
555    if {[%W focus] != {}} {
556	%W insert [%W focus] insert _
557	SetLabelText [lindex [$c gettags [%W focus]] 0] [$c itemcget [%W focus] -text]
558	set i [lindex [$c gettags [%W focus]] 0]
559	SetMsg [lindex $labels $i] $i
560    }
561}
562
563$c bind text <Key-Right> {
564    if {[%W focus] != {}} {
565	set __index [%W index [%W focus] insert]
566	%W icursor [%W focus] [expr $__index + 1]
567	if {$__index == [%W index [%W focus] insert]} {
568            set __focus [expr [lindex [$c gettags [%W focus]] 0] + 1]
569	    %W focus lab$__focus
570	    %W icursor lab$__focus 0
571	    set i [lindex [$c gettags [%W focus]] 0]
572	    SetMsg [lindex $labels $i] $i
573	    while {[expr $v(width) * [lindex [$c xview] 1] -10] < [lindex [%W coords [%W focus]] 0] && [lindex [$c xview] 1] < 1} {
574		$c xview scroll 1 unit
575	    }
576	}
577    }
578}
579
580$c bind text <Key-Left> {
581    if {[%W focus] != {}} {
582	set __index [%W index [%W focus] insert]
583	%W icursor [%W focus] [expr [%W index [%W focus] insert] - 1]
584	if {$__index == [%W index [%W focus] insert]} {
585            set __focus [expr [lindex [$c gettags [%W focus]] 0] - 1]
586	    %W focus lab$__focus
587	    %W icursor lab$__focus end
588	    set i [lindex [$c gettags [%W focus]] 0]
589	    SetMsg [lindex $labels $i] $i
590	    while {[expr $v(width) * [lindex [$c xview] 0] +10] > [lindex [%W coords [%W focus]] 0] && [lindex [$c xview] 0] > 0} {
591		$c xview scroll -1 unit
592	    }
593	}
594    }
595}
596
597set _mx 1
598set _mb 0
599#$c bind bound  <B1-Motion> { MoveBoundary %x }
600$c bind bound  <ButtonRelease-1> { set _mb 0 ; Redraw quick }
601$c bind m1     <B1-Motion> { PutMarker m1 %x %y 1 }
602$c bind m2     <B1-Motion> { PutMarker m2 %x %y 1 }
603$c bind m1     <ButtonPress-1>   { set _mx 0 }
604$c bind m2     <ButtonPress-1>   { set _mx 0 }
605$c bind obj    <ButtonPress-1> { PutMarker m1 %x %y 1 }
606$c bind obj    <B1-Motion>     { PutMarker m2 %x %y 1 }
607$c bind m1     <ButtonRelease-1> { SendPutMarker m1 %x ; set _mx 0 }
608$c bind m2     <ButtonRelease-1> { SendPutMarker m2 %x ; set _mx 0 }
609$c bind bound  <Any-Enter> { BoundaryEnter %x }
610$c bind mark   <Any-Enter> { MarkerEnter %x }
611$c bind bound  <Any-Leave> { BoundaryLeave %x }
612$c bind mark   <Any-Leave> { MarkerLeave %x }
613
614bind $c <ButtonPress-1>   {
615    if {%y > [expr $v(waveh)+$v(spegh)+$v(timeh)]} {
616    } else {
617	PutMarker m1 %x %y 1
618	SendPutMarker m1 %x
619	set _mx 1
620    }
621}
622
623bind $c <ButtonRelease-1> {
624    set _mb 0
625    if {%y > [expr $v(waveh)+$v(spegh)+$v(timeh)]} {
626	focus %W
627	if {[%W find overlapping [expr [$c canvasx %x]-2] [expr [$c canvasy %y]-2] [expr [$c canvasx %x]+2] [expr [$c canvasy %y]+2]] == {}} {
628	    %W focus {}
629	}
630    } else {
631	PutMarker m2 %x %y 1
632	SendPutMarker m2 %x
633	set _mx 1
634    }
635}
636bind $c <Delete> Cut
637bind $c <Motion> { PutCrossHairs %x %y }
638bind $c <Leave>  {
639    $c coords ch1 -1 -1 -1 -1
640    $c coords ch2 -1 -1 -1 -1
641}
642
643if {[string match macintosh $::tcl_platform(platform)] || \
644	[string match Darwin $::tcl_platform(os)]} {
645 bind $c <Control-1> { PopUpMenu %X %Y %x %y }
646} else {
647 bind $c <3> { PopUpMenu %X %Y %x %y }
648}
649
650bind .cf.fc.xscroll <ButtonRelease-1> SendXScroll
651bind .bf.lab <Any-KeyRelease> { InputFromMsgLine %K }
652bind all <Control-c> Exit
653wm protocol . WM_DELETE_WINDOW Exit
654bind .cf.fc.c <Configure> { if {"%W" == ".cf.fc.c"} Reconf }
655bind $c <F1> { PlayToCursor %x }
656bind $c <2>  { PlayToCursor %x }
657focus $c
658
659proc RecentFile fn {
660    global recentFiles
661
662    if {$fn == ""} return
663    if [info exists recentFiles] {
664	foreach e $recentFiles {
665	    snack::menuDelete File $e
666	}
667	snack::menuDeleteByIndex File 10
668    } else {
669	set recentFiles {}
670    }
671    snack::menuDelete File Exit
672    set index [lsearch -exact $recentFiles $fn]
673    if {$index != -1} {
674	set recentFiles [lreplace $recentFiles $index $index]
675    }
676    set recentFiles [linsert $recentFiles 0 $fn]
677    if {[llength $recentFiles] > 6} {
678	set recentFiles [lreplace $recentFiles 6 end]
679    }
680    foreach e $recentFiles {
681	snack::menuCommand File $e [list OpenFiles $e]
682    }
683    snack::menuSeparator File
684    snack::menuCommand File Exit Exit
685    if [catch {open [file join ~ .xsrf] w} out] {
686    } else {
687	puts $out "set recentFiles \[list $recentFiles\]"
688	close $out
689    }
690}
691
692set extTypes  [list {TIMIT .phn} {MIX .smp.mix} {HTK .lab} {WAVES .lab}]
693set loadTypes [list {{MIX Files} {.mix}} {{HTK Label Files} {.lab}} {{TIMIT Label Files} {.phn}} {{TIMIT Label Files} {.wrd}} {{Waves Label Files} {.lab}}]
694set loadKeys [list MIX HTK TIMIT WAVES]
695set saveTypes {}
696set saveKeys  {}
697
698if {[info exists snack::snacksphere]} {
699    lappend extTypes {SPHERE .sph} {SPHERE .wav}
700    lappend loadTypes {{SPHERE Files} {.sph}} {{SPHERE Files} {.wav}}
701    lappend loadKeys SPHERE SPHERE
702}
703if {[info exists snack::snackogg]} {
704  lappend extTypes  {OGG .ogg}
705  lappend loadTypes {{Ogg Vorbis Files} {.ogg}}
706  lappend loadKeys  OGG
707  lappend saveTypes {{Ogg Vorbis Files} {.ogg}}
708  lappend saveKeys  OGG
709
710  proc OggSettings {text} {
711    set w .ogg
712    catch {destroy $w}
713    toplevel $w
714    wm title $w "Ogg Vorbis Settings"
715
716    pack [frame $w.f1] -anchor w
717    pack [label $w.f1.l -text "Nominal bitrate:" -widt 16 -anchor w] -side left
718    pack [entry $w.f1.e -textvar ::ogg(nombr) -wi 7] -side left
719
720    pack [frame $w.f2] -anchor w
721    pack [label $w.f2.l -text "Max bitrate:" -width 16 -anchor w] -side left
722    pack [entry $w.f2.e -textvar ::ogg(maxbr) -wi 7] -side left
723
724    pack [frame $w.f3] -anchor w
725    pack [label $w.f3.l -text "Min bitrate:" -width 16 -anchor w] -side left
726    pack [entry $w.f3.e -textvar ::ogg(minbr) -wi 7] -side left
727
728    pack [frame $w.f4] -anchor w
729    pack [label $w.f4.l -text "Comment:" -width 16 -anchor w] -side left
730    pack [entry $w.f4.e -textvar ::ogg(com) -wi 40] -side left
731
732    pack [frame $w.f5] -anchor w
733    pack [checkbutton $w.f5.b -text "Query settings before saving" \
734	-variable ::ogg(query) -anchor w] -side left
735
736    pack [frame $w.fb] -side bottom -fill x
737    pack [button $w.fb.cb -text $text -command "destroy $w"] -side top
738  }
739}
740
741snack::addExtTypes $extTypes
742snack::addLoadTypes $loadTypes $loadKeys
743
744proc GetOpenFileName {} {
745    global f v
746
747    if {$v(smpchanged) || $v(labchanged)} {
748	if {[tk_messageBox -message "You have unsaved changes.\n Do you \
749		really want to close?" -type yesno \
750		-icon question] == "no"} return
751    }
752
753    set gotfn [snack::getOpenFile -initialdir $f(spath) \
754	    -initialfile $f(sndfile) -format $v(smpfmt)]
755
756    # Ugly hack for Tk8.0
757    if {$gotfn != ""} {
758	set tmp [file split $gotfn]
759	if {[lindex $tmp 0] == [lindex $tmp 1]} {
760	    set tmp [lreplace $tmp 0 0]
761	    set gotfn [eval file join $tmp]
762	}
763    }
764    update
765    if [string compare $gotfn ""] {
766	OpenFiles $gotfn
767    }
768}
769
770proc GetSaveFileName {title} {
771    global f v labels
772
773    if {$labels != {} && [string compare $title "Save sample file"] != 0} {
774	switch $v(labfmt) {
775	    MIX {
776	      lappend ::saveTypes {{MIX Files} {.mix}}
777	      lappend ::saveKeys  MIX
778	    }
779	    HTK {
780	      lappend ::saveTypes {{HTK Label Files} {.lab}}
781	      lappend ::saveKeys  HTK
782	    }
783	    TIMIT {
784	      lappend ::saveTypes {{TIMIT Label Files} {.phn}} {{TIMIT Label Files} {.wrd}}
785	      lappend ::saveKeys  TIMIT
786	    }
787	    WAVES {
788	      lappend ::saveTypes {{Waves Label Files} {.lab}}
789	      lappend ::saveKeys  WAVES
790	    }
791	    default
792	}
793	snack::addSaveTypes $::saveTypes $::saveKeys
794
795	set gotfn [snack::getSaveFile -initialdir $f(lpath) -initialfile $f(labfile) -format $v(labfmt) -title $title]
796 } else {
797	snack::addSaveTypes $::saveTypes $::saveKeys
798
799	set gotfn [snack::getSaveFile -initialdir $f(spath) -initialfile $f(sndfile) -format $v(smpfmt) -title $title]
800    }
801#    set tmp [string trimright $f(lpath) /].
802#    if {[regexp $tmp $gotfn] == 1 && $tmp != "."} {
803#	return ""
804#    }
805    update
806    return $gotfn
807}
808
809proc SaveAs {} {
810    set gotfn [GetSaveFileName ""]
811    if {[string compare $gotfn ""] != 0} {
812	SaveFile $gotfn
813    }
814}
815
816proc Save {} {
817    global f v
818
819    set fn $f(spath)$f(sndfile)
820    if {[string compare $f(spath)$f(sndfile) ""] == 0} {
821	set fn [GetSaveFileName "Save sample file"]
822    }
823    if {$fn != "" && $v(smpchanged)} {
824	SaveFile $fn
825    }
826    if $v(labchanged) {
827	set fn $f(lpath)$f(labfile)
828	if {[string compare $f(lpath)$f(labfile) ""] == 0} {
829	    set fn [GetSaveFileName "Save label file"]
830	}
831	if {$fn != ""} {
832	    SaveFile $fn
833	}
834    }
835}
836
837proc SaveFile {{fn ""}} {
838  global f v labels
839
840  SetCursor watch
841  set strip_fn [lindex [file split [file rootname $fn]] end]
842  set ext  [file extension $fn]
843  if [string match macintosh $::tcl_platform(platform)] {
844    set path [file dirname $fn]:
845  } else {
846    set path [file dirname $fn]/
847  }
848  if {$path == "./"} { set path ""}
849  if {![IsLabelFile $fn]} {
850    if {[info exists snack::snackogg]} {
851      if {$::ogg(query) && [string match -nocase .ogg $ext]} {
852	OggSettings Continue
853	tkwait window .ogg
854      }
855      if [catch {snd write $fn -progress snack::progressCallback \
856	  -nominalbitrate $::ogg(nombr) -maxbitrate $::ogg(maxbr) \
857	  -minbitrate $::ogg(minbr) -comment $::ogg(com)} msg] {
858	SetMsg "Save cancelled: $msg"
859      }
860    } else {
861      if [catch {snd write $fn -progress snack::progressCallback} msg] {
862	SetMsg "Save cancelled: $msg"
863      }
864    }
865    if {$v(linkfile)} {
866	snd configure -file $fn
867    }
868    set v(smpchanged) 0
869    wm title . "xs: $fn"
870    set f(spath) $path
871    set f(sndfile) $strip_fn$ext
872  } elseif {$labels != {}} {
873    SaveLabelFile $labels $fn
874    set v(labchanged) 0
875    wm title . "xs: $f(spath)$f(sndfile) - $fn"
876    set f(lpath) $path
877    set f(labfile) $strip_fn$ext
878  }
879  SetCursor ""
880}
881
882proc IsLabelFile {fn} {
883    set ext [file extension $fn]
884    if {$ext == ".lab"} { return 1 }
885    if {$ext == ".mix"} { return 1 }
886    if {$ext == ".phn"} { return 1 }
887    if {$ext == ".wrd"} { return 1 }
888    return 0
889}
890
891proc OpenFiles {fn} {
892    global c labels v f
893
894
895    if {![file readable $fn]} {
896	tk_messageBox -icon warning -type ok -message "No such file: $fn"
897	return
898    }
899    SetCursor watch
900    set strip_fn [lindex [file split [file rootname $fn]] end]
901    set ext  [file extension $fn]
902    if [string match macintosh $::tcl_platform(platform)] {
903	set path [file dirname $fn]:
904    } else {
905	set path [file dirname $fn]/
906    }
907    if {$path == "./"} { set path ""}
908
909    if [IsLabelFile $fn] {
910	set type "lab"
911	set f(lpath) $path
912    } else {
913	set type "smp"
914	set f(spath) $path
915    }
916
917    switch $ext {
918	.mix {
919	    set f(labfile) "$strip_fn.mix"
920	    set v(labfmt) MIX
921	    if $v(autoload) {
922		set f(sndfile) "$strip_fn"
923		if {$f(spath) == ""} { set f(spath) $f(lpath) }
924		if {[file exists $f(spath)$f(sndfile)] == 0} {
925		    set f(sndfile) "$strip_fn.smp"
926		}
927	    }
928	}
929	.lab {
930	    set f(labfile) "$strip_fn.lab"
931	    if {$v(smpfmt) == "SD"} {
932		set v(labfmt) WAVES
933		set v(labalign) e
934		if $v(autoload) {
935		    set f(sndfile) "$strip_fn.sd"
936		    if {$f(spath) == ""} { set f(spath) $f(lpath) }
937		}
938	    } else {
939		set v(labfmt) HTK
940		if $v(autoload) {
941		    set f(sndfile) "$strip_fn.smp"
942		    if {$f(spath) == ""} { set f(spath) $f(lpath) }
943		}
944	    }
945	}
946	.phn {
947	    set f(labfile) "$strip_fn.phn"
948	    set v(labfmt) TIMIT
949	    if $v(autoload) {
950		set f(sndfile) "$strip_fn.wav"
951		if {$f(spath) == ""} { set f(spath) $f(lpath) }
952	    }
953	}
954	.wrd {
955	    set f(labfile) "$strip_fn.wrd"
956	    set v(labfmt) TIMIT
957	    if $v(autoload) {
958		set f(sndfile) "$strip_fn.wav"
959		if {$f(spath) == ""} { set f(spath) $f(lpath) }
960	    }
961	}
962	.smp {
963	    set f(sndfile) "$strip_fn.smp"
964	    set v(labfmt) MIX
965	    if $v(autoload) {
966		set f(labfile) "$strip_fn.smp.mix"
967		if {$f(lpath) == ""} { set f(lpath) $f(spath) }
968		if {[file exists $f(lpath)$f(labfile)] == 0} {
969		    set f(labfile) "$strip_fn.mix"
970		}
971	    }
972	}
973	.wav {
974	    set f(sndfile) "$strip_fn.wav"
975	    set v(labfmt) TIMIT
976	    if $v(autoload) {
977		set f(labfile) "$strip_fn.phn"
978		if {$f(lpath) == ""} { set f(lpath) $f(spath) }
979	    }
980	}
981	.sd {
982	    set f(sndfile) "$strip_fn.sd"
983	    set v(labfmt) WAVES
984	    if $v(autoload) {
985		set f(labfile) "$strip_fn.lab"
986		if {$f(lpath) == ""} { set f(lpath) $f(spath) }
987	    }
988	}
989	.bin {
990	    set f(sndfile) "$strip_fn.bin"
991	    set v(labfmt) HTK
992	    if $v(autoload) {
993		set f(labfile) "$strip_fn.lab"
994		if {$f(lpath) == ""} { set f(lpath) $f(spath) }
995	    }
996	}
997	default {
998	    if {$type == "smp"} {
999		set f(sndfile) "$strip_fn$ext"
1000		if $v(autoload) {
1001		    set f(labfile) "$strip_fn$ext.mix"
1002		    set v(labfmt) MIX
1003		    if {$f(lpath) == ""} { set f(lpath) $f(spath) }
1004		}
1005	    } else {
1006		set f(labfile) "$strip_fn$ext"
1007		if $v(autoload) {
1008		    set f(sndfile) "$strip_fn.smp"
1009		    if {$f(spath) == ""} { set f(spath) $f(lpath) }
1010		}
1011	    }
1012	}
1013    }
1014
1015    if {($v(autoload) == 1) || ($type == "smp")} {
1016	$c delete wave speg
1017	.of.c delete overwave
1018	catch {.sect.c delete sect}
1019	StopPlay
1020
1021	set f(byteOrder) [snd cget -byteorder]
1022	set tmps [snack::sound -debug $::debug]
1023	set ffmt [$tmps read $f(spath)$f(sndfile) -end 1 -guessproperties 1]
1024	if {$ffmt == "RAW"} {
1025	    set v(rate)      [$tmps cget -rate]
1026	    set v(sfmt)      [$tmps cget -encoding]
1027	    set v(chan)      [$tmps cget -channels]
1028	    set f(byteOrder) [$tmps cget -byteorder]
1029	    if {[InterpretRawDialog] == "cancel"} {
1030		$tmps destroy
1031		SetCursor ""
1032		return
1033	    }
1034	}
1035	$tmps destroy
1036	if {$v(linkfile)} {
1037	    if [catch {snd configure -file $f(spath)$f(sndfile) \
1038		    -skip $f(skip) -byteorder $f(byteOrder) \
1039		    -rate $v(rate) -encoding $v(sfmt) -channels $v(chan) \
1040	    	     } ret] {
1041		 SetMsg "$ret"
1042		 return
1043	     }
1044	     set v(smpfmt) [lindex [snd info] 6]
1045	} else {
1046	    if [catch {set v(smpfmt) [snd read $f(spath)$f(sndfile) \
1047		    -skip $f(skip) -byteorder $f(byteOrder) \
1048		    -rate $v(rate) -encoding $v(sfmt) -channels $v(chan) \
1049		    -progress snack::progressCallback]} ret] {
1050		SetMsg "$ret"
1051		return
1052	    }
1053	}
1054	set v(rate) [snd cget -rate]
1055	set v(sfmt) [snd cget -encoding]
1056	set v(chan) [snd cget -channels]
1057	set v(startsmp) 0
1058	if {[snd cget -channels] == 1} {
1059	    set v(vchan) -1
1060	}
1061	set v(smpchanged) 0
1062	.tb.undo config -state disabled
1063	if {![regexp $v(rate) [snack::audio rates]]} {
1064	    tk_messageBox -icon warning -type ok -message "You need to \
1065		    convert this sound\nif you want to play it"
1066	}
1067    }
1068    if {($v(autoload) == 1) || ($type == "lab")} {
1069	set labels [OpenLabelFile $f(lpath)$f(labfile)]
1070	if {$labels == {}} { set f(labfile) "" }
1071    }
1072    if {$labels == {}} {
1073	wm title . "xs: $f(spath)$f(sndfile)"
1074    } else {
1075	wm title . "xs: $f(spath)$f(sndfile) - $f(lpath)$f(labfile)"
1076    }
1077
1078    if {[snd length -unit seconds] > 50 && $v(pps) > 100} {
1079	set v(pps) [expr $v(pps)/10]
1080    }
1081    if {[snd length -unit seconds] < 50 && $v(pps) < 100} {
1082	set v(pps) [expr $v(pps)*10]
1083    }
1084    wm geometry . {}
1085    Redraw
1086    event generate .cf.fc.c <Configure>
1087    SetMsg [InfoStr nopath]
1088#    MarkAll
1089    RecentFile $f(spath)$f(sndfile)
1090}
1091
1092proc InterpretRawDialog {} {
1093    global f v
1094
1095    set w .rawDialog
1096    toplevel $w -class Dialog
1097    frame $w.q
1098    pack $w.q -expand 1 -fill both -side top
1099    pack [frame $w.q.f1] -side left -anchor nw -padx 3m -pady 2m
1100    pack [frame $w.q.f2] -side left -anchor nw -padx 3m -pady 2m
1101    pack [frame $w.q.f3] -side left -anchor nw -padx 3m -pady 2m
1102    pack [frame $w.q.f4] -side left -anchor nw -padx 3m -pady 2m
1103    pack [label $w.q.f1.l -text "Sample Rate"]
1104    foreach e [snack::audio rates] {
1105	pack [radiobutton $w.q.f1.r$e -text $e -val $e -var ::v(rate)]\
1106		-anchor w
1107    }
1108    pack [label $w.q.f2.l -text "Sample Encoding"]
1109    foreach e [snack::audio encodings] {
1110	pack [radiobutton $w.q.f2.r$e -text $e -val $e -var ::v(sfmt)]\
1111		-anchor w
1112    }
1113    pack [label $w.q.f3.l -text Channels]
1114    pack [radiobutton $w.q.f3.r1 -text Mono -val 1 -var ::v(chan)] -anchor w
1115    pack [radiobutton $w.q.f3.r2 -text Stereo -val 2 -var ::v(chan)] -anchor w
1116    pack [radiobutton $w.q.f3.r4 -text 4 -val 4 -var ::v(chan)] -anchor w
1117    pack [entry $w.q.f3.e -textvariable ::v(chan) -width 3] -anchor w
1118    pack [label $w.q.f4.l -text "Byte Order"]
1119    pack [radiobutton $w.q.f4.ri -text "Little Endian\n(Intel)" \
1120	    -value littleEndian -var ::f(byteOrder)] -anchor w
1121    pack [radiobutton $w.q.f4.rm -text "Big Endian\n(Motorola)" \
1122	    -value bigEndian -var ::f(byteOrder)] -anchor w
1123    pack [label $w.q.f4.l2 -text "\nRead Offset (bytes)"]
1124    pack [entry $w.q.f4.e -textvar f(skip) -wi 6]
1125    snack::makeDialogBox $w -title "Interpret Raw File As" -type okcancel \
1126	-default ok
1127}
1128
1129proc Link2File {} {
1130    global f v
1131
1132    StopPlay
1133    if {$v(smpchanged)} {
1134	if {[tk_messageBox -message "You have unsaved changes.\n Do you \
1135		really want to loose them?" -type yesno \
1136		-icon question] == "no"} return
1137    }
1138    set v(smpchanged) 0
1139    if {$v(linkfile)} {
1140	.of.c delete overwave
1141	catch {.sect.c delete sect}
1142	if {$f(sndfile) == ""} {
1143	    snd configure -file _xs[pid].wav
1144	} else {
1145	    snd configure -file $f(spath)$f(sndfile)
1146	}
1147	cbs configure -file ""
1148    } else {
1149	if {$f(sndfile) == ""} {
1150	    snd config -load ""
1151	} else {
1152	    snd config -load $f(spath)$f(sndfile)
1153	}
1154	cbs config -load ""
1155    }
1156}
1157
1158proc ConfigEditMenu {} {
1159    global v
1160
1161    if {$v(linkfile)} {
1162	snack::menuEntryOff Edit Cut
1163	snack::menuEntryOff Edit Copy
1164	snack::menuEntryOff Edit Paste
1165	snack::menuEntryOff Edit Crop
1166    } else {
1167	snack::menuEntryOn Edit Cut
1168	snack::menuEntryOn Edit Copy
1169	snack::menuEntryOn Edit Paste
1170	snack::menuEntryOn Edit Crop
1171    }
1172    if {$v(smpchanged)} {
1173	snack::menuEntryOn Edit Undo
1174    } else {
1175	snack::menuEntryOff Edit Undo
1176    }
1177}
1178
1179proc ConfigTransformMenu {} {
1180    global v
1181
1182    if {$v(linkfile)} {
1183	snack::menuEntryOff Transform Conversions
1184	snack::menuEntryOff Transform Amplify...
1185	snack::menuEntryOff Transform Normalize...
1186	snack::menuEntryOff Transform Echo...
1187	snack::menuEntryOff Transform {Mix Channels...}
1188	snack::menuEntryOff Transform Invert
1189	snack::menuEntryOff Transform Reverse
1190	snack::menuEntryOff Transform Silence
1191	snack::menuEntryOff Transform {Remove DC}
1192    } else {
1193	snack::menuEntryOn Transform Conversions
1194	snack::menuEntryOn Transform Amplify...
1195	snack::menuEntryOn Transform Normalize...
1196	snack::menuEntryOn Transform Echo...
1197	snack::menuEntryOn Transform {Mix Channels...}
1198	snack::menuEntryOn Transform Invert
1199	snack::menuEntryOn Transform Reverse
1200	snack::menuEntryOn Transform Silence
1201	snack::menuEntryOn Transform {Remove DC}
1202    }
1203    if {[snd cget -channels] == 1} {
1204	snack::menuEntryOff Transform {Mix Channels...}
1205    }
1206}
1207
1208proc ConfigOptionsMenu {} {
1209    global v
1210
1211    if {[snd cget -channels] == 1} {
1212	snack::menuEntryOff Options {View Channel}
1213    } else {
1214	snack::menuEntryOn Options {View Channel}
1215    }
1216}
1217
1218proc OpenLabelFile {fn} {
1219    global f v undo
1220
1221    if [catch {open $fn} in] {
1222	SetMsg $in
1223	return {}
1224    } else {
1225	if [catch {set labelfile [read $in]}] { return {} }
1226	set l {}
1227	set undo {}
1228	set v(labchanged) 0
1229	.tb.undo config -state disabled
1230	close $in
1231	switch $v(labfmt) {
1232	    TIMIT -
1233	    HTK {
1234		foreach row [split $labelfile \n] {
1235		    set rest ""
1236		    if {[scan $row {%d %d %s %[^�]} start stop label rest] >= 3} {
1237			lappend l "$start\n$stop\n$label\n$rest"
1238		    }
1239		}
1240	    }
1241	    MIX {
1242		set f(header) ""
1243		set getHead 1
1244		foreach row [split $labelfile \n] {
1245		    if [string match FR* $row] {
1246			set getHead 0
1247			set rest ""
1248			if {[scan $row {%s %d %s %[^�]} junk start label rest] >= 3} {
1249			    lappend l "$start\n$label\n$rest"
1250			}
1251		    } else {
1252			if {$getHead == 1} {
1253			    set f(header) [lappend f(header) $row]
1254			}
1255		    }
1256		}
1257	    }
1258	    WAVES {
1259		set f(header) ""
1260		set getHead 1
1261		foreach row [split $labelfile \n] {
1262		    if {$getHead == 1} {
1263			set f(header) [lappend f(header) $row]
1264			if [string match # $row] { set getHead 0 }
1265			continue
1266		    }
1267		    set rest ""
1268		    if {[scan $row {%f %d %s %[^�]} end color label rest] >= 3} {
1269			lappend l "$end\n$color\n$label\n$rest"
1270		    }
1271		}
1272	    }
1273	}
1274    }
1275    return $l
1276}
1277
1278proc SaveLabelFile {labels fn} {
1279    global f v
1280
1281    if {$fn == "" || [regexp /$ $fn] == 1 || $labels == {}} return
1282    set f(labfile) [file tail $fn]
1283    if [string match macintosh $::tcl_platform(platform)] {
1284	set f(lpath) [file dirname $fn]:
1285    } else {
1286	set f(lpath) [file dirname $fn]/
1287    }
1288    catch {file copy $fn $fn~}
1289    if [catch {open $fn w} out] {
1290	SetMsg $out
1291        return
1292    } else {
1293	set v(labchanged) 0
1294	fconfigure $out -translation {auto lf}
1295	switch $v(labfmt) {
1296	    TIMIT -
1297	    HTK {
1298		foreach row $labels {
1299		    puts $out [join $row " "]
1300		}
1301	    }
1302	    MIX {
1303		if {$f(header) != ""} {
1304		    puts $out [join $f(header) \n]
1305		} else {
1306		    puts $out "NOLABELS\nTEXT: \nCT 1"
1307		}
1308		foreach row $labels {
1309		    set t4 [split $row \n]
1310		    set lab [lindex $t4 1]
1311		    if {[string compare $lab "OK"] == 0} {
1312		    } elseif {[string index $lab 0] == "$"} {
1313		    } elseif {[string index $lab 0] == "#"} {
1314		    } else {
1315			set t4 [lreplace $t4 1 1 "\$$lab"]
1316		    }
1317		    set t5 [join $t4 "\t"]
1318		    puts $out "FR\t$t5"
1319		}
1320	    }
1321	    WAVES {
1322		if {$f(header) != ""} {
1323		    puts $out [join $f(header) \n]
1324		} else {
1325		    set name [lindex [file split [file rootname $fn]] end]
1326		    set date [clock format [clock seconds] -format "%a %b %d %H:%M:%S %Y"]
1327		    puts $out "signal $name"
1328		    puts $out "type 0\ncolor 121"
1329		    puts $out "comment created using xs $date"
1330		    puts $out "font -misc-*-bold-*-*-*-15-*-*-*-*-*-*-*"
1331		    puts $out "separator ;\nnfields 1\n#"
1332		}
1333		foreach row $labels {
1334		    set rest ""
1335		    scan $row {%f %d %s %[^�]} end color label rest
1336		    puts $out [format "    %.6f  %d %s %s" $end $color $label $rest]
1337		}
1338	    }
1339	}
1340	close $out
1341    }
1342    SetMsg "Saved: $fn"
1343}
1344
1345proc SaveMark {} {
1346    global f v labels
1347
1348    set start [Marker2Sample m1]
1349    set end   [Marker2Sample m2]
1350
1351    set gotfn [snack::getSaveFile -initialdir $f(spath) -format $v(smpfmt)]
1352
1353    if [string compare $gotfn ""] {
1354	SetMsg "Saving range: $start $end"
1355
1356	set ext [file extension $gotfn]
1357	set root [file rootname $gotfn]
1358	if {$root == $gotfn} {
1359	    set fn $root[file extension $f(sndfile)]
1360	} else {
1361	    set fn $gotfn
1362	}
1363	if [catch {snd write $fn -start $start -end $end \
1364		-progress snack::progressCallback}] {
1365	    SetMsg "Save cancelled"
1366	}
1367	if {$labels != {}} {
1368	    set fn $root[file extension $f(labfile)]
1369	    switch $v(labfmt) {
1370		WAVES -
1371		HTK {
1372		    SaveLabelFile [CropLabels [Marker2Time m1] [Marker2Time m2]] $fn
1373		}
1374		TIMIT -
1375		MIX {
1376		    SaveLabelFile [CropLabels $start $end] $fn
1377		}
1378	    }
1379	}
1380    }
1381}
1382
1383proc Close {} {
1384    global labels f v c
1385
1386    if {$v(smpchanged) || $v(labchanged)} {
1387	if {[tk_messageBox -message "You have unsaved changes.\n Do you \
1388		really want to close?" -type yesno \
1389		-icon question] == "no"} return
1390    }
1391    StopPlay
1392    set labels {}
1393    set v(smpchanged) 0
1394    set v(labchanged) 0
1395    set undo {}
1396    .tb.undo config -state disabled
1397    set f(labfile) ""
1398    set f(sndfile) ""
1399    wm title . "xs:"
1400    if {$v(linkfile)} {
1401	catch {file delete -force _xs[pid].wav}
1402	snd configure -file _xs[pid].wav
1403    } else {
1404	snd flush
1405    }
1406    Redraw
1407}
1408
1409proc Exit {} {
1410    global v
1411
1412    if {$v(smpchanged) || $v(labchanged)} {
1413	if {[tk_messageBox -message \
1414		"You have unsaved changes.\n Do you really want to quit?" \
1415		-type yesno -icon question] == "no"} {
1416	    return
1417	}
1418    }
1419    catch {file delete -force _xs[pid].wav}
1420    exit
1421}
1422
1423proc OpenGetURLWindow {} {
1424    global f v
1425
1426    if {$v(linkfile)} {
1427	tk_messageBox -icon warning -type ok -message "This function not \
1428		available\nwhen using link to disk file."
1429	return
1430    }
1431
1432    set w .geturl
1433    catch {destroy $w}
1434    toplevel $w
1435    wm title $w {Get URL}
1436    wm geometry $w [xsGetGeometry]
1437
1438    set f(url) $f(http)
1439    pack [frame $w.f]
1440    pack [label $w.f.l -text {Enter the World Wide Web location (URL):}]
1441    pack [entry $w.f.e -width 60 -textvar f(url)]
1442    pack [frame $w.f2]
1443    pack [button $w.f2.b1 -text Get -command GetURL] -side left
1444    bind $w.f.e <Key-Return> GetURL
1445    pack [button $w.f2.b2 -text Stop -command StopURL] -side left
1446    pack [button $w.f2.b3 -text Close -command [list destroy $w]] -side left
1447}
1448
1449proc GetURL {} {
1450    global c f
1451
1452    SetCursor watch
1453    $c delete wave speg tran
1454    StopPlay
1455    StopURL
1456    set f(urlToken) [::http::geturl $f(url) -command URLcallback -progress Progress]
1457    set c .cf.fc.c
1458    SetMsg "Fetching: $f(url)"
1459}
1460
1461proc Progress {token total current} {
1462    if {$total > 0} {
1463	set p [expr {int(100 * $current/$total)}]
1464	SetMsg "Fetched $current bytes ($p%)"
1465    } else {
1466	SetMsg "Fetched $current bytes"
1467    }
1468}
1469
1470proc URLcallback {token} {
1471    global f v labels
1472    upvar #0 $token state
1473
1474    SetCursor ""
1475    if {$state(status) != "ok"} {
1476	return
1477    }
1478    if {[string match *200* [::http::code $token]] == 1} {
1479	snd data [::http::data $token]
1480	set f(sndfile) ""
1481	set f(labfile) ""
1482	set v(rate) [snd cget -rate]
1483	set v(sfmt) [snd cget -encoding]
1484	set v(startsmp) 0
1485	set labels {}
1486	wm title . "xs: $f(url)"
1487	Redraw
1488	event generate .cf.fc.c <Configure>
1489	MarkAll
1490	SetMsg [InfoStr nopath]
1491    } else {
1492	SetMsg [::http::code $token]
1493    }
1494    set f(urlToken) ""
1495}
1496
1497proc StopURL {} {
1498    global f v
1499
1500    if {$f(urlToken) != ""} {
1501	::http::reset $f(urlToken)
1502    }
1503    set f(urlToken) ""
1504    SetMsg "Transfer interrupted."
1505    SetCursor ""
1506}
1507
1508proc Crop {} {
1509    global labels v
1510
1511    set start [Marker2Sample m1]
1512    set end   [Marker2Sample m2]
1513    if {$start == $end} return
1514    SetMsg "Cropping to range: $start $end"
1515
1516    cbs copy snd -end [expr {$start - 1}]
1517    cbs insert snd [cbs length] -start [expr {$end + 1}]
1518    snd crop $start $end
1519
1520    set v(undoc) "snd insert cbs 0 -end [expr {$start-1}];snd insert cbs [expr {$end+1}] -start $start"
1521    set v(redoc) "snd crop $start $end"
1522    set v(smpchanged) 1
1523
1524    if {[llength $labels] != 0} {
1525	switch $v(labfmt) {
1526	    WAVES -
1527	    HTK {
1528		set labels [CropLabels [Marker2Time m1] [Marker2Time m2]]
1529	    }
1530	    TIMIT -
1531	    MIX {
1532		set labels [CropLabels $start $end]
1533	    }
1534	}
1535	set v(labchanged) 1
1536    }
1537    PutMarker m1 [DTime2Time 0.0] 0 0
1538    PutMarker m2 [DTime2Time [snd length -unit seconds]] 0 0
1539    .tb.undo config -state normal
1540    DrawOverAxis
1541    Redraw
1542}
1543
1544proc Reverse {} {
1545    global v
1546
1547    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1548    set start [Marker2Sample m1]
1549    set end   [Marker2Sample m2]
1550    SetMsg "Reversing range: $start $end"
1551
1552    cbs copy snd
1553    if [catch {snd reverse -start $start -end $end \
1554	    -progress snack::progressCallback}] {
1555	SetMsg "Reverse cancelled"
1556	snd copy cbs
1557	return
1558    }
1559
1560    set v(undoc) "snd reverse -start $start -end $end"
1561    set v(redoc) "snd reverse -start $start -end $end"
1562    set v(smpchanged) 1
1563    .tb.undo config -state normal
1564    Redraw
1565}
1566
1567proc Invert {} {
1568    global v filt
1569
1570    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1571    set start [Marker2Sample m1]
1572    set end   [Marker2Sample m2]
1573    SetMsg "Inverting range: $start $end"
1574
1575    $filt(f) configure -1.0
1576
1577    cbs copy snd
1578    if [catch {snd filter $filt(f) -start $start -end $end \
1579	    -progress snack::progressCallback}] {
1580	SetMsg "Invert cancelled"
1581	snd copy cbs
1582	return
1583    }
1584
1585    set v(undoc) "snd swap cbs"
1586    set v(redoc) "snd swap cbs"
1587    set v(smpchanged) 1
1588    .tb.undo config -state normal
1589    Redraw
1590}
1591
1592proc Silence {} {
1593    global v filt
1594
1595    set start [Marker2Sample m1]
1596    set end   [Marker2Sample m2]
1597    if {$start == $end} return
1598    SetMsg "Silencing range: $start $end"
1599
1600    $filt(f) configure 0.0
1601
1602    cbs copy snd
1603    if [catch {snd filter $filt(f) -start $start -end $end \
1604	    -progress snack::progressCallback}] {
1605	SetMsg "Silence cancelled"
1606	snd copy cbs
1607	return
1608    }
1609
1610    set v(undoc) "snd swap cbs"
1611    set v(redoc) "snd swap cbs"
1612    set v(smpchanged) 1
1613    .tb.undo config -state normal
1614    Redraw
1615}
1616
1617proc RemoveDC {} {
1618    global v remdc
1619
1620    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1621    set start [Marker2Sample m1]
1622    set end   [Marker2Sample m2]
1623    if {$start == $end} return
1624    SetMsg "Removing DC for range: $start $end"
1625
1626    cbs copy snd
1627    if [catch {snd filter $remdc(f) -start $start -end $end \
1628	    -progress snack::progressCallback -continuedrain 0}] {
1629	SetMsg "Remove DC cancelled"
1630	snd copy cbs
1631	return
1632    }
1633
1634    set v(undoc) "snd swap cbs"
1635    set v(redoc) "snd swap cbs"
1636    set v(smpchanged) 1
1637    .tb.undo config -state normal
1638    Redraw
1639}
1640
1641proc ConfAmplify {flag} {
1642    global amplify
1643
1644    set w .amp
1645    if {$amplify(db) == 1} {
1646	$w.f.l configure -text dB
1647	set tmp [expr {20.0*log10(($amplify(v)+0.000000000000000001)/100.0)}]
1648	$w.f.s1 configure -from -96.0 -to 24.0
1649    } else {
1650	$w.f.l configure -text %
1651	set tmp [expr {100.0*pow(10,$amplify(v)/20.0)}]
1652	$w.f.s1 configure -from 0.0 -to 300.0
1653    }
1654    if {$flag} {
1655	set amplify(v) $tmp
1656    }
1657}
1658
1659proc DoAmplify {} {
1660    global v amplify
1661
1662    set start [Marker2Sample m1]
1663    set end   [Marker2Sample m2]
1664    if {$start == $end} return
1665    SetMsg "Amplifying range: $start $end"
1666
1667    if {$amplify(db) == 1} {
1668	set tmp [expr {pow(10,$amplify(v)/20.0)}]
1669    } else {
1670	set tmp [expr {$amplify(v) / 100.0}]
1671    }
1672    $amplify(f) configure $tmp
1673
1674    cbs copy snd
1675    if [catch {snd filter $amplify(f) -start $start -end $end \
1676	    -progress snack::progressCallback}] {
1677	SetMsg "Amplify cancelled"
1678	snd copy cbs
1679	return
1680    }
1681
1682    set v(undoc) "snd swap cbs"
1683    set v(redoc) "snd swap cbs"
1684    set v(smpchanged) 1
1685    .tb.undo config -state normal
1686    Redraw
1687}
1688
1689proc Amplify {} {
1690    global amplify
1691
1692    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1693    set w .amp
1694    catch {destroy $w}
1695    toplevel $w
1696    wm title $w {Amplify}
1697
1698    pack [ label $w.l -text "Amplify by:"]
1699    pack [ frame $w.f] -fill both -expand true
1700    pack [ scale $w.f.s1 -command "" -orient horizontal \
1701	    -resolution .1 -showvalue 0 \
1702	    -variable amplify(v)] -side left
1703    pack [entry $w.f.e -textvariable amplify(v) -width 5] -side left
1704    pack [label $w.f.l -text xx -width 2] -side left
1705    pack [checkbutton $w.cb -text "Decibels" -variable amplify(db) \
1706	    -command [list ConfAmplify 1]]
1707    pack [ frame $w.f3]
1708    pack [ button $w.f3.b1 -text OK -width 6 \
1709	    -command "DoAmplify;destroy $w"] -side left
1710    pack [ button $w.f3.b2 -text Cancel -command "destroy $w"] -side left
1711    ConfAmplify 0
1712}
1713
1714proc ConfNormalize {flag} {
1715    global normalize
1716
1717    set w .norm
1718    if {$normalize(db) == 1} {
1719	$w.f.l configure -text dB
1720	set tmp [expr {20.0*log10(($normalize(v)+0.000000000000000001)/100.0)}]
1721	$w.f.s1 configure -from -96.0 -to 0.0
1722    } else {
1723	$w.f.l configure -text %
1724	set tmp [expr {100.0*pow(10,$normalize(v)/20.0)}]
1725	$w.f.s1 configure -from 0.0 -to 100.0
1726    }
1727    if {$flag} {
1728	set normalize(v) $tmp
1729    }
1730    if {[snd cget -channels] == 1} {
1731	$w.cb2 configure -state disabled
1732    } else {
1733	$w.cb2 configure -state normal
1734    }
1735}
1736
1737proc DoNormalize {} {
1738    global v normalize
1739
1740    set start [Marker2Sample m1]
1741    set end   [Marker2Sample m2]
1742    if {$start == $end} return
1743    SetMsg "Normalizing range: $start $end"
1744
1745    if {$normalize(db) == 1} {
1746	set tmp [expr {pow(10,$normalize(v)/20.0)}]
1747    } else {
1748	set tmp [expr {$normalize(v) / 100.0}]
1749    }
1750    if {[string match [snd cget -encoding] Lin8]} {
1751	set smax 255.0
1752    } elseif {[string match [snd cget -encoding] Lin24]} {
1753        set smax 8388608.0
1754    } else {
1755	set smax 32767.0
1756    }
1757    for {set c 0} {$c < [snd cget -channels]} {incr c} {
1758	if {$normalize(allEqual)} {
1759         set max [snd max -start $start -end $end]
1760         set min [snd min -start $start -end $end]
1761	} else {
1762  	 set max [snd max -start $start -end $end -channel $c]
1763	 set min [snd min -start $start -end $end -channel $c]
1764	}
1765	if {$max < -$min} {
1766	    set max [expr {-$min}]
1767	    if {$max > $smax} {
1768		set max $smax
1769	    }
1770	}
1771	if {$max == 0} {
1772	    set max 1.0
1773	}
1774	set factor [expr {$tmp * $smax / $max}]
1775	lappend factors $factor
1776	if {$normalize(allEqual)} break
1777	if {$c < [expr {[snd cget -channels] - 1}]} {
1778	    for {set i 0} {$i < [snd cget -channels]} {incr i} {
1779		    lappend factors 0.0
1780	    }
1781	}
1782    }
1783    eval $normalize(f) configure $factors
1784
1785    cbs copy snd
1786    if [catch {snd filter $normalize(f) -start $start -end $end \
1787	    -progress snack::progressCallback}] {
1788	SetMsg "Normalize cancelled"
1789	snd copy cbs
1790	return
1791    }
1792
1793    set v(undoc) "snd swap cbs"
1794    set v(redoc) "snd swap cbs"
1795    set v(smpchanged) 1
1796    .tb.undo config -state normal
1797    Redraw
1798}
1799
1800proc Normalize {} {
1801    global normalize
1802
1803    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1804    set w .norm
1805    catch {destroy $w}
1806    toplevel $w
1807    wm title $w {Normalize}
1808
1809    pack [ label $w.l -text "Normalize to:"]
1810    pack [ frame $w.f] -fill both -expand true
1811    pack [ scale $w.f.s1 -command "" -orient horizontal \
1812	    -resolution .1 -showvalue 0 \
1813	    -variable normalize(v)] -side left
1814    pack [entry $w.f.e -textvariable normalize(v) -width 5] -side left
1815    pack [label $w.f.l -text xx -width 2] -side left
1816    pack [checkbutton $w.cb1 -text "Decibels" -variable normalize(db) \
1817	    -command [list ConfNormalize 1] -anchor w] -fill both -expand true
1818    pack [checkbutton $w.cb2 -text "Normalize all channels equally" \
1819	    -variable normalize(allEqual) -anchor w] -fill both -expand true
1820    pack [ frame $w.f3]
1821    pack [ button $w.f3.b1 -text OK -width 6 \
1822	    -command "DoNormalize;destroy $w"] -side left
1823    pack [ button $w.f3.b2 -text Cancel -command "destroy $w"] -side left
1824    ConfNormalize 0
1825}
1826
1827proc ConfEcho {args} {
1828    global echo
1829
1830    set iGain [expr {0.01 * $echo(iGain)}]
1831    set oGain [expr {0.01 * $echo(oGain)}]
1832    set values "$iGain $oGain "
1833    for {set i 1} {$i <= $echo(n)} {incr i} {
1834	set decay [expr {0.01 * $echo(decay$i)}]
1835	append values "$echo(delay$i) $decay "
1836    }
1837
1838    eval $echo(f) configure $values
1839}
1840
1841proc DoEcho {} {
1842    global v echo
1843
1844    set start [Marker2Sample m1]
1845    set end   [Marker2Sample m2]
1846    if {$start == $end} return
1847    SetMsg "Applying echo filter to range: $start $end"
1848
1849    ConfEcho
1850
1851    cbs copy snd
1852    if [catch {snd filter $echo(f) -start $start -end $end \
1853	    -continuedrain $echo(drain) \
1854	    -progress snack::progressCallback}] {
1855	SetMsg "Echo filter cancelled"
1856	snd copy cbs
1857	return
1858    }
1859
1860    set v(undoc) "snd swap cbs"
1861    set v(redoc) "snd swap cbs"
1862    set v(smpchanged) 1
1863    .tb.undo config -state normal
1864    Redraw
1865}
1866
1867proc PlayEcho {} {
1868    global echo
1869
1870    set start [Marker2Sample m1]
1871    set end   [Marker2Sample m2]
1872    if {$start == $end} return
1873
1874    ConfEcho
1875
1876    snd stop
1877    snd play -filter $echo(f) -start $start -end $end
1878}
1879
1880proc AddEcho {} {
1881    global echo
1882
1883    if {$echo(n) > 9} return
1884    set w .proc
1885    incr echo(n)
1886    AddEchoW $echo(n)
1887}
1888
1889proc AddEchoW {n} {
1890    global echo
1891
1892    set w .proc
1893    set f [expr {$n + 2}]
1894    pack [frame $w.f.f$f -relief raised -bd 1] -side left -before $w.f.hidden
1895    if {![info exists echo(delay$n)]} {
1896	set echo(delay$n) 30.0
1897    }
1898    pack [label $w.f.f$f.l -text "Echo $n"] -side top
1899    pack [frame $w.f.f$f.f1] -side left
1900    pack [scale $w.f.f$f.f1.s -label "" -from 250.0 -to 10.0 \
1901	    -variable echo(delay$n) -command "" -showvalue 0 -command ConfEcho]
1902    pack [frame $w.f.f$f.f1.f]
1903    pack [entry $w.f.f$f.f1.f.e -textvariable echo(delay$n) -width 3] \
1904	    -side left
1905    pack [label $w.f.f$f.f1.f.l -text ms] -side left
1906
1907    if {![info exists echo(decay$n)]} {
1908	set echo(decay$n) 40
1909    }
1910    pack [frame $w.f.f$f.f2] -side left
1911    pack [scale $w.f.f$f.f2.s -label "" -from 100 -to 0 -resolution 1 \
1912	    -variable echo(decay$n) -command "" -showvalue 0 -command ConfEcho]
1913    pack [frame $w.f.f$f.f2.f]
1914    pack [entry $w.f.f$f.f2.f.e -textvariable echo(decay$n) -width 3] \
1915	    -side left
1916    pack [label $w.f.f$f.f2.f.l -text %] -side left
1917}
1918
1919proc RemEcho {} {
1920    global echo
1921
1922    if {$echo(n) < 2} return
1923
1924    set w .proc
1925    set f [expr {$echo(n) + 2}]
1926    destroy $w.f.f$f
1927    incr echo(n) -1
1928}
1929
1930proc Echo {} {
1931    global echo
1932
1933    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
1934    set w .proc
1935    catch {destroy $w}
1936    toplevel $w
1937    wm title $w {Echo}
1938
1939    pack [frame $w.f]
1940
1941    pack [frame $w.f.f1] -side left
1942    pack [label $w.f.f1.l -text In]
1943    pack [scale $w.f.f1.s -label "" -from 100 -to 0 -resolution 1 \
1944	    -variable echo(iGain) -command "" -showvalue 0 -command ConfEcho]
1945    pack [frame $w.f.f1.f]
1946    pack [entry $w.f.f1.f.e -textvariable echo(iGain) -width 3] -side left
1947    pack [label $w.f.f1.f.l -text %] -side left
1948
1949    pack [frame $w.f.f2] -side left
1950    pack [label $w.f.f2.l -text Out]
1951    pack [scale $w.f.f2.s -label "" -from 100 -to 0 -resolution 1 \
1952	    -variable echo(oGain) -command "" -showvalue 0 -command ConfEcho]
1953    pack [frame $w.f.f2.f]
1954    pack [entry $w.f.f2.f.e -textvariable echo(oGain) -width 3] -side left
1955    pack [label $w.f.f2.f.l -text %] -side left
1956
1957    pack [frame $w.f.fe] -side left
1958    pack [button $w.f.fe.1 -text + -command AddEcho]
1959    pack [button $w.f.fe.2 -text - -command RemEcho]
1960
1961    pack [frame $w.f.hidden] -side left
1962    for {set i 1} {$i <= $echo(n)} {incr i} {
1963     AddEchoW $i
1964    }
1965
1966    pack [checkbutton $w.cb -text "Drain beyond selection" \
1967	    -variable echo(drain)] -anchor w
1968
1969    pack [ frame $w.f3] -pady 10 -anchor w
1970    pack [ button $w.f3.b1 -bitmap snackPlay -command PlayEcho] -side left
1971    pack [ button $w.f3.b2 -bitmap snackStop -command "Stop snd"] -side left
1972    pack [ button $w.f3.b3 -text OK -width 6 -command "DoEcho;destroy $w"] \
1973	    -side left
1974    pack [ button $w.f3.b4 -text Cancel -command "destroy $w"] -side left
1975}
1976
1977proc ConfMix {args} {
1978    global mix
1979
1980    set n [snd cget -channels]
1981    for {set i 0} {$i < $n} {incr i} {
1982	for {set j 0} {$j < $n} {incr j} {
1983	    set val [expr {0.01 * $mix($i,$j)}]
1984	    append values "$val "
1985	}
1986    }
1987    eval $mix(f) configure $values
1988}
1989
1990proc DoMix {} {
1991    global v mix
1992
1993    set start [Marker2Sample m1]
1994    set end   [Marker2Sample m2]
1995    if {$start == $end} return
1996    SetMsg "Mixing channels in range: $start $end"
1997
1998    ConfMix
1999
2000    cbs copy snd
2001    if [catch {snd filter $mix(f) -start $start -end $end \
2002	    -progress snack::progressCallback}] {
2003	SetMsg "Mix channels cancelled"
2004	snd copy cbs
2005	return
2006    }
2007
2008    set v(undoc) "snd swap cbs"
2009    set v(redoc) "snd swap cbs"
2010    set v(smpchanged) 1
2011    .tb.undo config -state normal
2012    Redraw
2013}
2014
2015proc PlayMix {} {
2016    global mix
2017
2018    set start [Marker2Sample m1]
2019    set end   [Marker2Sample m2]
2020    if {$start == $end} return
2021
2022    ConfMix
2023
2024    snd stop
2025    snd play -filter $mix(f) -start $start -end $end
2026}
2027
2028proc MixChan {} {
2029    global mix
2030
2031    if {[Marker2Sample m1] == [Marker2Sample m2]} MarkAll
2032    set w .mix
2033    catch {destroy $w}
2034    toplevel $w
2035    wm title $w {Mix Channels}
2036
2037    pack [frame $w.f]
2038
2039    label $w.f.l -text "New channel"
2040    grid $w.f.l
2041
2042    set n [snd cget -channels]
2043
2044    for {set i 0} {$i < $n} {incr i} {
2045	if {$i == 0} {
2046	    set label Left
2047	} elseif {$i == 1} {
2048	    set label Right
2049	} else {
2050	    set label [expr {$i + 1}]
2051	}
2052	label $w.f.ly$i -text $label
2053	grid $w.f.ly$i -row [expr {$i + 1}] -column 0
2054	label $w.f.lx$i -text "Channel $label"
2055	grid $w.f.lx$i -row 0 -column [expr {$i + 1}]
2056	for {set j 0} {$j < $n} {incr j} {
2057	    if {![info exists mix($i,$j)]} {
2058		if {$i == $j} {
2059		    set mix($i,$j) 100
2060		} else {
2061		    set mix($i,$j) 0
2062		}
2063	    }
2064	    frame $w.f.f$i-f$j -relief raised -bd 1
2065	    grid $w.f.f$i-f$j -row [expr {$i + 1}] -column [expr {$j + 1}]
2066	    pack [scale $w.f.f$i-f$j.s -command "" -orient horizontal \
2067		    -from -100 -to 100 -showvalue 0 -command ConfMix \
2068		    -variable mix($i,$j)]
2069	    pack [frame $w.f.f$i-f$j.f]
2070	    pack [entry $w.f.f$i-f$j.f.e -textvariable mix($i,$j) -width 4] \
2071		    -side left
2072	    pack [label $w.f.f$i-f$j.f.l -text %] -side left
2073	}
2074    }
2075
2076    pack [ frame $w.f3] -pady 10
2077    pack [ button $w.f3.b1 -bitmap snackPlay -command PlayMix] -side left
2078    pack [ button $w.f3.b2 -bitmap snackStop -command "Stop snd"] -side left
2079    pack [ button $w.f3.b3 -text OK -width 6 -command "DoMix;destroy $w"] \
2080	    -side left
2081    pack [ button $w.f3.b4 -text Cancel -command "destroy $w"] -side left
2082}
2083
2084proc Cut {} {
2085    global c v
2086
2087    set start [Marker2Sample m1]
2088    set end   [Marker2Sample m2]
2089    if {$start == $end} return
2090    SetMsg "Cutting range: $start $end"
2091    cbs copy snd -start $start -end $end
2092    snd cut $start $end
2093    set v(undoc) "snd insert cbs $start"
2094    set v(redoc) "snd cut $start $end"
2095
2096    PutMarker m2 [Marker2Time m1] 0 0
2097    set v(smpchanged) 1
2098    .tb.undo config -state normal
2099    DrawOverAxis
2100    Redraw
2101}
2102
2103proc Copy {} {
2104    set start [Marker2Sample m1]
2105    set end   [Marker2Sample m2]
2106    if {$start == $end} return
2107    SetMsg "Copying range: $start $end"
2108    cbs copy snd -start $start -end $end
2109}
2110
2111proc Paste {} {
2112    global c v
2113
2114    set start [Marker2Sample m1]
2115    set startt [Marker2Time m1]
2116    if {$start > [snd length]} {
2117	set start [snd length]
2118	set startt [snd length -unit seconds]
2119    }
2120    SetMsg "Inserting at: $start"
2121    snd insert cbs $start
2122
2123    set tmp [expr {$start + [cbs length] - 1}]
2124    set v(undoc) "snd cut $start $tmp"
2125    set v(redoc) "snd insert cbs $start"
2126
2127    PutMarker m2 [expr {$startt + [DTime2Time [cbs length -unit seconds]]}] 0 0
2128    set v(smpchanged) 1
2129    .tb.undo config -state normal
2130    DrawOverAxis
2131    Redraw
2132}
2133
2134proc SendXScroll {} {
2135    global c v
2136
2137    if $v(slink) {
2138	foreach prg [winfo interps] {
2139	    if [regexp .*xs.* $prg] {
2140		if {[winfo name .] != $prg} {
2141		    send $prg RecvXScroll [Coord2Time [expr [lindex [.cf.fc.xscroll get] 0] * $v(width)]]
2142		}
2143	    }
2144	}
2145    }
2146}
2147
2148proc RecvXScroll {a} {
2149    global c v
2150
2151    set f [Time2Coord [expr double($a / $v(width))]]
2152    eval $c xview moveto $f
2153}
2154
2155proc Redraw {args} {
2156    global c labels f v
2157
2158    SetCursor watch
2159    set length [snd length]
2160    if {$args != "quick"} {
2161	$c delete obj
2162	$c config -bg $v(bg)
2163	.cf.fyc.yc config -bg $v(bg)
2164	.of.c config -bg $v(bg)
2165	if {$length == 0} { set length 1 }
2166	set v(endsmp) [expr $v(startsmp) + $v(rate) * $v(scrw) / $v(pps)]
2167	if {$v(endsmp) > $length} {
2168	    set v(endsmp) $length
2169	}
2170
2171	if {[expr int(double($length * $v(pps)) / $v(rate))] < $v(scrw)} {
2172	    if [winfo exist .of] { pack forget .of }
2173	    set v(startsmp) 0
2174	    set v(endsmp) $length
2175	} else {
2176	    pack .of -side top -fill x -before .cf
2177	    if {$::tcl_platform(platform) == "windows"} {
2178		DrawOverAxis
2179	    }
2180	}
2181	.of.xscroll set [expr double($v(startsmp)) / $length] [expr double($v(endsmp)) / $length]
2182
2183	.cf.fyc.yc delete axis
2184	if {$v(waveh) > 0} {
2185	    if {$v(linkfile) && $f(sndfile) != ""} {
2186		snack::deleteInvalidShapeFile [file tail $f(spath)$f(sndfile)]
2187		$c create waveform 0 0 -sound snd -height $v(waveh) \
2188			-pixels $v(pps) -tags [list obj wave] \
2189			-start $v(startsmp) -end $v(endsmp) \
2190			-channel $v(vchan) -debug $::debug -fill $v(fg) \
2191	-shapefile [file rootname [file tail $f(spath)$f(sndfile)]].shape\
2192			-progress snack::progressCallback
2193		snack::makeShapeFileDeleteable [file tail $f(spath)$f(sndfile)]
2194	    } else {
2195		$c create waveform 0 0 -sound snd -height $v(waveh) \
2196			-pixels $v(pps) -tags [list obj wave] \
2197			-start $v(startsmp) -end $v(endsmp) \
2198			-channel $v(vchan) -debug $::debug -fill $v(fg)
2199	    }
2200	    $c lower wave
2201	    .cf.fyc.yc create text $v(yaxisw) 2 -text [snd max]\
2202		    -font $v(sfont) -anchor ne -tags axis -fill $v(fg)
2203	    .cf.fyc.yc create text $v(yaxisw) $v(waveh) -text [snd min]\
2204		    -font $v(sfont) -anchor se -tags axis -fill $v(fg)
2205	    .cf.fyc.yc create line 0 [expr $v(waveh)+0] $v(yaxisw) \
2206		    [expr $v(waveh)+0] -tags axis -fill $v(fg)
2207	}
2208	if {$v(topfr) > [expr $v(rate)/2]} {
2209	    set v(topfr) [expr $v(rate)/2]
2210	}
2211	if {$v(spegh) > 0} {
2212	    set v(winlen) [expr int($v(rate) / $v(anabw))]
2213	    if {$v(winlen) > $v(fftlen)} {
2214	      set v(winlen) $v(fftlen)
2215	    }
2216	    $c create spectrogram 0 $v(waveh) -sound snd -fftlen $v(fftlen) \
2217		    -winlen $v(winlen) -height $v(spegh) -pixels $v(pps) \
2218		    -preemph $v(preemph) -topfr $v(topfr) -tags [list obj speg] \
2219		    -start $v(startsmp) -end $v(endsmp)\
2220		    -contrast $v(contrast) -brightness $v(brightness)\
2221		    -gridtspacing $v(gridtspacing) \
2222		    -gridfspacing $v(gridfspacing) -channel $v(vchan) \
2223		    -colormap $v($v(cmap)) -gridcol $v(gridcolor) \
2224		    -progress snack::progressCallback -debug $::debug
2225	    $c lower speg
2226	    snack::frequencyAxis .cf.fyc.yc 0 $v(waveh) $v(yaxisw) $v(spegh)\
2227		    -topfrequency $v(topfr) -tags axis -fill $v(fg)\
2228		    -font $v(sfont)
2229	    .cf.fyc.yc create line 0 [expr $v(spegh) + $v(waveh)+0] \
2230		    $v(yaxisw) [expr $v(spegh) + $v(waveh)+0] -tags axis\
2231		    -fill $v(fg)
2232	}
2233
2234	set v(width) [expr int($v(pps) * double($v(endsmp) - $v(startsmp)) / $v(rate))]
2235	if {$v(width) == 0} { set v(width) 600 }
2236	$c create line 0 0 $v(width) 0 -tags obj -fill $v(fg)
2237	$c create line 0 $v(waveh) $v(width) $v(waveh) -tags obj -fill $v(fg)
2238
2239    }
2240
2241    $c delete tran axis
2242    set y [expr $v(waveh) + $v(spegh)]
2243    $c create line 0 $y $v(width) $y -tags axis -fill $v(fg)
2244
2245    snack::timeAxis $c 0 $y $v(width) $v(timeh) $v(pps)\
2246	    -tags axis -starttime [expr double($v(startsmp)) / $v(rate)]\
2247	    -fill $v(fg) -font $v(sfont)
2248    incr y $v(timeh)
2249    $c create line 0 $y $v(width) $y -tags axis -fill $v(fg)
2250
2251    .cf.fyc.yc configure -height $y
2252    set tlab t
2253    .cf.fyc.yc create text 5 [expr $v(waveh) + $v(spegh) + 2] -text $tlab \
2254	    -font $v(sfont) -anchor nw -tags axis -fill $v(fg)
2255
2256    if $v(ipa) {
2257	incr y [DrawLabels $y $labels ipa]
2258    }
2259    incr y [DrawLabels $y $labels lab]
2260
2261    foreach p $v(plugins) {
2262	incr y [namespace inscope $p Redraw $y]
2263    }
2264
2265    set v(toth) $y
2266    $c configure -height $v(toth) -width $v(width) -scrollregion "0 0 $v(width) $v(toth)"
2267    .cf.fyc.yc configure -height $v(toth) -scrollregion "0 0 $v(yaxisw) $v(toth)"
2268
2269# Someday in a perfect universe
2270
2271    if {$::tcl_platform(os) == "Linux" || \
2272	$::tcl_platform(platform) == "macintosh"} {
2273      set maxw [lindex [wm maxsize .] 0]
2274      if {$v(width) > $maxw} {
2275	if [winfo exist .of] {
2276	  . config -width $maxw -height [expr $v(toth) + 130]
2277	} else {
2278	  . config -width $maxw -height [expr $v(toth) + 40]
2279	}
2280	pack propagate . 0
2281      } else {
2282	pack propagate . 1
2283      }
2284    }
2285    if {$::tcl_platform(platform) == "windows"} {
2286      set maxw [lindex [wm maxsize .] 0]
2287      if {$v(width) > $maxw} {
2288	if {[expr int(double($length * $v(pps)) / $v(rate))] >= $v(scrw)} {
2289	  wm geometry . [expr $maxw - 15]x[expr $v(toth) + 120]
2290	} else {
2291	  wm geometry . [expr $maxw - 15]x[expr $v(toth) + 70]
2292	}
2293      }
2294    }
2295
2296    catch {PutMarker m1 [Marker2Time m1] 0 0}
2297    catch {PutMarker m2 [Marker2Time m2] 0 0}
2298    SetCursor ""
2299}
2300
2301proc DrawLabels {y labels type} {
2302    global c v f ipa
2303
2304    if {[llength $labels] == 0} {
2305	return 0
2306    } else {
2307	$c create line 0 [expr $y + $v(labelh)]	[expr $v(width) -1] \
2308		[expr $y + $v(labelh)] -tags obj -fill $v(fg)
2309	set start 0
2310	set end   0
2311	set label ""
2312	set i 0
2313	foreach row $labels {
2314	    switch $v(labfmt) {
2315		TIMIT -
2316		HTK {
2317		    scan $row {%d %d %s} start end label
2318		    set lx [Time2Coord $start]
2319#		    if {!$v(zerolabs) && $end == $start} continue
2320		}
2321		MIX {
2322		    scan $row {%d %s} start label
2323		    set lx [Time2Coord $start]
2324		    set end [Coord2Time $v(width)]
2325		    scan [lindex $labels [expr $i+1]] {%d} end
2326		}
2327		WAVES {
2328		    scan $row {%f %d %s} end color label
2329		    set lx [Time2Coord $end]
2330		    set start 0
2331		    scan [lindex $labels [expr $i-1]] {%f} start
2332		}
2333	    }
2334	    if {$lx >= 0 && $lx <= $v(width)} {
2335		if {$v(labalign) == "c"} {
2336		    set tx [Time2Coord [expr ($start+$end)/2]]
2337		} elseif {$v(labalign) == "w"} {
2338		    set tx [expr [Time2Coord $start] + 2]
2339		} else {
2340		    set tx [Time2Coord $end]
2341		}
2342		if {$type == "lab"} {
2343		    $c create text $tx [expr $y+12] -text $label\
2344			    -font $v(font) -anchor $v(labalign)\
2345			    -tags [list $i obj text lab$i tran] -fill $v(fg)
2346		    $c create line $lx $y $lx [expr $y+$v(labelh)] \
2347			    -tags [list b$i obj bound tran] -fill $v(fg)
2348		} else {
2349		    if {$v(labfmt) == "MIX"} {
2350			regsub -all {\$} $label "" t1
2351			regsub -all {\"} $t1    "" t2
2352			regsub -all # $t2       "" t3
2353			regsub -all {\`} $t3    "" t4
2354			regsub -all {\'} $t4    "" tmp
2355			set label $tmp
2356		    }
2357#		catch {$c create image $tx [expr $y+2] \
2358#		   -image [image create photo -file $f(ipapath)/$ipa($label)] \
2359#		   -anchor n -tags [list obj tran]}
2360		    if {$::tcl_platform(platform) == "windows"} {
2361			$c create text $tx [expr $y+12] \
2362				-text $label -font IPAKiel -fill $v(fg)\
2363				-anchor $v(labalign) -tags [list obj tran]
2364		    } else {
2365			catch {$c create bitmap $tx [expr $y+2] \
2366				-bitmap @$f(ipapath)/$ipa($label) \
2367				-anchor n -tags [list obj tran]}
2368		    }
2369		    $c create line $lx [expr $y] $lx [expr $y+$v(labelh)] \
2370			    -tags [list obj tran] -fill $v(fg)
2371		}
2372	    }
2373	    incr i
2374	}
2375    }
2376    return $v(labelh)
2377}
2378
2379proc ScrollCmd {args} {
2380    global v
2381
2382    if {[lindex $args 0] == "moveto"} {
2383	set delta [expr [lindex [.of.xscroll get] 1] - [lindex [.of.xscroll get] 0]]
2384	set pos [lindex $args 1]
2385	if {$pos < 0.0} { set pos 0.0 }
2386	if {$pos > [expr 1.0 - $delta]} { set pos [expr 1.0 - $delta] }
2387	.of.xscroll set $pos [expr $pos + $delta]
2388    } elseif {[lindex $args 0] == "scroll" && $v(scroll) == 1} {
2389	set pos [expr double($v(startsmp)) / [snd length]]
2390	set delta [expr double($v(endsmp)) / [snd length] - $pos]
2391	if {[lindex $args 1] > 0} {
2392            set pos [expr $pos + $delta]
2393	    if {$pos > [expr 1.0 - $delta]} { set pos [expr 1.0 - $delta] }
2394	}
2395	if {[lindex $args 1] < 0} {
2396            set pos [expr $pos - $delta]
2397            if {$pos < 0.0} { set pos 0.0 }
2398	}
2399	.of.xscroll set $pos [expr $pos + $delta]
2400	set v(scroll) 0
2401    }
2402}
2403
2404proc RePos {args} {
2405    global v c
2406
2407    set v(startsmp) [expr int ([lindex [.of.xscroll get] 0] * [snd length])]
2408    set v(endsmp)   [expr int ([lindex [.of.xscroll get] 1] * [snd length])]
2409    $c xview moveto 0
2410    Redraw
2411}
2412
2413proc DrawOverAxis {} {
2414  global v
2415
2416  set totw [winfo width .]
2417  set scrh [winfo height .of.xscroll]
2418  set width [expr $totw - 2 * $scrh]
2419  set length [snd length -unit seconds]
2420  if {$length > 0} {
2421    set v(opps) [expr $width/$length]
2422  } else {
2423    set v(opps) 400
2424  }
2425  .of.c delete overaxis
2426  snack::timeAxis .of.c $scrh 20 $width 11 $v(opps) -tags overaxis \
2427      -fill $v(fg)
2428}
2429
2430proc OverPlay {x} {
2431    global v
2432
2433    set start [expr int($v(rate)*(($x - [winfo height .of.xscroll]) * 1000 / $v(opps)))]
2434    set end   [snd length]
2435    Stop snd
2436    if {$start < 0} { set start 0 }
2437    set v(s0) $start
2438    set v(s1) $end
2439    Play snd $start $end
2440    .of.c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2441    after cancel PutPlayMarker
2442    after 50 PutPlayMarker
2443}
2444
2445proc Reconf {} {
2446    global c v f
2447
2448    set dox 0
2449    set doy 0
2450    if {[$c xview] == "0 1"} { set dox 1 }
2451    if {[$c yview] == "0 1"} { set doy 1 }
2452
2453    if {$dox} {
2454	pack forget .cf.fc.xscroll
2455	pack forget .cf.fyc.yc2
2456    } else {
2457	pack .cf.fc.xscroll -side bottom -fill x -before $c
2458	.cf.fyc.yc2 config -height [winfo height .cf.fc.xscroll]
2459	pack .cf.fyc.yc2 -side bottom -fill x -before .cf.fyc.yc
2460    }
2461    if {$doy} {
2462	pack forget .cf.fc.yscroll
2463    } else {
2464	pack .cf.fc.yscroll -side right -fill y -before $c
2465    }
2466
2467    set ww [.of.c itemcget overwave -width]
2468    set v(scrh) [winfo height .of.xscroll]
2469    set totw [expr [winfo width .] - 2 * $v(scrh)]
2470    if {$ww != $totw && ![catch {pack info .of}]} {
2471	.of.c delete overwave
2472        if {$v(linkfile) && $f(sndfile) != ""} {
2473	    .of.c create waveform $v(scrh) 0 -sound snd -height 20 \
2474		    -width $totw -tags overwave -fill $v(fg) -debug $::debug \
2475	    -shapefile [file rootname [file tail $f(spath)$f(sndfile)]].shape
2476	} else {
2477	    .of.c create waveform $v(scrh) 0 -sound snd -height 20 \
2478		    -width $totw -tags overwave -fill $v(fg) -debug $::debug
2479	}
2480	.of.c create rectangle -1 -1 -1 -1 -tags mark -fill yellow -stipple gray25
2481    }
2482    if {[snd length] > 0} DrawOverAxis
2483#    if {$::tcl_platform(platform) == "unix"} {
2484#	if {$v(propflag) > 1} { pack propagate . 0 }
2485#    }
2486#    if {$dox && $doy} { incr v(propflag) }
2487}
2488
2489proc SetMsg {msg args} {
2490    global v
2491
2492    if {$args == ""} {
2493	set v(msg) $msg
2494	.bf.lab config -state disabled
2495    } elseif {$args == "mark"} {
2496	set v(msg) $msg
2497	set v(currline) -1
2498	.bf.lab config -state normal
2499    } else {
2500	set v(msg) $msg
2501	set v(currline) $args
2502	.bf.lab config -state normal
2503    }
2504    SetCursor ""
2505}
2506
2507proc InputFromMsgLine {key} {
2508    global v labels
2509
2510    if {$key == "BackSpace"} return
2511    if {$v(currline) >= 0} {
2512	set labels [lreplace $labels $v(currline) $v(currline) $v(msg)]
2513	Redraw quick
2514    } else {
2515	if {[scan $v(msg) {From: %s to: %s length: %s ( %f - %f , %f} t0 t1 t2 t3 t4 t5] == 6} {
2516	    if {$t0 != [lindex $v(fromto) 0]} {
2517		PutMarker m1 $t0 0 0
2518	    }
2519	    if {$t1 != [lindex $v(fromto) 1]} {
2520		set t2 [expr $t1-$t0]
2521		PutMarker m2 $t1 0 0
2522	    }
2523	    if {$t2 != [lindex $v(fromto) 2]} {
2524		set t1 [expr $t0+$t2]
2525		PutMarker m2 $t1 0 0
2526	    }
2527	    if {$t3 != [lindex $v(fromto) 3]} {
2528		set t0 [DTime2Time $t3]
2529		PutMarker m1 $t0 0 0
2530	    }
2531	    if {$t4 != [lindex $v(fromto) 4]} {
2532		set t1 [expr [DTime2Time $t4]-[DTime2Time $t3]]
2533		PutMarker m2 [DTime2Time $t4] 0 0
2534	    }
2535	    if {$t5 != [lindex $v(fromto) 5]} {
2536		set t1 [expr [DTime2Time $t3]+[DTime2Time $t5]]
2537		PutMarker m2 $t1 0 0
2538	    }
2539	    set t3 [format "%.3f" [Time2DTime $t0]]
2540	    set t4 [format "%.3f" [Time2DTime $t1]]
2541	    set t5 [format "%.3f" [expr $t4 - $t3]]
2542	    SetMsg [format "From: %9s to: %9s length: %9s\t(%.3f - %.3f, %.3f)"\
2543		    $t0 $t1 $t2 $t3 $t4 $t5] mark
2544	    set v(fromto) [list $t0 $t1 $t2 $t3 $t4 $t5]
2545	}
2546    }
2547}
2548
2549proc PlayToCursor {x} {
2550    global c f v
2551
2552    Stop snd
2553    if {[snd length] == 0} return
2554    set start [Marker2Sample m1]
2555    set s [Coord2Sample [$c canvasx $x]]
2556    if {$s < $start} {
2557	set end $start
2558	set start $s
2559    } else {
2560	set end $s
2561    }
2562    SetMsg "Playing range: $start $end"
2563    set v(s0) $start
2564    set v(s1) $end
2565    Play snd $start $end
2566    set v(pause) 0
2567    .of.c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2568    $c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2569    after 50 PutPlayMarker
2570}
2571
2572proc PlayMark {args} {
2573    global c f v
2574
2575    Stop snd
2576    if {[snd length] == 0} return
2577    set start [Marker2Sample m1]
2578    set end   [Marker2Sample m2]
2579    if {$start > [snd length]} return
2580    if {[llength $args] > 0} {
2581	set x [Coord2Sample [$c canvasx [lindex $args 0]]]
2582	if {$x < $start} {
2583	    set end $start
2584	    set start 0
2585	}
2586	if {$x > $end} {
2587	    set start $end
2588	    set end [snd length]
2589	}
2590    }
2591    if {$start == $end} {
2592	set start $end
2593	set end [snd length]
2594    }
2595    SetMsg "Playing range: $start $end"
2596    set v(s0) $start
2597    set v(s1) $end
2598    Play snd $start $end
2599    set v(pause) 0
2600    .of.c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2601    $c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2602    after 50 PutPlayMarker
2603}
2604
2605proc PlayAll {} {
2606    global c v
2607
2608    Stop snd
2609    SetMsg "Playing all samples"
2610    set v(s0) 0
2611    set v(s1) [snd length]
2612    Play snd
2613    set v(pause) 0
2614    .of.c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2615    $c create poly -1 -1 -1 -1 -1 -1 -fill red -tags playmark
2616    after 50 PutPlayMarker
2617}
2618
2619proc Play {s {start 0} {end -1}} {
2620    global v
2621
2622    if !$v(remote) {
2623	$s play -start $start -end $end
2624    } else {
2625	set sock [socket $v(ashost) $v(asport)]
2626	if {$end == -1} {
2627	    set end [snd length]
2628	}
2629	set v(rp_s) $s
2630	set v(rp_sock) $sock
2631	set end2 $end
2632	if {$end2 > [expr $start + 10000]} {
2633	    set end2 [expr $start + 10000]
2634	}
2635	set v(rp_next) $end2
2636	set v(rp_end) $end
2637	fconfigure $sock -translation binary -blocking 0
2638	puts -nonewline $sock play
2639	flush $sock
2640	set handle [gets $sock]
2641	set v(handle) $handle
2642	puts -nonewline $sock [$s data -fileformat au -start $start -end $end2]
2643	fileevent $sock writable PlayHandler
2644    }
2645}
2646
2647proc PlayHandler {} {
2648    global v
2649
2650    if {$v(rp_next) < $v(rp_end)} {
2651	set end2 $v(rp_end)
2652	if {$end2 > [expr $v(rp_next) + 10000]} {
2653	    set end2 [expr $v(rp_next) + 10000]
2654	}
2655	puts -nonewline $v(rp_sock) [$v(rp_s) data -fileformat raw -start $v(rp_next) -end $end2 -byteorder bigEndian]
2656	set v(rp_next) $end2
2657    } else {
2658	close $v(rp_sock)
2659    }
2660}
2661
2662proc Stop {s} {
2663    global v
2664
2665    if !$v(remote) {
2666	$s stop
2667    } else {
2668	catch {close $v(rp_sock)}
2669	catch {set sock [socket $v(ashost) $v(asport)]}
2670	if ![info exists sock] return
2671	fconfigure $sock -translation binary
2672	puts -nonewline $sock stop
2673	puts $sock $v(handle)
2674	close $sock
2675    }
2676}
2677
2678proc StopPlay {} {
2679    global c v
2680
2681    after cancel PutPlayMarker
2682    Stop snd
2683    set v(pause) 0
2684    set v(s1) 0
2685    .of.c delete playmark
2686    $c delete playmark
2687    if $v(activerec) {
2688	after cancel UpdateRec
2689	Redraw
2690	event generate .cf.fc.c <Configure>
2691	MarkAll
2692	set v(activerec) 0
2693    }
2694}
2695
2696proc PausePlay {} {
2697    global c v
2698
2699    if $v(activerec) {
2700	snd pause
2701	return
2702    }
2703    set v(pause) [expr 1 - $v(pause)]
2704    if $v(pause) {
2705	after cancel PutPlayMarker
2706	set v(s0) [expr $v(s0) + int([snack::audio elapsedTime] * $v(rate))]
2707	Stop snd
2708    } else {
2709	after 50 PutPlayMarker
2710	Play snd $v(s0) $v(s1)
2711    }
2712}
2713
2714proc PutPlayMarker {} {
2715    global v c
2716
2717    if $v(pause) return
2718
2719    set time [expr [snack::audio elapsedTime] + double($v(s0)) / $v(rate)]
2720    if {$time > [expr double($v(s1)) / $v(rate)] || ![snack::audio active]} {
2721	.of.c delete playmark
2722	$c delete playmark
2723	return
2724    }
2725    SetMsg "Playing at [format "%.2f" $time]"
2726    set ox [expr $v(scrh) + $time * $v(opps) / 1000.0]
2727    set x [expr ($time - double($v(startsmp)) / $v(rate)) * $v(pps)]
2728    set y [expr $v(waveh) + $v(spegh) + 4]
2729    .of.c coords playmark $ox 22 [expr $ox-5] 30 [expr $ox+5] 30
2730    $c coords playmark $x $y [expr $x-5] [expr $y+10] [expr $x+5] [expr $y+10]
2731    update idletasks
2732    after 50 PutPlayMarker
2733}
2734
2735proc InfoStr {arg} {
2736    global f v labels
2737
2738    set samps [snd length]
2739    set time  [snd length -unit seconds]
2740    if {$arg == "path"} {
2741	set snd "$f(spath)$f(sndfile)"
2742	set lab "$f(lpath)$f(labfile)"
2743    } else {
2744	set snd $f(sndfile)
2745	set lab $f(labfile)
2746    }
2747    set info [format "Sample file: %s (%s)  %d samples %.2f seconds" $snd $v(smpfmt) $samps $time]
2748    if {$labels != {}} {
2749	set info "$info  Label file: $lab  ($v(labfmt))"
2750    }
2751    return $info
2752}
2753
2754proc xsGetGeometry {} {
2755    scan [wm geometry .] "%dx%d+%d+%d" w h x y
2756    if {$::tcl_platform(platform) == "windows"} {
2757	return +$x+[expr $y+$h+40]
2758    } else {
2759	return +$x+[expr $y+$h+68]
2760    }
2761}
2762
2763proc ToggleSpeg {} {
2764    global v
2765
2766    if [snack::audio active] return
2767    if $v(showspeg) {
2768        set v(spegh) $v(remspegh)
2769    } else {
2770        set v(remspegh) $v(spegh)
2771        set v(spegh) 0
2772    }
2773    Redraw
2774}
2775
2776proc ToggleRecording {} {
2777    global v
2778
2779    if $v(recording) {
2780	.tb.rec config -state normal
2781    } else {
2782	.tb.rec config -state disabled
2783    }
2784
2785}
2786
2787proc Record {} {
2788    global c v rec
2789
2790    StopPlay
2791    set v(smpchanged) 1
2792    if [winfo exist .of] { pack forget .of }
2793    $c delete obj
2794    .of.c delete overwave
2795    set width [winfo width $c]
2796    $c xview moveto 0
2797    if {$v(waveh) > 0} {
2798	$c create waveform 0 0 -sound snd -height $v(waveh) -pixels $v(pps) \
2799		-width $width -tags [list obj recwave] -channel $v(vchan) \
2800		-debug $::debug -fill red
2801    }
2802    if {$v(spegh) > 0} {
2803	$c create spectrogram 0 $v(waveh) -sound snd -height $v(spegh) \
2804		-pixels $v(pps) \
2805		-width $width -tags [list obj recwave] -channel $v(vchan) \
2806		-colormap $v($v(cmap)) -debug $::debug
2807    }
2808    if {$v(linkfile)} {
2809	catch {file delete -force _xs[pid].wav}
2810	snd configure -file _xs[pid].wav
2811    }
2812    snd record
2813    set v(activerec) 1
2814    after 100 UpdateRec
2815}
2816
2817proc UpdateRec {} {
2818    global c v
2819
2820    if {$v(activerec) == 0} return
2821    set secs [expr int([snd length -unit seconds])]
2822    set dec [format "%.2d" [expr int(100*([snd length -unit seconds] - $secs))]]
2823    set time [clock format $secs -format "Length: %M:%S.$dec"]
2824#    if {$secs > 9} {
2825#	$c delete recwave rectext
2826#	$c create text [expr [lindex [$c xview] 0] * $v(width) + 60] 20 \
2827#		-fill red -text $time -tags [list obj rectext]
2828#	update
2829#    }
2830    SetMsg $time
2831    after 100 UpdateRec
2832}
2833
2834proc MoveBoundary {x} {
2835    global c labels v
2836
2837    set coords [$c coords current]
2838    set x [$c canvasx $x]
2839    if {$x < 0} { set x 0 }
2840    set i [string trim [lindex [$c gettags current] 0] b]
2841    if [string match [$c type current] text] return
2842    if {$i == "obj" || $i == "mark" || $i == "axis" || $i == ""} {
2843	return
2844    }
2845
2846    set h [expr $i - 1]
2847    set j [expr $i + 1]
2848
2849    if {$v(lastmoved) != $i} {
2850	set v(labchanged) 1
2851	SetUndo $labels
2852	set v(lastmoved) $i
2853    }
2854
2855    $c raise current
2856    set px 0
2857    set nx $v(width)
2858    set pb [$c find withtag b$h]
2859    set nb [$c find withtag b$j]
2860    if {$pb != ""} { set px [lindex [$c coords $pb] 0]}
2861    if {$nb != ""} { set nx [lindex [$c coords $nb] 0]}
2862
2863    if {$x <= $px} { set x [expr $px + 1] }
2864    if {$nx <= $x} { set x [expr $nx - 1] }
2865
2866    $c coords current $x [lindex $coords 1] $x [lindex $coords 3]
2867    set rest ""
2868
2869    switch $v(labfmt) {
2870	TIMIT -
2871	HTK {
2872	    scan [lindex $labels $i] {%d %d %s %[^�]} start stop label rest
2873	    if {$j == [llength $labels]} { set length [expr $stop - $start] }
2874	    set start [Coord2Time $x]
2875	    if {$j == [llength $labels]} { set stop [expr $start + $length] }
2876	    set labels [lreplace $labels $i $i "$start\n$stop\n$label\n$rest"]
2877	    if {$h <= 0} return
2878	    while {[lindex [lindex $labels $h] 0] == [lindex [lindex $labels $h] 1]} {
2879		set hlabel [lindex [lindex $labels $h] 2]
2880		set hrest [lindex [lindex $labels $h] 3]
2881		set labels [lreplace $labels $h $h "$start\n$start\n$hlabel\n$hrest"]
2882		incr h -1
2883	    }
2884	    set rest ""
2885	    scan [lindex $labels $h] {%d %d %s %[^�]} start stop label rest
2886	    if {$v(labfmt) == "HTK"} {
2887		set stop [expr [Coord2Time $x]-(10000000/$v(rate))]
2888	    } else {
2889		set stop [Coord2Time $x]
2890	    }
2891	    set labels [lreplace $labels $h $h "$start\n$stop\n$label\n$rest"]
2892	}
2893	MIX {
2894	    scan [lindex $labels $i] {%d %s %[^�]} start label rest
2895	    set start [Coord2Time $x]
2896	    set labels [lreplace $labels $i $i "$start\n$label\n$rest"]
2897	}
2898	WAVES {
2899	    scan [lindex $labels $i] {%f %d %s %[^�]} end color label rest
2900	    set end [Coord2Time $x]
2901	    set labels [lreplace $labels $i $i "$end\n$color\n$label\n$rest"]
2902	}
2903    }
2904    SetMsg [Coord2Time $x]
2905}
2906
2907proc SetLabelText {i label} {
2908    global labels v
2909
2910    set rest ""
2911    switch $v(labfmt) {
2912	TIMIT -
2913	HTK {
2914	    scan [lindex $labels $i] {%d %d %s %[^�]} start stop junk rest
2915	    set labels [lreplace $labels $i $i "$start\n$stop\n$label\n$rest"]
2916	}
2917	MIX {
2918	    scan [lindex $labels $i] {%d %s %[^�]} start junk rest
2919	    set labels [lreplace $labels $i $i "$start\n$label\n$rest"]
2920	}
2921	WAVES {
2922	    scan [lindex $labels $i] {%f %d %s %[^�]} end color junk rest
2923	    set labels [lreplace $labels $i $i "$end\n$color\n$label\n$rest"]
2924	}
2925    }
2926}
2927
2928proc Undo {} {
2929    global c v labels undo
2930
2931    if {[cbs length] != 0} {
2932	eval $v(undoc)
2933        foreach {v(undoc) v(redoc)} [list $v(redoc) $v(undoc)] break
2934	DrawOverAxis
2935	Redraw
2936    } else {
2937        foreach {labels undo} [list $undo $labels] break
2938	Redraw quick
2939    }
2940    SetMsg ""
2941}
2942
2943proc SetUndo {l} {
2944    global undo
2945
2946    set undo $l
2947    .tb.undo config -state normal
2948}
2949
2950proc MarkAll {} {
2951    global v
2952
2953    PutMarker m1 0 0 0
2954    PutMarker m2 [Coord2Time $v(width)] 0 0
2955}
2956
2957proc ZeroXAdjust {} {
2958    global v
2959
2960    foreach m {m1 m2} {
2961	set start [Marker2Sample $m]
2962	snd sample [expr $start-100] ;# to fill sample buffer with leftmost
2963	for {set i 0} {$i < 100} {incr i} {
2964	    set j [expr {$start + $i}]
2965	    if {$j >= [snd length]} break
2966	    if {$v(vchan) == 1} {
2967		set sample [lindex [snd sample $j] 1]
2968		set psample [lindex [snd sample [expr {$j-1}]] 1]
2969	    } else {
2970		set sample [lindex [snd sample $j] 0]
2971		set psample [lindex [snd sample [expr {$j-1}]] 0]
2972	    }
2973	    if {[expr {$sample*$psample}] < 0} break
2974	    set j [expr {$start - $i}]
2975	    if {$j < 0} break
2976	    if {$v(vchan) == 1} {
2977		set sample [lindex [snd sample $j] 1]
2978		set psample [lindex [snd sample [expr {$j-1}]] 1]
2979	    } else {
2980		set sample [lindex [snd sample $j] 0]
2981		set psample [lindex [snd sample [expr {$j-1}]] 0]
2982	    }
2983	    if {[expr {$sample*$psample}] < 0} break
2984	}
2985	if {$i < 100} {
2986	    PutMarker $m [Sample2Time $j] 0 0
2987	}
2988
2989    }
2990    # Copied from PutMarker
2991    DrawZoom 1
2992    DrawSect
2993    set t1 [Marker2Time m1]
2994    set t2 [Marker2Time m2]
2995    set l  [expr $t2 - $t1]
2996    set tt1 [Time2DTime $t1]
2997    set tt2 [Time2DTime $t2]
2998    set ll  [expr $tt2 - $tt1]
2999    SetMsg [format "From: %9s to: %9s length: %9s\t(%.3f - %.3f, %.3f)"\
3000	    $t1 $t2 $l $tt1 $tt2 $ll] mark
3001    set v(fromto) [list $t1 $t2 $l $tt1 $tt2 $ll]
3002}
3003
3004proc InsertLabel {x y} {
3005    global c v labels
3006
3007    set v(labchanged) 1
3008    SetUndo $labels
3009    InsertLabelEntry [Coord2Time [$c canvasx $x]]
3010
3011    $c delete bound text
3012    Redraw quick
3013}
3014
3015proc InsertLabelEntry {t} {
3016    global labels v
3017
3018    set i 0
3019    switch $v(labfmt) {
3020	TIMIT -
3021	HTK {
3022	    foreach l $labels {
3023		if {([lindex $l 0] < $t) && ([lindex $l 1] > $t)} { break }
3024		incr i
3025	    }
3026	    if {[llength $labels] == $i} { incr i -1 }
3027	    if {$labels == ""} {
3028		set sto [DTime2Time [snd length -unit seconds]]
3029		set labels [list "$t\n$sto\nx"]
3030	    } elseif {$t < [lindex [lindex $labels 0] 0]} {
3031		set sto [lindex [lindex $labels 0] 0]
3032		set labels [linsert $labels 0 "$t\n$sto\nx"]
3033	    } elseif {[llength $labels] == [expr $i+1]} {
3034		set sta1 [lindex [lindex $labels $i] 0]
3035		set sto1 $t
3036		set lab1 [lindex [lindex $labels $i] 2]
3037		set sta2 $t
3038		set sto2 [lindex [lindex $labels $i] 1]
3039		set lab2 x
3040		set labels [lreplace $labels $i $i "$sta1\n$sto1\n$lab1" "$sta2\n$sto2\n$lab2"]
3041            } else {
3042		SetMsg [lindex [lindex $labels $i] 2]
3043		set sta1 [lindex [lindex $labels $i] 0]
3044		set sto1 $t
3045		set lab1 [lindex [lindex $labels $i] 2]
3046		set sta2 $t
3047		set sto2 [lindex [lindex $labels [expr $i+1]] 0]
3048		set lab2 x
3049		set labels [lreplace $labels $i $i "$sta1\n$sto1\n$lab1" "$sta2\n$sto2\n$lab2"]
3050            }
3051	}
3052	MIX {
3053	    foreach l $labels {
3054		if {[lindex $l 0] > $t} { break }
3055		incr i
3056	    }
3057	    SetMsg [lindex [lindex $labels $i] 1]
3058	    set labels [linsert $labels $i "$t\nx"]
3059	}
3060	WAVES {
3061	    foreach l $labels {
3062		if {[lindex $l 0] > $t} { break }
3063		incr i
3064	    }
3065	    SetMsg [lindex [lindex $labels $i] 1]
3066	    set labels [linsert $labels $i "$t\n121\nx"]
3067	}
3068    }
3069}
3070
3071proc DeleteLabel {x y} {
3072    global c v labels
3073
3074    set v(labchanged) 1
3075    SetUndo $labels
3076    if {[string compare [lindex [$c gettags [$c find closest\
3077	    [$c canvasx $x] [$c canvasy $y]]] 2] text] == 0} {
3078	set i [lindex [$c gettags [$c find closest\
3079		[$c canvasx $x] [$c canvasy $y]]] 0]
3080	RemoveLabelEntry $i
3081
3082	$c delete bound text
3083	Redraw quick
3084    }
3085}
3086
3087proc RemoveLabelEntry {i} {
3088    global labels v
3089
3090    switch $v(labfmt) {
3091	TIMIT -
3092	HTK {
3093	    set start [lindex [lindex $labels [expr $i-1]] 0]
3094	    set stop  [lindex [lindex $labels $i] 1]
3095	    set label [lindex [lindex $labels [expr $i-1]] 2]
3096	    set labels [lreplace $labels [expr $i-1] $i "$start\n$stop\n$label"]
3097	}
3098	WAVES -
3099	MIX {
3100	    set labels [lreplace $labels $i $i]
3101	}
3102    }
3103}
3104
3105# if called by clicking on the text of a label, this label will be aligned to
3106# the selection
3107# FIXME: but this isn't foolproofed because if there is another label between
3108# the one to change and the selection only the selected label
3109# (and with HTK-format the right neighbour) will be changed ...
3110
3111proc AlignLabel {x y} {
3112    global c v labels
3113
3114    set v(labchanged) 1
3115    SetUndo $labels
3116    if {[string compare [lindex [$c gettags [$c find closest\
3117	    [$c canvasx $x] [$c canvasy $y]]] 2] text] == 0} {
3118	set i [lindex [$c gettags [$c find closest\
3119		[$c canvasx $x] [$c canvasy $y]]] 0]
3120
3121	SetUndo $labels
3122	set start [Marker2Time m1]
3123	set end   [Marker2Time m2]
3124	set rest ""
3125
3126	switch $v(labfmt) {
3127	    TIMIT -
3128	    HTK {
3129		scan [lindex $labels $i] {%d %d %s %[^�]} junk junk label rest
3130		set labels [lreplace $labels $i $i "$start\n$end\n$label\n$rest"]
3131		set rest ""
3132		set j [expr $i-1]
3133		if {$j >= 0} {
3134		    scan [lindex $labels $j] {%d %d %s %[^�]} st junk label rest
3135		    set labels [lreplace $labels $j $j "$st\n$start\n$label\n$rest"]
3136		}
3137		set rest ""
3138		set j [expr $i+1]
3139		if {$j < [llength $labels]} {
3140		    scan [lindex $labels $j] {%d %d %s %[^�]} junk st label rest
3141		    set labels [lreplace $labels $j $j "$end\n$st\n$label\n$rest"]
3142		}
3143	    }
3144	    MIX {
3145		scan [lindex $labels $i] {%d %s %[^�]} junk label rest
3146		set labels [lreplace $labels $i $i "$start\n$label\n$rest"]
3147		set rest ""
3148		set j [expr $i+1]
3149		catch {scan [lindex $labels $j] {%d %s %[^�]} junk label rest}
3150		catch {set labels [lreplace $labels $j $j "$end\n$label\n$rest"]}
3151	    }
3152	    WAVES {
3153		scan [lindex $labels $i] {%f %d %s %[^�]} junk color label rest
3154		set labels [lreplace $labels $i $i "$end\n$color\n$label\n$rest"]
3155		set rest ""
3156		set j [expr $i-1]
3157		if {$j >= 0} {
3158		    scan [lindex $labels $j] {%f %d %s %[^�]} junk color label rest
3159		    set labels [lreplace $labels $j $j "$start\n$color\n$label\n$rest"]
3160		}
3161	    }
3162	}
3163
3164	$c delete bound text
3165	Redraw quick
3166    } else {
3167	puts "AlignLabel error: x=$x; y=$y"
3168    }
3169}
3170
3171proc CropLabels {cstart cend} {
3172    global labels v
3173
3174    set l {}
3175    switch $v(labfmt) {
3176	TIMIT -
3177	HTK {
3178	    foreach row $labels {
3179		set rest ""
3180		scan $row {%d %d %s %[^�]} start stop label rest]
3181		if {$cend < $start} {
3182		} elseif {$cend > $start && $cend < $stop} {
3183		    set start [expr $start - $cstart]
3184		    set stop  [expr $cend - $cstart]
3185		    lappend l "$start\n$stop\n$label\n$rest"
3186		} elseif {$cstart > $start && $cstart < $stop} {
3187		    set start 0
3188		    set stop  [expr $stop  - $cstart]
3189		    lappend l "$start\n$stop\n$label\n$rest"
3190		} elseif {$cstart < $start} {
3191		    set start [expr $start - $cstart]
3192		    set stop  [expr $stop  - $cstart]
3193		    lappend l "$start\n$stop\n$label\n$rest"
3194		}
3195	    }
3196	}
3197	MIX {
3198	    foreach row $labels {
3199		set rest ""
3200		scan $row {%d %s %[^�]} start label rest
3201		if {$cend < $start} {
3202		} elseif {$cstart > $start} {
3203		    set l [list "0\n$label\n$rest"]
3204		} elseif {$cstart < $start} {
3205		    set start [expr $start - $cstart]
3206		    lappend l "$start\n$label\n$rest"
3207		}
3208	    }
3209	}
3210	WAVES {
3211	    set flag 0
3212	    foreach row $labels {
3213		set rest ""
3214		scan $row {%f %d %s %[^�]} end color label rest
3215		if {$cend < $end && $flag} {
3216		    set end [expr $cend - $cstart]
3217		    lappend l "$end\n$color\n$label\n$rest"
3218		    break
3219		}
3220		if {$cstart < $end} {
3221		    set end [expr $end - $cstart]
3222		    lappend l "$end\n$color\n$label\n$rest"
3223		    set flag 1
3224		}
3225	    }
3226	}
3227    }
3228    return $l
3229}
3230
3231# moves the startpoint of the right label to the cursorposition
3232
3233proc GetRightLabel {x y} {
3234    global c labels v
3235
3236    set t [Coord2Time [$c canvasx $x]]
3237    set i 0
3238    set v(labchanged) 1
3239    SetUndo $labels
3240    set rest ""
3241    switch $v(labfmt) {
3242	TIMIT -
3243	HTK {
3244	    foreach l $labels {
3245		if {$t < [lindex $l 0]} { break }
3246		if {([lindex $l 0] < $t) && ([lindex $l 1] > $t)} { break }
3247		incr i
3248	    }
3249	    if {[llength $labels] <= [expr $i+1]} return
3250	    if {$t < [lindex [lindex $labels 0] 0]} {
3251		set sto [lindex [lindex $labels 0] 1]
3252		set lab [lindex [lindex $labels 0] 2]
3253		set labels [lreplace $labels 0 0 "$t\n$sto\n$lab"]
3254	    } elseif {[llength $labels] == [expr $i-1]} {
3255		set sta1 [lindex [lindex $labels $i] 0]
3256		set sto1 $t
3257		set lab1 [lindex [lindex $labels $i] 2]
3258		set labels [lreplace $labels $i $i "$sta1\n$sto1\n$lab1"]
3259		SetMsg [lindex [lindex $labels $i] 2]
3260            } else {
3261		set sta1 [lindex [lindex $labels $i] 0]
3262		set sto1 $t
3263		set lab1 [lindex [lindex $labels $i] 2]
3264		set sta2 $t
3265		set sto2 [lindex [lindex $labels [expr $i+1]] 1]
3266		set lab2 [lindex [lindex $labels [expr $i+1]] 2]
3267		set labels [lreplace $labels $i [expr $i+1] "$sta1\n$sto1\n$lab1" "$sta2\n$sto2\n$lab2"]
3268		SetMsg [lindex [lindex $labels $i] 2]
3269            }
3270        }
3271	MIX {
3272	    foreach l $labels {
3273		if {[lindex $l 0] > $t} { break }
3274		incr i
3275	    }
3276	    if {$i == [llength $labels]} return
3277	    scan [lindex $labels $i] {%d %s %[^�]} junk label rest
3278	    set labels [lreplace $labels $i $i "$t\n$label\n$rest"]
3279	    SetMsg [lindex [lindex $labels $i] 1]
3280	}
3281	WAVES {
3282	    foreach l $labels {
3283		if {([lindex $l 0] > $t)} { break }
3284		incr i
3285	    }
3286	    if {$i == [llength $labels]} return
3287	    scan [lindex $labels $i] {%f %d %s %[^�]} junk color label rest
3288	    set labels [lreplace $labels $i $i "$t\n$color\n$label\n$rest"]
3289	    SetMsg [lindex [lindex $labels $i] 1]
3290	}
3291    }
3292    $c delete bound text
3293    Redraw quick
3294}
3295
3296proc PlayLabel {x y} {
3297    global c labels v
3298
3299    set t [Coord2Time [$c canvasx $x]]
3300    set i 0
3301    switch $v(labfmt) {
3302	TIMIT -
3303	HTK {
3304	    foreach l $labels {
3305		if {([lindex $l 0] < $t) && ([lindex $l 1] > $t)} { break }
3306		incr i
3307	    }
3308	    if {[llength $labels] == $i} { incr i -1 }
3309	}
3310	MIX {
3311	    foreach l $labels {
3312		if {[lindex $l 0] > $t} { break }
3313		incr i
3314	    }
3315	    incr i -1
3316	}
3317	WAVES {
3318	    foreach l $labels {
3319		if {[lindex $l 0] > $t} { break }
3320		incr i
3321	    }
3322	}
3323    }
3324    PlayNthLab $i
3325}
3326
3327proc PlayNthLab {n} {
3328    global labels v
3329
3330    switch $v(labfmt) {
3331	TIMIT -
3332	HTK {
3333	    set start [lindex [lindex $labels $n] 0]
3334	    set stop  [lindex [lindex $labels $n] 1]
3335	    Play snd [Time2Sample $start] [Time2Sample $stop]
3336	}
3337	MIX {
3338	    set start [lindex [lindex $labels $n] 0]
3339	    if {$n == -1} { set start 0 }
3340	    catch {set stop  [lindex [lindex $labels [expr $n + 1]] 0]}
3341	    if {$stop == ""} { set stop [Coord2Time $v(width)] }
3342	    Play snd [Time2Sample $start] [Time2Sample $stop]
3343	}
3344	WAVES {
3345	    set start [lindex [lindex $labels [expr $n - 1]] 0]
3346	    if {$start == ""} { set start 0 }
3347	    set stop  [lindex [lindex $labels $n] 0]
3348	    Play snd [Time2Sample $start] [Time2Sample $stop]
3349	}
3350    }
3351}
3352
3353proc MarkLabel {x y} {
3354    global c labels v
3355
3356    set t [Coord2Time [$c canvasx $x]]
3357    set i 0
3358    switch $v(labfmt) {
3359      TIMIT -
3360      HTK {
3361          foreach l $labels {
3362              if {([lindex $l 0] < $t) && ([lindex $l 1] > $t)} { break }
3363              incr i
3364          }
3365          if {[llength $labels] == $i} { incr i -1 }
3366      }
3367      MIX {
3368          foreach l $labels {
3369              if {[lindex $l 0] > $t} { break }
3370              incr i
3371          }
3372          incr i -1
3373      }
3374      WAVES {
3375          foreach l $labels {
3376              if {[lindex $l 0] > $t} { break }
3377              incr i
3378          }
3379      }
3380    }
3381    MarkNthLab $i
3382}
3383
3384proc MarkNthLab {n} {
3385    global labels v
3386
3387    switch $v(labfmt) {
3388      TIMIT -
3389      HTK {
3390          set start [lindex [lindex $labels $n] 0]
3391          set stop  [lindex [lindex $labels $n] 1]
3392      }
3393      MIX {
3394          set start [lindex [lindex $labels $n] 0]
3395          if {$n == -1} { set start 0 }
3396          catch {set stop  [lindex [lindex $labels [expr $n + 1]] 0]}
3397          if {$stop == ""} { set stop [Coord2Time $v(width)] }
3398      }
3399      WAVES {
3400          set start [lindex [lindex $labels [expr $n - 1]] 0]
3401          if {$start == ""} { set start 0 }
3402          set stop  [lindex [lindex $labels $n] 0]
3403      }
3404      default {
3405          puts "Wrong Labelformat $v(labfmt)"
3406          return
3407      }
3408    }
3409    # cause the left marker is always m1 we have to move the marker
3410    # in the right order
3411    if {$start > [Marker2Time m2]} {
3412      PutMarker m2 $stop 0 0
3413      SendPutMarker m2 [Time2Coord $stop]
3414      PutMarker m1 $start 0 0
3415      SendPutMarker m1 [Time2Coord $start]
3416    } else {
3417      PutMarker m1 $start 0 0
3418      SendPutMarker m1 [Time2Coord $start]
3419      PutMarker m2 $stop 0 0
3420      SendPutMarker m2 [Time2Coord $stop]
3421    }
3422}
3423
3424
3425proc SetRaw {} {
3426    global v
3427
3428    StopPlay
3429    set v(smpchanged) 1
3430    snd config -rate $v(rate) -encoding $v(sfmt) -channels $v(chan)
3431    Redraw
3432    Reconf
3433}
3434
3435proc Convert {encoding rate channels} {
3436    global v c
3437
3438    SetCursor watch
3439    StopPlay
3440    $c delete speg wave
3441    cbs copy snd
3442    if [catch {
3443	if {$rate != ""} {
3444	    SetMsg "Converting sample rate [snd cget -rate] -> $rate"
3445	    snd convert -rate $rate -progress snack::progressCallback
3446	    set v(rate) [snd cget -rate]
3447	    set v(undoc) "snd copy cbs"
3448	    set v(redoc) "snd convert -rate $rate -progress snack::progressCallback"
3449	}
3450	if {$encoding != ""} {
3451	    SetMsg "Converting sample encoding [snd cget -encoding] -> $encoding"
3452	    snd convert -encoding $encoding -progress snack::progressCallback
3453	    set v(sfmt) [snd cget -encoding]
3454	    set v(undoc) "snd copy cbs"
3455	    set v(redoc) "snd convert -encoding $encoding -progress snack::progressCallback"
3456	}
3457	if {$channels != ""} {
3458	    SetMsg "Converting number of channels [snd cget -channels] -> $channels"
3459	    snd convert -channels $channels -progress snack::progressCallback
3460	    set v(chan) [snd cget -channels]
3461	    set v(undoc) "snd copy cbs"
3462	    set v(redoc) "snd convert -channels $channels -progress snack::progressCallback"
3463	}
3464    }] {
3465	SetMsg "Convert cancelled"
3466    }
3467
3468    Redraw
3469    set v(smpchanged) 1
3470    .tb.undo config -state normal
3471}
3472
3473proc Time2Sample {t} {
3474    global v
3475
3476    switch $v(labfmt) {
3477	HTK {
3478	    expr {int($t/(10000000/$v(rate)))}
3479	}
3480	TIMIT -
3481	MIX {
3482	    expr {int($t)}
3483	}
3484	WAVES {
3485	    expr {int($t*$v(rate))}
3486	}
3487    }
3488}
3489
3490proc Sample2Time {s} {
3491    global v
3492
3493    switch $v(labfmt) {
3494	HTK {
3495	    expr {int($s*(10000000.0/$v(rate)))}
3496	}
3497	TIMIT -
3498	MIX {
3499	    set s
3500	}
3501	WAVES {
3502	    expr {double($s)/$v(rate)}
3503	}
3504    }
3505}
3506
3507proc TimeRound {t} {
3508    global v
3509
3510    switch $v(labfmt) {
3511	HTK -
3512	TIMIT -
3513	MIX {
3514	    expr {int($t)}
3515	}
3516	WAVES {
3517	    expr {$t}
3518	}
3519    }
3520}
3521
3522proc Time2Coord {t} {
3523    global v
3524
3525    switch $v(labfmt) {
3526	HTK {
3527	    expr {(($t-10000000*(double($v(startsmp))/$v(rate)))/((10000000.0/$v(rate))*$v(rate)/$v(pps)))}
3528	}
3529	TIMIT -
3530	MIX {
3531	    expr {(($t - $v(startsmp)) / (double($v(rate))/$v(pps)))}
3532	}
3533	WAVES {
3534	    expr {(($t - (double($v(startsmp))/$v(rate)) )*$v(pps))}
3535	}
3536    }
3537}
3538
3539proc Time2DTime {t} {
3540    global v
3541
3542    switch $v(labfmt) {
3543	HTK {
3544	    expr {($t/10000000.0)}
3545	}
3546	WAVES {
3547	    expr {$t}
3548	}
3549	TIMIT -
3550	MIX -
3551	default {
3552	    expr {double($t)/$v(rate)}
3553	}
3554    }
3555}
3556
3557proc DTime2Time {t} {
3558    global v
3559
3560    switch $v(labfmt) {
3561	HTK {
3562	    expr {int($t*10000000.0)}
3563	}
3564	WAVES {
3565	    expr {$t}
3566	}
3567	TIMIT -
3568	MIX -
3569	default {
3570	    expr {int($t*$v(rate))}
3571	}
3572    }
3573}
3574
3575proc Coord2Time {x} {
3576    global v
3577
3578    switch $v(labfmt) {
3579	HTK {
3580	    expr {int(($x*$v(rate)/$v(pps)+$v(startsmp))*(10000000.0/$v(rate)))}
3581	}
3582	WAVES {
3583	    expr {double($x)/$v(pps)+double($v(startsmp))/$v(rate)}
3584	}
3585	TIMIT -
3586	MIX -
3587	default {
3588	    expr {int($v(startsmp)+$x*(double($v(rate))/$v(pps)))}
3589	}
3590    }
3591}
3592
3593proc Coord2Sample {x} {
3594    global v
3595
3596    expr {int($v(startsmp)+$x*double($v(rate))/$v(pps))}
3597}
3598
3599proc BoundaryEnter {x} {
3600    global c _mb
3601
3602    set _mb 1
3603    $c itemconfig current -fill red
3604    $c configure -cursor sb_h_double_arrow
3605}
3606
3607proc BoundaryLeave {x} {
3608    global c v
3609
3610    $c itemconfig current -fill $v(fg)
3611    $c configure -cursor {}
3612}
3613
3614proc MarkerEnter {x} {
3615    global c
3616
3617    $c itemconfig current -fill red
3618    $c configure -cursor sb_h_double_arrow
3619}
3620
3621proc MarkerLeave {x} {
3622    global c v
3623
3624    $c itemconfig current -fill $v(fg)
3625    $c configure -cursor {}
3626}
3627
3628proc PutMarker {m x y f} {
3629    global c v _mx _mb
3630
3631    if {$_mx == 0} return
3632    if {$y > [expr $v(waveh) + $v(spegh) + $v(timeh)]} {
3633	if {$_mb == 1 && $f == 1} {
3634	    MoveBoundary $x
3635	}
3636	return
3637    }
3638    if {$f == 1} {
3639	if {$x < 0 && [lindex [$c xview] 0] > 0} {
3640	    $c xview scroll -1 unit
3641	    update
3642	    SendXScroll
3643	}
3644	if {$x >= [winfo width $c]} {
3645	    $c xview scroll 1 unit
3646	    update
3647	    SendXScroll
3648	}
3649
3650	set xc [$c canvasx $x]
3651
3652	if {$xc < 0} { set xc 0 }
3653	if {$xc > $v(width)} { set xc $v(width) }
3654
3655	set t [Coord2Time $xc]
3656    } else {
3657	set xc [Time2Coord $x]
3658	set t $x
3659    }
3660    if {$t >= [snd length]} {
3661      set t [expr {[snd length]-1}]
3662    }
3663    $c itemconf $m -tags [list mark $t $m]
3664    $c coords $m $xc 0 $xc $v(toth)
3665
3666    if {$m == "m1"} {
3667	set tm2 [Marker2Time m2]
3668	if {$t > $tm2} {
3669	    $c itemconf m2 -tags [list mark $tm2 m3]
3670	    $c itemconf m1 -tags [list mark $t m2]
3671	    $c itemconf m3 -tags [list mark [Marker2Time m3] m1]
3672	}
3673    } else {
3674	set tm1 [Marker2Time m1]
3675	if {$t < $tm1} {
3676	    $c itemconf m1 -tags [list mark $tm1 m3]
3677	    $c itemconf m2 -tags [list mark $t m1]
3678	    $c itemconf m3 -tags [list mark [Marker2Time m3] m2]
3679	}
3680    }
3681
3682    if {$v(fillmark)} {
3683	$c coords mfill [Time2Coord [Marker2Time m1]] 0 \
3684		        [Time2Coord [Marker2Time m2]] $v(toth)
3685    }
3686
3687    set ox1 [expr $v(scrh) + [Time2DTime [Marker2Time m1]] * $v(opps) / 1000.0]
3688    set ox2 [expr $v(scrh) + [Time2DTime [Marker2Time m2]] * $v(opps) / 1000.0]
3689    .of.c coords mark $ox1 2 $ox2 30
3690
3691    if {$f == 1} {
3692	DrawZoom 1
3693	DrawSect
3694	set t1 [Marker2Time m1]
3695	set t2 [Marker2Time m2]
3696	set l  [expr $t2 - $t1]
3697	set tt1 [Time2DTime $t1]
3698	set tt2 [Time2DTime $t2]
3699	set ll  [expr $tt2 - $tt1]
3700	SetMsg [format "From: %9s to: %9s length: %9s\t(%.3f - %.3f, %.3f)"\
3701		$t1 $t2 $l $tt1 $tt2 $ll] mark
3702	set v(fromto) [list $t1 $t2 $l $tt1 $tt2 $ll]
3703    }
3704
3705    foreach p $v(plugins) {
3706	namespace inscope $p Putmark $m
3707    }
3708    update
3709}
3710
3711proc SendPutMarker {m x} {
3712    global c v
3713
3714    set xc [$c canvasx $x]
3715    if {$v(mlink) == 1} {
3716	foreach prg [winfo interps] {
3717	    if [regexp .*xs.* $prg] {
3718		if {[winfo name .] != $prg} {
3719		    set t [Coord2Time $xc]
3720		    send $prg PutMarker $m $t 0 0
3721		}
3722	    }
3723	}
3724    }
3725}
3726
3727proc Marker2Sample {m} {
3728    global c
3729
3730    Time2Sample [lindex [$c gettags $m] 1]
3731}
3732
3733proc Marker2Time {m} {
3734    global c
3735
3736    lindex [$c gettags $m] 1
3737}
3738
3739proc DrawCrossHairs {} {
3740    global c v
3741
3742    if {$v(ch)} {
3743	$c delete ch1 ch2
3744	if {$::tcl_platform(platform) == "windows"} {
3745#	    $c create line 0 0 0 0 -width 2 -stipple gray50 -tags [list ch1]\
3746#		    -fill $v(gridcolor)
3747#	    $c create line 0 0 0 0 -width 2 -stipple gray50 -tags [list ch2]\
3748#		    -fill $v(gridcolor)
3749	    $c create line 0 0 0 0 -width 1 -tags [list ch1]\
3750		    -fill $v(gridcolor)
3751	    $c create line 0 0 0 0 -width 1 -tags [list ch2]\
3752		    -fill $v(gridcolor)
3753	} else {
3754	    $c create line 0 0 0 0 -width 1 -stipple gray50 -tags [list ch1]\
3755		    -fill $v(gridcolor)
3756	    $c create line 0 0 0 0 -width 1 -stipple gray50 -tags [list ch2]\
3757		    -fill $v(gridcolor)
3758	}
3759	$c lower ch1 m1
3760	$c lower ch2 m1
3761    } else {
3762	$c delete ch1 ch2
3763    }
3764}
3765
3766proc PutCrossHairs {x y} {
3767    global c v
3768
3769    set xc [$c canvasx $x]
3770    set yc [$c canvasy $y]
3771    set f 0.0
3772    catch { set f [expr $v(topfr) * ($v(spegh) - ($yc - $v(waveh))) / $v(spegh)]}
3773    if {$f < 0.0} { set f 0.0 }
3774    if {$f > 0.5*$v(rate)} { set f [expr 0.5*$v(rate)] }
3775
3776    if {$v(ch)} {
3777	$c coords ch1 $xc 0 $xc $v(toth)
3778	$c coords ch2 0 $yc $v(width) $yc
3779	set s [Coord2Time $xc]
3780	set t [expr double($xc) / $v(pps)]
3781
3782	SetMsg "time: $t\tsample: $s\tfrequency: $f"
3783    } else {
3784	$c coords ch1 -1 -1 -1 -1
3785	$c coords ch2 -1 -1 -1 -1
3786    }
3787    if [winfo exists .sect] { DrawSectMarks f $f }
3788}
3789
3790proc OpenSectWindow {} {
3791    global s v
3792
3793    catch {destroy .sect}
3794    toplevel .sect -width $s(sectwinw) -height $s(sectwinh)
3795    wm title .sect "Spectrum section plot"
3796    wm geometry .sect +$s(sectwinx)+$s(sectwiny)
3797    pack propagate .sect 0
3798
3799    set s(ostart) ""
3800
3801    pack [frame .sect.f] -side bottom -fill x
3802    label .sect.f.lab -width 1 -relief sunken -bd 1 -anchor w
3803    pack .sect.f.lab -side left -expand yes -fill x
3804    pack [button .sect.f.exitB -text Close -command {destroy .sect}] -side left
3805    pack [canvas .sect.c -closeenough 5 -cursor draft_small -bg $v(bg)] -fill both -expand true
3806
3807    pack [frame .sect.f1]
3808    label .sect.f1.l1 -text "FFT points:" -anchor w
3809#    pack [entry .sect.f2.e1 -textvar s(fftlen) -wi 6] -side left
3810    tk_optionMenu .sect.f1.m1 s(fftlen) 64 128 256 512 1024 2048 4096 8192 16384
3811    for {set n 0} {$n < 7} {incr n} {
3812      .sect.f1.m1.menu entryconfigure $n -command DrawSect
3813    }
3814    label .sect.f1.l2 -text "Window:"
3815    tk_optionMenu .sect.f1.m2 s(wintype) \
3816	Hamming Hanning Bartlett Blackman Rectangle
3817    pack .sect.f1.l1 .sect.f1.m1 .sect.f1.l2 .sect.f1.m2 -side left
3818#    pack [label .sect.f2.l2 -text "Preemphasis:" -anchor w] -side left
3819#    pack [entry .sect.f2.e2 -textvar s(ref) -wi 6] -side left
3820
3821    pack [frame .sect.f2]
3822    label .sect.f2.l1 -text "Analysis:"
3823    tk_optionMenu .sect.f2.m1 s(atype) FFT LPC
3824    .sect.f2.m1.menu entryconfigure 0 -command [list LPCcontrols disabled]
3825    .sect.f2.m1.menu entryconfigure 1 -command [list LPCcontrols normal]
3826    label .sect.f2.l2 -text "Order:"
3827    entry .sect.f2.e -textvariable s(lpcorder) -width 3
3828    scale .sect.f2.s -variable s(lpcorder) -from 1 -to 40 -orient horiz \
3829	-length 80 -show no
3830    bind .sect.f2.s <Button1-Motion> DrawSect
3831    pack .sect.f2.l1 .sect.f2.m1 .sect.f2.l2 .sect.f2.e .sect.f2.s -side left
3832    if {$s(atype) != "LPC"} { LPCcontrols disabled }
3833    if {$s(lpcorder) < 1} { set s(lpcorder) 20 }
3834
3835    pack [frame .sect.f3]
3836    pack [label .sect.f3.l2 -text "Reference:" -anchor w] -side left
3837    pack [entry .sect.f3.e2 -textvar s(ref) -wi 6] -side left
3838    pack [label .sect.f3.u1 -text "dB" -anchor w] -side left
3839    pack [label .sect.f3.l3 -text "Range:" -anchor w] -side left
3840    pack [entry .sect.f3.e3 -textvar s(range) -wi 5] -side left
3841    pack [label .sect.f3.u2 -text "dBfs" -anchor w] -side left
3842
3843#    label $w.r.f11.l -text "Analysis bandwidth (Hz):" -width 25 -anchor w
3844#    entry $w.r.f11.e -textvar s(anabw) -wi 6
3845#    pack $w.r.f11.l $w.r.f11.e -side left
3846
3847    pack [frame .sect.f4]
3848    pack [button .sect.f4.lockB -text Lock -command {set s(ostart) $s(start);set s(oend) $s(end)}] -side left
3849    pack [button .sect.f4.printB -text Print... -command {Print .sect.c $s(sectwinh)}] -side left
3850    pack [button .sect.f4.exportB -text Export... -command Export] -side left
3851
3852    update idletasks
3853    DrawSect
3854
3855    bind .sect <Configure> DrawSect
3856    bind .sect <Any-Key> DrawSect
3857    bind .sect.c <ButtonPress-1>  { set s(rx) %x; set s(ry) %y ;.sect.c coords relmark 0 0 0 0;.sect.c coords df -10 -10;.sect.c coords db -10 -10}
3858    bind .sect.c <ButtonRelease-1>  { set s(rx) -1 }
3859    bind .sect.c <Motion>  {DrawSectMarks %x %y}
3860    bind .sect.c <Leave>  {.sect.c coords sx -1 -1 -1 -1;.sect.c coords sy -1 -1 -1 -1}
3861}
3862
3863proc LPCcontrols {state} {
3864  .sect.f2.e configure -state $state
3865  .sect.f2.s configure -state $state
3866}
3867
3868proc DrawSect {} {
3869    global c s v
3870
3871    if [winfo exists .sect] {
3872	set geom [lindex [split [wm geometry .sect] +] 0]
3873	set s(sectwinw) [lindex [split $geom x] 0]
3874	set s(sectwinh) [lindex [split $geom x] 1]
3875        set s(sectwinx) [lindex [split [wm geometry .sect] +] 1]
3876        set s(sectwiny) [lindex [split [wm geometry .sect] +] 2]
3877	set s(sectw) [expr [winfo width .sect.c] - 25]
3878	set s(secth) [expr [winfo height .sect.c] - 20]
3879	set s(sectcw) [winfo width .sect.c]
3880	set s(sectch) [winfo height .sect.c]
3881
3882	set s(start) [Marker2Sample m1]
3883	set s(end)   [Marker2Sample m2]
3884	if {$s(start) == $s(end)} { set s(start) [expr $s(end) - 1]}
3885	.sect.c delete sect
3886        set s(top) [expr int(($s(ref) + $s(range)) / 10.0)]
3887        set s(bot) [expr int($s(ref) / 10.0 )]
3888
3889catch {
3890	if {$s(ostart) != ""} {
3891	    .sect.c create section 25 0 -sound snd -height $s(secth)\
3892		    -width $s(sectw) -maxvalue [expr 10.0*$s(top)] \
3893		    -minvalue [expr 10.0*$s(bot)] \
3894		    -start $s(ostart) -end $s(oend) -tags sect \
3895		    -fftlen $s(fftlen) -analysistype $s(atype) \
3896		    -lpcorder $s(lpcorder) \
3897		    -winlen $s(fftlen) -channel $v(vchan) -fill red \
3898		    -topfr $v(topfr) -windowtype $s(wintype)
3899	}
3900	.sect.c create section 25 0 -sound snd -height $s(secth) \
3901		-width $s(sectw) -maxvalue [expr 10.0*$s(top)] \
3902		-minval [expr 10.0*$s(bot)] \
3903		-start $s(start) -end $s(end) -tags sect -fftlen $s(fftlen) \
3904		-winlen $s(fftlen) -channel $v(vchan) -frame 1 \
3905		-debug $::debug -fill $v(fg) -analysistype $s(atype) \
3906		-lpcorder $s(lpcorder) -topfr $v(topfr) -windowtype $s(wintype)
3907    }
3908	.sect.c create text -10 -10 -text df: -font $v(sfont) -tags df \
3909		-fill blue
3910	.sect.c create text -10 -10 -text "0 db" -font $v(sfont) -tags db \
3911		-fill red
3912	set pps [expr int(double($s(sectw))/($v(topfr)/1000.0) + .5)]
3913	snack::timeAxis .sect.c 25 $s(secth) $s(sectw) 20 $pps \
3914		-tags sect -fill $v(fg) -font $v(sfont)
3915
3916	for {set i $s(top)} {$i > $s(bot)} {incr i -1} {
3917	    set lab [expr 10 * $i]
3918	    .sect.c create text 0 \
3919		   [expr ($i - $s(top)) * $s(secth) / ($s(bot) - $s(top))] \
3920		    -text $lab \
3921		    -tags sect -font $v(sfont) -anchor w -fill $v(fg)
3922	}
3923
3924	.sect.c create text 2 2 -text dB -font $v(sfont) -tags sect -anchor nw\
3925		-fill $v(fg)
3926	.sect.c create text $s(sectw) $s(secth) -text kHz -font $v(sfont)\
3927		-tags sect -anchor nw -fill $v(fg)
3928    }
3929}
3930
3931proc Export {} {
3932    global s v f
3933
3934    set s(start) [Marker2Sample m1]
3935    set s(end)   [Marker2Sample m2]
3936
3937    if {$s(start) == $s(end)} { set s(start) [expr $s(end) - 1]}
3938
3939    set ps [snd dBPowerSpectrum -start $s(start) -end $s(end) \
3940	    -fftlen $s(fftlen) -windowlen $s(fftlen) -channel $v(vchan) \
3941	    -windowtype $s(wintype) -analysistype $s(atype) \
3942	    -lpcorder $s(lpcorder)]
3943
3944    set file [tk_getSaveFile -title "Export spectral data" -initialfile spectrum.txt]
3945    if {$file == ""} return
3946    if {[catch {open $file w} out]} {
3947	return $out
3948    } else {
3949	set df [expr {([snd cget -rate] / 2.0) / $s(fftlen)}]
3950	set freq [expr {$df / 2.0}]
3951	puts $out "File: $f(sndfile) $s(start)-$s(end)"
3952	puts $out "$s(wintype) window, $s(fftlen) points"
3953	puts $out "Frequency (Hz) Level (dB)"
3954	foreach e $ps {
3955	    puts $out [format "%f\t%f" $freq $e]
3956	    set freq [expr {$freq + $df}]
3957	}
3958	close $out
3959    }
3960}
3961
3962proc DrawSectMarks {x y} {
3963    global s v
3964
3965    if {[.sect.c find withtag sm] == ""} {
3966	if {$::tcl_platform(platform) == "windows"} {
3967#	    .sect.c create line 0 0 0 $s(sectch) -width 2 -stipple gray50 -tags [list sx sm] -fill $v(fg)
3968#	    .sect.c create line 0 0 $s(sectcw) 0 -width 2 -stipple gray50 -tags [list sy sm] -fill $v(fg)
3969#	    .sect.c create line 0 0 0 0 -width 2 -stipple gray50 -tags [list relmark] -fill $v(fg)
3970	    .sect.c create line 0 0 0 $s(sectch) -width 1 -tags [list sx sm] -fill $v(fg)
3971	    .sect.c create line 0 0 $s(sectcw) 0 -width 1 -tags [list sy sm] -fill $v(fg)
3972	    .sect.c create line 0 0 0 0 -width 1 -tags [list relmark] -fill $v(fg)
3973	} else {
3974	    .sect.c create line 0 0 0 $s(sectch) -width 1 -stipple gray50 -tags [list sx sm] -fill $v(fg)
3975	    .sect.c create line 0 0 $s(sectcw) 0 -width 1 -stipple gray50 -tags [list sy sm] -fill $v(fg)
3976	    .sect.c create line 0 0 0 0 -width 1 -stipple gray50 -tags [list relmark relmarkux] -arrow both -fill $v(fg)
3977	}
3978    }
3979
3980    if {$x != "f"} {
3981	set xc [.sect.c canvasx $x]
3982	set yc [.sect.c canvasx $y]
3983    } else {
3984	set xc [expr 25+int($s(sectw) * $y / $v(topfr))]
3985	set yc [lindex [.sect.c coords sy] 1]
3986    }
3987    .sect.c coords sx $xc 0 $xc $s(sectch)
3988    .sect.c coords sy 0 $yc $s(sectcw) $yc
3989    set f [expr int(double($v(topfr)) * ($xc - 25) / $s(sectw) + .5)]
3990    if {$f < 0} { set f 0 }
3991    set db [format "%.1f" [expr 10.0 * ($s(bot) - $s(top)) * double($yc) / $s(secth) + 10.0 * $s(top)]]
3992
3993    if {$s(rx) != -1} {
3994	set rx [.sect.c canvasx $s(rx)]
3995	set ry [.sect.c canvasy $s(ry)]
3996	.sect.c coords relmark $rx $ry $xc $yc
3997	.sect.c coords df [expr $rx + ($xc-$rx)/2] $ry
3998	.sect.c coords db $rx [expr $ry + ($yc-$ry)/2]
3999
4000	set df [expr abs(int($v(topfr) * ($rx - $xc)/ $s(sectw)))]
4001	.sect.c itemconf df -text "df: $df"
4002	set ddb [format "%.1f" [expr $s(range) * ($ry - $yc) / $s(secth)]]
4003	.sect.c itemconf db -text "db: $ddb"
4004    } else {
4005#	.sect.c coords relmark 0 0 0 0
4006#	.sect.c coords df -10 -10
4007#	.sect.c coords db -10 -10
4008    }
4009
4010    .sect.f.lab config -text "Frequency: $f Hz, amplitude: $db dB"
4011}
4012
4013proc OpenZoomWindow {} {
4014    global z v
4015
4016    catch {destroy .zoom}
4017    catch {destroy .zmenu}
4018    toplevel .zoom -width $z(zoomwinw) -height $z(zoomwinh)
4019    wm title .zoom "Zoom view"
4020    wm geometry .zoom +$z(zoomwinx)+$z(zoomwiny)
4021    pack propagate .zoom 0
4022
4023    frame .zoom.f
4024    label .zoom.f.lab -text "Press right mouse button for menu" -width 1 -relief sunken -bd 1 -anchor w
4025    pack .zoom.f.lab -side left -expand yes -fill x
4026    pack [button .zoom.f.xzoomB -text X-zoom -command {DrawZoom 1}] -side left
4027    pack [button .zoom.f.yizoomB -text "Y-zoom in" -command {DrawZoom 2}] -side left
4028    pack [button .zoom.f.yozoomB -text "Y-zoom out" -command {DrawZoom .5}] -side left
4029    pack [button .zoom.f.exitB -text Close -command {destroy .zoom}] -side left
4030    pack .zoom.f -side bottom -fill x
4031    pack [canvas .zoom.c -closeenough 5 -bg $v(bg)] -fill both -expand true
4032
4033    update idletasks
4034    DrawZoom 1
4035
4036    menu .zmenu -tearoff false
4037    .zmenu add command -label "Play Range" -command PlayMark
4038    .zmenu add command -label "Mark Start" -command {PutZMarker zm1 $x}
4039    .zmenu add command -label "Mark End" -command {PutZMarker zm2 $x}
4040    if {[string match macintosh $::tcl_platform(platform)] || \
4041	    [string match Darwin $::tcl_platform(os)]} {
4042	bind $c <Control-1> \
4043		{set x %x; set y %y; catch {tk_popup .zmenu %X %Y 0}}
4044    } else {
4045	bind .zoom.c <3> {set x %x; set y %y; catch {tk_popup .zmenu %X %Y 0}}
4046    }
4047    bind .zoom <Configure> { DrawZoom 1 }
4048}
4049
4050proc DrawZoom {factor} {
4051    global z v f
4052
4053    if [winfo exists .zoom] {
4054	set geom [lindex [split [wm geometry .zoom] +] 0]
4055	set z(zoomwinw) [lindex [split $geom x] 0]
4056	set z(zoomwinh) [lindex [split $geom x] 1]
4057        set z(zoomwinx) [lindex [split [wm geometry .zoom] +] 1]
4058        set z(zoomwiny) [lindex [split [wm geometry .zoom] +] 2]
4059	set z(zoomwavw) [winfo width .zoom.c]
4060	set z(zoomwavh) [winfo height .zoom.c]
4061	set z(f) [expr $z(f) * $factor]
4062
4063	set start [Marker2Sample m1]
4064	set end   [Marker2Sample m2]
4065
4066	if {$start == $end} { set end [expr $start + 1]}
4067	set zoompps [expr double($z(zoomwavw)) * $v(rate) / ($end - $start)]
4068
4069	.zoom.c delete zoomwave zm1 zm2
4070	if {$v(linkfile) && $f(sndfile) != ""} {
4071	    .zoom.c create waveform 0 [expr $z(zoomwavh)/2] -sound snd \
4072		    -height [expr int($z(zoomwavh) * $z(f))] \
4073		    -start $start -end $end -channel $v(vchan) \
4074		    -pixels $zoompps -tags zoomwave -anchor w -fill $v(fg) \
4075	    -shapefile [file rootname [file tail $f(spath)$f(sndfile)]].shape
4076	} else {
4077	    .zoom.c create waveform 0 [expr $z(zoomwavh)/2] -sound snd \
4078		    -height [expr int($z(zoomwavh) * $z(f))] \
4079		    -start $start -end $end	-channel $v(vchan) \
4080		    -pixels $zoompps -tags zoomwave -anchor w -fill $v(fg)
4081	}
4082	.zoom.c create line 1 0 1 $z(zoomwavh) -width 1 -tags zm1 -fill $v(fg)
4083	.zoom.c create line [expr $z(zoomwavw) - 1] 0 [expr $z(zoomwavw) - 1] $z(zoomwavh) -width 1 -tags zm2 -fill $v(fg)
4084	.zoom.c bind zm1 <B1-Motion> { PutZMarker zm1 %x }
4085	.zoom.c bind zm2 <B1-Motion> { PutZMarker zm2 %x }
4086	.zoom.c bind zm1 <ButtonPress-1> { set _mx 0 }
4087	.zoom.c bind zm2 <ButtonPress-1> { set _mx 0 }
4088	.zoom.c bind zm1 <ButtonRelease-1> { set _mx 0 }
4089	.zoom.c bind zm2 <ButtonRelease-1> { set _mx 0 }
4090	bind .zoom.c <ButtonPress-1>   { PutZMarker zm1 %x; set _mx 1 }
4091	bind .zoom.c <ButtonRelease-1> { PutZMarker zm2 %x; set _mx 1}
4092	set z(zoomt1) [Marker2Time m1]
4093	set z(zoomt2) [Marker2Time m2]
4094    }
4095}
4096
4097proc PutZMarker {m x} {
4098    global z _mx
4099
4100    if {$_mx == 0} return
4101
4102    set xc [.zoom.c canvasx $x]
4103    if {$xc < 0} { set xc 0 }
4104    if {$xc > $z(zoomwavw)} { set xc $z(zoomwavw) }
4105    .zoom.c coords $m $xc 0 $xc $z(zoomwavh)
4106
4107    set t [TimeRound [expr $z(zoomt1) + ($z(zoomt2) - $z(zoomt1)) * double($xc) / $z(zoomwavw)]]
4108    set n [Time2Sample $t]
4109    set s [snd sample $n]
4110    if {$m == "zm1"} {
4111	.zoom.f.lab config -text "Marker 1 at $n ($s)"
4112	PutMarker m1 $n 0 0
4113    } else {
4114	.zoom.f.lab config -text "Marker 2 at $n ($s)"
4115	PutMarker m2 $n 0 0
4116    }
4117}
4118
4119proc WS {} {
4120    catch {destroy .ws}
4121    toplevel .ws
4122    wm title .ws "WaveSurfer window"
4123
4124    lappend ::auto_path /afs/tmh.kth.se/tmh/home/speech/kare/wavesurfer/src
4125
4126    package require -exact wsurf 1.0
4127
4128    set w [wsurf .ws.ws -collapser 0 -title ""]
4129    pack $w -expand 0 -fill both
4130    $w configure -sound snd
4131    $w configure -configuration ../wavesurfer/src/configurations/Spectrogram.conf
4132    update idletasks
4133    $w xzoom 0.4 0.6
4134    $w xscroll moveto 0.4
4135}
4136
4137proc Version {} {
4138    global c v
4139
4140    SetMsg "xs version $v(p_version), settings for $v(s_version)"
4141    catch {::http::geturl http://www.speech.kth.se/snack/xs.html\
4142	    -command VersionMore}
4143    set c .cf.fc.c
4144}
4145
4146proc VersionMore {token} {
4147    global v
4148
4149    set data [::http::data $token]
4150    regexp {version is ([0-9].[0-9])} $data junk version
4151    SetMsg "xs version $v(p_version), settings for $v(s_version), current download version is $version"
4152}
4153
4154#
4155# Miscellaneous subroutines
4156#
4157
4158proc Help {url} {
4159    global v lab_path
4160
4161    if {$::tcl_platform(platform) == "windows"} {
4162	if {[string match $::tcl_platform(os) "Windows NT"]} {
4163	    exec $::env(COMSPEC) /c start $url &
4164	} {
4165	    exec start $url &
4166	}
4167    } else {
4168	if [catch {exec sh -c "netscape -remote 'openURL($url)' -raise"} res] {
4169	    if [string match *netscape* $res] {
4170		exec sh -c "netscape $url" &
4171	    }
4172	}
4173    }
4174}
4175
4176proc NewWin {} {
4177    global f
4178
4179    if {$::tcl_platform(platform) == "windows"} {
4180	exec [info nameofexecutable] $f(prog) &
4181    } else {
4182	exec $f(prog) -geometry [xsGetGeometry] &
4183    }
4184}
4185
4186proc Reset {} {
4187    global v f s v_copy f_copy s_copy
4188
4189    array set v $v_copy
4190    array set f $f_copy
4191    array set s $s_copy
4192}
4193
4194proc Settings {} {
4195    global v c f s v_copy f_copy s_copy
4196
4197    StopPlay
4198    set w .dim
4199    catch {destroy $w}
4200    toplevel $w
4201    wm title $w {Settings}
4202
4203    set start [Coord2Sample [$c canvasx [expr [winfo width .cf.fc]/2 - 100]]]
4204    set end   [Coord2Sample [$c canvasx [expr [winfo width .cf.fc]/2 + 100]]]
4205
4206    set v_copy [array get v]
4207    set f_copy [array get f]
4208    set s_copy [array get s]
4209
4210    pack [frame $w.ll] -side left -anchor e
4211    pack [canvas $w.ll.c -height [expr $v(waveh)+$v(spegh)] -width 200 \
4212	    -highlightthickness 0]
4213
4214    pack [frame $w.l] -side left -anchor n -fill y
4215    pack [label $w.l.l1 -text Appearance:]
4216
4217    pack [frame $w.l.f3]
4218    pack [label $w.l.f3.l -text "Time scale (pixels/second):" -width 25 -anchor w] -side left
4219    pack [entry $w.l.f3.e -textvar v(pps) -wi 6] -side left
4220    pack [scale $w.l.f3.s -variable v(pps) -orient horiz -from 1 -to 1000 -command "$w.ll.c itemconf both -width 200 -start $start -pixels " -showvalue no] -side left
4221
4222    pack [frame $w.l.f1]
4223    pack [label $w.l.f1.l -text "Waveform height:" -width 25 -anchor w] -side left
4224    pack [entry $w.l.f1.e -textvar v(waveh) -wi 6] -side left
4225    pack [scale $w.l.f1.s -variable v(waveh) -orient horiz -from 0 -to 1000 -showvalue no -command {.dim.ll.c configure -height [expr $v(waveh) + $v(spegh)];.dim.ll.c coords speg 0 $v(waveh);.dim.ll.c itemconf wave -height }] -side left
4226
4227    pack [frame $w.l.f2]
4228    pack [label $w.l.f2.l -text "Spectrogram height:" -width 25 -anchor w] -side left
4229    pack [entry $w.l.f2.e -textvar v(spegh) -wi 6] -side left
4230    pack [scale $w.l.f2.s -variable v(spegh) -orient horiz -from 0 -to 1000 -command {.dim.ll.c configure -height [expr $v(waveh) + $v(spegh)];.dim.ll.c itemconf speg -height } -showvalue no] -side left
4231
4232    pack [frame $w.l.f20]
4233    pack [label $w.l.f20.l -text "Cut spectrogram at freq:" -width 25 -anchor w] -side left
4234    pack [entry $w.l.f20.e -textvar v(topfr) -wi 6] -side left
4235    pack [scale $w.l.f20.s -variable v(topfr) -orient horiz -from 0 -to [expr $v(rate)/2] -command "DrawSect;$w.ll.c itemconf speg -topfreq " -showvalue no] -side left
4236
4237    pack [frame $w.l.f30]
4238    pack [label $w.l.f30.l -text "Brightness" -width 25 -anchor w] -side left
4239    pack [entry $w.l.f30.e -textvar v(brightness) -wi 6] -side left
4240    pack [scale $w.l.f30.b -variable v(brightness) -showvalue no \
4241	    -orient horiz -command "$w.ll.c itemconf speg -brightness " \
4242	    -from -100 -to 100 -res 0.1]
4243
4244    pack [frame $w.l.f31]
4245    pack [label $w.l.f31.l -text "Contrast" -width 25 -anchor w] -side left
4246    pack [entry $w.l.f31.e -textvar v(contrast) -wi 6] -side left
4247    pack [scale $w.l.f31.c -variable v(contrast) -showvalue no\
4248	    -orient horiz -command "$w.ll.c itemconf speg -contrast" \
4249	    -from -100 -to 100 -res 0.1]
4250
4251#    pack [frame $w.l.f21]
4252#    label $w.l.f21.l -text "Scroll area width:" -width 25 -anchor w
4253#    entry $w.l.f21.e -textvar v(scrw) -wi 6
4254#    pack $w.l.f21.l $w.l.f21.e -side left
4255
4256    pack [frame $w.l.f41]
4257    label $w.l.f41.l -text "Foreground color:" -width 25 -anchor w
4258    entry $w.l.f41.e -textvar v(fg) -wi 6
4259    pack $w.l.f41.l $w.l.f41.e -side left
4260    bind $w.l.f41.e <Key-Return> {.dim.ll.c itemconf wave -fill $v(fg)}
4261
4262    pack [frame $w.l.f41b]
4263    label $w.l.f41b.l -text "Background color:" -width 25 -anchor w
4264    entry $w.l.f41b.e -textvar v(bg) -wi 6
4265    pack $w.l.f41b.l $w.l.f41b.e -side left
4266    bind $w.l.f41b.e <Key-Return> {$c config -bg $v(bg); .cf.fyc.yc config -bg $v(bg); catch {.zoom.c config -bg $v(bg)}; catch {.sect.c config -bg $v(bg)}}
4267
4268    pack [frame $w.l.f42]
4269    label $w.l.f42.l -text "Grid frequency spacing (Hz):" -width 25 -anchor w
4270    entry $w.l.f42.e -textvar v(gridfspacing) -wi 6
4271    pack $w.l.f42.l $w.l.f42.e -side left
4272    bind $w.l.f42.e <Key-Return> {.dim.ll.c itemconf speg -gridf $v(gridfspacing)}
4273
4274    pack [frame $w.l.f43]
4275    label $w.l.f43.l -text "Grid time spacing: (s)" -width 25 -anchor w
4276    entry $w.l.f43.e -textvar v(gridtspacing) -wi 6
4277    pack $w.l.f43.l $w.l.f43.e -side left
4278    bind $w.l.f43.e <Key-Return> {.dim.ll.c itemconf speg -gridt $v(gridtspacing)}
4279
4280    pack [frame $w.l.f44]
4281    label $w.l.f44.l -text "Grid color:" -width 25 -anchor w
4282    entry $w.l.f44.e -textvar v(gridcolor) -wi 6
4283    pack $w.l.f44.l $w.l.f44.e -side left
4284    bind $w.l.f44.e <Key-Return> {DrawCrossHairs;.dim.ll.c itemconf speg -gridc $v(gridcolor)}
4285
4286    pack [frame $w.l.f45]
4287    label $w.l.f45.l -text "Spectrogram color:" -width 25 -anchor w
4288    tk_optionMenu $w.l.f45.cm v(cmap) grey color1 color2
4289    $w.l.f45.cm.menu entryconfigure 0 -command {.dim.ll.c itemconf speg -col $v($v(cmap))}
4290    $w.l.f45.cm.menu entryconfigure 1 -command {.dim.ll.c itemconf speg -col $v($v(cmap))}
4291    $w.l.f45.cm.menu entryconfigure 2 -command {.dim.ll.c itemconf speg -col $v($v(cmap))}
4292    pack $w.l.f45.l $w.l.f45.cm -side left
4293
4294    pack [frame $w.r] -side left -anchor n -fill y -expand true
4295
4296    pack [label $w.r.l2 -text "Spectrogram analysis:"]
4297
4298    pack [frame $w.r.f1]
4299    label $w.r.f1.l -text "FFT window length (points):" -width 25 -anchor w
4300    entry $w.r.f1.e -textvar v(fftlen) -wi 6
4301    pack $w.r.f1.l $w.r.f1.e -side left
4302    bind $w.r.f1.e <Key-Return> {.dim.ll.c itemconf speg -fftlen $v(fftlen)}
4303
4304    pack [frame $w.r.f2]
4305    label $w.r.f2.l -text "Analysis bandwidth (Hz):" -width 25 -anchor w
4306    entry $w.r.f2.e -textvar v(anabw) -wi 6
4307    pack $w.r.f2.l $w.r.f2.e -side left
4308    bind $w.r.f2.e <Key-Return> {.dim.ll.c itemconf speg -winlen [expr int($v(rate) / $v(anabw))]}
4309
4310    pack [frame $w.r.f3]
4311    label $w.r.f3.l -text "Pre-emphasis factor:" -width 25 -anchor w
4312    entry $w.r.f3.e -textvar v(preemph) -wi 6
4313    pack $w.r.f3.l $w.r.f3.e -side left
4314    bind $w.r.f3.e <Key-Return> {.dim.ll.c itemconf speg -preem $v(preemph)}
4315
4316#    pack [label $w.r.l3 -text "Spectrum section analysis:"] -pady 10
4317
4318#    pack [frame $w.r.f10]
4319#    label $w.r.f10.l -text "FFT window length (points):" -width 25 -anchor w
4320#    entry $w.r.f10.e -textvar s(fftlen) -wi 6
4321#    pack $w.r.f10.l $w.r.f10.e -side left
4322
4323#    pack [frame $w.r.f11]
4324#    label $w.r.f11.l -text "Analysis bandwidth (Hz):" -width 25 -anchor w
4325#    entry $w.r.f11.e -textvar s(anabw) -wi 6
4326#    pack $w.r.f11.l $w.r.f11.e -side left
4327
4328##    pack [button $w.r.sectB -text Apply -command DrawSect] -pady 5
4329#    bind $w.r.f10.e <Key-Return> DrawSect
4330#    bind $w.r.f11.e <Key-Return> DrawSect
4331
4332#    pack [frame $w.r.f5]
4333#    label $w.r.f5.l -text "Label font:" -width 11 -anchor w
4334#    entry $w.r.f5.e -textvar v(font) -wi 20
4335#    pack $w.r.f5.l $w.r.f5.e -side left
4336
4337#    pack [frame $w.r.f6]
4338#    label $w.r.f6.l -text "Axes font:" -width 11 -anchor w
4339#    entry $w.r.f6.e -textvar v(sfont) -wi 20
4340#    pack $w.r.f6.l $w.r.f6.e -side left
4341#    bind $w.r.f6.e <Key-Return> DrawSect
4342
4343#    pack [label $w.r.l4 -text "Raw/unknown file input:"] -pady 10
4344#    pack [frame $w.r.f12]
4345#    label $w.r.f12.l -text "Unknown file header size:" -width 25 -anchor w
4346#    entry $w.r.f12.e -textvar f(skip) -wi 6
4347#    pack $w.r.f12.l $w.r.f12.e -side left
4348
4349#    pack [frame $w.r.f9]
4350#    label $w.r.f9.l -text "Byte order of sample data:" -width 25 -anchor w
4351#    entry $w.r.f9.e -textvar f(byteOrder) -wi 12
4352#    pack $w.r.f9.l $w.r.f9.e -side left
4353
4354    pack [checkbutton $w.r.b5 -text "Use audio server at:" -var v(remote)] -pady 10
4355    pack [frame $w.r.f13]
4356    label $w.r.f13.l1 -text "Host" -width 4
4357    entry $w.r.f13.e1 -textvar v(ashost) -wi 20
4358    label $w.r.f13.l2 -text "Port" -width 4
4359    entry $w.r.f13.e2 -textvar v(asport) -wi 5
4360    pack $w.r.f13.l1 $w.r.f13.e1 $w.r.f13.l2 $w.r.f13.e2 -side left
4361
4362#    pack [label $w.r.l5 -text "Browser command:"] -pady 5
4363#    pack [frame $w.r.f16]
4364#    entry $w.r.f16.e -textvar v(browser) -wi 30
4365#    pack $w.r.f16.e -side left
4366
4367    pack [label $w.r.l6 -text "Initial path:"]
4368    pack [frame $w.r.f14]
4369    entry $w.r.f14.e -textvar f(ipath) -wi 30
4370    pack $w.r.f14.e -side left
4371
4372    pack [label $w.r.l7 -text "Initial http:"]
4373    pack [frame $w.r.f15]
4374    entry $w.r.f15.e -textvar f(ihttp) -wi 30
4375    pack $w.r.f15.e -side left
4376
4377    pack [frame $w.r.f] -anchor e -pady 5 -padx 5 -side bottom
4378    pack [button $w.r.f.okB -text OK -wi 6 -command {Redraw;destroy .dim}] -side right
4379    pack [button $w.r.f.appB -text Apply -wi 6 -command Redraw] -side right
4380    pack [button $w.r.f.exitB -text Cancel -command {Reset;DrawSect;Redraw;destroy .dim}] -side right
4381    update
4382
4383    if {$v(linkfile) && $f(sndfile) != ""} {
4384	.dim.ll.c create waveform 0 0 -sound snd -height $v(waveh) -width 200 \
4385		-pixels $v(pps) -tags [list wave both] -start $start \
4386		-channel $v(vchan) -fill $v(fg) -frame yes -debug 0 \
4387	-shapefile [file rootname [file tail $f(spath)$f(sndfile)]].shape
4388    } else {
4389	.dim.ll.c create waveform 0 0 -sound snd -height $v(waveh) -width 200 \
4390		-pixels $v(pps) -tags [list wave both] -start $start \
4391		-channel $v(vchan) -fill $v(fg) -frame yes -debug 0
4392    }
4393    if {$v(spegh) > 0} {
4394	.dim.ll.c create spectrogram 0 $v(waveh) -sound snd -fftlen $v(fftlen)\
4395		-height $v(spegh) -width 200 -pixels $v(pps) \
4396		-preemph $v(preemph) -topfr $v(topfr) \
4397		-start $start -tags [list speg both] \
4398		-contrast $v(contrast) \
4399		-brightness $v(brightness) -gridtspacing $v(gridtspacing) \
4400		-gridfspacing $v(gridfspacing) -channel $v(vchan) \
4401		-colormap $v($v(cmap)) -gridcol $v(gridcolor)
4402    }
4403}
4404
4405proc Plugins {} {
4406    global v
4407
4408    set w .plugins
4409    catch {destroy $w}
4410    toplevel $w
4411    wm title $w {Plug-ins}
4412
4413    pack [ label $w.lPlugins -text "Installed plug-ins:"]
4414    pack [ frame $w.f] -fill both -expand true
4415    pack [ scrollbar $w.f.scroll -command "$w.f.list yview"] -side right -fill y
4416    listbox $w.f.list -yscroll "$w.f.scroll set" -setgrid 1 -height 6 -width 50
4417    pack $w.f.list -side left -expand true -fill both
4418    foreach e $v(pluginfiles) {
4419	$w.f.list insert end $e
4420    }
4421
4422    pack [ label $w.lDesc -text Description:]
4423    pack [ frame $w.f2] -fill x
4424    pack [ text $w.f2.text -height 4 -wrap word] -fill x -expand true
4425
4426    pack [ frame $w.f3]
4427    pack [ button $w.f3.b1 -text Load... -command "PluginsAdd $w"] -side left
4428    pack [ button $w.f3.b2 -text Unload -command "PluginsRemove $w"] -side left
4429    pack [ button $w.f3.b3 -text Close -command [list destroy $w]] -side left
4430
4431    bind $w.f.list <ButtonRelease-1> {.plugins.f2.text delete 0.0 end;.plugins.f2.text insert end [namespace inscope [lindex $v(plugins) [.plugins.f.list curselection]] Describe]}
4432}
4433
4434proc PluginsAdd {w} {
4435    global v
4436
4437    set types {
4438	{{xs Plug-in Files} {.plg}}
4439	{{Tcl Files} {.tcl}}
4440	{{All Files}    *  }
4441    }
4442    set file [tk_getOpenFile -title "Select plug-in" -filetypes $types]
4443    if {$file == ""} return
4444    if {[source $file] == "fail"} return
4445    $w.f.list insert end $file
4446    set v(pluginfiles) [$w.f.list get 0 end]
4447}
4448
4449proc PluginsRemove {w} {
4450    global v
4451
4452    set i [$w.f.list curselection]
4453    namespace inscope [lindex $v(plugins) $i] Unload
4454    set v(plugins) [lreplace $v(plugins) $i $i]
4455    catch {$w.f.list delete $i}
4456    set v(pluginfiles) [$w.f.list get 0 end]
4457    $w.f2.text delete 0.0 end
4458}
4459
4460proc Print {canvas h} {
4461    global v
4462
4463    set w .print
4464    catch {destroy $w}
4465    toplevel $w
4466    wm title $w {Printer setup}
4467
4468    set v(lastpage) [expr int(($v(width)+999)/1000)]
4469    set v(firstpage) 1
4470
4471    frame $w.f1
4472    label $w.f1.l1 -text "Pages:"
4473    entry $w.f1.e1 -textvar v(firstpage) -width 3
4474    label $w.f1.l2 -text "to"
4475    entry $w.f1.e2 -textvar v(lastpage) -width 3
4476    pack $w.f1.l1 $w.f1.e1 $w.f1.l2 $w.f1.e2 -side left
4477
4478    frame $w.f2
4479    label $w.f2.l1 -text "Print command:" -wi 16
4480    entry $w.f2.e1 -textvar v(printcmd)   -wi 40
4481    button $w.f2.b1 -text Print -command [list DoPrint print $canvas $h] -wi 8
4482    pack $w.f2.l1 $w.f2.e1 $w.f2.b1 -side left
4483    bind $w.f2.e1 <Key-Return> [list DoPrint print $canvas $h]
4484
4485    frame $w.f3
4486    label $w.f3.l1 -text "Preview command:" -wi 16
4487    entry $w.f3.e1 -textvar v(gvcmd)        -wi 40
4488    button $w.f3.b1 -text Preview -command [list DoPrint preview $canvas $h] \
4489	    -wi 8
4490    pack $w.f3.l1 $w.f3.e1 $w.f3.b1 -side left
4491    bind $w.f3.e1 <Key-Return> [list DoPrint preview $canvas $h]
4492
4493    frame $w.f4
4494    label $w.f4.l1 -text "Save to ps-file:" -wi 16
4495    entry $w.f4.e1 -textvar v(psfilet)       -wi 40
4496    button $w.f4.b1 -text Save -command [list DoPrint save $canvas $h] -wi 8
4497    pack $w.f4.l1 $w.f4.e1 $w.f4.b1 -side left
4498    bind $w.f4.e1 <Key-Return> [list DoPrint save $canvas $h]
4499
4500    frame $w.f
4501    label $w.f.lab -text "" -width 1 -relief sunken -bd 1 -anchor w
4502    pack $w.f.lab -side left -expand yes -fill x
4503    button $w.f.exitB -text Close -command [list destroy $w]
4504    pack $w.f.exitB -side left
4505    pack $w.f1 $w.f2 $w.f3 $w.f4 $w.f -side top -fill x
4506}
4507
4508proc DoPrint {type c canvh} {
4509    global v
4510
4511    set n 0
4512    set pageno 0
4513    set x 0
4514    if {$c == ".sect.c"} {
4515	set w 1000
4516    } else {
4517	set w $v(width)
4518    }
4519    set title [InfoStr path]
4520    set time [clock format [clock seconds] -format "%a %b %d %T"]
4521    set width 1020
4522    set skip  1000
4523
4524    if {$canvh == -1} {
4525	set canvh $v(toth)
4526    }
4527
4528    $c delete ch1 ch2 sm
4529    $c itemconf relmarkux -stipple ""
4530
4531    while {$w > 0} {
4532	incr pageno
4533	if {$pageno >= $v(firstpage)} {
4534	    if {$pageno > $v(lastpage)} break
4535	    $c create text [expr $x + 10] -10 -text "$title   Page: $pageno of $v(lastpage)   Printed: $time" -anchor w -tags decor
4536	    if {$c != ".sect.c"} {
4537		$c create line $x 0 $x $canvh -tags decor
4538		if {$w < $width} {
4539		    set ww [expr $x + $w]
4540		} else {
4541		    set ww [expr $x + $width]
4542		}
4543		$c create line $ww 0 $ww $canvh -tags decor
4544		snack::frequencyAxis $c $x [expr $v(waveh)-1] $v(yaxisw) \
4545			$v(spegh)\
4546			-topfrequency $v(topfr) -tags decor -fill $v(fg)
4547	    }
4548	    $c postscript -file _xspr$n.ps -colormode mono -rotate true -x $x -y -20 -width $width -height [expr $canvh + 20] -pagewidth 26c
4549
4550            switch $type {
4551		print {
4552		    regsub {\$FILE} $v(printcmd) _xspr$n.ps cmd
4553		}
4554		preview {
4555		    regsub {\$FILE} $v(gvcmd) _xspr$n.ps cmd
4556		}
4557		save {
4558		    regsub {\$FILE} $v(psfilecmd) _xspr$n.ps cmd
4559		    regsub {\$N} $v(psfilet) $n v(psfile)
4560		}
4561	    }
4562	    eval exec $cmd
4563	    file delete _xspr$n.ps
4564	    incr n
4565	    $c delete decor
4566	}
4567	incr x $skip
4568	incr w -$skip
4569    }
4570    if {$n == 1} {
4571	SetMsg "Printed 1 page"
4572    } else {
4573	SetMsg "Printed $n pages"
4574    }
4575    DrawCrossHairs
4576    $c itemconf relmarkux -stipple gray50
4577}
4578
4579menu .popmenu -tearoff false
4580proc PopUpMenu {X Y x y} {
4581    global v
4582
4583    .popmenu delete 0 end
4584
4585    if {$y < [expr $v(waveh) + $v(spegh) + $v(timeh)]} {
4586	.popmenu add command -label "Play Range" -command [list PlayMark $x]
4587    } else {
4588	.popmenu add command -label "Play Label" -command [list PlayLabel $x $y]
4589	.popmenu add command -label "Mark Label" -command [list MarkLabel $x $y]
4590    }
4591    .popmenu add command -label "Save Range" -command SaveMark
4592    .popmenu add command -label "Mark Start" -command "PutMarker m1 $x 0 1;SendPutMarker m1 $x"
4593    .popmenu add command -label "Mark End" -command "PutMarker m2 $x 0 1;SendPutMarker m2 $x"
4594    .popmenu add command -label "Zoom" -command OpenZoomWindow
4595    if {$y > [expr $v(waveh) + $v(spegh) + $v(timeh)]} {
4596	.popmenu add command -label "Insert Label" -command [list InsertLabel $x $y]
4597	.popmenu add command -label "Delete Label" -command [list DeleteLabel $x $y]
4598	.popmenu add command -label "Align Label" -command [list AlignLabel $x $y]
4599	.popmenu add command -label "Get Right Label" -command [list GetRightLabel $x $y]
4600    }
4601    catch {tk_popup .popmenu $X $Y 0}
4602}
4603
4604proc SaveSettings {} {
4605    global v f s
4606
4607    if [catch {open [file join ~ .xsrc] w} out] {
4608	SetMsg $out
4609    } else {
4610	puts $out "set v(s_version) $v(p_version)"
4611	puts $out "set v(waveh) $v(waveh)"
4612	puts $out "set v(spegh) $v(spegh)"
4613#	puts $out "set v(scrw) $v(scrw)"
4614	puts $out "set v(pps) $v(pps)"
4615	puts $out "set v(fftlen) $v(fftlen)"
4616	puts $out "set v(winlen) $v(winlen)"
4617	puts $out "set v(anabw) $v(anabw)"
4618	puts $out "set v(preemph) $v(preemph)"
4619	puts $out "set v(ipa) $v(ipa)"
4620	puts $out "set v(autoload) $v(autoload)"
4621	puts $out "set v(ch) $v(ch)"
4622	puts $out "set v(slink) $v(slink)"
4623	puts $out "set v(mlink) $v(mlink)"
4624	puts $out "set v(printcmd) \{$v(printcmd)\}"
4625	puts $out "set v(gvcmd) \{$v(gvcmd)\}"
4626	puts $out "set v(pluginfiles) {$v(pluginfiles)}"
4627#	puts $out "set v(browser) \{$v(browser)\}"
4628	puts $out "set v(rate) $v(rate)"
4629	puts $out "set v(sfmt) $v(sfmt)"
4630	puts $out "set v(chan) $v(chan)"
4631#	puts $out "set v(offset) $v(offset)"
4632#	puts $out "set v(zerolabs) $v(zerolabs)"
4633	puts $out "set v(ipafmt) $v(ipafmt)"
4634	puts $out "set v(labalign) $v(labalign)"
4635	puts $out "set v(fg) $v(fg)"
4636	puts $out "set v(bg) $v(bg)"
4637	puts $out "set v(fillmark) $v(fillmark)"
4638	puts $out "set v(font) \{$v(font)\}"
4639	puts $out "set v(sfont) \{$v(sfont)\}"
4640	puts $out "set v(gridfspacing) $v(gridfspacing)"
4641	puts $out "set v(gridtspacing) $v(gridtspacing)"
4642	puts $out "set v(gridcolor) $v(gridcolor)"
4643	puts $out "set v(remote) \{$v(remote)\}"
4644	puts $out "set v(ashost) \{$v(ashost)\}"
4645	puts $out "set v(asport) \{$v(asport)\}"
4646	puts $out "set v(recording) \{$v(recording)\}"
4647	puts $out "set v(cmap) \{$v(cmap)\}"
4648	puts $out "set v(showspeg) \{$v(showspeg)\}"
4649	puts $out "set v(linkfile) \{$v(linkfile)\}"
4650
4651	puts $out "set f(skip)  $f(skip)"
4652	puts $out "set f(ipath) $f(ipath)"
4653	puts $out "set f(ihttp) $f(ihttp)"
4654
4655	puts $out "set s(fftlen)  $s(fftlen)"
4656	puts $out "set s(anabw)   $s(anabw)"
4657	puts $out "set s(wintype) $s(wintype)"
4658	puts $out "set s(ref)     $s(ref)"
4659	puts $out "set s(range)   $s(range)"
4660	puts $out "set s(atype)   $s(atype)"
4661	puts $out "set s(lpcorder) $s(lpcorder)"
4662
4663      if {[info exists snack::snackogg]} {
4664	puts $out "set ogg(nombr) $::ogg(nombr)"
4665	puts $out "set ogg(maxbr) $::ogg(maxbr)"
4666	puts $out "set ogg(minbr) $::ogg(minbr)"
4667	puts $out "set ogg(com)   $::ogg(com)"
4668	puts $out "set ogg(query) $::ogg(query)"
4669      }
4670
4671	close $out
4672    }
4673}
4674
4675proc SetCursor {flag} {
4676    foreach widget [winfo children .] {
4677	$widget config -cursor $flag
4678    }
4679    update idletasks
4680}
4681
4682# Put custom procedures between the lines below
4683# Custom procs start here
4684# Custom procs end here
4685
4686foreach plug [split $v(pluginfiles)] {
4687    source $plug
4688}
4689
4690DrawCrossHairs
4691ToggleRecording
4692Link2File
4693
4694if {$tcl_platform(platform) == "windows"} {
4695    update idletasks
4696    Redraw
4697}
4698
4699proc GetStdin {} {
4700    global v pipevar
4701
4702    append pipevar [read -nonewline stdin]
4703    if [eof stdin] {
4704	fileevent stdin readable ""
4705	if {$pipevar != ""} {
4706	    snd data $pipevar
4707	    set v(rate) [snd cget -rate]
4708	    set v(sfmt) [snd cget -encoding]
4709	    set v(chan) [snd cget -channels]
4710	    wm geometry . {}
4711	    Redraw
4712	    event generate .cf.fc.c <Configure>
4713	    MarkAll
4714	    PlayAll
4715	}
4716    }
4717}
4718
4719if [info exists demoFlag] {
4720    OpenFiles [file join [pwd] ex2.wav]
4721    OpenFiles [file join [pwd] ex2.phn]
4722    return
4723}
4724if {$argv == "-"} {
4725    fconfigure stdin -translation binary -blocking 0
4726    if {$tcl_version > 8.0} {
4727	fconfigure stdin -encoding binary
4728    }
4729    fileevent stdin readable GetStdin
4730} elseif [llength $argv] {
4731    if {[llength $argv] > 1} { set v(autoload) 0 }
4732    foreach file $argv {
4733	OpenFiles $file
4734    }
4735} else {
4736    if [string compare macintosh $::tcl_platform(platform)] {
4737	GetOpenFileName
4738    }
4739}
4740