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 }
9
10# If they are present add new filetypes to file dialogs
11set extTypes  {}
12set loadTypes {}
13set loadKeys  {}
14set saveTypes {}
15set saveKeys  {}
16if {[info exists snack::snacksphere]} {
17    lappend extTypes {SPHERE .sph} {SPHERE .wav}
18    lappend loadTypes {{SPHERE Files} {.sph}} {{SPHERE Files} {.wav}}
19    lappend loadKeys SPHERE SPHERE
20}
21if {[info exists snack::snackogg]} {
22  lappend extTypes  {OGG .ogg}
23  lappend loadTypes {{Ogg Vorbis Files} {.ogg}}
24  lappend loadKeys  OGG
25  lappend saveTypes {{Ogg Vorbis Files} {.ogg}}
26  lappend saveKeys  OGG
27}
28snack::addExtTypes $extTypes
29snack::addLoadTypes $loadTypes $loadKeys
30snack::addSaveTypes $saveTypes $saveKeys
31
32
33snack::debug 0
34snack::sound s -debug 0
35snack::sound s2
36snack::sound sa
37
38set timestr ""
39option add *font {Helvetica 10 bold}
40wm title . "Snack Audio MPEG Player"
41
42if 0 {
43set draw 1
44pack [frame .f]
45pack [canvas .f.c -width 140 -height 40] -side left
46pack [checkbutton .f.a -text Analyzer -variable draw] -side left
47
48for {set i 0} {$i<16} {incr i} {
49  .f.c create rect [expr 10*$i] 20 [expr 10*$i+10] 40 -fill green  -outline ""
50  .f.c create rect [expr 10*$i] 10 [expr 10*$i+10] 20 -fill yellow -outline ""
51  .f.c create rect [expr 10*$i] 0  [expr 10*$i+10] 10 -fill red   -outline ""
52  .f.c create rect [expr 10*$i] 0  [expr 10*$i+10] 40 -fill black -tag c$i
53}
54for {set i 0} {$i<17} {incr i} {
55  .f.c create line [expr 10*$i] 0 [expr 10*$i] 40 -width 5
56}
57for {set i 0} {$i<7} {incr i} {
58  .f.c create line 0 [expr 6*$i] 140 [expr 6*$i] -width 3
59}
60}
61
62pack [frame .frame] -side top -expand yes -fill both
63scrollbar .frame.scroll -command ".frame.list yview"
64listbox .frame.list -yscroll ".frame.scroll set" -setgrid 1 -selectmode single -exportselection false -height 16
65pack .frame.scroll -side right -fill y
66pack .frame.list -side left -expand 1 -fill both
67bind .frame.list <Double-ButtonPress-1> Play
68bind .frame.list <B1-Motion> {Drag %y}
69bind .frame.list <ButtonPress-1> {Select %y}
70bind . <BackSpace> Cut
71
72snack::createIcons
73pack [frame .panel] -side bottom -before .frame
74pack [button .panel.bp -bitmap snackPlay -command Play] -side left
75pack [button .panel.bs -bitmap snackStop -command Stop] -side left
76pack [button .panel.bo -image snackOpen -command Open] -side left
77set p 0
78pack [scale .panel.ss -show no -orient horiz -len 130 -var p] -side left
79set gain [snack::audio play_gain]
80pack [scale .panel.sv -show no -orient horiz -command {snack::audio play_gain}\
81	-len 70 -var gain] -side left
82set setdrag 1
83bind .panel.ss <ButtonPress-1> {set setdrag 0}
84bind .panel.ss <ButtonRelease-1> {set setdrag 1 ; Play2}
85pack [label .panel.l -textvar timestr]
86
87proc Open {} {
88    global files
89    set file [snack::getOpenFile -format MP3]
90    if {$file != ""} {
91	set name [file tail $file]
92	set files($name) $file
93	.frame.list insert end $name
94    }
95}
96
97proc Play args {
98    global files t0 filelen
99    if {[.frame.list curselection] == ""} {
100	set i 0
101    } else {
102	set i [lindex [.frame.list curselection] 0]
103    }
104    .frame.list selection set $i
105    Stop
106    s config -file $files([.frame.list get $i])
107    sa config -file $files([.frame.list get $i])
108    if {$args == ""} {
109	s play -command Next
110	set t0 [clock scan now]
111    } else {
112	s play -start $args -command Next
113	set t0 [expr [clock scan now] - $args / [s cget -rate]]
114    }
115    set filelen [s length]
116    Timer
117}
118
119proc Play2 {} {
120    global filelen p
121    Play [expr int($p/100.0*[s length])]
122}
123
124proc Stop {} {
125    s stop
126    after cancel Timer
127}
128
129proc Timer {} {
130    global t0 timestr setdrag
131    set time [expr [clock scan now] - $t0]
132    set timestr [clock format $time -format "%M:%S"]
133    if $setdrag {
134	.panel.ss set [expr int(100 * $time / [s length -unit sec])]
135    }
136#    Draw
137    after 100 Timer
138}
139
140proc Next {} {
141    set i [lindex [.frame.list curselection] 0]
142    if {$i == ""} return
143    .frame.list selection clear $i
144    incr i
145    .frame.list selection set $i
146    .frame.list see $i
147    after 10 Play
148}
149
150set cut ""
151proc Cut {} {
152    global cut
153    if {[.frame.list curselection] != ""} {
154	set cut [.frame.list get [.frame.list curselection]]
155	.frame.list delete [.frame.list curselection]
156    }
157}
158
159proc Select y {
160    global old timestr files
161    set old [.frame.list nearest $y]
162    s2 config -file $files([.frame.list get $old])
163    set timestr [clock format [expr int([s2 length -unit sec])] -format "%M:%S"]
164}
165
166proc Drag y {
167    global old
168    set new [.frame.list nearest $y]
169    if {$new == -1} return
170    set tmp [.frame.list get $old]
171    .frame.list delete $old
172    .frame.list insert $new $tmp
173    .frame.list selection set $new
174    set old $new
175}
176
177array set map {
178    0 2
179    1 3
180    2 4
181    3 5
182    4 7
183    5 9
184    6 12
185    7 15
186    8 19
187    9 23
188    10 28
189    11 34
190    12 41
191    13 49
192    14 56
193    15 63
194}
195
196proc Draw {} {
197    global draw
198    if ![snack::audio active] return
199    if {$draw == 1} {
200puts [time {
201	set pos [expr int([s cget -rate] * [snack::audio elapsed])]
202        if {$pos > 1000} {
203         set junk [sa sample [expr $pos - 1000]]
204         set junk [sa sample [expr $pos - 100]]
205         set junk [sa sample [expr $pos]]
206puts $junk
207	}
208	set spec [sa dBPower -start $pos -fftlen 128 -windowlength 128]
209	for {set i 0} {$i < 16} {incr i} {
210	    set val [lindex $spec $::map($i)]
211	    .f.c coords c$i [expr 10*($i-2)] 0 [expr 10*($i-2)+9] \
212		    [expr 100-1.4*($val+100)]
213	}
214    }]
215    }
216}
217
218if [info exists argv] {
219 if [file isdirectory $argv] {
220  catch {cd $argv}
221 }
222}
223
224wm protocol . WM_DELETE_WINDOW exit
225
226if {$::tcl_platform(platform) == "windows"} {
227 set filelist [glob -nocomplain *.mp3 *.wav]
228} else {
229 set filelist [glob -nocomplain *.mp3 *.wav *.MP3 *.WAV]
230}
231if {[info exists snack::snackogg]} {
232  set filelist [concat $filelist [glob -nocomplain *.ogg *.OGG]]
233}
234foreach file [lsort -dictionary $filelist] {
235    set name [file tail $file]
236    set files($name) $file
237    .frame.list insert end $file
238}
239