BASIC Sound On The Atari ST
Almost any music or sound effect can be created with the WAVE and SOUND commands in Atari SI BASIC. This article shows how to get started with ST sound and includes sample sound effects and a simulated piano program. The article is an excerpt from the newly released COMPUTEI's ST Programmers Guide (by the editors of COMPUTE!, $16.95).
The Atari 520ST contains a General Instruments sound chip that has three voices (sound channels) and a range of eight octaves. In fact, it's the same sound chip found in the MSX-standard computers sold in Japan and Europe. The chip's best feature is that it supports envelope-oriented sound—you can create a sound by defining the shape of its envelope. This allows considerable flexibility when designing sound effects and musical instrument tones. However, for programmers, it also requires more work than the SOUND command found in Atari BASIC for the eight-bit computers.
There are two sound commands in ST BASIC: WAVE and SOUND. WAVE controls the makeup of the Sound:Figure 1: Bit Values
WAVE sound type, envelope, shape, period, delay
Some of these parameters require values that toggle certain bits to activate certain functions. If you're not familiar with bit manipulation, refer to Figure 1. The first step is to decide which function(s) you want to select. Then add up the bit values—not the bit numbers—corresponding to those functions. The resulting number is what you use for that particular parameter in the WAVE statement.
For instance, the first parameter, sound type, controls whether a voice is set to noise, tone, or both. Bits 0-2, when set, turn on tone output for voices 1-3. Bits 3-5, when set, select noise output for the three voices. Both tone and noise may be turned on at the same time. Here are some example bit values:
WAVE 1—turns on tone for voice 1 WAVE 3—turns on tone for voice 1 and voice 2 WAVE 8—turns on noise for voice 1 WAVE 15—turns on tone and noise for all three voices
Bits 0-2 of envelope determine which of the three voices is controlled by the envelope generator. If a bit is set, its corresponding voice is controlled by the envelope generator.Figure 2: Available Envelopes
The third parameter, shape, controls the way the sound's volume rises and falls. Figure 2 gives the possible values for this parameter and shows the shape of the subsequent sound.
Each of the envelope-shape drawings is a graph of volume over time. Take a close look at envelope zero and imagine what kind of sound it would make. The first thing to notice is that as soon as the sound begins, volume is at maximum. As time passes, the volume slowly fades away until it reaches zero. This type of sound is made by a piano. The hammer hits the string and almost immediately the volume reaches its maximum. The vibration of the string continues and slowly decays.
As you can see from Figure 2 envelopes 8, 10, 12, and 14 art repetitive. The sound continues to surge and fall long after the WAVE command is given.
The period parameter sets the period of the envelope, which is how fast the sound cycles. The larger the period, the longer the note takes to repeat. The last parameter, delay, controls the amount of time the program waits before executing the next BASIC command. Period is measured in one-fiftieth of a second increments. To hear a couple of interesting sound effects produced by WAVE, type in Program 1, "Helicopter," and Program 2, "Ding."
SOUNDing Off And On
The other music command, SOUND, turns on one of the voices for a specified duration. Its syntax is:
SOUND voice, volume, note, octave, duration
Voice selects which voice you want to turn on (1-3). Volume can be any number from 0 (off) to 15 (loudest). Note is a number from 1 to 12 and corresponds to the 12 notes in a scale (C, C#, D, D#, E, F, F#, G, G#, A, A#, B). The octave ranges from 1 (lowest) to 8 (highest). Duration can be any number from 0-65535. Each increment corresponds to one-fiftieth of a second.
Program 3, "Piano," uses the SOUND and WAVE commands to simulate a piano. Although it's intended as a sound demonstration, it also shows how to use other techniques, such as graphics and reading the mouse from BASIC.
You can run Program 3 in any graphics mode. When the piano keyboard appears on the screen, point to any key with the mouse and press the mouse button. The corresponding note is played.
Before typing in and running Program 3, you must make sure there's enough free memory available in BASIC. At this writing (mid-December), all 520STs were being shipped with the operating system (TOS) on disk. Later versions of the 520ST may be shipped with TOS in Read Only Memory (ROM). Until then, however, TOS must be loaded from disk into Random Access Memory (RAM). Because of the large amount of memory this requires, only a small area of storage remains for BASIC programs. When TOS and BASIC are loaded into a 520ST with 512K RAM, only about 5K is free for BASIC— enough for a program about 20 lines long. To check how much memory is available, load BASIC and type PRINT FRE(0).
Fortunately, there is a way to increase the amount of free memory by 32K. Normally, when windows are manipulated, the previous screen is saved in memory because part of it may be covered by a window and have to be restored later. The technique of saving the screen to memory is called buffered graphics. Although it can be quick and convenient, it requires 32K of memory to hold the screen.
If the buffered graphics option is turned off, 32K of memory is freed for BASIC. Click on Buf Graphics in the Run menu to toggle the buffered graphics on and off. This should increase free memory to 37986 bytes for BASIC programs.
Building The Piano
Let's trace through Program 3 to see how it works. Line 10 dimensions two arrays, B% and W%. These hold the note values of the black and white keys, respectively.
Next, the subroutine DRAW-SCREEN is called. ST BASIC allows the use of labels instead of line numbers for many of its commands that need to make a reference to a line, like GOTO and GOSUB. Whenever you use a label in a line, make sure it is separated from the rest of the line with a colon.
The DRAWSCREEN subroutine (beginning at line 150) draws the piano keyboard. The first command of DRAWSCREEN sets the color of all screen output to black. Using only a single color for drawing ensures that the program will work in all graphics modes. The COLOR command also sets the fill pattern to solid. FULLW expands the window to full size, and CLEARW clears it.
The remaining commands of the DRAWSCREEN subroutine create the piano keyboard. Since there is no box drawing command in ST BASIC, we will simulate one using the LINEF command and FILL commands. LINEF draws a line between any two pairs of coordinates. The syntax is:
LINEF xcoordl, ycoordl, xcoordl, ycoord2
Next, line 20 calls the subroutine SETARRAY, which reads the note values of the black and white keys into the integer arrays B% and W%
Reading The Mouse From BASIC
Now that the screen is set up and the arrays have been initialized, it's time to read the position of the mouse and check if the mouse button is pressed. This is done in the subroutine labeled READMOUSE at line 90. There is no BASIC command to read the mouse, so we must use one of the computer's Virtual Device Interface (VDI) routines. VDI routines are part of the computer's operating system.
The procedures necessary to call VDI routines are beyond the scope of this article, but basically involve POKEing various parameters into certain memory locations. These memory locations are not absolute addresses—instead, they're accessed via a reserved variable in ST BASIC named CONTRL. The ST automatically ass 5ns an address to this variable which corresponds to the entry point into the VDI. By POKEing values into offsets from this address, various VDI routines can be executed.
The VDI routine for reading the position of the mouse and determining whether the mouse button is pressed has an opcode of 124, so we POKE CONTRL,124. We must tell the VDI routine that no other parameters are being passed, so two more POKEs are necessary: POKE CONTRL+2,0 and POKE CONTRL+6,0. Now we can call the VDI routine to read the mouse.
To read the horizontal and vertical position of the mouse, PEEK PTSOUT and PTSOUT+2, respectively. If the mouse button is pressed, PEEKing INTOUT will give a value of 1; otherwise, a zero is returned. (PTSOUT and INTOUT, like CONTRL, are also reserved variables for accessing VDI routines.)
The main loop of the piano program (line 30) simply waits until a mouse button is pressed. Once the button has been pressed, the vertical coordinates are checked to see if they are in the range of the piano keyboard (line 50). Then the vertical position is used to determine whether the key pressed is black or white (lines 50 and 60). If a black key is pressed, the note is calculated using the array B%; otherwise, the array W% is used.
Line 70 breaks the note value down into note and octave and then, using the SOUND command, plays the note.
Line 80 sets the envelope shape to zero. This creates a note with a similar shape to a piano's envelope. Program execution is then sent back to the main loop to check the mouse button again and SOUND another note when it is pressed.
Program 1: Helicopter
10 for a = 1000 to 643 step -2 20 wave 8,3,14,a 30 for td=l to 100:next:next 40 for a=643 to 1000 step 2 50 wave 8,3,14,a 60 for td=l to 100:next:next 70 sound l,0:sound 2,0
Program 2: Ding
10 for a= 1 to 12 15 sound l,15,a,7 20 wave 1,1,14,5,1 30 for td = l to 100:next:next 40 goto 10
Program 3: Piano
10 dim b°/o(16),w%(16) 20 gosub DRAWSCREEN:gosub SETARRAY 30 gosub READMOUSE:if button=0 then 30 40 if y<70 or y>120 then 30 50 if y<100 then n=b%((x–16)/16.25) 60 if y>99 then n=w%((x–4)/16.25) 70 sound l,15+15*(n=0),n–12*int(n–l)/12,3+int((n–1/12) 80 wave l,l,0,10000:goto 30 90 READMOUSE: poke contrl,124 100 poke contrl+2,0:poke contrl+6,0 110 vdisys(O) 120 x=peek(ptsout):y=peek(ptsout+2) 130 button=peek(intout) 140 return 150 DRAWSCREEN: color l,l,l,l,l:fullw 2:clearw 2 160 for a=50 to 100 step 50 170 linef 20,a,280,a:next 180 for a=20 to 280 step 16.25 190 linef a,50,a,100:next 200 for a=l to ll:read s 210 gosub 250:next:return 220 data 32.5,48.75,81.25,97.5 230 data 113.75,146.25,162.5 240 data 195,211.25,227.5,260 250 linef s,50,s,78 260 linef s,78,s+8,78 270 linef s +8,78,8+8,50 280 fill s+l,51:fill s + 5,51 290 return 300 SETARRAY: for a=l to 16:read w%(a):next 310 for a=l to 16:read b%(a):next 320 return 330 data 1,3,5,6,8,10,12,13 340 data 15,17,18,20,22,24 350 data 25,27 360 data 2,4,0,7,9,11,0,14,16 370 data 0,19,21,23,0,26,0