DEFINITION MODULE PlayBuff; (****************************************************************) (* *) (* THIS IS A SPECIAL VERSION OF PLAYBUFF.DEF, WHICH *) (* FORCES PARAMETERS TO BE PASSED ON THE STACK. *) (* *) (****************************************************************) (*# module(implementation=>off,init_code=>off) *) (*# call(reg_param=>()) *) (********************************************************) (* *) (* Playing buffered sounds *) (* *) (* Programmer: P. Moylan *) (* Last edited: 28 February 1995 *) (* Status: OK *) (* *) (* This module consists mainly of a timer *) (* interrupt routine which continuously sends *) (* data from a memory buffer to the speaker. *) (* Higher-level modules can produce sound by *) (* computing the desired waveform and stuffing *) (* it into the buffer (using synchronisation *) (* calls to ensure that they don't overwrite *) (* data that haven't yet been sent to the *) (* speaker). *) (* *) (* Note that synchronisation will be lost, *) (* resulting in a distorted sound, if the caller *) (* is unable to supply data fast enough. *) (* Programs using this module are not necessarily *) (* portable to slower processors. *) (* *) (********************************************************) (*# module (init_code => off) *) FROM Types IMPORT (* proc *) FarPointer; (************************************************************************) (* THE FORMAT OF THE USER DATA *) (************************************************************************) (* *) (* The waveform to be played is specified as an array of bytes, each of *) (* which specifies a desired output level at one sample time. We're *) (* operating under some very tight time constraints, so to gain speed *) (* the caller writes directly into the memory buffer from which the *) (* timer interrupt routine in this module is taking the values. It's *) (* a single circular buffer internally, but the caller is expected to *) (* treat it as two half-buffers, each of size OutputBufferSize. As the *) (* interrupt routine is working on one half, the caller is expected to *) (* fill the other. The interrupt routine never waits for the caller to *) (* supply data, so it's up to the caller to supply new data as quickly *) (* as possible, using procedures Synch0 and Synch1 to avoid overtaking *) (* the interrupt routine. *) (* *) (************************************************************************) CONST OutputBufferSize = 128; (* OutputBufferSize is the size of a half-buffer. Do not alter *) (* this constant unless you are prepared to make consequential *) (* changes in the implementation module. *) PROCEDURE BufferAddress (): FarPointer; (* Returns the address of the first half-buffer. *) PROCEDURE SetCycleCount (count: BYTE); (* Sets the cycle count which determines the sampling frequency. *) (* The default value is 64, giving an 18.643 kHz sampling rate. *) PROCEDURE StartPlaying; (* Call this just after the first half-buffer has been filled. *) (*# save, call(near_call=>off, seg_name=>PlayBuff) *) PROCEDURE Synch0; (* Call this when you've just finished filling the second half of *) (* the buffer and want to move back to the first half. On return *) (* from this procedure, the caller is permitted to start filling *) (* the first half. *) PROCEDURE Synch1; (* Call this when you've just finished filling the first half of *) (* the buffer and want to move on to the second half. On return *) (* from this procedure, the caller is permitted to start filling *) (* the second half. *) (*# restore *) PROCEDURE StopPlaying; (* Stops the music and cleans up. *) END PlayBuff.