3 # Copyright (C) 2000-2004 Jonas Beskow and Kare Sjolander
5 # This file is part of the WaveSurfer package.
6 # The latest version can be found at http://www.speech.kth.se/wavesurfer/
8 # -----------------------------------------------------------------------------
10 wsurf::RegisterPlugin transcription \
11 -description "This plug-in is used to create transcription panes. Use the\
12 properties-dialog to specify which transcription file that should be\
13 displayed in a pane. It is usually practical to create a special\
14 configuration for a certain combination of sound and transcription\
15 files, specifying file properties such as filename extension, format,\
16 file path, and encoding. There are\
17 many options to control appearance and\
18 editing functionality. Depending on the transcription file format\
19 additional options might be available. There is a special pop-up menu\
20 with functions to edit, play, convert and search labels. Unicode\
21 characters are supported if using the source version of WaveSurfer,\
22 in order to keep the binary versions small. The transcription plug-in is\
23 used in combination with format handler plug-ins which handle\
24 the conversion between file formats and the internal format\
25 used by the transcription plug-in." \
26 -url "http://www.speech.kth.se/wavesurfer/" \
27 -addmenuentriesproc trans::addMenuEntries \
28 -widgetcreatedproc trans::widgetCreated \
29 -widgetdeletedproc trans::widgetDeleted \
30 -panecreatedproc trans::paneCreated \
31 -panedeletedproc trans::paneDeleted \
32 -redrawproc trans::redraw \
33 -getboundsproc trans::getBounds \
34 -cursormovedproc trans::cursorMoved \
35 -printproc trans::print \
36 -propertiespageproc trans::propertyPane \
37 -applypropertiesproc trans::applyProperties \
38 -getconfigurationproc trans::getConfiguration \
39 -openfileproc trans::openFile \
40 -savefileproc trans::saveFile \
41 -needsaveproc trans::needSave \
43 -copyproc trans::copy \
44 -pasteproc trans::paste \
45 -stateproc trans::state \
46 -playproc trans::play \
47 -stopproc trans::stop \
48 -registercallbackproc trans::regCallback \
49 -soundchangedproc trans::soundChanged
51 # -----------------------------------------------------------------------------
53 namespace eval trans {
59 # -----------------------------------------------------------------------------
61 proc trans::addMenuEntries {w pane m hook x y} {
62 if {[string match query $hook]} {
63 upvar [namespace current]::${pane}::var v
64 if {[info exists v(drawTranscription)]} {
65 if {$v(drawTranscription)} {
71 if {[string match main $hook]} {
72 upvar [namespace current]::${pane}::var v
73 if {[info exists v(drawTranscription)]} {
74 if {$v(drawTranscription)} {
76 for {set j 0} {$j < $v(menuNcols)} {incr j } {
77 for {set i 0} {$i < $v(menuNrows)} {incr i } {
78 if {$i==0} {set cb 1} else {set cb 0}
79 $m add command -label [subst $v($i$j)] -columnbreak $cb \
80 -command [namespace code [list InsertLabel $w $pane $x $y \
86 $m add command -label "Onsets Detection ..." \
87 -command [namespace code [list getComputeAubioOnset $w $pane]]
88 $m add command -label "Play Label" -columnbreak 1 \
89 -command [namespace code [list PlayLabel $w $pane $x $y]]
90 $m add command -label "Insert Label" \
91 -command [namespace code [list InsertLabel $w $pane $x $y]]
92 $m add command -label "Select Label" \
93 -command [namespace code [list SelectLabel $w $pane $x $y]]
94 $m add command -label "Align Label" \
95 -command [namespace code [list AlignLabel $w $pane $x $y]]
96 $m add command -label "Browse..." \
97 -command [namespace code [list browse $w $pane]]
98 $m add command -label "Delete Label" \
99 -command [namespace code [list DeleteLabel $w $pane $x $y]]
101 $m add command -label "Convert..." \
102 -command [namespace code [list convert $w $pane]]
103 $m add command -label "Load Transcription..." \
104 -command [namespace code [list getOpenTranscriptionFile $w $pane]]
105 $m add command -label "Load Text Labels..." \
106 -command [namespace code [list getOpenTextLabelFile $w $pane]]
107 $m add command -label "Save Transcriptions" \
108 -command [namespace code [list saveTranscriptionFiles $w $pane]]
109 $m add command -label "Save Transcription As..." \
110 -command [namespace code [list getSaveTranscriptionFile $w $pane]]
111 $m add command -label "Split Sound on Labels" \
112 -command [namespace code [list SplitSoundFile $w $pane]]
118 if {[string match create $hook]} {
119 $m.$hook add command -label "AubioTranscription" \
120 -command [namespace code [list createTranscription $w $pane]]
121 } elseif {[string length $hook] == 0} {
122 upvar [namespace current]::${pane}::var v
123 if {[info exists v(drawTranscription)]} {
124 if {$v(drawTranscription)} {
130 proc trans::widgetCreated {w} {
132 set Info($w,active) ""
135 proc trans::widgetDeleted {w} {
137 foreach key [array names Info $w*] {unset Info($key)}
140 proc trans::paneCreated {w pane} {
141 namespace eval [namespace current]::${pane} {
144 upvar [namespace current]::${pane}::var v
145 set v(drawTranscription) 0
147 # foreach otherpane [$w _getPanes] {
148 # upvar wsurf::trans::${otherpane}::var ov
149 # if {[info exists ov(extBounds)] && $ov(extBounds)} {
156 proc trans::paneDeleted {w pane} {
157 upvar [namespace current]::${pane}::var v
159 foreach otherpane [$w _getPanes] {
160 if {$pane == $otherpane} continue
161 upvar wsurf::analysis::${otherpane}::var ov
162 upvar wsurf::dataplot::${otherpane}::var dv
163 if {$ov(drawWaveform) || $ov(drawSpectrogram) || $dv(drawDataPlot)} {
164 set othercanvas [$otherpane canvas]
165 if {[winfo exists $othercanvas]} {
166 $othercanvas delete tran$pane
170 namespace delete [namespace current]::${pane}
173 proc trans::createTranscription {w pane} {
174 set pane [$w addPane -before $pane -height 20 -closeenough 3 \
175 -minheight 20 -maxheight 20]
176 addTranscription $w $pane
179 ### Add-ons from Paul Brossier <piem@altern.org>
182 proc trans::getComputeAubioOnset {w pane} {
183 set execFileName aubioonset
184 #exec which $execFileName > /dev/null || echo "$execFileName not found in the path"
185 # save selection to a file
186 # (from wavesurfer.tcl : SaveSelection)
187 set w [::wsurf::GetCurrent]
191 set pane [lindex [$w _getPanes] 0]
193 set length [$pane cget -maxtime]
195 set length [[$w cget -sound] length -unit seconds]
197 $w configure -selection [list 0.0 $length]
200 foreach {left right} [$w cget -selection] break
201 if {$left == $right} return
202 set s [$w cget -sound]
203 set start [expr {int($left*[$s cget -rate])}]
204 set end [expr {int($right*[$s cget -rate])}]
205 set path [file dirname [$w getInfo fileName]]
207 set tmpdir $::wsurf::Info(Prefs,tmpDir)
208 set fileName "$tmpdir/wavesurfer-tmp-aubio.snd"
209 set fileNameTxt "$tmpdir/wavesurfer-tmp-aubio.txt"
210 set aubioThreshold 0.2
211 #[snack::getSaveFile -initialdir $path \
212 #-format $::surf(fileFormat)]
213 #if {$fileName == ""} return
214 $s write $fileName -start $start -end $end -progress progressCallback
216 # system command : compute onsets
217 exec aubioonset -i $fileName -t $aubioThreshold > $fileNameTxt 2> /dev/null
218 # some ed hacks to put the .txt in .lab format
219 # copy the times 3 times: 0.0000 0.0000 0.0000
220 exec echo -e "e $fileNameTxt\\n,s/\\(.*\\)/\\\\1 \\\\1 \\\\1/\\nwq" | ed 2> /dev/null
222 # open the file as a labelfile
223 openTranscriptionFile $w $pane $fileNameTxt labelfile
225 exec rm -f $fileName $fileNameTxt
229 proc trans::getOpenTranscriptionFile {w pane} {
231 upvar [namespace current]::${pane}::var v
234 if {[string match no [tk_messageBox -message "You have unsaved changes.\nDo you really want to continue?" -type yesno -icon question]]} {
238 set file [file tail $v(fileName)]
239 if {$Info(path) != ""} {
242 if {$v(labdir) == ""} {
243 set path [file dirname $v(fileName)]
245 set path [file normalize [file dirname $v(fileName)]]
246 set pathlist [file split $path]
247 set path [eval file join [lreplace $pathlist end end $v(labdir)]]
250 set fileName [tk_getOpenFile -title "Load Transcription" -initialfile $file \
251 -initialdir $path -defaultextension $v(labext)]
252 if {$fileName == ""} return
254 if {[string compare $path [file dirname $fileName]] != 0} {
255 set Info(path) [file dirname $fileName]
258 openTranscriptionFile $w $pane $fileName labelfile
262 proc trans::getOpenTextLabelFile {w pane} {
264 upvar [namespace current]::${pane}::var v
267 if {[string match no [tk_messageBox -message "You have unsaved changes.\nDo you really want to continue?" -type yesno -icon question]]} {
271 set file [file tail $v(fileName)]
272 if {$Info(path) != ""} {
275 if {$v(labdir) == ""} {
276 set path [file dirname $v(fileName)]
278 set path [file normalize [file dirname $v(fileName)]]
279 set pathlist [file split $path]
280 set path [eval file join [lreplace $pathlist end end $v(labdir)]]
283 set fileName [tk_getOpenFile -title "Load Text Labels" -initialfile $file \
284 -initialdir $path -defaultextension $v(labext)]
285 if {$fileName == ""} return
287 if {[string compare $path [file dirname $fileName]] != 0} {
288 set Info(path) [file dirname $fileName]
291 set f [open $fileName]
292 fconfigure $f -encoding utf-8
293 set labels [split [read -nonewline $f]]
297 set start [expr 0.5 * [$pane cget -maxtime]]
298 set delta [expr 0.5 * [$pane cget -maxtime] / [llength $labels]]
301 foreach label $labels {
302 set v(t1,$i,end) [expr {$start + $i * $delta}]
303 set v(t1,$i,label) $label
308 set v(t1,end) [$pane cget -maxtime]
312 set v(headerFmt) WaveSurfer
317 proc trans::saveTranscriptionFiles {w pane} {
318 foreach pane [$w _getPanes] {
319 upvar [namespace current]::${pane}::var v
320 if {$v(drawTranscription) && $v(changed)} {
321 saveTranscriptionFile $w $pane
326 proc trans::getSaveTranscriptionFile {w pane} {
327 upvar [namespace current]::${pane}::var v
329 set file [file tail $v(fileName)]
330 if {$v(labdir) == ""} {
331 set path [file dirname $v(fileName)]
333 set path [file normalize [file dirname $v(fileName)]]
334 set pathlist [file split $path]
335 set path [eval file join [lreplace $pathlist end end $v(labdir)]]
338 set fileName [tk_getSaveFile -title "Save Transcription" -initialfile $file \
339 -initialdir $path -defaultextension $v(labext)]
340 if {$fileName == ""} return
342 set v(fileName) $fileName
343 set v(labext) [file extension $fileName]
345 saveTranscriptionFile $w $pane
348 proc trans::addTranscription {w pane args} {
350 upvar [namespace current]::${pane}::var v
355 -boundarycolor black \
356 -backgroundcolor white \
362 -adjustleftevent Control-l \
363 -adjustrightevent Control-r \
364 -playlabelevent Control-space \
365 -labelmenu {2 7 lab1 lab2 lab3 lab4 lab5 lab6 lab7 lab8} \
368 -quickentertolerance 20 \
369 -extendboundaries 0 \
373 if {[string match macintosh $::tcl_platform(platform)]} {
374 set a(-labelmenuevent) Shift-ButtonPress-1
376 set a(-labelmenuevent) Shift-ButtonPress-3
378 if {[string match Darwin $::tcl_platform(os)]} {
379 set a(-labelmenuevent) Shift-ButtonPress-1
380 set a(-labelmenu) {1 6 lab1 lab2 lab3 lab4 lab5 lab6}
382 if {[string match unix $::tcl_platform(platform)] } {
383 set a(-font) {Courier 10}
387 set v(alignment) $a(-alignment)
388 set v(labColor) $a(-labelcolor)
389 set v(bdColor) $a(-boundarycolor)
390 set v(bgColor) $a(-backgroundcolor)
391 set v(labext) .[string trim $a(-extension) .]
392 set v(font) $a(-font)
393 set v(format) $a(-format)
394 set v(labdir) $a(-labeldirectory)
395 set v(encoding) $a(-fileencoding)
396 set v(menuNcols) [lindex $a(-labelmenu) 0]
397 set v(menuNrows) [lindex $a(-labelmenu) 1]
398 set v(labelMenuEvent) $a(-labelmenuevent)
399 set v(adjustLeftEvent) $a(-adjustleftevent)
400 set v(adjustRightEvent) $a(-adjustrightevent)
401 set v(playLabelEvent) $a(-playlabelevent)
402 set v(locked) $a(-locked)
403 set v(quickenter) $a(-quickenter)
404 set v(quicktol) $a(-quickentertolerance)
405 set v(extBounds) $a(-extendboundaries)
406 set v(linkBounds) $a(-linkboundaries)
407 set v(highlight) $a(-playhighlight)
416 set v(drawTranscription) 1
417 set v(headerFmt) WaveSurfer
423 event add <<LabelMenuEvent>> <$v(labelMenuEvent)>
424 event add <<AdjustLeftEvent>> <$v(adjustLeftEvent)>
425 event add <<AdjustRightEvent>> <$v(adjustRightEvent)>
426 event add <<PlayLabelEvent>> <$v(playLabelEvent)>
428 for {set i 0} {$i < $v(menuNrows)} {incr i } {
429 for {set j 0} {$j < $v(menuNcols)} {incr j } {
430 set v($i$j) [lindex $a(-labelmenu) \
431 [expr {2 + $v(menuNcols) * $i + $j}]]
437 foreach tag {text bg bound} {
438 util::canvasbind $c $tag <<LabelMenuEvent>> \
439 [namespace code [list labelsMenu $w $pane %X %Y %x %y]]
442 util::canvasbind $c bound <B1-Motion> \
443 [namespace code [list MoveBoundary $w $pane %x]]
444 util::canvasbind $c bound <ButtonPress-1> ""
446 bind $c <ButtonPress-2> \
447 [namespace code [list handleEvents PlayLabel %x %y]]
449 $c bind bound <Enter> [list $c configure \
450 -cursor sb_h_double_arrow]
451 $c bind bound <Leave> [list $c configure -cursor {}]
452 $c bind text <Enter> [list $c configure -cursor xterm]
453 $c bind text <Leave> [list $c configure -cursor {}]
455 util::canvasbind $c text <B1-Motion> [namespace code \
456 [list textB1Move $w $pane %W %x %y]]
457 util::canvasbind $c text <ButtonRelease-1> ""
458 util::canvasbind $c text <ButtonPress-1> [namespace code \
459 [list textClick $w $pane %W %x %y]]
461 util::canvasbind $c bg <ButtonPress-1> [namespace code \
462 [list boxClick $w $pane %W %x %y]]
463 bind $c <Any-Key> [namespace code [list handleAnyKey $w $pane %W %x %y %A]]
464 bind $c <BackSpace> [namespace code [list handleBackspace $w $pane %W]]
466 %W insert current insert ""
470 bind $c <Enter> [namespace code [list handleEnterLeave $w $pane 1]]
471 bind $c <Leave> [namespace code [list handleEnterLeave $w $pane 0]]
473 bind [winfo toplevel $c] <<AdjustRightEvent>> \
474 [namespace code [list handleEvents AdjustLabel %x %y right]]
475 bind [winfo toplevel $c] <<AdjustLeftEvent>> \
476 [namespace code [list handleEvents AdjustLabel %x %y left]]
478 util::canvasbind $c text <<AdjustRightEvent>> ""
479 util::canvasbind $c text <<AdjustLeftEvent>> ""
481 bind $c <<PlayLabelEvent>> \
482 [namespace code [list handleEvents PlayLabel %x %y]]
483 bind [winfo toplevel $c] <<PlayLabelEvent>> \
484 [namespace code [list handleEvents PlayLabel %x %y]]
486 bind $c <<Delete>> "[namespace code [list handleDelete $w $pane %W]];break"
487 bind $c <space> "[namespace code [list handleSpace $w $pane %W]];break"
488 bind $c <Shift-Control-space> "[namespace code [list FindNextLabel $w $pane]];break"
489 $c bind text <Key-Right> [namespace code [list handleKeyRight $w $pane %W]]
490 $c bind text <Key-Left> [namespace code [list handleKeyLeft $w $pane %W]]
492 if {[$w getInfo fileName] != ""} {
493 openTranscriptionFile $w $pane [$w getInfo fileName] soundfile
497 if {$::tcl_version > 8.2} {
499 $c configure -state disabled
501 $c configure -state normal
504 # If the label file is longer than any current displayed pane, update them all
505 if {[info exists v(t1,end)]} {
506 if {$v(t1,end) > [$pane cget -maxtime]} {
512 proc trans::handleEvents {proc args} {
513 if {![info exists ::trpane]} {
516 if {[namespace which -variable \
517 [namespace current]::${::trpane}::var] == ""} return
518 upvar [namespace current]::${::trpane}::var v
520 if {[info exists v(cursorInPane)]} {
521 if {$v(cursorInPane)} {
522 eval $proc $::trw $::trpane $args
527 proc trans::handleEnterLeave {w pane arg} {
528 upvar [namespace current]::${pane}::var v
530 set v(cursorInPane) $arg
533 proc trans::activateInput {w pane state} {
535 upvar [namespace current]::${pane}::var v
537 if {[info exists Info($w,active)]} {
539 set Info($w,active) $pane
540 [$pane yaxis] configure -relief solid
541 [$pane canvas] configure -relief solid
543 drawExtendedBoundaries $w $pane
546 foreach p [$w _getPanes] {
547 if {$state == 0 || [string compare $p $pane]} {
548 if {[info exists v(drawTranscription)]} {
549 if {$v(drawTranscription)} {
550 [$p yaxis] configure -relief flat
551 [$p canvas] configure -relief flat
559 proc trans::state {w state} {
562 if {[info exists Info($w,active)]} {
563 if {$Info($w,active) != ""} {
564 activateInput $w $Info($w,active) $state
565 set c [$Info($w,active) canvas]
567 boxClick $w $Info($w,active) $c 0 0
573 proc trans::labelsMenu {w pane X Y x y} {
574 upvar [namespace current]::${pane}::var v
576 if {[winfo exists $m]} {destroy $m}
578 $m add command -label "Play Label" \
579 -command [namespace code [list PlayLabel $w $pane $x $y]]
580 $m add command -label "Insert Label" \
581 -command [namespace code [list InsertLabel $w $pane $x $y]]
582 $m add command -label "Select Label" \
583 -command [namespace code [list SelectLabel $w $pane $x $y]]
584 $m add command -label "Align Label" \
585 -command [namespace code [list AlignLabel $w $pane $x $y]]
586 $m add command -label "Browse..." \
587 -command [namespace code [list browse $w $pane]]
588 $m add command -label "Convert..." \
589 -command [namespace code [list convert $w $pane]]
591 $m add command -label "Delete Label" \
592 -command [namespace code [list DeleteLabel $w $pane $x $y]]
594 for {set j 0} {$j < $v(menuNcols)} {incr j } {
595 for {set i 0} {$i < $v(menuNrows)} {incr i } {
596 if {$i==0} {set cb 1} else {set cb 0}
597 $m add command -label [subst $v($i$j)] -columnbreak $cb \
598 -command [namespace code [list InsertLabel $w $pane $x $y \
604 if {[string match macintosh $::tcl_platform(platform)]} {
605 tk_popup $w.popup $X $Y 0
607 tk_popup $w.popup $X $Y
611 proc trans::textClick {w pane W x y} {
612 upvar [namespace current]::${pane}::var v
618 $W icursor current @[$W canvasx $x],[$W canvasy $y]
620 $W select from current @[$W canvasx $x],[$W canvasy $y]
621 set tagno [lindex [$c gettags current] 0]
622 activateInput $w $pane 1
624 set i [lsearch -exact $v(map) $tagno]
626 set start [GetStartByIndex $w $pane $i]
627 set end $v(t1,$tagno,end)
628 set len [expr $end - $start]
630 "$v(t1,$tagno,label) ($tagno) start: $start end: $end length: $len"
633 proc trans::textB1Move {w pane W x y} {
634 # clear widget selection before selecting any text
635 foreach {start end} [$w cget -selection] break
636 $w configure -selection [list $start $start]
638 $W select to current @[$W canvasx $x],[$W canvasy $y]
641 proc trans::boxClick {w pane W x y} {
642 upvar [namespace current]::${pane}::var v
648 set cx [$c canvasx $x]
649 set t [$pane getTime $cx]
650 $w configure -selection [list $t $t]
651 activateInput $w $pane 1
655 proc trans::handleAnyKey {w pane W x y A} {
656 upvar [namespace current]::${pane}::var v
657 if {[string length $A] == 0} return
658 if {[string is print $A] == 0} return
660 if {[$W focus] != $v(hidden)} {
662 catch {$W dchars $tag sel.first sel.last}
663 $W insert $tag insert $A
664 SetLabelText $w $pane [lindex [$c gettags $tag] 0] \
665 [$c itemcget $tag -text]
667 if {$v(quickenter) == 0} return
668 set dx [expr {abs($v(lastPos) - $x)}]
669 if {$v(quicktol) > $dx && $v(clicked) == 0} {
670 set tagno $v(lastTag)
671 append v(t1,$tagno,label) $A
672 $c itemconf lab$v(lastTag) -text $v(t1,$tagno,label)
674 set v(lastTag) [InsertLabel $w $pane $x $y $A]
675 if {$v(lastTag) == ""} return
683 proc trans::handleDelete {w pane W} {
685 if {[$W focus] != {}} {
687 if {![catch {$W dchars $tag sel.first sel.last}]} {
690 $W dchars $tag insert
691 SetLabelText $w $pane [lindex [$c gettags $tag] 0] \
692 [$c itemcget $tag -text]
697 proc trans::handleBackspace {w pane W} {
699 if {[$W focus] != {}} {
701 if {![catch {$W dchars $tag sel.first sel.last}]} {
704 set ind [expr {[$W index $tag insert]-1}]
707 $W dchars $tag insert
708 SetLabelText $w $pane [lindex [$c gettags $tag] 0] \
709 [$c itemcget $tag -text]
715 proc trans::handleSpace {w pane W} {
717 if {[$W focus] != {}} {
719 $W insert [$W focus] insert _
720 SetLabelText $w $pane [lindex [$c gettags [$W focus]] 0] \
721 [$c itemcget [$W focus] -text]
725 proc trans::handleKeyRight {w pane W} {
726 upvar [namespace current]::${pane}::var v
728 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
729 if {[$W focus] != {}} {
731 set __index [$W index [$W focus] insert]
732 $W icursor [$W focus] [expr {$__index + 1}]
733 if {$__index == [$W index [$W focus] insert]} {
734 set ti [lindex [$c gettags [$W focus]] 0]
735 set i [lsearch -exact $v(map) $ti]
736 set __focus [lindex $v(map) [expr {$i+1}]]
738 $W icursor lab$__focus 0
739 while {$width * [lindex [$c xview] 1]-10 < \
740 [lindex [$W coords [$W focus]] 0] && [lindex [$c xview] 1] < 1} {
741 $w xscroll scroll 1 unit
747 proc trans::handleKeyLeft {w pane W} {
748 upvar [namespace current]::${pane}::var v
750 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
751 if {[$W focus] != {}} {
753 set __index [$W index [$W focus] insert]
754 $W icursor [$W focus] [expr {[$W index [$W focus] insert] - 1}]
755 if {$__index == [$W index [$W focus] insert]} {
756 set ti [lindex [$c gettags [$W focus]] 0]
757 set i [lsearch -exact $v(map) $ti]
758 set __focus [lindex $v(map) [expr {$i-1}]]
760 $W icursor lab$__focus end
761 while {$width * [lindex [$c xview] 0] +10 > \
762 [lindex [$W coords [$W focus]] 0] && [lindex [$c xview] 0] > 0} {
763 $w xscroll scroll -1 unit
769 proc trans::openFile {w soundFileName} {
772 foreach pane [$w _getPanes] {
773 upvar [namespace current]::${pane}::var v
774 if {$v(drawTranscription)} {
775 openTranscriptionFile $w $pane [$w getInfo fileName] soundfile
781 proc trans::saveFile {w soundFileName} {
782 foreach pane [$w _getPanes] {
783 upvar [namespace current]::${pane}::var v
784 if {$v(drawTranscription) && $v(changed)} {
785 saveTranscriptionFile $w $pane
791 proc trans::openTranscriptionFile {w pane fn type} {
793 upvar [namespace current]::${pane}::var v
795 if {[info exists v(drawTranscription)]} {
796 if {$v(drawTranscription) == 0} return
799 if {[string match soundfile $type]} {
800 set path [file normalize [file dirname $fn]]
801 set pathlist [file split $path]
802 set rootname [file tail [file rootname $fn]]
803 set name $rootname.[string trim $v(labext) .]
805 # Try to locate the corresponding label file
807 if {$v(labdir) != ""} {
808 # Try the following directories in order
809 # 1. try to locate file in specified label file directory
810 # 2. try 'sound file path'/../'specified dir'
811 # 3. look in current directory
812 # 4. look in same directory as sound file
814 if {[file readable [file join $v(labdir) $name]]} {
815 set fileName [file join $v(labdir) $name]
816 } elseif {[file readable [eval file join [lreplace $pathlist end end $v(labdir)] $name]]} {
817 set fileName [eval file join [lreplace $pathlist end end $v(labdir)] $name]
820 if {$fileName == ""} {
821 if {[file readable $name]} {
823 } elseif {[file readable [file join $path $name]]} {
824 set fileName [file join $path $name]
833 # This filename should be correct, remember it
835 set v(fileName) $fileName
838 set v(labext) [file extension $fileName]
840 foreach {format loadProc saveProc} $Info(formats) {
841 if {[string compare $format $v(format)] == 0} {
842 set res [[namespace parent]::$loadProc $w $pane]
852 proc trans::saveTranscriptionFile {w pane} {
854 upvar [namespace current]::${pane}::var v
857 set strip_fn [file tail [file rootname $fn]]
858 if {$strip_fn == ""} {
859 set strip_fn [file tail [file rootname [$w getInfo fileName]]]
861 set path [file dirname $fn]
862 set v(fileName) [file join $path $strip_fn.[string trim $v(labext) .]]
864 catch {file copy $fn $fn~}
866 foreach {format loadProc saveProc} $Info(formats) {
867 if {[string compare $format $v(format)] == 0} {
868 set res [[namespace parent]::$saveProc $w $pane]
880 proc trans::needSave {w pane} {
881 upvar [namespace current]::${pane}::var v
883 if {[info exists v(drawTranscription)]} {
884 if {$v(drawTranscription)} {
893 proc trans::redraw {w pane} {
894 upvar [namespace current]::${pane}::var v
896 if {!$v(drawTranscription)} return
900 foreach otherpane [$w _getPanes] {
901 upvar wsurf::analysis::${otherpane}::var ov
902 upvar wsurf::dataplot::${otherpane}::var dv
903 if {$ov(drawWaveform) || $ov(drawSpectrogram) || $dv(drawDataPlot)} {
904 set othercanvas [$otherpane canvas]
905 $othercanvas delete tran$pane
908 _redraw $w $pane $c 0 0
909 # boxClick $w $pane $c 0 0
912 proc trans::_redraw {w pane c x y} {
913 upvar [namespace current]::${pane}::var v
915 set progressproc [$w cget -progressproc]
916 if {$progressproc != "" && $v(nLabels) > 0} {
917 # $progressproc "Creating labels" 0.0
919 set height [$pane cget -height]
920 set v(height) $height
921 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
922 set ascent [font metrics $v(font) -ascent]
923 set v(ascent) $ascent
924 $c configure -bg $v(bgColor)
926 [$pane yaxis] delete ext
928 set yw [winfo width $vc]
929 if {$::tcl_version > 8.2 && [string match disabled [$c cget -state]]} {
930 [$pane yaxis] create text [expr {$yw/2}] [expr {$height/2}] \
932 -font $v(font) -tags ext \
935 [$pane yaxis] create text [expr {$yw/2}] [expr {$height/2}] \
937 -font $v(font) -tags ext \
940 if {$v(nLabels) == 0} {
941 set slen [[$w cget -sound] length -unit seconds]
942 set endx [$pane getCanvasX $slen]
943 $c create rectangle [expr {$x+0}] $y \
944 [expr {$x+$endx}] [expr {$y+$height-4}] -outline "" \
945 -tags [list gEnd obj bg tran] -fill $v(bgColor)
946 set v(hidden) [$c create text [expr {$x-100}] [expr {$y+10}] \
947 -text "" -tags [list hidden tran]]
954 for {set i [expr $v(nLabels)-1]} {$i >= 0} {incr i -1} {
955 set ind [lindex $v(map) $i]
957 set start $v(t1,start)
959 set ind2 [lindex $v(map) [expr {$i - 1}]]
960 set start $v(t1,$ind2,end)
962 set end $v(t1,$ind,end)
963 set label $v(t1,$ind,label)
964 set lx [$pane getCanvasX $start]
965 set rx [$pane getCanvasX $end]
967 if {$lx >= 0 && $lx <= $width} {
968 #DrawLabel $w $pane $c $ind $i $x $y $lx $rx $label
969 set tx [ComputeTextPosition $w $pane $lx $rx]
970 $c create rectangle [expr {$x+$lx}] $y \
971 [expr {$x+$rx}] [expr {$y+$height-4}] -outline "" \
972 -tags [list g$ind obj bg tran] -fill $v(bgColor)
973 $c create text [expr {$x+$tx}] [expr {$y+$ascent}] -text $label\
974 -font $v(font) -anchor $v(alignment)\
975 -tags [list $ind obj text lab$ind tran] \
977 $c create line [expr {$x+$rx}] $y [expr {$x+$rx}] [expr {$y+$height}] \
978 -tags [list b$ind obj bound tran topmost] -fill $v(bdColor)
980 if {$progressproc != "" && $i % 100 == 99} {
981 # $progressproc "Creating labels" [expr double($v(nLabels)-$i)/$v(nLabels)]
984 set start $v(t1,start)
985 set sx [$pane getCanvasX $start]
986 $c create rectangle [expr {$x+0}] $y \
987 [expr {$x+$sx}] [expr {$y+$height-4}] -outline "" \
988 -tags [list gStart obj bg tran] -fill $v(bgColor)
989 $c create line [expr {$x+$sx}] $y [expr {$x+$sx}] [expr {$y+$height}] \
990 -tags [list bStart obj bound tran topmost] -fill $v(bdColor)
992 set slen [[$w cget -sound] length -unit seconds]
993 set endx [$pane getCanvasX $slen]
994 $c create rectangle [expr {$x+$rx}] $y \
995 [expr {$x+$endx}] [expr {$y+$height-4}] -outline "" \
996 -tags [list gEnd obj bg tran] -fill $v(bgColor)
997 set prev [lindex $v(map) end]
1000 set v(hidden) [$c create text [expr {$x-100}] [expr {$y+10}] \
1001 -text "" -tags [list hidden tran]]
1003 if {$v(extBounds)} {
1004 drawExtendedBoundaries $w $pane
1007 if {$progressproc != ""} {
1008 # $progressproc "Creating labels" 1.0
1014 proc trans::drawExtendedBoundaries {w pane} {
1015 upvar [namespace current]::${pane}::var v
1017 foreach otherpane [$w _getPanes] {
1018 upvar wsurf::analysis::${otherpane}::var ov
1019 upvar wsurf::dataplot::${otherpane}::var dv
1020 if {$ov(drawWaveform) || $ov(drawSpectrogram) || $dv(drawDataPlot)} {
1021 set othercanvas [$otherpane canvas]
1022 $othercanvas delete tran$pane
1026 set height [$pane cget -height]
1027 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
1029 if {$v(nLabels) > 0} {
1034 for {set i [expr $v(nLabels)-1]} {$i >= 0} {incr i -1} {
1035 set ind [lindex $v(map) $i]
1037 set start $v(t1,start)
1039 set ind2 [lindex $v(map) [expr {$i - 1}]]
1040 set start $v(t1,$ind2,end)
1042 set end $v(t1,$ind,end)
1043 set label $v(t1,$ind,label)
1044 set lx [$pane getCanvasX $start]
1045 set rx [$pane getCanvasX $end]
1047 if {$lx >= 0 && $lx <= $width} {
1048 foreach otherpane [$w _getPanes] {
1049 upvar wsurf::analysis::${otherpane}::var av
1050 upvar wsurf::dataplot::${otherpane}::var dv
1051 if {$av(drawWaveform) || $av(drawSpectrogram) || $dv(drawDataPlot)} {
1052 set othercanvas [$otherpane canvas]
1053 set height [$otherpane cget -height]
1054 $othercanvas create line $rx 0 $rx \
1055 $height -tags [list b$ind$pane obj bound tran$pane] \
1064 proc trans::DrawLabel {w pane c tagno i x y lx rx label} {
1065 upvar [namespace current]::${pane}::var v
1066 # set ascent [font metrics $v(font) -ascent]
1067 # set height [$pane cget -height]
1068 set ascent $v(ascent)
1069 set height $v(height)
1071 set tx [ComputeTextPosition $w $pane $lx $rx]
1072 $c create rectangle [expr {$x+$lx}] $y \
1073 [expr {$x+$rx}] [expr {$y+$height-4}] -outline "" \
1074 -tags [list g$tagno obj bg tran] -fill $v(bgColor)
1075 $c create text [expr {$x+$tx}] [expr {$y+$ascent}] -text $label\
1076 -font $v(font) -anchor $v(alignment)\
1077 -tags [list $tagno obj text lab$tagno tran] \
1079 $c create line [expr {$x+$rx}] $y [expr {$x+$rx}] [expr {$y+$height}] \
1080 -tags [list b$tagno obj bound tran topmost] -fill $v(bdColor)
1083 set prev [lindex $v(map) [expr {$i-1}]]
1084 $c lower g$tagno g$prev
1085 $c lower lab$tagno g$prev
1086 $c lower b$tagno g$prev
1088 $c lower g$tagno gStart
1089 $c lower lab$tagno gStart
1090 $c lower b$tagno gStart
1093 if {$v(extBounds)} {
1094 foreach otherpane [$w _getPanes] {
1095 upvar wsurf::analysis::${otherpane}::var av
1096 upvar wsurf::dataplot::${otherpane}::var dv
1097 if {$av(drawWaveform) || $av(drawSpectrogram) || $dv(drawDataPlot)} {
1098 set othercanvas [$otherpane canvas]
1099 set height [$otherpane cget -height]
1100 $othercanvas create line $rx 0 $rx \
1101 $height -tags [list b$tagno obj bound tran$pane] -fill $v(bdColor)
1107 proc trans::isLabel {tags} {
1108 expr [string compare [lindex $tags 2] bg] == 0 || \
1109 [string compare [lindex $tags 2] text] == 0
1112 proc trans::GetStartByIndex {w pane i} {
1113 upvar [namespace current]::${pane}::var v
1114 if {$i <= 0 || $i == "Start"} {
1117 set ind [lindex $v(map) [expr $i-1]]
1118 return $v(t1,$ind,end)
1122 proc trans::PlaceLabel {w pane tagno coords start end} {
1123 upvar [namespace current]::${pane}::var v
1124 set c [$pane canvas]
1125 if {$tagno != "Start"} {
1126 # Place background and boundary
1127 $c coords b$tagno $end [lindex $coords 1] $end [lindex $coords 3]
1128 $c coords g$tagno $start [lindex $coords 1] $end [expr [lindex $coords 3]-4]
1131 set tx [ComputeTextPosition $w $pane $start $end]
1132 $c coords lab$tagno $tx [lindex [$c coords lab$tagno] 1]
1134 $c coords b$tagno $end [lindex $coords 1] $end [lindex $coords 3]
1135 $c coords g$tagno 0 [lindex $coords 1] $end [expr [lindex $coords 3]-4]
1138 if {$v(extBounds)} {
1139 foreach otherpane [$w _getPanes] {
1140 upvar wsurf::analysis::${otherpane}::var av
1141 upvar wsurf::dataplot::${otherpane}::var dv
1142 if {$av(drawWaveform) || $av(drawSpectrogram) || $dv(drawDataPlot)} {
1143 set othercanvas [$otherpane canvas]
1144 set height [$otherpane cget -height]
1145 $othercanvas coords b$tagno$pane $end 0 $end $height
1151 proc trans::getBounds {w pane} {
1152 upvar [namespace current]::${pane}::var v
1154 if {$v(drawTranscription)} {
1155 list 0 0 $v(t1,end) 0
1161 proc trans::MoveBoundary {w pane x} {
1162 upvar [namespace current]::${pane}::var v
1164 set c [$pane canvas]
1165 set s [$w cget -sound]
1166 set coords [$c coords current]
1167 set xc [$c canvasx $x]
1168 if {$xc < 0} { set xc 0 }
1169 set tagno [string trim [lindex [$c gettags current] 0] b]
1170 set i [lsearch -exact $v(map) $tagno]
1172 # Logic which prevents a boundary to be moved past its neighbor
1173 set h [lindex $v(map) [expr {$i-1}]]
1174 set j [lindex $v(map) [expr {$i+1}]]
1176 set nx [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
1177 set pb [$c find withtag b$h]
1178 set nb [$c find withtag b$j]
1179 if {$pb != ""} { set px [lindex [$c coords $pb] 0]}
1180 if {$nb != ""} { set nx [lindex [$c coords $nb] 0]}
1181 if {$xc <= $px} { set xc [expr {$px + 1}] }
1182 if {$nx <= $xc} { set xc [expr {$nx - 1}] }
1184 set start [$pane getCanvasX [GetStartByIndex $w $pane $i]]
1188 set v(t1,start) [$pane getTime $xc]
1190 set this [lindex $v(map) $i]
1191 set oldTime $v(t1,$this,end)
1192 set v(t1,$this,end) [$pane getTime $xc]
1196 PlaceLabel $w $pane $tagno $coords $start $xc
1199 PlaceNextLabel $w $pane $i $xc
1201 if {$v(linkBounds)} {
1202 foreach otherpane [$w _getPanes] {
1203 upvar [namespace current]::${otherpane}::var ov
1204 if {$otherpane != $pane && $ov(drawTranscription) && \
1205 [info exists oldTime]} {
1206 foreach tag $ov(map) {
1207 if {$ov(t1,$tag,end) == $oldTime} {
1208 set ov(t1,$tag,end) [$pane getTime $xc]
1209 PlaceLabel $w $otherpane $tag $coords $start $xc
1217 if {$v(lastmoved) != $i} {
1219 if {$tagno == "Start"} {
1220 # wsurf::PrepareUndo "set [namespace current]::var(t1,start) \[list $v(t1,start)\]" ""
1222 # wsurf::PrepareUndo "set [namespace current]::var(t1,$tagno,end) \[list $v(t1,$tagno,end)\]" ""
1226 vtcanvas::motionEvent $pane $x 0
1229 proc trans::SetLabelText {w pane tagno label} {
1230 upvar [namespace current]::${pane}::var v
1232 $w messageProc [format "Transcription - %s" $label]
1233 set v(t1,$tagno,label) $label
1236 proc trans::InsertLabel {w pane x y {label ""}} {
1237 upvar [namespace current]::${pane}::var v
1239 set s [$w cget -sound]
1240 set c [$pane canvas]
1241 set cx [$c canvasx $x]
1242 set t [$pane getTime $cx]
1244 set tags [$c gettags [$c find closest [$c canvasx $x] [$c canvasy $y]]]
1245 if {[isLabel $tags]} {
1246 set tagno [string trim [lindex $tags 0] g]
1247 if {$tagno == "End"} {
1250 foreach ind $v(map) {
1251 if {$t < $v(t1,$ind,end)} break
1255 set i [lsearch -exact $v(map) $tagno]
1259 foreach ind $v(map) {
1260 if {$t < $v(t1,$ind,end)} break
1265 # Create label with a randomly chosen tag number
1266 set n [clock clicks]
1268 set v(t1,$n,label) $label
1269 set v(t1,$n,rest) ""
1270 set v(map) [linsert $v(map) $i $n]
1273 # Update start time if new label was inserted first
1276 set co [$c coords bStart]
1277 $c coords bStart 0 [lindex $co 1] 0 [lindex $co 3]
1278 set co [$c coords gStart]
1279 $c coords gStart 0 [lindex $co 1] 0 [lindex $co 3]
1282 set start [$pane getCanvasX [GetStartByIndex $w $pane $i]]
1285 # Draw inserted label
1286 DrawLabel $w $pane $c $n $i 0 0 $start $cx $label
1289 if {$i < 0} { incr i }
1290 PlaceNextLabel $w $pane $i $cx
1292 # Display cursor if label is empty
1294 focus [$pane canvas]
1295 [$pane canvas] focus lab$n
1296 [$pane canvas] icursor lab$n @[$c canvasx $x],[$c canvasy $y]
1303 proc trans::DeleteLabel {w pane x y} {
1304 upvar [namespace current]::${pane}::var v
1305 set c [$pane canvas]
1306 set tags [$c gettags [$c find closest [$c canvasx $x] [$c canvasy $y]]]
1308 if {[isLabel $tags] || [string compare [lindex $tags 2] bound] == 0} {
1309 set tagno [string trim [lindex $tags 0] gb]
1310 set i [lsearch -exact $v(map) $tagno]
1311 if {$i == -1} return
1313 # Delete everything related to this label
1314 unset v(t1,$tagno,label)
1315 unset v(t1,$tagno,end)
1316 unset v(t1,$tagno,rest)
1317 set v(map) [lreplace $v(map) $i $i]
1319 $c delete b$tagno lab$tagno g$tagno
1320 if {$v(extBounds)} {
1321 foreach otherpane [$w _getPanes] {
1322 upvar wsurf::analysis::${otherpane}::var av
1323 upvar wsurf::dataplot::${otherpane}::var dv
1324 if {$av(drawWaveform) || $av(drawSpectrogram) || $dv(drawDataPlot)} {
1325 set othercanvas [$otherpane canvas]
1326 $othercanvas delete b$tagno$pane
1331 # Place previous label box
1332 set prev [lindex $v(map) [expr {$i-1}]]
1334 set end [lindex [$c coords g$prev] 2]
1336 set end [$pane getCanvasX $v(t1,start)]
1339 set iprev [lsearch -exact $v(map) $prev]
1340 PlaceNextLabel $w $pane $iprev $end
1346 proc trans::AdjustLabel {w pane x y boundary} {
1347 upvar [namespace current]::${pane}::var v
1349 set c [$pane canvas]
1350 set xc [$c canvasx $x]
1351 set t [$pane getTime $xc]
1352 set tags [$c gettags [$c find closest $xc [$c canvasy $y]]]
1354 if {[isLabel $tags]} {
1355 set tagno [string trim [lindex $tags 0] g]
1356 set i [lsearch -exact $v(map) $tagno]
1359 foreach ind $v(map) {
1360 if {$t < $v(t1,$ind,end)} break
1363 set tagno [lsearch -exact $v(map) $i]
1366 if {$i == $v(nLabels)} return
1368 if {$tagno != "End" && [string match left $boundary]} {
1370 set tagno [lindex $v(map) $i]
1372 if {$tagno == "End"} return
1374 set v(t1,$tagno,end) $t
1379 set co [$c coords bStart]
1380 set sx [$pane getCanvasX $t]
1381 $c coords bStart $sx [lindex $co 1] $sx [lindex $co 3]
1382 $c coords gStart 0 [lindex $co 1] $sx [lindex $co 3]
1384 set start [$pane getCanvasX [GetStartByIndex $w $pane $i]]
1387 set co [$c coords b$tagno]
1388 PlaceLabel $w $pane $tagno $co $start $xc
1391 PlaceNextLabel $w $pane $i $xc
1395 $w messageProc [format "Transcription - %s" [$w formatTime $t]]
1398 proc trans::PlayLabel {w pane x y} {
1399 upvar [namespace current]::${pane}::var v
1400 set c [$pane canvas]
1401 set tags [$c gettags [$c find closest [$c canvasx $x] [$c canvasy $y]]]
1403 if {[isLabel $tags]} {
1404 set tagno [string trim [lindex $tags 0] g]
1405 set i [lsearch -exact $v(map) $tagno]
1406 if {$i == -1} return
1409 set cx [$c canvasx $x]
1410 set t [$pane getTime $cx]
1411 foreach ind $v(map) {
1412 if {$t < $v(t1,$ind,end)} break
1416 set start [GetStartByIndex $w $pane $i]
1417 set this [lindex $v(map) $i]
1418 if {$this == ""} return
1419 set end $v(t1,$this,end)
1424 proc trans::SelectLabel {w pane x y} {
1425 upvar [namespace current]::${pane}::var v
1426 set c [$pane canvas]
1427 set tags [$c gettags [$c find closest [$c canvasx $x] [$c canvasy $y]]]
1429 if {[isLabel $tags]} {
1430 set tagno [string trim [lindex $tags 0] g]
1431 set i [lsearch -exact $v(map) $tagno]
1432 if {$i == -1} return
1434 set start [GetStartByIndex $w $pane $i]
1435 set end $v(t1,$tagno,end)
1437 $w configure -selection [list $start $end]
1441 proc trans::AlignLabel {w pane x y} {
1442 upvar [namespace current]::${pane}::var v
1443 set c [$pane canvas]
1444 set tags [$c gettags [$c find closest [$c canvasx $x] [$c canvasy $y]]]
1446 if {[isLabel $tags]} {
1447 set tagno [string trim [lindex $tags 0] g]
1448 set i [lsearch -exact $v(map) $tagno]
1449 if {$i == -1} return
1451 # Get current selection
1452 foreach {start end} [$w cget -selection] break
1453 if {$start == $end} return
1455 # Validate that selection and label overlap, otherwise generate warning msg
1457 set ostart [GetStartByIndex $w $pane $i]
1458 set oend $v(t1,$tagno,end)
1460 if {$start >= $oend || $end <= $ostart} {
1461 tk_messageBox -message "Label and selection must overlap!"
1465 # Update boundaries according to current selection
1467 set v(t1,start) $start
1469 set ind [lindex $v(map) [expr $i-1]]
1470 set v(t1,$ind,end) $start
1473 set v(t1,$tagno,end) $end
1475 $w _redrawPane $pane
1479 proc trans::FindNextLabel {w pane} {
1480 upvar [namespace current]::${pane}::var v
1481 foreach {start end} [$w cget -selection] break
1483 foreach ind $v(map) {
1484 if {$end < $v(t1,$ind,end)} break
1487 set tagno [lsearch -exact $v(map) $i]
1488 if {$tagno == -1} return
1489 set start [GetStartByIndex $w $pane $i]
1490 set end $v(t1,$tagno,end)
1492 $w configure -selection [list $start $end]
1493 set s [$w cget -sound]
1494 set length [$s length -unit seconds]
1495 $w xscroll moveto [expr {($start-1.0)/$length}]
1497 set delay [expr 500 + int(1000 * ($end - $start))]
1498 after $delay [namespace code [list FindNextLabel $w $pane]]
1501 proc trans::ComputeTextPosition {w pane start end} {
1502 upvar [namespace current]::${pane}::var v
1503 if {$v(alignment) == "c"} {
1504 return [expr {($start+$end)/2}]
1505 } elseif {$v(alignment) == "w"} {
1506 return [expr {$start + 2}]
1508 return [expr {$end - 2}]
1512 proc trans::PlaceNextLabel {w pane index pos} {
1513 upvar [namespace current]::${pane}::var v
1514 set c [$pane canvas]
1516 set next [lindex $v(map) $index]
1520 set co [$c coords g$next]
1521 $c coords g$next $pos [lindex $co 1] [lindex $co 2] [lindex $co 3]
1523 set co [$c coords b$next]
1524 $c coords g$next $pos [lindex $co 1] [lindex $co 2] [expr [lindex $co 3]-4]
1525 # $c itemconf g$next -fill yellow
1526 set xc [ComputeTextPosition $w $pane $pos [lindex $co 2]]
1527 $c coords lab$next $xc [lindex [$c coords lab$next] 1]
1531 proc trans::print {w pane c x y} {
1532 upvar [namespace current]::${pane}::var v
1534 upvar wsurf::analysis::${pane}::var ov
1535 upvar wsurf::dataplot::${pane}::var dv
1536 if {$ov(drawWaveform) || $ov(drawSpectrogram) || $dv(drawDataPlot)} {
1537 foreach otherpane [$w _getPanes] {
1538 upvar wsurf::trans::${otherpane}::var tv
1539 if {[info exists tv(extBounds)] && $tv(extBounds)} {
1546 if {[info exists drawExtBounds]} {
1547 set height [$pane cget -height]
1548 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
1549 set yAxisCanvas [$pane yaxis]
1550 set yAxisWidth [winfo width $yAxisCanvas]
1552 if {$tv(nLabels) > 0} {
1557 for {set i [expr $tv(nLabels)-1]} {$i >= 0} {incr i -1} {
1558 set ind [lindex $tv(map) $i]
1560 set start $tv(t1,start)
1562 set ind2 [lindex $tv(map) [expr {$i - 1}]]
1563 set start $tv(t1,$ind2,end)
1565 set end $tv(t1,$ind,end)
1566 set label $tv(t1,$ind,label)
1567 set lx [$pane getCanvasX $start]
1568 set rx [$pane getCanvasX $end]
1570 if {$lx >= 0 && $lx <= $width} {
1571 $c create line [expr {$rx+$yAxisWidth}] $y \
1572 [expr {$rx+$yAxisWidth}] [expr {$y+$height}] \
1573 -tags [list b$ind$pane obj bound tran$pane print tmpPrint] \
1581 if {!$v(drawTranscription)} return
1585 set yAxisCanvas [$pane yaxis]
1586 set yAxisWidth [winfo width $yAxisCanvas]
1587 set h [$pane cget -height]
1588 set width [expr {[$pane cget -maxtime] * [$pane cget -pixelspersecond]}]
1590 $c create rectangle $yAxisWidth $y \
1591 [expr {$x+$width+$yAxisWidth}] [expr {$y+$h}] \
1592 -tags print -outline black
1593 _redraw $w $pane $c $yAxisWidth $y
1596 proc trans::cursorMoved {w pane time value} {
1597 upvar [namespace current]::${pane}::var v
1599 if {$v(drawTranscription)} {
1601 [format "%s: %s | $v(labelMenuEvent): Label menu" $v(fileName) [$w formatTime $time]]
1605 proc trans::soundChanged {w flag} {
1606 set s [$w cget -sound]
1607 foreach pane [$w _getPanes] {
1608 upvar [namespace current]::${pane}::var v
1609 if {$v(drawTranscription)} {
1610 $w _redrawPane $pane
1615 proc trans::propertyPane {w pane} {
1616 if {$pane==""} return
1617 upvar [namespace current]::${pane}::var v
1619 if {$v(drawTranscription)} {
1620 list Trans1 [namespace code drawPage1] \
1621 Trans2 [namespace code drawPage2]
1625 proc trans::applyProperties {w pane} {
1626 if {[string match *wavebar $pane]} return
1628 upvar [namespace current]::${pane}::var v
1630 if {[info exists v(drawTranscription)]} {
1631 if {$v(drawTranscription)} {
1632 foreach var {format alignment labext labdir encoding \
1633 labColor bdColor bgColor \
1634 font menuNrows menuNcols labelMenuEvent adjustLeftEvent \
1635 adjustRightEvent playLabelEvent locked quickenter quicktol \
1636 extBounds linkBounds highlight} {
1637 if {[string compare $v(t,$var) $v($var)] !=0} {
1638 if [string match labelMenuEvent $var] {
1639 event delete <<LabelMenuEvent>> <$v($var)>
1640 event add <<LabelMenuEvent>> <$v(t,$var)>
1642 if [string match adjustLeftEvent $var] {
1643 event delete <<AdjustLeftEvent>> <$v($var)>
1644 event add <<AdjustLeftEvent>> <$v(t,$var)>
1646 if [string match adjustRightEvent $var] {
1647 event delete <<AdjustRightEvent>> <$v($var)>
1648 event add <<AdjustRightEvent>> <$v(t,$var)>
1650 if [string match playLabelEvent $var] {
1651 event delete <<PlayLabelEvent>> <$v($var)>
1652 event add <<PlayLabelEvent>> <$v(t,$var)>
1654 if {$::tcl_version > 8.2 && [string match locked $var] == 1} {
1655 set c [$pane canvas]
1657 $c configure -state disabled
1659 $c configure -state normal
1662 if {[string match format $var] || \
1663 [string match labext $var] || \
1664 [string match encoding $var] || \
1665 [string match labdir $var]} {
1667 if {[string match no [tk_messageBox -message "This operation will cause the transcription to be re-read from disk and you have unsaved changes.\nDo you want to continue?" -type yesno -icon question]]} {
1671 set v($var) $v(t,$var)
1672 openTranscriptionFile $w $pane [$w getInfo fileName] soundfile
1675 set v($var) $v(t,$var)
1676 if {[string match labColor $var] || \
1677 [string match bdColor $var] || \
1678 [string match font $var] || \
1679 [string match extBounds $var] || \
1680 [string match alignment $var] || \
1681 [string match bgColor $var]} {
1684 if {[string match format $var]} {
1689 if {[info exists doRedraw]} {
1690 $w _redrawPane $pane
1692 if {[info exists formatChanged]} {
1693 wsurf::_remeberPropertyPage $w $pane
1694 wsurf::_drawPropertyPages $w $pane
1696 for {set i 0} {$i < $v(menuNrows)} {incr i } {
1697 for {set j 0} {$j < $v(menuNcols)} {incr j } {
1698 set v($i$j) $v(t,$i$j)
1705 proc trans::drawPage1 {w pane path} {
1707 upvar [namespace current]::${pane}::var v
1709 foreach f [winfo children $path] {
1713 foreach var {format alignment labext labdir encoding \
1714 labColor bdColor bgColor \
1715 font locked quickenter quicktol extBounds linkBounds} {
1716 set v(t,$var) $v($var)
1719 pack [frame $path.f1] -anchor w
1720 label $path.f1.l -text "Label file format:" -width 25 -anchor w
1721 foreach {format loadProc saveProc} $Info(formats) {
1724 eval tk_optionMenu $path.f1.om [namespace current]::${pane}::var(t,format) \
1726 pack $path.f1.l $path.f1.om -side left -padx 3
1728 pack [frame $path.f2] -anchor w
1729 label $path.f2.l -text "Label alignment:" -width 25 -anchor w
1730 tk_optionMenu $path.f2.om [namespace current]::${pane}::var(t,alignment) \
1732 $path.f2.om.menu entryconfigure 0 -value w
1733 $path.f2.om.menu entryconfigure 1 -value c
1734 $path.f2.om.menu entryconfigure 2 -value e
1735 pack $path.f2.l $path.f2.om -side left -padx 3
1737 stringPropItem $path.f3 "Label filename extension:" 25 16 "" \
1738 [namespace current]::${pane}::var(t,labext)
1740 pack [frame $path.f4] -anchor w
1741 label $path.f4.l -text "Label file path:" -width 25 -anchor w
1742 entry $path.f4.e -textvar [namespace current]::${pane}::var(t,labdir) -wi 16
1743 pack $path.f4.l $path.f4.e -side left -padx 3
1744 if {[info command tk_chooseDirectory] != ""} {
1745 button $path.f4.b -text Choose... \
1746 -command [namespace code [list chooseDirectory $w $pane]]
1747 pack $path.f4.b -side left -padx 3
1750 stringPropItem $path.f5 "Label file encoding:" 25 16 "" \
1751 [namespace current]::${pane}::var(t,encoding)
1753 colorPropItem $path.f6 "Label color:" 25 \
1754 [namespace current]::${pane}::var(t,labColor)
1756 colorPropItem $path.f7 "Boundary color:" 25 \
1757 [namespace current]::${pane}::var(t,bdColor)
1759 colorPropItem $path.f8 "Background color:" 25 \
1760 [namespace current]::${pane}::var(t,bgColor)
1762 stringPropItem $path.f9 "Font:" 25 16 "" \
1763 [namespace current]::${pane}::var(t,font)
1765 if {$::tcl_version > 8.2} {
1766 booleanPropItem $path.f10 "Lock transcription" "" \
1767 [namespace current]::${pane}::var(t,locked)
1770 booleanPropItem $path.f11 "Quick transcribe" "" \
1771 [namespace current]::${pane}::var(t,quickenter)
1773 stringPropItem $path.f12 "Max cursor movement for current label:" 34 4 \
1774 pixels [namespace current]::${pane}::var(t,quicktol)
1776 booleanPropItem $path.f13 "Extend boundaries into waveform and spectrogram panes" "" \
1777 [namespace current]::${pane}::var(t,extBounds)
1779 booleanPropItem $path.f14 "Move coinciding boundaries in other transcription panes" "" \
1780 [namespace current]::${pane}::var(t,linkBounds)
1783 proc trans::confPage {w pane path} {
1784 upvar [namespace current]::${pane}::var v
1786 for {set i 0} {$i < $v(t,menuNrows)} {incr i } {
1787 if {![winfo exists $path.fl$i]} {
1788 pack [frame $path.fl$i] -anchor w
1790 for {set j 0} {$j < $v(t,menuNcols)} {incr j } {
1791 if {![winfo exists $path.fl$i.e$j]} {
1792 pack [entry $path.fl$i.e$j -width 6 \
1793 -textvar [namespace current]::${pane}::var(t,$i$j)] -side left
1795 $path.fl$i.e$j configure -font $v(t,font)
1797 while {[winfo exists $path.fl$i.e$j] == 1} {
1798 destroy $path.fl$i.e$j
1802 while {[winfo exists $path.fl$i] == 1} {
1808 proc trans::chooseDirectory {w pane} {
1809 upvar [namespace current]::${pane}::var v
1810 set dir $v(t,labdir)
1814 set res [tk_chooseDirectory -initialdir $dir -mustexist yes]
1816 set v(t,labdir) $res
1820 proc trans::drawPage2 {w pane path} {
1821 upvar [namespace current]::${pane}::var v
1823 foreach f [winfo children $path] {
1827 foreach var {adjustLeftEvent adjustRightEvent playLabelEvent labelMenuEvent \
1828 menuNrows menuNcols highlight} {
1829 set v(t,$var) $v($var)
1831 for {set i 0} {$i < $v(menuNrows)} {incr i } {
1832 for {set j 0} {$j < $v(menuNcols)} {incr j } {
1833 set v(t,$i$j) $v($i$j)
1837 booleanPropItem $path.f0 "Highlight labels during playback" "" \
1838 [namespace current]::${pane}::var(t,highlight)
1840 stringPropItem $path.f1 "Adjust left boundary event:" 28 25 "" \
1841 [namespace current]::${pane}::var(t,adjustLeftEvent)
1843 stringPropItem $path.f2 "Adjust right boundary event:" 28 25 "" \
1844 [namespace current]::${pane}::var(t,adjustRightEvent)
1846 stringPropItem $path.f3 "Play label event:" 28 25 "" \
1847 [namespace current]::${pane}::var(t,playLabelEvent)
1849 stringPropItem $path.f4 "Label menu event:" 28 25 "" \
1850 [namespace current]::${pane}::var(t,labelMenuEvent)
1852 pack [frame $path.f5] -anchor w
1853 pack [label $path.f5.l -text "Label menu pane:" -width 25 -anchor w] -padx 3
1854 pack [frame $path.f6] -anchor w
1855 pack [label $path.f6.lc -text "Columns:" -anchor w] -side left -padx 3
1856 pack [entry $path.f6.ec -width 2 -textvar \
1857 [namespace current]::${pane}::var(t,menuNcols)] -side left
1858 pack [label $path.f6.lr -text "Rows:" -anchor w] -side left
1859 pack [entry $path.f6.er -width 2 -textvar \
1860 [namespace current]::${pane}::var(t,menuNrows)] -side left
1861 pack [button $path.f6.b -text Update \
1862 -command [namespace code [list confPage $w $pane $path]]] -side left \
1864 bind $path.f6.ec <Key-Return> [namespace code [list confPage $w $pane $path]]
1865 bind $path.f6.er <Key-Return> [namespace code [list confPage $w $pane $path]]
1867 for {set i 0} {$i < $v(t,menuNrows)} {incr i } {
1868 pack [frame $path.fl$i] -anchor w
1869 for {set j 0} {$j < $v(t,menuNcols)} {incr j } {
1870 pack [entry $path.fl$i.e$j -font $v(t,font) \
1871 -textvar [namespace current]::${pane}::var(t,$i$j) -wi 6] \
1877 proc trans::getConfiguration {w pane} {
1878 upvar [namespace current]::${pane}::var v
1881 if {$pane==""} {return {}}
1882 if {$v(drawTranscription)} {
1884 lappend labmenu $v(menuNcols) $v(menuNrows)
1885 for {set i 0} {$i < $v(menuNrows)} {incr i } {
1886 for {set j 0} {$j < $v(menuNcols)} {incr j } {
1887 if {[info exists v($i$j)]} {
1888 lappend labmenu $v($i$j)
1890 lappend labmenu \"\"
1895 append result "\$widget trans::addTranscription \$pane\
1896 -alignment $v(alignment)\
1897 -format \"$v(format)\"\
1898 -extension \"$v(labext)\"\
1899 -labelcolor $v(labColor)\
1900 -boundarycolor $v(bdColor)\
1901 -backgroundcolor $v(bgColor)\
1902 -labeldirectory \"$v(labdir)\"\
1903 -fileencoding \"$v(encoding)\"\
1904 -labelmenuevent $v(labelMenuEvent)\
1905 -adjustleftevent $v(adjustLeftEvent)\
1906 -adjustrightevent $v(adjustRightEvent)\
1907 -playlabelevent $v(playLabelEvent)\
1909 -quickenter $v(quickenter)\
1910 -quickentertolerance $v(quicktol)\
1911 -extendboundaries $v(extBounds)\
1912 -linkboundaries $v(linkBounds)\
1913 -playhighlight $v(highlight)\
1915 append result " -labelmenu \{\n"
1916 append result "[lrange $labmenu 0 1]\n"
1917 for {set i 0} {$i < $v(menuNrows)} {incr i } {
1918 append result "[lrange $labmenu [expr 2+$i*$v(menuNcols)] [expr 1+($i+1)*$v(menuNcols)]]\n"
1926 proc trans::cut {w t0 t1} {
1927 set dt [expr {$t1-$t0}]
1928 foreach pane [$w _getPanes] {
1929 upvar [namespace current]::${pane}::var v
1930 if $v(drawTranscription) {
1931 if {[llength $v(map)] == 0} continue
1932 set c [$pane canvas]
1935 foreach ind $v(map) {
1936 if {$t0 < $v(t1,$ind,end)} break
1941 if {$t0 < $v(t1,start)} {
1942 if {$t1 < $v(t1,start)} {
1943 # Current selection is to the left of start time
1944 set v(t1,start) [expr {$v(t1,start)-$dt}]
1946 # Left boundary of current selection is to the left of start time
1951 # Left boundary is new end time for first label
1952 if {$t0 < $v(t1,$ind,end) && \
1953 $t1 > $v(t1,$ind,end)} {
1954 set v(t1,$ind,end) $t0
1956 set ind [lindex $v(map) $i]
1960 # Delete labels within the selection
1961 while {$ind != "" && $t1 > $v(t1,$ind,end)} {
1962 # unset v(t1,$ind,label)
1963 # unset v(t1,$ind,end)
1964 # unset v(t1,$ind,rest)
1966 set ind [lindex $v(map) $i]
1968 if {$j <= [expr $i - 1] && $j < [llength $v(map)]} {
1969 set v(map) [lreplace $v(map) $j [expr $i - 1]]
1970 set v(nLabels) [llength $v(map)]
1973 # Move all remaining labels $dt to the left
1974 set ind [lindex $v(map) $j]
1975 while {$ind != "" && $t1 < $v(t1,$ind,end)} {
1976 set v(t1,$ind,end) [expr {$v(t1,$ind,end)-$dt}]
1978 set ind [lindex $v(map) $j]
1981 $w _redrawPane $pane
1986 proc trans::copy {w t0 t1} {
1987 foreach pane [$w _getPanes] {
1988 upvar [namespace current]::${pane}::var v
1989 if $v(drawTranscription) {
1990 set c [$pane canvas]
1991 if {[$c focus] != {}} {
1993 if {[catch {set s [$c index $tag sel.first]}]} return
1994 set e [$c index $tag sel.last]
1995 clipboard append [string range [$c itemcget $tag -text] $s $e]
2001 proc trans::paste {w t length} {
2002 foreach pane [$w _getPanes] {
2003 upvar [namespace current]::${pane}::var v
2004 if $v(drawTranscription) {
2005 set c [$pane canvas]
2006 if {[focus] == $c && [$c focus] != $v(hidden)} {
2007 catch {set cbText [selection get -selection CLIPBOARD]}
2008 if {[info exists cbText] == 0} { return 0 }
2009 $c insert [$c focus] insert [selection get -selection CLIPBOARD]
2010 SetLabelText $w $pane [lindex [$c gettags [$c focus]] 0] \
2011 [$c itemcget [$c focus] -text]
2018 foreach pane [$w _getPanes] {
2019 upvar [namespace current]::${pane}::var v
2020 if $v(drawTranscription) {
2021 if {[llength $v(map)] == 0} return
2023 foreach ind $v(map) {
2024 if {$t < $v(t1,$ind,end)} break
2029 if {$t < $v(t1,start)} {
2030 set v(t1,start) [expr {$v(t1,start)+$length}]
2033 # Move all remaining labels $length to the left
2034 while {$ind != ""} {
2035 set v(t1,$ind,end) [expr {$v(t1,$ind,end)+$length}]
2037 set ind [lindex $v(map) $i]
2040 $w _redrawPane $pane
2045 proc trans::find {w pane} {
2046 upvar [namespace current]::${pane}::var v
2050 $p.f2.list delete 0 end
2052 if {$v(matchCase)} {
2057 foreach ind $v(map) {
2058 if {[eval regexp $nocase $v(pattern) \{$v(t1,$ind,label)\}]} {
2060 set start $v(t1,start)
2062 set prev [lindex $v(map) [expr $i-1]]
2063 set start $v(t1,$prev,end)
2065 if {[string match *\"* \{$v(t1,$ind,label)\}]} {
2066 set tmp "\{$v(t1,$ind,label):\} $start $v(t1,$ind,end)"
2068 set tmp "$v(t1,$ind,label): $start $v(t1,$ind,end)"
2070 $p.f2.list insert end $tmp
2077 proc trans::select {w pane} {
2078 upvar [namespace current]::${pane}::var v
2082 set cursel [$p.f2.list curselection]
2083 if {$cursel == ""} return
2084 set start [lindex [$p.f2.list get [lindex $cursel 0]] end-1]
2085 set end [lindex [$p.f2.list get [lindex $cursel end]] end]
2086 $w configure -selection [list $start $end]
2087 set s [$w cget -sound]
2088 set length [$s length -unit seconds]
2089 $w xscroll moveto [expr {$start/$length}]
2092 proc trans::findPlay {w pane} {
2093 upvar [namespace current]::${pane}::var v
2096 set cursel [$p.f2.list curselection]
2097 if {$cursel != ""} {
2098 set start [lindex [$p.f2.list get [lindex $cursel 0]] end-1]
2099 set end [lindex [$p.f2.list get [lindex $cursel end]] end]
2104 proc trans::browse {w pane} {
2105 upvar [namespace current]::${pane}::var v
2107 regsub -all {\.} $pane _ tmp
2108 set v(browseTL) .browse$tmp
2109 catch {destroy .browse$tmp}
2110 set p [toplevel .browse$tmp]
2111 wm title $p "Browse Labels"
2114 pack [entry $p.f.e -textvar [namespace current]::${pane}::var(pattern)]\
2116 pack [button $p.f.l -text Find \
2117 -command [namespace code [list find $w $pane]]] -side left
2119 pack [ label $p.l -text "Results:"]
2120 pack [ frame $p.f2] -fill both -expand true
2121 pack [ scrollbar $p.f2.scroll -command "$p.f2.list yview"] -side right \
2123 listbox $p.f2.list -yscroll "$p.f2.scroll set" -setgrid 1 \
2124 -selectmode extended -height 6 -width 40
2125 pack $p.f2.list -side left -expand true -fill both
2127 pack [checkbutton $p.cb -text "Match case" -anchor w \
2128 -variable [namespace current]::${pane}::var(matchCase)]
2130 pack [ frame $p.f3] -pady 10 -fill x -expand true
2131 pack [ button $p.f3.b1 -bitmap snackPlay \
2132 -command [namespace code [list findPlay $w $pane]]] \
2134 pack [ button $p.f3.b2 -bitmap snackStop -command "$w stop"] -side left
2135 pack [ button $p.f3.b3 -text Close -command "destroy $p"] -side right
2137 bind $p.f.e <Return> [namespace code [list find $w $pane]]
2138 bind $p.f2.list <ButtonRelease-1> [namespace code [list select $w $pane]]
2139 if {$v(pattern) != ""} {
2142 bind $p.f2.list <Double-Button-1> [namespace code [list findPlay $w $pane]]
2146 proc trans::convert {w pane} {
2147 upvar [namespace current]::${pane}::var v
2149 regsub -all {\.} $pane _ tmp
2150 set v(convertTL) .convert$tmp
2151 catch {destroy .convert$tmp}
2152 set p [toplevel .convert$tmp]
2153 wm title $p "Convert Transcription File format"
2155 pack [ label $p.l1 -text "Current transcription file format: $v(format)"]
2157 set v(t,format) $v(format)
2158 pack [frame $p.f1] -anchor w
2159 label $p.f1.l -text "New transcription file format:" -anchor w
2160 foreach {format loadProc saveProc} $Info(formats) {
2161 lappend fmtlist $format
2163 eval tk_optionMenu $p.f1.om [namespace current]::${pane}::var(t,format) \
2165 pack $p.f1.l $p.f1.om -side left -padx 3
2168 pack [ button $p.f.b1 -text OK -command [namespace code [list doConvert $w $pane]]\n[list destroy $p]] -side left -padx 3
2169 pack [ button $p.f.b2 -text Close -command "destroy $p"] -side left -padx 3
2172 proc trans::doConvert {w pane} {
2173 upvar [namespace current]::${pane}::var v
2174 set v(format) $v(t,format)
2177 proc trans::play {w} {
2178 foreach pane [$w _getPanes] {
2179 upvar [namespace current]::${pane}::var v
2180 if {$v(drawTranscription) && $v(highlight)} {
2184 after 200 [namespace code [list _updatePlay $w]]
2187 proc trans::stop {w} {
2188 foreach pane [$w _getPanes] {
2189 upvar [namespace current]::${pane}::var v
2190 set c [$pane canvas]
2191 if {$v(drawTranscription)} {
2192 after cancel [namespace code [list FindNextLabel $w $pane]]
2197 proc trans::_updatePlay {w} {
2198 if {[winfo exists $w] == 0} {
2201 if {[$w getInfo isPlaying] == 0} {
2202 foreach pane [$w _getPanes] {
2203 upvar [namespace current]::${pane}::var v
2204 set c [$pane canvas]
2205 if {$v(drawTranscription)} {
2206 if {$v(highlight) && [info exists v(playIndex)]} {
2207 set ind [lindex $v(map) $v(playIndex)]
2209 $c itemconf g$ind -fill $v(bgColor)
2216 set s [$w cget -sound]
2217 foreach pane [$w _getPanes] {
2218 upvar [namespace current]::${pane}::var v
2219 if {$v(drawTranscription) && $v(highlight)} {
2220 set cursorpos [$pane cget -cursorpos]
2221 set c [$pane canvas]
2222 set ind [lindex $v(map) $v(playIndex)]
2224 $c itemconf g$ind -fill $v(bgColor)
2226 set ind [lindex $v(map) $v(playIndex)]
2227 if {$ind == ""} return
2228 if {$cursorpos < $v(t1,$ind,end)} break
2231 $c itemconf g$ind -fill [$w cget -cursorcolor]
2235 if {[$w getInfo isPlaying]} {
2236 after 50 [namespace code [list _updatePlay $w]]
2240 # -----------------------------------------------------------------------------
2243 proc trans::regCallback {name callback script} {
2245 # puts [info level 0]
2246 if {$callback != "-transcription::transcriptionchangedproc"} {
2247 error "unknown callback \"$callback\""
2249 set Info(Callback,$name,transChangedProc) $script
2253 proc trans::changed {w pane} {
2254 # puts [info level 0]([info level -1])
2256 upvar [namespace current]::${pane}::var v
2258 foreach key [array names Info Callback,*,transChangedProc] {
2259 puts "invoking callback $key"
2260 $Info($key) $w $pane
2269 proc trans::SplitSoundFile {w pane} {
2270 upvar [namespace current]::${pane}::var v
2271 set s [$w cget -sound]
2273 foreach ind $v(map) {
2274 set start [expr {int([GetStartByIndex $w $pane $ind] * [$s cget -rate])}]
2275 set end [expr {int($v(t1,$ind,end) * [$s cget -rate])}]
2276 $s write $v(t1,$ind,label).wav -start $start -end $end