developer-toolkit-v1.01/installed/SAMPLE/RECORD.C

UTF-8 rendering of a DOS-encoded (CP437 / CRLF) file. Byte-for-byte original: RECORD.C.

/***************************************************************************
	 Record.c
 	
	 Records Ad Lib .SMP files, using a the Wave Driver in a double
	 buffering scheme, and direct-to-disk.
****************************************************************************/


#include	<stdio.h>
#include	<stdlib.h>
#include	<fcntl.h>
#include	<malloc.h>
#include	<sys\types.h>
#include	<sys\stat.h>
#include	<io.h>
#include	<conio.h>
#include	<string.h>

#include	"Global.h"
#include	"control.h"
#include	"wave.h"

#ifdef	TURBO
#undef inportb
#undef outportb
#endif

/*
 * Experiment with those values if you wish.
 * When pushing the sample rate and size too high, the file I/O throughput
 * may become slower than the sampling rate. This would result in audible
 *	clicks. This is dependant on CPU speed and disk speed.
 * 
 */

#define BLOCK_QTY	 	3
#define BUF_SIZE    	8192
#define myFormat		WAVE_FORMAT_PCM16
#define mySamplingFreq	22075
#define myTransferMode   WAVE_TRANSF_DMA


/***** OOPS! ****/

#define DWORD      DWord

/* Header of a Gold Sample */
typedef
	struct {
		unsigned format;			/* data format:
									WAVE_FORMAT_ADPCM4:	Format is ADPCM 4 bits
									WAVE_FORMAT_PCM8: format is PCM 8 bits
									WAVE_FORMAT_PCM12: format is PCM 12 bits
									WAVE_FORMAT_PCM16: format is PCM 16 bits
									*/
		char	 stereo;			/* 0: mono, 1: stereo */
		char	 filler1;           /* 0 fro now */
		unsigned long	samplingFreq;	/* sampling frequency, in Hertz */
		unsigned long	nbBytes;	/* sample size, in bytes */
		unsigned long	startLoop;	/* offset of loop point, from start of sample */
		unsigned long	loopLength;	/* nombre of bytes of loop */
		unsigned loopCount;			/* # of time to do the loop, 0 if none */
		char filler2[ 8];			/* spares set to 0 for now*/
	} SampleHdr;

/* File format of AdLib SMP files: */
typedef struct {
		char	sig[ 12];			/* == "GOLD SAMPLE "   */
		char	majorVersion;		/* file version, major */
		char	minorVersion;		/* file version, minor */
		SampleHdr	sh;
	/*  char	data[].....	*/
	} FileHeader;


static int file;
/* Points to the recording device */
static HWaveIn   lphWaveIn;			

/* Block headers for the 	
   recording blocks				 */
static WaveHdr     waveHeaders[BLOCK_QTY];
						  
/* Playback data storage for the  					
   recording blocks				 */
static char        buffer[BLOCK_QTY][BUF_SIZE];		/* Playback data storage area    */

/* Count of bytes written to disk */
static long	byteCount = 0;

/* Queue of blocks, released, to kkep them in sequence	*/
LpWaveHdr	filledQueue;
LpWaveHdr	freeQueue;


/*************************************************************************
A couple of macros and functions to manage lists. The list pointers, used
for the queues, are placed in dwUser field, and we need a lot of typecasting
***************************************************************************/
#define GetNext(h)	((LpWaveHdr) (h->dwUser))
#define SetNext(h, n) ( ((LpWaveHdr)h)->dwUser = (DWORD) n) 
 
/*************************************************************************
TerminateBuffer is the Callback routine for sampling recording.  
It just marks the buffer as having some data ready to write.
The dwUser field is used to maintain a queue of blocks filled and
ready to be witten to disk (filledQueue) and a queue of freeBlocks
(freeQueue)

Since this routine is called in interrupt time, we minimize the activities
performed within. The next routine, RecordLoop(), runs in the foreground 
and handles most of the work.
*************************************************************************/

static int  far	TerminateBuffer (HWaveIn dev, LpWaveHdr block, DWORD dummy)
	{
	int	retVal;
	LpWaveHdr	tailBlock;
	(void) dev;
	(void) dummy;

	/* Add the block to the end of a queue of filled blocks    		*/
	/* the queue prevents blocks from going out of sequence			*/
	SetNext(block, NULL);
	
	if (!filledQueue) {
		filledQueue = block;
		}
	else {
		tailBlock = filledQueue;
		while(GetNext(tailBlock))	tailBlock = GetNext(tailBlock);
		SetNext(tailBlock, block);
		}
   return(1);
	}


/*************************************************************************
WriteFilledBlocks puts all the filled blocks to disk.
The blocks are transferred from the filledQueue to the freeQueue. 

*************************************************************************/

int WriteFilledBlocks()

	{
	int 		bytesRead;
	int			i;
	LpWaveHdr	block;

	/* 
	 * Write all the blocks received so far. We used a queue to
	 * Make sure that we maintained the sequence
	 */
printf("<");
	if (filledQueue) {
printf("!");
		block = filledQueue;	
		filledQueue = GetNext(block);
		write(file, (void *)block->lpData, block->dwBytesRecorded);
		byteCount += block->dwBytesRecorded;
		/* Add to the free queue */
		SetNext(block, freeQueue);
		freeQueue = block;
		}
printf(">");

	return(1);
	}

/*************************************************************************
SendFreeBlocks queues back all of the free blocks to the wave driver.
We do not keep a queue of the busy blocks, since the WaveDriver maintains
one internally.
*************************************************************************/
int SendFreeBlocks()
	{
	LpWaveHdr	block;

	if (!freeQueue) return(0);
printf("{");

	while (freeQueue) {
printf("!");
		block = freeQueue;	  
		freeQueue = GetNext(block);
		block->dwBufferLength = BUF_SIZE;
		SetNext(block, NULL);
		WaveInAddBuffer(lphWaveIn, block, sizeof(WaveHdr));
		}
printf("}");
	return(1);
	}

/*************************************************************************
RecordLoop() runs in the foreground. 
It scans the data blocks for free blocks.
If a free block has been found, it fills it with data and queues it back 
for recording.

*************************************************************************/

int  RecordLoop ()
	{
	WriteFilledBlocks();

	if (kbhit()) {
		getch();
		WaveInReset(lphWaveIn);
		return(0);
		}

	SendFreeBlocks();
	return(1);
	}

/*************************************************************************
WaitForEnd() waits until all of the blocks have been released
by the WaveDriver.

If a block is released, write the data it contains to the driver.


*************************************************************************/

int WaitForEnd()

	{
	int i;
	int count;
	LpWaveHdr	block;

	do {
		WriteFilledBlocks();
		
		/* Scan the list of free blocks for a count of those */
		for	(count = 0, block = freeQueue; 
			 block != NULL; 
			 block = GetNext(block), count++);
		if (kbhit()) {
			getch();
			break;
			}
		}
		while(count < BLOCK_QTY);

	return(1);
	}

/*************************************************************************/

static int	WriteHeader(int file, WaveFormat *format)

	{
   WORD  bytesRead;
	FileHeader	hdr;

	hdr.majorVersion = 1;
   hdr.minorVersion = 1;
	strcpy(hdr.sig, "GOLD SAMPLE");
   hdr.sh.stereo = format->nChannels -1;
   hdr.sh.format = format->wFormatTag;
   hdr.sh.samplingFreq = format->samplingFreq;
	hdr.sh.nbBytes = byteCount;
   switch (hdr.sh.format) {
       case    WAVE_FORMAT_ADPCM4:
	        format->nAvgBytesPerSec = format->samplingFreq / 2;
	        format->nBlockAlign = 1;
	        break;
       case    WAVE_FORMAT_PCM8:
	   		format->nAvgBytesPerSec = format->samplingFreq;
	   		format->nBlockAlign = 1;
	   		break;

       case    WAVE_FORMAT_PCM12:
	   		format->nAvgBytesPerSec = format->samplingFreq * 2;
	   		format->nBlockAlign = 2;
	   		break;

       case    WAVE_FORMAT_PCM16:
	   		format->nAvgBytesPerSec = format->samplingFreq * 2;
	   		format->nBlockAlign = 2;
	   		break;

       default: 
	   		format->nAvgBytesPerSec = format->samplingFreq;
	   		format->nBlockAlign = 1;
	   		break;
       }


	lseek(file, 0L, SEEK_SET);
	write(file, &hdr, sizeof(FileHeader));
	return(1);
	}

static void  PrepareBlocks()
	{
	int i;
	/* Initialize the queue pointers */
	freeQueue = NULL;
	filledQueue = NULL;

	/* Initialize the data blocks */
	for (i = 0; i < BLOCK_QTY; i++) {
   	memset ((void *) &waveHeaders[i], 0, sizeof (WaveHdr));
   	waveHeaders[i].lpData = (WaveDataP) buffer[i];
		waveHeaders[i].reserved = i;
		SetNext((&waveHeaders[i]), freeQueue);
		freeQueue = &waveHeaders[i];
	 	}
	}


/************************************************************************
                          Record a sample. 
**************************************************************************/

main (int argc, char *argv[])
	{
	WaveFormat  sFormat;	/* Format description for the file	*/
	int			result;

   int channel = 0;

   if (argc < 2) {
		puts ("Format:\n\trecord file\n");
		exit (1);
   	}

	printf("Recording to -%s-\n", argv[1]);
   file = open(argv[1], O_WRONLY | O_CREAT | O_BINARY, 
                         S_IREAD | S_IWRITE);

	if (file < 0) {
		puts ("Unable to open file");
		return(1);
    	}

	sFormat.wFormatTag = myFormat;
	sFormat.nChannels = 1;
	sFormat.samplingFreq = mySamplingFreq;
   sFormat.wTransMode =  myTransferMode;

	WriteHeader(file, &sFormat);

	if (InitControlDriver()) {
		printf("Can not link to control chip module\n");
		exit(1);
		}

	InitWaveDriver();
   result = WaveInOpen (&lphWaveIn, channel, &sFormat, TerminateBuffer, (long)file, 0L);
	if (result) {
		printf("Cannot open input device\n");
		exit(1);
		}

   PrepareBlocks();

	/* Queue first few blocks	*/
	SendFreeBlocks();

	/* 
	 * Ideally, WaveInStart should not be called until there are
	 * some buffers queued in.
	 */
	WaveInStart(lphWaveIn);
	
  	/* Queue blocks until user stops the recording. */
	byteCount = 0;
	printf("Recording, hit any key to stop\n");
   while (RecordLoop ());

	/* Wait until all blocks are released */
	WaitForEnd();

   WaveInClose (lphWaveIn);

	/* Rewrite header, to put byteCount in there	*/
	WriteHeader(file, &sFormat);

	close(file);
   CloseWaveDriver ();
	CloseControlDriver();
	return 0;
	}