Structures in AMPLE – part 2

Kevin Doyle
There are three main types of structure available in AMPLE:
a) Loops;
b) Conditionals;
c) Conditional loops.
I will examine each of these in turn over the next few issues of AMPLINEX and give examples of their use. In this issue I will look at conditionals.

Conditionals in AMPLE

One of the facilities available to the AMPLE programmer, which is absent from many non-language based computer music packages, is the ability to interact with the listener by passing him or her a degree of control as the music is playing. Another is the ability to introduce random elements into the music which can generate a different result each time it is played.
To develop either of these techniques fully the use of conditional structures will become necessary.
Many people will be familiar with the conditional structure in BASIC of -
IF test THEN action1 ELSE action2
where 'test' is normally an expression (e.g. X=2 or A$="Y") which is evaluated to be TRUE or FALSE. IF it is TRUE then 'action1' is performed; if FALSE then 'action2' is performed.
In AMPLE the equivalent form is -
test IF( action1 )ELSE( action2 )IF
so that the result of evaluating the expression 'test' is either ON (true) or OFF (false). If the result is ON then 'action1' is performed; if OFF then 'action2' is performed.
So, using the AMPLE expression #= which tests the equivalence of two numbers, the following trivial example can be constructed:
"equal" [
#= IF( "Yes" )ELSE( "No" )IF $OUT
]
which takes two numbers and reports whether they are equal. Thus, entering '3 4 equal' gives the result 'No' and entering '5 5 equal' gives the result 'Yes'.
Using more complex conditions it is possible to construct an interactive music program. Suppose you have written four different melody lines for a particular player in a piece. Normally you may decide on a suitable sequence for these and then construct a word to play them in that order. So, if the melody lines were stored as mdy1, mdy2, mdy3 and mdy4, and were to be assigned to player 1 you might create:
"part1" [ mdy1 mdy3 mdy4
          mdy2 mdy1 mdy3 ]
to play the melodies in the sequence you wanted.
To allow the listener (or yourself) to control the order of the melody lines as the piece is playing, you could construct the following word:
"part1" [
6 FOR(
 "Choose a melody (1-4) " $OUT
 #IN
 #11 49 #= IF( mdy1 )ELSE(
 #11 50 #= IF( mdy2 )ELSE(
 #11 51 #= IF( mdy3 )ELSE(
 #11 52 #= IF( mdy4 )IF)IF)IF)IF
 #2
 )FOR
]
This word uses 'nested' conditional structures to test for four possibilities. When the message "Choose a melody (1-4)" is displayed the AMPLE word #IN is used to wait for a keypress and to return its ASCII number. The AMPLE word #11 is then used to duplicate this number. This is because the following test (#=) 'uses' the two numbers supplied to produce the ON or OFF flag and the keypress number may be needed for further tests if the result of the first test is OFF.
The first test takes the keypress number and compares it to 49 (the ASCII code for "1"). If they are equal this will leave the ON flag and the action between the IF( and the )ELSE( statements will be performed - in this case the word 'mdy1'.
If the keypress number is not equal to 49 then the action after the )ELSE( statement will be performed - in this case this is another IF()ELSE()IF structure.
Again the keypress number is first duplicated for further use and then a test is performed to see if it is equal to 50 (the ASCII code for "2"). If it is then 'mdy2' is performed, otherwise another IF()ELSE()IF begins.
This happens two more times and at the end each of the four conditional structures is terminated by its )IF statement. In this case all four occur together.
Finally, there will be a redundant keypress number left over from one of the conditional structures and this must be discarded using the AMPLE word #2.
In this example the keypress tests are performed six times to generate a sequence of six melody lines.
There are, however, two problems with this example as it stands. Firstly, if another key is pressed other than 1-4 then nothing will happen. To get round this problem we could change the structure so that a default melody is chosen, for example:
"part1" [
6 FOR(
 "Choose a melody (1-4) " $OUT
 #IN
 #11 49 #= IF( mdy1 )ELSE(
 #11 50 #= IF( mdy2 )ELSE(
 #11 51 #= IF( mdy3 )ELSE(
               mdy4 )IF)IF)IF
 #2
 )FOR
]
which would select 'mdy4' if neither 1, 2 nor 3 were selected.
Alternatively, we could construct a loop which would repeat the question until an answer within the range 1-4 was returned. I will look at this type of conditional loop in the next issue.
The second problem with these examples is that of timing. The word 'part1' waits for a keypress in order to select a melody line - but this might mean that the melody begins at the wrong point in the piece or that no melody is played at all.
It might be easier in this case to let the system select the melody lines as it requires them but still allow for variation in the melody lines played.
To achieve this we can use the random number generating facilities of AMPLE.
In the above example we could replace the keypress number with a random number generated by AMPLE. To do this we can use the AMPLE word RANDL.
This takes the form:
n RANDL
where n is a number in the range -32768 to 32767 and which produces a number in the range 0 to n inclusive.
The four melody example above might then become:
"part1" [
6 FOR(
 3 RANDL
 #11 0 #= IF( mdy1 )ELSE(
 #11 1 #= IF( mdy2 )ELSE(
 #11 2 #= IF( mdy3 )ELSE(
 #11 3 #= IF( mdy4 )IF)IF)IF)IF
 #2
 )FOR
]
Here the random number range is 0 to 3 giving the four options required. Using RANDL means there is no need to consider the possibility of a number outside this range as there was with the first keypress example.
There are two other AMPLE words related to random numbers. The first, RAND, generates a random number in the range -32768 to 32767. The second, RAND!, allows a 'seed' number to be set for random number generation so that the same sequence is produced by the use of RAND or RANDL. So in the example above we could allow the listener to enter a number and thereby generate a 'random' but repeatable sequence of melody lines. Thus:
"Enter a number :"$OUT #IN RAND!
could be inserted at the start of the piece, perhaps in the 'RUN' word before the players begin.
By careful setting of random number limits and use of conditional structures complex randomly generated pieces can be created. The tests used in these conditional structures may themselves be complex and there are several logical operators in AMPLE to enable such tests to be performed.
The operators are shown below with truth tables for their results and examples of their use.
AND        | OFF | ON  |
       –––––––––––––––––
       OFF | OFF | OFF |
       –––––––––––––––––
       ON  | OFF | ON  |
       –––––––––––––––––
E.g. to test if a number n is in the range 3 to 7:
n #11 2 #> #12 8 #< AND
(#12 swaps two numbers - in this case it swaps the result of the first test with the second copy of the number n.)
OR         | OFF | ON  |
       –––––––––––––––––
       OFF | OFF | ON  |
       –––––––––––––––––
       ON  | ON  | ON  |
       –––––––––––––––––
 E.g. to test if a number n is either greater than 10 or less than 5:
n #11 10 #> #12 5 #< OR
XOR        | OFF | ON  |
       –––––––––––––––––
       OFF | OFF | ON  |
       –––––––––––––––––
       ON  | ON  | OFF |
       –––––––––––––––––
E.g. to test whether either, but not both, of two numbers n1 and n2 are equal to 3:
n1 n2 3 #= #12 3 #= XOR
NOT        |     |
       –––––––––––
       OFF | ON  |
       –––––––––––
       ON  | OFF |
       –––––––––––
E.g. to test whether a number n is not equal to 12:
n 12 #= NOT

In the next I will be looking at conditional loops using the AMPLE expression REP( )UNTIL( )REP.

Published in AMPLINEX 002, November1987