home *** CD-ROM | disk | FTP | other *** search
/ Digispeech DS201A Drivers / dsapi_v120.zip / SOURCE / RECORD.C < prev   
C/C++ Source or Header  |  1993-08-13  |  21KB  |  662 lines

  1. /* RECORD.C */
  2. /****************************************************************************/
  3. /*                                                                          */
  4. /*  Abstract: General purpose sound file recorder for DIGI drivers.         */
  5. /*                                                                          */
  6. /*  Language: Microsoft C, v6.00a  Model: LARGE    Machine type: IBM PC     */
  7. /*                                                                          */
  8. /****************************************************************************/
  9. /*
  10.  
  11.  REV  DATE       AUTHOR          DESCRIPTION
  12.  1.0  04-20-92   Lee Mulcahy     Initial version.
  13.  ---  06-15-93   Lee Mulcahy     Latest change.
  14.  
  15. */
  16.  
  17. #include <stdio.h>
  18. #include <signal.h>
  19. #include <conio.h>
  20. #include <io.h>
  21. #include <dos.h>
  22.  
  23.  
  24. /****************************************************************************/
  25. /*                                                                          */
  26. /*  Literals, etc.                                                          */
  27. /*                                                                          */
  28. /****************************************************************************/
  29.  
  30. #include "digi.h"
  31.  
  32. #define ESC_KEY             0x1B
  33.  
  34. #define REC_UNSUPPORTED     0xFF
  35.  
  36. #define EXIT_BAD_USAGE      1
  37. #define EXIT_FILE_ERR       2
  38. #define EXIT_BAD_STATUS     3
  39. #define EXIT_NOT_SUPPORTED  4
  40.  
  41. static char writeError [] = "\nError writing output file.\n";
  42.  
  43. static WORD rateValues [] = { 1800, 2400, 3000, 3600, 4200, 4800 };
  44.  
  45. static char *formatMsgs [] =
  46.     {
  47.     "Linear 8 bit PCM",         //  DF_PCM8         0
  48.     "Mu-Law PCM",               //  DF_PCMMU        1
  49.     "A-Law PCM",                //  DF_PCMA         2
  50.     "Linear 16 bit PCM",        //  DF_PCM16        3
  51.     "SB ADPCM 2 bit",           //  DF_SB2          4
  52.     "SB ADPCM 2.6 bit",         //  DF_SB3          5
  53.     "SB ADPCM 4 bit",           //  DF_SB4          6
  54.     "OKI ADPCM 4 bit",          //  DF_OKI4         7
  55.     "DVI ADPCM 4 bit",          //  DF_DVI4         8
  56.     "Digispeech Realtime",      //  DF_DIGIREAL     9
  57.     "Digispeech STD",           //  DF_DIGISTD      10
  58.     "Digispeech FIX",           //  DF_DIGIFIX      11
  59.     "",
  60.     "",
  61.     "CVSD",                     //  DF_CVSD         14
  62.     NULL
  63.     };
  64.  
  65.     /* Digispeech header format codes, indexed by data format DF_xx.    */
  66.  
  67. static int digiHeaders [] =
  68.     {
  69.     H_PCMRAW,   //  DF_PCM8    
  70.     H_PCMMU,    //  DF_PCMMU   
  71.     H_PCMA,     //  DF_PCMA    
  72.     H_PCMRAW16, //  DF_PCM16   
  73.     H_SB2,      //  DF_SB2     
  74.     H_SB3,      //  DF_SB3     
  75.     H_SB4,      //  DF_SB4     
  76.     H_ADPCM,    //  DF_OKI4    
  77.     H_PCMDVI,   //  DF_DVI4    
  78.     H_DIGIREAL, //  DF_DIGIREAL
  79.     H_DIGISTD,  //  DF_DIGISTD 
  80.     H_DIGIFIX,  //  DF_DIGIFIX
  81.     0,
  82.     0,
  83.     H_DIGICVSD  //  DF_CVSD    
  84.     };
  85.  
  86.     /* Driver information.  */
  87.  
  88. static WORD support = 0;
  89.  
  90.     /* Global audio file information.   */
  91.  
  92. static WORD rate;
  93. static WORD format;
  94. static WORD headerLen;
  95. static int  recordLen;
  96. static DWORD length;
  97.  
  98.     /* Audio data buffers.  */
  99.  
  100. static char buf0[16384];
  101. static char buf1[16384];
  102.  
  103. static void Usage ( void );
  104. static int KeyPressed ( void );
  105. static void DSExit ( int status );
  106. static int DSSetup ( void );
  107. static void WriteFileHeader ( FILE *fd );
  108. static void SetFormat ( void );
  109. static int RecordManager ( FILE *fd );
  110. static int ParseCommandLine ( int argc, char *argv[] );
  111. int main ( int argc, char *argv[] );
  112.  
  113.  
  114. /** Usage *******************************************************************/
  115. /*                                                                          */
  116. /*  Display the program syntax and parameter list.                          */
  117. /*                                                                          */
  118. /****************************************************************************/
  119.  
  120. static void Usage ( void )
  121.     {
  122.  
  123.     puts( "  Usage: RECORD <switches> <filename> \n" );
  124.     puts( "  Records sound files of the following formats:" );
  125.     puts( "  (Requires DIGI driver. Some versions/configurations" );
  126.     puts( "   do not support all formats)\n" );
  127.     puts( "  Digispeech CVSD    (*.CVS, *.CVx)" );
  128.     puts( "  PCM 8-bit linear   (*.PCM)" );
  129.     puts( "  PCM Mu-Law         (*.LIN)" );
  130.     puts( "  OKI ADPCM          (*.OKI)" );
  131.     puts( "  DVI ADPCM          (*.DVI)\n" );
  132.     puts( "  /a     - Record as A-Law  PCM" );
  133.     puts( "  /c     - Record as Digispeech CVSD" );
  134.     puts( "  /d     - Record as DVI ADPCM" );
  135.     puts( "  /o     - Record as OKI ADPCM" );
  136.     puts( "  /p     - Record as RAW    PCM (default)" );
  137.     puts( "  /u     - Record as u-Law PCM" );
  138.     puts( "  /rx    - Set Record sample rate to x (8000 or 11025)" );
  139.     puts( "           CVSD rate range is 0 - 5\n" );
  140.  
  141.     exit( EXIT_BAD_USAGE );
  142.     }
  143.  
  144.  
  145. /** KeyPressed **************************************************************/
  146. /*                                                                          */
  147. /*  If a key was pressed, return the key, else return FALSE.                */
  148. /*                                                                          */
  149. /****************************************************************************/
  150.  
  151. int KeyPressed ( void )
  152.     {
  153.     int key;
  154.  
  155.         /* Check for ESC key.   */
  156.  
  157.     key = 0;
  158.     if ( kbhit())
  159.         {
  160.         key = getch();
  161.  
  162.             /* If NULL, get another key and */
  163.             /* discard it (IBM PC oddity).  */
  164.  
  165.         if ( key == 0)
  166.             getch();
  167.         }
  168.  
  169.     return( key );
  170.     }
  171.  
  172.  
  173. /** DSExit ******************************************************************/
  174. /*                                                                          */
  175. /*  Print error message, status code, and then exit with error code.        */
  176. /*                                                                          */
  177. /*  Quite a few of the error messages listed here will never be encountered */
  178. /*  by this program, but are included 'cause I wanted all of 'em.           */
  179. /*                                                                          */
  180. /****************************************************************************/
  181.  
  182. static void DSExit ( int status )
  183.     {
  184.     char *ptr;
  185.  
  186.         /* If status is 0, this is actually a 'good' exit.  */
  187.  
  188.     if ( status == 0 )
  189.         exit( 0 );
  190.  
  191.     ptr = NULL;
  192.  
  193.     switch ( status )
  194.         {
  195.         case E_UNDEFINED:   ptr = "Undefined command.";                     break;
  196.         case E_BUF0:        ptr = "Current buffer is 0.";                   break;
  197.         case E_BUF1:        ptr = "Current buffer is 1.";                   break;
  198.         case E_MUSIC:       ptr = "Driver is in 3-tone gen mode.";          break;
  199.         case E_COM:         ptr = "Communication error.";                   break;
  200.         case E_LPC:         ptr = "LPC index out of range.";                break;
  201.         case E_CVSDSPEED:   ptr = "CVSD speed is invalid.";                 break;
  202.         case E_TIMEOUT:     ptr = "Audio Unit not responding.";             break;
  203.         case E_ERR:         ptr = "Audio Unit reported an error.";          break;
  204.  
  205.         case E_PAUSE:       ptr = "Audio is paused.";                       break;
  206.         case E_GAIN:        ptr = "Invalid gain index.";                    break;
  207.         case E_INDEX:       ptr = "Buffer index is invalid.";               break;
  208.         case E_LENGTH:      ptr = "Buffer length is invalid.";              break;
  209.         case E_NOBUFFER:    ptr = "No buffers were defined.";               break;
  210.         case E_IGNORED:     ptr = "Command ignored.";                       break;
  211.         case E_INVALID:     ptr = "Bad tone index specified.";              break;
  212.         case E_BUSY:        ptr = "Already performing requested action";    break;
  213.  
  214.         case E_RATE:        ptr = "Invalid RATE argument.";                 break;
  215.         case E_FORMAT:      ptr = "Invalid FORMAT argument.";               break;
  216.         case E_MODE:        ptr = "Invalid MODE argument (REC/PLAY).";      break;
  217.         case E_VXD:         ptr = "Windows VxD Request error.";             break;
  218.         case E_CHANNELS:    ptr = "Invalid channel count.";                 break;
  219.  
  220.         default:
  221.             printf(
  222.                 "\n\nUnknown Driver Error: 0x%04X\n\n",
  223.                 status
  224.                 );
  225.             break;
  226.         }
  227.  
  228.         /* Recognized the error code, so print the message. */
  229.  
  230.     if ( ptr != NULL )
  231.         printf( "\n\n%s\n\n", ptr );
  232.  
  233.     exit( EXIT_BAD_STATUS );
  234.     }
  235.  
  236.  
  237. /** DSSetup *****************************************************************/
  238. /*                                                                          */
  239. /*  Do some driver housekeeping.                                            */
  240. /*                                                                          */
  241. /****************************************************************************/
  242.  
  243. static int DSSetup ( void )
  244.     {
  245.     long support;
  246.     int status;
  247.  
  248.         /* Setup driver stuff.  */
  249.  
  250.     if (( status = DSReset()) != E_OK )
  251.         DSExit( status );
  252.     if (( status = DSSetBuffer( 0, sizeof(buf0), (char far *)buf0)) != E_OK )
  253.         DSExit( status );
  254.     if (( status = DSSetBuffer( 1, sizeof(buf1), (char far *)buf1)) != E_OK )
  255.         DSExit( status );
  256.     if (( status = DSSetGain( 21 )) != E_OK )
  257.         DSExit( status );
  258.  
  259.     DSQuery( NULL, NULL, &support );
  260.  
  261.     return( (int)support );     // We don't care about upper bits.
  262.     }
  263.  
  264.  
  265. /** WriteFileHeader *********************************************************/
  266. /*                                                                          */
  267. /*  Write a file header appropriate to the passed format.  This routine     */
  268. /*  always rewinds the file so that it can be used to update the header     */
  269. /*  with the correct file length after recording is complete.               */
  270. /*                                                                          */
  271. /****************************************************************************/
  272.  
  273. static void WriteFileHeader ( FILE *fd )
  274.     {
  275.     WORD   tLength;
  276.  
  277.     rewind( fd );
  278.     switch ( format )
  279.         {
  280.         case DF_CVSD:
  281.             fputc( H_DIGICVSD, fd );
  282.  
  283.             tLength = ( length > 65535 ) ? 65535 : (WORD)length;
  284.  
  285.             fputc( rate, fd);                   // CVSD Rate.
  286.             fputc( (int)(tLength / 256), fd );  // File length MSB
  287.             fputc( (int)(tLength & 255), fd );  // File length LSB
  288.             break;
  289.  
  290.         case DF_OKI4:
  291.             fputc( H_ADPCM, fd );
  292.  
  293.                 /* Convert sample rate to OKI rate code.    */
  294.  
  295.             fputc( ((int)rate / 1000) - 8, fd );
  296.             break;
  297.                 
  298.         case DF_DVI4:
  299.             fputc( H_PCMDVI, fd );
  300.             putw( (int)rate, fd );
  301.             break;
  302.  
  303.         default:
  304.  
  305.                 /* Rest of digispeech headers that we can   */
  306.                 /* write are single byte headers.           */
  307.  
  308.             fputc( digiHeaders[ format], fd );
  309.             break;
  310.         }
  311.  
  312.     }
  313.  
  314.  
  315. /** SetFormat ***************************************************************/
  316. /*                                                                          */
  317. /*  Setup global variables for recording operation.                         */
  318. /*                                                                          */
  319. /*  Exits program if illegal file type encountered.                         */
  320. /*                                                                          */
  321. /****************************************************************************/
  322.  
  323. static void SetFormat ( void )
  324.     {
  325.     int displayRate;
  326.  
  327.         /* Parse selected format.   */
  328.  
  329.     displayRate = rate;
  330.     switch ( format )
  331.         {
  332.         case DF_PCM8:
  333.         case DF_PCMMU:
  334.             break;
  335.  
  336.         case DF_PCMA:
  337.  
  338.                 /* Only PORT-ABLE Sound supports recording A-law PCM.   */
  339.  
  340.             if (( support & CAPS_301 ) == 0 )
  341.                 format = REC_UNSUPPORTED;
  342.             break;
  343.  
  344.         case DF_DVI4:
  345.  
  346.                 /* Only PORT-ABLE Sound supports DVI.   */
  347.  
  348.             if (( support & CAPS_301 ) == 0 )
  349.                 format = REC_UNSUPPORTED;
  350.             break;
  351.  
  352.         case DF_OKI4:
  353.  
  354.                 /* Only PORT-ABLE Sound supports recording OKI ADPCM.   */
  355.  
  356.             if (( support & CAPS_301 ) == 0 )
  357.                 format = REC_UNSUPPORTED;
  358.             break;
  359.  
  360.         case DF_CVSD:
  361.             displayRate = rateValues[ rate];
  362.  
  363.                 /* Current PORT-ABLE Sound does not support CVSD.   */
  364.  
  365.             if ( support & CAPS_301 )
  366.                 {
  367.                 if (( support & CAPS_DIGI ) == 0 )
  368.                     format = REC_UNSUPPORTED;
  369.                 }
  370.             break;
  371.  
  372.         default:
  373.             format = REC_UNSUPPORTED;
  374.             break;
  375.         }
  376.  
  377.     if ( format == REC_UNSUPPORTED )
  378.         {
  379.         printf( "\nAudio device or driver does not support requested function." );
  380.         exit( EXIT_NOT_SUPPORTED );
  381.         }
  382.  
  383.         /* Display selected format and rate.    */
  384.  
  385.     printf(
  386.         "\nData Format:\t%s\nSample Rate:\t%u\n",
  387.         formatMsgs[ format],
  388.         displayRate
  389.         );
  390.     }
  391.  
  392.  
  393. /** RecordManager ***********************************************************/
  394. /*                                                                          */
  395. /*  Manage the background record operation.                                 */
  396. /*                                                                          */
  397. /*  Returns E_OK if recording went well.                                    */
  398. /*                                                                          */
  399. /****************************************************************************/
  400.  
  401. static int RecordManager ( FILE *fd )
  402.     {
  403.     int     stopRecording, bufferIndex, status, written;
  404.     DWORD   rest;
  405.  
  406.         /* Warn user and wait for starting gun. */
  407.  
  408.     printf( "\nPress any key to begin recording . . .\n" );
  409.     getch();
  410.     printf( "\nRecording . . . ['ESC' aborts, 'P' pauses]\n" );
  411.  
  412.         /* Start appropriate background process.    */
  413.         /* We pass StartRecord a length of FFFFFFFF */
  414.         /* so that it will record a loooonnng time. */
  415.         /* The user must cancel the recording       */
  416.         /* using the ESC key at the desired time.   */
  417.  
  418.     status = StartRecord( format, rate, 1, 0xFFFFFFFF );
  419.     if ( status != E_OK )
  420.         return( status );
  421.  
  422.         /* Monitor the background process.  */
  423.  
  424.     stopRecording = FALSE;
  425.     bufferIndex = 0;
  426.     for ( ;; )
  427.         {
  428.             /* Check for user intervention. */
  429.  
  430.         switch ( KeyPressed() )
  431.             {
  432.             case 'p':
  433.             case 'P':
  434.                 status = DSPause();
  435.                 printf( "-- Pause ('R' resumes recording) :" );
  436.                 break;
  437.  
  438.             case 'r':
  439.             case 'R':
  440.                 status = DSResume();
  441.                 printf( "-- Resume\n" );
  442.                 break;
  443.  
  444.             case ESC_KEY:
  445.                 stopRecording = TRUE;
  446.                 break;
  447.  
  448.             default:
  449.                 break;
  450.             }
  451.  
  452.         status = DSGetStatus();
  453.         if ( stopRecording )
  454.             break;
  455.  
  456.             /* Skip rest if buffers are still busy. */
  457.  
  458.         if (status == E_PAUSE)
  459.             continue;
  460.         if ((status == E_BUF0) && bufferIndex == 0)
  461.             continue;
  462.         if ((status == E_BUF1) && bufferIndex == 1)
  463.             continue;
  464.  
  465.             /* Exit if error or end of action.  */
  466.  
  467.         if (( status != E_BUF0) && ( status != E_BUF1))
  468.             break;
  469.  
  470.             /* Save buffer 0 to disk.   */
  471.  
  472.         if ( bufferIndex == 0)
  473.             {
  474.             if ( fwrite( buf0, 1, sizeof(buf0), fd ) != sizeof(buf0))
  475.                 {
  476.                 printf( writeError );
  477.                 exit( EXIT_FILE_ERR );
  478.                 }
  479.             bufferIndex = 1;
  480.             }
  481.  
  482.             /* Save buffer 1 on disk.   */
  483.  
  484.         else 
  485.             {
  486.             if ( fwrite( buf1, 1, sizeof(buf1), fd ) != sizeof(buf1))
  487.                 {
  488.                 printf( writeError );
  489.                 exit( EXIT_FILE_ERR );
  490.                 }
  491.             bufferIndex = 0;
  492.             }
  493.         }
  494.  
  495.         /* Save any data left over from last time through loop. */
  496.  
  497.     if (( status == E_OK) || ( status == E_BUF0) || ( status == E_BUF1))
  498.         {
  499.             /* Get number of bytes unsaved. */
  500.  
  501.         rest = (DWORD)DSGetByteCount( &length );
  502.  
  503.             /* Write to disk.   */
  504.  
  505.         if ( bufferIndex == 0)
  506.             written = fwrite( buf0, 1, (WORD)rest, fd );
  507.         else
  508.             written = fwrite( buf1, 1, (WORD)rest, fd );
  509.  
  510.         if ( written != (int)rest )
  511.             {
  512.             printf( writeError );
  513.             exit( EXIT_FILE_ERR );
  514.             }
  515.         }
  516.  
  517.     return( status );
  518.     }
  519.  
  520.  
  521. /** ParseCommandLine ********************************************************/
  522. /*                                                                          */
  523. /*  Parse the command line (duh!).                                          */
  524. /*                                                                          */
  525. /****************************************************************************/
  526.  
  527. static int ParseCommandLine ( int argc, char *argv[] )
  528.     {
  529.     int annette, fileArg;
  530.  
  531.         /* Parse any parameters.    */
  532.  
  533.     format = DF_PCM8;           // Default record format.
  534.     rate = 8000;                // Default rate PCM.
  535.     fileArg = -1;
  536.     for ( annette = 1; annette < argc; annette++ )
  537.         {
  538.             /* Parse switches.  */
  539.  
  540.         if (( *argv[ annette] == '/' ) || ( *argv[ annette] == '-' ))
  541.             {
  542.             argv[ annette]++;
  543.             switch ( *argv[ annette] )
  544.                 {
  545.                 case 'a':
  546.                 case 'A':
  547.                     format = DF_PCMA;
  548.                     break;
  549.  
  550.                 case 'c':
  551.                 case 'C':
  552.                     format = DF_CVSD;
  553.                     rate = 5;                       // Default CVD rate
  554.                     break;
  555.  
  556.                 case 'd':
  557.                 case 'D':
  558.                     format = DF_DVI4;
  559.                     break;
  560.  
  561.                 case 'o':
  562.                 case 'O':
  563.                     format = DF_OKI4;
  564.                     break;
  565.  
  566.                 case 'p':
  567.                 case 'P':
  568.                     format = DF_PCM8;
  569.                     break;
  570.  
  571.                 case 'r':
  572.                 case 'R':
  573.                     argv[ annette]++;           // Get past switch character.
  574.                     if ( *argv[ annette] )
  575.                         rate = atoi( argv[ annette] );
  576.                     break;
  577.  
  578.                 case 'u':
  579.                 case 'U':
  580.                     format = DF_PCMMU;
  581.                     break;
  582.  
  583.                 default:
  584.                     Usage();
  585.                     break;
  586.                 }
  587.             }
  588.         else
  589.             fileArg = annette;
  590.         }
  591.  
  592.         /* Must specify a sound file.   */
  593.  
  594.     if ( fileArg == -1 )
  595.         Usage();
  596.  
  597.     return( fileArg );
  598.     }
  599.  
  600.  
  601. /** main ********************************************************************/
  602. /*                                                                          */
  603. /*  Record a sound file through DSP Solutions audio adapter.                */
  604. /*                                                                          */
  605. /****************************************************************************/
  606.  
  607. int main ( int argc, char *argv[] )
  608.     {
  609.     FILE    *fd;
  610.     int     status, fileArg;
  611.  
  612.     puts( "\nSound File Recorder, v1.00" );
  613.     puts( "Copyright 1993, DSP Solutions, Inc.\n" );
  614.  
  615.     fileArg = ParseCommandLine( argc, argv );
  616.  
  617.         /* Start up appropriate drivers.    */
  618.  
  619.     if ( !DriverInstalled() )
  620.         {
  621.         printf( "Cannot find Audio Driver");
  622.         exit( EXIT_BAD_STATUS );
  623.         }
  624.  
  625.     signal( SIGINT, SIG_IGN );          // Ignore Ctrl-Break
  626.  
  627.         /* File housekeeping.   */
  628.  
  629.     fd = fopen( argv[ fileArg], "wb" );
  630.     if ( fd == NULL )
  631.         {
  632.         printf( "\nCould not open %s \n\n", argv[ fileArg] );
  633.         exit( EXIT_FILE_ERR );
  634.         }
  635.  
  636.         /* Get audio driver capabilities, then write    */
  637.         /* audio file header information.               */
  638.  
  639.     support = DSSetup();
  640.     SetFormat();
  641.     WriteFileHeader( fd );
  642.  
  643.         /* Do recording.    */
  644.  
  645.     status = RecordManager( fd );
  646.  
  647.         /* If everything went well, we need */
  648.         /* to update the file header with   */
  649.         /* the actual file length.          */
  650.  
  651.     if ( status == E_OK )
  652.         WriteFileHeader( fd );
  653.  
  654.         /* Clean up and go home.    */
  655.  
  656.     DSReset();
  657.     fclose( fd );
  658.     DSExit( status );
  659.     }
  660.  
  661.  
  662.