home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Digispeech DS201A Drivers
/
dsapi_v120.zip
/
SOURCE
/
RECORD.C
< prev
Wrap
C/C++ Source or Header
|
1993-08-13
|
21KB
|
662 lines
/* RECORD.C */
/****************************************************************************/
/* */
/* Abstract: General purpose sound file recorder for DIGI drivers. */
/* */
/* Language: Microsoft C, v6.00a Model: LARGE Machine type: IBM PC */
/* */
/****************************************************************************/
/*
REV DATE AUTHOR DESCRIPTION
1.0 04-20-92 Lee Mulcahy Initial version.
--- 06-15-93 Lee Mulcahy Latest change.
*/
#include <stdio.h>
#include <signal.h>
#include <conio.h>
#include <io.h>
#include <dos.h>
/****************************************************************************/
/* */
/* Literals, etc. */
/* */
/****************************************************************************/
#include "digi.h"
#define ESC_KEY 0x1B
#define REC_UNSUPPORTED 0xFF
#define EXIT_BAD_USAGE 1
#define EXIT_FILE_ERR 2
#define EXIT_BAD_STATUS 3
#define EXIT_NOT_SUPPORTED 4
static char writeError [] = "\nError writing output file.\n";
static WORD rateValues [] = { 1800, 2400, 3000, 3600, 4200, 4800 };
static char *formatMsgs [] =
{
"Linear 8 bit PCM", // DF_PCM8 0
"Mu-Law PCM", // DF_PCMMU 1
"A-Law PCM", // DF_PCMA 2
"Linear 16 bit PCM", // DF_PCM16 3
"SB ADPCM 2 bit", // DF_SB2 4
"SB ADPCM 2.6 bit", // DF_SB3 5
"SB ADPCM 4 bit", // DF_SB4 6
"OKI ADPCM 4 bit", // DF_OKI4 7
"DVI ADPCM 4 bit", // DF_DVI4 8
"Digispeech Realtime", // DF_DIGIREAL 9
"Digispeech STD", // DF_DIGISTD 10
"Digispeech FIX", // DF_DIGIFIX 11
"",
"",
"CVSD", // DF_CVSD 14
NULL
};
/* Digispeech header format codes, indexed by data format DF_xx. */
static int digiHeaders [] =
{
H_PCMRAW, // DF_PCM8
H_PCMMU, // DF_PCMMU
H_PCMA, // DF_PCMA
H_PCMRAW16, // DF_PCM16
H_SB2, // DF_SB2
H_SB3, // DF_SB3
H_SB4, // DF_SB4
H_ADPCM, // DF_OKI4
H_PCMDVI, // DF_DVI4
H_DIGIREAL, // DF_DIGIREAL
H_DIGISTD, // DF_DIGISTD
H_DIGIFIX, // DF_DIGIFIX
0,
0,
H_DIGICVSD // DF_CVSD
};
/* Driver information. */
static WORD support = 0;
/* Global audio file information. */
static WORD rate;
static WORD format;
static WORD headerLen;
static int recordLen;
static DWORD length;
/* Audio data buffers. */
static char buf0[16384];
static char buf1[16384];
static void Usage ( void );
static int KeyPressed ( void );
static void DSExit ( int status );
static int DSSetup ( void );
static void WriteFileHeader ( FILE *fd );
static void SetFormat ( void );
static int RecordManager ( FILE *fd );
static int ParseCommandLine ( int argc, char *argv[] );
int main ( int argc, char *argv[] );
/** Usage *******************************************************************/
/* */
/* Display the program syntax and parameter list. */
/* */
/****************************************************************************/
static void Usage ( void )
{
puts( " Usage: RECORD <switches> <filename> \n" );
puts( " Records sound files of the following formats:" );
puts( " (Requires DIGI driver. Some versions/configurations" );
puts( " do not support all formats)\n" );
puts( " Digispeech CVSD (*.CVS, *.CVx)" );
puts( " PCM 8-bit linear (*.PCM)" );
puts( " PCM Mu-Law (*.LIN)" );
puts( " OKI ADPCM (*.OKI)" );
puts( " DVI ADPCM (*.DVI)\n" );
puts( " /a - Record as A-Law PCM" );
puts( " /c - Record as Digispeech CVSD" );
puts( " /d - Record as DVI ADPCM" );
puts( " /o - Record as OKI ADPCM" );
puts( " /p - Record as RAW PCM (default)" );
puts( " /u - Record as u-Law PCM" );
puts( " /rx - Set Record sample rate to x (8000 or 11025)" );
puts( " CVSD rate range is 0 - 5\n" );
exit( EXIT_BAD_USAGE );
}
/** KeyPressed **************************************************************/
/* */
/* If a key was pressed, return the key, else return FALSE. */
/* */
/****************************************************************************/
int KeyPressed ( void )
{
int key;
/* Check for ESC key. */
key = 0;
if ( kbhit())
{
key = getch();
/* If NULL, get another key and */
/* discard it (IBM PC oddity). */
if ( key == 0)
getch();
}
return( key );
}
/** DSExit ******************************************************************/
/* */
/* Print error message, status code, and then exit with error code. */
/* */
/* Quite a few of the error messages listed here will never be encountered */
/* by this program, but are included 'cause I wanted all of 'em. */
/* */
/****************************************************************************/
static void DSExit ( int status )
{
char *ptr;
/* If status is 0, this is actually a 'good' exit. */
if ( status == 0 )
exit( 0 );
ptr = NULL;
switch ( status )
{
case E_UNDEFINED: ptr = "Undefined command."; break;
case E_BUF0: ptr = "Current buffer is 0."; break;
case E_BUF1: ptr = "Current buffer is 1."; break;
case E_MUSIC: ptr = "Driver is in 3-tone gen mode."; break;
case E_COM: ptr = "Communication error."; break;
case E_LPC: ptr = "LPC index out of range."; break;
case E_CVSDSPEED: ptr = "CVSD speed is invalid."; break;
case E_TIMEOUT: ptr = "Audio Unit not responding."; break;
case E_ERR: ptr = "Audio Unit reported an error."; break;
case E_PAUSE: ptr = "Audio is paused."; break;
case E_GAIN: ptr = "Invalid gain index."; break;
case E_INDEX: ptr = "Buffer index is invalid."; break;
case E_LENGTH: ptr = "Buffer length is invalid."; break;
case E_NOBUFFER: ptr = "No buffers were defined."; break;
case E_IGNORED: ptr = "Command ignored."; break;
case E_INVALID: ptr = "Bad tone index specified."; break;
case E_BUSY: ptr = "Already performing requested action"; break;
case E_RATE: ptr = "Invalid RATE argument."; break;
case E_FORMAT: ptr = "Invalid FORMAT argument."; break;
case E_MODE: ptr = "Invalid MODE argument (REC/PLAY)."; break;
case E_VXD: ptr = "Windows VxD Request error."; break;
case E_CHANNELS: ptr = "Invalid channel count."; break;
default:
printf(
"\n\nUnknown Driver Error: 0x%04X\n\n",
status
);
break;
}
/* Recognized the error code, so print the message. */
if ( ptr != NULL )
printf( "\n\n%s\n\n", ptr );
exit( EXIT_BAD_STATUS );
}
/** DSSetup *****************************************************************/
/* */
/* Do some driver housekeeping. */
/* */
/****************************************************************************/
static int DSSetup ( void )
{
long support;
int status;
/* Setup driver stuff. */
if (( status = DSReset()) != E_OK )
DSExit( status );
if (( status = DSSetBuffer( 0, sizeof(buf0), (char far *)buf0)) != E_OK )
DSExit( status );
if (( status = DSSetBuffer( 1, sizeof(buf1), (char far *)buf1)) != E_OK )
DSExit( status );
if (( status = DSSetGain( 21 )) != E_OK )
DSExit( status );
DSQuery( NULL, NULL, &support );
return( (int)support ); // We don't care about upper bits.
}
/** WriteFileHeader *********************************************************/
/* */
/* Write a file header appropriate to the passed format. This routine */
/* always rewinds the file so that it can be used to update the header */
/* with the correct file length after recording is complete. */
/* */
/****************************************************************************/
static void WriteFileHeader ( FILE *fd )
{
WORD tLength;
rewind( fd );
switch ( format )
{
case DF_CVSD:
fputc( H_DIGICVSD, fd );
tLength = ( length > 65535 ) ? 65535 : (WORD)length;
fputc( rate, fd); // CVSD Rate.
fputc( (int)(tLength / 256), fd ); // File length MSB
fputc( (int)(tLength & 255), fd ); // File length LSB
break;
case DF_OKI4:
fputc( H_ADPCM, fd );
/* Convert sample rate to OKI rate code. */
fputc( ((int)rate / 1000) - 8, fd );
break;
case DF_DVI4:
fputc( H_PCMDVI, fd );
putw( (int)rate, fd );
break;
default:
/* Rest of digispeech headers that we can */
/* write are single byte headers. */
fputc( digiHeaders[ format], fd );
break;
}
}
/** SetFormat ***************************************************************/
/* */
/* Setup global variables for recording operation. */
/* */
/* Exits program if illegal file type encountered. */
/* */
/****************************************************************************/
static void SetFormat ( void )
{
int displayRate;
/* Parse selected format. */
displayRate = rate;
switch ( format )
{
case DF_PCM8:
case DF_PCMMU:
break;
case DF_PCMA:
/* Only PORT-ABLE Sound supports recording A-law PCM. */
if (( support & CAPS_301 ) == 0 )
format = REC_UNSUPPORTED;
break;
case DF_DVI4:
/* Only PORT-ABLE Sound supports DVI. */
if (( support & CAPS_301 ) == 0 )
format = REC_UNSUPPORTED;
break;
case DF_OKI4:
/* Only PORT-ABLE Sound supports recording OKI ADPCM. */
if (( support & CAPS_301 ) == 0 )
format = REC_UNSUPPORTED;
break;
case DF_CVSD:
displayRate = rateValues[ rate];
/* Current PORT-ABLE Sound does not support CVSD. */
if ( support & CAPS_301 )
{
if (( support & CAPS_DIGI ) == 0 )
format = REC_UNSUPPORTED;
}
break;
default:
format = REC_UNSUPPORTED;
break;
}
if ( format == REC_UNSUPPORTED )
{
printf( "\nAudio device or driver does not support requested function." );
exit( EXIT_NOT_SUPPORTED );
}
/* Display selected format and rate. */
printf(
"\nData Format:\t%s\nSample Rate:\t%u\n",
formatMsgs[ format],
displayRate
);
}
/** RecordManager ***********************************************************/
/* */
/* Manage the background record operation. */
/* */
/* Returns E_OK if recording went well. */
/* */
/****************************************************************************/
static int RecordManager ( FILE *fd )
{
int stopRecording, bufferIndex, status, written;
DWORD rest;
/* Warn user and wait for starting gun. */
printf( "\nPress any key to begin recording . . .\n" );
getch();
printf( "\nRecording . . . ['ESC' aborts, 'P' pauses]\n" );
/* Start appropriate background process. */
/* We pass StartRecord a length of FFFFFFFF */
/* so that it will record a loooonnng time. */
/* The user must cancel the recording */
/* using the ESC key at the desired time. */
status = StartRecord( format, rate, 1, 0xFFFFFFFF );
if ( status != E_OK )
return( status );
/* Monitor the background process. */
stopRecording = FALSE;
bufferIndex = 0;
for ( ;; )
{
/* Check for user intervention. */
switch ( KeyPressed() )
{
case 'p':
case 'P':
status = DSPause();
printf( "-- Pause ('R' resumes recording) :" );
break;
case 'r':
case 'R':
status = DSResume();
printf( "-- Resume\n" );
break;
case ESC_KEY:
stopRecording = TRUE;
break;
default:
break;
}
status = DSGetStatus();
if ( stopRecording )
break;
/* Skip rest if buffers are still busy. */
if (status == E_PAUSE)
continue;
if ((status == E_BUF0) && bufferIndex == 0)
continue;
if ((status == E_BUF1) && bufferIndex == 1)
continue;
/* Exit if error or end of action. */
if (( status != E_BUF0) && ( status != E_BUF1))
break;
/* Save buffer 0 to disk. */
if ( bufferIndex == 0)
{
if ( fwrite( buf0, 1, sizeof(buf0), fd ) != sizeof(buf0))
{
printf( writeError );
exit( EXIT_FILE_ERR );
}
bufferIndex = 1;
}
/* Save buffer 1 on disk. */
else
{
if ( fwrite( buf1, 1, sizeof(buf1), fd ) != sizeof(buf1))
{
printf( writeError );
exit( EXIT_FILE_ERR );
}
bufferIndex = 0;
}
}
/* Save any data left over from last time through loop. */
if (( status == E_OK) || ( status == E_BUF0) || ( status == E_BUF1))
{
/* Get number of bytes unsaved. */
rest = (DWORD)DSGetByteCount( &length );
/* Write to disk. */
if ( bufferIndex == 0)
written = fwrite( buf0, 1, (WORD)rest, fd );
else
written = fwrite( buf1, 1, (WORD)rest, fd );
if ( written != (int)rest )
{
printf( writeError );
exit( EXIT_FILE_ERR );
}
}
return( status );
}
/** ParseCommandLine ********************************************************/
/* */
/* Parse the command line (duh!). */
/* */
/****************************************************************************/
static int ParseCommandLine ( int argc, char *argv[] )
{
int annette, fileArg;
/* Parse any parameters. */
format = DF_PCM8; // Default record format.
rate = 8000; // Default rate PCM.
fileArg = -1;
for ( annette = 1; annette < argc; annette++ )
{
/* Parse switches. */
if (( *argv[ annette] == '/' ) || ( *argv[ annette] == '-' ))
{
argv[ annette]++;
switch ( *argv[ annette] )
{
case 'a':
case 'A':
format = DF_PCMA;
break;
case 'c':
case 'C':
format = DF_CVSD;
rate = 5; // Default CVD rate
break;
case 'd':
case 'D':
format = DF_DVI4;
break;
case 'o':
case 'O':
format = DF_OKI4;
break;
case 'p':
case 'P':
format = DF_PCM8;
break;
case 'r':
case 'R':
argv[ annette]++; // Get past switch character.
if ( *argv[ annette] )
rate = atoi( argv[ annette] );
break;
case 'u':
case 'U':
format = DF_PCMMU;
break;
default:
Usage();
break;
}
}
else
fileArg = annette;
}
/* Must specify a sound file. */
if ( fileArg == -1 )
Usage();
return( fileArg );
}
/** main ********************************************************************/
/* */
/* Record a sound file through DSP Solutions audio adapter. */
/* */
/****************************************************************************/
int main ( int argc, char *argv[] )
{
FILE *fd;
int status, fileArg;
puts( "\nSound File Recorder, v1.00" );
puts( "Copyright 1993, DSP Solutions, Inc.\n" );
fileArg = ParseCommandLine( argc, argv );
/* Start up appropriate drivers. */
if ( !DriverInstalled() )
{
printf( "Cannot find Audio Driver");
exit( EXIT_BAD_STATUS );
}
signal( SIGINT, SIG_IGN ); // Ignore Ctrl-Break
/* File housekeeping. */
fd = fopen( argv[ fileArg], "wb" );
if ( fd == NULL )
{
printf( "\nCould not open %s \n\n", argv[ fileArg] );
exit( EXIT_FILE_ERR );
}
/* Get audio driver capabilities, then write */
/* audio file header information. */
support = DSSetup();
SetFormat();
WriteFileHeader( fd );
/* Do recording. */
status = RecordManager( fd );
/* If everything went well, we need */
/* to update the file header with */
/* the actual file length. */
if ( status == E_OK )
WriteFileHeader( fd );
/* Clean up and go home. */
DSReset();
fclose( fd );
DSExit( status );
}