Welcome to Beginner Programming 
Lesson #7

Course content Enter chat room Send email
to mailing list
Check calendar

There are two essential components to any computer game:

We have covered the basics of some animation in our previous lessons.

In this lesson we are going to investigate how to play sounds in Tcl/Tk.

Fortunately for us Tcl/Tk is a very extendable computer language.    We are also fortunate that Tcl/Tk has been around for a while now and many developers have created extensions to do a whole variety of things.    One of the things that have already been done is to write extensions that play sounds.

Computer sound is stored in what is called digital format.    The most common form of digital sound is the music CD that we all have used.    In the PC world computer sounds can be stored in a variety of digital formats.     One of the most common is the WAV format, so named because the sound files come with the ".wav" extension on the end of the file name.     Fortunately for us most PC's come with lots of ".wav" files already available.     To locate some on your machine simply use the file finder and search for all files that contain "*.wav".     This should produce a list of files including the one we will use in this lesson.    On my machine here it is found in:

    c:/windows/media/Ding.wav

(Macintosh users will have other wav files available).
 
 

Step 1: the first part

The sound playing software tools are not packaged with the Tcl/Tk tool set that we downloaded and installed as part of lesson #1.    Our first step is to download and install the sound extensions to Tcl/Tk.    After having done this process once in lesson #1 you will find that this exercise is very familiar to you now.    In fact the sound extension is much smaller than the full Tcl/Tk package and is easier to install.    The sound extension we want is called the Snack Sound Extension.  You can read more about it's capabilities at the website hilited in the previous sentence.    We will be making use of only a small subset of the snack sound extension's capabilities: namely its ability to play .wav files.

To get to the download page for Snack simply click here.  On this web page you should find a link like

    Binary Release for Windows 95/98/NT/2K with Tcl/Tk 8.1.1 or later 529Kb (April 19, 2000)

which when clicked will start the download of the Snack Extension Software.   (Macintosh or Linux users will use the link appropriate to their OS).

Follow all the instructions to download and install this software.   It will be basically similar to what happened in lesson #1.
 
 

Step 2: the second part

If you remember back to lesson #1 after we downloaded the tools we checked them out by typing a short program in the wish console window.     We can do the same thing here.

Bring up the wish program as you have done for many lessons now ... only this time don't bother with the editor window just yet.   At the wish console prompt simply type the following lines (hitting Enter after each)
 
    package   require   snack
    snack::sound   s
    s  read   c:/windows/media/Ding.wav
    s  play

At this point your screen should look something like that below and you should have heard a sound immediately after typing the last line and hitting Enter.

You can experiment by retyping the last two lines for each of the ".wav" files that you have found on your machine. eg on my machine I have another wav file called "Chimes.wav".   If I retype these two lines in my console window I'll hear another sound ... which come to think of it sounds a lot like chimes.
 
    s  read   c:/windows/media/Chimes.wav
    s  play

 

Step 3: the third part

Let's now add the sound capability to the program we just wrote for lesson #6.

What we basically need to do is add the 4 lines we have typed above into the program at appropriate locations.   While we do this let's attempt to understand a little about what these lines are doing.

The first thing you will want to do is to create a copy of the file called lesson6.tcl and call it lesson7.tcl .    Bring this lesson7.tcl file up inside your editor program.

The first change I want you to make is to go through and change every line where you see the words "lesson 6" to read "lesson 7".

The next change I want you to make is to add the "package require" line you typed above as the very first non comment line in your new program.  ie.
 
#========================================
# FILE: lesson7.tcl

# AUTHOR:
#
# REVISIONS:
#
#========================================

package  require  snack

Computer languages often come with extensions.   Sometimes these are called libraries.   Sometimes they are called packages.

Tcl/Tk has adopted the package name for these code extensions.

Basically what you are telling the wish program by this first line is that you will be using some commands that can be found in the package called "snack".

We will need to place the second and third lines that we typed in the console above into the main part of our program.     There is no requirement for them to go in any particular place provided that it is before the vwait statement.    As such let's put it just ahead of that statement.   ie.
 
#
# initialize the sound
#
snack::sound   s
s   read   c:/windows/media/Ding.wav

#
# pause here until we hit quit button
#
vwait  x_it

The first line is at first a little curious because it contains a "double colon" as part of the command name.

This is what is termed in Tcl/Tk as a namespace specific command.

Namespaces themselves are a subject for a more advanced course.   Suffice to say they are a good technique to use to help modularize and simplify larger Tcl/Tk programs.    The authors of the snack package chose to place all their variables and commands under the "snack" namespace.     In this case the command is "sound" and thus "snack::sound" refers directly to that particular command.

For our purposes just think of the "snack::"  as simply part of the wierd name they gave to their command inside their package.    In English this command says:

    "create a variable called s which we can use to store a sound"

The next line is simply the command to "read in" the ".wav" file to our newly created structure/variable called "s".    The file we have chosen to read in is located on my C drive inside the "windows" folder and inside the "media" folder contained there.  The file itself is called "Ding.wav".   You are free to add whatever sound file you may have on your machine.

So far all we have done is effectively initialize our sound effect.    It is now time to find appropriate locations within our code to play this sound effect.    To me it makes sense to play it whenever our fruit object strikes the ground.

What do we mean by striking the ground?   In lesson#6 our fruit didn't move.   OK let's fix that now.
 

Step 3: the fourth part

In computer animation image moving is sometimes called blitting.

What we are going to be doing is exactly what any computer game programmer does:   draw the image; wait some short amount of time; then move the image slightly and redraw it.

The first change we want to do is to give ourselves some more room to work.  In other words let's make our window bigger.   This line we want to change is:
 
#
# setup top level window size, position and title
#
wm  geometry  .  350x400+10+10
wm  title  .  "lesson7"
Don't forget that you can run your code after each one of these changes.    It is in fact a very good habit to get into as a programmer.    Make small changes and test them as you go along.    If you were to do that you would now find that your wish window has gotten bigger.    It actually grew from 200x250 pixels to 350x400 pixels according to the change in the geometry line above.

Next let's give ourselves a bigger canvas to draw on.   The line in question is:
 
#
# set up our animation canvas
#
canvas  $f_a.c  -height  210  -width  300  -background  lightBlue
pack  $f_a.c  -side  top

Notice now that our canvas area has grown from 100x150 pixels to 210x300 pixels.

But hey you say ... the buttons are all messed up.   No problem let's move those down to a new location.
 
#
# arrange the animation frame and button frames 
# in main frame
#
place  $f_a  -x  10  -y  10
place  $f_b  -x  30  -y  250
place  $f_b2  -x  70  -y  300

We are now almost ready to make our fruit "fall".     First let's position the fruit in an appropriate place to fall from.
 
#
# display the new image and text
#
$f_a.c  create  image  10  10  -image  $myimage  -tag  imagetag
$f_a.c  create  text  80  50  -text  $myimage  -tag  texttag

How are we going to start this "falling" process off?

That's right we need another button.

Let's call that button "Drop" and place it beside the "Quit" button that we already have.
 
button  $f_b2.quit  -text  "quit"  -command  { set  x_it  1 }
button  $f_b2.go  -text  "drop"  -command  { dropImage }
pack  $f_b2.go  $f_b2.quit  -side  left

For those of you who are following the advice above and trying the code at each change will notice that the program now is complaining about the lack of a procedure called "dropImage" which we have connected to our "Drop" button above.
 
This sequence of adjustments to the code is very deliberate.   It is always better to change the "infrastructure" parts to get your new feature enabled before you actually write the code for your new feature.    That way you are immediately in a position to test anything you add to the new feature itself as you build it.    This is what is known as "incremental code design" or "design-code-test".   There are other formal ways to develop software but this has the best proven track record in real world projects.

Here's what we want to do to move our graphics.
 
#========================================
# dropImage - entry point
#========================================
proc  dropImage { }   {
global  f_a
global  s

set  nextdrop  1
for  { set  i  0 }  { $i  <  2000 }  { incr  i  200 }   {
   after  $i  "$f_a.c move imagetag 12 $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ] 
   }

set  nextdrop  -18
for  { }  { $i  <  4000 }  { incr  i  200 }   {
   after  $i  "$f_a.c move imagetag 6 $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ]
   }

set  nextdrop  -8
for { }  { $i  <  5000 }  { incr  i  200 }   {
   after $i "$f_a.c move imagetag 3 $nextdrop"
   set nextdrop [expr $nextdrop + 4]
   }

puts  stdout  "dropImage:done"
}   ;#end dropImage

#========================================
# showImage - entry point
#========================================

The actual math and physics behind the dropping of objects is somewhat complex.

When you come to study these things later in high school and you ask "Why would I want to know this #$@!?"

Well here's a less than obvious answer ... "because I want to be a video game designer and I will need to make my falls realistic".

Each leg of the drop above has a construct like this below.
 
set  nextdrop  1
for  { set  i  0 }  { $i  <  2000 }  { incr  i  200 }   {
   after  $i  "$f_a.c move imagetag 12 $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ] 
   }

By now you should be comfortable with all of the Tcl/Tk language used in this construct so let's try to reproduce it's logic in plain English.

"Starting with the nextdrop variable set to 1, loop 10 times incrementing the time by 200ms each loop.   At each loop iteration setup to move the image to the right by 12 pixels and down by an accelerating amount each time ... to simulate the pull of gravity.  Each vertical drop amount will increase by 4 pixels each time through the loop starting at the initial value of 1 ...ie. 1, 5, 9, 13 etc."

Notice how we conveniently handle the bounce up by simply starting our loop with a negative vertical drop amount.   Remember from the previous lesson where coordinates were discussed.   Vertical pixel direction is a positive number in the downward direction.   ie. pixel row 10 is higher up on the screen than pixel row 100 is.

I warned you that all that math you are learning actually has some real uses.
 
 

The fun part

Now let's finally add the sound effect to our code !!!

While we describe the line(s) that we will be adding to the code, why don't you think about where these lines might go in the code before I give you the answer below.

As with our image moving (or blitting as it is sometimes called) we would probably want to schedule our sound effect at specific times in our little cartoon sequence.   If you understood the code above you would see that the object strikes the ground after 2000ms on the first fall, after 4000ms on the second bounce and after 5000ms on the third bounce.    These would be good times in which to insert our sound effects.     When we were playing the sound on the console above the line we were typing was:

    s  play

This is precisely the command we would want to execute at these 3 times.

How do we do this?

Why we use exactly the same "after" command that we used inside the loop but with the "$i" and the command to execute parts changed: ie. at the 2sec mark (2000ms = 2sec) we would need a command like:

    after  2000  "s play"

We would need to play our sound effect after the object had completed the first fall. ie.
 
for  { set  i  0 }  { $i  <  2000 }  { incr  i  200 }   {
   after  $i  "$f_a.c  move  imagetag  12  $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ] 
   }

after  2000  "s  play"

I will leave it up to you to add the other two lines that we require to complete our sound effects at the appropriate place in the code.   Those lines would be:

    after 4000 "s play"
and
    after 5000 "s play"
 

Final source listing

#========================================
# FILE: lesson7.tcl
#
# AUTHOR:
#
# REVISIONS:
#
#========================================

package  require  snack

#========================================
# dropImage - entry point
#========================================
proc  dropImage { }   {
global  f_a
global  s

set  nextdrop  1
for  { set  i  0 }  { $i  <  2000 }  { incr  i  200 }   {
   after  $i  "$f_a.c move imagetag 12 $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ] 
   }
after  2000  "s  play"

set  nextdrop  -18
for  { }  { $i  <  4000 }  { incr  i  200 }   {
   after  $i  "$f_a.c move imagetag 6 $nextdrop"
   set  nextdrop  [ expr  $nextdrop  +  4 ]
   }
after  4000  "s  play"

set  nextdrop  -8
for { }  { $i  <  5000 }  { incr  i  200 }   {
   after $i "$f_a.c move imagetag 3 $nextdrop"
   set nextdrop [expr $nextdrop + 4]
   }
after  5000  "s  play"

puts  stdout  "dropImage:done"
}   ;#end dropImage

#========================================
# showImage - entry point
#========================================
proc  showImage  { myimage }   {
global  f_a

#
# remove the previous image and text
#
$f_a.c  delete  imagetag
$f_a.c  delete  texttag

#
# display the new image and text
#
$f_a.c  create  image  10  10  -image  $myimage  -tag  imagetag
$f_a.c  create  text  80  50  -text  $myimage  -tag  texttag

}   ;# end showImage

#========================================
#  main - entry point
#========================================
destroy   .myArea

#
# setup top level window size, position and title
#
wm  geometry  . 350x400+10+10
wm  title   .   "lesson7"

#
# setup all the frames
#
set  f  [ frame  .myArea  -borderwidth  5  -background  blue ]
set  f_a [ frame  $f.animateFrame  -background  lightBlue ]
set  f_b [ frame  $f.buttons  -background  blue ]
set  f_b2 [ frame  $f.buttons2 ]

puts  stdout  "lesson6 starting"

#
# initialize some variables
#
set  x_it  0

#
# initialize some images
#
image  create  photo  apple  -file  apple.gif
image  create  photo  grapes  -file  grapes.gif
image  create  photo  chilli  -file  chilli.gif

#
# set up our animation canvas
#
canvas  $f_a.c  -height  210  -width  300  -background  lightBlue
pack  $f_a.c  -side  top

#
# set up our buttons
#
button  $f_b.apple  -image  apple \
  -command  { showImage  "apple" }

button  $f_b.grapes  -image  grapes \
  -command  { showImage  "grapes" }

button  $f_b.chilli  -image  chilli \
  -command  { showImage  "chilli" }

pack  $f_b.apple  $f_b.grapes  $f_b.chilli  -side  left

button  $f_b2.quit  -text  "quit"  -command  { set  x_it  1 }
button  $f_b2.go  -text  "drop"  -command  { dropImage }
pack  $f_b2.go  $f_b2.quit  -side  left

#
# arrange the animation frame and button frames 
# in main frame
#
place  $f_a  -x  10  -y  10
place  $f_b  -x  30  -y  250
place  $f_b2  -x  70  -y  300
#
# make the whole thing visible
#
pack  $f  -side  top  -expand  true  -fill  both

#
# initialize the sound
#
snack::sound  s
s  read  c:/windows/media/Ding.wav

#
# pause here until we hit quit button
#
vwait  x_it

#
# disable the buttons
#
$f_b.apple   config   -state   disabled
$f_b.grapes   config   -state   disabled
$f_b.chilli   config   -state   disabled
$f_b2.quit   config   -state   disabled

#
# clear the canvas
#
$f_a.c   delete   imagetag
$f_a.c   delete   texttag

puts  stdout  "lesson7 done"

When you run this program and hit the drop button you should see an image similar to that below:


 

Summary


End of Lesson 7.