DEFINITION MODULE Play3;

	(********************************************************)
	(*							*)
	(*		    3-part music			*)
	(*							*)
	(*  Programmer:		P. Moylan, T. Channon		*)
	(*  Last edited:	21 March 1995			*)
	(*  Status:		Working				*)
	(*							*)
	(*	This is a version that does waveform shaping.	*)
	(*	Note that the caller's data format is different	*)
	(*	from that used by module Play3S.		*)
	(*							*)
	(*	Requires a fast processor, because the timing	*)
	(*	constraints are fairly tight.			*)
	(*							*)
	(********************************************************)

(************************************************************************)
(*			THE FORMAT OF THE USER DATA			*)
(************************************************************************)
(*									*)
(* The music to be played is specified in the form of three arrays,	*)
(* one for each voice.  Each of the words specifies a time scale for	*)
(* stepping through a waveform (higher values give higher frequencies).	*)
(* A value of 0 indicates a rest.					*)
(*									*)
(* The largest possible data values are reserved as special codes:	*)
(*									*)
(*	CODE	    MEANING		    EXTRA WORDS			*)
(*									*)
(*	65531	Set envelope		new envelope number (one word)	*)
(*	65532	Set waveform		new wave number (one word)	*)
(*	65533	obsolete, do not use	(none)				*)
(*	65534	Set note duration	new duration (one word)		*)
(*	65535	End of data		(none)				*)
(*									*)
(************************************************************************)

FROM SYSTEM IMPORT BYTE;

CONST
    (* CycleCount defines the sampling rate.  It has to be low enough	*)
    (* to avoid problems with frequency aliasing, but not so low that	*)
    (* the processor has trouble keeping up.				*)

    (* Note that a change to CycleCount would require the caller to	*)
    (* re-scale the frequency specifications in the input data.  This	*)
    (* module adjusts note durations to compensate for changes in	*)
    (* CycleCount, but it does not adjust the frequencies.		*)

    CycleCount = 64;

TYPE
    VoiceNumber = [1..3];

    (* In the attack/decay envelope shaping is defined by an array of	*)
    (* gains (see below).  The next declaration defines the possible	*)
    (* gain values.							*)

    GainType = [0..31];

    (* We allow the caller to define up to eight different waveforms.	*)

    WaveNumber = [0..7];

    (* A wave table is a set of values for one complete cycle.  To	*)
    (* avoid overflow, values in a wave table should not normally	*)
    (* exceed (CycleCount DIV 6) in magnitude, although it's possible	*)
    (* to go higher by trading off the amplitude of the different	*)
    (* voices.								*)
    (*									*)
    (* Recent change: the above needs to be scaled up, because I'm	*)
    (* now using INTEGER rather than SHORTINT values in the tables.	*)
    (*									*)
    (* To speed up execution, we require the user to supply a complete	*)
    (* set of wave tables, one for each gain level, for each waveform.	*)
    (* This means that the scaling is done ahead of time rather than	*)
    (* having to be done while the music is playing.			*)

    WaveTablePointer = POINTER TO ARRAY BYTE OF INTEGER;
    WaveGroup = ARRAY GainType OF WaveTablePointer;

    (* We allow the caller to define several different attack/decay	*)
    (* envelopes.							*)

    EnvelopeNumber = [0..7];

    (* The main body of an envelope is defined by a time sequence	*)
    (* of gains.							*)

    EnvelopeArrayIndex = [0..63];
    EnvelopePtr = POINTER TO ARRAY EnvelopeArrayIndex OF GainType;

PROCEDURE DefineWaveform (N: WaveNumber;  VAR (*IN*) data: WaveGroup);

    (* Defines a new waveform for wave number N. *)

PROCEDURE DefineEnvelope (E: EnvelopeNumber;  shapeptr: EnvelopePtr);

    (* Defines one of the attack/decay envelopes. *)

PROCEDURE Play (VAR (*IN*) A1, A2, A3: ARRAY OF CARDINAL);

    (* Plays the music encoded in arrays A1, A2, A3.  The encoding is	*)
    (* explained in the comments above.  Assumption: DefineWaveform and	*)
    (* DefineEnvelope have already been called for any waveform and	*)
    (* envelope that will be used.  In the absence of any specification	*)
    (* in the data, waveform 0 and envelope 0 are used.			*)

END Play3.
