!lm11
!rm75
Making Noise and Other Sounds

The Apple's built-in speaker is one of its most delightful features.  To be sure, it is very limited; but I have used it for everything from sound effects in games to music in six parts (weird-sounding guitar chords) and even speech.  Too many ways to put all in one AAL article!  I will describe some of the sound effects I have used, and maybe you can go on from there.

The speaker hardware is very simple.  A flip-flop controls the current through the speaker coil.  Everytime you address $C030, the flip-flop changes state.  This in turn reverses the current through the speaker coil.  If the speaker cone was pulled in, it pops out; if it was out, it pulls in.  If we "toggle" the state at just the right rate, we can make a square-wave sound.  By changing the time between reversals dynamically, we can make very complex sounds.  We have no control over the amplitude of the speaker motions, only the frequency.

Simple Tone:  This program generates a tone burst of 128 cycles (or 256 half-cycles, or 256 pulses), with each half-cycle being 1288 Apple clocks.  Just to make it easy, let's call Apple's clock 1MHz.  It is really a little faster, but that will be close enough.  So the tone will be about 388 Hertz (cycles per second, if you are as old as me!).

How did I figure out those numbers?  To get the time for a half-cycle (which I am going to start calling a pulse), I added up the Apple 6502 cycles for each instruction in the loop.  LDA SPEAKER takes 4 cycles.  DEX is 2 cycles, and BNE is 3 cycles when it branches.  The DEX-BNE pair will be executed 256 times for each pulse, but the last time BNE does not branch; BNE only takes 2 cycles when it does not branch.  The DEY-BNE pair will branch during each pulse, so we use 5 cycles there.  So the total is 4+256*5-1+5=1288 cycles.  I got the frequency by the formula f=1/T; T is the time for a whole cycle, or 2576 microseconds.

Apple "Bell" Subroutine:  Inside your monitor ROM there is a subroutine at $FBE2 which uses the speaker to make a bell-like sound.  Here is a copy of that code.  Notice that the pulse width is controlled by calling another monitor subroutine, WAIT.

Machine-Gun Noise:  What if we use a random pulse width?  Then we get something called noise, instead of a tone.  We can create a burst of pulses of random-sounding width by using values from some arbitrary place in the Apple's memory as loop counts.  The program uses the 256 values starting at $BA00 (which is inside DOS).  If you make just one burst like that, it doesn't sound like much.  But if you make ten in a row, you get a pattern of repetitious random noise bursts that in this case sounds like machine-gun fire.  Doesn't it?  Well, close enough....
!np
Laser "SWOOP" Sound:  We can change the pulse width by making it go from wide to narrow in steps of 5 microseconds.  It sounds like a low tone that gradually slides higher and higher until it is beyond the range of the human ear (or the Apple speaker).  I used this program in a "space war" game to go with the laser fire.  Even though the sound was entirely generated before the laser even appeared on the screen, it looks and sounds like the light beam and sound are simultaneous.

I have indicated in line 1110 that you should try experimenting with some other values for the maximum pulse width count.  I have included a separate entry point at SWOOP2 to make ten swoops in a row.  Try the various values for the maximum width and run each one from SWOOP2.  You might also experiment with running the pulse width in the opposite direction (from narrow to wide) by changing line 1200 to INC PULSE.WIDTH.

Another Laser Blast:  This one sounds very much the same as the swoop of the previous program, but it uses less memory.  You should try experimenting with the pulse widths of the first and last pulses in lines 1060 and 1130.  You could also try changing the direction by substituting a DEX in line 1120.

Inch-Worm Sounds:  I stumbled onto this one by accident, while looking for some sound effects for a lo-res graphics demo.  The demo shows what is supposed to be an inch-worm, inching itself across the screen.  By plugging various values (as indicated in lines 1100 and 1130), I got some sounds that synchronized beautifully with the animation.  Complete with an exhausted sigh at the end!

Touch-Tones Simulator:  I used this one with a telephone demo program.  The screen shows a touch tone pad.  As you press digits on the keyboard, the corresponding button on the screen lights up (displays in inverse mode).  Then the demo program CALLs this machine language code to produce the twin-tone sound that your telephone makes.  It isn't perfect, you can't fool the Bell System.  But it makes a good demo!

I will describe the program from the top down.  The four variables in page zero are kept in a "safe" area, inside Applesoft's floating point accumulator.  Applesoft doesn't use these locations while executing a CALLed machine language routine.

The Applesoft demo program stores the button number (0-9) in location $E7.  This could be done with "POKE 231,DGT", but I had more fun using "SCALE=DGT".  SCALE= is a hi-res graphics command, but all it really does is store the value as a one-byte integer in $E7.  Since we aren't using hi-res graphics, the location is perfectly safe to use.
!np
CALL 768 gets us to line 1150, TWO.TONES.  This is the main routine.  It uses the button number to select the two tone numbers from LOW.TONES and HIGH.TONES.  ONE.TONE is called to play first the low tone, then the high tone, back and forth, for ten times each.  This is my attempt to fool the ear, to make it sound like both are being played at once.

ONE.TONE wiggles the speaker for LENGTH half-cycles.  Each half-cycle is controlled by either the UPTIME or DOWNTIME counts.  These three parameters are selected from three tables, according to the tone number selected by TWO.TONES.  Lines 1270-1340 pick up the values from the three tables and load the page zero variables.  Lines 1360-1500 do the actual speaker motions and time everything.  The purpose of having two routines, one for uptime and one for downtime, is to be able to more closely approximate the frequency.  For example, if the loop count we ought to use is 104.5, we could use an uptime of 104 and a down time of 105; this makes the total time for the full cycle correct.  The redundant BEQ in line 1420 is there to make the loop times for UPTIME and DOWNTIME exactly the same.

Since you do not have my Applesoft program, which drives this, I wrote a simulated drive to just "push" the buttons 0-9.  Lines 1650-1790 do this.  I separated each button push by a call to the monitor WAIT subroutine, to make them easier to distinguish.

Morse Code Output:  I have always thought that computers really only need one output line and one input line for communicating with humans.  I could talk to my Apple with a code key, and it could beep back at me.  One of the first programs I attempted in 6502 language was a routine to echo characters in Morse code.  I looked it up about two hours ago, and shuddered at my sloppy, inefficient, hard to follow code.  So, I wrote a new one.

I broke the problem down into three littler ones:  1) getting the characters which are to be output; 2) converting the ASCII codes to the right number of dots and dashes; and 3) making tones and spaces of the right length.

SETUP.MORSE (lines 1190-1240) links my output routine through the monitor output vector.  Line 1240 JMPs to $3EA to re-hook DOS after me.

MORSE (lines 1260-1310) are an output filter.  If the character code is less than $B0, I don't know how to send it in Morse code; therefore, I just go to $FDF0 to finish the output on the screen.  Codes exist for these other characters, but I did not look them up.  If you want a complete routine, you should modify line 1260 to CMP #$A0 and add the extra codes to the code table (lines 1130-1170).

SEND.CHAR looks up the Morse code for the character in the code table, and splits it into the number of code elements (low-order three bits) and the code elements themselves (high-order five bits).  If a code element is zero, a short beep (dot) is sounded.  If an element is one, three calls to the short beep routine make one long beep (dash).  Between elements, a silence equal to the length of a short beep intervenes.  After the last beep of a character, a longer silence, equal to three short silences, is produced.  A 00 code from the code table makes a silent gap of three times the inter-character gap.

EL.SPACE and EL.DIT are nearly identical.  The only difference is that EL.DIT makes a sound by addressing the speaker, while EL.SPACE does not.  The value of EL.PITCH determines the pulse width, and EL.SPEED determines the number of pulses for an inter-element-space or a short beep.  If the code stream is too fast for you, you can slow it down by increasing either or both of these two numbers.
