You should start by breaking down the sentence to be spoken into separate blocks. For example, you might have one block as the prefix, one block as the number, and one block as the suffix. You can have as many blocks as you need. Each block is processed in the sequence they appear in the file, but you can also skip blocks using the e command.

A block starts with a keyword from the following list. The block ends when the next keyword is encountered or the filenames section is reached. The keyword is used to specify which part of the input value is used by the instructions in the block. The following keywords are available:

integer Use the integer input value for this block.
decimal Use the decimals input value for this block (default 2 decimal places, always expanded with trailing zeros to the number of decimal places specified).
both Use a dummy "value" created as follows: 0 if both the integer and decimal values are zero, 1 if the only integer is non-zero, two if only the decimals are non-zero, and 3 of both are non-zero. You can use this to condition link-words such as and and point.
sign Use a dummy "value" which is 0 if the original value is positive, 1 if it is negative. The integer and decimal values passed to the algorithm are always positive.
string Use the input string value and process the block repeatedly for each character in the string.
length Use a dummy "value" which is the length of the input string.
cut This keyword is followed (on the same line) by two values which represent the starting and ending position (base 1) in the input string, which are to be used in the following blocks until overridden.

The "cut" keyword is useful for speaking from fixed-format input such as times and dates, not from variable-length amounts. For example if the string passed contains a four-digit time value followed by a concatenated six-digit date value, you could use the keyword as follows:

cut 1 4  ; consider first four digits only

....     ; command blocks to speak the time

cut 5 10 ; consider next six digits only

....     ; command blocks to speak the date

The algorithm blocks following a cut block operate only on the designated part of the input string. The cut line counts as a block when skipping blocks with the e command even though the block consists of a single line only. This feature allows the building of much more complex algorithms in a multiple-phrase file, and avoids the need to create and speak a single logical phrase from separate algorithm sets. Apart from making the output speech more uniform, this also means that a caller can skip the whole phrase by keying a single touch-tone.

A starting value of zero for the cut introduces a special block selection in which the decimals (digits following ".") only are selected for subsequent processing as a string block. In this case the ending value of the cut specifies the minimum number of digits to include, with the addition of trailing zeros only if the number of decimals in the string is fewer than this number. See the note below for more information on working with non-integer values. The decimals separator is taken to be any one of "." and the defined decimal separator based on the Windows country settings.

A starting value of a single character outside the range '0' to '9' is taken to be a delimiter character. In this case the ending value is taken to be an occurrence number, which must be from 1 to 16 inclusive.  Occurrence 1 selects the input characters, if any, which precede the first occurrence of the delimiter character in the string; occurrence two selects the characters, if any, between the first and second occurrences of the delimiter character, and so on for later occurrences. A virtual delimiter is presumed to be present at the end of the string. Your algorithm should of course cater for an empty 'cut' string if delimiters can be consecutive in your input string.

For example, for input: 12:34:56

cut : 1   ; consider hours


cut : 2   ; consider minutes


cut : 3   ; consider seconds

Each algorithm file starts with an implicit cut 1 127 block (127 being the maximum input string length) so that algorithm blocks preceding any cut block process the whole input string.

Following the keyword line come lines specifying a low and high value which will be processed by each line and the instructions for processing numbers which fall into the range. For example, a simple prefix block could be coded as:

integer      ; work with integer value of input

1 1 i32      ; use phrase 32: "there is"

0 999999 i32 ; use phrase 33: "there are"

In the above code, if the integer value is 1, the phrase there is will be added to the sentence; otherwise, the phrase there are will be added. When processing each block, as soon as one line has been matched on the low and high value it is processed and no further lines in the same block are tested.

The low and high limit values normally refer to the current value being processed. When processing string, the low and high limit values refer instead to the ASCII value of the current character of the string, and only the D, I, P and S instructions can be used.

The decimal keyword defaults to take two decimal places of the input string only. If the string contains no decimal point, the decimals value will be zero. You can change the number of decimals by assigning a new value to the DECIMALS system variable. When testing using FFAMOUNT, the override is provided by the value of the DECIMALS environment variable. This variable is not used by the main COPIAFACTS program. See the note below for more information on working with non-integer values.

The instructions on each line consist of one or more white-space separated codes. There are only ten different instructions, which will be described below:

Dn - speak the input string as digits

This instruction is used to speak the current input string as separate digits. This instruction can only be used in a string block. The string block is repeatedly processed for each character in the input string. In this case, the low and high limits refer to the ASCII value of each input character. The value n is the segment number of the voice segment containing the lowest ASCII digit in the range. For example:

string       ; work with string value

48 57    d1  ; speak 0 - 9 48='0' 49='1', etc

46 46    d11 ; speak 46='point'

In the above example, it is assumed that voice segments numbers 1 to 10 contain the spoken digits zero to nine and that voice segment 11 contains the word point.

In - individual voice segment

This instruction is used to add a single voice segment to the sentence. Often this is used for a prefix phrase or for a connecting word like and. The example already described above shows this usage:

integer      ; work with integer value of input

1 1      i32 ; use phrase 32: "there is"

0 999999 i32 ; use phrase 33: "there are"

Xn - indexed voice segment

This instruction is used to add a voice segment into the sentence based on the current value of the amount to be spoken. For example:

0 19      x1 ; select numbers zero to 19

The above example processes values between 0 and 19, using the value as an index into the segment table, starting at position n. In the example, the starting point is 1 (shown as x1), so segment 1 is used when the value is zero, segment 2 when the value is 1, and so on up to segment 20 when the value is 19.

/n - Divide original value

This instruction takes the original input value for this block and divides it by the number specified to produce a new current value of the quotient. For example:

20 99 /10 x19 ; (incomplete example)

This takes an input value between 20 and 99 and divides it by ten to produce a new "current" value between 2 and 9, then uses this as an index to select a phrase between 21 and 28. Presumably, segment number 21 contains the word twenty and segment 28 contains the word ninety.

Note that this instruction always works on the original input value or the block, not on the current value.

%n - Remainder of original value

This instruction takes the original input value for this block and divides it by the number specified to produce a new current value of the remainder. For example:

20 99 /10 x19 %10 ; (incomplete example)

Following the processing described above, the additional instruction divides the original value (between 20 and 99) by 10 and uses the remainder as the new current value (for more details of how this is used, see the description of recursion below).

Note that this instruction always works on the original input value for the block, not on the current value.

R - process block recursively

This instruction processes the current block recursively, using the current value. Recursion can only be used on integer and decimal blocks. For example:


0 19    x1            ; select numbers zero to 19

20 99   /10 x19 %10 r ; speak tens from 20 to 90

This instruction processes an input value of 35 as follows: because it lies between 20 and 99, it uses the second line; it divides the amount by 10 giving 3 and selects segment 3+19 (thirty); then divides the original number by ten giving a remainder of 5; then calls the block recursively with a value of 5; in this case line 1 is selected, and segment 5+1 (five). The net result is that the segments for thirty and five are added to the output sentence. Special processing is used for a zero value: nothing is output for value zero unless the original outermost amount value is zero. You may recurse up to eight times in any one block.

The above code is the complete specification to speak numbers between 0 and 99 in English.

Fn - Flag error

This instruction sets a system variable @AMOUNT_ERROR to the value n. You can test this value in a conditional statement in a separate IIF (not the one containing the $play_var command which caused this amount to be played). This feature allows the algorithm logic to be used to validate the amount that it is playing, and you can make a decision based on whether the last amount played was correctly formatted. The @AMOUNT_ERROR contents reflect the status of the most recently played amount.

En - exit and skip blocks

Without any number n, this instruction terminates processing of a value. With a number n the instruction terminates the processing of a block and skips the following n blocks. The effect of this instruction is the same whether it is executed from the outer level of a block or when a block is processed recursively.

Pn - Select using prefix number

Sn - Select using suffix number

These instructions use the passed prefix or suffix number to select a phrase, starting from the item indicated. Note that a passed number of 0 results in no phrase being added. A passed number of 1 selects phrase n, number 2 selects phrase n+1, etc. For example:

decimal         ; work with decimal value

0 0             ; nothing if no decimals

1 1 s37         ; use phrase 37/38: penny/cent

2 999999 s39    ; use phrase 39/40: pence/cents



00000037.VOX ; penny   37

00000038.VOX ; cent    38

00000039.VOX ; pence   39

00000040.VOX ; cents   40

When this block is processed with a suffix code of 1 it comes out with English (penny or pence); with a suffix code of 2 it comes out with American (cent or cents).