home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Digispeech DS201A Drivers
/
dsapi_v120.zip
/
SOURCE
/
EMULATE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-08-13
|
27KB
|
800 lines
/* EMULATE.C */
/****************************************************************************/
/* */
/* Abstract: Synthesizer Emulator demonstration. */
/* */
/* Language: Microsoft C, v6.00a Model: LARGE Machine type: IBM PC */
/* */
/****************************************************************************/
/*
REV DATE AUTHOR DESCRIPTION
1.0 08-01-92 Lee Mulcahy Initial version.
--- 08-13-93 Lee Mulcahy Latest change.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <conio.h>
#include <io.h>
#include <dos.h>
#include <sys\types.h>
#include <sys\timeb.h>
/****************************************************************************/
/* */
/* Literals, etc. */
/* */
/****************************************************************************/
#include "digi.h"
#define E_NOEMULATE (E_CHANNELS + 1) // Define our own error message
#define SYNTH_INIT 0
#define SYNTH_READ 1
#define SYNTH_WRITE 2
#define SYNTH_RESET 3
#define KEY_ON 0x20 // Key-on bit for REG_FMSB
#define REG_SOUND 0x20 // Sound Characteristics register
#define REG_KSLTL 0x40 // KSL / Total Level register
#define REG_ARDR 0x60 // Attack Rate / Decay Rate register
#define REG_SLRR 0x80 // Sustain Level / Release Rate register
#define REG_FLSB 0xA0 // Frequency LSB register
#define REG_FMSB 0xB0 // Frequency MSB register + key-on bit
#define REG_MOD 0xC0 // Modulation mode / feedback register
#define REG_WAVE 0xE0 // Wave Select register
#define NOT_FORCED -2
#define ESC_KEY 0x1B
#define PLAY_UNSUPPORTED 0xFF
#define EXIT_BAD_USAGE 1
#define EXIT_FILE_ERR 2
#define EXIT_BAD_STATUS 3
#define EXIT_NOT_SUPPORTED 4
static int note = 0;
static int octave = 0;
static int paused = FALSE;
static char *notes [] =
{
"C ",
"C#",
"D ",
"D#",
"E ",
"F ",
"F#",
"G ",
"G#",
"A ",
"A#",
"B "
};
/* OKI ADPCM play rates. */
static int playRates [4] = { 8000, 9000, 10000, 11000 };
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
};
/* Driver information. */
static WORD support = 0;
/* Synthesizer entry point pointer. */
static int (_far *Emulate)( int function, BYTE addr, BYTE value );
static BYTE freqLSB;
static BYTE freqMSB;
/* BIOS Timer Tick Counter location. */
static long far *tTickCount = (long far *)0x0040006CL;
/* Global audio file information. */
static WORD rate;
static WORD format;
static WORD headerLen;
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 void LoadInstrument ( void );
static int DSSetup ( void );
static void PlayNote ( int note, int octave );
static void Pause ( void );
static void Resume ( void );
static void ParseFileHeader ( FILE *fd );
static void PlayScales ( void );
static void PlayRandom ( void );
static int PlayManager ( FILE *fd );
static int ParseCommandLine ( int argc, char *argv[] );
int main ( int argc, char *argv [] );
/****************************************************************************/
/* */
/* Default Instrument Table */
/* */
/****************************************************************************/
typedef struct _instrumentDef
{
BYTE mSoundChar; // Modulator sound characteristic
BYTE cSoundChar; // Carrier sound characteristic
BYTE mScaling; // Modulator Scaling and Output level
BYTE cScaling; // Carrier Scaling and Output level
BYTE mAttackDecay; // Modulator Attack and Decay rates
BYTE cAttackDecay; // Carrier Attack and Decay rates
BYTE mSustainRelease; // Modulator Sustain and Release rates
BYTE cSustainRelease; // Carrier Sustain and Release rates
BYTE mWaveSelect; // Modulator Wave Select
BYTE cWaveSelect; // Carrier Wave Select
BYTE feedback; // Feedback / connection
BYTE reserved [5]; // Reserved for future use.
} IDEF;
static IDEF testInstrument =
{
0x21, 0x31, // MOD/CAR 0x2x Sound Characteristic
0x00, 0x90, // MOD/CAR 0x4x Scaling / Output Level
0x81, 0xF1, // MOD/CAR 0x6x Attack / Decay
0x03, 0x05, // MOD/CAR 0x8x Sustain / Release
0x00, 0x00, // MOD/CAR 0xEx Wave Select
0x02, // 0xCx Feedback / Connection
0x00, 0x00, 0x00, 0x00, 0x00 // Unused
};
static WORD freqTable [12] =
{
// C C# D D# E F F# G
0x0157, 0x016B, 0x0181, 0x0198, 0x01B0, 0x01CA, 0x01E5, 0x0202,
// G# A A# B
0x0220, 0x0241, 0x0263, 0x0287
};
/** Usage *******************************************************************/
/* */
/* Display the program syntax and parameter list. */
/* */
/****************************************************************************/
static void Usage ( void )
{
puts( " Usage: EMULATE <switches> filename \n" );
puts( " Demonstrates use of PDIGI /X emulator interface.\n" );
puts( " Plays a PCM Mu-Law or Linear file simultaneously" );
puts( " with synthesized sound.\n" );
puts( " /rx - Force playback rate to x (default 8000)" );
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 = "Driver or device is busy"; break;
case E_SYNTH: ptr = "Driver is in synthesizer mode"; 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;
case E_NOEMULATE: ptr = "No Synth Emulator, use PDIGI /X"; break;
default:
printf(
"\nUnknown Driver Error: 0x%04X\n\n",
status
);
break;
}
/* Recognized the error code, so print the message. */
if ( ptr != NULL )
printf( "\n%s\n\n", ptr );
exit( EXIT_BAD_STATUS );
}
/** LoadInstrument **********************************************************/
/* */
/* Program the emulator for a single instrument on voice cell 0. */
/* */
/****************************************************************************/
static void LoadInstrument ( void )
{
Emulate( SYNTH_WRITE, REG_SOUND, testInstrument.mSoundChar );
Emulate( SYNTH_WRITE, REG_SOUND + 3, testInstrument.cSoundChar );
Emulate( SYNTH_WRITE, REG_KSLTL, testInstrument.mScaling );
Emulate( SYNTH_WRITE, REG_KSLTL + 3, testInstrument.cScaling );
Emulate( SYNTH_WRITE, REG_ARDR, testInstrument.mAttackDecay );
Emulate( SYNTH_WRITE, REG_ARDR + 3, testInstrument.cAttackDecay );
Emulate( SYNTH_WRITE, REG_SLRR, testInstrument.mSustainRelease );
Emulate( SYNTH_WRITE, REG_SLRR + 3, testInstrument.cSustainRelease );
Emulate( SYNTH_WRITE, REG_WAVE, testInstrument.mWaveSelect );
Emulate( SYNTH_WRITE, REG_WAVE + 3, testInstrument.cWaveSelect );
Emulate( SYNTH_WRITE, REG_MOD, testInstrument.feedback );
}
/** DSSetup *****************************************************************/
/* */
/* Do some driver housekeeping. */
/* */
/****************************************************************************/
static int DSSetup ( void )
{
DWORD 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 );
/* Get the emulator entry point, init the synthesizer, */
/* and load an instrument. */
Emulate = DSGetEntry();
if ( Emulate == NULL )
DSExit( E_NOEMULATE );
if (( status = Emulate( SYNTH_INIT, 0, 0 )) != E_OK )
DSExit( status );
LoadInstrument();
return( (int)support ); // We don't care about upper bits.
}
/** PlayNote ****************************************************************/
/* */
/* Program the emulator to play a single note on voice cell 0. */
/* */
/****************************************************************************/
static void PlayNote ( int note, int octave )
{
WORD freq;
octave = ( octave << 10 ) & 0x1C00; // move to correct position
/* Determine frequency value. */
freq = freqTable[ note] | octave;
freqLSB = (BYTE)freq;
freqMSB = (BYTE)((freq & 0xFF00) >> 8);
/* Write frequency with key-on bit set. */
Emulate( SYNTH_WRITE, REG_FLSB, freqLSB );
Emulate( SYNTH_WRITE, REG_FMSB, (BYTE)(freqMSB | KEY_ON) );
}
/** Pause *******************************************************************/
/* */
/* Pause the driver. This involves turning off the current synthesizer */
/* note and then calling the driver pause function. */
/* */
/****************************************************************************/
static void Pause ( void )
{
/* Leave if already in paused state. */
if ( paused )
return;
/* Set Sustain Level / Release Rate to diminishing sound. */
Emulate( SYNTH_WRITE, REG_SLRR + 3, 0x13 );
/* Write the same frequency, but with the KEYON bit cleared. */
Emulate( SYNTH_WRITE, REG_FLSB, freqLSB );
Emulate( SYNTH_WRITE, REG_FMSB, freqMSB );
/* We must wait for a short period of time to */
/* allow the synthesizers commands to be sent */
/* to the PORT*ABLE Sound before going on. */
/* This is because the DSPause() routine is */
/* immediate and may prevent these commands */
/* from reaching the unit. */
_Wait( 120 );
/* Now send a pause to the digitized portion of the driver. */
DSPause();
printf( "\n-- Pause ('R' resumes play) : " );
paused = TRUE;
}
/** Resume ******************************************************************/
/* */
/* Allow the driver to continue. */
/* */
/****************************************************************************/
static void Resume ( void )
{
/* Leave if not in paused state. */
if ( !paused )
return;
/* Restore the previous sustain level */
/* and release rate for the voice. */
Emulate( SYNTH_WRITE, REG_SLRR + 3, testInstrument.cSustainRelease );
/* Let rest of driver resume. */
DSResume();
printf( "-- Resume\n" );
paused = FALSE;
}
/** ParseFileHeader *********************************************************/
/* */
/* Determine file type and audio characteristics from file header. */
/* */
/* Exits program if illegal file type encountered. */
/* */
/****************************************************************************/
static void ParseFileHeader ( FILE *fd )
{
char fileHead[10];
length = filelength( fileno( fd )); // Length of sound data.
/* First byte of file should determine its type. */
format = fgetc( fd );
rewind( fd );
/* Parse headers by file type. */
headerLen = 0;
switch ( format & 0xFF )
{
case H_PCMRAW:
format = DF_PCM8;
headerLen = 1;
length--;
if ( rate == NOT_FORCED)
rate = 8000;
if (( support & CAPS_EPCM ) == 0 )
format = PLAY_UNSUPPORTED;
break;
case H_PCMMU:
format = DF_PCMMU;
headerLen = 1;
length--;
if ( rate == NOT_FORCED)
rate = 8000;
if (( support & CAPS_EPCM ) == 0 )
format = PLAY_UNSUPPORTED;
break;
default:
printf( "\nMust be PCM8 or PCMMU format digitized audio.\n" );
DSReset();
fclose( fd );
exit( EXIT_FILE_ERR );
break;
}
if ( format == PLAY_UNSUPPORTED )
{
printf( "\nAudio device or driver does not support requested function." );
exit( EXIT_NOT_SUPPORTED );
}
/* Display format and rate. */
printf(
"\nData Format:\t%s\nSample Rate:\t%u\n",
formatMsgs[ format],
rate
);
}
/** PlayScales **************************************************************/
/* */
/* Play a 12 note scale through 8 octaves. Allows the user to break the */
/* sequence by pressing any key. */
/* */
/****************************************************************************/
static void PlayScales ( void )
{
for ( octave = 0; octave < 8; octave++ )
{
for ( note = 0; note < 12; note++ )
{
printf( "Note: %s Octave: %d\r", notes[ note], octave + 1 );
PlayNote( note, octave );
_Wait( 250 );
if ( kbhit())
break;
}
if ( kbhit())
break;
}
/* Get rid of any keystrokes and reset note index. */
while ( kbhit())
getch();
printf( "\n\n" );
note = 0;
octave = 0;
}
/** PlayRandom **************************************************************/
/* */
/* Play a random note from one of the 8 octaves. */
/* */
/****************************************************************************/
static void PlayRandom ( void )
{
static int noteDelay = 0;
if (( ++noteDelay % 500) == 0 )
{
printf( "Note: %s Octave: %d\n", notes[ note], octave + 1 );
PlayNote( note, octave );
note = (WORD)((++note + (*tTickCount ^ 0x55)) % 12);
octave = (WORD)((++octave + (*tTickCount ^ 0x55)) % 8);
}
}
/** PlayManager *************************************************************/
/* */
/* Manage the background play operation. */
/* */
/****************************************************************************/
static int PlayManager ( FILE *fd )
{
int stopPlay, bufferIndex, status;
/* Discard header and fill first two buffers. */
rewind( fd );
fread( buf0, 1, headerLen, fd );
fread( buf0, 1, sizeof(buf0), fd );
fread( buf1, 1, sizeof(buf1), fd );
/* Start and manage appropriate background process. */
status = StartPlay( format, rate, 1, length );
if ( status != E_OK )
return( status );
/* Monitor background process. */
stopPlay = FALSE;
bufferIndex = 0;
for ( ;; )
{
/* Check for user intervention. */
switch ( KeyPressed() )
{
case 'p':
case 'P':
Pause();
break;
case 'r':
case 'R':
Resume();
break;
case ESC_KEY:
stopPlay = TRUE;
break;
default:
break;
}
status = DSGetStatus();
if ( stopPlay )
break;
/* Skip rest if paused. */
if (status == E_PAUSE)
continue;
PlayRandom();
/* Skip rest if buffers are still busy. */
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;
/* Fill empty buffer. */
if ( bufferIndex == 0)
{
status = fread( buf0, 1, sizeof(buf0), fd );
bufferIndex = 1;
}
else
{
status = fread( buf1, 1, sizeof(buf1), fd );
bufferIndex = 0;
}
}
/* E_SYNTH just means that the synthesizer */
/* is active, it is not an actual error. */
if ( status == E_SYNTH )
status = E_OK;
return( status );
}
/** ParseCommandLine ********************************************************/
/* */
/* Parse the command line (duh!). */
/* */
/****************************************************************************/
static int ParseCommandLine ( int argc, char *argv[] )
{
int annette, fileArg;
/* Parse any parameters. */
rate = NOT_FORCED;
fileArg = -1;
for ( annette = 1; annette < argc; annette++ )
{
/* Parse switches. */
if (( *argv[ annette] == '/' ) || ( *argv[ annette] == '-' ))
{
argv[ annette]++;
switch ( *argv[ annette] )
{
case 'r':
case 'R':
argv[ annette]++; // Get past switch character.
if ( *argv[ annette] )
rate = atoi( argv[ annette] );
break;
default:
Usage();
break;
}
}
else
fileArg = annette;
}
/* Must specify a sound file. */
if ( fileArg == -1 )
Usage();
return( fileArg );
}
/** main ********************************************************************/
/* */
/* Play a sound file through DIGI driver. */
/* */
/****************************************************************************/
int main ( int argc, char *argv [] )
{
FILE *fd;
int fileArg, status;
puts( "\nEmulator Interface Demonstration, 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], "rb" );
if ( fd == NULL )
{
printf( "\nCould not open %s \n\n", argv[ fileArg] );
exit( EXIT_FILE_ERR );
}
/* Get audio driver capabilities, then get audio */
/* data information from file header. */
support = DSSetup();
PlayScales();
/* Find out what type of digitized audio file we have. */
ParseFileHeader( fd );
/* Start and manage appropriate background process. */
status = PlayManager( fd );
/* Stop the synthesizer. */
Emulate( SYNTH_RESET, 0, 0 );
/* Clean up and go home. */
DSReset();
fclose( fd );
DSExit( status );
}