Bottom     Previous     Contents

CHAPTER 9
Making Micro Music

After experimenting with the programs in Chapter 8, you will probably have come to the conclusion that the QWERTY keyboard is not the easiest musical instrument to play. If you have tried to play a three-part or even a two-part tune with any degree of accuracy, you will be aware of the restrictions the keyboard places upon us. It's fine for improvising and playing along with a rhythm or bass pattern, but playing true multi-part tunes is extremely difficult.
We can overcome these problems by providing the computer with all necessary information regarding pitch, envelopes, durations, etc, and letting it get on with the hard work of putting them together. It can play sequences and harmonies we would never be able to imagine and it will play them right every time.
The simplest and most obvious method is to read the information into the program through DATA statements: this method is used in most of the programs in this book. For short musical examples this is fine, but it soon becomes quite complicated when you have more than 20 or 30 items of data. This is where Program 3.1 can help by converting note names and octave numbers into pitch values. This permits us to enter data in a way more comprehensible to us and it makes subsequent editing considerably easier. If you include error routines it will also report instances of incorrect data.
Another problem arises if we want more than one channel to sound at once, namely that of synchronization. If we just want to play a single melody line there are no problems. This short program will do exactly that:

10 FOR Note=1 TO NumberOfNotes
20 READ Env,Pitch,Dur
30 SOUND1,Env,Pitch,Dur
40 NEXT Note
50 DATA E1,P1,D2,E2,P2,D2,E3,P3 etc

If we want to play more than one channel at a time we need to use a slightly different method. We mentioned this in earlier chapters and now we will examine it in detail.

Playing two- or three-part tunes

Because of the sound queues used by the BBC micro, the sound channels seem to race ahead of the rest of the program. We found this in Program 6.5 where we had to hold up the program with a TIME loop before redefining the envelope.
The optional synchronization parameter of the SOUND command can be used to delay a sound channel until another channel appears with the same synchronization value. However, the organisation of information to each channel must be carefully controlled if the program is not to seize up.
Problems arise when two or three voices are to be played together and each voice contains notes of different durations. If we try to read data for two channels from one DATA statement, each time the data is read it must supply information for both channels. This means we would have to organise the data so that the two sets of notes end up with the same number of data items. This is a time-consuming and inconvenient process. With three-part tunes the problem is even worse as one channel will probably only be playing one note to the others' four or more.
The most obvious answer is to fill arrays, one for each channel, with the relevant data, and only read from a particular array either when a note is required or when the sound channel is capable of taking it without holding up the program.

Figure 9.1

As an example, Figure 9.1 shows the first eight bars of Mozart's Rondo Alla Turca. You can see that, by the time we reach the end of bar 3, 24 notes will have passed through channel 1 and only eight through channel 3. If these were queued together, the program would be held up waiting for the notes on channel 3 to execute. We can solve this in two ways:

(1) By keeping track of the relative durations of each channel and releasing data accordingly.

Or

(2) By only releasing data when the channels can take it at a time determined by the negative ADVAL function.

Before beginning our experiments, let us see how we can best organise the data.

Selecting the notes and octave range

The programs presented here read note information from DATA statements. This gives us immediate visual and physical access to the information which is saved along with the program.
When programming a musical score, the first thing we must look at is the range of notes it uses. This example ranges from B an octave below middle C to C two octaves above middle C. This is fortunate in that we can use the correct notes and octaves produced by the sound chip and illustrated on the keyboard in Figure 2.4. It does mean that we need to transpose or mentally shift the keyboard up an octave to make middle C correspond to the middle C on the stave. This is not as complicated as it might seem. If you enter the notes as names and octave numbers, read down from the stave to the note name and octave number and, if you want it lower, subtract 1 from the octave number.
An alternative, as we mentioned in Chapter 3, is to enter the notes exactly as they read, eg middle C as 101, and use the variable, Key, to take the piece down an octave. Using this method, of course, you can play the tune in any key at all. I decided on the first method.
The second eight bars of Rondo Alla Turca (not illustrated) uses A an octave below middle C. In this case you would use the keyboard and octave numbers exactly as set out in Figure 2.4, an octave higher than written.
Another solution is to 'cheat' by substituting a note within your range for the A. If only one or two notes are outside the range this will usually produce acceptable results and I prefer to do this rather than take the whole tune up an octave. Musically (and, perhaps, sardonically) this is known as doing an arrangement.
I have stated before my preference for the lower octaves. I tend to program as low as possible. These notes are richer and more full than the high ones, but use whichever range suits you and the music best. Not all pieces will benefit by being so low.
I normally arrange the channels as follows:

Channel 1: Melody line.
Channel 2: Bass line.
Channel 3: Anything in between, often reinforcing the bass/accompaniment

The melody and bass fines are the most important and set the character of the piece. I use channel 3 to fill in the harmony where required, and it can switch from bass to melody as necessary. If the piece has a prominent bass fine, I may sometimes program that first into channel 1.
The next program uses only one channel to play a melody. You may prefer not to type it in and use the listing only as a reference as we will be moving on to a more sophisticated program. However, as we will be using the data and PROCAnalyseNote in the other program, if you type it in, it will not be wasted.
This raises another point worthy of consideration, which is - the programs need only be as complicated and sophisticated as you require. If you only want a monophonic tune, this program will do fine.

10 REM PROGRAM 9.1

20 REM 1 Channel Version of Mozart's

30 REM Rondo Alla Turca

40

50 Scale$=" C C# D D# E F F# G

G# A A# B"

60 Key=1

70

80 ENVELOPE1,1,0,0,0,0,0,0,126,-2,0,-

10,126,100

90 CurrentEnv=1

100

110 FOR N=1 TO 46

120 READ Note$,Dur

130 PROCPlayNote

140 NEXT N

150

160 END

170

180 DATA B2,2,A2,2,G#2,2,A2,2

190 DATA C3,4,R,4,D3,2,C3,2,B2,2,C3,2

200 DATA E3,4,R,4,F3,2,E3,2,D#3,2,E3,2

210 DATA B3,2,A3,2,G#3,2,A3,2,B3,2,A3,

2,G#3,2,A3,2

220 DATA C4,8,A3,4,C4,2,B3,1,A3,1

230 DATA B3,4,A3,4,G3,4,A3,2,G3,1,A3,1

240 DATA B3,4,A3,4,G3,4,A3,2,G3,1,A3,1

250 DATA B3,4,A3,4,G3,4,F#3,4

260 DATA E3,8

270

280 DEF PROCPlayNote

290 PROCAnalyseNote

300 PRINT Note$,Pitch,Octave

310 SOUND1,Env,Pitch,Dur

320 ENDPROC

330

340 DEF PROCAnalyseNote

350 IF Note$="R" Env=0:ENDPROC ELSE En

v=CurrentEnv

360 IF LEN(Note$)<2 OR LEN(Note$)>3 TH

EN PRINT"ERROR IN DATA ";Note$:PRINT"Not

e Number ";N:STOP

370 IF LEN(Note$)=2 THEN NoteName$=LEF

T$(Note$,1) ELSE NoteName$=LEFT$(Note$,2

)

380 Octave=VAL(RIGHT$(Note$,1))

390 Pitch=Key+INSTR(Scale$,NoteName$)/

3*4+(Octave-1)*48

400 IF Pitch<0 OR Pitch>255 THEN PRINT

"ERROR IN PITCH DATA ";Note$;" Pitch = "

;Pitch:PRINT"Note Number ";N:STOP

410 ENDPROC

Program notes

This is very similar to Program 3.1. PROCCa1culatePitch has been incorporated into PROCAnalyseNote and the tune is under envelope control. Line 350 sets the volume to 0 if a rest is required. Extra information has been included in lines 360 and 400 to print the note number in case of an error. The data has been organised into a bar per fine to aid editing and debugging.
The main points of the program were discussed in Chapter 3 and, from here, we will move straight on to a three channel version and discuss some options open to us.

The tracking method

This entails keeping track of the elapsed durations of the three channels. When a SOUND command is executed the duration value is added to its 'track'. The program is arranged to execute the SOUND commands as follows :

  1. If all three channels show the same amount of elapsed duration then sync them.
  2. If two channels are behind the other with the same elapsed duration then sync them.
  3. If neither 1 nor 2 above apply, send a command to the channel which is lagging behind the most.
  4. Repeat the above until the tune is finished.

You may like to work out a program which follows the above guidelines. In operation, you will find that the buffers are usually full and consequently the program will be unable to do anything other than play the tune. Why should that matter? Well, if that's all you want to do, fine, but the BBC micro is capable of (apparently) doing more than one thing at a time. If the programming is not too long or complex it is no hardship to program for that possibility. By using, once more, the negative ADVAL function we can do just that.

The negative ADVAL method

ADVAL with a negative argument is described quite clearly in the User Guide on page 203. If we fill two or three arrays with the required notes and use ADVAL to send SOUND commands only when a channel has space to take them, this will be enough to synchronize the music.
To control every aspect of a note we need to specify not only its pitch and duration but also which envelope it is to use and whether or not the sound channel is given a flush, synchronization or hold command. It takes no computer to calculate that each note would require four items of data. This in itself presents no problem providing you do not object to entering it.
The next program gives control over the four elements of each note. In practice, it is possible to devise some time-saving procedures, which I have done in certain cases - and you will find that you rarely have to enter four data items for each note.

10 REM PROGRAM 9.2

20 REM 3 Channel Version of Mozart's

30 REM Rondo Alla Turca

40 REM Using a Single Array for

50 REM Each Channel

60

70 REM C1=Number of Notes for

80 REM Channel 1 etc

90 C1=46:C2=30:C3=29

100

110 REM 1st Subscript Refers to:

120 REM 1 - Channel Number/Attributes

130 REM 2 - Envelope Number

140 REM 3 - Pitch Value

150 REM 4 - Duration

160

170 DIM Chan1(4,C1)

180 DIM Chan2(4,C2)

190 DIM Chan3(4,C3)

200

210 Scale$=" C C# D D# E F F# G

G# A A# B"

220 Key=1

230 Tempo=1

240

250 ENVELOPE1,1,0,0,0,0,0,0,126,-2,0,-

5,126,100

260 ENVELOPE2,4,0,0,1,1,0,1,63,-1,0,-1

0,126,100

270 ENVELOPE3,1,0,0,0,0,0,0,126,-4,-1,

-4,126,100

280

290 REM Channel 1

300 FOR N=1 TO C1

310 PROCChan

320 Chan1(1,N)=Chan

330 IF Note$="R" Env=0 ELSE IF N=5 OR

N=11 OR N=25 OR N=30 OR N=36 OR N=42 Env

=2 ELSE Env=1

340 Chan1(2,N)=Env

350 PROCAnalyseNote

360 Chan1(3,N)=Pitch

370 Chan1(4,N)=Duration

380 NEXT N

390 PRINT"Channel 1 Complete"

400

410 REM Channel 2

420 FOR N=1 TO C2

430 PROCChan

440 Chan2(1,N)=Chan

450 IF Note$="R" Env=0 ELSE Env=3

460 Chan2(2,N)=Env

470 PROCAnalyseNote

480 Chan2(3,N)=Pitch

490 Chan2(4,N)=Duration

500 NEXT N

510 PRINT"Channel 2 Complete"

520

530 REM Channel 3

540 FOR N=1 TO C3

550 PROCChan

560 Chan3(1,N)=Chan

570 IF Note$="R" Env=0 ELSE Env=1

580 Chan3(2,N)=Env

590 PROCAnalyseNote

600 Chan3(3,N)=Pitch

610 Chan3(4,N)=Duration

620 NEXT N

630 PRINT"Channel 3 Complete"

640

650 Ch1=0:Ch2=0:Ch3=0

660

670 REPEAT

680 IF ADVAL(-6)>0 AND Ch1 1:SOUNDChan1(1,Ch1)+1,Chan1(2,Ch1),Chan1

(3,Ch1),Chan1(4,Ch1)*Tempo

690 IF ADVAL(-7)>0 AND Ch2 1:SOUNDChan2(1,Ch2)+2,Chan2(2,Ch2),Chan2

(3,Ch2),Chan2(4,Ch2)*Tempo

700 IF ADVAL(-8)>0 AND Ch3 1:SOUNDChan3(1,Ch3)+3,Chan3(2,Ch3),Chan3

(3,Ch3),Chan3(4,Ch3)*Tempo

710 UNTIL Ch1=C1 AND Ch2=C2 AND Ch3=C3

720

730 END

740

750 DEF PROCChan

760 READ Note$:IF LEFT$(Note$,1)="&" C

han=EVAL(Note$):READ Note$,Duration ELSE

Chan=0:READ Duration

770 ENDPROC

780

790 DEF PROCAnalyseNote

800 IF Note$="R" Pitch=255:ENDPROC

810 IF LEN(Note$)<2 OR LEN(Note$)>3 TH

EN PRINT"ERROR IN DATA ";Note$:PRINT"Not

e Number ";N:STOP

820 IF LEN(Note$)=2 THEN NoteName$=LEF

T$(Note$,1) ELSE NoteName$=LEFT$(Note$,2

)

830 Octave=VAL(RIGHT$(Note$,1))

840 Pitch=Key+INSTR(Scale$,NoteName$)/

3*4+(Octave-1)*48

850 IF Pitch<0 OR Pitch>255 THEN PRINT

"ERROR IN PITCH DATA ";Note$;" Pitch = "

;Pitch:PRINT"Note Number ";N:STOP

860 ENDPROC

870

880 REM Channel 1

890 DATA &200,B2,2,A2,2,G#2,2,A2,2

900 DATA &200,C3,4,R,4,D3,2,C3,2,B2,2,

C3,2

910 DATA &200,E3,4,R,4,F3,2,E3,2,D#3,2

,E3,2

920 DATA &200,B3,2,A3,2,G#3,2,A3,2,B3,

2,A3,2,G#3,2,A3,2

930 DATA &200,C4,8,A3,4,C4,2,G3,1,A3,1

940 DATA &200,B3,4,A3,4,G3,4,A3,2,G3,1

,A3,1

950 DATA &200,B3,4,A3,4,G3,4,A3,2,G3,1

,A3,1

960 DATA &200,B3,4,A3,4,G3,4,F#3,4

970 DATA &200,E3,8

980

990 REM Channel 2

1000 DATA &200,R,8

1010 DATA &200,A1,4,C2,4,C2,4,C2,4

1020 DATA &200,A1,4,C2,4,C2,4,C2,4

1030 DATA &200,A1,4,C2,4,C2,4,C2,4

1040 DATA &200,A1,4,C2,4,C2,4,C2,4

1050 DATA &200,E1,4,B1,4,B1,4,B1,4

1060 DATA &200,E1,4,B1,4,B1,4,B1,4

1070 DATA &200,E1,4,B1,4,B1,4,B1,4

1080 DATA &200,E1,8

1090

1100 REM Channel 3

1110 DATA &200,R,8

1120 DATA &200,R,4,E2,4,E2,4,E2,4

1130 DATA &200,R,4,E2,4,E2,4,E2,4

1140 DATA &200,R,4,E2,4,R,4,E2,4

1150 DATA &200,R,4,E2,4,E2,4,E2,4

1160 DATA &200,R,4,E2,4,E2,4,E2,4

1170 DATA &200,R,4,E2,4,E2,4,E2,4

1180 DATA &200,R,4,E2,4,R,8

1190 DATA &200,R,8

Like Program 9.1, the data is arranged in lines of one bar: you can use the data from Program 9.1 and add the extra data items to it. PROCAnalyseNote has one change in it at line 800 as you will see.

Program notes

The numbers of notes for each part are assigned to variables, C1, C2, and C3 in fine 90. These numbers need to be accessed several times during the program so that, if you want or need to alter data, you only need change these values once. They refer to the number of notes allocated to each channel, not to the number of data items.
Three arrays are dimensioned at lines 170 and 190 to hold information about each note. The first subscript refers to the following aspects of the note:

Subscript-l: Channel attributes, ie sync, flush, etc.
Subscript=2: Envelope number.
Subscript=3: Pitch value.
Subscript=4: Duration value.

The next section sets the data into its relevant arrays and the process is repeated once for each channel. I have kept it this way to aid understanding, although you might like to substitute a single procedure to avoid the repetition. As the process is exactly the same for each channel we will only look at channel 1 in detail.
A FOR . . . NEXT loop runs through the data, once for each note. The first step is a call to PROCChan at line 750. This 'cautiously' examines the first item of data. If the data begins with an ampersand (&) it knows it is a channel instruction, and evaluates the string with EV AL to produce the channel attribute, Chan. It then proceeds to read Note$ and Duration. I have used hexadecimal notation to represent the sound channel parameters because it is easy to use and the ampersand tells us at a glance that this data item is a special channel command. (See Chapter 4 for further details.)
If the string does not begin with an ampersand it is taken to be a note and a second item of data, Duration, is read. As the procedure has no specific channel information, Chan is set to 0. This method of assigning the channel attributes saves us having to enter a channel parameter for every note. We only enter a parameter if we require a sync, flush or hold. The channel attribute is assigned to Chan1(1,N) in line 320.
Next, Note$ is examined to see if it is an 'R', which I have used to represent a rest. If it is an 'R', Env is set to 0 to produce a sound at zero volume: otherwise, it is set to the required envelope number. You can see this more clearly in lines 450 and 570, where only one other envelope is used.
Line 330 is arranged to give certain notes different envelopes. This saves us including a whole set of new data for the envelope number, which can be time-consuming if the envelope only changes once or twice throughout the piece. Env is assigned to Chan1(2,N) in line 340.
The program then calls PROCAnalyseNote. Unlike Program 9.1, which sent each note through PROCAnalyseNote before playing it, this program sends it through to calculate the correct pitch value of the note, and to check for any incorrect data entries. The first thing it does is check if Note$ equals 'R', ie a rest. If it does, Pitch is given an arbitrary value of 255 and the procedure ends. This is the only way this procedure differs from that in Program 9.1. Having turned the note name into a pitch value, assuming no errors, Chan(3,N) is set equal to Pitch at line 360.
Line 370 copies Duration directly into Chan1(4,N).
At the end of these three sections, the arrays are filled with figures which the SOUND command can work on directly; although they can be modified if required as we shall see.
Line 650 sets three variables, Chl, Ch2 and Ch3, to 0 and these are used to check the number of data items sent to each channel.
The REPEAT loop at lines 670 and 710 does all the work. The principle is the same for each sound channel, so only channel 1 at fine 680 will be described.
The sound buffer is checked for free spaces and the checker variable. Ch1, is compared with C1 to see if channel 1 has had all its notes. If there is free space in the buffer and there are more notes to come, Ch1 is incremented and used to provide information for the sound channel from the Chan1 array. It is at this point that the channel number is assigned, and this; is added to the channel attribute derived from PROCChan. The duration is adjusted according to the variable, Tempo.
Line 710 ensures that the loop repeats enough times to play all the notes, and then terminates it.

Modifications, alterations and suggestions

Once you have this program, you will be able to play any three-part tune by inserting new data and altering the variables C1, C2 and C3 in line 90.
If you arrange the data in fines of one bar it will aid editing and debugging.
You will notice that the channel attributes are arranged to synchronize at the beginning of each bar. This may not always be possible, especially if the music is heavily syncopated (where notes are tied or held over from one bar to the next) but you should be able to fit some sync commands in somewhere.
Earlier I said that simply using ADVAL statements would provide synchronization. Basically it will, but we must be aware of one or two points. If none of the notes are synchronized, the channels will not be exactly in sync because of the time taken by BASIC to move from the first sound command to the second, etc, especially if it has to look up values in arrays and do some calculating as in this program. If you synchronize even only the first notes, the program should keep good time provided that it is not interrupted. This can be done by altering the READ command to read only Note$ and Duration and by inserting a line such as:

635 Chan1(1,1)=&200:Chan2(1,1)=&200:Chan 3(1,1)=&200

The other elements in this array will be set to 0 upon dimensioning.
If you were to go to the trouble of removing the sync commands, you would find that the program stayed in time reasonably well, although it might drift if programmed with a longer piece. Another reason for synchronizing the channels is to allow the computer to perform other functions as the music is playing. We will cover this later in the book, but for the time being assume that we want a print-out of the notes as the music is playing. We could insert a line similar to fine 300 in Program 9 .1 such as:

685 PRINT;Chan1(1,Ch1)+1TAB(4)Chan1(2,Ch1)TAB(9)Chan1
(3,Ch1)TAB(14)Chan1(4,Ch1)*Tempo

If the channels are not synchronized, the delay between channel 1 and channel 2 (caused by BASIC taking time out to process the command) will throw the program out of sync.
If you feel that you do not require four parameters per note, it is easy to alter the arrays.
The envelope changes are often tedious {a enter. If your program requires a lot of envelopes you could arrange a separate routine to set the envelope numbers such as:

100 FOR E=1 TO 100
1010 IF E>0 Chan1(2,E)=1
1020 IF E>12 Chan1(2,E)=2
1030 IF E>28 Chan1(2,E)=3
1040 IF E>40 Chan1(2,E)=1
. . . .
. . . .
1100 NEXT E

You may find this more convenient than entering 100 or more separate figures. Assuming you do want 100 or so envelopes, you could enter them in a separate DATA statement.
The method of allocating envelopes in Program 9.2 by a series of IF . . . THEN . . . ELSE statements (the THEN commands are implied) will prove satisfactory for all but the most demanding of programs,
When programming other tunes, if all channels {lo not start together, ie if each does not sound a note at the start of the piece, be sure to include rests in the channels which are not used to keep them in sync. This was necessary in Program 9.2.
When calculating the duration of each note, I base the values on those in the chart in Figure 2.6. The important thing is to get the relationship between the notes correct. Adjustments can be made with the variable Tempo in fine 230. This is most effective when given integer values. Insert values less than 1 and observe the results.

Debugging the data

It will be a rare occurrence if you enter a set of data statements for a tune and get no errors. PROCAnalyseNote will detect errors in pitch data entry and tell you which data item it does not understand and which note number this is associated with. You will know which channel the item belongs to because a message is printed when a channel has been successfully filled with data.
An easy mistake to make is to insert wrong C1, C2 or C3 values, causing a channel to be filled with another channel's notes. You could provide further checks by inserting a termination character on the end of each channel's data: so, for example, if Note$ read a 'F and N did not equal C1 you would know that you had set C1 to the wrong value. You could use this principle to read through the data before assigning any information and use the value to dimension the arrays. You could also incorporate checks on the other data.
Once the data is correct, you could remove the error routines.

Saving the tune

If you want to play the music in a separate program, for example as background to a graphics display, you can use the program to check out the data values and then *SPOOL the figures for the sound commands to tape or disk. The next program shows the additions necessary to do this.

1 REM PROGRAM 9.3

2 REM *SPOOL Routine To Put Sound

3 REM Data Onto TAPE or DISC

4 REM Include These Lines In

5 REM PROGRAM 9.2

6 REM Tempo=2 For DISC, 5 For TAPE

7

230 Tempo=2

664 Line=5000

665 PRINT"INSERT DISC OR TAPE then RET

URN"

666 REPEAT:A=GET:UNTIL A=13

667 *SPOOL"TUNE"

680 IF ADVAL(-6)>0 AND Ch1 1:SOUNDChan1(1,Ch1)+1,Chan1(2,Ch1),Chan1

(3,Ch1),Chan1(4,Ch1)*Tempo:PROCSpool1

690 IF ADVAL(-7)>0 AND Ch2 1:SOUNDChan2(1,Ch2)+2,Chan2(2,Ch2),Chan2

(3,Ch2),Chan2(4,Ch2)*Tempo:PROCSpool2

700 IF ADVAL(-8)>0 AND Ch3 1:SOUNDChan3(1,Ch3)+3,Chan3(2,Ch3),Chan3

(3,Ch3),Chan3(4,Ch3)*Tempo:PROCSpool3

715 *SPOOL

861

862 DEF PROCSpool1

863 PRINT;Line;" DATA ";Chan1(1,Ch1)+1

;",";Chan1(2,Ch1);",";Chan1(3,Ch1);",";C

han1(4,Ch1)*Tempo

864 Line=Line+10

865 ENDPROC

866

867 DEF PROCSpool2

868 PRINT;Line;" DATA ";Chan2(1,Ch2)+1

;",";Chan2(2,Ch2);",";Chan2(3,Ch2);",";C

han2(4,Ch2)*Tempo

869 Line=Line+10

870 ENDPROC

871

872 DEF PROCSpool3

873 PRINT;Line;" DATA ";Chan3(1,Ch3)+1

;",";Chan3(2,Ch3);",";Chan3(3,Ch3);",";C

han3(4,Ch3)*Tempo

874 Line=Line+10

875 ENDPROC

876

10000 DATA -1,-1,-1,-1

10010

10020 REM These Lines Play the DATA

10030 RESTORE5000

10040 REPEAT

10050 READ Chan,Env,Pitch,Dur

10060 REM Set Divisor Equal to Tempo

10070 SOUNDChan,Env,Pitch,Dur/2

10080 UNTIL Chan=-1

Program notes

The three Spool procedures are identical for each channel and the data items are printed one note per line. You may like to modify the program to print more data per line and reduce the Spool procedures into one. Be sure to REM out these lines or insert a GOTO to jump over them until your data has been checked and verified.
The data is retrieved by executing:

*EXEC"TUNE"

See page 402 of the User Guide for more information about *SPOOL and *EXEC.
As they stand, these procedures may require some modification. The principle is to let the ADVAL function calculate the correct spacing of the data items. However, because the filing systems themselves require attention from the operating system, the ADVAL function may sometimes be waiting for notes when the operating system is not ready to give them. This will happen if a channel is supplied with a lot of short notes.
One way around this is to increase the variable Tempo in line 230, so that the sound channels do not empty while waiting for the filing system to finish with the OS. Tape will require longer values than disk, but experiments have shown that setting Tempo to 5 for Tape and to 2 for disk will work for Rondo Alla Turca. On playback, divide the duration by the increase in tempo value or, in the PROCSpool procedures, divide Tempo by whatever you needed to multiply it by in line 230. Other tunes may require different values.
The routine beginning at line 10000 will play the data. If you substitute 'SOUND' for the string 'DATA' in the procedures at fines 863, 868 and 873, the *SPOOLed program will play the tune when run.
The program depends so much upon the buffers not emptying before another SOUND command comes along, that the whole program may be better served by a routine which produces SOUND commands according to the tracking method described earlier. This would remove it from the mercy of the *SPOOLing filing system: although if you are using disks this will not be a serious problem.

Experimenting with the program

Program 9.2 is only the beginning. You could add an extra array and include channel 0 for drum effects. You could also use channel 0 instead of channel 1 to produce the lower notes we mentioned in Chapter 7.
If 16 envelopes are not enough, you can redefine envelopes in mid-program. You can do this by calling a procedure from a fine which is inserted in a similar way to the PRINT statement in fine 685 described earlier.
If you include the variable Key inside the REPEAT loop instead of in PROCAna1yseNote, you can produce a key change by altering the value of Key as explained in Chapter 3.
A separate array can be used to trigger calls to any number of procedures, to create any of the effects we've covered in the previous chapters; you can produce your own arrangements of your favourite tunes as well as programming your own compositions - which leads us straight into the next chapter. But first . . .

More tunes to play

These three programs include the data and information required to play other tunes. If the programs are entered as written and saved as files, using *SPOOL, you can load Program 9.2 and *EXEC these programs into the computer: they will be ready to run. See Appendix 2 and the User Guide page 402 for futher information.
Program 9.4 plays the next 16 bars of Ronda Aha Turca and includes a repeat of the first eight.

1 REM PROGRAM 9.4

2 REM Anothr 24 bars of Mozart's

3 REM Rondo Alla Turca

4 REM Insert These Lines into

5 REM PROGRAM 9.2

6

90 C1=168:C2=114:C3=114

275 ENVELOPE4,4,0,8,-8,0,1,1,126,-1,0,

-2,126,100

305 IF N=47 OR N=129 RESTORE 890

306 IF N=153 RESTORE 979

330 IF Note$="R" Env=0 ELSE IF N=52 OR

N=58 OR N=72 OR N=77 OR N=83 OR N=89 En

v=2 ELSE Env=1

335 IF N=153 OR N=165 Env=4

345 IF N=153 OR N=165 Env=4

425 IF N=31 OR N=85 RESTORE 1000

426 IF N=98 RESTORE 1089

545 IF N=30 OR N=85 RESTORE 1110

546 IF N=98 RESTORE 1199

880 REM Channel 1

970 DATA &200,E3,8,E3,4,F3,4

971 DATA &200,G3,4,G3,4,A3,2,G3,2,F3,2

,E3,2

972 DATA &200,D3,8,E3,4,F3,4

973 DATA &200,G3,4,G3,4,A3,2,G3,2,F3,2

,E3,2

974 DATA &200,D3,8,C3,4,D3,4

975 DATA &200,E3,4,E3,4,F3,2,E3,2,D3,2

,C3,2

976 DATA &200,B2,8,C3,4,D3,4

977 DATA &200,E3,4,E3,4,F3,2,E3,2,D3,2

,C3,2

978 DATA &200,B2,8

979 DATA &200,C4,8,A3,4,B3,4

980 DATA &200,C4,4,B3,4,A3,4,G#3,4

981 DATA &200,A3,4,E3,4,F3,4,D3,4

982 DATA &200,C3,8,B2,6,A2,1,B2,1

983 DATA &200,A2,8

990 REM Channel 2

1080 DATA &200,E1,8,R,8

1081 DATA &200,C1,4,C2,4,E1,4,E2,4

1082 DATA &200,G1,8,R,8

1083 DATA &200,C1,4,C2,4,E1,4,E2,4

1084 DATA &200,G1,8,R,8

1085 DATA &200,C1,4,A1,4,C1,4,C2,4

1086 DATA &200,E1,8,R,8

1087 DATA &200,C1,4,A1,4,C1,4,C2,4

1088 DATA &200,E1,8,R,8

1089 DATA &200,F1,4,A1,4,A1,4,A1,4

1090 DATA &200,E1,4,A1,4,D1,4,F1,4

1091 DATA &200,C1,4,E1,4,D1,4,F1,4

1092 DATA &200,E1,4,E1,4,E1,4,E1,4

1093 DATA &200,A1,8

1100 REM Channel 3

1190 DATA &200,R,8,C3,4,D3,4

1191 DATA &200,E3,4,E3,4,R,8

1192 DATA &200,B2,4,G2,4,C3,4,D3,4

1193 DATA &200,E3,4,E3,4,R,8

1194 DATA &200,B2,8,A2,4,B2,4

1195 DATA &200,C3,4,C3,4,R,8

1196 DATA &200,G#2,4,E2,4,A2,4,B2,4

1197 DATA &200,C3,4,C3,4,R,8

1198 DATA &200,R,8

1199 DATA &200,R,4,D#2,4,D#2,4,D#2,4

1200 DATA &200,R,4,E2,4,R,4,B1,4

1201 DATA &200,R,4,A1,4,R,4,B1,4

1202 DATA &200,A1,4,A1,4,G#1,4,G#1,4

1203 DATA &200,A1,8

Notice how repeats have easily been programmed by the RESTORE function at lines 305, 306, 425, 426, 545 and 546. The only other programming has been an additional envelope.

10 REM PROGRAM 9.5

20 REM Tschaikowsky's

30 REM Dance of the Sugar-plum Fairy

40 REM Insert These Lines Into

50 REM PROGRAM 9.2

60

90 C1=104:C2=95:C3=86

230 Tempo=2

250 ENVELOPE1,6,0,0,0,0,0,0,126,-8,0,-

8,126,94

260 ENVELOPE2,4,0,0,1,1,0,1,100,-10,-1

0,-10,100,70

270 ENVELOPE3,3,0,0,0,0,0,0,116,-8,0,-

8,116,84

275 ENVELOPE4,6,0,0,1,1,0,1,100,-5,-5,

-5,100,80

276 ENVELOPE5,1,0,0,1,1,0,1,100,-5,-1,

-10,100,50

330 IF Note$="R" Env=0 ELSE IF N=C1 En

v=5 ELSE IF N<17 Env=2 ELSE Env=1

450 IFNote$="R" Env=0 ELSE IF N=C2 Env

=5 ELSE IF N>73 Env=4 ELSE Env=2

570 IF Note$="R" Env=0 ELSE IF N=C3 En

v=5 ELSE IF N<68 Env=2 ELSE Env=3

870

880 REM Channel 1

890 DATA &200,R,4,E3,4,R,4,F#3,4

900 DATA &200,R,4,G3,4,R,4,D#3,4

910 DATA &200,R,4,E3,4,R,4,F#3,4

920 DATA &200,R,4,G5,2,E5,2,G5,4,F#5,4

930 DATA &200,D#5,4,E5,4,D5,2,D5,2,D5,

4

940 DATA &200,B4,2,E5,2,C5,2,E5,2,B4,4

,R,4

950 DATA &200,R,4,G4,2,E4,2,G4,4,F#4,4

960 DATA &200,B4,2,E5,2,C5,2,E5,2,B4,4

,R,4

970 DATA &200,R,4,G4,2,E4,2,G4,4,F#4,4

980 DATA &200,C5,4,B4,4,G5,2,G5,2,G5,4

990 DATA &200,F#5,2,F#5,2,F#5,4,E5,2,E

5,2,E5,4

1000 DATA &200,D#5,2,F#5,2,E5,2,F#5,2,D

#5,4,R,4

1010 DATA &200,R,4,G5,2,E5,2,G5,4,F#5,4

1020 DATA &200,D#5,4,E5,4,D5,2,D5,2,D5,

4

1030 DATA &200,C#5,2,C#5,2,C#5,4,C5,2,C

5,2,C5,4

1040 DATA &200,B4,2,E5,2,C5,2,E5,2,B4,4

,R,4

1050 DATA &200,R,4,E4,2,C#4,2,E4,4,D#4,

4

1060 DATA &200,R,4,D4,2,B3,2,D4,4,C#4,4

1070 DATA &200,R,4,C4,2,A3,2,C4,4,B3,4

1080 DATA &200,R,4,B3,1,D#4,1,F#4,1,B4,

1,E4,4,B2,4

1090

1100 REM Channel 2

1110 DATA &200,E2,4,G2,4,E2,4,A2,4

1120 DATA &200,E2,4,A#2,4,E2,4,A2,4

1130 DATA &200,E2,4,G2,4,E2,4,A2,4

1140 DATA &200,E2,4,A#2,4,E2,4,A2,4

1150 DATA &200,E2,4,B2,4,E2,4,C3,4

1160 DATA &200,E2,4,C#3,4,E2,4,D3,4

1170 DATA &200,E2,4,E3,4,E2,4,F#3,4

1180 DATA &200,E3,4,E3,4,E3,4,E2,1,D2,1

,C2,1,B1,1

1190 DATA &200,A#1,4,C3,4,A1,4,C3,4

1200 DATA &200,G1,4,B2,4,F#1,4,A#2,4

1210 DATA &200,F#2,4,B2,4,F#2,4,C#3,4

1220 DATA &200,B1,4,C2,4,B1,4,B1,1,A1,1

,G1,1,F#1,1

1230 DATA &200,E1,4,B2,4,E2,4,C3,4

1240 DATA &200,E2,4,C#3,4,E2,4,D3,4

1250 DATA &200,E2,4,E3,4,E2,4,F#3,4

1260 DATA &200,E3,4,E3,4,E3,4,G3,1,F#3,

1,E3,1,D3,1

1270 DATA &200,C#3,4,F#2,8,F#3,1,E3,1,D

#3,1,C#3,1

1280 DATA &200,B2,4,E2,8,E3,1,D3,1,C#3,

1,B2,1

1290 DATA &200,A2,4,D2,8,D3,1,C3,1,B2,1

,A2,1

1300 DATA &200,G2,4,F#2,4,E2,4,B0,4

1310

1320 REM Channel 3

1330 DATA &200,R,4,B2,4,R,4,C3,4

1340 DATA &200,R,4,C#4,4,R,4,C4,4

1350 DATA &200,R,4,B2,4,R,4,C3,4

1360 DATA &200,R,4,C#4,4,R,4,C4,4

1370 DATA &200,R,4,G2,4,R,4,A2,4

1380 DATA &200,R,4,C#4,4,R,4,C4,4

1390 DATA &200,R,4,G2,4,R,4,A2,4

1400 DATA &200,G3,4,A3,4,G3,4,R,4

1410 DATA &200,R,4,E2,4,R,4,D#2,4

1420 DATA &200,F#4,4,E4,4,A#4,2,A#4,2,A

#4,2

1430 DATA &200,G#4,2,G#4,2,G#4,4,F#4,2,

F#4,2,F#4,4

1440 DATA &200,B2,4,A#2,4,F#2,4,R,4

1450 DATA &200,R,4,G2,4,R,4,A2,4

1460 DATA &200,R,4,A#2,4,R,4,B2,4

1470 DATA &200,R,4,C#3,4,R,4,D#4,4

1480 DATA &200,G3,4,A3,4,G3,4,R,4

1490 DATA &200,R,4,A#3,2,F#3,2,A#3,4,A3

,4

1500 DATA &200,R,4,G#3,2,E3,2,G#3,4,G3,

4

1510 DATA &200,R,4,F#3,2,D3,2,F#3,4,G3,

4

1520 DATA &200,R,4,A2,4,E2,4,B1,4

The envelopes have been redefined and re-allocated, but otherwise this just supplies new data to Program 9.2

10 REM PROGRAM 9.6

20 REM John Philip Sousa's

30 REM The Liberty Bell March

40 REM Insert These Lines Into

50 REM PROGRAM 9.2

90 C1=174:C2=207:C3=173

250 ENVELOPE1,1,0,0,0,0,0,0,50,50,-2,-

2,50,126

260 ENVELOPE2,1,0,0,0,0,0,0,32,-8,-2,-

4,126,90

270 ENVELOPE3,1,0,0,0,0,0,0,16,-8,-2,-

4,126,90

275 ENVELOPE4,4,0,8,-8,0,1,1,110,0,0,-

10,110,110

276 ENVELOPE5,2,1,0,1,0,1,1,16,-8,-2,-

4,126,90

277 ENVELOPE6,1,1,0,1,0,1,1,32,-8,-2,-

4,126,90

305 IF N=65 RESTORE 930

330 IF Note$="R" Env=0 ELSE IF (N=1 OR

N=78 OR N=93 OR N=108 OR N=162) Env=4 E

LSE Env=1

425 IF N=79 RESTORE 2560

450 IF Note$="R" Env=0 ELSE IF N=C2-1

Env=1 ELSE IF N>78 Env=5 ELSE Env=3

545 IF N=71 RESTORE 3050

570 IF Note$="R" Env=0 ELSE IF N=130 E

nv=4 ELSE IF N=C3-1 Env=1 ELSE Env=2

880 REM Channel 1

890 DATA &200,F4,60,&1000,R,9

900

910

920 DATA C3,3

930 DATA &200,A2,6,A2,3,A2,3,G#2,3,A2,

3

940 DATA &200,F3,6,C3,3,C3,6,A2,3

950 DATA &200,A#2,6,A#2,3,A#2,6,C3,3

960 DATA &200,D3,15,A#2,3

970 DATA &200,G2,6,G2,3,G2,3,F#2,3,G2,

3

980 DATA &200,E3,6,D3,3,D3,6,A#2,3

990 DATA &200,A2,6,A2,3,A2,6,A#2,3

1000 DATA &200,C3,15,C3,3

1010 DATA &200,A2,6,A2,3,A2,3,G#2,3,A2,

3

1020 DATA &200,A3,6,F3,3,F3,6,C3,3

1030 DATA &200,B2,6,G3,3,G3,6,G3,3

1040 DATA &200,G3,15,F3,3

1050 DATA &200,E3,6,G3,3,G3,3,F#3,3,G3,

3

1060 DATA &200,D3,6,G3,3,G3,3,F#3,3,G3,

3

1070 DATA &200,C3,6,B2,3,C3,6,B2,3

1080 DATA &200,C3,9,C3,9

1090 REM 2nd Part

1100 DATA &200,A2,3,G#2,3,A2,3,D3,6,C3,

3

1110 DATA &200,A2,9,F2,9

1120 DATA &100,D2,9,G2,9

1130 DATA &100,F2,15,F2,3

1140 DATA &200,G2,3,A2,3,A#2,3,E3,6,D3,

3

1150 DATA &200,C3,9,F3,9

1160 DATA &200,E3,9,D3,9

1170 DATA &200,C3,15,C3,3

1180 DATA &200,D3,6,D3,2,E3,1,D3,3,C#2,

3,D3,3

1190 DATA &200,E3,9,E3,9

1200 DATA &200,F3,6,F3,2,A3,1,G3,3,F3,3

,G3,3

1210 DATA &200,A3,15,A3,2,A3,1

1220 DATA &200,G3,6,F3,3,D3,6,A#2,3

1230 DATA &200,A2,9,F2,9

1240 DATA &200,G2,9,E2,9

1250 DATA &200,F2,12,R,6

2500

2510 REM Channel 2

2520 DATA &200,F2,6,E2,3,D#2,6,D2,3

2530 DATA &100,C2,6,B1,3,A#1,6,A1,3

2540 DATA &100,G1,3,A1,3,A#1,3,A1,6,G1,

3

2550 DATA &100,C1,6,R,12

2560 DATA &200,F1,6,F2,3,F2,6,F2,3

2570 DATA &200,F1,6,F2,3,F2,6,F2,3

2580 DATA &200,E1,6,F1,3,G1,6,F1,3

2590 DATA &200,E1,6,D1,3,C1,6

2600 DATA &200,C1,6,A#1,3,A#1,6,A#1,3

2610 DATA &200,C1,6,A#1,3,A#1,6,A#1,3

2620 DATA &200,F1,6,G1,3,A1,6,G1,3

2630 DATA &200,F1,6,E1,3,D1,6,C1,3

2640 DATA &200,F1,6,F2,3,F2,6,F2,3

2650 DATA &200,F1,6,F2,3,F2,6,F2,3

2660 DATA &200,D1,6,E1,3,F1,6,E1,3

2670 DATA &200,D1,6,C1,3,B0,6,A1,3

2680 DATA &200,G1,6,E2,3,E2,6,E2,3

2690 DATA &200,G1,6,F2,3,F2,6,F2,3

2700 DATA &200,C2,6,T1,3,C2,6,G1,3

2710 DATA &200,C1,6,R,3,C2,6,R,3

2720 REM 2nd Part

2730 DATA &200,F1,6,A1,3,C1,6,A1,3

2740 DATA &200,F1,6,A1,3,C1,6,A1,3

2750 DATA &100,A#1,6,F1,3,C1,6,E1,3

2760 DATA &100,F1,6,A1,3,A1,6,A1,3

2770 DATA &200,E1,6,A#1,3,C1,6,A#1,3

2780 DATA &200,F1,6,A1,3,D1,6,G#1,3

2790 DATA &200,C1,4,F1,2,G1,3,D1,4,F1,2

,G1,3

2800 DATA &200,C1,6,G1,3,G1,6,81,3

2810 DATA &200,B0,6,B1,3,B1,6,B1,3

2820 DATA &200,C#1,6,A1,3,A1,6,A1,3

2830 DATA &200,D1,6,A1,3,D1,6,A#1,3

2840 DATA &200,C1,6,A1,3,A1,6,A1,3

2850 DATA &200,G1,6,A#1,3,A#1,6,A#1,3

2860 DATA &200,C1,6,A1,3,A1,6,A1,3

2870 DATA &200,E1,6,A#1,3,C1,6,A#1,3

2880 DATA &200,F1,6,R,3,F1,6,R,3

2890

3000 REM Channel 3

3010 DATA &200,F3,6,E3,3,D#3,6,D3,3

3020 DATA &100,C3,6,B2,3,A#2,6,A2,3

3030 DATA &100,G2,3,A2,3,A#2,3,A2,6,G2,

3

3040 DATA &100,C2,6,R,12

3050 DATA &200,F2,6,C2,3,C2,6,C2,3

3060 DATA &200,F2,6,C2,3,C2,6,C2,3

3070 DATA &200,G2,6,G2,3,G2,6,G2,3

3080 DATA &200,G2,15,R,3

3090 DATA &200,G1,6,C2,3,C2,6,C2,3

3100 DATA &200,G1,6,C2,3,C2,6,C2,3

3110 DATA &200,F2,6,F2,3,F2,6,G2,3

3120 DATA &200,A2,15,A2,3

3130 DATA &200,F2,6,C2,3,C2,6,C2,3

3140 DATA &200,F2,6,C2,3,C2,6,C2,3

3150 DATA &200,D2,9,B1,9

3160 DATA &200,B1,6,C2,3,D2,6,R,3

3170 DATA &200,G2,6,C2,3,C2,6,C2,3

3180 DATA &200,G2,6,B1,3,B1,6,B1,3

3190 DATA &200,E2,6,R,3,G1,6,R,3

3200 DATA &200,E2,6,R,3,E2,6,R,3

3210 REM 2nd Part

3220 DATA &200,R,18

3230 DATA &200,F4,54

3240

3250

3260 DATA &200,R,6,C2,3,R,6,C2,3

3270 DATA &200,R,6,A1,3,R,6,G#1,3

3280 DATA &200,G2,9,F2,9

3290 DATA &200,R,6,C2,3,C2,6,C2,3

3300 DATA &200,F2,6,D2,3,D2,6,D2,3

3310 DATA &200,R,6,C#2,3,C#2,6,C#2,3

3320 DATA &200,R,6,D2,3,R,6,D2,3

3330 DATA &200,R,6,E2,3,E2,6,E2,3

3340 DATA &200,R,6,G2,3,G2,6,G2,3

3350 DATA &200,R,6,C2,3,C2,6,C2,3

3360 DATA &200,R,6,C2,3,C2,6,C2,3

3370 DATA &200,F2,6,R,3,F2,6,R,3

This makes uses of the RESTORE function for repeats and several envelopes for tone variation. The trill envelope is also used to good effect.
Notice the empty data lines, where a note lasts for more than one bar, and the necessary change in sync parameters in the other channels. The empty fines are there purely to aid readability.
Notice also the use of the dummy hold parameter to allow the initial trill to fade rather than cease abruptly.


Next     Top