Basic Sound Extension Richard Taylor improves the Spectrum's sound This program adds a further four commands designed to enhance its musical and sound capabilities to Spectrum Basic. They start where Beep left off, providing a Play instruction which allows tunes to be played by just speci- fying the notes rather than having to labouriously convert them into the numerical form required by Beep. Secondly, the program facilitates the use of tone envelopes whereby the pitch of a sound varies with time to produce the laser sounds and zaps that are all too popular in commercial games. Random "noise" can also be produced and, when used in conjunction with envelopes, can be made to create some convincing effects. The program occupies about the top 2.5K of memory just below the UDG graphics area. [...] To load it, use: CLEAR 62682: LOAD ""CODE The new commands are used in a similar fashion to those which are standard, except that the commands have to be typed out in full - in either upper or lower case - there's no lazy single key entry system. However, before any of the commands will be accepted by the Spectrum the machine code must be initialised with: RANDOMIZE USR 62683 This instruction completely resets the program and intro- duces the new commands to the Spectrum so that when Basic comes across them they'll be accepted without any quibbles. Because of the somewhat obscure manner in which it opera- tes, any errors reported by Interface 1 disconnect the sound commands so that they are no longer part of Basic's vocabulary. To reinitialise use: RANDOMIZE USR 62686 The most mundane of the new instructions is *Sound. In its simplest form it can be used as a substitute for Beep. In an analogous manner to Beep, the command needs two arguments, the first being the length of the sound (in units of 1/100 of a second - so 300 means three seconds) and the next argument is the pitch of the sound. The pitch is described in a completely different way to that expected by Beep - the smaller the number the higher the pitch! The best way to produce a certain tone is by experimentation - there's no easy method of getting a pitch number for a specific frequency. If the pitch of a sound is made nega- tive then noise is produced with a pitch of very approxi- mately what it would have been if it wasn't negative, e.g. *SOUND 200,-300 produces noise for two seconds. In fact, the sound may not last exactly two seconds. When generating noise, the dura- tion you specify is only followed approximately - the sound may last much longer. If this is a problem then you'll have to compensate for it when defining sound lengths. The *Env command is used to define the envelopes I men- tioned. The definition of an envelope describes how the pitch of a sound varies from its initial value as time progresses. These tone changes give rise to the laser/zap effects which were previously unobtainable from Basic with- out enlisting the help of machine-code routines. A helpful aid in producing envelopes is to illustrate its effect graphically with the aid of a graph. For instance, the graphical form of a steadily increasing note would be: ^ ____/ H| ____/ C| ____/ T| ____/ I| ____/ P| ____/ | ____/ | ____/ |____/ | +---------------------------------------------> TIME Notice how the graph shows the pitch undergoing a con- tinuous, smooth change from the initial to the final pitch value. [Well, it did in the original magazine print. ASCII art isn't so smooth, of course, so you'll have to imagine it.] In reality the computer cannot manage this but has to break the change down into a number of small, discrete pitch variations as illustrated below: ^ ________ H| | C| ________| T| | I| ________| P| | | ________| | | |________| | +---------------------------------------------> TIME You decide how long each individual step will last and by how much the pitch then "jumps" at the culmination of each of the steps. Say you wanted to create an envelope similar to the one above. We have to specify three quantities, namely the total number of steps, the size of pitch variation after each of them and the length of each step. From these values the following can be calculated: Total pitch change = number of steps x size of step Total length of env = number of steps x length of step Consider that the envelope is to last a total of one second and produce an increase in pitch of 100 units. Because of the weird way in which the pitch is measured, an increase is represented by a decreasing pitch number - *Env uses the same method of pitch representation as *Sound. If we wanted to have a total of five steps in the effect, then each would have a length of 1/5th second and a step of -20. To define the envelope, *ENV 0,5,-20,20 is used. The first expression after the command is the number of the envelope, in this case 0. Up to 16 completely independent envelopes can be stored in the Spectrum's memory at one time, each one being specified by a number between 0 and 15. The second expression is the number of steps, the third the pitch variation per step and, finally, the 20 is the step length in 1/100ths of a second. Since each step lasts a comparatively lengthy 1/5th second, it's quite easy to hear the individual steps composing the envelope. To hear the envelope, type: *SOUND -1,250,0 Notice how a third argument has been tagged onto the end of *Sound to specify the envelope to be used. The length of the sound is given as -1. This means that the envelope should be played only once. Similarly, if it was -2 then the envelope would be repeated twice. Try *SOUND -5,250,0 The pitch value given in *Sound is the initial pitch which is decreased down to 150 by the envelope. If an envelope is being repeated then the pitch commences from its initial value at the start of every recital of the envelope. There is nothing to stop noise being enveloped in a similar fashion; try: *SOUND -5,-250,0 Note that when using enveloped noise the pitch is varied as though the negative sign in front of it didn't exist (although noise is produced), otherwise decreasing -250 by 100 would produce -350 instead of -150, which is what we're after. Enveloped noise is the basis of many of the sound effects to be found in games, especially those of the "space-war" genre. In order to produce a much smoother envelope, the step length has to be drastically reduced. Redefine envelope 0 with: *ENV 0,100,-2,1 If you try *SOUND -8,250,0 a greatly improved effect will be heard. So far the envelopes that have been utilised are rela- tively simple, in that they consist of only a single type of pitch change. In reality we may wish to use more complex envelopes, such as: ^ H| ______ C| ______/ C \_ T| __/ \_ I| __/ B D\_ P| __/ \__ | / \__ | /A E \__ |/ \__ | +---------------------------------------------> TIME The envelope can be subdivided into five discernable different sections, A-E. We could define a separate enve- lope for each of A-E and play them together using a string of *Sound commands. This method, as well as being unelegant and cumbersome, is further complicated because we need to calculate the value envelope A, say, leaves the pitch at when it terminates so that we know what to use as the starting pitch for envelope B. Thankfully the *Env command allows an envelope to be composed of up to eight individual sections. We could define the above envelope as follows: *ENV 1,20,-4,2,20,-1,2,10,-1,4,20,4,2,20,2,2 As you can hear, such an envelope provides a much more complex sound. The Beep instruction was designed for playing music rather than making explosion sounds etc. However, life still isn't easy if you're converting music, since all the note values have to be converted into the relevant numbers required by the somewhat awkward Beep. The *Play command enables music to be converted without the need for any number crunching. It has a string argument which is used to contain the notes you wish to play and other associated information. Any letter from A to G found in the string is played as that particular musical note - so: *PLAY "abcdefg" plays all the seven notes available. Of course it would be extremely limiting if you only had seven notes to play with, so there is a facility to change octave. Although the program supports eight octaves, those at the extreme levels of pitch are of little use for musical pur- poses. In fact, the upper few notes in the top octave will produce an error if you try to play them, for the simple reason that the computer can't manage to produce such a high-pitched sound. To change octave, an O is simply put in the string, followed by the new octave number - 1 to 8. All subsequent notes in that string are then played in the new octave. At the start of every new *Play the octave is reset to 3. To further increase the quantity of notes available, sharps are also supported. A sharp note has a pitch slightly above - one semitone in fact, whatever that is - its ordinary value. To play a sharp note simply suffix the note with a hash character (#): *PLAY "c#" gives C sharp. 10 FOR a=1 TO 7 20 *PLAY "o"+STR$ a+"c#dd#eff#gg#aa#b" 30 NEXT a This short program demonstrates the full scope of the notes available in the lower seven octaves. The O "direc- tive" - or any other directive that needs an argument, you'll meet some more in a moment - must be followed by a number, no variables or expressions are allowed. You can, however, circumvent this difficulty by using STR$ to construct the numerical part of the string - as in the above program - out of an expression, variable or whatever. You can play noise instead of plain notes by utilising the n directive: n0 turns noise off while n1 switches it on. To hear the effect, try the above program with n1 preceding the o in the string. The pause (p) directive enables a silent gap, equivalent to a musical "rest", to be placed in a tune. The number following p is the length of the pause measured in 1/100ths of a second. The pause can be up to 2.55 seconds. It is possible to modify the duration of each note using the L directive. The L is succeeded by a number between 1 and 255 representing the new length, in 1/100ths of a second, of each note. At the start of each new *Play command the note length is reset to 0.2 seconds. Dotted notes are also supported by the program. A dotted note is played for 1.5 times as long as an ordinary note. To make a dotted note, simply put a full stop after the letter in much the same way as you would with a #: *PLAY "L100c#." plays c# for 1.5 seconds. If you require a dotted sharp note then the full stop should come after the #. To add a little more interest to a tune it is possible to use an envelope. So far all the notes have kept the same tone value throughout their duration, like the sound pro- duced by Beep. To turn envelopes on, a Y directive is used. The number following the Y is the envelope you wish to use, or 16 to turn envelopes off. All subsequent notes are played using that envelope. By experimenting with a number of different envelope effects it is often possible to enhance the sound of a tune considerably from its flat, unenveloped beginnings. To summarise, these are the directives available in a *Play string - actually, there is still one to come - I'll mention that one soon. A-G = The notes. # = If placed after a note will make it a sharp. . = If placed after a note will play it 1.5 times as long. On = Lets you play subsequent notes in octave n. Pn = Pauses for n 1/100ths of a second. Nn = If n=0 then noise is turned off. If n=1 then it is switched on. Ln = Makes the duration of subsequent notes n 1/100ths of a second. Yn = Makes following notes to be played in envelope n (or normal if n=16). If a sequence of sound commands are always used together then they can be made into a single unit called an effect. An effect is a construction of up to eight separate "sounds" strung together. Like envelopes, effects can be defined and stored in the Spectrum's memory for later, and repeated, use. You can have up to eight effects defined at any one time. *EFFECT 0,1,100,200,16,100,150,16 This effect is equivalent to *SOUND 100,200:*SOUND 100,150 The first value in an *Effect is the effect (0-7) and the next is the number of times the whole effect is to be repeated when used. Every block of three expressions from there onwards represents one of the maximum eight indivi- dual sounds that compose the effect. They are given in the same format as that expected by *Sound, i.e. length, pitch and then the envelope number. In an effect sequence it is mandatory to specify an envelope number; it cannot simply be left off. If no enve- lope is required then 16 should be specified. The only way to play an effect is by using the *Play command. An X directive, followed by an effect number, will sound the appropriate effect.