Playing a sound file from disk
Batch processing of sound files
Compiling
scripts into stand-alone executables
Unix: type wish8.4 at a terminal window prompt (depending on which version you have installed, or simply wish)
Windows: choose wish from the Start menu (Start | Programs | Tcl | Wish)
Macintosh: double-click the wish icon in the Tcl/Tk folder
Two windows will appear,
one console and one initial application window which can be used for graphical
user interfaces.
Now load the Snack extension
by typing
package require snack
at the console prompt. Wish
will return a number which corresponds to the version of Snack that has
been installed.
Create an initial sound
object by typing
snack::sound s
You have now created a Snack
sound object named s. Sound objects are the main building blocks
of Snack scripts.
You can read a sound file
into a sound object using the read command.
Do this by typing
s read ../snack2.2/ex1.wav
the path name might vary
depending on your installation, Unix users can find the file as snack2.2a1/demos/tcl/ex1.wav.
The command will return
the string WAV corresponding to the file format of the file read.
As a convenience you can
combine the last two commands into one. That is, you can specify a file
to be read when the sound object is first created
snack::sound s -load ../snack2.2/ex1.wav
In order to play the sound you simply type
s play
Note that if you want to play the same sound again you simply repeat this command. There is no need to create new objects and read the sound file all over again.
If you want to save this sound in AU file format you can do this by typing
s write test.au
If you want the length of the sound in seconds type
s length -unit seconds
The console is only usable for small tasks because of the need to type everything by hand. The normal use would be to put the commands in a text file and run it as a script. Scripts should have the extension .tcl and have this general form
#!/bin/sh
package require snack
snack::sound snd
snd read ex1.wav
exit
# the next line restarts
using wish \
exec wish8.4 "$0" "$@"
snd write ex1.au
The first three lines makes
this script executable (with correct permissions) on Unix. It is good practice
to start every script like that. Next two statements load snack and creates
a sound object named snd.
The read command
is used to read the sound file ex1.wav in the current directory
and write is used to save it back as an AU file (the file format
to use for saving is inferred from the file name extension).
Finally exit quits the script.
snack::sound s -file ex1.wav
The audio data will now reside
on disk and only be accessed as necessary.
If you want to play another
file later, you can configure which file the sound object should link to
using the configure command
s configure -file ex2.wav
set f [snack::filter generator
440.0 30000 0.0 sine 8000]
set s [snack::sound]
$s play -filter $f
The last parameter to the generator filter is the number of samples to generate, in this case 8000. The value -1 would generate samples for ever, or until a "stop" command is issued.
A procedure for generating beeps might be defined like this
proc beep {freq} {
set len 8000
set f [snack::filter
generator $freq 30000 0.0 sine $len]
set s [snack::sound
-rate 22050]
$s play -filter
$f -command "$s destroy;$f destroy"
}
This would create the necessary filter and sound objects, play the sound, and clean up everything once the sound has been played. The option -command, which is given to the play command, specifies a command to execute once playback has completed. In this case it is the commands needed to deallocate (destroy) the objects. The newly defined procedure is called with one argument giving the freqency of the beep
beep 440
Note that the sound generated is immediately sent to the audio device, none of it is kept in memory. What you actually have done is to play an empty sound with a generator filter applied. This means that you can not save it to disk with a subsequent write command. If you want to generate the beep and keep the result in the sound object the following statements would be used
set f [snack::filter generator
440.0 30000 0.0 sine 8000]
set s [snack::sound]
$s filter $f
At this point the sound can be written to a file using a write command. In this case you can simply play it using
$s play
Try the commands below for
a more advanced example. The filters create a simple formant speech synthesizer
using a square wave generator pipelined with four formant filters. The
resulting combination will generate a neutral vowel.
set g [snack::filter generator 75 2500 0.1 rectangle -1]
set f1 [snack::filter formant 500 50]
set f2 [snack::filter formant 1500 75]
set f3 [snack::filter formant 2500 100]
set f4 [snack::filter formant 3500 150]
set synth [snack::filter compose $g $f1 $f2 $f3 $f4]
snack::sound s
s play -filter $synth
The filters can naturally
be reconfigured for other frequencies while the sound is being generated.
Try this by adding a slider connected to the first formant filter
pack [scale .s -from 200 -to 1000 -command "$f1 configure"]
See also the formant.tcl
demo in the distribution.
sound s -channel stdin
s play -command exit -blocking yes
Now try this command line in an ordinary terminal window, e.g. in an xterm or similar
cat ex1.wav | playpipe.tcl
package require snack
proc ServerCmd {sock args} {
set snd [snack::sound -channel $sock]
$snd play -command "[list close $sock];$snd destroy"
}
socket -server ServerCmd 23654
Each time a client makes a connection at port 23654, of the computer that runs this code, the procedure ServerCmd will be called. This will create a new sound object linked to the client channel and start a play operation. Note that this code snippet is capable of playing several simultaneous MP3 streams.
The following commands create matching client connection
package require snack
set sock [socket localhost 23654]
sound s -channel $sock
fconfigure $sock -translation binary
fconfigure $sock -encoding binary
s record -fileformat wav
Change localhost to be the network name of the computer the server code is run on. The socket channel is first configured for binary data, otherwise the stream would be corrupted by end-of-line conversions and similar. There will be a delay in the sound transfer due to buffering.
See also the aserver.tcl, rplay.tcl, and recrplay.tcl demos in the distribution.
#!/bin/sh
# the next line restarts using wish \
exec wish8.4 "$0" "$@"
package require snack
snack::sound s
foreach file [glob *.sd *.nsp] {
s read $file
s write [file rootname $file].wav
}
exit
#!/bin/sh
# the next line restarts using wish \
exec wish8.4 "$0" "$@"
package require snack
snack::sound s
foreach file [glob *.au] {
s read $file
s convert -frequency 16000 -channels mono -format Lin16
s write [file rootname $file].wav
}
exit
#!/bin/sh
package require snack
snack::sound s
set f [open list.txt]
foreach file $list {
exit
# the next line restarts using wish \
exec wish8.4 "$0" "$@"
set list [read $f]
close $f
s read $file
set fd [open [file rootname $file].f0 w]
puts $fd [join [s pitch -method esps] \n]
close $fd
set fd [open [file rootname $file].frm w]
puts $fd [join [s formant] \n]
close $fd
}
#!/bin/sh
# the next line restarts using wish \
exec wish8.4 "$0" "$@"
package require snack
snack::sound s
set f [open list.txt]
set list [read $f]
close $f
foreach file $list {
s read $file
if [catch {open [file rootname $file].ltas w} out] {
error $out
} else {
foreach value [s dBPowerSpectrum -fftlength 512 -windowlength 512 \
-windowtype Hanning] {
puts $out $value
}
}
close $out
}
exit
#!/bin/sh
# the next line restarts using wish \
exec wish8.4 "$0" "$@"
package require snack
snack::sound snd
foreach speaker {sp01 sp02 sp03 sp04} {
set sumPitch 0.0
set count 0
foreach fn [lsort [glob D:/test/$speaker/$speaker.*.wav]] {
snd read $fn
foreach value [snd pitch] {
if {$value != 0} {
set sumPitch [expr $sumPitch + $value]
incr count
}
}
}
puts "$sp: [expr $sumPitch / $count]"
}
wrap.tcl app.tcl
Windows users simply run wrap.tcl and choose the name of the
script to compile, in the dialog that pops up.
Running wrap.tcl will generate an application called app
(app.exe on Windows). The resulting binary has no dependencies on
Tcl/Tk or Snack at all and does not need any installation apart from the
copying of the file itself. Most applications will fit on a diskette.
The wrap.tcl script relies on the freewrap tool which needs to be installed first. You might have to edit wrap.tcl first in order to specify the path to freewrap.
Last up dated January 23, 2006