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