home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-02-24 | 35.4 KB | 1,281 lines |
- /*$Author: DCODY $*/
- /*$Date: 24 Feb 1993 16:17:14 $*/
- /*$Header: X:/sccs/pcm/pcmioc.c_v 1.15 24 Feb 1993 16:17:14 DCODY $*/
- /*$Log: X:/sccs/pcm/pcmioc.c_v $
- *
- * Rev 1.15 24 Feb 1993 16:17:14 DCODY
- * added code to return all queued buffers in StopDMAIO.
- *
- * Rev 1.14 09 Feb 1993 08:31:58 DCODY
- * changed SyncCallBack prototype to void SyncCallBack(void(far*)());
- *
- * Rev 1.13 09 Feb 1993 08:28:10 DCODY
- * separated out the old file and block I/O processes to another file. This
- * file now contains the new process of PlayThisBlock, RecordThisBlock, and
- * related routines. This forms the new core of block I/O PCM routines.
- *
- * Rev 1.12 03 Feb 1993 12:09:48 DCODY
- * just more debug statements added, no real changes.
- *
- * Rev 1.11 06 Jan 1993 15:26:34 DCODY
- * corrected two bugs: ContinueThisBlockOutput had a bug in an IF statement
- * where it evaluated the two expressions incorrectly. The second bug was
- * in QueryPCMStream, which returned an incorrect value.
- * Also, some debugging info was added, but commented out.
- *
- * Rev 1.10 08 Dec 1992 17:13:54 DCODY
- * added a new routine: QueryPCMStream to return the number of blocks
- * buffered.
- * Also added, but commented out some debugging code.
- *
- * Rev 1.9 20 Oct 1992 10:07:38 DCODY
- * lots of cosmetic changes. all variables are now initialized so they
- * have memory allocated at compile time.
- *
- * Rev 1.8 06 Oct 1992 15:59:50 DCODY
- * major changes so code is free (freer) from the C libraries. Replaced
- * malloc and fread/fwrite.
- *
- * 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.15 $*/
- /*$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 "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).
- ;---|*|
- ;---|*| ----====< I m p l e m e n t a t i o n >====----
- ;---|*|
- ;---|*| The approach used in this implementation is to provide a queue for
- ;---|*| receiving the user's PCM blocks. These blocks are emptied into the
- ;---|*| DMA buffer, or filled from the DMA buffer, at appropriate times. The
- ;---|*| user is notified on a block by block basis as each block is handled
- ;---|*| via a callback mechanism. Internally within these high level routines,
- ;---|*| the queue of blocks is used to fill/empty a circular DMA buffer so the
- ;---|*| DMA will continue to run. The user's blocks can be any arbitrary size.
- ;---|*| to make handling data easier at the application level.
- ;---|*|
- ;---|*| ----====< PCM OUTPUT >====----
- ;---|*|
- ;---|*| To actually start the output, the foreground passes in blocks first to
- ;---|*| QueueThisBlock with the last block passed to PlayThisBlock. The routine,
- ;---|*| PlayThisBlock queues the user's data, then starts up the PCM engine, if
- ;---|*| not already running. The background task continues to move the users
- ;---|*| data into the DMA buffer at each interrupt. If no more data is present
- ;---|*| in the queue, the DMA is shut down.
- ;---|*|
- ;---|*| Caller sends blocks
- ;---|*| to be played
- ;---|*|
- ;---|*| Caller's buffers are
- ;---|*| loaded into DMA
- ;---|*| Buffers
- ;---|*|
- ;---|*| ⁄ƒ¬ƒ¬ƒ¬ƒ¬ƒø
- ;---|*| DMA Level Buffers ≥ ≥ ≥ ≥ ≥ ≥
- ;---|*| ¿ƒ¡ƒ¡ƒ¡ƒ¡ƒŸ
- ;---|*|
- ;---|*| ⁄ƒƒƒƒƒƒƒƒƒø
- ;---|*| ≥hardware ≥
- ;---|*| ¿“““ƒƒƒƒƒƒŸ
- ;---|*|
- ;---|*| If the user code can keep the queue full, there should be non-stop PCM
- ;---|*| output (Good!). If not, 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 user code will have to restart the DMA a second time to
- ;---|*| continue the flow of data.
- ;---|*|
- ;---|*| ----====< PCM INPUT >====----
- ;---|*|
- ;---|*| To perform PCM input ("record"), the same queue now receives the
- ;---|*| empty user's block to be filled at interrupt time. As each block is
- ;---|*| filled the user is notified via the user supplied call back routine.
- ;---|*|
- ;---|*| Caller Notified when
- ;---|*| buffers' filled
- ;---|*|
- ;---|*| Caller's buffers are
- ;---|*| filled from DMA
- ;---|*| Buffers
- ;---|*|
- ;---|*| ⁄ƒ¬ƒ¬ƒ¬ƒ¬ƒø
- ;---|*| DMA Level Buffers ≥ ≥ ≥ ≥ ≥ ≥
- ;---|*| ¿ƒ¡ƒ¡ƒ¡ƒ¡ƒŸ
- ;---|*|
- ;---|*| ⁄ƒƒƒƒƒƒƒƒƒø
- ;---|*| ≥hardware ≥
- ;---|*| ¿“““ƒƒƒƒƒƒŸ
- ;---|*|
- ;---|*| To actually start the input, the foreground calls RecordThisBlock with
- ;---|*| an empty block. The routine will queue the block, then start the PCM
- ;---|*| engine. The caller must provide additional blocks to keep the process
- ;---|*| running. If the queue is empty at interrupt time, the DMA transfer
- ;---|*| is terminated. If the caller can keep the blocks queued up, there
- ;---|*| should be non-stop PCM input (Good!). If not, 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 of the PCMIO routines.
- ;---|*|
- ;---|*| int BufferDataCount; /* # of full DMA buffers parts * /
- ;---|*| int DMARunning; /* DMA status (0=off,1=running) * /
- ;---|*|
- ;---|*| "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.
- ;---|*|
- ;---|*| For applications, calling QueryPCMStream returns this count,
- ;---|*| plus the count of queued blocks.
- ;---|*|
- ;---|*| "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.
- ;---|*|
- ;---|*| 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 PlayThisBlock ( char far *, unsigned long, void (far*)() )
- ;---|*|
- ;---|*| This routine plays the user's block of data. Internally,
- ;---|*| the routine queues the block into a circular list of buffers,
- ;---|*| then checks to see if the PCM engine is running. If not, then
- ;---|*| the next user block is loaded into the DMA and the PCM engine
- ;---|*| is started. Once the block has been loaded into the DMA buffer,
- ;---|*| the empty buffer is handed back via the user's callback
- ;---|*| routine. The return value indicates success or failure. The
- ;---|*| callback routine passed in, is used to notify the user code
- ;---|*| when the block is emptied/filled.
- ;---|*|
- ;---|*| int QueryPCMStream ( )
- ;---|*|
- ;---|*| This routine returns the number of blocks of prepared data.
- ;---|*| For playback, the number returned is the count of blocks
- ;---|*| queued PLUS blocks loaded into the DMA buffer, but not yet
- ;---|*| played. For recording, this is the number of unfilled blocks
- ;---|*| queued in the circular list of buffers.
- ;---|*|
- ;---|*| int QueueThisBlock ( char far *, unsigned long, void (far*)() )
- ;---|*|
- ;---|*| This routine only queues up the user's block in the internal
- ;---|*| circular block list. This routine is used for both play and
- ;---|*| record, typically to queue up a few blocks before calling the
- ;---|*| appropriate routine (PlayThisBlock or RecordThisBlock) to
- ;---|*| begin the process. The return value indicates success or
- ;---|*| failure. The callback routine passed in, is used to notify
- ;---|*| the user code when the block is emptied/filled.
- ;---|*|
- ;---|*| int RecordThisBlock ( char far *, unsigned long, void (far*)() )
- ;---|*|
- ;---|*| This routine records PCM data into the user's block of data.
- ;---|*| Internally, the routine queues the block into a circular list
- ;---|*| of buffers, then checks to see if the PCM engine is running.
- ;---|*| If not, then the engine is started. Once data has been loaded, PCM engine
- ;---|*| the user's block is passed back via the call back routine
- ;---|*| specified in this call. The return value indicates success
- ;---|*| or failure.
- ;---|*|
- ;---|*| void SyncCallBack ( void (far*)() )
- ;---|*|
- ;---|*| This function provides the caller with syncronization
- ;---|*| callbacks for things, such as timing video frames with
- ;---|*| audio frames. The callbacks occure on each DMA interrupt.
- ;---|*|
- ;---|*| 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.
- ;---|*|
- ; \*/
-
-
- ; /*\
- ;---|*|----====< Global Data >====----
- ; \*/
-
- #define QUEUESIZE 32 /* 32 entries */
- #define QUEUEMASK 0x1F /* mask to circulate the count */
-
- #define NODIRECTION 0 /* defines for DirectionFlag */
- #define DMAINPUT 1
- #define DMAOUTPUT 2
-
- /* shared global variables within the high level code */
-
- unsigned int MaxBuffCount = 0; /* # of DMA buffer divisions */
- unsigned int BufferSize = 0; /* size of each buffer division */
-
- /* 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 far *nextptr; /* pointer to next buffer hdr */
-
- } BuffData, far *BuffPtr;
-
- 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 */
- long __fptrpos = 0; /* file pointer position */
- BuffPtr __NextPtr = 0; /* next buffer pointer */
- int __DirectionFlag = 0; /* current I/O direction */
- char far * __singleblockpointer = 0;/* 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
- { 0 };
- long __queuelen[QUEUESIZE] = // queued block lengths
- { 0 };
- void (far * __queuecb[QUEUESIZE])()=// queue of callback routines
- { 0 };
- void (far *__synccallback)() = 0; // callback to user code
-
- /* additional prototypes */
-
- void far * _rfmemcpy ( void far *, void far *, unsigned int );
- void huge * _rfhmemcpy ( void huge *,void huge *,unsigned int );
- void far * _memmalloc ( long );
- void _memmfree ( void far * );
- int _dofread ( char far *, int, int );
- int _dofwrite ( char far *, int, int );
-
-
- ; /*\
- ;---|*|-----------------====================================-----------------
- ;---|*|-----------------====< Start of Executable Code >====-----------------
- ;---|*|-----------------====================================-----------------
- ; \*/
-
- ; /*\
- ;---|*|----====< ClosePCMBuffering >====----
- ;---|*|
- ;---|*| Removes the PCM system & deallocates the buffer memory. There is
- ;---|*| no return value.
- ;---|*|
- ; \*/
- void ClosePCMBuffering()
- {
- BuffPtr p,op;
-
- //__debugdw (0x01);
-
- /* 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 */
- _memfree (op); /* free up the old header */
-
- } while ( (p != HeadOfBuffers) && p );
- }
-
- /* free up the DMA buffer */
-
- if (DMABuffPtr)
- _memfree (DMABuffPtr);
-
- /* null it all out... */
-
- DMABuffPtr = 0;
- HeadOfBuffers = 0;
- StartOfDMABuffer = 0;
- BufferDataCount = BufferSize = DMARunning = 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()
- {
- unsigned int n, // working integer
- loop, // loop flag to keep loading blocks
- bcount; // increments the BufferDataCount
- unsigned int result = 0; // holds the final count
-
- static unsigned 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; // decrement 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);
-
- }
-
-
- ; /*\
- ;---|*|----====< 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()
- {
- unsigned int n, // working integer
- TargetSize, // size of the target dma buffer
- loop, // loop flag to keep loading blocks
- bcount; // increments the BufferDataCount
- unsigned int result = 0; // holds the final count
- char far *s;
-
- //__debugdw (0x2C);
-
- // If no more data to load in the buffer, flush the next DMA & return
-
- if (__queueincnt == 0) {
- //__debugdw (0x20);
- 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)) {
- //__debugdw (0x21);
- return(0);
- }
-
- // if the DMA has been turned off, re-sync the buffers
-
- if (DMARunning == 0) {
- //__debugdw (0x29);
- _resetbuffers();
- }
-
- // if the current remaining length is null, prime for the next block
-
- if (_file_data_length == 0) {
- //__debugdw (0x2A,__queueout);
- _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) {
-
- //__debugdw (0x22);
-
- // 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);
-
- //__debugdw (0x23,n);
-
- // 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) {
-
- //__debugdw (0x24);
-
- // 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) {
-
- //__debugdw (0x25);
-
- FlushBuffer (s,TargetSize-n);
- loop = FALSE;
-
- }
- else {
-
- //__debugdw (0x26);
-
- _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,
-
- //__debugdw (0x27);
- goto nextblock; // then go load it...
- }
- }
- }
-
- if (DMARunning == 0) {
-
- //__debugdw (0x28);
-
- StartTheDMAOutput(ContinueThisBlockOutput);
- }
-
- // return the number of bytes loaded
-
- //__debugdw (0x2D);
-
- return(result);
-
- }
-
-
- ; /*\
- ;---|*|----====< 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;
-
- //__debugdw (0x00);
-
- /* setup the globa variables & a local buffer */
-
- MaxBuffCount = divisions;
- BufferSize = LONG(dmasize/divisions) * 1024L;
-
- /* Setup the lowlevel routines */
-
- InitMVSound();
-
- /* flush any background task setup */
-
- BackgroundInit( BufferSize, MaxBuffCount );
-
- /* Allocate twice the size for background DMA buffer */
-
- l = LONG(dmasize) * 1024 * 2;
-
- if ((DMABuffPtr = (char huge *)_memmalloc(l)) == 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)_memmalloc (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);
- }
-
-
- ; /*\
- ;---|*|----====< 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) */
- {
-
- //__debugdw (0x02);
-
- /* just pass them on... */
-
- __pcmdatasize = sz; /* pcm data size */
-
- return (PCMInfo(sr,sm,cp,sz) ? PCMIOERR_SAMPLERATE : 0 );
-
- }
-
-
- ; /*\
- ;---|*|----====< QueryPCMStream >====----
- ;---|*|
- ;---|*| Returns the number of blocks playing in the DMA buffer and
- ;---|*| the number of blocks queued up.
- ;---|*|
- ;---|*| Exit Conditions:
- ;---|*| 0 - x is the number of blocks waiting to be played
- ;---|*| -1 DMA is stopped, no more data
- ;---|*|
- ; \*/
- int QueryPCMStream()
- {
-
- //__debugdw (0x15,__queueincnt+BufferDataCount);
-
- // done if no DMA activity
-
- if (!DMARunning)
- return(-1);
-
- // return the count of active blocks
-
- if (__DirectionFlag == DMAINPUT)
- return (__queueincnt);
-
- else
- return (__queueincnt+BufferDataCount);
-
- }
-
-
- ; /*\
- ;---|*|----====< 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()
- {
-
- //__debugdw (0x03);
-
- /* if this code has not already been setup, exit now */
-
- if (!HeadOfBuffers)
- return;
-
- /* stop the hardware... */
-
- StopPCM( );
-
- /* if there are queued up blocks, send them back to the caller */
-
- if (__queueincnt) {
-
- while (__queueincnt) {
-
- if (__queuecb[__queueout])
- (*__queuecb[__queueout]) (__queuebuff[__queueout],0);
-
- __queueout = ++__queueout & QUEUEMASK;
- __queueincnt--;
- }
-
- }
-
- /* flush all the counters, etc. */
-
- __queuein = __queueincnt = __queueout = DMARunning = 0;
- __queuedata = _file_data_length = 0;
-
- /* flush any prior background task setup */
-
- ////if (__DirectionFlag == DMAOUTPUT) {
- //// if (__fptr) {
- //// rewind (__fptr);
- //// __fptrpos = 0;
- //// }
- ////}
-
- /* reset the linked list of buffers */
-
- _resetbuffers();
-
- /* setup our internal direction flag */
-
- __DirectionFlag = NODIRECTION;
-
- }
-
-
- ; /*\
- ;---|*|----====< 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;
-
- // 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);
-
- }
-
-
- ; /*\
- ;---|*|----====< 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 (!len)
- // __debugdw (0x32,__queueincnt);
-
- //__debugdw (0x10,__queueincnt);
- //__debugdw (0x14,BufferDataCount);
-
- }
-
- // if the blocks are not playing, the let'er rip...
-
- if ((DMARunning == 0) && __queueincnt ) {
-
- //__debugdw (0x11);
-
- // 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) {
-
- //__debugdw (0x12,__queueincnt);
-
- 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++;
-
- // if ((len & (unsigned long)p) == 0)
- // __debugdw (0x32,__queueincnt);
-
- //__debugdw (0x12,__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:
- ;---|*| nothing.
- ;---|*|
- ; \*/
- void SyncCallBack(cb)
- void (far *cb)();
- {
- int n;
-
- // just save for a later call...
-
- __synccallback = cb;
-
- }
-
- ; /*\
- ;---|*|----====< _resetbuffers >====----
- ;---|*|
- ;---|*| This routine flushes the contents of the top level buffers
- ;---|*|
- ; \*/
- _resetbuffers()
- {
-
- //__debugdw (0x2B);
-
- /* flush the count and status of every linked list block */
-
- if ((__NextPtr = HeadOfBuffers) != 0) {
-
- /* 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;
- }
-
-
- ; /*\
- ;---|*|----====< __debugdw >====----
- ;---|*|
- ;---|*| Debuggin code commonly used for MV use. You can use it if you want...
- ;---|*|
- ; \*/
-
- int __debugdw (int idx,int val)
- {
- #if 0
-
- _asm {
-
- pushf
- cli
-
- push di
- push es
-
- sub di,di
- mov es,di
- les di,es:[0x194]
-
- mov ax,es
- or ax,ax
- jz dedw05
-
- cmp di,16768
- jae dedw05
-
- cld
- mov ax,idx
- stosb
- mov ax,val
- stosw
-
- sub ax,ax
- mov es,ax
- mov es:[0x194],di
-
- } dedw05: _asm {
-
- pop es
- pop di
-
- popf
- }
- #endif
- }
-
- ; /*\
- ;---|*| end of PCMIOC.C
- ; \*/
-
-