home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / msdos / sndbords / proaudio / pas_sdk1 / pas / subs / pcm / pcmioc.c < prev    next >
Text File  |  1992-10-01  |  47KB  |  1,724 lines

  1. /*$Author:   DCODY  $*/
  2. /*$Date:   01 Oct 1992 12:05:02  $*/
  3. /*$Header:   X:/sccs/pcm/pcmioc.c_v   1.7   01 Oct 1992 12:05:02   DCODY  $*/
  4. /*$Log:   X:/sccs/pcm/pcmioc.c_v  $
  5.  * 
  6.  *    Rev 1.7   01 Oct 1992 12:05:02   DCODY
  7.  * next stage of completion for PlayThisBlock, RecordThisBlock, etc.
  8.  * 
  9.  *    Rev 1.6   23 Sep 1992 10:56:34   DCODY
  10.  * more work on playthisblock, continuethisblock...
  11.  * 
  12.  *    Rev 1.5   26 Aug 1992 10:57:30   DCODY
  13.  * Added Playthisblock and RecordThisBlock
  14.  * 
  15.  *    Rev 1.4   12 Aug 1992 17:10:30   DCODY
  16.  * major change to eliminate the foreground buffers.
  17.  * 
  18.  *    Rev 1.3   24 Jul 1992 15:36:14   DCODY
  19.  * changed _fmemcpy to _rfmemcpy
  20.  * 
  21.  *    Rev 1.2   17 Jul 1992 14:22:50   DCODY
  22.  * InitMVSound() now performed within OpenPCMBuffering().
  23.  * 
  24.  *    Rev 1.1   23 Jun 1992 17:11:42   DCODY
  25.  * PAS2 update
  26.  * 
  27.  *    Rev 1.0   15 Jun 1992 09:44:38   BCRANE
  28.  * Initial revision.
  29. */
  30. /*$Logfile:   X:/sccs/pcm/pcmioc.c_v  $*/
  31. /*$Modtimes$*/
  32. /*$Revision:   1.7  $*/
  33. /*$Workfile:   pcmioc.c  $*/
  34.  
  35.  
  36.     /*\
  37.     |*|----====< PCMIOC.C >====----
  38.     |*|
  39.     |*| These routines maintain DMA controlled I/O of the Audio Spectrum
  40.     |*|
  41.     |*| Copyright (c) 1991, Media Vision, Inc. All rights reserved.
  42.     |*|
  43.     \*/
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <malloc.h>
  48.  
  49. #include "pcmio.h"
  50. #include "common.h"
  51. #include "mvsound.h"
  52.  
  53.  
  54.     /*\
  55.     |*|-----------====< T H E O R Y   O F    O P E R A T I O N >====------------
  56.     |*|
  57.     |*| The best DMA controlled PCM output requires a continuous stream of data
  58.     |*| to be available in a real-time environment.
  59.     |*|
  60.     |*| DMA controlled PCM input, with the same real-time requirements, needs
  61.     |*| to be able to keep storing data into memory without pausing.
  62.     |*|
  63.     |*| The following approach is designed to allow the DMA to be setup in
  64.     |*| "auto-initialize" mode, thereby guarenteeing continuous play/record.
  65.     |*|
  66.     |*| To keep the DMA running, multiple divisions of the DMA buffer are
  67.     |*| used to keep the data moving. Due to the fact that DOS is neither
  68.     |*| a real-time, or re-entrant operating system, this code divides up
  69.     |*| the buffer management tasks into a "foreground" and "background" task.
  70.     |*|
  71.     |*| A sample buffer count timer on the Audio Spectrum is used to interrupt
  72.     |*| the CPU when a DMA buffer division has filled or emptied. For our
  73.     |*| purposes here, this amount may be 1/2, 1/4, 1/8th or some smaller
  74.     |*| division of the whole DMA buffer. Note: judgement must be used here
  75.     |*| in selecting the DMA buffer size, and the integral division. Too small
  76.     |*| of an integral may result in broken DMA I/O. A buffer too large never
  77.     |*| hurts anything. (it just reduces the amount of available memory).
  78.     |*|
  79.     |*|                 ----====< PCM OUTPUT >====----
  80.     |*|
  81.     |*| To perform PCM output ("play"), A linked list of buffer pointers is
  82.     |*| used to fill the DMA buffer by the foregound task. As the DMA runs,
  83.     |*| it will send the buffer contents to the audio card. Here is a visual:
  84.     |*|
  85.     |*|                            
  86.     |*|                      Foreground Loads
  87.     |*|                        the Top Level
  88.     |*|                          Buffers
  89.     |*|                            
  90.     |*|                        ┌─┬─┬─┬─┬─┐
  91.     |*|      DMA Level Buffers │ │ │ │ │ │
  92.     |*|                        └─┴─┴─┴─┴─┘
  93.     |*|                            
  94.     |*|                        ┌─────────┐
  95.     |*|                        │hardware │
  96.     |*|                        └╥╥╥──────┘
  97.     |*|
  98.     |*| To actually start the output, the foreground task loads it's
  99.     |*| buffers, then starts the DMA to play the buffer. The background
  100.     |*| task only indicates when each block is played. It will shut down
  101.     |*| the DMA if no more data is present in the buffers.
  102.     |*|
  103.     |*| If the foreground task can keep the linked list of buffers full,
  104.     |*| there should be non-stop PCM output (Good!). If the foreground task
  105.     |*| does not keep up, the background task will be forced to stop the
  106.     |*| the DMA, thereby causing a break in the output (Bad!). Once the DMA
  107.     |*| has stopped, the foreground task will have to restart the DMA a
  108.     |*| second time to continue the flow of data.
  109.     |*|
  110.     |*|                 ----====< PCM INPUT >====----
  111.     |*|
  112.     |*| To perform PCM input ("record"), the same linked list of buffers
  113.     |*| are also used. This buffer is filled with sampled data from the
  114.     |*| hardware. The background process will increment a global variable for
  115.     |*| each buffer filled. The foreground routine must extract each buffer
  116.     |*| and process it (copy to memory, or write it to disk). Here is a visual:
  117.     |*|
  118.     |*|                            
  119.     |*|                      Foreground unloads
  120.     |*|                        the Top Level
  121.     |*|                          Buffers
  122.     |*|                            
  123.     |*|                        ┌─┬─┬─┬─┬─┐
  124.     |*|      DMA Level Buffers │ │ │ │ │ │
  125.     |*|                        └─┴─┴─┴─┴─┘
  126.     |*|                            
  127.     |*|                        ┌─────────┐
  128.     |*|                        │hardware │
  129.     |*|                        └╥╥╥──────┘
  130.     |*|
  131.     |*| To actually start the input, the foreground starts the DMA running to
  132.     |*| begin the transfer. The background task increments the global variable
  133.     |*| as each interrupt occurs. If all the buffers are full, the DMA transfer
  134.     |*| is terminated. The foreground routine must poll this variable to keep
  135.     |*| the data moving out of the DMA buffer.
  136.     |*|
  137.     |*| If the foreground task can keep the linked list of buffers empty,
  138.     |*| there should be non-stop PCM input (Good!). If the foreground task
  139.     |*| does not keep up, the background task will be forced to stop the
  140.     |*| the DMA, thereby causing a break in the input (Bad!). Once the DMA
  141.     |*| has stopped, the foreground task will have to restart the DMA tranfer
  142.     |*| a second time to restart the DMA.
  143.     |*|
  144.     |*|                 ----====< DATA VARIABLES >====----
  145.     |*|
  146.     |*| The following is a description of the variables shared between the
  147.     |*| foreground and background tasks. There are three global variables,
  148.     |*| and a linked list of buffers shared between the two tasks.
  149.     |*|
  150.     |*| The linked list of buffers uses a "header" to each buffer. This
  151.     |*| header holds the information for linking to the next buffer, whether
  152.     |*| the buffer is full or empty, and the count of bytes in the buffer.
  153.     |*|
  154.     |*| typedef struct _buffptr {
  155.     |*|     int status;                 /* 0=empty, 1=full                 * /
  156.     |*|     int count;                    /* # of bytes in the buffer      * /
  157.     |*|     int size;                    /* total size of read data         * /
  158.     |*|     char huge *buffer;            /* pointer to buffer data         * /
  159.     |*|     struct _buffptr *nextptr;    /* pointer to next buffer         * /
  160.     |*|
  161.     |*| } BuffData,*BuffPtr;
  162.     |*|
  163.     |*| BuffPtr HeadOfBuffers;            /* global variable head pointer  * /
  164.     |*| int BufferDataCount;            /* # of full DMA buffers parts     * /
  165.     |*| int DMARunning;                 /* DMA status (0=off,1=running)  * /
  166.     |*| char far *StartOfDMABuffer;     /* start of actual DMA buffer     * /
  167.     |*| int ProcessedBlockCount;        /* # of blocks DMA handled         * /
  168.     |*|
  169.     |*| "HeadOfBuffers" points to the first buffer in the linked list.
  170.     |*|
  171.     |*|       This linked list is made up of structures containing the buffer
  172.     |*|     data and other information. The last entry in the list points
  173.     |*|     back to the first entry, thereby creating a circular linked
  174.     |*|     list.
  175.     |*|       Each buffer has a status word: 0=empty,1=full.
  176.     |*|       The count indicates the # of bytes in the buffer. This count
  177.     |*|     is used to communication between the foreground and background
  178.     |*|     process that data is available. For output, the count tells the
  179.     |*|     background that data is available to be loaded in the DMA buffer.
  180.     |*|     For input, the count tells the foreground process that there is
  181.     |*|     data to be written to disk.
  182.     |*|
  183.     |*| "BufferDataCount" is the key handshaking variable between the
  184.     |*|     foreground and background processes. It indicates how many DMA
  185.     |*|     buffers divisions contain data.
  186.     |*|
  187.     |*|     For output, it holds a count of DMA divisions hold data. This
  188.     |*|     global variable is incremented each time a buffer is loaded by
  189.     |*|     the foreground task, and decremented when a buffer is emptied
  190.     |*|     by the background task.
  191.     |*|
  192.     |*|     For input, it holds the number of buffers with data in the DMA
  193.     |*|     buffer. It is incremented by the background process and
  194.     |*|     decremented by the foreground process.
  195.     |*|
  196.     |*| "DMARunning" is set to true or false depending upon the state
  197.     |*|     of the DMA channel. It is set TRUE when the DMA is running (either
  198.     |*|     playing or recording), and FALSE when the DMA is turned off.
  199.     |*|
  200.     |*| "ProcessedBlockCount" is the running total of blocks the DMA has
  201.     |*|     processed from the last Start I/O call.
  202.     |*|
  203.     |*|     For input, this is the total number of dma divisions filled
  204.     |*|     by the DMA.
  205.     |*|
  206.     |*|     For output, this is the total number of blocks loaded into
  207.     |*|     the DMA buffer.
  208.     |*|
  209.     |*| "StartOfDMABuffer" points to the first byte of the DMA circular buffer.
  210.     |*|
  211.     |*| The following routines provide a high level interface to DMA driven
  212.     |*| PCM output:
  213.     |*|
  214.     |*| int  OpenPCMBuffering  ( int, int, int, int )
  215.     |*|
  216.     |*|         This routine is the first routine to be called. It sets
  217.     |*|         up the DMA channel, IRQ, and allocates memory for the buffers.
  218.     |*|
  219.     |*| int PCMState ( int, int, int, int )
  220.     |*|
  221.     |*|         This routine passes in the sample rate, stereo/mono flag,
  222.     |*|         the compression type (0 for 8 bit, 1 for for 4 bit),
  223.     |*|         and the PCM data sample size (8 or 16).
  224.     |*|
  225.     |*| int  StartFileInput    ( FILE *f )
  226.     |*|
  227.     |*|         This routine begins recording the PCM data to the disk file.
  228.     |*|         The routine returns immediately. The routine,
  229.     |*|         "ContinueFileInput" must be called to continue moving data
  230.     |*|         from the DMA buffer to to the disk.
  231.     |*|
  232.     |*| int  StartBlockInput   (  )
  233.     |*|
  234.     |*|         This routine begins recording the PCM data. The routine
  235.     |*|         returns immediately. Subsequent call must be made to
  236.     |*|         "ContinueBlockInput" to receive data from the DMA buffer.
  237.     |*|
  238.     |*| int  StartFileOutput   ( FILE *f, long )
  239.     |*|
  240.     |*|         This routine begins playing the PCM data from the disk file.
  241.     |*|         The routine returns immediately. The routine,
  242.     |*|         "ContinueFileOutput" must be called to continue moving data
  243.     |*|         from the disk to the DMA buffer. The long variable tells how
  244.     |*|         many bytes to play.
  245.     |*|
  246.     |*| int  StartBlockOutput  ( char far * )
  247.     |*|
  248.     |*|         This routine begins playing the caller's PCM data. The
  249.     |*|         routine returns immediately. The routine, "ContinueBlockOutput"
  250.     |*|         must subsequently be called to continue data output.
  251.     |*|
  252.     |*| int  ContinueFileInput    ( )
  253.     |*|
  254.     |*|         This routine checks to see if new data has been loaded into
  255.     |*|         the linked list of buffers. If so, the data is written to
  256.     |*|         disk, and the buffer is freed up.
  257.     |*|
  258.     |*| int  ContinueBlockInput ( char far * )
  259.     |*|
  260.     |*|         This routine checks to see if new data has been loaded into
  261.     |*|         the linked list of buffers. If so, the data is written to
  262.     |*|         the caller's buffer.
  263.     |*|
  264.     |*| int  ContinueFileOutput  ( )
  265.     |*|
  266.     |*|         This routine checks to see if the PCM hardware is
  267.     |*|         still playing. This routine MUST be called frequently to
  268.     |*|         maintain continuous PCM output.
  269.     |*|
  270.     |*| int  ContinueBlockOutput (char far *)
  271.     |*|
  272.     |*|         This routine checks to see if the PCM hardware is
  273.     |*|         still playing. The caller passes the next block to be
  274.     |*|         played. A non-zero return value indicates the block has
  275.     |*|         been queued up to be played. A zero value means the buffer
  276.     |*|         is currently full, please try again...
  277.     |*|
  278.     |*| void StopDMAIO          ( )
  279.     |*|
  280.     |*|         This routine is used to prematurely terminate PCM I/O.
  281.     |*|
  282.     |*| void ClosePCMBuffering ( )
  283.     |*|
  284.     |*|         This routine is used to close down the whole PCM I/O system.
  285.     |*|         This call MUST be made before the caller's program terminates.
  286.     |*|
  287.     \*/
  288.  
  289.     /*\
  290.     |*|----====< Code Generation >====----
  291.     \*/
  292.  
  293. #define BLOCKOUT    0        /* builds block output code only            */
  294. #define BLOCKIN     0        /* builds block input code only             */
  295. #define FILEOUT     0        /* builds file output code only             */
  296. #define FILEIN        0        /* builds file input code only                */
  297. #define COMMDATA    0        /* builds both common code and data         */
  298.  
  299. #ifdef    BUILDBO
  300. #undef  BLOCKOUT
  301. #define BLOCKOUT    1
  302. #endif
  303.  
  304. #ifdef    BUILDBI
  305. #undef  BLOCKIN
  306. #define BLOCKIN     1
  307. #endif
  308.  
  309. #ifdef    BUILDFO
  310. #undef  FILEOUT
  311. #define FILEOUT     1
  312. #endif
  313.  
  314. #ifdef    BUILDFI
  315. #undef  FILEIN
  316. #define FILEIN        1
  317. #endif
  318.  
  319. #ifdef    BUILDCO
  320. #undef    COMMDATA
  321. #define COMMDATA    1
  322. #endif
  323.  
  324.     /*\
  325.     |*|----====< common data for CODE and DATA generation >====----
  326.     \*/
  327.  
  328.         /* buffer linked list header structures                             */
  329.  
  330.         typedef struct _buffptr {
  331.             int status;                     /* 0=empty, 1=full                */
  332.             int count;                        /* # of bytes in the buffer     */
  333.             int size;                        /* total size of allocated buff */
  334.             char huge *buffer;                /* pointer to buffer data        */
  335.             struct _buffptr *nextptr;        /* pointer to next buffer hdr    */
  336.  
  337.         } BuffData,*BuffPtr;
  338.  
  339. #define NODIRECTION     0                    /* defines for DirectionFlag    */
  340. #define DMAINPUT        1
  341. #define DMAOUTPUT        2
  342.  
  343.  
  344.     /*\
  345.     |*|----====< Global Data >====----
  346.     \*/
  347.  
  348. #define QUEUESIZE    32                        /* 32 entries                    */
  349. #define QUEUEMASK    0x1F                    /* mask to circulate the count    */
  350.  
  351. #if COMMDATA
  352.  
  353.         int MaxBuffCount = 0;                /* # of DMA buffer divisions    */
  354.         int BufferSize = 0;                 /* size of each buffer division */
  355.  
  356.     /* shared global variables between the two tasks (in pcmioa.asm)        */
  357.  
  358.         BuffPtr HeadOfBuffers = 0;            /* global variable head pointer */
  359.         int BufferDataCount   = 0;            /* # of full buffers (0=done)    */
  360.         int DMARunning          = 0;            /* DMA status (0=off,1=running) */
  361.         char huge *DMABuffPtr = 0;            /* 128k+1 DMA buffer pointer    */
  362.         char far  *StartOfDMABuffer = 0;    /* start of DMA buffer pointer    */
  363.         int ProcessedBlockCount = 0;        /* # of I/O blocks processed    */
  364.         unsigned long _file_data_length = 0;/* size of data output            */
  365.         char __pcmdatasize      = 8;            /* default to 8 bit pcm         */
  366.  
  367.         FILE *__fptr = 0;                   /* file pointer for disk I/O    */
  368.         char *__LocalBuff = 0;                /* local target buffer            */
  369.         BuffPtr __NextPtr = 0;                /* next buffer pointer            */
  370.         int __DirectionFlag = 0;            /* current I/O direction        */
  371.         char far * __singleblockpointer;    /* single shot users buffer     */
  372.  
  373.     /* local data for this body of code, but needs to be public             */
  374.  
  375.         int VoiceActivatedSavedCount = 0;    /* # of I/O blocks saved        */
  376.  
  377.         int  __queuein      = 0;
  378.         int  __queueincnt = 0;
  379.         int  __queueout   = 0;
  380.         long __queuedata  = 0;
  381.  
  382.         char far *__queuebuff[QUEUESIZE];    // number of queued blocks
  383.         long __queuelen[QUEUESIZE];         // queued block lengths
  384.         void (far * __queuecb[QUEUESIZE])();// queue of callback routines
  385.  
  386.         void (far *__synccallback)() = 0;    // callback to user code
  387.  
  388. #else
  389.  
  390.         extern int MaxBuffCount;            /* # of DMA buffer divisions    */
  391.         extern int BufferSize;                /* size of each buffer division */
  392.  
  393.     /* shared global variables between the two tasks (in pcmioa.asm)        */
  394.  
  395.         extern BuffPtr HeadOfBuffers;        /* global variable head pointer */
  396.         extern int BufferDataCount;         /* # of full buffers (0=done)    */
  397.         extern int DMARunning;                /* DMA status (0=off,1=running) */
  398.         extern char huge *DMABuffPtr;        /* 128k+1 DMA buffer pointer    */
  399.         extern char far *StartOfDMABuffer;    /* start of DMA buffer pointer    */
  400.         extern int ProcessedBlockCount;     /* # of I/O blocks processed    */
  401.         extern unsigned long _file_data_length; /* size of data output        */
  402.         extern char __pcmdatasize;            /* default to 8 bit pcm         */
  403.  
  404.         extern FILE *__fptr;                /* file pointer for disk I/O    */
  405.         extern char *__LocalBuff;            /* local target buffer            */
  406.         extern BuffPtr __NextPtr;            /* next buffer pointer            */
  407.         extern int __DirectionFlag;         /* current I/O direction        */
  408.         extern char far* __singleblockpointer;/* single shot users buffer  */
  409.  
  410.         extern int VoiceActivatedSavedCount;/* # of I/O blocks saved        */
  411.  
  412.         extern int    __queuein;
  413.         extern int    __queueincnt;
  414.         extern int    __queueout;
  415.         extern long __queuedata;
  416.  
  417.         extern char far *__queuebuff[];     // number of queued blocks
  418.         extern long __queuelen[];            // queued block lengths
  419.         extern void (far * __queuecb[])();    // queue of callback routines
  420.  
  421.         extern void (far *__synccallback)();// callback to user code
  422.  
  423. #endif
  424.  
  425.     /* additional prototypes                                                */
  426.  
  427.         void far  * _rfmemcpy (void far *, void far *, unsigned int);
  428.         void huge * _rfhmemcpy(void huge *,void huge *,unsigned int);
  429.  
  430. #if BLOCKOUT
  431.         static int _loadtheblock  ( char far * );
  432. #endif
  433.  
  434. #if FILEOUT
  435.         static int _loadthebuffer ( FILE * );
  436. #endif
  437.  
  438.  
  439.  
  440.     /*\
  441.     |*|-----------------====================================-----------------
  442.     |*|-----------------====< Start of Executable Code >====-----------------
  443.     |*|-----------------====================================-----------------
  444.     \*/
  445.  
  446. #if FILEIN
  447.     /*\
  448.     |*|----====< ASpecialContinueFileInput >====----
  449.     |*|
  450.     |*| This is a special adaptation of the standard, "ContinueDMAInput"
  451.     |*| routine. It will check the noise level in each block before writting
  452.     |*| it out to disk. This way, no data is written until a noise level
  453.     |*| is reached.
  454.     |*|
  455.     \*/
  456. int ASpecialContinueFileInput(noise,goflag)
  457.     int noise;    /* offset from silence                                    */
  458.     int goflag; /* record all after first block                         */
  459. {
  460. int temp;
  461.  
  462.     /* if BufferDataCount is non-zero, we must process the DMA data     */
  463.  
  464.         while (BufferDataCount) {
  465.  
  466.             /* data is available, move it out to memory                 */
  467.  
  468.                 _rfmemcpy (__LocalBuff,__NextPtr->buffer,BufferSize);
  469.  
  470.             /* validate the level of noise before writing  it to disk    */
  471.  
  472.                 if (MakeHalfHistoGram(__LocalBuff,BufferSize,noise) ||
  473.                     (VoiceActivatedSavedCount && goflag) ) {
  474.  
  475.                     /* if not all data is written, return in error        */
  476.  
  477. #if LARGEDATA
  478.                     if (fwrite (__NextPtr->buffer,1,BufferSize,__fptr) != BufferSize) {
  479.                         StopDMAIO();
  480.                         return (0);
  481.                     }
  482. #else
  483.                     if (fwrite (__LocalBuff,1,BufferSize,__fptr) != BufferSize) {
  484.                         StopDMAIO();
  485.                         return (0);
  486.                     }
  487. #endif
  488.                     VoiceActivatedSavedCount++;
  489.                 }
  490.                 else
  491.                     ProcessedBlockCount--;
  492.  
  493.             /* move to the next buffer                                    */
  494.  
  495.                 __NextPtr->count = __NextPtr->status = 0;
  496.                 __NextPtr = __NextPtr->nextptr;
  497.                 BufferDataCount--;
  498.         }
  499.         return (DMARunning);
  500. }
  501. #endif
  502.  
  503.  
  504. #if COMMDATA
  505.     /*\
  506.     |*|----====< ClosePCMBuffering >====----
  507.     |*|
  508.     |*| Removes the PCM system & deallocates the buffer memory. There is
  509.     |*| no return value.
  510.     |*|
  511.     \*/
  512. void ClosePCMBuffering()
  513. {
  514. BuffPtr p,op;
  515.  
  516.     /* we will kill the DMA low level processing                        */
  517.  
  518.         StopDMAIO();
  519.         _unloadirqvector();
  520.  
  521.     /* Free up the linked list of buffers                                */
  522.  
  523.         if ((p = HeadOfBuffers) != 0) {
  524.  
  525.             do {
  526.                 op    = p;                /* save the old ptr             */
  527.                 p    = p->nextptr;        /* point to the next buffer     */
  528.                 free  (op);             /* free up the old header        */
  529.  
  530.             } while ( (p != HeadOfBuffers) && p );
  531.        }
  532.  
  533.     /* free up the DMA buffer                                            */
  534.  
  535.         if (DMABuffPtr)
  536.             hfree (DMABuffPtr);
  537.  
  538.     /* null it all out...                                                */
  539.  
  540.         DMABuffPtr         = 0;
  541.         HeadOfBuffers     = 0;
  542.         StartOfDMABuffer = 0;
  543.         BufferDataCount  = BufferSize = DMARunning = 0;
  544.  
  545. }
  546. #endif
  547.  
  548.  
  549. #if BLOCKIN
  550.     /*\
  551.     |*|----====< ContinueBlockInput >====----
  552.     |*|
  553.     |*| This routine checks to see if another buffer can be stored in memory.
  554.     |*| if so, it will load copy the DMA buffer to the caller's local buffer,
  555.     |*| A return value of 0 indicates the caller's buffer is empty.
  556.     |*|
  557.     \*/
  558. int ContinueBlockInput(buff)
  559.     char far *buff;
  560. {
  561.  
  562.     /* if BufferDataCount is non-zero, we must move the data to memory    */
  563.  
  564.         if (BufferDataCount) {
  565.  
  566.             /* data is available, just move it out                        */
  567.  
  568.                 _rfmemcpy (buff,__NextPtr->buffer,BufferSize);
  569.  
  570.             /* move to the next buffer                                    */
  571.  
  572.                 __NextPtr->count = __NextPtr->status = 0;
  573.                 __NextPtr = __NextPtr->nextptr;
  574.                 BufferDataCount--;
  575.  
  576.             /* returns the fact that the data has been loaded            */
  577.  
  578.                 return(1);
  579.         }
  580.         return (0);
  581. }
  582.  
  583.  
  584.     /*\
  585.     |*|----====< ContinueThisBlockInput >====----
  586.     |*|
  587.     |*| This routine extracts a DMA buffer into one or
  588.     |*| more target user buffers.
  589.     |*|
  590.     |*| Returns:
  591.     |*|    Nonzero for running & processing, else 0 for dead.
  592.     |*|
  593.     \*/
  594. int  ContinueThisBlockInput()
  595. {
  596. int n,          // working integer
  597.     loop,        // loop flag to keep loading blocks
  598.     bcount;     // increments the BufferDataCount
  599. int result = 0; // holds the final count
  600.  
  601. static int TargetSize;     // remaining size of the target dma buffer
  602. static char far *dmaptr; // pointer to this DMA block
  603.  
  604.     // if the DMA is dead, give it a jump start. Bad thing, it flushes all...
  605.  
  606.         if (DMARunning == 0) {
  607.  
  608.             // blow off anything that is saved locally
  609.  
  610.                 dmaptr = 0;
  611.                 TargetSize = 0;
  612.  
  613.             // reset and restart the low level stuff...
  614.  
  615.                 _resetbuffers();
  616.                 StartTheDMAInput(ContinueThisBlockInput);
  617.  
  618.             // we have no more data, just return now
  619.  
  620.                 return(DMARunning);
  621.         }
  622.  
  623.     // if the current remaining length is null, prime for the next block
  624.  
  625.         if (_file_data_length == 0) {
  626.  
  627.             // bomb out if no data buffers queued up
  628.  
  629.             if (__queueincnt == 0)
  630.                 return(1);
  631.  
  632.             // get the next block from the queue
  633.  
  634.             _file_data_length     = __queuelen [__queueout];
  635.             __singleblockpointer = __queuebuff[__queueout];
  636.         }
  637.  
  638.     // loop here to stuff as many blocks as possible into the DMA buffer
  639.  
  640.     nextblock:
  641.  
  642.     // move up to one buffer division worth of data
  643.  
  644.         if (!TargetSize) {
  645.  
  646.             dmaptr = __NextPtr->buffer;
  647.             TargetSize = BufferSize;
  648.         }
  649.  
  650.         loop = TRUE;
  651.         bcount = 1;
  652.  
  653.     // move as many blocks as possible into the DMA buffer
  654.  
  655.         while (loop) {
  656.  
  657.             // Get the block length, up to the division size
  658.  
  659.                 if (_file_data_length <= TargetSize) {
  660.  
  661.                     n = _file_data_length;    // full target size
  662.                     _file_data_length = 0;
  663.  
  664.                 }
  665.                 else                        // partial target size
  666.  
  667.                     _file_data_length -= (n = TargetSize);
  668.  
  669.             // copy the data to the buffer, and advance the buffer that far
  670.  
  671.                 if (n) {
  672.  
  673.                     // move the recorded data into the buffer
  674.  
  675.                     __singleblockpointer
  676.                         = _rfmemcpy(__singleblockpointer,dmaptr,n);
  677.                     dmaptr += n;                // move the dma pointer
  678.                     result += n;                // more for the return value
  679.  
  680.                     __queuedata -= (n &0xffff); // less queued up
  681.                     BufferDataCount -= bcount;    // increment buffer count once
  682.                     bcount = 0;
  683.  
  684.                 }
  685.  
  686.             // if the length is zero, this buffer is done, let the caller know
  687.  
  688.                 if (!_file_data_length) {
  689.  
  690.                     // let the app. know we are done with this buffer
  691.  
  692.                         if (__queuecb[__queueout])
  693.                             (*__queuecb[__queueout])(__queuebuff[__queueout],__queuelen[__queueout]);
  694.  
  695.                         __queueincnt--;
  696.                         __queueout = ++__queueout & QUEUEMASK;
  697.  
  698.                     // Now, try to get the next available block out of the list
  699.  
  700.                     if (__queuein != __queueout) {
  701.  
  702.                         _file_data_length     = __queuelen[__queueout];
  703.                         __singleblockpointer = __queuebuff[__queueout];
  704.                     }
  705.                     else
  706.                         loop = FALSE;
  707.                 }
  708.  
  709.             // we are now done with this much of the buffer, stop if zero
  710.  
  711.                 if (!(TargetSize -= n))
  712.                     loop = FALSE;
  713.  
  714.         }
  715.  
  716.     // advance the list to the next DMA buffer and count one more...
  717.  
  718.         __NextPtr = __NextPtr->nextptr;
  719.  
  720.     // if we can do more, then DO IT!!!
  721.  
  722.         if (BufferDataCount > 0) {                // if there is data in the DMA...
  723.             if (__queueincnt)                    // if we have buffers...
  724.                     goto nextblock;             // then go load it...
  725.         }
  726.  
  727.     // return the number of bytes loaded
  728.  
  729.         return(result);
  730.  
  731. }
  732.  
  733. #if 0
  734.  
  735.     //////////////// Original ContinueThisBlockInpu ///////////////
  736.  
  737.  
  738.     /* if there is no more data, just exit                                    */
  739.  
  740.         if (!_file_data_length)
  741.             return(0);
  742.  
  743.     /* move as much as possible...                                            */
  744.  
  745.  
  746.         if (_file_data_length <= BufferSize) {
  747.             n = _file_data_length;
  748.             _file_data_length = 0;
  749.         }
  750.         else {
  751.             _file_data_length -= (n = (BufferSize & 0xffff));
  752.         }
  753.  
  754.         __singleblockpointer =
  755.             _rfmemcpy(__singleblockpointer,__NextPtr->buffer,n);
  756.  
  757.         BufferDataCount--;
  758.  
  759.  
  760.  
  761.         __NextPtr         = __NextPtr->nextptr; /* advance the list     */
  762.  
  763.         return(n);
  764. #endif
  765.  
  766.  
  767. #endif
  768.  
  769.  
  770. #if FILEIN
  771.     /*\
  772.     |*|----====< ContinueFileInput >====----
  773.     |*|
  774.     |*| This routine checks to see if another buffer can be written to disk.
  775.     |*| if so, it will load copy the buffer to a local buffer, then write it
  776.     |*| out to disk. A return value of 0 indicates recording has stopped,
  777.     |*| which could mean that the disk file is full, so the DMA had to be
  778.     |*| stopped prematurely.
  779.     |*|
  780.     \*/
  781. int ContinueFileInput()
  782. {
  783.  
  784.     /* if BufferDataCount is non-zero, we must write out the data        */
  785.  
  786.         while (BufferDataCount) {
  787.  
  788.             /* data is available, move it out to disk                    */
  789.  
  790. #if LARGEDATA
  791.  
  792.             /* if not all data is written, return in error                */
  793.  
  794.                 if (fwrite (__NextPtr->buffer,1,BufferSize,__fptr) != BufferSize) {
  795.                     StopDMAIO();
  796.                     return (0);
  797.                 }
  798. #else
  799.             /* if not all data is written, return in error                */
  800.  
  801.                 _rfmemcpy  (__LocalBuff,__NextPtr->buffer,BufferSize);
  802.                 if (fwrite (__LocalBuff,1,BufferSize,__fptr) != BufferSize) {
  803.                     StopDMAIO();
  804.                     return (0);
  805.                 }
  806. #endif
  807.             /* move to the next buffer                                  */
  808.  
  809.                 __NextPtr->status = 0;
  810.                 __NextPtr = __NextPtr->nextptr;
  811.                 BufferDataCount--;
  812.         }
  813.         return (DMARunning);
  814. }
  815. #endif
  816.  
  817.  
  818. #if BLOCKOUT
  819.     /*\
  820.     |*|----====< ContinueBlockOutput >====----
  821.     |*|
  822.     |*| This routine checks to see if another DMA buffer can be loaded.
  823.     |*| If so, it will load the user's block data into an empty buffer.
  824.     |*| A return value of 1 indicates the buffer has been loaded, else
  825.     |*| it must be sent in again until it is loaded.
  826.     |*|
  827.     \*/
  828. int ContinueBlockOutput(buff)
  829.     char far *buff;
  830. {
  831.  
  832.     /* if the internal count is not max-ed out, try to load the next buffer */
  833.  
  834.         if (BufferDataCount < MaxBuffCount ) {
  835.  
  836.             _loadtheblock (buff);
  837.  
  838.             if (DMARunning == 0) {        /* yuck! a DMA break!            */
  839.                 _resetbuffers();
  840.                 StartTheDMAOutput(0);
  841.             }
  842.  
  843.             return (1);                 /* return running                */
  844.         }
  845.         else
  846.             return(0);
  847. }
  848.  
  849.  
  850.     /*\
  851.     |*|----====< ContinueThisBlockOutput >====----
  852.     |*|
  853.     |*| This routine checks to see if another DMA buffer can be loaded.
  854.     |*| If so, it will load the user's block data into an empty buffer.
  855.     |*| A return value of ~0 indicates the buffer has been loaded.
  856.     |*|
  857.     |*| The foreground routine will call this when DMARunning == 0. The
  858.     |*| background routine will call this at every interrupt to keep the
  859.     |*| buffers loaded
  860.     |*|
  861.     \*/
  862. int ContinueThisBlockOutput()
  863. {
  864.  
  865. int n,          // working integer
  866.     TargetSize, // size of the target dma buffer
  867.     loop,        // loop flag to keep loading blocks
  868.     bcount;     // increments the BufferDataCount
  869. int result = 0; // holds the final count
  870. char far *s;
  871.  
  872.     // If no more data to load in the buffer, flush the next DMA & return
  873.  
  874.         if (__queueincnt == 0) {
  875.             FlushBuffer (__NextPtr->buffer,BufferSize);
  876.             __NextPtr = __NextPtr->nextptr;
  877.             return(0);
  878.         }
  879.  
  880.     // if there is little data, but a lot in the DMA, just return
  881.  
  882.         if ((__queuedata < BufferSize) && BufferDataCount > 2)
  883.             return(0);
  884.  
  885.     // if the DMA has been turned off, re-sync the buffers
  886.  
  887.         if (DMARunning == 0)
  888.             _resetbuffers();
  889.  
  890.     // if the current remaining length is null, prime for the next block
  891.  
  892.         if (_file_data_length == 0) {
  893.             _file_data_length     = __queuelen [__queueout];
  894.             __singleblockpointer = __queuebuff[__queueout];
  895.         }
  896.  
  897.     // loop here to stuff as many blocks as possible into the DMA buffer
  898.  
  899.     nextblock:
  900.  
  901.     // move up to one buffer division worth of data
  902.  
  903.         TargetSize = BufferSize;
  904.         s = __NextPtr->buffer;
  905.         loop = TRUE;
  906.         bcount = 1;
  907.  
  908.     // move as many blocks as possible into the DMA buffer
  909.  
  910.         while (loop) {
  911.  
  912.             // Get the block length, up to the division size
  913.  
  914.                 if (_file_data_length <= TargetSize) {
  915.  
  916.                     n = _file_data_length;    // full target size
  917.                     _file_data_length = 0;
  918.  
  919.                 }
  920.                 else                        // partial target size
  921.  
  922.                     _file_data_length -= (n = TargetSize);
  923.  
  924.             // copy the data to the buffer, and advance the buffer that far
  925.  
  926.                 if (n) {
  927.  
  928.                     s = _rfmemcpy(s, __singleblockpointer, n );
  929.  
  930.                     result += n;                // more for the return value
  931.                     __singleblockpointer += n;    // advance the pointer
  932.                     __queuedata -= (n &0xffff); // less queued up
  933.  
  934.                     BufferDataCount += bcount;  // increment buffer count once
  935.                     bcount = 0;
  936.  
  937.                 }
  938.                 else
  939.                     s = __NextPtr->buffer;        // no data, but do point here
  940.  
  941.             // if the length is zero, this buffer is done, let the caller know
  942.  
  943.                 if (!_file_data_length) {
  944.  
  945.                     // if this old block was valid, send a DONE msg.
  946.  
  947.                     if(__queueincnt) {
  948.  
  949.                         // let the app. know we are done with this buffer
  950.  
  951.                         if (__queuecb[__queueout])
  952.                             (*__queuecb[__queueout])(__queuebuff[__queueout],FALSE);
  953.  
  954.                         __queueincnt--;
  955.                         __queueout = ++__queueout & QUEUEMASK;
  956.                     }
  957.  
  958.                     // Now, try to get the next available block out of the list
  959.  
  960.                     if (__queuein == __queueout) {
  961.  
  962.                         FlushBuffer (s,TargetSize-n);
  963.                         loop = FALSE;
  964.  
  965.                     }
  966.                     else {
  967.  
  968.                         _file_data_length     = __queuelen[__queueout];
  969.                         __singleblockpointer = __queuebuff[__queueout];
  970.  
  971.                     }
  972.                 }
  973.  
  974.             // we are now done with this much of the buffer, stop if zero
  975.  
  976.                 if (!(TargetSize -= n))
  977.                     loop = FALSE;
  978.         }
  979.  
  980.     // advance the list to the next DMA buffer and count one more...
  981.  
  982.         __NextPtr = __NextPtr->nextptr;
  983.  
  984.     // if we can do more, then DO IT!!!
  985.  
  986.         if (BufferDataCount < MaxBuffCount) {    // if there is room in the DMA
  987.             if (__queueincnt) {                 // if we have pcm data
  988.                 if (__queuedata >= BufferSize)    // and its GE a buffer division,
  989.                     goto nextblock;             // then go load it...
  990.             }
  991.         }
  992.  
  993.         if (DMARunning == 0)
  994.             StartTheDMAOutput(ContinueThisBlockOutput);
  995.  
  996.     // return the number of bytes loaded
  997.  
  998.         return(result);
  999.  
  1000. }
  1001. #endif
  1002.  
  1003.  
  1004. #if FILEOUT
  1005.     /*\
  1006.     |*|----====< ContinueFileOutput >====----
  1007.     |*|
  1008.     |*| This routine checks to see if another buffer can be loaded. If so, it
  1009.     |*| will load the data into an empty buffer. All empty buffers will be
  1010.     |*| loaded. A return value of 0 indicates playing has finished.
  1011.     |*|
  1012.     \*/
  1013. int ContinueFileOutput()
  1014. {
  1015.  
  1016.     /* if BufferDataCount is not max-ed out, try to load the next buffer*/
  1017.  
  1018.         if (BufferDataCount < MaxBuffCount ) {
  1019.  
  1020.             if (_loadthebuffer (__fptr)) {
  1021.  
  1022.                 if (DMARunning == 0) {     /* yuck! a DMA break!            */
  1023.                     _resetbuffers();
  1024.                     if (StartTheDMAOutput(0))
  1025.                         return(0);
  1026.                 }
  1027.             }
  1028.         }
  1029.         return (DMARunning);            /* return the DMA state         */
  1030. }
  1031. #endif
  1032.  
  1033.  
  1034. #if COMMDATA
  1035.     /*\
  1036.     |*|----====< OpenPCMBuffering >====----
  1037.     |*|
  1038.     |*|  This routine is the first-call routine. It initializes the buffers
  1039.     |*|  needed for the PCM play/record system. A return value of non-zero
  1040.     |*|  indicates a failure to initialize the system.
  1041.     |*|
  1042.     |*|  Entry Conditions:
  1043.     |*|
  1044.     |*|         dma -- New DMA #. (1-3, or -1 for no changes)
  1045.     |*|         irq -- New IRQ #. (3,5,6,7, or -1 for no changes)
  1046.     |*|
  1047.     |*|  Exit Conditions:
  1048.     |*|
  1049.     |*|         non-zero return indicates an error
  1050.     |*|
  1051.     \*/
  1052. int OpenPCMBuffering(dma,irq,dmasize,divisions)
  1053.    int dma;         /* DMA channel # (-1 for no changes)    */
  1054.    int irq;         /* IRQ channel # (-1 for no changes)    */
  1055.    int dmasize;     /* requested DMA size (4/8/16/32/64)    */
  1056.    int divisions;    /* # of divisions in the DMA buffer     */
  1057. {
  1058. BuffPtr op,p;
  1059. long l;
  1060. int n;
  1061. char far *db;
  1062.  
  1063.     /* setup the globa variables & a local buffer                        */
  1064.  
  1065.         MaxBuffCount = divisions;
  1066.         BufferSize     = LONG(dmasize) * 1024L / LONG(MaxBuffCount);
  1067.  
  1068.     /* Setup the lowlevel routines                                        */
  1069.  
  1070.         InitMVSound();
  1071.  
  1072.     /* flush any background task setup                                  */
  1073.  
  1074.         BackgroundInit( BufferSize, MaxBuffCount );
  1075.  
  1076.         if ((__LocalBuff=(char*)malloc(BufferSize)) == 0)
  1077.              return (PCMIOERR_OPENFILE);
  1078.  
  1079.     /* Allocate twice the size for background DMA buffer                */
  1080.  
  1081.         l = LONG(dmasize) * 1024 * 2;
  1082.  
  1083.         if ((DMABuffPtr = (char huge *) halloc (l,1)) == 0)
  1084.             return(PCMIOERR_NOMEM);
  1085.  
  1086.         if ((db=StartOfDMABuffer=FindDMABuffer(DMABuffPtr,dmasize)) == 0)
  1087.             return (PCMIOERR_OPENPCM);
  1088.  
  1089.     /* if the low level code doesn't like it, bomb out                  */
  1090.  
  1091.         if (!DMABuffer ( StartOfDMABuffer, dmasize, MaxBuffCount ))
  1092.             return(PCMIOERR_OPENPCM);
  1093.  
  1094.     /* Attempt to allocate each foreground buffer                        */
  1095.  
  1096.         op = 0;
  1097.         for (n=0;n<divisions;n++) {
  1098.  
  1099.             /* allocate the linked list header for each buffer            */
  1100.  
  1101.                 if ((p = (BuffPtr) malloc (sizeof(BuffData))) == 0)
  1102.                     return(PCMIOERR_NOMEM);
  1103.  
  1104.             /* reset the pointer in case of other failures during init    */
  1105.  
  1106.                 p->nextptr = 0;
  1107.  
  1108.             /* if first block, save as the head of the list             */
  1109.  
  1110.                 if (!HeadOfBuffers)
  1111.                     HeadOfBuffers = p;
  1112.  
  1113.             /* if we have already allocated a block, setup the fwd ptr    */
  1114.  
  1115.                 if (op)
  1116.                     op->nextptr = p;
  1117.  
  1118.                 p->buffer = db;
  1119.                 p->size   = BufferSize;
  1120.                 db          += BufferSize;
  1121.  
  1122.             /* save as the old pointer for linking purposes             */
  1123.  
  1124.                 op = p;
  1125.         }
  1126.  
  1127.     /* link the last buffer back to the first                            */
  1128.  
  1129.         p->nextptr = HeadOfBuffers;
  1130.  
  1131.     /* Possibly select new DMA & IRQ channels                            */
  1132.  
  1133.         if (dma != -1)
  1134.             if (SelectDMA(dma))
  1135.                 return(PCMIOERR_BADDMA);
  1136.  
  1137.         if (irq != -1)
  1138.             if (SelectIRQ(irq))
  1139.                 return(PCMIOERR_BADIRQ);
  1140.  
  1141.     /* well, it looks good so far, flush any variables                    */
  1142.  
  1143.         BufferDataCount   = ProcessedBlockCount =
  1144.         _file_data_length = __queuedata         =
  1145.         VoiceActivatedSavedCount = __queueincnt =
  1146.         __queuein  = __queueout = 0;
  1147.  
  1148.     /* and return good!                                                 */
  1149.  
  1150.         return (0);
  1151. }
  1152. #endif
  1153.  
  1154.  
  1155. #if COMMDATA
  1156.     /*\
  1157.     |*|----====< PCMState >====----
  1158.     |*|
  1159.     |*| This routine passes in the sample rate, stereo/mono flag, and any
  1160.     |*| other miscellaneous data (to be determined later...)
  1161.     |*|
  1162.     |*| Exit Conditions:
  1163.     |*|    Non-zero means the sample rate was in error.
  1164.     |*|    Zero means the sample rate was okay error.
  1165.     |*|
  1166.     \*/
  1167. int PCMState(sr,sm,cp,sz)
  1168.     long sr;    /* sample rate    */
  1169.     int  sm;    /* stereo/mono    */
  1170.     int  cp;    /* compression    */
  1171.     int  sz;    /* size(8/16)    */
  1172. {
  1173.  
  1174.     /* just pass them on...                                             */
  1175.  
  1176.         __pcmdatasize = sz;                     /* pcm data size        */
  1177.         return (!PCMInfo (sr,sm,cp,sz));
  1178.  
  1179. }
  1180. #endif
  1181.  
  1182.  
  1183. #if COMMDATA
  1184.     /*\
  1185.     |*|----====< StopDMAIO >====----
  1186.     |*|
  1187.     |*| This routine forceably kills the PCM I/O. All buffers will be
  1188.     |*| reset, the current position of the input file is not altered. There
  1189.     |*| is no return value.
  1190.     |*|
  1191.     \*/
  1192. void StopDMAIO()
  1193. {
  1194.  
  1195.     /* if this code has not already been setup, exit now                */
  1196.  
  1197.         if (!HeadOfBuffers)
  1198.             return;
  1199.  
  1200.     /* stop the hardware...                                             */
  1201.  
  1202.         StopPCM( );
  1203.  
  1204.         __queuein    = __queueincnt = __queueout = DMARunning = 0;
  1205.         __queuedata = _file_data_length = 0;
  1206.  
  1207.     /* flush any prior background task setup                            */
  1208.  
  1209.         if (__DirectionFlag == DMAOUTPUT) {
  1210.             if (__fptr)
  1211.                 rewind    (__fptr);
  1212.         }
  1213.  
  1214.     /* reset the linked list of buffers                                 */
  1215.  
  1216.         _resetbuffers();
  1217.  
  1218.     /* setup our internal direction flag                                */
  1219.  
  1220.         __DirectionFlag = NODIRECTION;
  1221. }
  1222. #endif
  1223.  
  1224.  
  1225. #if BLOCKIN
  1226.     /*\
  1227.     |*|----====< StartBlockInput >====----
  1228.     |*|
  1229.     |*| This routine resets the buffer pointers, then starts up
  1230.     |*| the DMA PCM input. Nothing else needs to be done. A return
  1231.     |*| value of 0 indicates the DMA failed to startup; No input
  1232.     |*| is occuring.
  1233.     |*|
  1234.     \*/
  1235. int StartBlockInput()
  1236. {
  1237.  
  1238.     /* setup our internal direction flag                                */
  1239.  
  1240.         __DirectionFlag = DMAINPUT;
  1241.  
  1242.     /* Reset the # of blocks seen during I/O processing                 */
  1243.  
  1244.         ProcessedBlockCount = 0;
  1245.  
  1246.     /* Flush all buffers                                                */
  1247.  
  1248.         _resetbuffers();
  1249.  
  1250.     /* if the hardware level code isn't gonna work, then return a failure. */
  1251.  
  1252.         return (!StartTheDMAInput(0));
  1253. }
  1254.  
  1255.  
  1256.     /*\
  1257.     |*|----====< RecordThisBlock >====----
  1258.     |*|
  1259.     |*| This routine offers the caller a simplified recording of one
  1260.     |*| variable length block of data. The call just needs to call
  1261.     |*| OpenPCMBuffering, then RecordThisBlock. The caller just has
  1262.     |*| to poll DMARunning to see if the block has completed. Calling
  1263.     |*| StopDMAIO will stop the process, if need be.
  1264.     |*|
  1265.     \*/
  1266. int RecordThisBlock(p,len,cb)
  1267.     char far *p;
  1268.     unsigned long len;
  1269.     void (far *cb)();
  1270. {
  1271. int n;
  1272.  
  1273.         ///////////////////// under construction! /////////////////////////
  1274.  
  1275.     // if the pointer is valid, then queue it up
  1276.  
  1277.         if (p) {
  1278.  
  1279.             // return if already full
  1280.  
  1281.             if (__queuein == QUEUESIZE)
  1282.                 return(2);
  1283.  
  1284.             // extract the 1st entry from the queue
  1285.  
  1286.             __queuebuff[__queuein] = p;
  1287.             __queuedata += (__queuelen [__queuein] = len);
  1288.             __queuecb  [__queuein] = cb;
  1289.             __queuein  = ++__queuein & QUEUEMASK;
  1290.             __queueincnt++;
  1291.  
  1292.         }
  1293.  
  1294.     // if the blocks are not recording , the let'er rip...
  1295.  
  1296.         if ((DMARunning == 0) && __queueincnt ) {
  1297.  
  1298.             // setup the direction flag
  1299.  
  1300.                 __DirectionFlag = DMAINPUT;
  1301.  
  1302.             /* reset the DMA block pointers                             */
  1303.  
  1304.                 _resetbuffers();
  1305.  
  1306.             /* return good or bad if the PCM engine is running            */
  1307.  
  1308.                 return (ContinueThisBlockInput() ? 1 : 0 );
  1309.  
  1310.         }
  1311.  
  1312.     // assume the block is now recording
  1313.  
  1314.         return(0);
  1315.  
  1316. }
  1317. #endif
  1318.  
  1319.  
  1320. #if FILEIN
  1321.     /*\
  1322.     |*|----====< StartFileInput >====----
  1323.     |*|
  1324.     |*| This routine resets the buffer pointers, then starts up the DMA PCM
  1325.     |*| input. Nothing else needs to be done.
  1326.     |*|
  1327.     \*/
  1328. int StartFileInput(f)
  1329.    FILE *f;
  1330. {
  1331.  
  1332.     /* save our local file handle                                        */
  1333.  
  1334.         __fptr      = f;
  1335.  
  1336.     /* setup our internal direction flag                                */
  1337.  
  1338.         __DirectionFlag = DMAINPUT;
  1339.  
  1340.     /* Reset the # of blocks seen during I/O processing                 */
  1341.  
  1342.         ProcessedBlockCount = 0;
  1343.  
  1344.     /* Flush all buffers and other stuff..                                */
  1345.  
  1346.         _resetbuffers();
  1347.         VoiceActivatedSavedCount = 0;
  1348.  
  1349.     /* start the DMA engine                                             */
  1350.  
  1351.         return (!StartTheDMAInput(0));
  1352. }
  1353. #endif
  1354.  
  1355.  
  1356. #if BLOCKOUT
  1357.     /*\
  1358.     |*|----====< StartBlockOutput >====----
  1359.     |*|
  1360.     |*| This routine allocates and loads the necessary buffers with data from
  1361.     |*| the PCM disk file. Upon return, if there is data available, the
  1362.     |*| background task will be called to start the DMA. The file handle will
  1363.     |*| be saved in a global variable to be access from other foreground
  1364.     |*| routines. a non-zero return value indicates PCM output is playing.
  1365.     |*|
  1366.     \*/
  1367. int StartBlockOutput(buff)
  1368.     char far *buff;
  1369. {
  1370.  
  1371.     /* setup our internal direction flag                                */
  1372.  
  1373.         __DirectionFlag = DMAOUTPUT;
  1374.  
  1375.     /* Reset the # of blocks seen during I/O processing                 */
  1376.  
  1377.         ProcessedBlockCount = 0;
  1378.  
  1379.     /* load the DMA buffers                                             */
  1380.  
  1381.         _resetbuffers();
  1382.         _loadtheblock (buff);
  1383.  
  1384.     /* return good or bad if the engine is started                        */
  1385.  
  1386.         return (!StartTheDMAOutput(0));
  1387. }
  1388.  
  1389.  
  1390.     /*\
  1391.     |*|----====< PlayThisBlock >====----
  1392.     |*|
  1393.     |*| This routine offers the caller a simplified playback of one
  1394.     |*| variable length block of data. The call just needs to call
  1395.     |*| OpenPCMBuffering, then PlayThisBlock. The caller just has
  1396.     |*| to poll DMARunning to see if the block has completed. Calling
  1397.     |*| StopDMAIO will stop the process, if need be.
  1398.     |*|
  1399.     |*| Also see QueueThisBlock.
  1400.     |*|
  1401.     |*| Entry:
  1402.     |*|    p   is the far pointer to a block of data (64k max size). If
  1403.     |*|        the pointer is null, the block will not be queued.
  1404.     |*|    len is the length of the block in bytes (one based count).
  1405.     |*|    cb  is the callback routine to call when the block is empty.
  1406.     |*|
  1407.     |*| Returns:
  1408.     |*|    0 - block is queued and playing
  1409.     |*|    1 - Block failed to start
  1410.     |*|    2 - queue is full, try later
  1411.     |*|
  1412.     \*/
  1413. int PlayThisBlock(p,len,cb)
  1414.     char far *p;
  1415.     unsigned long len;
  1416.     void (far *cb)();
  1417. {
  1418. int n;
  1419.  
  1420.     // if the pointer is valid, then queue it up
  1421.  
  1422.         if (p) {
  1423.  
  1424.             // return if already full
  1425.  
  1426.             if (__queuein == QUEUESIZE)
  1427.                 return(2);
  1428.  
  1429.             // extract the 1st entry from the queue
  1430.  
  1431.             __queuebuff[__queuein] = p;
  1432.             __queuedata += (__queuelen [__queuein] = len);
  1433.             __queuecb  [__queuein] = cb;
  1434.             __queuein  = ++__queuein & QUEUEMASK;
  1435.             __queueincnt++;
  1436.  
  1437.         }
  1438.  
  1439.     // if the blocks are not playing, the let'er rip...
  1440.  
  1441.         if ((DMARunning == 0) && __queueincnt ) {
  1442.  
  1443.             // setup to transfer this block
  1444.  
  1445.                 __DirectionFlag = DMAOUTPUT;
  1446.  
  1447.             // start the process by loading the DMA buffers
  1448.  
  1449.                 return (ContinueThisBlockOutput() ? 0 : 1 );
  1450.  
  1451.         }
  1452.  
  1453.     // Indicate this has been queued up
  1454.  
  1455.         return(0);
  1456. }
  1457.  
  1458.  
  1459.     /*\
  1460.     |*|----====< QueueThisBlock >====----
  1461.     |*|
  1462.     |*| This routine will queue up one block, but not start the PCM
  1463.     |*| transfers. Once X number of blocks are queued up, then the caller
  1464.     |*| can call PlayThisBlock/RecordThisBlock to start the process.
  1465.     |*|
  1466.     |*| Also see PlayThisBlock/RecordThisBlock.
  1467.     |*|
  1468.     |*| Entry:
  1469.     |*|    p   is the far pointer to a block of data (64k max size).
  1470.     |*|        All pointers are assumed to be valid.
  1471.     |*|    len is the length of the block in bytes (one based count).
  1472.     |*|    cb  is the callback routine to call when the block is empty.
  1473.     |*|
  1474.     |*| Returns:
  1475.     |*|    0 - block is queued and playing
  1476.     |*|    2 - queue is full, try later
  1477.     |*|
  1478.     \*/
  1479. int QueueThisBlock(p,len,cb)
  1480.     char far *p;
  1481.     unsigned long len;
  1482.     void (far *cb)();
  1483. {
  1484. int n;
  1485.  
  1486.     // return if already full
  1487.  
  1488.         if (__queuein == QUEUESIZE)
  1489.             return(2);
  1490.  
  1491.     // if idle, setup our internal direction flag, and the desired length
  1492.  
  1493.         __queuebuff[__queuein] = p;
  1494.         __queuedata += (__queuelen [__queuein] = len);
  1495.         __queuecb  [__queuein] = cb;
  1496.         __queuein  = ++__queuein & QUEUEMASK;
  1497.         __queueincnt++;
  1498.  
  1499.     // return all queued up
  1500.  
  1501.         return(0);
  1502.  
  1503. }
  1504.  
  1505.  
  1506.     /*\
  1507.     |*|----====< SyncCallBack >====----
  1508.     |*|
  1509.     |*| This routine will setup a callback to the caller's routine
  1510.     |*| at the end of every DMA block interrupt
  1511.     |*|
  1512.     |*| Returns:
  1513.     |*|
  1514.     \*/
  1515. int SyncCallBack(cb)
  1516.     void (far *cb)();
  1517. {
  1518. int n;
  1519.  
  1520.         ///////////////////// under construction! /////////////////////////
  1521.  
  1522.     // just save for a later call...
  1523.  
  1524.         __synccallback = cb;
  1525.  
  1526. }
  1527.  
  1528. #endif
  1529.  
  1530.  
  1531. #if FILEOUT
  1532.     /*\
  1533.     |*|----====< StartFileOutput >====----
  1534.     |*|
  1535.     |*| This routine allocates and loads the necessary buffers with data from
  1536.     |*| the PCM disk file. Upon return, if there is data available, the
  1537.     |*| background task will be called to start the DMA. The file handle will
  1538.     |*| be saved in a global variable to be access from other foreground
  1539.     |*| routines. a non-zero return value indicates PCM output is playing.
  1540.     |*|
  1541.     \*/
  1542. int StartFileOutput(f,len)
  1543.     FILE *f;
  1544.     long len;
  1545. {
  1546.  
  1547.     /* save our local file handle                                        */
  1548.  
  1549.         __fptr = f;
  1550.  
  1551.     /* setup our internal direction flag, and the desired length        */
  1552.  
  1553.         __DirectionFlag   = DMAOUTPUT;
  1554.         _file_data_length = len;
  1555.  
  1556.     /* Reset the # of blocks seen during I/O processing                 */
  1557.  
  1558.         ProcessedBlockCount = 0;
  1559.  
  1560.     /* if any data is loaded into the buffer, start the DMA & return    */
  1561.  
  1562.         _resetbuffers();
  1563.  
  1564.         do {
  1565.  
  1566.             /* get the next buffer full & exit if done                    */
  1567.  
  1568.             if (!_loadthebuffer (f))
  1569.                 break;
  1570.  
  1571.         } while (__NextPtr != HeadOfBuffers);
  1572.  
  1573.     /* return good or bad if the engine is running                        */
  1574.  
  1575.         return (!StartTheDMAOutput(0));
  1576.  
  1577. }
  1578. #endif
  1579.  
  1580.  
  1581. #if BLOCKOUT
  1582.     /*\
  1583.     |*|----====< _loadtheblock >====----
  1584.     |*|
  1585.     |*| This routine loads the block into the DMA buffer.
  1586.     |*|
  1587.     \*/
  1588. static int _loadtheblock(buff)
  1589.     char far *buff;
  1590. {
  1591.  
  1592.     /* load the block of data into the DMA buffer                        */
  1593.  
  1594.         _rfmemcpy(__NextPtr->buffer, buff, BufferSize );
  1595.  
  1596.     /* now that the data is secure, fill out the rest of the header to    */
  1597.     /* allow the background process to "see" this new data              */
  1598.  
  1599.         __NextPtr->status = 1;
  1600.         __NextPtr->count  = BufferSize;
  1601.         __NextPtr          = __NextPtr->nextptr; /* advance the list     */
  1602.         BufferDataCount++;
  1603.  
  1604.     /* we have data, return the size                                    */
  1605.  
  1606.        return (BufferSize);
  1607. }
  1608. #endif
  1609.  
  1610.  
  1611. #if FILEOUT
  1612.     /*\
  1613.     |*|----====< _loadthebuffer >====----
  1614.     |*|
  1615.     |*| This routine loads the disk contents into an available buffer.
  1616.     |*| A return value of 0 indicates no more data has been loaded.
  1617.     |*|
  1618.     \*/
  1619. static int _loadthebuffer(f)
  1620.     FILE *f;
  1621. {
  1622. char huge *s;
  1623. register int n;
  1624. long l;
  1625.  
  1626.     /* reset the header data                                            */
  1627.  
  1628.         __NextPtr->count = __NextPtr->status = 0;
  1629.  
  1630.     /* exit if there is no data to be read                                */
  1631.  
  1632.         if (feof (f) || (!_file_data_length))
  1633.             return (0);
  1634.  
  1635.     /* adjust the max count we want to process from the file            */
  1636.  
  1637.         if (_file_data_length <= BufferSize) {
  1638.             l = _file_data_length;
  1639.             _file_data_length = 0;
  1640.         }
  1641.         else {
  1642.             _file_data_length -= (l = (BufferSize & 0xffff));
  1643.         }
  1644.  
  1645.     /* read the data from the file                                        */
  1646.  
  1647. #if LARGEDATA
  1648.         if((n = fread (__NextPtr->buffer, 1,((unsigned int)(l & 0xffff)), f)) == 0)
  1649.             return(0);
  1650.  
  1651.         s = __NextPtr->buffer+n; // point to the end of the block
  1652. #else
  1653.         if((n = fread (__LocalBuff, 1,((unsigned int)(l & 0xffff)), f)) == 0)
  1654.             return(0);
  1655.  
  1656.     /* move the data to the linked list. Yuck! double handling of data    */
  1657.     /* because fread won't take a far pointer in small model!           */
  1658.  
  1659.         s = _rfmemcpy(__NextPtr->buffer, __LocalBuff, n ); // s points to eob
  1660. #endif
  1661.  
  1662.     // flush to the end if not a full buffer worth of data
  1663.  
  1664.         if (n < BufferSize)
  1665.             FlushBuffer (s,BufferSize-n);
  1666.  
  1667.     /* now that the data is secure, fill out the rest of the header to    */
  1668.     /* allow the background process to "see" this new data              */
  1669.  
  1670.         __NextPtr->status = 1;
  1671.         __NextPtr->count  = BufferSize;
  1672.         __NextPtr          = __NextPtr->nextptr; /* advance the list     */
  1673.         BufferDataCount++;
  1674.  
  1675.     /* we have data, return the size                                    */
  1676.  
  1677.        return (n);
  1678. }
  1679. #endif
  1680.  
  1681.  
  1682. #if COMMDATA
  1683.     /*\
  1684.     |*|----====< _resetbuffers >====----
  1685.     |*|
  1686.     |*| This routine flushes the contents of the top level buffers
  1687.     |*|
  1688.     \*/
  1689. _resetbuffers()
  1690. {
  1691.  
  1692.     /* flush the count and status of every linked list block            */
  1693.  
  1694.         if ((__NextPtr = HeadOfBuffers)) {
  1695.  
  1696.             /* if there are buffers, reset them all                     */
  1697.  
  1698.             do {
  1699.  
  1700.                 /* get the next buffer full & exit if done                */
  1701.  
  1702.                     __NextPtr->count = __NextPtr->status = 0;
  1703.  
  1704.                 /* break when we reach the top                            */
  1705.  
  1706.                     if ((__NextPtr = __NextPtr->nextptr) == 0)
  1707.                             break;
  1708.  
  1709.             } while (__NextPtr != HeadOfBuffers);
  1710.  
  1711.         }
  1712.  
  1713.     /* reset the global hand shake count.                                */
  1714.  
  1715.         BufferDataCount = 0;
  1716. }
  1717. #endif
  1718.  
  1719.  
  1720.     /*\
  1721.     |*| end of PCMIOC.C
  1722.     \*/
  1723.  
  1724.