Sound Advice Series

Make your SOUND statements stand patiently in line with our latest micro-music article.

Volume 1

Number 11

January 1984

RIGHT ON QUEUE

More sound advice from NIGEL PETERS

THE Editor passed my desk on his way to his remedial reading group.

"Well", he said, "this series of articles has answered one of my doubts about you".

"What's that?" I asked rising to the bait, stupidly.

"I used to wonder whether you were deaf or daft. After hearing those programs of yours I know. You're both".

SO far our treatment of the SOUND command has been fairly limited. Ignoring the special effects channel, the BBC Micro has three sound channels available but we've mostly avoided using more than one at a time.

When we did the result was a mess -remember the random music generator!

To make more interesting noises we have to use more than one channel at a time.

To allow us to do this without causing chaos BBC Basic allows the SOUND command to be extended to control the flow of notes through the three channels.

For the time being, though, let's just stick to one channel and play three notes one after the other.

Program I isn't very exciting but there's a lot going on which repays closer attention. The three notes play one after the other in the order of the SOUND statements.

10 REM PROGRAM I
20 SOUND 1,-15,60,20
30 SOUND 1,-15,68,20
40 SOUND 1,-15,76,20

The first note plays for a count of 20, then the second and when that finishes the third sounds. All very logical.

Let's try Program II and see what happens. You'll notice that it's the same as Program I except that each SOUND command is followed by a PRINT statement.

10 REM PROGRAM II
20 SOUND 1,-15,60,20
30 PRINT "NOTE1"
40 SOUND 1,-15,68,20
50 PRINT "NOTE2"
60 SOUND 1,-15,76,20
70 PRINT "NOTE3"

Now you might think that the first note will be played followed by the PRINT statement putting something on screen, then the second note and so on. Have a go and see.

What actually occurred was that the three messages were printed on the screen before the first note had finished playing. Then the second note played, then the third.

The micro appeared to have-jumped around the program lines instead of following them one by one in line number order as usual.

Appearances are deceptive, for the micro read each line in turn and followed its instructions before moving onto the next one and obeying that.

What happened was that the program read line 20 and started playing the first note. It then read line 30, printed the message, and had a look at line 40 which told it to produce the next note.

The trouble was that there was already a note playing on that channel and (in the very fast world of the microprocessor) it would continue playing for some time.

So as not to hold up the program, the micro put the sound it had created on a special queue for that channel and carried on with the next instruction which is to print the second message.

This it did and, because BBC Basic is so fast, it did it before the first note had

stopped playing. It then came to line 60 which told it to play yet another note.

Because it hadn't finished the first yet, nor gone on to the second, it popped that note on the queue and went on to line 70.

The micro printed the last message and, as there were no more lines, the program stopped running. It did not stop making the noises, however, but carried on working its way down the queue.

The important point is that the micro continues to play the notes in the queue even after it has stopped processing a program's Basic commands.

In this sense the queues - each channel has its own — are independent of the program.

From this, you can see that it's possible for a program to rush through its Basic statements, finish whatever it was meant to do and the micro will carry on playing the sounds in the queues regardless.

This can at times be embarrassing. Imagine a game that sounds a fanfare every time you zap an alien. Suppose you actually manage to get a few of them so that the fanfares build up in the queue. Then you get hit, it's your last life and the game is over.

The program might order the micro to make a "losing" type of noise on the same channel.

This it will do and, since there are still fanfares to be played, it will pop it on the queue.

Instead of the losing noise when you get hit, you get a succession of fanfares until the queue reaches the losing sound.

In this sense the queue can be a bit of a nuisance, causing the sounds to be out of step with the program.

While the example above is fairly trivial it does show that silly things can happen.

It would be nice if there were a way of emptying the notes from the queue to give another note priority. This would allow an "important" sound to take precedence by wiping all the waiting sounds from the queue and so playing it immediately.

In our example, this would mean that the losing sound would be played immediately.

In fact, there is a way that you can get a SOUND command to take priority over others in the queue. It involves a different way of using the parameter that selects the channel.

This, if you remember, is the first number following the SOUND command. Up until now it has only been a single digit - 1, 2 or 3.

By using this channel parameter as a four figure hexadecimal number you can do all sorts of clever things with the simple SOUND command.

It is not as complicated as it sounds, especially as we'll only be concentrating on two of the figures.

Previously we've used the SOUND command in the format: SOUND W,X.Y,Z where W selects the channel, X the loudness, Y the pitch and Z the duration of the note.

In its expanded role as a hexadecimal number, the channel selection parameter can be looked on as: &TUVW where T, U, V, and W represent figures.

The T parameter we shall ignore for the time being. Similarly, the U parameter, which controls the synchronisation of notes, will be left to a later date.

All we are left with is V, which can have the values 0 or 1, and W which is the channel selection number and is used exactly as before - except that it comes at the end of a four digit number.

Previously, when we have been using SOUND, the micro has only come across W in the first position after SOUND and so has assumed that T, U and V were all 0.

What this means is that where we have used: SOUND 1,-15,60,40 we could equally have used: SOUND &0001,-15,60,40

The result would be the same and the SOUND command would work as we have come to expect.

If we want a SOUND command to be obeyed immediately, overriding any other notes that might be in the queue, we change the V parameter from 0 to 1.

When the program gets to this line it will obey that command immediately, stopping any note that's playing and "flushing" the queue.

Let's see what happens if we change the last line of Program I to make Program III.

10 REM PROGRAM III
20 SOUND 1,-15,60,20
30 SOUND 1,-15,68,20
40 SOUND&0011,-15,76,20

It seems as though the micro only plays the last note. What's actually happened is that the micro has read line 20 and started to play a note.

It then read the next line and put that note on the queue and got to the last SOUND instruction.

This had the V parameter set to 1 so the micro immediately cleared the queue, stopped playing the note that it was playing and played the final note.

This happens so fast that you only hear the final sound.

Program IV slows things down a little with a delay procedure. Run it and you will hear all three notes.

10 REM PROGRAM IV
20 SOUND 1,-15,60,20
30 PROCdelay
40 SOUND 1,-15,68,20
50 PROCdelay
60 SOUND&0011,-15,76,20
70 END
80 DEF PROCdelay
90 FOR N=1 TO 1000:NEXT
100 ENDPROC

The second line tells the micro to make a noise and, since there is nothing in the queue, it does this.

While this is still playing, the micro whirls through the first of the delay loops and when it gets to the second SOUND command it puts this on the queue. It then goes on to the second delay loop.

In the meantime, the first note has reached the end of its alloted time and stops.

The second note comes off the queue and starts playing but never reaches the full length specified by its duration parameter.

This is because the micro finishes the second delay loop and reads the next SOUND command.

This has a 1 for its V parameter so the computer immediately stops playing its present note and obeys that SOUND command straight away. Hence the shortened second note.

By making the V parameter a 1 you can ensure that SOUND command gets priority over all other sounds playing on that channel. In effect, it cuts short the note that is playing.

We could use this in the games program mentioned earlier to make the "losing" SOUND command get rid of all the fanfares in the queue and play immediately.

Also it can be used to ensure that noises are synchronised with whatever a program is doing by flushing the queues as in Program V.

10 REM PROGRAM V
20 SOUND 1,-15,60,20
30 PRINT "NOTE1"
40 SOUND 1,-15,68,20
50 PRINT "NOTE2"
60 SOUND 1,-15,76,20
70 PRINT "NOTE3"
80 FOR N=1 TO 100
90 PRJNT"SOUNDING"
100 NEXT
110 SOUND &11,0,0,0

What has happened is that the micro has read the first SOUND command and played a note and then put the other two notes on the queue for channel 1.

Then the loop prints "sounding" on the screen and when it has finished the micro reads the last SOUND command.

This has a V parameter of 1, so it is executed immediately, flushing the queue at the same time.

However, since all the other parameters are 0 there is just silence and the sound's ending coincides with the ending of the printing loop.

You might notice from the last line that there is no need to put in the first two zeroes after the ampersand (&).

So far we've covered how the Sound commands can be stored in queues and how they can appear to operate independently of Basic.

We have also seen that this can lead to problems where sounds can be out of phase with what the Basic program is doing and how to remedy this by flushing the queues.

We have not mentioned how many sounds can be held in each queue and what happens when they are full. Try Program VI and see what happens.

10 REM PROGRAM VI
20 SOUND 1,-15,60,20
30 PRINT "NOISE1"
40 SOUND 1,-15,68,20
50 PRINT "NOISE2"
60 SOUND 1,-15,76,20
70 PRINT 'NOISE3'
80 SOUND 1,-15,60,20
90 PRINT "NOISE4'
100 SOUND 1,-15,68,20
110 PRINT "NOISE5"
120 SOUND 1,-15,76,20
130 PRINT "NOISE6"
140 SOUND 1,-15,60,20
150 PRINT "NOISE7"
160 SOUND 1,-15,68,20
170 PRINT "NOISE8"
180 SOUND 1,-15,76,20
190 PRINT "NOISE9"

From what happened in Program I you might expect that it would sound the first note while obeying all the PRINT statements.

That is, NOISE1, NOISE2, ... NOISE9 would appear on the screen while the first note sounded.

When that note had finished, the other notes on the queue would take their turn.

What does take place is that the first note sounds with NOISE 1 to NOISE6 appearing on the screen.

When the first note is finished, the second starts playing and NOISE7 is printed. The third note coincides with NOISE8, the fourth with NOISE9.

The reason for this is that although the Basic can carry on quickly by putting the SOUND commands on the queues these queues only have a limited capacity.

The manual says that each channel can hold four sounds as well as the one currently playing.

When the channels are full up, the micro will happily work its way through all the Basic statements until it comes to the next SOUND command. Here it conies to a halt until there's a place available on the queue.

This means that the program hangs until the note that's playing reaches the end of its duration parameter and the next note can come off the queue.

Then the SOUND command that's caused the delay can be processed and popped onto the queue allowing the program to continue.

This is the explanation for what happened in the last program. The program happily played the first sound and popped the rest on the queue until it could do no more and the program came to a stop.

When the first note stopped playing the sounds shuffled along the queue and made room for the SOUND command that was causing the delay.

The program then obeyed the next PRINT command, putting NOISE7 on the screen and went on to the next line. Here it came to a halt at the next SOUND statement and had to wait until the note stopped playing and so on.

The trouble is that, by my calculations, the program plays one sound and puts five on the queue before it grinds to a halt at the seventh SOUND command and has to wait to print NOISE7.

This seems to be one more than the manual would allow. Still, the point remains the same. When a channel's queue is full the next SOUND command for that channel will cause the program to wait.

Anyway that's all for now — I have to finish.- The editor's come back after three or four pints of remedial reading and is sounding off. He looks flushed after so much queueing at the bar.

SOUND DOODLE

10 REM SOUND DOODLER
20 REM Nigel Peters
25 MODE 6
30 REPEAT
40 INPUT TAB(5,5)"Channel",channel
50 INPUT TAB(5,10)"Loudness",loudness
60 INPUT TAB(5,15)"Pitch",pitch
70 INPUT TAB(5,20)"Duration",duration
80 SOUND channel,loudness,pitch,duration
90 REM Note the cunning use of the flush command!
100 pause$=GET$:SOUND &11,0,0,0:SOUND &12,0,0,0:SOUND &13,0,0,0:SOUND &14,0,0,0
105 CLS
110 UNTIL FALSE