AMPLE 0.30 language description
===============================

The basic structure of AMPLE programs
-------------------------------------

AMPLE programs consist of a collection of "words". These words can be either
system words, which are provided by AMPLE, or user words, which are built up
from system words as part of the program. In turn, user words can contain
other user words as well as system words, until there is one user word
(usually called RUN) which contains the entire AMPLE program. AMPLE programs
are therefore constructed in a hierarchical way from low-level system words
up to the top-level user word.

Each user word is defined in the AMPLE program text as follows:

"UserWord"[....system or previously-defined user words....]

The name of the new user word is enclosed in double quotes. The opening
square bracket indicates the start of the definition of the new user word,
and the closing square bracket indicates the end of the same definition.
Within the square brackets it is possible to place system words, user words,
numbers or text strings.

Once UserWord has been defined, it is then possible to use it in
later definitions of other user words, for example:

"HigherUserWord"[........UserWord.........]

Word definitions can extend over more than one line - carriage returns are
generally ignored by AMPLE, except for AMPLE comments (see later).

 The last user word in the program will usually be called
"RUN" (though it does not have to be called that) and will be the word that
in effect contains the entire program.

An important point to note is that there is technically no difference between
system words and user words; once a user word has been defined it behaves in
exactly the same way as a system word, and can be used in exactly the same
way.

The AMPLE numeric stack
-----------------------

Each word takes its numeric arguments from a numeric stack. If a word
requires an argument, it is placed before the word in the AMPLE program, for
example:

"HigherUserWord"[......10 UserWord.......]

In this example, the number 10 will be the top entry on the stack before
UserWord is called, and UserWord can take this number from the stack and
process it. UserWord may also leave numbers on the numeric stack when it
completes.

As an example, the AMPLE system word #+ adds the top two numbers on the
stack, and leaves the result on the stack. Therefore:

2 3 #+

will take the two numbers 3 and 2 from the stack in that order, add them,
then put the result 5 on the stack. It would be possible to define a user
word that added 5 to the number on the top of the stack as follows:

"Add5"[5 #+]

This user word would rely on there being a number already on the stack when
it was called, as follows:

10 Add5 NOUT

The AMPLE system word NOUT takes the number from the top of the stack and
displays it in the AMPLE window. So this sequence of words would, in order:

- Put the number 10 on the top of the numeric stack;
- Call Add5, which removes the number, adds 5 to it and puts the result 15 on
the top of the stack;
- Display the number in the AMPLE window.

Note that it is illegal to call Add5 before it has been defined.

AMPLE players
-------------

In order to achieve concurrency for playing music, AMPLE supports up to 32
"players", numbered 1 to 32. These are in effect separate processes that run
in parallel, and allow multi-part music to be performed. These players are
known as the "dynamic" players, since they are created after the program has
started to execute.

Each player has its own numeric stack, but all word definitions are global,
so any player can use any word.

A player is given a number, and a sequence of words to execute, bracketed
between P(...)P. For example, this would declare player number 1:

1 P(...)P

Once all players are defined, the system word GO will start all players off
simultaneously. So a typical four-part (say) music composition will be
defined as follows:

"RUN"[
1 P(....player 1 material....)P
2 P(....player 2 material....)P
3 P(....player 3 material....)P
4 P(....player 4 material....)P
GO]

Again, as before, carriage returns are not significant.

The static player
-----------------

Player 0 is the "static" player, which exists all the time and does not have
to be created using P(...)P. The "RUN" word is executed within player 0,
and is used to create and configure the dynamic players, and start them off
using GO. It can also play music, but will not do so until GO is issued.

In BBC AMPLE, the static player accepted commands from the user, even while a
program was running. Since RISC OS AMPLE does not have a command line, the
static player does not have this function.

Typically, the static player terminates once all dynamic players have been
created, configured and started.

The AMPLE string stack
----------------------

AMPLE supports a global string stack that is shared between all players.
Strings are enclosed in double quotes, and when a string occurs in an AMPLE
program, it is put onto the top of the string stack. For example, to display
a text string in the AMPLE window:

"Display this string" $OUT

The AMPLE system word $OUT removes the string from the top of the string
stack and displays it. Some simple string operations are provided by AMPLE
system words (see later).

It is important to be aware that the string stack is shared between all
players. If more than one player uses the string stack to store strings on,
then when AMPLE switches to another player the string stack will not contain
what that player was expecting to find there.

The AMPLE music model
---------------------

As already stated, AMPLE supports up to 32 players, which can run in
parallel. There are also 32 "ensembles" of 16 "voices" each, one of which is
associated with a player at any one time. Normally, a player number X will
own ensemble number X, but this can be altered with the SHARE system word.

AMPLE voices
------------

A "voice" is a monophonic sound source which can either be RISC OS internal
or a single MIDI channel. A player can select a subset of voices to use with
the VOICES system word, since a player would not normally need all 16 voices.

AMPLE player settings
---------------------

Settings such as tempo apply globally to the whole orchestra of players, but
volume settings apply to all the voices within a player, and can be different
between players. Other parameters such as duration, accent level, staccato
level and key signature also apply to the player, not individually to each
voice.

AMPLE notation
--------------

The notes A-G are expressed in AMPLE notation using the letters A-G and a-g.
An upper-case note indicates that the specified note is of a higher pitch
than the note previously, and a lower-case note indicates a lower pitch. The
following sequence of notes plays "Happy Birthday" in C major (ignoring
rhythm for now):

GGAgCb ggAgDc ggGecba FFecDc^

This line also illustrates what happens if two identical note letters
immediately follow each other, for example the first two notes of "Happy
Birthday"; the note is repeated. The ^ symbol at the end is an AMPLE system
word meaning "play a rest" - in this case it stops the voice sounding. If
this was omitted, then the last note would not be silenced. You can also use
^ in the midst of note sequences to play rests.

Note that in this example, all notes are of the same duration, so the rhythm
of the tune is absent.

Adding rhythm and changing durations
------------------------------------

To extend the duration of a note without re-sounding it, AMPLE provides the
symbol / which is a system word meaning "hold the note for the same
duration", or just "hold". Using this to elaborate "Happy Birthday" to
aproximate the true rhythm more closely:

GGA/g/C/b/// ggA/g/D/c/// ggG/e/c/b/a/// FFe/c/D/c/^

Imagine that a note A followed by / is twice as long as a note without /.
Similarly, a note b followed by /// is four times as long. It is also quite
legal in AMPLE to use / after ^ to extend the duration of a rest.

It is possible to alter the duration of a note using the AMPLE system word ,
(comma) which is preceded by a number indicating the number of time 'ticks'
(duration units). Conventionally, the value 48 is used for a crotchet
(quarter note) since this is easily subdivided, but any value can be used.

Using , to change duration, we can achieve a more realistic rhythmic
performance:

36,G 12,G 48,AgC 96,b 36,g 12,g 48,AgD 96,c 36,g 12,g 48,Gecba 36,F 12,f
48,ecDc^

Obviously this is more tedious than using / and usually a mix of use of / and
, is employed in practice:

12,G//G 48,AgCb/ 12,g//g 48,AgDc/ 12,g//g 48,Gecba 12,F//F 48,ecDc^/

Setting the octave and transposition
-----------------------------------

It is possible to specify the octave explicitly at which a musical line will
start. This is done using the AMPLE system word : preceded by the octave
number.

The expression 0: sets the octave to start at Middle C, so 0:C will play
Middle C, and 0:B will play the B above Middle C. -1:C will play the C below
Middle C and 1:C will play the C above Middle C.

0:c is equivalent to -1:C.

In order to fix the pitch of "Happy Birthday" to start on the G above Middle
C we do the following:

0:12,G//G 48,AgCb/ 12,g//g 48,AgDc/ 12,g//g 48,Gecba 12,F//F 48,ecDc^/

If we wish to transpose up or down by a whole octave, it is more convenient
to use the AMPLE system word !. The following example:

0:48,CGC!C^

is exactly equivalent to:

0:48,CGC 2:C^

Also:

0:48,Cgc!c^

is exactly equivalent to:

0:48,Cgc -2:C^

! can be used more than once to add extra octaves of transposition:

0:48,Cgc!!c^

However, this feature does not work with AMPLE versions earlier than 0.24.

It is also possible to transpose an entire voice up or down by a set number
of semitones using the AMPLE system word @ preceded by a value indicating the
number of semitones. 1@ will transpose the line up by one semitone, and -1@
will transpose down by one semitone.

Sharps and flats
----------------

The AMPLE system words + and - sharpen or flatten the following note by one
semitone respectively. Therefore +F means F sharp, and -B means B flat. Note
that the sign only applies to the following note, therefore in a repeated
sequence of F sharp, each note must be preceded by +:

+F+F+F+F+F

Mutliple sharp or flat can be expressed by repeating the sign, ie ++F means F
double-sharp.

It is important to note that the + or - must IMMEDIATELY PRECEDE the note. It
is an error to write such as the following:

+ 0:F

This must be re-written as follows to work:

0:+F

This is because RISC OS AMPLE combines the + and - words with the note
information on compilation, and therefore + and - are not "standalone" words.
This is a difference compared to BBC AMPLE.

Key signatures and naturals
---------------------------

Since most tonal music is written in a key which involves one or more sharps
or flats, AMPLE provides a way of specifying that particular notes are
normally sharp or flat.

The system words K( and )K specify a key signature for the player. Between K(
and )K we specify the notes that are to be sharp or flat by default. The
following specifies a key signature of D major/B minor (F sharp and C sharp):

K( +F +C )K

It is possible to use non-standard key signatures as well, as are found in
some twentieth-century music.

This means that if the note F appears in a musical part for the player, the
system will play F sharp instead of F. If an explicit sharp or flat sign
appears before the note, this will override the key signature. In addition,
the AMPLE system word = specifies a 'natural', which would also override the
key signature.

Bars and barlines
-----------------

AMPLE supports the use of bars and barlines, which can be used when
transcribing standard music notation.

To specify (say) 3 beats per bar, you would use the following:

3 BAR

This takes the current duration (set by ,) and calculates the total duration
of a single bar. Bars are delimited using the AMPLE system word |.

AMPLE makes no distinction between 3/4 time and 3/8 time - it is only
the absolute number of durations in a bar that is important.

When the barline | is executed by AMPLE, a check is made that the durations
that have been executed since the last barline matches the expected bar
length. If they do not match, then AMPLE will raise an error and stop
executing the program.

Re-expressing "Happy Birthday" in 3/4 time using barlines:

48,3 BAR ^^12,G//G|48,AgC|b/12,g//g|48,AgD|c/12,g//g|48,Gec|ba12,F//F|
48,ecDc^/|

In this case, a bar is 48*3 ticks long, ie 144. Note that two rests are
placed at the start of the first bar to make the bar add up.

The use of bars is entirely optional. If 0 BAR is specified (as it is by
default) then even if barlines are present in the music, they are ignored.

AMPLE Chords
------------

AMPLE expresses chords using a notation as follows:

C(EG)

This means that a three-note chord is played, with C at the bottom, E above
it and G above E, ie a C major triad in root position. To play this chord,
the player would have to be allocated three voices (more on this later).

A sequence of three-note chords would be notated thus:

48,2 BAR C(EG)D(FA)|E(GC)d(FA)|c(EG)^;|

This produces a rising then falling sequence of chords. Note that the
relative pitch of the chord depends only on the note before the parentheses,
and not at all on the notes within the parentheses. Whether a note is upper
or lower-case within the parentheses simply determines its relationship with
the root note, or the previous note within the parentheses. It is
conventional for notes within parentheses to be upper-case, but this is not a
requirement.

Rests ^ and holds / are also permissible within chord parentheses, as are
almost all AMPLE words, in fact; however, it is not wise to put too many
words into chord parentheses.

The AMPLE system word ^; means "chord rest", which plays a rest on all
voices within a player without having to know how many voices it has. It is
equivalent to ^(^....^) where the number of rests in parentheses can be
variable.

In fact, ^; will start from the current music voice, so it is possible to
silence all voices from voice 2 upwards as follows:

2;^;

^; can also be used to silence all notes within a chord, thus:

C(EGC)g(^;)

though the () word (see below) is more convenient for this.

RISC OS AMPLE also provides the word (), which means "silence all chord
notes", which is equivalent to (^....^). The root note is not affected by ().
This means that the following, for example:

C(EGC)g(^^^)D(FAD)g(^^^)E(GCE)g(^^^)C^

can be more helpfully and conveniently written:

C(EGC)g()D(FAD)g()E(GCE)g()C^

Note that the notes within parentheses do not contribute towards the total
bar count.

AMPLE broken chords
-------------------

It is possible to achieve a "broken chord" effect by putting a duration
specification using , within the parentheses:

48,2 BAR C(4,EG)D(4,FA)|E(4,GC)d(4,FA)|c(4,EG)^;|

This only applies within the parentheses, and does not affect the original
duration setting.

Setting and changing volume
---------------------------

The following AMPLE system words are used to control volume within a player:

=L  - set volume
+L  - increase volume (crescendo)
-L  - decrease volume (diminuendo)

The volume level is between 0 (very quiet) and 127 (very loud) inclusive. So,
to set a mid-range volume level, use the following:

64 =L

To define a crescendo, a change in volume plus a note count must be
specified:

64 4 +L

This means "increase the volume by 64 units over 4 notes". So, if the current
duration set by , is 48, this means that the crescendo will take place over
48*4 = 192 ticks.

A diminuendo works identically:

64 4 -L

This means "decrease the volume by 64 units over 4 notes".

Setting and changing tempo
--------------------------

The following AMPLE system words are used to control tempo over the whole
orchestra:

=T  - set tempo
+T  - increase tempo (accelerando)
-T  - decrease tempo (rallentando or ritardando)

When setting the tempo using =T the value supplied refers to the metronome
mark. This means that 60=T means "1 beat = MM 60" or "1 beat per second", and
120=T means "1 beat = MM 120" or "2 beats per second". For those who are not
familiar with metronome marks, the value is the number of ticks per minute.

To define a gradual increase in tempo, a change in tempo plus a note count
must be specified:

64 4 +T

The change in tempo is on an exponential scale, where -64 halves the tempo,
zero does not change it, and 64 doubles it. This means that it is possible to
change tempo, and get right back to the original tempo merely by specifying
the same change, but the opposite sign. The range of this value is -127 to
127 inclusive.

The exact formula for calculating the percentage tempo change is as follows:

dT% = 100*(2^(change / 64))

ie 2 to the power of (change divided by 64), all multiplied by 100.
Therefore, a change value of 64 will give a new tempo that is 200% of the
old, and -64 will give 50%.

To define a rallentando:

64 4 -T

This will slow the music down to half its previous speed over 4 notes.

Again this uses an exponential scale. In fact, 64 4 -T is exactly equivalent
to -64 4 +T.

Readying the system
-------------------

The AMPLE system word READY initialises the entire system to a default value
before any players are created. It should be used at the start of the "RUN"
word, and not inside P(...)P.

All GVAR variables are set to zero, and all dynamically allocated memory is
freed.

Configuring AMPLE players
-------------------------

As already stated, a player is declared using the AMPLE system words P( and
)P. Between these words the program will need to configure the player for
performance of a musical line.

Players each possess one ensemble at a time, which contains 16 voices. The
player has to select a range of voices from the ensemble to configure, and
this is done using the AMPLE system word VOICES:

1 P(3 VOICES ...)P

This selects voices 1-3 inclusive of the player's ensemble. By default, these
are set up as internal voices, and so will play through the internal sound
system. To make these MIDI voices:

1 P(3 VOICES MIDIV ...)P

They can be explicitly declared as internal voices in the following way:

1 P(3 VOICES INTERNALV ...)P

This is useful if you wish to change a voice from being a MIDI voice to being
an internal one.

Once the voices are selected, various things can be done to configure them.
They can be assigned MIDI channels and MIDI programs (patches), or they can
be assigned internal channels and patches (confusingly called "voices" in
RISC OS parlance).

To set up Player 1 as a four-voice player with all voices playing on MIDI
channel 1 with MIDI program 1:

1 P(4 VOICES MIDIV 1 MIDICHANNEL 1 MIDIPROGRAM ...)P

If you omitted the MIDIV, the program would still run, but the sound would
come from the internal speaker, and not through your MIDI instrument.

It is also possible to select individual voices using the AMPLE system word
VOICE; this allows different configuration for different voices within the
same ensemble.

1 P(2 VOICES MIDIV 1 VOICE 1 MIDICHANNEL 2 VOICE 2 MIDICHANNEL ...)P

When configuring internal voices, we have to assign them to internal sound
channels, of which there are 8. Unlike MIDI, where it is quite possible to
have several voices all playing into one MIDI channel, it is not possible to
have more than one voice per channel. In this case, we would use VOICE
to allocate one voice to one channel at a time.

This player definition allocates 3 voices to 3 separate sound channels, and
programs their stereo position and patch:

1 P(3 VOICES INTERNALV
1 VOICE 1 CHANNEL -127 STEREO "StringLib-Pluck" CHANNELVOICE
2 VOICE 2 CHANNEL    0 STEREO "StringLib-Soft"  CHANNELVOICE
3 VOICE 3 CHANNEL +127 STEREO "StringLib-Steel" CHANNELVOICE
)P

It would not be sensible to do this:

1 P(3 VOICES 1 CHANNEL ...)P

since we would be trying to play 3 notes on a single RISC OS sound channel.
The program would run, but the musical result would not be as you might
expect!

Playing notes
-------------

Once a player has been configured in this way, it is then possible to
allocate some music to it. The AMPLE system word SCORE is used to initialise
the musical parameters to default values for the player, and should always be
used before any notes are played or volume set.

Here is "Happy Birthday" again, this time as as complete "RUN" word. Note the
use of the word GO after the dynamic player has been declared. This word will
play the tune over MIDI channel 1 with MIDI program 1:

"RUN"[
READY 120=T
1 P(1 VOICES MIDIV 1 MIDICHANNEL 1 MIDIPROGRAM SCORE 64=L 3
BAR 0: 48,^^12,G//G|48,AgC|b/12,g//g|48,AgD|c/12,g//g|48,Gec|ba
12,F//F|48,ecD|c^/ )P
GO]

Here is an alternative version playing the tune over RISC OS internal
sound channel 1 using the "StringLib-Pluck" sound:

"RUN"[
READY 120=T
1 P(1 VOICES 1 VOICE 1 CHANNEL 0 STEREO "StringLib-Pluck" CHANNELVOICE
SCORE 64=L 3 BAR 0:
48,^^12,G//G|48,AgC|b/12,g//g|48,AgD|c/12,g//g|48,Gec|ba 12,F//F|48,ecD|c^/
)P GO]

The GO word can only be used from player 0, ie it is illegal to use it within
P(...)P statements. Without GO, no music will be heard, since this is what
starts the timebase running and allows notes to be played.

Selecting the music voice
-------------------------

If a player has (say) three voices configured, then by default the main line
of music will be played on voice 1, and chords will be played on voices 2 and
3. It is possible to change the voice on which the main line of music will be
played using the AMPLE system word ; as follows:

"RUN"[
READY 120=T
1 P(4 VOICES
1 VOICE 1 CHANNEL 0 STEREO "StringLib-Pluck" CHANNELVOICE
2 VOICE 2 CHANNEL 0 STEREO "WaveSynth-Beep"  CHANNELVOICE
3 VOICE 3 CHANNEL 0 STEREO "StringLib-Soft"  CHANNELVOICE
4 VOICE 4 CHANNEL 0 STEREO "Percussion-Noise"CHANNELVOICE 
SCORE 64=L 3 BAR 0:48,
1;^^12,G//G|48,AgC|b/0,^
2;12,g//g|48,AgD|c/ 0,^
3;12,g//g|48,Gec|ba 0,^
4;12,F//F|48,ecD|c^/
)P GO]

This will give a somewhat less conventional version of "Happy Birthday",
where each phrase will sound different. Note the use of a zero-length rest at
the end of each phrase; this will silence the voice at the end of the
phrase without affecting the overall rhythm of the tune.

Articulation of notes
---------------------

AMPLE supports accenting and staccato effects on individual notes. Preceding
a note with ' (apostrophe) will accent it, and preceding it with . (full
stop) will cause it to be played staccato.

The degree of accenting and staccato are settable. The AMPLE system word 'L
allows the accent level to be set to the number preceding it, for example:

64=L 20'L 48,0:CDE'Gfed'c^

The accented notes here will be played with a dynamic level 20 units above
the normal level (64).

It is also possible to use more than one accent before a note - this will
multiply the accent level by the number of accents, for example:

64=L 20'L 48,0:CDE'Gfed''c^

The last note will be accented by 40 units instead of 20.

Setting the degree of staccato is done using the AMPLE system word =. where
the number preceding it gives the fraction of the full note duration that
staccato will cause. The fraction is a number in the range 1-12 inclusive,
and is internally divided by 12 to obtain the fraction. For example:

64=L 6=. 48,0:.C.D.E.G.f.e.dc^

Here, each staccato note will last 6/12 of the full duration, ie half of 48,
which is 24. Setting either 12=. or 0=. will turn off staccato. The fraction
is maintained, even if the full duration (set by ,) is altered.

Note that explicit staccato notation is new to RISC OS AMPLE, and was not
supported on BBC AMPLE.

SCORE sets the following default values:

15'L 6=.

Hits
----

The symbol X denotes a 'hit', which is in effect a pitchless note. It is used
to send percussive events, but in every other way is identical to a note.

The pitch sent by X is in fact the last pitch setting for that voice, which
will have been set by the normal A-G notes. This value can also be set
explicitly using the PITCH system word.

AMPLE pitch numbers
-------------------

The pitch numbers used by AMPLE define 0 as middle C, and 16 divisions per
semitone. Therefore:

0:C 	is 0 PITCH
1:C	is 12*16 = 192 PITCH
-1:C	is -192 PITCH

To set the pitch used by X, simply precede X with a PITCH value:

1 VOICE 192 PITCH XXXX

Setting the pitch is useful when using General MIDI percussion sounds on
channel 10, because the actual sound that is played depends on the MIDI
pitch.

Proportional rhythms
--------------------

RISC OS AMPLE supports a method of specifying proportional rhythms (ie 5 in
the time of 4, 9 in the time of 8, etc) with AMPLE automatically adjusting
the length of the notes to fit. This is done using the system word PROP:

48, 5 4 PROP 0:CDEFG c^

5 4 PROP tells AMPLE that the next 48*5 = 240 ticks are to be played in the
time of 48*4 = 192 ticks. AMPLE automatically recalculates the duration of
the next 240 ticks' worth of notes to ensure that they fit into 192 ticks.

It is also possible to change note duration within the range of a PROP:

48, 5 4 PROP 0:CD 24,EFGf 48,G c^

This will still work, since the proportionality will take effect over five
notes' worth at duration 48.

It is not possible to 'nest' proportional rhythms, so the following will not
work as expected:

48, 7 4 PROP 0:CDE 3 2 PROP FGf GB C^

Internal voice support
----------------------

The following system words are provided to support the RISC OS internal sound
system:

INTERNALV		- set the current voice to be internal
n CHANNEL		- specify the sound channel to be used (1-8)
"patch" CHANNELVOICE	- specify the "patch" (or "voice") to be used
n STEREO		- set the stereo position (-127 to +127)
n TUNING		- set the overall tuning (-16383 to +16383)
n VOLUME		- set the overall volume (1 to 127)

The words CHANNELVOICE, STEREO, TUNING and VOLUME are equivalent to the RISC
OS star commands of the same name, except that CHANNELVOICE does not take the
channel number, but assumes it from the currently selected voice.

Note that CHANNELVOICE and STEREO are put into the sound queue with a program
time, just like any other event; therefore patch changes and stereo position
changes happen at the right time in the music. However, TUNING and VOLUME are
executed immediately they are encountered.

Another important point to note is that AMPLE reads in all the available
patch names for CHANNELVOICE on startup, and when a new program starts
executing. AMPLE has space to record up to 32 different patches, which is
also the RISC OS limit. When, for example:

"StringLib-Pluck" CHANNELVOICE

is executed, AMPLE translates the patch name into a number, and sends the
event plus patch number to the sound queue.

MIDI voice support
------------------

The following system words are provided to support the RISC OS MIDI sound
system:

MIDIV			- set the current voice(s) to be MIDI
n MIDILINE              - set the MIDI port (1 to 4) for the entire player
n MIDICHANNEL		- set the MIDI channel (1 to 16) for the current voice(s)
n MIDIPROGRAM	 	- set the MIDI program (1 to 128) for the current voice(s)
flag MIDIOMNI		- set MIDI OMNI mode (ON or OFF)
n MIDIMONO		- set MIDIMONO mode (base channel 1 to 16)
MIDIPOLY		- set MIDIPOLY mode
MIDIALLNOTESOFF		- send a MIDI ALL NOTES OFF command
MIDIRESET		- send a MIDI RESET command
MIDISTART		- send a MIDI START command
MIDICONTINUE		- send a MIDI CONTINUE command
MIDISTOP		- send a MIDI STOP command
n MIDIPRESSURE		- send a MIDI POLYPHONIC KEY PRESSURE command
			  (0 to 127)
n MIDICHPRESSURE	- send a MIDI CHANNEL PRESSURE command
			  (0 to 127)
cont MIDIC		- declare a MIDI controller and attach it to the current voice
value cont =MIDIC	- set a MIDI controller value for the current music voice
			  (value 0 to 127, cont 0 to 127)
chg beats cont +MIDIC	- increment a MIDI controller value over a period
			  on the current music voice
			  (chg -127 to +127, cont 0 to 127)
chg beats cont -MIDIC	- decrement a MIDI controller value over a period
			  on the current music voice
			  (chg -127 to +127, cont 0 to 127)
MIDIP			- declare a MIDI pitch wheel and attach it to the current voice
value =MIDIP		- set the MIDI pitch wheel value for the current music voice
			  (0 to 16383 [&3FFF hex], 8192 [&2000 hex] default)
chg beats +MIDIP	- increment the MIDI pitch wheel value over a period
			  on the current music voice
			  (chg -16363 to +16383)
chg beats -MIDIP	- decrement the MIDI pitch wheel value over a period
			  on the current music voice
			  (chg -16383 to +16383)
pitch MIDIPITCH		- convert from MIDI pitch value to internal AMPLE pitch			  

To declare a voice to be MIDI, and to set various parameters:

1 VOICES MIDIV 1 MIDILINE 1 MIDICHANNEL ON MIDIOMNI 1 MIDIPROGRAM

Usually MIDILINE will not be required, since if only one MIDI podule is
installed there will be only one MIDILINE anyway. You will require two or
more MIDI podules to use more than one MIDILINE.

It is not possible to have more than one MIDILINE per player, but it is quite
possible to have several voices in one player all connected to different MIDI
channels.

Since most words require a MIDI channel to be included (the exceptions are
MIDIRESET, MIDISTART, MIDICONTINUE and MIDISTOP) it is advisable to use
MIDICHANNEL as soon as possible after MIDIV to ensure that MIDI commands go
to the right MIDI channel. These commands will take their MIDI channel from
the currently selected voice(s).

Many instruments will not respond to MIDIPRESSURE (which takes the last
MIDI note on the current voice as part of the command) but they should
respond to MIDICHPRESSURE (which works over all notes on the channel).

MIDI controllers
----------------

Each player can control up to 16 MIDI controllers; these can be from any
of the 128 controllers supported by MIDI. If a MIDI controller is to be
modified by a voice, it should be first declared as attached to that voice:

1 P(1 VOICES MIDIV 1 MIDICHANNEL 1 VOICE 1 MIDIC ...

This will attach a MIDI controller number 1 to the first voice (which is
attached to MIDI channel 1). This will "declare" the MIDI controller to the
player, which will insert it in its MIDI controller table.

To set it to a specific value, use the system word =MIDIC, for example:

64 1 =MIDIC

This will set MIDI controller 1 to the value 64 on the current music voice,
only if a controller of that number has been attached to that voice. If not,
then AMPLE will give an error.

This ability to attach MIDI controllers to specific voices allows a single
player to control various MIDI controllers on different channels merely by
selecting one of its voices before setting the controller value:

1 P(2 VOICES MIDIV
1 VOICE 1 MIDICHANNEL 1 MIDIC 2 VOICE 2 MIDICHANNEL 2 MIDIC
...
SCORE 1; 64 1=MIDIC 2; 32 2=MIDIC

To increase the value of the controller over time:

48, 32 4 10 +MIDIC

This will increase the value of MIDI controller 10 by 32 units over 48*4
ticks. Another example:

48, 32 4 10 -MIDIC

This will decrease the value of the same MIDI controller by 32 units over
48*4 ticks.

The +MIDIC and -MIDIC words work similarly to +L, -L, +T and -T in terms of
their operation over time.

Note that using +MIDIC and -MIDIC without a =MIDIC earlier in the player will
cause an error, since AMPLE does not know what value the MIDI controller
should start at.

MIDI pitch wheel
----------------

The MIDI pitch wheel words again work the same way:

1 P(1 VOICES MIDIV 1 MIDICHANNEL 1 VOICE MIDIP ...

This will "declare" a MIDI pitch wheel to the specified voice.

To set the position of the wheel:

&2000 =MIDIP

This will set the MIDI pitch wheel to value &2000, which is the centre
position, on the current music voice. If that voice does not have the MIDI
pitch wheel attached, then AMPLE will give an error.

48, &1000 4 +MIDIP

This will increase the pitch wheel value by &1000 units in 48*4 ticks on the
current music voice. Another example:

48, &500 4 -MIDIP

This will decrease the pitch wheel value by &500 units in 48*4 ticks. It is
more convenient to use hexadecimal when working with the MIDI pitch wheel.

MIDI pitch numbers
------------------

MIDI uses its own pitch numbering system, which numbers pitches in semitones,
with Middle C being number 60. However, AMPLE uses a different format (see
"AMPLE pitch numbers" above) with Middle C being number 0.

The MIDIPITCH system word is the same as the PITCH system word, but uses MIDI
pitch numbers instead of internal AMPLE pitch numbers. For example:

60 MIDIPITCH

is exactly equivalent to:

0 PITCH

This is useful in conjunction with "hits" to produce MIDI percussion sounds
via General MIDI channel 10, since General MIDI assigns different percussion
sounds to different MIDI pitches on this channel.

Music actions
-------------

AMPLE allows powerful control over the processing of note events by the use
of music actions. These allow the nine parameters associated with any event
to be modified as each event is generated.

The nine parameters associated with every note event are as follows, in
reverse order:

9	Program time
8	Time flag
7 	Event duration
6	Gate ON/OFF flag
5	Gate voice
4	Dynamic level
3	Level voice
2	Pitch
1	Pitch voice

The voice parameter values will obviously depend on the currently selected
voice, but for certain types of event a voice parameter may be zero (OFF).

The time flag (parameter 8) is either ON or OFF, and specifies whether or not
the event duration is added into the player's program time. This is useful
for producing "echo" effects where the extra "echo" note will not affect the
player's program time. This parameter will usually be ON for normal notes,
and OFF for staccato rests and events inside chord parentheses.

The program time (parameter 9) is necessary to support staccato notes,
since a staccato note sends two events; a note which is processed at once,
and a rest which is processed in the future. However, it can also be useful
for adjusting the absolute time position of events on the fly.

Parameters 8 and 9 are new to RISC OS AMPLE; BBC AMPLE only used the first
seven parameters.

A note (A-G, a-g) outside chord parentheses will pass the following nine
parameters:

9	Program time
8	ON
7	Event duration
6	ON
5	Current voice, or OFF if preceded by ~
4	Dynamic level
3	Current voice, or OFF if preceded by ~
2	Pitch
1	Current voice

A note (A-G, a-g) inside chord parentheses will pass the following nine
parameters:

9	Program time
8	OFF
7	Event duration
6	ON
5	Current voice, or OFF if preceded by ~
4	Dynamic level
3	Current voice, or OFF if preceded by ~
2	Pitch
1	Current voice

A rest (^) outside chord parentheses will pass the following nine parameters:

9	Program time
8	ON
7	Event duration
6	OFF
5	Current voice
4	Undefined
3	OFF
2	Undefined
1	OFF

A rest (^) inside chord parentheses will pass the following nine parameters:

9	Program time
8	OFF
7	Event duration
6	OFF
5	Current voice
4	Undefined
3	OFF
2	Undefined
1	OFF

A hold or back hold (/ or \) outside chord parentheses will pass the
following nine parameters:

9	Program time
8	ON
7	Event duration
6	Undefined
5	OFF
4	Undefined
3	OFF
2	Undefined
1	OFF

A hold or back hold (/ or \) inside chord parentheses will pass the following
nine parameters:

9	Program time
8	OFF
7	Event duration
6	Undefined
5	OFF
4	Undefined
3	OFF
2	Undefined
1	OFF

In RISC OS version 0.23 or later, chord start (open parenthesis) will pass the following nine parameters:

9	Program time
8	ON
7	Event duration to retract (will be negative)
6	Undefined
5	OFF
4	Undefined
3	OFF
2	Undefined
1	OFF

In RISC OS version 0.23 or later, chord end (close parenthesis) will pass the following nine parameters:

9	Program time
8	ON
7	Event duration required to catch up
6	Undefined
5	OFF
4	Undefined
3	OFF
2	Undefined
1	OFF

A hit (X) outside chord parentheses will pass the following nine parameters:

9	Program time
8	ON
7	Event duration
6	ON
5	Current voice
4	Dynamic level
3	Current voice
2	Undefined
1	OFF

A hit (X) outside chord parentheses will pass the following nine parameters:

9	Program time
8	OFF
7	Event duration
6	ON
5	Current voice
4	Dynamic level
3	Current voice
2	Undefined
1	OFF


A staccato note or hit (a note or hit preceded by .) will pass the normal
nine parameters for the event, but immediately followed by the nine
parameters to declare a 'staccato rest', regardless of whether or not it is
within chord parentheses:

9	Program time+staccato offset
8	OFF
7	Staccato rest duration
6	OFF
5	Current voice
4	Undefined
3 	OFF
2 	Undefined
1	OFF

This is the order in which they are put onto the numeric stack, with
parameter 1 on the top of the stack.

Defining a music action
-----------------------

A music action is defined thus:

10 ACT( ... )ACT

The number defines where in the chain of music actions this action is placed.
The lower-numbered actions are executed first, followed by the
higher-numbered actions.

The parameters can be accessed using the FVAR system word:

1 FVAR		leaves the address of parameter 1
2 FVAR	  	leaves the address of parameter 2

so to read the program time from within a music action:

9 FVAR #?

or to set the time flag to OFF:

OFF 8 FVAR #!

It is also possible to set all the voice parameters (1, 3 and 5) to the same
value using the system word VOICE!. For example:

3 VOICE!

will set parameters 1, 3 and 5 to the value 3.

The system word ACT within the music action will cause the next action in the
chain to be called:

10 ACT( ... ACT ... )ACT

If there are no more actions in the chain, then ACT will cause the event to
be executed.

Prefixing the action with 0 will delete it from the chain:

0 ACT( ... )ACT

The system word SIMPLEACT will delete all music actions from the player, and
restore the default music action:

10 ACT( ACT )ACT

which simply executes the event.

AMPLE program flow control
--------------------------

Though AMPLE is unique in being able to directly play music, it is also a
conventional programming language in that it supports conditional structures,
loops and variables like other languages such as C or BASIC. AMPLE also
supports limited mathematical capabilities (but no floating-point).

Conditional structures
----------------------

AMPLE supports the IF...THEN...ELSE...ENDIF conditional structure found in
other languages by using the following syntax:

<condition> IF( ...do this if TRUE... )ELSE( ...do this if FALSE... )IF

The condition is simply a value on the stack, where -1 is TRUE and zero
is FALSE. The )ELSE( can be omitted if there is no need to do anything if the
condition is FALSE.

The condition is put there by various comparison system words
provided by AMPLE:

#>    	- greater than
#<    	- less than
#=    	- equal to
#<=	- less than or equal to
#>=	- greater than or equal to

The last two of these are not provided in BBC AMPLE.

These take two values from the stack, perform the comparison and leave a
result behind on the stack. For example:

10 5 #>

would leave -1, since 10 is greater than 5.

10 5 #<

would leave zero, since 10 is not less than 5.

6 6 #>=

would leave -1.

The system word NOT inverts the condition on top of the stack:

10 5 #> NOT

would leave 0, since the #> leaves -1, which is converted to 0 by NOT.

10 5 #< NOT

would leave -1, since the #< leaves zero, which is converted to -1 by NOT.

In fact, NOT will convert any non-zero value to zero, and will convert zero
to -1.

The system word SIGN takes a number from the top of the stack and leaves -1
if the number is negative, and 0 if the number is zero or positive. It is
equivalent to 0 #< but faster:

-6 SIGN

would leave -1.

Here is an example of using IF(...)ELSE(...)IF with comparisons:

"Test"[ SIGN IF( "Less than zero" $OUT )ELSE( "Greater than zero" $OUT )IF ]

Therefore:

-1 Test

would print out "Less than zero", and:

1 Test

would print out "Greater than zero".

AMPLE also provides two words, ON and OFF, which leave -1 or 0 on the
numeric stack respectively. In fact, any non-zero value is considered to be TRUE.

Repeating loops
---------------

The AMPLE systems words REP( and )REP specify that any words between the two
should be repeated indefinitely:

"NoExit"[ REP( "Print this out over and over again" $OUT NL )REP ]

This will print the text over and over again on the display, and the only
way to stop it is to click on the Stop button.

The NL system word simply moves to the start of the next line on the display,
and the SP system word prints out a single space.

It is possible to break out of the loop using the AMPLE system word )UNTIL(.
This takes a value from the top of the numeric stack, and if it is non-zero
will jump to the closing )REP.

The )UNTIL( can be placed anywhere within the REP(...)REP structure; in fact
it is quite possible to have more than one )UNTIL( in the loop.

Here is a simple use of REP(...)UNTIL(...)REP:

"WaitForSpace"[ REP( #IN 32 #= )UNTIL( "Please press SPACE!" $OUT NL )REP ]

This word waits for the space bar to be pressed, and will only exit when it
is. #IN waits for a single key to be pressed, and this is compared with the
ASCII code for a space, which is 32. If the comparison is TRUE, then the
)UNTIL( word will jump to the closing )REP.

IDLE
----

With the REP(...)REP structure, it is possible that a player can be held up
waiting for something to happen. In certain circumstances, this will also
hold up other players that may be running concurrently, and freeze the
playback of music. To avoid this, AMPLE provides the system word IDLE, which
allows other players to have a share of the processor's time:

REP( ...... )UNTIL( ... IDLE ...)REP

Every time IDLE is executed, the program will change from this player to
other players to allow them to run for a short period. When control returns
to this player, the REP(...)REP loop is executed again.

IDLE also permits Wimp_Poll to be called, allowing other programs running on
the RISC OS desktop to have some processor time.

The AMPLE words #IN and $IN (see later) both call IDLE internally, so there
is no need to use it in conjunction with these words. However, if the player
was, for example, checking for a particular keypress using QKEY (see
later) then the player would have to call IDLE explicitly to allow other
players to run while it was waiting for the mouse button to be pressed. If
this was not done, then not only would other players be held up but the
entire RISC OS desktop would be frozen as well until that key was pressed.

Various other AMPLE words call IDLE internally; all note/rest/hold events do
so for example. This is how AMPLE achieves its multitasking; it is in fact a
co-operative system like the RISC OS desktop. Players give up the processor
when certain words are executed, and this allows the next player to run for a
while, until it in turn relinquishes the processor to the next, and so on.

FOR(...)FOR loops
-----------------

The AMPLE construct FOR(...)FOR allows a section of code to repeated a set
number of times:

"Print5Stars"[5 FOR( "*" $OUT )FOR NL]

This word will print out 5 asterisks in a row on the display. The FOR( word
takes the top number from the numeric stack and uses this as the loop count.

"Print5By5Stars"[5 FOR( 5 FOR( "*" $OUT )FOR NL )FOR NL]

This word illustrates the use of "nested" loops (loops within loops) and will
print out a 5 by 5 box of asterisks on the display.

From version 0.21 of AMPLE, it is possible to exit prematurely from a
FOR(...)FOR loop using )UNTIL(, as for REP(...)REP.

FOR(...)FOR is useful in a musical context for repeating musical sections or
phrases.

Stack operators
---------------

As stated, AMPLE relies on its numeric stack to pass values into and out
of words, be they system or user words. AMPLE provides various words for
manipulating values directly on the stack, which can be very useful.

The words are named in such as way as to illustrate their action on the
following hypothetical stack:

5 4 3 2 1

where 1 is on the top of the stack, 2 below 1, 3 below 2 etc.

The words are:

#11	- duplicate the top number on the stack
#12	- swap the top two numbers on the stack
#2 	- discard the top number
#212	- duplicate the number below the top number
#2121	- duplicate the top two numbers
#213	- rotate the positions of the top three numbers

Taking our hypothetical stack:

5 4 3 2 1 #11	will leave	5 4 3 2 1 1
5 4 3 2 1 #12	will leave	5 4 3 1 2
5 4 3 2 1 #2	will leave	5 4 3 2
5 4 3 2 1 #212	will leave	5 4 3 2 1 2
5 4 3 2 1 #2121	will leave	5 4 3 2 1 2 1
5 4 3 2 1 #213	will leave	5 4 2 1 3

The two most useful of these words are #11 and #2. #11 is useful when doing
multiple comparisons, for example:

"WaitForSpaceOrReturn"[
REP( #IN #11 32 #= )UNTIL( #11 13 #= )UNTIL(
#2 "Please press SPACE or RETURN!" $OUT NL )REP #2 ]

In this case, the word waits for either the space bar or the RETURN key to be
pressed. #11 duplicates the keypress on the top of the numeric
stack so that a value is available for later comparisons. This word also
illustrates the use of multiple )UNTIL( words in one REP(...)REP structure.

"WaitForAnyKey"[#IN #2]

This word waits for any key to be pressed, then discards the keypress from
the top of the numeric stack.

Arithmetic operators
--------------------

AMPLE does not support floating point arithmetic, but does support basic
integer arithmetic. All integers are signed, and are 32 bits in size (unlike
BBC AMPLE, which used 16-bit integers). Numbers can be expressed in decimal
(the default) and hexadecimal; hexadecimal numbers are prefixed with an
ampersand. Numbers can be printed in hexadecimal using the system word &NOUT:

&30 NOUT		prints 48
&30 &NOUT		prints 30
48 &NOUT		prints 30

Note that &NOUT does not prefix the output with an ampersand.

The arithmetic operators provided are:

#+	- add the top two numbers and leave the result
#-	- subtract the top two numbers and leave the result
#*	- multiply the top two numbers and leave the result
#/	- divide the top two numbers and leave the quotient and remainder
MAX	- compare the top two numbers and leave the largest
MIN	- compare the top two numbers and leave the smallest
#>>	- shift right by n places
#<<	- shift left  by n places

Some examples of these words in use:

5 3 #+ NOUT		prints 8

5 3 #- NOUT		prints 2

3 5 #- NOUT		prints -2

5 3 #* NOUT		prints 15

5 3 #/ NOUT		prints 2 (the remainder)

5 3 #/ #2 NOUT		prints 1 (the quotient)

5 3 MAX NOUT		prints 5

5 3 MIN NOUT		prints 3

1 4 #<< NOUT		prints 16 (1 shifted left by 4)

128 4 #>> NOUT		prints 8  (128 shifted right by 4)

Logical operations
------------------

AMPLE supports the following bit operations:

AND	- logical AND the top two numbers and leave the result
OR	- logical OR  the top two numbers and leave the result
XOR	- logical exclusive-OR the top two numbers and leave the result

Some examples of these words in use:

5 1 AND NOUT		prints 1

4 8 OR NOUT		prints 12

5 1 XOR NOUT		prints 4

It is possible to use these operators with the comparison words (see above)
to perform more complex comparisons, as follows:

"RangeCheck"[
#11 5 #> #12 10 #< AND IF("In range" $OUT )ELSE( "Out of range" $OUT )IF ]

This word takes a value on the numeric stack and checks that it is greater
than 5 AND less than 10. Since two copies of the test number are
required, we need the #11 at the start of the word. Note also the need for
#12, to ensure that the result of #> is put below the test number on the
stack.

8 RangeCheck	will print	In range

11 RangeCheck	will print	Out of range

3 RangeCheck	will print	Out of range

Variables
---------

AMPLE does provide a facility whereby values can be stored in variables,
though this is not so commonly used in AMPLE as in other languages.

A variable is declared using the system word GVAR (global variable) as
follows:

"Variable"[GVAR]

When the program is started, and when READY is executed, all GVAR variables
are initialised to zero. This is different to BBC AMPLE, where GVAR variables
were undefined.

A value (say 5) is stored to Variable using the system word #! as follows:

5 Variable #!

This value is read out using the system word #? as follows:

Variable #? NOUT	will print	5

The system word #+! adds a value to a variable as follows:

5 Variable #! 10 Variable #+! Variable #? NOUT

This sequence of words stores 5 in Variable, adds 10 to it, and prints it
out.

Variables are the best way of implementing communication between players.
Since each player has a private numeric stack, it is not possible to transfer
data between players except by using variables.

String stack operations
-----------------------

AMPLE provides some system words which operate on the top strings on the
global string stack for processing strings.

The $+ system word concatenates two strings on the string stack into one:

"bar" "foo" $+ $OUT			will print	foobar

The $- system word splits a string at a specified character position, and
leaves the left part of the string on top of the stack, with the right part
of the string underneath:

"foobar" 3 $- $OUT SP $OUT		will print	foo bar

The "foo" is on the top of the stack, and "bar" is underneath.

The $2 system word discards the top string from the string stack.

"foobar" 3 $- $2 $OUT			will print	bar

The $12 system word swaps the two strings on the top of the string stack:

"foobar" 3 $- $12 $OUT SP $OUT		will print	bar foo

The $REV system word reverses the string on the top of the string stack:

"foobar" $REV $OUT			will print	raboof

The $? system word puts the address of the string on the top of the string
stack onto the numeric stack without removing the string from the stack. This
is useful for SYS calls (see later) or for direct in-memory manipulation of
strings:

"foobar" "F" ASC $? #B! $OUT		will print	Foobar

$? is new to RISC OS AMPLE.

Numeric and string conversions
------------------------------

For the purposes of converting between numeric data and string data, AMPLE
provides various system words.

The system words $CHR and ASC convert between ASCII codes and strings, and
vice-versa. $CHR will leave a single-character string on the string stack,
and ASC will convert the first character of the string to its ASCII code, and
leave that on the numeric stack.

65 $CHR $OUT			will print	A
"A" ASC NOUT			will print	65

VAL converts between a string and a decimal number. If the string on the top
of the string stack starts with decimal digits, these are converted to a
number and put onto the numeric stack together with an ON flag. If, however,
VAL was unable to find any digits to convert, then it will leave an OFF flag.

"65AG" VAL NOUT SP NOUT		will print	-1 65

"AG65" VAL NOUT			will print	0

The equivalent word &VAL converts between a string and a hexadecimal number:

"65AG" &VAL NOUT SP NOUT	will print	-1 1626
"65AG" &VAL #2 &NOUT		will print	65A

VAL and &VAL will stop conversion when they reach an invalid decimal or
hexadecimal digit, ie the letter G in the above examples. &VAL will not,
however, ignore a leading ampersand, and will treat this as an invalid digit.
Leading spaces will also be treated as invalid digits. Note that neither VAL
nor &VAL will convert negative numbers.

The opposite conversion (a number to a string) is performed by the two words
$STR and &$STR, one doing decimal conversion and the other doing hexadecimal
conversion:

65 $STR $OUT		will print	65
1626 &$STR $OUT		will print	65A

It is possible to prefix a hex number with an ampersand as follows:

1626 &$STR "&" $+ $OUT	will print	&65A

$STR will convert a negative number so that it is correctly prefixed with a
minus sign, whereas &$STR works in unsigned values only:

-65 $STR $OUT		will print	-65
-65 &$STR $OUT		will print	FFFFFFBF

String input
------------

AMPLE provides the system word $IN to input a line of text from the user. In
RISC OS AMPLE this line of text is typed in using a writable icon in the
AMPLE window, below the volume and tempo sliders. When AMPLE executes $IN,
this area becomes highlighted and a caret appears. Any text that is typed in
will be collected into a string and put on top of the string stack.

Here is an example word using $IN:

"Greeting"["What is your name?" $OUT NL $IN "Nice to meet you, " $+ $OUT NL]

This word prompts you for your name (say John) and when you have typed it in
AMPLE prints out "Nice to meet you, John".

$IN internally calls IDLE, so If one player is waiting for input from the
user, other players are not held up. However, care has to be taken if the
waiting player has music to play, since the timebase will have clearly
advanced some way before the waiting player continues. In this case, the
player's notes are played rapidly in order to catch up.

Number input
------------

AMPLE does not provide a numeric equivalent to $IN. What has to be done is
that a string is input using $IN, and VAL or &VAL is used to convert it to a
number.

To overcome the possible problem of leading spaces being present at the start
of an input string, AMPLE provides the system word $STRIP which removes any
leading spaces from the string. In order to be safe it is best to always
process input strings with $STRIP before converting to a number.

"Input"[$IN $STRIP VAL IF( ON )ELSE( "Error: invalid number" $OUT NL OFF )IF]

This word inputs a string, converts it to a number and checks if it is valid.
If it is invalid, then an error message is printed and OFF is returned,
otherwise ON is returned, with the number itself below ON.

Formatting number output
------------------------

AMPLE provides the system word $PAD which performs the opposite function to
$STRIP, in that it adds spaces to the start of the string on the top of the
string stack. $PAD also takes a number which determines the total length of
the string.

For example:

"foobar" 10 $PAD		will print	....foobar

where . represents a space.

$PAD can be used to tabulate numbers by printing them in a fixed-size field
on the screen:

"FPrint"[$STR 10 $PAD $OUT]

This word will print out a number right-justified into a 10-character field:

20 FPrint		will print	........20
1500 FPrint		will print	......1500

again, where . represents a space.

Character input and output
--------------------------

The system word #IN waits for a single keypress, and puts the ASCII code of
the key onto the numeric stack. We have seen this example of its use earlier:

"WaitForSpace"[ REP( #IN 32 #= )UNTIL( "Please press SPACE!" $OUT NL )REP ]

#IN internally calls IDLE, so it does not hold up other players.

The system word #OUT outputs the character whose ASCII code is on the numeric
stack. For example, &2A #OUT will print an asterisk. ASCII codes less than 32
are filtered out and have no effect, except for codes 10 and 13.

The system word QKEY scans the keyboard directly for either any key, or a
prticular key. If QKEY has a zero argument, then it will scan for any key,
whereas a value between 1 and 127 inclusive will scan for a particular key.
The RISC OS 3 Programmer's Reference Manual page 1-849 gives a full
description of the key numbers for RISC OS 3; more important ones are listed
below:

ESCAPE			112
SPACE			98
RETURN			73
LEFT CTRL		4
RIGHT CTRL		7
LEFT SHIFT		3
RIGHT SHIFT	 	6
LEFT ALT		5
RIGHT ALT		8
BACKSPACE		47
Left mouse button	9
Middle mouse button	10
Right mouse button	11
F1-F4			113, 114, 115, 20
F5-F8			116, 117, 22, 118
F9-F12			119, 30, 28, 29

Scanning for F12 using 29 QKEY will work, but will also cause the command
shell to be entered, as is standard on RISC OS machines. Once the user
returns from the command shell, the AMPLE program would continue as normal.

QKEY does not wait for the key to be pressed; it returns -1 on the stack if
the key is pressed at the moment of the scan, or 0 if not. It is normally
used in a REP(...)REP loop to wait for a key to be pressed:

"WaitCtrl"[REP( 4 QKEY )UNTIL( 7 QKEY )UNTIL( IDLE )REP]

Note the use of IDLE; if IDLE is not used, not only would the other players
be held up, but the entire RISC OS desktop would be also.

It is important to note that QKEY bypasses the RISC OS desktop and goes
straight to the kernel for its keypress (in fact, it calls OS_Byte 129). This
means that it will not fit in with the traditional desktop way of working; in
particular, an AMPLE program waiting for a keypress using QKEY will pick up
that keypress even if the AMPLE window does not have the current input focus.

Dynamic memory storage
----------------------

The AMPLE system word DIM reserves a block of 32-bit words in a special area
of memory, and returns a pointer to that area. For example:

"Block"[100 DIM]

reserves a block of 101 32-bit words (numbered 0 to 100 inclusive) and
returns a pointer to the first word. This is useful enough in itself, but it
is usually used in conjunction with the system word ARRAY, which allows the
DIM area to be used as a single dimension array.

This is how an array of 101 words is declared:

"Array"[100 DIM ARRAY]

and this is how to assign the value 20 to (say) the 5th element:

20 5 Array #!

and this is how to read (say) the 10th element:

10 Array #?

ARRAY performs range checking, so that the following would be faulted:

102 Array #?

Array element 0 is available and valid, but is usually not used. Arrays
normally are considered to start at 1, though if you are a C programmer you
will be used to starting arrays at 0.

Number lists
------------

This is a new feature within RISC OS AMPLE, and provides a way of declaring a
fixed list of constant 32-bit words at run-time. For example, the following
would declare a list of the Fibonacci numbers:

"FibList"[ { 1 1 2 3 5 8 13 21 34 } ]

The braces { } enclose the list, and the first brace { causes a pointer to
the first word in the list to be put onto the numeric stack. Note that the
numbers in the list itself are not stored on the stack, but are in fact
stored within the user dictionary.

To access (say) the 7th item in the list, which is the value 13:

7 FibList #?

It is possible to modify the list at run-time using the #! word, for example:

100 7 Fiblist #!

to change 13 to 100. However, it must be stressed that the list is NOT
restored back to its default values when either READY is executed, or when
the program is started. It is set to its default values only when the program
is loaded in and compiled. For this reason, it is strongly advised that lists
are not modified at run-time, unless you know what you are doing.

This feature has been added to support, in the future, MIDI System Exclusive
messages, where sequences of values are sent out to MIDI synthesisers to
configure them. The SysEx feature is not, however, supported in the current
version of AMPLE.

RISC OS SWI calls
-----------------

RISC OS AMPLE supports calling of SWIs from within AMPLE using the following
words:

SYS	- call a SWI based on its number
$SYS	- call a SWI based on its name

So to call the OS_Byte SWI for example, you could use:

6 SYS

or:

"OS_Byte" $SYS

Before SWIs are called, ARM registers usually have to be set with particular
values. This is done using the AMPLE words R? and R!, which are also new with
RISC OS AMPLE.

To set register R0 with the value &100 before calling a SWI:

&100 0 R!

and to read register R0 after a SWI has returned:

0 R?

To set registers to point to strings, we can use the $? word, as in the
following example:

"PrintSWINum"[$? 1 R! "OS_SWINumberFromString" $SYS 0 R? &NOUT $2]

This word prints out the SWI number for the SWI name supplied. The SWI
"OS_SWINumberFromString" takes a pointer to a string in register R1,and
returns the SWI number in R0.

"MIDI_Init" PrintSWINum		will print	404E2

Take care when calling SWIs, as some of these can affect the operation of
your AMPLE program in unexpected ways.

AMPLE comments
--------------

Comments are declared in AMPLE using the % system word:

"RUN"[% Main start word
...
]

The comment lasts until the end of the line, or until the next %:

"Vln1"[%1st violin part
SCORE 0: ...
]

"Vln2"[% 2nd violin part % SCORE 0: ... ]

Comments can exist inside or outside word definitions.

Displaying banners
------------------

The DISPLAY system word uses comments to display a banner:

"Banner"[DISPLAY
%
% Overture to "The Marriage of Figaro"
%
% by Wolfgang Amadeus Mozart
%
]

In this case, the format is very important. If a line begins with a % symbol,
then the DISPLAY word will treat it as the next line of the banner, and will
display everything on the line, including further % symbols. As soon as a
non-% character is encountered as the first character on the line, the
DISPLAY word terminates.

Vectors
-------

RISC OS AMPLE offers a mechanism for reading the address of a user word,
which can be stored in a GVAR variable or an array. This address can then be
called, as though the user word itself was called.

This feature would be useful if the user wished to set up a list of
"vectors", which can be called depending on the value of a particular
parameter.

The system word ADDR takes the name of a user word from the string stack,
finds it in the user dictionary, and puts its address onto the numeric stack.
This address can then be stored in a variable or an array using #!. For
example:

"VectorList"[3 DIM ARRAY]
"Word1"["This is word 1" $OUT NL]
"Word2"["This is word 2" $OUT NL]
"Word3"["This is word 3" $OUT NL]

"SetupVector"[
"Word1" ADDR 1 VectorList #!
"Word2" ADDR 2 VectorList #!
"Word3" ADDR 3 VectorList #!]

In this case, the array VectorList contains the addresses of Word1, Word2 and
Word3 respectively.

ADDR will raise an error if the word name does not exist.

The system word CALL takes an address from the numeric stack, and calls the
user word corresponding to that address. Therefore:

"CallVector"[VectorList #? CALL]
"RUN"[SetupVector 2 CallVector]

Executing the RUN word will in effect call user word Word2, and will produce
the output:

This is word 2

on the display. Of course, the value held in a vector can be altered to point
to a different user word if so desired.

ADDR and CALL should be used with care. If CALL is presented with an illegal
address, it will check that it lies within the user dictionary, but this is
not a completely foolproof test. If an illegal address is presented to CALL,
the system behaviour may be undefined.

Timebase
--------

The timebase is the timer that is driven by the AMPLESupport module to set
the speed at which music is played. It cannot be modified by an AMPLE program
except by altering the tempo, but it can be indirectly interrogated using two
system words. This is useful when it is necessary to synchronise some action
with the actual rate at which the music is played, bearing in mind that
usually the program time (ie the total number of durations so far) per player
is ahead of the timebase. A particular example of this is the display of song
lyrics in time with the music.

The AMPLE system word QTIME returns the difference between the player's
program time and the timebase. When a player is playing music normally, QTIME
is a slightly-varying positive number, meaning that the player's program time
is ahead of the timebase. When a duration is executed, the program time is
increased by the size of the duration, and as real time passes the timebase
is incremented at a steady rate depending on the tempo of the music, so QTIME
would decrement at the same rate. The aim of the AMPLE player is to keep
executing durations to stay ahead of the timebase.

If QTIME should go negative, this means that the timebase has gone ahead of
the program time. It is possible to catch up by issuing a duration equal to
what has gone by, ie by the absolute value of QTIME:

"CatchUp" [0 QTIME #- 0 MAX DURATION]

The use of MAX here is to ensure that if QTIME is in fact positive, then
a zero-length DURATION is issued instead of a negative-length one.

Specifically for the occasions where a player has to idle until the timebase
catches up with its program time (for the display of song lyrics, for
example) RISC OS AMPLE provides the system word WAIT. This will idle the
player until the timebase is within the specified number of ticks:

48 WAIT

This will idle the player until the timebase is 48 ticks or less behind the
player's program time.

0 WAIT

This will idle the player until the timebase equals the player's program
time.

-48 WAIT

This will idle the player until the timebase exceeds the player's program
time by 48 or more ticks.

Care should be taken in using WAIT in a music-performing player, since a
player performs music more accurately if it is allowed to run ahead of the
timebase. If you make it wait for the timebase to catch up with it, then the
player may "stutter" in its playback as it tries to get ahead again.

Displaying song lyrics in time with music
-----------------------------------------

This is best done using a separate player that issues non-sounding durations
or holds, uses 0 WAIT to wait for the timebase to catch up with it, then
displays the correct line from the lyric. Here is a simple example that
displays a line every fourth bar, exactly in time with the music that other
players (not specified here) are playing:

SCORE 48, 4 BAR
"Line 1" $OUT NL
^///|////|////|////| 0 WAIT
"Line 2" $OUT NL
////|////|////|////| 0 WAIT
"Line 3" $OUT NL
////|////|////|////| 0 WAIT
"Line 4" $OUT NL
////|////|////|////| 0 WAIT

and so on.

AMPLE commands
--------------

There are a few AMPLE system words that are not intended to be used within
word definitions, but are used in the text file to issue commands to AMPLE to
influence processing of the AMPLE program. These are called "commands", and
obey the same syntax as standard AMPLE system words. If a command is used
within an AMPLE word, a syntax error is reported.

The LIBRARY command instructs AMPLE to immediately load in and process the
specified AMPLE text file, for example:

"AMPLE:GMLib" LIBRARY

This command loads in and processes the file GMLib from the Library directory
within the AMPLE application directory. This is useful for including
predefined user words held in other files. A full path name can be specified
if required, with a length limit of 128 characters.

The EXEC command specifies the word name that appears in the Execute window
that is displayed when the user clicks on the Start button:

"RUN" EXEC

This is set by default to "RUN", but this command permits this to be changed.

The AUTOSTART command specifies that the supplied word name is to be executed
as soon as the AMPLE program has been successfully processed:

"RUN" AUTOSTART

When auto-starting an AMPLE program, the AMPLE status window is not
automatically displayed. However, if the program requires input from the
user, the status window is displayed.

This command permits a program to be started merely by dragging its text file
onto the AMPLE icon.

The VOLUMESLIDER and TEMPOSLIDER commands specify the start value of the
volume and tempo sliders respectively:

0 VOLUMESLIDER
32 TEMPOSLIDER

The range is -127 to 127 inclusive, with 0 meaning centre position.

Rehearsal marks
---------------

It is possible to insert rehearsal marks into the AMPLE program which can be
searched for when the program is started. The AMPLE system word * is used to
do this:

1 P( ... 1* ... 2* ... 3* ... 4* ... )P

Rehearsal mark numbers must be greater than zero, and each one should only
appear once in the entire program; it is not necessary or desirable to insert
the same mumber into every player at the same point in the music. It is,
however, quite possible to spread them around the players as the programmer
wishes.

The Execute dialogue box allows the user to enter a rehearsal mark number
from which to start playing the music. If this is done, then AMPLE will run
the program as fast as possible, and will not play any notes, until the
rehearsal mark is reached; at which point normal speed will be resumed and
notes will be played. All other types of events (tempo changes, MIDI
control changes, internal patch changes etc) are executed during the search.

Differences between RISC OS AMPLE dialect and BBC AMPLE dialect
---------------------------------------------------------------

The AMPLE dialect supported by RISC OS AMPLE is based on that specified by
BBC AMPLE Nucleus version 1.00, plus the M200 module for MIDI support. No
other BBC AMPLE modules are included in this implementation at present.

Old BBC AMPLE files are not loadable into RISC OS AMPLE at present.

All words supported by RISC OS AMPLE are given in the glossary (see the file
Glossary in the Docs directory).  Almost all BBC AMPLE commands as follows:

AMPLE
CLEAR
DELETE
FIND
INSTALL
LOAD
MCAT
MDELETE
MEM
MLOAD
MODE
MPREFIX
MSHOW
NEW
QUIT
RENAME
SAVE
SHOW
TYPE
WRITE

are not supported, since there is no command line in RISC OS AMPLE. In
addition, the OSCLI and CODE words are not supported by RISC OS AMPLE, since
SYS and $SYS effectively replace them. It is possible to define OSCLI as a
user word, should it be necessary:

"OSCLI"[$? 0 R! 5 SYS]

and this is used as follows:

"MODULES" OSCLI

Note that the use of RISC OS commands from within AMPLE in this way is
undefined; in particular, commands that display text will not display their
text in the AMPLE status window.

The * command in BBC AMPLE has been redefined as a rehearsal mark in AMPLE
0.17 and later; in earlier versions * was undefined.

Some words that can be used as commands as well, such as WIND, PAUSE, HALT
and FAST, are supported and can only be used within user words.

All Music 4000 and 5000 words are currently unsupported, since there is no
support for synthesis; this may be supported in a limited sense in a future
version of AMPLE. Most Music 2000 words are supported; the only ones that are
not supported currently are MIDIRT, MIDIOUT and MIDIWOUT.

The PLAY word is not supported by RISC OS AMPLE. Players have to be
explicitly declared using P(...)P and started using GO.

Due to the lack of a command line, AMPLE programs have to be complete when
the file is dragged to the AMPLE icon. In effect, RISC OS AMPLE is more like
a "compiler" than an "interpreter". No system or user words are permitted
outside [ ] within the program.

The following AMPLE system words are new to RISC OS AMPLE:

{
}
#<=
#>=
#<<
#>>
$?
.
=.
ADDR
CALL
CLS
PROP
R?
R!
SYS
$SYS
WAIT
CHANNEL
CHANNELVOICE
INTERNALV
SPEAKER 
STEREO 
TUNING 
VOLUME
MIDIALLOFF
=MIDIC
+MIDIC
-MIDIC
MIDICHANNEL
MIDICHPRESSURE
MIDICONTINUE
MIDILINE
MIDIMONO
MIDIOMNI
=MIDIP
+MIDIP
-MIDIP
MIDIPITCH
MIDIPOLY
MIDIPRESSURE
MIDIPROGRAM
MIDIRESET
MIDISTART
MIDISTOP
MIDIV

See the Glossary file for explanations of these words.