Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : TN9002.ZIP
Filename : PLAY_IT_.TXT

 
Output of file : PLAY_IT_.TXT contained in archive : TN9002.ZIP

Play It Again, Sam

Mike Carlisle

You've added animated screen displays and humorous messages to your
programs, so now what? How about music?

In dBASE IV, the SET BELL command allows you to change the pitch and
duration of the bell tone. But you can do more than just change the
bell to a higher pitch or a longer duration. You can make several
changes in rapid succession; resulting in a rendering of your favorite
song. The only question is how to do it fast enough. Hard coding it
is one answer, but might be impractical. The tact we'll take here is
to write a UDF, called PlayIt, that reads a pre-loaded array.

Creating the Array

PlayIt() reads the array values from an array named Song, sets the
pitch and duration of the bell based on the values in the array and
then sounds the bell with the command ?? CHR(7). With the proper
pitches and the right durations, you're making music!

All you need to do is declare an array named Note dimensioned as [n,2]
where n is the number of notes in the tune + 1. So, to create a tune
with five notes you would give the command

DECLARE song[6,2]

There's a reason for that last row which we'll discuss a little later.
Having declared the array, the next step would be to store the pitch
and duration values for each note of the tune to the array. The pitch
will be stored in the first column of each row, the duration in the
second column. Here's an example:

STORE 250 TO song[1,1] && Pitch.
STORE 5 TO song[1,2] && Duration.
STORE 275 TO song[2,1] && Pitch.
STORE 3 TO song[2,2] && Duration.
etc...

Once the note values have been stored to the array, save it to a .MEM
file (SAVE TO ) with a name that describes the song. That
way all you need to do is restore the .MEM file when you want to play
that tune again (RESTORE FROM ADDITIVE).

Facing the Music

The process of playing back these notes is a simple one.
Read the pitch and duration values from a row of the array
Set the bell accordingly
Beep
Move to the next array row
Repeat until you come to the end of the array.

Finding the End of the Array

Here's where you use the extra row. You'll store numeric values to
every element in the array except for those in the last row. To
determine if it's reached the end of the song, PlayIt() will check the
value type as it goes along; once it reaches a non-numeric type it
will stop. Because you're checking for a data type, the array could
have many more rows than it has notes, but not less; at the first
non-numeric value, PlayIt() will stop.

FUNCTION PlayIt
offset = 1
DO WHILE TYPE("song[offset,1]") = "N"
SET BELL TO song[offset, 1], song[offset,2]
?? CHR(7)
offset = offset +1
ENDDO
SET BELL TO
RETURN .T.

To play a tune you need only to call PlayIt() as you would any other
function.

Bells and Whistles

If what you're playing is going to be lengthy, you may want to send a
message along with the music. Either display your message before
calling PlayIt(), or expand the function so that it can send the
message itself.

PlayIt2() takes three parameters: message (a character string), row
and column (position on the screen at which to display the message).

FUNCTION PlayIt2()
* Michael B. Carlisle. TechNotes/dBASE IV, Jan. 1990
* Displays a message and plays a song.
PARAMETERS output, mrow, mcol
@ mrow,mcol SAY output
SET TALK OFF
offset = 1
DO WHILE TYPE("song[offset,1]") = "N"
SET BELL TO song[offset,1],song[offset,2]
?? CHR(7) AT mcol
offset = offset + 1
ENDDO
SET TALK ON
SET BELL TO
RETURN .T.

You'll note that the ?? command has changed a little here. We've
included the AT clause because, without it, the ?? command will cause
the cursor to move (regardless of the use of the @...SAY command).
That might not be a problem if you like the "follow-the-bouncing-ball"
effect and you don't mind if your screen display gets moved up every
once in a while. But since we have a row and column coordinate, we
might as well keep the cursor in that one location.

One-liners

You can use PlayIt2 as you would any other function, but here's a few
one-line commands that may not have occurred to you.

ON ERROR ? PlayIt2(MESSAGE(), 10, 10)

and

SET PROMPT TO PlayIt2("Play it again...", 24, 30)

You're probably wondering where you can find the pitch values. The
most convenient source for most people will be their BASIC manual Ä
look under the SOUND statement. Here's the values for the pitches
from C below middle C to C above middle C.

Note Value
C 261.63
D 293.66
E 329.63
F 349.23
G 392.00
A 440.00
B 493.88
C 523.25
D 587.33
E 659.26
F 698.46
G 783.99
A 880.00
B 987.77
C 1046.50

For those of you who are musically educated, note that these values
are untempered. There is an algorithm (albeit fairly weird) for
deriving tempered pitch values. You can find that information in,
among other places, the Harvard Dictionary of Music under "Acoustics".
- The Editors

Ana One, Ana Two.

To get you started, try these little ditties on for size.
When things go wrong:

STORE 220 TO song[1, 1]
STORE 10 TO song[1, 2]
STORE 220 TO song[2, 1]
STORE 10 TO song[2, 2]
STORE 220 TO song[3, 1]
STORE 2 TO song[3, 2]
STORE 220 TO song[4, 1]
STORE 10 TO song[4, 2]
STORE 261.63 TO song[5, 1]
STORE 7 TO song[5, 2]
STORE 246.94 TO song[6, 1]
STORE 2 TO song[6, 2]
STORE 246.94 TO song[7, 1]
STORE 5 TO song[7, 2]
STORE 220 TO song[8, 1]
STORE 5 TO song[8, 2]
STORE 220 TO song[9, 1]
STORE 5 TO song[9, 2]
STORE 205 TO song[10, 1]
STORE 5 TO song[10, 2]
STORE 220 TO song[11, 1]
STORE 15 TO song[11, 2]

When sending something to the printer:

STORE 523.25 TO song[1, 1]
STORE 2 TO song[1, 2]
STORE 587.33 TO song[2, 1]
STORE 2 TO song[2, 2]
STORE 659.29 TO song[3, 1]
STORE 2 TO song[3, 2]
STORE 783.99 TO song[4, 1]
STORE 7 TO song[4, 2]
STORE 659.29 TO song[5, 1]
STORE 2 TO song[5, 2]
STORE 783.99 TO song[6, 1]
STORE 10 TO song[6, 2]

You may be tempted to do two things after reading this article:

shorten the assignment syntax to variable = value instead of STORE
value TO variable, and possibly name the array "note".

Renaming the array to "note" makes sense as each row of the array is a
note. Shortening the syntax makes it easier for you to store values
to the array.

You can do one or the other, but don't do both. Unfortunately, the
combination has a problem. If we store a note to the array like this

note[1,1] = 250

it won't take. "Note" is a reserved word here; it's the same as
starting the line with a asterisk, meaning that the command will be
ignored. Thus, with an array named "note", the only acceptable syntax
is to use the STORE command for variable assignment.

Perhaps a prefered alternative would be to create a database structure
with two fields and use BROWSE to record the pitch and duration
values. Using COPY TO ARRAY, the array would be filled and the need
for redundant STORE commands and the conflict here "noted" would be
avoided.