home *** CD-ROM | disk | FTP | other *** search
- /*$Author: DCODY $*/
- /*$Date: 01 Oct 1992 12:05:02 $*/
- /*$Header: X:/sccs/pcm/pcmioc.c_v 1.7 01 Oct 1992 12:05:02 DCODY $*/
- /*$Log: X:/sccs/pcm/pcmioc.c_v $
- *
- * Rev 1.7 01 Oct 1992 12:05:02 DCODY
- * next stage of completion for PlayThisBlock, RecordThisBlock, etc.
- *
- * Rev 1.6 23 Sep 1992 10:56:34 DCODY
- * more work on playthisblock, continuethisblock...
- *
- * Rev 1.5 26 Aug 1992 10:57:30 DCODY
- * Added Playthisblock and RecordThisBlock
- *
- * Rev 1.4 12 Aug 1992 17:10:30 DCODY
- * major change to eliminate the foreground buffers.
- *
- * Rev 1.3 24 Jul 1992 15:36:14 DCODY
- * changed _fmemcpy to _rfmemcpy
- *
- * Rev 1.2 17 Jul 1992 14:22:50 DCODY
- * InitMVSound() now performed within OpenPCMBuffering().
- *
- * Rev 1.1 23 Jun 1992 17:11:42 DCODY
- * PAS2 update
- *
- * Rev 1.0 15 Jun 1992 09:44:38 BCRANE
- * Initial revision.
- */
- /*$Logfile: X:/sccs/pcm/pcmioc.c_v $*/
- /*$Modtimes$*/
- /*$Revision: 1.7 $*/
- /*$Workfile: pcmioc.c $*/
-
-
- /*\
- |*|----====< PCMIOC.C >====----
- |*|
- |*| These routines maintain DMA controlled I/O of the Audio Spectrum
- |*|
- |*| Copyright (c) 1991, Media Vision, Inc. All rights reserved.
- |*|
- \*/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <malloc.h>
-
- #include "pcmio.h"
- #include "common.h"
- #include "mvsound.h"
-
-
- /*\
- |*|-----------====< T H E O R Y O F O P E R A T I O N >====------------
- |*|
- |*| The best DMA controlled PCM output requires a continuous stream of data
- |*| to be available in a real-time environment.
- |*|
- |*| DMA controlled PCM input, with the same real-time requirements, needs
- |*| to be able to keep storing data into memory without pausing.
- |*|
- |*| The following approach is designed to allow the DMA to be setup in
- |*| "auto-initialize" mode, thereby guarenteeing continuous play/record.
- |*|
- |*| To keep the DMA running, multiple divisions of the DMA buffer are
- |*| used to keep the data moving. Due to the fact that DOS is neither
- |*| a real-time, or re-entrant operating system, this code divides up
- |*| the buffer management tasks into a "foreground" and "background" task.
- |*|
- |*| A sample buffer count timer on the Audio Spectrum is used to interrupt
- |*| the CPU when a DMA buffer division has filled or emptied. For our
- |*| purposes here, this amount may be 1/2, 1/4, 1/8th or some smaller
- |*| division of the whole DMA buffer. Note: judgement must be used here
- |*| in selecting the DMA buffer size, and the integral division. Too small
- |*| of an integral may result in broken DMA I/O. A buffer too large never
- |*| hurts anything. (it just reduces the amount of available memory).
- |*|
- |*| ----====< PCM OUTPUT >====----
- |*|
- |*| To perform PCM output ("play"), A linked list of buffer pointers is
- |*| used to fill the DMA buffer by the foregound task. As the DMA runs,
- |*| it will send the buffer contents to the audio card. Here is a visual:
- |*|
- |*|
- |*| Foreground Loads
- |*| the Top Level
- |*| Buffers
- |*|
- |*| ┌─┬─┬─┬─┬─┐
- |*| DMA Level Buffers │ │ │ │ │ │
- |*| └─┴─┴─┴─┴─┘
- |*|
- |*| ┌─────────┐
- |*| │hardware │
- |*| └╥╥╥──────┘
- |*|
- |*| To actually start the output, the foreground task loads it's
- |*| buffers, then starts the DMA to play the buffer. The background
- |*| task only indicates when each block is played. It will shut down
- |*| the DMA if no more data is present in the buffers.
- |*|
- |*| If the foreground task can keep the linked list of buffers full,
- |*| there should be non-stop PCM output (Good!). If the foreground task
- |*| does not keep up, the background task will be forced to stop the
- |*| the DMA, thereby causing a break in the output (Bad!). Once the DMA
- |*| has stopped, the foreground task will have to restart the DMA a
- |*| second time to continue the flow of data.
- |*|
- |*| ----====< PCM INPUT >====----
- |*|
- |*| To perform PCM input ("record"), the same linked list of buffers
- |*| are also used. This buffer is filled with sampled data from the
- |*| hardware. The background process will increment a global variable for
- |*| each buffer filled. The foreground routine must extract each buffer
- |*| and process it (copy to memory, or write it to disk). Here is a visual:
- |*|
- |*|
- |*| Foreground unloads
- |*| the Top Level
- |*| Buffers
- |*|
- |*| ┌─┬─┬─┬─┬─┐
- |*| DMA Level Buffers │ │ │ │ │ │
- |*| └─┴─┴─┴─┴─┘
- |*|
- |*| ┌─────────┐
- |*| │hardware │
- |*| └╥╥╥──────┘
- |*|
- |*| To actually start the input, the foreground starts the DMA running to
- |*| begin the transfer. The background task increments the global variable
- |*| as each interrupt occurs. If all the buffers are full, the DMA transfer
- |*| is terminated. The foreground routine must poll this variable to keep
- |*| the data moving out of the DMA buffer.
- |*|
- |*| If the foreground task can keep the linked list of buffers empty,
- |*| there should be non-stop PCM input (Good!). If the foreground task
- |*| does not keep up, the background task will be forced to stop the
- |*| the DMA, thereby causing a break in the input (Bad!). Once the DMA
- |*| has stopped, the foreground task will have to restart the DMA tranfer
- |*| a second time to restart the DMA.
- |*|
- |*| ----====< DATA VARIABLES >====----
- |*|
- |*| The following is a description of the variables shared between the
- |*| foreground and background tasks. There are three global variables,
- |*| and a linked list of buffers shared between the two tasks.
- |*|
- |*| The linked list of buffers uses a "header" to each buffer. This
- |*| header holds the information for linking to the next buffer, whether
- |*| the buffer is full or empty, and the count of bytes in the buffer.
- |*|
- |*| typedef struct _buffptr {
- |*| int status; /* 0=empty, 1=full * /
- |*| int count; /* # of bytes in the buffer * /
- |*| int size; /* total size of read data * /
- |*| char huge *buffer; /* pointer to buffer data * /
- |*| struct _buffptr *nextptr; /* pointer to next buffer * /
- |*|
- |*| } BuffData,*BuffPtr;
- |*|
- |*| BuffPtr HeadOfBuffers; /* global variable head pointer * /
- |*| int BufferDataCount; /* # of full DMA buffers parts * /
- |*| int DMARunning; /* DMA status (0=off,1=running) * /
- |*| char far *StartOfDMABuffer; /* start of actual DMA buffer * /
- |*| int ProcessedBlockCount; /* # of blocks DMA handled * /
- |*|
- |*| "HeadOfBuffers" points to the first buffer in the linked list.
- |*|
- |*| This linked list is made up of structures containing the buffer
- |*| data and other information. The last entry in the list points
- |*| back to the first entry, thereby creating a circular linked
- |*| list.
- |*| Each buffer has a status word: 0=empty,1=full.
- |*| The count indicates the # of bytes in the buffer. This count
- |*| is used to communication between the foreground and background
- |*| process that data is available. For output, the count tells the
- |*| background that data is available to be loaded in the DMA buffer.
- |*| For input, the count tells the foreground process that there is
- |*| data to be written to disk.
- |*|
- |*| "BufferDataCount" is the key handshaking variable between the
- |*| foreground and background processes. It indicates how many DMA
- |*| buffers divisions contain data.
- |*|
- |*| For output, it holds a count of DMA divisions hold data. This
- |*| global variable is incremented each time a buffer is loaded by
- |*| the foreground task, and decremented when a buffer is emptied
- |*| by the background task.
- |*|
- |*| For input, it holds the number of buffers with data in the DMA
- |*| buffer. It is incremented by the background process and
- |*| decremented by the foreground process.
- |*|
- |*| "DMARunning" is set to true or false depending upon the state
- |*| of the DMA channel. It is set TRUE when the DMA is running (either
- |*| playing or recording), and FALSE when the DMA is turned off.
- |*|
- |*| "ProcessedBlockCount" is the running total of blocks the DMA has
- |*| processed from the last Start I/O call.
- |*|
- |*| For input, this is the total number of dma divisions filled
- |*| by the DMA.
- |*|
- |*| For output, this is the total number of blocks loaded into
- |*| the DMA buffer.
- |*|
- |*| "StartOfDMABuffer" points to the first byte of the DMA circular buffer.
- |*|
- |*| The following routines provide a high level interface to DMA driven
- |*| PCM output:
- |*|
- |*| int OpenPCMBuffering ( int, int, int, int )
- |*|
- |*| This routine is the first routine to be called. It sets
- |*| up the DMA channel, IRQ, and allocates memory for the buffers.
- |*|
- |*| int PCMState ( int, int, int, int )
- |*|
- |*| This routine passes in the sample rate, stereo/mono flag,
- |*| the compression type (0 for 8 bit, 1 for for 4 bit),
- |*| and the PCM data sample size (8 or 16).
- |*|
- |*| int StartFileInput ( FILE *f )
- |*|
- |*| This routine begins recording the PCM data to the disk file.
- |*| The routine returns immediately. The routine,
- |*| "ContinueFileInput" must be called to continue moving data
- |*| from the DMA buffer to to the disk.
- |*|
- |*| int StartBlockInput ( )
- |*|
- |*| This routine begins recording the PCM data. The routine
- |*| returns immediately. Subsequent call must be made to
- |*| "ContinueBlockInput" to receive data from the DMA buffer.
- |*|
- |*| int StartFileOutput ( FILE *f, long )
- |*|
- |*| This routine begins playing the PCM data from the disk file.
- |*| The routine returns immediately. The routine,
- |*| "ContinueFileOutput" must be called to continue moving data
- |*| from the disk to the DMA buffer. The long variable tells how
- |*| many bytes to play.
- |*|
- |*| int StartBlockOutput ( char far * )
- |*|
- |*| This routine begins playing the caller's PCM data. The
- |*| routine returns immediately. The routine, "ContinueBlockOutput"
- |*| must subsequently be called to continue data output.
- |*|
- |*| int ContinueFileInput ( )
- |*|
- |*| This routine checks to see if new data has been loaded into
- |*| the linked list of buffers. If so, the data is written to
- |*| disk, and the buffer is freed up.
- |*|
- |*| int ContinueBlockInput ( char far * )
- |*|
- |*| This routine checks to see if new data has been loaded into
- |*| the linked list of buffers. If so, the data is written to
- |*| the caller's buffer.
- |*|
- |*| int ContinueFileOutput ( )
- |*|
- |*| This routine checks to see if the PCM hardware is
- |*| still playing. This routine MUST be called frequently to
- |*| maintain continuous PCM output.
- |*|
- |*| int ContinueBlockOutput (char far *)
- |*|
- |*| This routine checks to see if the PCM hardware is
- |*| still playing. The caller passes the next block to be
- |*| played. A non-zero return value indicates the block has
- |*| been queued up to be played. A zero value means the buffer
- |*| is currently full, please try again...
- |*|
- |*| void StopDMAIO ( )
- |*|
- |*| This routine is used to prematurely terminate PCM I/O.
- |*|
- |*| void ClosePCMBuffering ( )
- |*|
- |*| This routine is used to close down the whole PCM I/O system.
- |*| This call MUST be made before the caller's program terminates.
- |*|
- \*/
-
- /*\
- |*|----====< Code Generation >====----
- \*/
-
- #define BLOCKOUT 0 /* builds block output code only */
- #define BLOCKIN 0 /* builds block input code only */
- #define FILEOUT 0 /* builds file output code only */
- #define FILEIN 0 /* builds file input code only */
- #define COMMDATA 0 /* builds both common code and data */
-
- #ifdef BUILDBO
- #undef BLOCKOUT
- #define BLOCKOUT 1
- #endif
-
- #ifdef BUILDBI
- #undef BLOCKIN
- #define BLOCKIN 1
- #endif
-
- #ifdef BUILDFO
- #undef FILEOUT
- #define FILEOUT 1
- #endif
-
- #ifdef BUILDFI
- #undef FILEIN
- #define FILEIN 1
- #endif
-
- #ifdef BUILDCO
- #undef COMMDATA
- #define COMMDATA 1
- #endif
-
- /*\
- |*|----====< common data for CODE and DATA generation >====----
- \*/
-
- /* buffer linked list header structures */
-
- typedef struct _buffptr {
- int status; /* 0=empty, 1=full */
- int count; /* # of bytes in the buffer */
- int size; /* total size of allocated buff */
- char huge *buffer; /* pointer to buffer data */
- struct _buffptr *nextptr; /* pointer to next buffer hdr */
-
- } BuffData,*BuffPtr;
-
- #define NODIRECTION 0 /* defines for DirectionFlag */
- #define DMAINPUT 1
- #define DMAOUTPUT 2
-
-
- /*\
- |*|----====< Global Data >====----
- \*/
-
- #define QUEUESIZE 32 /* 32 entries */
- #define QUEUEMASK 0x1F /* mask to circulate the count */
-
- #if COMMDATA
-
- int MaxBuffCount = 0; /* # of DMA buffer divisions */
- int BufferSize = 0; /* size of each buffer division */
-
- /* shared global variables between the two tasks (in pcmioa.asm) */
-
- BuffPtr HeadOfBuffers = 0; /* global variable head pointer */
- int BufferDataCount = 0; /* # of full buffers (0=done) */
- int DMARunning = 0; /* DMA status (0=off,1=running) */
- char huge *DMABuffPtr = 0; /* 128k+1 DMA buffer pointer */
- char far *StartOfDMABuffer = 0; /* start of DMA buffer pointer */
- int ProcessedBlockCount = 0; /* # of I/O blocks processed */
- unsigned long _file_data_length = 0;/* size of data output */
- char __pcmdatasize = 8; /* default to 8 bit pcm */
-
- FILE *__fptr = 0; /* file pointer for disk I/O */
- char *__LocalBuff = 0; /* local target buffer */
- BuffPtr __NextPtr = 0; /* next buffer pointer */
- int __DirectionFlag = 0; /* current I/O direction */
- char far * __singleblockpointer; /* single shot users buffer */
-
- /* local data for this body of code, but needs to be public */
-
- int VoiceActivatedSavedCount = 0; /* # of I/O blocks saved */
-
- int __queuein = 0;
- int __queueincnt = 0;
- int __queueout = 0;
- long __queuedata = 0;
-
- char far *__queuebuff[QUEUESIZE]; // number of queued blocks
- long __queuelen[QUEUESIZE]; // queued block lengths
- void (far * __queuecb[QUEUESIZE])();// queue of callback routines
-
- void (far *__synccallback)() = 0; // callback to user code
-
- #else
-
- extern int MaxBuffCount; /* # of DMA buffer divisions */
- extern int BufferSize; /* size of each buffer division */
-
- /* shared global variables between the two tasks (in pcmioa.asm) */
-
- extern BuffPtr HeadOfBuffers; /* global variable head pointer */
- extern int BufferDataCount; /* # of full buffers (0=done) */
- extern int DMARunning; /* DMA status (0=off,1=running) */
- extern char huge *DMABuffPtr; /* 128k+1 DMA buffer pointer */
- extern char far *StartOfDMABuffer; /* start of DMA buffer pointer */
- extern int ProcessedBlockCount; /* # of I/O blocks processed */
- extern unsigned long _file_data_length; /* size of data output */
- extern char __pcmdatasize; /* default to 8 bit pcm */
-
- extern FILE *__fptr; /* file pointer for disk I/O */
- extern char *__LocalBuff; /* local target buffer */
- extern BuffPtr __NextPtr; /* next buffer pointer */
- extern int __DirectionFlag; /* current I/O direction */
- extern char far* __singleblockpointer;/* single shot users buffer */
-
- extern int VoiceActivatedSavedCount;/* # of I/O blocks saved */
-
- extern int __queuein;
- extern int __queueincnt;
- extern int __queueout;
- extern long __queuedata;
-
- extern char far *__queuebuff[]; // number of queued blocks
- extern long __queuelen[]; // queued block lengths
- extern void (far * __queuecb[])(); // queue of callback routines
-
- extern void (far *__synccallback)();// callback to user code
-
- #endif
-
- /* additional prototypes */
-
- void far * _rfmemcpy (void far *, void far *, unsigned int);
- void huge * _rfhmemcpy(void huge *,void huge *,unsigned int);
-
- #if BLOCKOUT
- static int _loadtheblock ( char far * );
- #endif
-
- #if FILEOUT
- static int _loadthebuffer ( FILE * );
- #endif
-
-
-
- /*\
- |*|-----------------====================================-----------------
- |*|-----------------====< Start of Executable Code >====-----------------
- |*|-----------------====================================-----------------
- \*/
-
- #if FILEIN
- /*\
- |*|----====< ASpecialContinueFileInput >====----
- |*|
- |*| This is a special adaptation of the standard, "ContinueDMAInput"
- |*| routine. It will check the noise level in each block before writting
- |*| it out to disk. This way, no data is written until a noise level
- |*| is reached.
- |*|
- \*/
- int ASpecialContinueFileInput(noise,goflag)
- int noise; /* offset from silence */
- int goflag; /* record all after first block */
- {
- int temp;
-
- /* if BufferDataCount is non-zero, we must process the DMA data */
-
- while (BufferDataCount) {
-
- /* data is available, move it out to memory */
-
- _rfmemcpy (__LocalBuff,__NextPtr->buffer,BufferSize);
-
- /* validate the level of noise before writing it to disk */
-
- if (MakeHalfHistoGram(__LocalBuff,BufferSize,noise) ||
- (VoiceActivatedSavedCount && goflag) ) {
-
- /* if not all data is written, return in error */
-
- #if LARGEDATA
- if (fwrite (__NextPtr->buffer,1,BufferSize,__fptr) != BufferSize) {
- StopDMAIO();
- return (0);
- }
- #else
- if (fwrite (__LocalBuff,1,BufferSize,__fptr) != BufferSize) {
- StopDMAIO();
- return (0);
- }
- #endif
- VoiceActivatedSavedCount++;
- }
- else
- ProcessedBlockCount--;
-
- /* move to the next buffer */
-
- __NextPtr->count = __NextPtr->status = 0;
- __NextPtr = __NextPtr->nextptr;
- BufferDataCount--;
- }
- return (DMARunning);
- }
- #endif
-
-
- #if COMMDATA
- /*\
- |*|----====< ClosePCMBuffering >====----
- |*|
- |*| Removes the PCM system & deallocates the buffer memory. There is
- |*| no return value.
- |*|
- \*/
- void ClosePCMBuffering()
- {
- BuffPtr p,op;
-
- /* we will kill the DMA low level processing */
-
- StopDMAIO();
- _unloadirqvector();
-
- /* Free up the linked list of buffers */
-
- if ((p = HeadOfBuffers) != 0) {
-
- do {
- op = p; /* save the old ptr */
- p = p->nextptr; /* point to the next buffer */
- free (op); /* free up the old header */
-
- } while ( (p != HeadOfBuffers) && p );
- }
-
- /* free up the DMA buffer */
-
- if (DMABuffPtr)
- hfree (DMABuffPtr);
-
- /* null it all out... */
-
- DMABuffPtr = 0;
- HeadOfBuffers = 0;
- StartOfDMABuffer = 0;
- BufferDataCount = BufferSize = DMARunning = 0;
-
- }
- #endif
-
-
- #if BLOCKIN
- /*\
- |*|----====< ContinueBlockInput >====----
- |*|
- |*| This routine checks to see if another buffer can be stored in memory.
- |*| if so, it will load copy the DMA buffer to the caller's local buffer,
- |*| A return value of 0 indicates the caller's buffer is empty.
- |*|
- \*/
- int ContinueBlockInput(buff)
- char far *buff;
- {
-
- /* if BufferDataCount is non-zero, we must move the data to memory */
-
- if (BufferDataCount) {
-
- /* data is available, just move it out */
-
- _rfmemcpy (buff,__NextPtr->buffer,BufferSize);
-
- /* move to the next buffer */
-
- __NextPtr->count = __NextPtr->status = 0;
- __NextPtr = __NextPtr->nextptr;
- BufferDataCount--;
-
- /* returns the fact that the data has been loaded */
-
- return(1);
- }
- return (0);
- }
-
-
- /*\
- |*|----====< ContinueThisBlockInput >====----
- |*|
- |*| This routine extracts a DMA buffer into one or
- |*| more target user buffers.
- |*|
- |*| Returns:
- |*| Nonzero for running & processing, else 0 for dead.
- |*|
- \*/
- int ContinueThisBlockInput()
- {
- int n, // working integer
- loop, // loop flag to keep loading blocks
- bcount; // increments the BufferDataCount
- int result = 0; // holds the final count
-
- static int TargetSize; // remaining size of the target dma buffer
- static char far *dmaptr; // pointer to this DMA block
-
- // if the DMA is dead, give it a jump start. Bad thing, it flushes all...
-
- if (DMARunning == 0) {
-
- // blow off anything that is saved locally
-
- dmaptr = 0;
- TargetSize = 0;
-
- // reset and restart the low level stuff...
-
- _resetbuffers();
- StartTheDMAInput(ContinueThisBlockInput);
-
- // we have no more data, just return now
-
- return(DMARunning);
- }
-
- // if the current remaining length is null, prime for the next block
-
- if (_file_data_length == 0) {
-
- // bomb out if no data buffers queued up
-
- if (__queueincnt == 0)
- return(1);
-
- // get the next block from the queue
-
- _file_data_length = __queuelen [__queueout];
- __singleblockpointer = __queuebuff[__queueout];
- }
-
- // loop here to stuff as many blocks as possible into the DMA buffer
-
- nextblock:
-
- // move up to one buffer division worth of data
-
- if (!TargetSize) {
-
- dmaptr = __NextPtr->buffer;
- TargetSize = BufferSize;
- }
-
- loop = TRUE;
- bcount = 1;
-
- // move as many blocks as possible into the DMA buffer
-
- while (loop) {
-
- // Get the block length, up to the division size
-
- if (_file_data_length <= TargetSize) {
-
- n = _file_data_length; // full target size
- _file_data_length = 0;
-
- }
- else // partial target size
-
- _file_data_length -= (n = TargetSize);
-
- // copy the data to the buffer, and advance the buffer that far
-
- if (n) {
-
- // move the recorded data into the buffer
-
- __singleblockpointer
- = _rfmemcpy(__singleblockpointer,dmaptr,n);
- dmaptr += n; // move the dma pointer
- result += n; // more for the return value
-
- __queuedata -= (n &0xffff); // less queued up
- BufferDataCount -= bcount; // increment buffer count once
- bcount = 0;
-
- }
-
- // if the length is zero, this buffer is done, let the caller know
-
- if (!_file_data_length) {
-
- // let the app. know we are done with this buffer
-
- if (__queuecb[__queueout])
- (*__queuecb[__queueout])(__queuebuff[__queueout],__queuelen[__queueout]);
-
- __queueincnt--;
- __queueout = ++__queueout & QUEUEMASK;
-
- // Now, try to get the next available block out of the list
-
- if (__queuein != __queueout) {
-
- _file_data_length = __queuelen[__queueout];
- __singleblockpointer = __queuebuff[__queueout];
- }
- else
- loop = FALSE;
- }
-
- // we are now done with this much of the buffer, stop if zero
-
- if (!(TargetSize -= n))
- loop = FALSE;
-
- }
-
- // advance the list to the next DMA buffer and count one more...
-
- __NextPtr = __NextPtr->nextptr;
-
- // if we can do more, then DO IT!!!
-
- if (BufferDataCount > 0) { // if there is data in the DMA...
- if (__queueincnt) // if we have buffers...
- goto nextblock; // then go load it...
- }
-
- // return the number of bytes loaded
-
- return(result);
-
- }
-
- #if 0
-
- //////////////// Original ContinueThisBlockInpu ///////////////
-
-
- /* if there is no more data, just exit */
-
- if (!_file_data_length)
- return(0);
-
- /* move as much as possible... */
-
-
- if (_file_data_length <= BufferSize) {
- n = _file_data_length;
- _file_data_length = 0;
- }
- else {
- _file_data_length -= (n = (BufferSize & 0xffff));
- }
-
- __singleblockpointer =
- _rfmemcpy(__singleblockpointer,__NextPtr->buffer,n);
-
- BufferDataCount--;
-
-
-
- __NextPtr = __NextPtr->nextptr; /* advance the list */
-
- return(n);
- #endif
-
-
- #endif
-
-
- #if FILEIN
- /*\
- |*|----====< ContinueFileInput >====----
- |*|
- |*| This routine checks to see if another buffer can be written to disk.
- |*| if so, it will load copy the buffer to a local buffer, then write it
- |*| out to disk. A return value of 0 indicates recording has stopped,
- |*| which could mean that the disk file is full, so the DMA had to be
- |*| stopped prematurely.
- |*|
- \*/
- int ContinueFileInput()
- {
-
- /* if BufferDataCount is non-zero, we must write out the data */
-
- while (BufferDataCount) {
-
- /* data is available, move it out to disk */
-
- #if LARGEDATA
-
- /* if not all data is written, return in error */
-
- if (fwrite (__NextPtr->buffer,1,BufferSize,__fptr) != BufferSize) {
- StopDMAIO();
- return (0);
- }
- #else
- /* if not all data is written, return in error */
-
- _rfmemcpy (__LocalBuff,__NextPtr->buffer,BufferSize);
- if (fwrite (__LocalBuff,1,BufferSize,__fptr) != BufferSize) {
- StopDMAIO();
- return (0);
- }
- #endif
- /* move to the next buffer */
-
- __NextPtr->status = 0;
- __NextPtr = __NextPtr->nextptr;
- BufferDataCount--;
- }
- return (DMARunning);
- }
- #endif
-
-
- #if BLOCKOUT
- /*\
- |*|----====< ContinueBlockOutput >====----
- |*|
- |*| This routine checks to see if another DMA buffer can be loaded.
- |*| If so, it will load the user's block data into an empty buffer.
- |*| A return value of 1 indicates the buffer has been loaded, else
- |*| it must be sent in again until it is loaded.
- |*|
- \*/
- int ContinueBlockOutput(buff)
- char far *buff;
- {
-
- /* if the internal count is not max-ed out, try to load the next buffer */
-
- if (BufferDataCount < MaxBuffCount ) {
-
- _loadtheblock (buff);
-
- if (DMARunning == 0) { /* yuck! a DMA break! */
- _resetbuffers();
- StartTheDMAOutput(0);
- }
-
- return (1); /* return running */
- }
- else
- return(0);
- }
-
-
- /*\
- |*|----====< ContinueThisBlockOutput >====----
- |*|
- |*| This routine checks to see if another DMA buffer can be loaded.
- |*| If so, it will load the user's block data into an empty buffer.
- |*| A return value of ~0 indicates the buffer has been loaded.
- |*|
- |*| The foreground routine will call this when DMARunning == 0. The
- |*| background routine will call this at every interrupt to keep the
- |*| buffers loaded
- |*|
- \*/
- int ContinueThisBlockOutput()
- {
-
- int n, // working integer
- TargetSize, // size of the target dma buffer
- loop, // loop flag to keep loading blocks
- bcount; // increments the BufferDataCount
- int result = 0; // holds the final count
- char far *s;
-
- // If no more data to load in the buffer, flush the next DMA & return
-
- if (__queueincnt == 0) {
- FlushBuffer (__NextPtr->buffer,BufferSize);
- __NextPtr = __NextPtr->nextptr;
- return(0);
- }
-
- // if there is little data, but a lot in the DMA, just return
-
- if ((__queuedata < BufferSize) && BufferDataCount > 2)
- return(0);
-
- // if the DMA has been turned off, re-sync the buffers
-
- if (DMARunning == 0)
- _resetbuffers();
-
- // if the current remaining length is null, prime for the next block
-
- if (_file_data_length == 0) {
- _file_data_length = __queuelen [__queueout];
- __singleblockpointer = __queuebuff[__queueout];
- }
-
- // loop here to stuff as many blocks as possible into the DMA buffer
-
- nextblock:
-
- // move up to one buffer division worth of data
-
- TargetSize = BufferSize;
- s = __NextPtr->buffer;
- loop = TRUE;
- bcount = 1;
-
- // move as many blocks as possible into the DMA buffer
-
- while (loop) {
-
- // Get the block length, up to the division size
-
- if (_file_data_length <= TargetSize) {
-
- n = _file_data_length; // full target size
- _file_data_length = 0;
-
- }
- else // partial target size
-
- _file_data_length -= (n = TargetSize);
-
- // copy the data to the buffer, and advance the buffer that far
-
- if (n) {
-
- s = _rfmemcpy(s, __singleblockpointer, n );
-
- result += n; // more for the return value
- __singleblockpointer += n; // advance the pointer
- __queuedata -= (n &0xffff); // less queued up
-
- BufferDataCount += bcount; // increment buffer count once
- bcount = 0;
-
- }
- else
- s = __NextPtr->buffer; // no data, but do point here
-
- // if the length is zero, this buffer is done, let the caller know
-
- if (!_file_data_length) {
-
- // if this old block was valid, send a DONE msg.
-
- if(__queueincnt) {
-
- // let the app. know we are done with this buffer
-
- if (__queuecb[__queueout])
- (*__queuecb[__queueout])(__queuebuff[__queueout],FALSE);
-
- __queueincnt--;
- __queueout = ++__queueout & QUEUEMASK;
- }
-
- // Now, try to get the next available block out of the list
-
- if (__queuein == __queueout) {
-
- FlushBuffer (s,TargetSize-n);
- loop = FALSE;
-
- }
- else {
-
- _file_data_length = __queuelen[__queueout];
- __singleblockpointer = __queuebuff[__queueout];
-
- }
- }
-
- // we are now done with this much of the buffer, stop if zero
-
- if (!(TargetSize -= n))
- loop = FALSE;
- }
-
- // advance the list to the next DMA buffer and count one more...
-
- __NextPtr = __NextPtr->nextptr;
-
- // if we can do more, then DO IT!!!
-
- if (BufferDataCount < MaxBuffCount) { // if there is room in the DMA
- if (__queueincnt) { // if we have pcm data
- if (__queuedata >= BufferSize) // and its GE a buffer division,
- goto nextblock; // then go load it...
- }
- }
-
- if (DMARunning == 0)
- StartTheDMAOutput(ContinueThisBlockOutput);
-
- // return the number of bytes loaded
-
- return(result);
-
- }
- #endif
-
-
- #if FILEOUT
- /*\
- |*|----====< ContinueFileOutput >====----
- |*|
- |*| This routine checks to see if another buffer can be loaded. If so, it
- |*| will load the data into an empty buffer. All empty buffers will be
- |*| loaded. A return value of 0 indicates playing has finished.
- |*|
- \*/
- int ContinueFileOutput()
- {
-
- /* if BufferDataCount is not max-ed out, try to load the next buffer*/
-
- if (BufferDataCount < MaxBuffCount ) {
-
- if (_loadthebuffer (__fptr)) {
-
- if (DMARunning == 0) { /* yuck! a DMA break! */
- _resetbuffers();
- if (StartTheDMAOutput(0))
- return(0);
- }
- }
- }
- return (DMARunning); /* return the DMA state */
- }
- #endif
-
-
- #if COMMDATA
- /*\
- |*|----====< OpenPCMBuffering >====----
- |*|
- |*| This routine is the first-call routine. It initializes the buffers
- |*| needed for the PCM play/record system. A return value of non-zero
- |*| indicates a failure to initialize the system.
- |*|
- |*| Entry Conditions:
- |*|
- |*| dma -- New DMA #. (1-3, or -1 for no changes)
- |*| irq -- New IRQ #. (3,5,6,7, or -1 for no changes)
- |*|
- |*| Exit Conditions:
- |*|
- |*| non-zero return indicates an error
- |*|
- \*/
- int OpenPCMBuffering(dma,irq,dmasize,divisions)
- int dma; /* DMA channel # (-1 for no changes) */
- int irq; /* IRQ channel # (-1 for no changes) */
- int dmasize; /* requested DMA size (4/8/16/32/64) */
- int divisions; /* # of divisions in the DMA buffer */
- {
- BuffPtr op,p;
- long l;
- int n;
- char far *db;
-
- /* setup the globa variables & a local buffer */
-
- MaxBuffCount = divisions;
- BufferSize = LONG(dmasize) * 1024L / LONG(MaxBuffCount);
-
- /* Setup the lowlevel routines */
-
- InitMVSound();
-
- /* flush any background task setup */
-
- BackgroundInit( BufferSize, MaxBuffCount );
-
- if ((__LocalBuff=(char*)malloc(BufferSize)) == 0)
- return (PCMIOERR_OPENFILE);
-
- /* Allocate twice the size for background DMA buffer */
-
- l = LONG(dmasize) * 1024 * 2;
-
- if ((DMABuffPtr = (char huge *) halloc (l,1)) == 0)
- return(PCMIOERR_NOMEM);
-
- if ((db=StartOfDMABuffer=FindDMABuffer(DMABuffPtr,dmasize)) == 0)
- return (PCMIOERR_OPENPCM);
-
- /* if the low level code doesn't like it, bomb out */
-
- if (!DMABuffer ( StartOfDMABuffer, dmasize, MaxBuffCount ))
- return(PCMIOERR_OPENPCM);
-
- /* Attempt to allocate each foreground buffer */
-
- op = 0;
- for (n=0;n<divisions;n++) {
-
- /* allocate the linked list header for each buffer */
-
- if ((p = (BuffPtr) malloc (sizeof(BuffData))) == 0)
- return(PCMIOERR_NOMEM);
-
- /* reset the pointer in case of other failures during init */
-
- p->nextptr = 0;
-
- /* if first block, save as the head of the list */
-
- if (!HeadOfBuffers)
- HeadOfBuffers = p;
-
- /* if we have already allocated a block, setup the fwd ptr */
-
- if (op)
- op->nextptr = p;
-
- p->buffer = db;
- p->size = BufferSize;
- db += BufferSize;
-
- /* save as the old pointer for linking purposes */
-
- op = p;
- }
-
- /* link the last buffer back to the first */
-
- p->nextptr = HeadOfBuffers;
-
- /* Possibly select new DMA & IRQ channels */
-
- if (dma != -1)
- if (SelectDMA(dma))
- return(PCMIOERR_BADDMA);
-
- if (irq != -1)
- if (SelectIRQ(irq))
- return(PCMIOERR_BADIRQ);
-
- /* well, it looks good so far, flush any variables */
-
- BufferDataCount = ProcessedBlockCount =
- _file_data_length = __queuedata =
- VoiceActivatedSavedCount = __queueincnt =
- __queuein = __queueout = 0;
-
- /* and return good! */
-
- return (0);
- }
- #endif
-
-
- #if COMMDATA
- /*\
- |*|----====< PCMState >====----
- |*|
- |*| This routine passes in the sample rate, stereo/mono flag, and any
- |*| other miscellaneous data (to be determined later...)
- |*|
- |*| Exit Conditions:
- |*| Non-zero means the sample rate was in error.
- |*| Zero means the sample rate was okay error.
- |*|
- \*/
- int PCMState(sr,sm,cp,sz)
- long sr; /* sample rate */
- int sm; /* stereo/mono */
- int cp; /* compression */
- int sz; /* size(8/16) */
- {
-
- /* just pass them on... */
-
- __pcmdatasize = sz; /* pcm data size */
- return (!PCMInfo (sr,sm,cp,sz));
-
- }
- #endif
-
-
- #if COMMDATA
- /*\
- |*|----====< StopDMAIO >====----
- |*|
- |*| This routine forceably kills the PCM I/O. All buffers will be
- |*| reset, the current position of the input file is not altered. There
- |*| is no return value.
- |*|
- \*/
- void StopDMAIO()
- {
-
- /* if this code has not already been setup, exit now */
-
- if (!HeadOfBuffers)
- return;
-
- /* stop the hardware... */
-
- StopPCM( );
-
- __queuein = __queueincnt = __queueout = DMARunning = 0;
- __queuedata = _file_data_length = 0;
-
- /* flush any prior background task setup */
-
- if (__DirectionFlag == DMAOUTPUT) {
- if (__fptr)
- rewind (__fptr);
- }
-
- /* reset the linked list of buffers */
-
- _resetbuffers();
-
- /* setup our internal direction flag */
-
- __DirectionFlag = NODIRECTION;
- }
- #endif
-
-
- #if BLOCKIN
- /*\
- |*|----====< StartBlockInput >====----
- |*|
- |*| This routine resets the buffer pointers, then starts up
- |*| the DMA PCM input. Nothing else needs to be done. A return
- |*| value of 0 indicates the DMA failed to startup; No input
- |*| is occuring.
- |*|
- \*/
- int StartBlockInput()
- {
-
- /* setup our internal direction flag */
-
- __DirectionFlag = DMAINPUT;
-
- /* Reset the # of blocks seen during I/O processing */
-
- ProcessedBlockCount = 0;
-
- /* Flush all buffers */
-
- _resetbuffers();
-
- /* if the hardware level code isn't gonna work, then return a failure. */
-
- return (!StartTheDMAInput(0));
- }
-
-
- /*\
- |*|----====< RecordThisBlock >====----
- |*|
- |*| This routine offers the caller a simplified recording of one
- |*| variable length block of data. The call just needs to call
- |*| OpenPCMBuffering, then RecordThisBlock. The caller just has
- |*| to poll DMARunning to see if the block has completed. Calling
- |*| StopDMAIO will stop the process, if need be.
- |*|
- \*/
- int RecordThisBlock(p,len,cb)
- char far *p;
- unsigned long len;
- void (far *cb)();
- {
- int n;
-
- ///////////////////// under construction! /////////////////////////
-
- // if the pointer is valid, then queue it up
-
- if (p) {
-
- // return if already full
-
- if (__queuein == QUEUESIZE)
- return(2);
-
- // extract the 1st entry from the queue
-
- __queuebuff[__queuein] = p;
- __queuedata += (__queuelen [__queuein] = len);
- __queuecb [__queuein] = cb;
- __queuein = ++__queuein & QUEUEMASK;
- __queueincnt++;
-
- }
-
- // if the blocks are not recording , the let'er rip...
-
- if ((DMARunning == 0) && __queueincnt ) {
-
- // setup the direction flag
-
- __DirectionFlag = DMAINPUT;
-
- /* reset the DMA block pointers */
-
- _resetbuffers();
-
- /* return good or bad if the PCM engine is running */
-
- return (ContinueThisBlockInput() ? 1 : 0 );
-
- }
-
- // assume the block is now recording
-
- return(0);
-
- }
- #endif
-
-
- #if FILEIN
- /*\
- |*|----====< StartFileInput >====----
- |*|
- |*| This routine resets the buffer pointers, then starts up the DMA PCM
- |*| input. Nothing else needs to be done.
- |*|
- \*/
- int StartFileInput(f)
- FILE *f;
- {
-
- /* save our local file handle */
-
- __fptr = f;
-
- /* setup our internal direction flag */
-
- __DirectionFlag = DMAINPUT;
-
- /* Reset the # of blocks seen during I/O processing */
-
- ProcessedBlockCount = 0;
-
- /* Flush all buffers and other stuff.. */
-
- _resetbuffers();
- VoiceActivatedSavedCount = 0;
-
- /* start the DMA engine */
-
- return (!StartTheDMAInput(0));
- }
- #endif
-
-
- #if BLOCKOUT
- /*\
- |*|----====< StartBlockOutput >====----
- |*|
- |*| This routine allocates and loads the necessary buffers with data from
- |*| the PCM disk file. Upon return, if there is data available, the
- |*| background task will be called to start the DMA. The file handle will
- |*| be saved in a global variable to be access from other foreground
- |*| routines. a non-zero return value indicates PCM output is playing.
- |*|
- \*/
- int StartBlockOutput(buff)
- char far *buff;
- {
-
- /* setup our internal direction flag */
-
- __DirectionFlag = DMAOUTPUT;
-
- /* Reset the # of blocks seen during I/O processing */
-
- ProcessedBlockCount = 0;
-
- /* load the DMA buffers */
-
- _resetbuffers();
- _loadtheblock (buff);
-
- /* return good or bad if the engine is started */
-
- return (!StartTheDMAOutput(0));
- }
-
-
- /*\
- |*|----====< PlayThisBlock >====----
- |*|
- |*| This routine offers the caller a simplified playback of one
- |*| variable length block of data. The call just needs to call
- |*| OpenPCMBuffering, then PlayThisBlock. The caller just has
- |*| to poll DMARunning to see if the block has completed. Calling
- |*| StopDMAIO will stop the process, if need be.
- |*|
- |*| Also see QueueThisBlock.
- |*|
- |*| Entry:
- |*| p is the far pointer to a block of data (64k max size). If
- |*| the pointer is null, the block will not be queued.
- |*| len is the length of the block in bytes (one based count).
- |*| cb is the callback routine to call when the block is empty.
- |*|
- |*| Returns:
- |*| 0 - block is queued and playing
- |*| 1 - Block failed to start
- |*| 2 - queue is full, try later
- |*|
- \*/
- int PlayThisBlock(p,len,cb)
- char far *p;
- unsigned long len;
- void (far *cb)();
- {
- int n;
-
- // if the pointer is valid, then queue it up
-
- if (p) {
-
- // return if already full
-
- if (__queuein == QUEUESIZE)
- return(2);
-
- // extract the 1st entry from the queue
-
- __queuebuff[__queuein] = p;
- __queuedata += (__queuelen [__queuein] = len);
- __queuecb [__queuein] = cb;
- __queuein = ++__queuein & QUEUEMASK;
- __queueincnt++;
-
- }
-
- // if the blocks are not playing, the let'er rip...
-
- if ((DMARunning == 0) && __queueincnt ) {
-
- // setup to transfer this block
-
- __DirectionFlag = DMAOUTPUT;
-
- // start the process by loading the DMA buffers
-
- return (ContinueThisBlockOutput() ? 0 : 1 );
-
- }
-
- // Indicate this has been queued up
-
- return(0);
- }
-
-
- /*\
- |*|----====< QueueThisBlock >====----
- |*|
- |*| This routine will queue up one block, but not start the PCM
- |*| transfers. Once X number of blocks are queued up, then the caller
- |*| can call PlayThisBlock/RecordThisBlock to start the process.
- |*|
- |*| Also see PlayThisBlock/RecordThisBlock.
- |*|
- |*| Entry:
- |*| p is the far pointer to a block of data (64k max size).
- |*| All pointers are assumed to be valid.
- |*| len is the length of the block in bytes (one based count).
- |*| cb is the callback routine to call when the block is empty.
- |*|
- |*| Returns:
- |*| 0 - block is queued and playing
- |*| 2 - queue is full, try later
- |*|
- \*/
- int QueueThisBlock(p,len,cb)
- char far *p;
- unsigned long len;
- void (far *cb)();
- {
- int n;
-
- // return if already full
-
- if (__queuein == QUEUESIZE)
- return(2);
-
- // if idle, setup our internal direction flag, and the desired length
-
- __queuebuff[__queuein] = p;
- __queuedata += (__queuelen [__queuein] = len);
- __queuecb [__queuein] = cb;
- __queuein = ++__queuein & QUEUEMASK;
- __queueincnt++;
-
- // return all queued up
-
- return(0);
-
- }
-
-
- /*\
- |*|----====< SyncCallBack >====----
- |*|
- |*| This routine will setup a callback to the caller's routine
- |*| at the end of every DMA block interrupt
- |*|
- |*| Returns:
- |*|
- \*/
- int SyncCallBack(cb)
- void (far *cb)();
- {
- int n;
-
- ///////////////////// under construction! /////////////////////////
-
- // just save for a later call...
-
- __synccallback = cb;
-
- }
-
- #endif
-
-
- #if FILEOUT
- /*\
- |*|----====< StartFileOutput >====----
- |*|
- |*| This routine allocates and loads the necessary buffers with data from
- |*| the PCM disk file. Upon return, if there is data available, the
- |*| background task will be called to start the DMA. The file handle will
- |*| be saved in a global variable to be access from other foreground
- |*| routines. a non-zero return value indicates PCM output is playing.
- |*|
- \*/
- int StartFileOutput(f,len)
- FILE *f;
- long len;
- {
-
- /* save our local file handle */
-
- __fptr = f;
-
- /* setup our internal direction flag, and the desired length */
-
- __DirectionFlag = DMAOUTPUT;
- _file_data_length = len;
-
- /* Reset the # of blocks seen during I/O processing */
-
- ProcessedBlockCount = 0;
-
- /* if any data is loaded into the buffer, start the DMA & return */
-
- _resetbuffers();
-
- do {
-
- /* get the next buffer full & exit if done */
-
- if (!_loadthebuffer (f))
- break;
-
- } while (__NextPtr != HeadOfBuffers);
-
- /* return good or bad if the engine is running */
-
- return (!StartTheDMAOutput(0));
-
- }
- #endif
-
-
- #if BLOCKOUT
- /*\
- |*|----====< _loadtheblock >====----
- |*|
- |*| This routine loads the block into the DMA buffer.
- |*|
- \*/
- static int _loadtheblock(buff)
- char far *buff;
- {
-
- /* load the block of data into the DMA buffer */
-
- _rfmemcpy(__NextPtr->buffer, buff, BufferSize );
-
- /* now that the data is secure, fill out the rest of the header to */
- /* allow the background process to "see" this new data */
-
- __NextPtr->status = 1;
- __NextPtr->count = BufferSize;
- __NextPtr = __NextPtr->nextptr; /* advance the list */
- BufferDataCount++;
-
- /* we have data, return the size */
-
- return (BufferSize);
- }
- #endif
-
-
- #if FILEOUT
- /*\
- |*|----====< _loadthebuffer >====----
- |*|
- |*| This routine loads the disk contents into an available buffer.
- |*| A return value of 0 indicates no more data has been loaded.
- |*|
- \*/
- static int _loadthebuffer(f)
- FILE *f;
- {
- char huge *s;
- register int n;
- long l;
-
- /* reset the header data */
-
- __NextPtr->count = __NextPtr->status = 0;
-
- /* exit if there is no data to be read */
-
- if (feof (f) || (!_file_data_length))
- return (0);
-
- /* adjust the max count we want to process from the file */
-
- if (_file_data_length <= BufferSize) {
- l = _file_data_length;
- _file_data_length = 0;
- }
- else {
- _file_data_length -= (l = (BufferSize & 0xffff));
- }
-
- /* read the data from the file */
-
- #if LARGEDATA
- if((n = fread (__NextPtr->buffer, 1,((unsigned int)(l & 0xffff)), f)) == 0)
- return(0);
-
- s = __NextPtr->buffer+n; // point to the end of the block
- #else
- if((n = fread (__LocalBuff, 1,((unsigned int)(l & 0xffff)), f)) == 0)
- return(0);
-
- /* move the data to the linked list. Yuck! double handling of data */
- /* because fread won't take a far pointer in small model! */
-
- s = _rfmemcpy(__NextPtr->buffer, __LocalBuff, n ); // s points to eob
- #endif
-
- // flush to the end if not a full buffer worth of data
-
- if (n < BufferSize)
- FlushBuffer (s,BufferSize-n);
-
- /* now that the data is secure, fill out the rest of the header to */
- /* allow the background process to "see" this new data */
-
- __NextPtr->status = 1;
- __NextPtr->count = BufferSize;
- __NextPtr = __NextPtr->nextptr; /* advance the list */
- BufferDataCount++;
-
- /* we have data, return the size */
-
- return (n);
- }
- #endif
-
-
- #if COMMDATA
- /*\
- |*|----====< _resetbuffers >====----
- |*|
- |*| This routine flushes the contents of the top level buffers
- |*|
- \*/
- _resetbuffers()
- {
-
- /* flush the count and status of every linked list block */
-
- if ((__NextPtr = HeadOfBuffers)) {
-
- /* if there are buffers, reset them all */
-
- do {
-
- /* get the next buffer full & exit if done */
-
- __NextPtr->count = __NextPtr->status = 0;
-
- /* break when we reach the top */
-
- if ((__NextPtr = __NextPtr->nextptr) == 0)
- break;
-
- } while (__NextPtr != HeadOfBuffers);
-
- }
-
- /* reset the global hand shake count. */
-
- BufferDataCount = 0;
- }
- #endif
-
-
- /*\
- |*| end of PCMIOC.C
- \*/
-
-