File: formant.tcl

package info (click to toggle)
snack 2.2.10.20090623-dfsg-8
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 3,764 kB
  • sloc: ansic: 32,662; sh: 8,558; tcl: 1,086; python: 761; makefile: 582
file content (395 lines) | stat: -rwxr-xr-x 11,830 bytes parent folder | download | duplicates (12)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#!/bin/sh
# the next line restarts using wish \
exec wish8.3 "$0" "$@"

package require -exact snack 2.2

# there is no way (?) to find out from Tk if we can display UNICODE IPA
# but it seems to be standard on windows installations
if {[string match windows $tcl_platform(platform)]} {set UNICODE_IPA 1}

switch $tcl_platform(platform) {
 windows {
  proc milliseconds { } {clock clicks}
 }
 unix {
  proc milliseconds { } {expr {[clock clicks]/1000}}
 }
}


set vowels(sw) {
 O: u      300 600 2350 3250
 O  \u028a 350 700 2600 3200
 : o      400 700 2450 3250
   \u0254 500 850 2550 3250
 A: \u0251 600 950 2550 3300
 A  a      750 1250 2500 3350
 I: i      250 2200 3150 3750
 I  \u026a 350 2150 2750 3500
 E: e      350 2250 2850 3550
 E/ \u025b 500 1900 2550 3350
 3 \ue6   650 1700 2500 3450
 Y: y      250 2050 2700 3300
 Y  \u028f 300 2000 2400 3250
 : \uf8   400 1750 2300 3350
   \u153  550 1550 2450 3300
 3 ""     550 1150 2450 3250
 U: \u0289 300 1650 2250 2250
 U  \u0275 450 1050 2300 3300
}

set vowels(us) {
 i i 280 2250 2890 {}
 I \u026a 400 1920 2560 {}
 E \u025b 550 1770 2490 {}
 @ \u00e 6690 1660 2490 {}
 A \u0251 710 1100 2540 {}
 > \u0254 590 880 2540 {}
 U \u028a 450 1030 2380 {}
 u u 310 870 2250 {}
}

set vowels(lang) us
 
proc vok4Create {w {wid 200} {hei 200}} {
 upvar #0 $w a
 frame $w -width $wid -height $hei
 pack [canvas $w.c -bg black] -fill both -expand 1
 pack propagate $w 0
 set a(xm) 20
 set a(ym) 20
 set a(F10) 800
 set a(F11) 200
 set a(F20) 2300
 set a(F21) 500
 
 $w.c create line 0 0 0 0 -fill white -tags axes -arrow both
 $w.c create text 0 0 -anchor e -text F2 -fill yellow -tags ylabel
 $w.c create text 0 0 -anchor n -text F1 -fill yellow -tags xlabel
 menu $w.m -tearoff 0
 $w.m add radiobutton -variable vowels(lang) -value sw \
   -command [list vok4Config $w] -label "Swedish vowels (after Fant)"
 $w.m add radiobutton -variable vowels(lang) -value us \
   -command [list vok4Config $w] -label "American vowels (after Ladefoged)"
 $w.m add radiobutton -variable vowels(lang) -value NIL \
   -command [list vok4Config $w] -label "Don't display vowels"
 
 # trailInit $w 10
 
 bind $w.c <ButtonPress-1> "vok4Move $w %x %y;Play"
 bind $w.c <B1-Motion> [list vok4Move $w %x %y]
 bind $w.c <ButtonRelease-1> "Stop"
 bind $w.c <Configure> [list vok4Config $w %w %h]
 bind $w.c <ButtonPress-3> [list tk_popup $w.m %X %Y]
 return $w
}

proc vok4Config {w {wid -1} {hei -1}} {
 upvar #0 $w a
 
 if {$wid==-1} {
  set wid $a(width)
  set hei $a(height)
 } else {
  set a(width) $wid
  set a(height) $hei
 }
 set a(x0) $a(xm)
 set a(x1) [expr $wid-$a(xm)]
 set a(y0) [expr $hei-$a(ym)]
 set a(y1) $a(ym)
 $w.c coords axes $a(x0) $a(y1) \
   $a(x1) $a(y1) $a(x1) $a(y0)
 $w.c coords ylabel $a(x0) $a(y1)
 $w.c coords xlabel $a(x1) $a(y0)
 
 $w.c delete sym

 set lang $::vowels(lang)
 if [info exists ::vowels($lang)] {
  foreach {ascii uni f1 f2 f3 f4} $::vowels($lang) {
   if [info exists ::UNICODE_IPA] {set sym $uni} else {set sym $ascii}
   set x [expr {$a(x0)+($a(x1)-$a(x0))*($f2-$a(F20))*1.0/($a(F21)-$a(F20))}]
   set y [expr {$a(y0)+($a(y1)-$a(y0))*($f1-$a(F10))*1.0/($a(F11)-$a(F10))}]
   $w.c create text $x $y -font "times 16" -anchor c -text $sym -fill gray -tags sym
  }
 }
}

proc vok4Move {w x y} {
# puts [info level 0]
 upvar #0 $w a
 
 set f1 [expr {int($a(F10)+($a(F11)-$a(F10))*($y-$a(y0))*1.0/($a(y1)-$a(y0)))}]
 set f2 [expr {int($a(F20)+($a(F21)-$a(F20))*($x-$a(x0))*1.0/($a(x1)-$a(x0)))}]
 set ::v(f1) $f1
 set ::v(f2) $f2
 Config
 set a(curx) $x
 set a(cury) $y
 #  trailUpdate $w
 return ""
}

proc updatePreview {} {
 $::v(pGen) configure \
   $::v(g,freq) $::v(g,ampl) [expr 0.01*$::v(g,shape)] $::v(g,type) 1024
 $::v(pF1) configure $::v(f1) $::v(b1)
 $::v(pF2) configure $::v(f2) $::v(b2)
 $::v(pF3) configure $::v(f3) $::v(b3)
 $::v(pF4) configure $::v(f4) $::v(b4)

 preview2 copy s
 preview2 filter $::v(pAll)
 preview1 copy s
 preview1 filter $::v(pGen)

 after cancel updatePreview
 if {$::v(on) && $::v(g,type)=="noise"} {
  after 100 updatePreview
 }
}

proc Config {args} {
 $::v(Gen) configure \
   $::v(g,freq) $::v(g,ampl) [expr 0.01*$::v(g,shape)] $::v(g,type) -1
 $::v(F1) configure $::v(f1) $::v(b1)
 $::v(F2) configure $::v(f2) $::v(b2)
 $::v(F3) configure $::v(f3) $::v(b3)
 $::v(F4) configure $::v(f4) $::v(b4)
 updatePreview
}

proc Play {} {
 set ::v(on) 1
 s stop
 s play -filter $::v(All)
 updatePreview
 set ::v(tstart) [milliseconds]
 #  updateTracks
 .f1.b config -relief sunken
}

proc Stop {} {
 s stop
 set ::v(on) 0
 .f1.b config -relief raised
}

proc Load {} {
 set file [snack::getOpenFile]
 if {$file != ""} {s read $file}
}

proc updateTracks {} {
 set tt 50
 set now [milliseconds]
 set then $::v(tstart)
 set dt [expr 1.0*([milliseconds]-$::v(tstart))]
 #set ::v(g,freq) [expr 100+100*(1.0*$dt/$tt)*exp(-$dt/$tt)]
 set ::v(g,freq) [expr {100+2*cos(2*3.1415*$dt/$tt)}]

 Config

 if $::v(on) {
  after 50 updateTracks
 }
}

proc labeledScale {w args} {
 array set a {-valwidth 4 -labwidth 8}
 array set a $args
 catch {set a(-text) $a(-label)}

 frame $w
 pack [label $w.l -anchor w -width $a(-labwidth)] -side left
 foreach opt {-text -bg -width -font} {
  if [info exists a($opt)] {$w.l config $opt $a($opt)}
 }
 pack [scale $w.s -showvalue 0 -bd 1 -width 10] -side left -expand 1 -fill x
 pack [label $w.v -textvariable $a(-variable) -width $a(-valwidth) -anchor w] -side left
 foreach opt {-bg -font} {
  if [info exists a($opt)] {$w.v config $opt $a($opt)}
 }
 foreach opt {-length -bg -from -to -variable -orient -resolution -command} {
  if [info exists a($opt)] {$w.s config $opt $a($opt)}
 }
 return $w
}

proc About {} {
 set w .about
 catch {destroy $w}
 toplevel $w
 wm title $w "About: Formant Synthesis Demo"
 set text " This application demonstrates formant-based synthesis
 of vowels in real time, in the spirit of Gunnar Fant's 
 Orator Verbis Electris (OVE-1) synthesizer of 1953.

 Set source and filter parameters at the top. Click and 
 drag in the \"vowel space\" to hear the vowels. 
 Right-click to select target language for vowel symbols.
 
 Power spectrum of source (red) and output signal (green) are 
 to the right, waveforms are displayed at the bottom.
 
 The source type \"sampled\" will use a sound file 
 containing a single period of a waveform as voice source.

 Copyright  2000 Jonas Beskow
 Centre for Speech Technology
 KTH, Stockholm"

 label $w.l -text $text -relief groove -bd 2
 button $w.b -text OK -command [list set about_done 1]
 pack $w.l -side top -expand 1 -fill both -padx 5 -pady 5
 pack $w.b -side top -padx 5 -pady 5
 if [catch {::tk::PlaceWindow $w center}] {
  wm geometry $w +[winfo rootx .]+[winfo rooty .]
 }
 vwait about_done
 destroy $w
}


wm title . "Formant Synthesizer Demo"
wm resizable . 0 0

# Menu bar

menu .m
.m add cascade -label File -menu [menu .m.file -tearoff 0]
.m add cascade -label Help -menu [menu .m.help -tearoff 0]
.m.file add command -label "Load source waveform..." -command Load
.m.file add separator
.m.file add command -label Exit -command exit
.m.help add command -label About... -command About
. configure -menu .m

# Generator GUI

frame .f1 -relief groove -bd 2
grid .f1 -row 0 -column 0 -sticky news -padx 5 -pady 5
label .f1.l -text Source -bg red -anchor w
tk_optionMenu .f1.gt v(g,type) rectangle triangle sine sampled noise
button .f1.b -bitmap snackPlay -command Play
button .f1.c -bitmap snackStop -command Stop

labeledScale .f1.gf -label "Freq." -variable v(g,freq) -from 0.0 -to 1000 -resolution 1.0 -orient horiz -command Config
labeledScale .f1.ga -label "Ampl." -variable v(g,ampl) -from 0.0 -to 6000 -resolution 1.0 -length 160 -orient horiz -command Config
labeledScale .f1.gs -label "Shape" -variable v(g,shape) -from 0.0 -to 100 -resolution 1.0 -length 160 -orient horiz -command Config

grid .f1.l .f1.gt .f1.b .f1.c -sticky we -padx 5
grid .f1.gf -columnspan 4 -sticky we
grid .f1.ga -columnspan 4 -sticky we
grid .f1.gs -columnspan 4 -sticky we
grid columnconfigure .f1 0 -weight 1
grid rowconfigure .f1 4 -weight 1
# Formant filter GUI

frame .f2 -relief groove -bd 2
grid .f2 -row 0 -column 1 -sticky news -padx 5 -pady 5
label .f2.l -text "Formants" -bg green -anchor w
grid .f2.l -columnspan 5 -sticky we -padx 5 -pady 5
label .f2.lf -text "Frequency" -anchor w
label .f2.lfu -text "Hz "
label .f2.lb -text "Bandwidth" -anchor w
label .f2.lbu -text "Hz "
grid .f2.lf -row 1 -column 1 -sticky w
grid .f2.lfu -row 1 -column 2 -sticky w
grid .f2.lb -row 1 -column 3 -sticky w
grid .f2.lbu -row 1 -column 4 -sticky w

for {set i 1} {$i<=4} {incr i} {
 label .f2.l0$i -text F$i -width 2
 scale .f2.f$i -variable v(f$i) -from 0 -to 5000 -resolution 1.0 -orient horiz -command Config -showvalue 0 -bd 1 -width 10
 label .f2.l1$i -textvariable v(f$i) -anchor w -width 4
 scale .f2.b$i -variable v(b${i}) -from 1.0 -to 500 -resolution 1.0 -orient horiz -command Config -showvalue 0 -bd 1 -width 10 -length 80
 label .f2.l2$i -textvariable v(b$i) -anchor w -width 3
 grid .f2.l0$i .f2.f$i .f2.l1$i .f2.b$i .f2.l2$i -sticky news
}
grid columnconfigure .f2 1 -weight 1

set vokh 250
set vokw 275

# Vowel space

vok4Create .voc $vokw $vokh
grid .voc -row 1 -column 0 -sticky news

# Spectrum section preview

snack::sound preview1
snack::sound preview2

set secw $vokw
set sech $vokh

canvas .c2 -bg black -height 100 -width $secw
grid .c2 -row 1 -column 1  -sticky news
.c2 create section 0 0 -sound preview1 -fill red -height $sech -topfrequency 4000 -width $secw -analysistype lpc -tags sect -maxvalue 30
.c2 create section 0 0 -sound preview2 -fill green -height $sech -topfrequency 4000 -width $secw -analysistype lpc -tags sect -maxvalue 30

foreach freq {1 2 3 4} {
 set x [expr {$freq*$secw/4.0}]
 .c2 create line $x 0 $x $sech -fill #999999
 .c2 create text $x 0 -anchor ne -text $freq -fill #999999
}
.c2 create text 0 0 -anchor nw -text kHz -fill #999999
.c2 raise sect

# Waveforms preview

set wavw 550
set wavh 90

canvas .c1 -bg black -height 100 -width $wavw
grid .c1 -row 2 -columnspan 2 -sticky news
.c1 create waveform 0 50 -anchor w -sound preview1 -fill red  -height $wavh -pixelspersecond 16000
.c1 create waveform 0 50 -anchor w -sound preview2 -fill green  -height $wavh -pixelspersecond 16000

# Default values

set v(f1) 500
set v(b1) 50
set v(f2) 1500
set v(b2) 75
set v(f3) 2500
set v(b3) 100
set v(f4) 3500
set v(b4) 150
set v(g,freq) 75
set v(g,ampl) 2500
set v(g,shape) 10
set v(g,type) rectangle

# Create the filters
set v(F1) [snack::filter formant $v(f1) $v(b1)]
set v(F2) [snack::filter formant $v(f2) $v(b2)]
set v(F3) [snack::filter formant $v(f3) $v(b3)]
set v(F4) [snack::filter formant $v(f4) $v(b4)]
set v(Gen) [snack::filter generator $v(g,freq)]
set v(All) [snack::filter compose $v(Gen) $v(F1) $v(F2) $v(F3) $v(F4)]

# Create spearate filters for the preview
set v(pF1) [snack::filter formant $v(f1) $v(b1)]
set v(pF2) [snack::filter formant $v(f2) $v(b2)]
set v(pF3) [snack::filter formant $v(f3) $v(b3)]
set v(pF4) [snack::filter formant $v(f4) $v(b4)]
set v(pGen) [snack::filter generator $v(g,freq)]
set v(pAll) [snack::filter compose $v(pGen) $v(pF1) $v(pF2) $v(pF3) $v(pF4)]

set v(on) 0

snack::sound s
snack::createIcons

set samples {135 1477 969 -524 -784 314 781 -19 -543 70 696 366 -141 154 694 484 -122 -179 199 290 136 229 429 293 0 48 326 321 44 -15 210 296 137 99 256 254 82 193 625 800 497 234 346 478 354 264 411 516 420 412 628 724 524 389 563 714 557 378 477 608 476 320 450 658 598 395 380 545 628 558 486 484 461 393 383 446 464 413 399 459 520 559 612 668 670 618 569 536 481 390 312 278 255 224 199 176 152 148 158 119 0 -130 -209 -275 -405 -594 -777 -922 -1046 -1187 -1420 -1822 -2267 -2179}
s length [llength $samples]
for {set i 0} {$i<[s length]} {incr i} {
 s sample $i [lindex $samples $i]
}

trace variable v(g,type) w Config