/**************************************************************************\
** 
** File:		playlpt.c
** 
** Description:  Driver to test audio sample output.
**					Preliminary to MOD-like Sampler-Sequencer.
** 
** Created by:	John Howard	08-Jan-1993
** 
\**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <conio.h>
#include <time.h>
#include <math.h>
#include <graph.h>


/* general definitions */
#ifndef boolean
	#define	boolean	unsigned int
	#define	TRUE	1
	#define	FALSE	0
#endif
//#define _outp(__p,__v)	outp((__p),(__v))

/* specific definitions */
typedef unsigned int 		lpt_length_type;
/* changing this type, changes the treatment of sample byte values */
typedef unsigned char 		lpt_sample_byte_type;
#define MAX_BUF_LEN			64000
#define BYTE_SCOPE_HALF		128			/* zero amplitude offset for 
											unsigned data */
#define INTR_CYCLE			.055		/* normal timer interrupt interval */
#define Hz_To_InterruptFactor(__hz)	\
	(((__hz)*INTR_CYCLE))


/* variables with defaults */
static lpt_sample_byte_type lpt_sample_byte_array_1[ MAX_BUF_LEN + 1 ];
static lpt_sample_byte_type lpt_sample_byte_array_2[ MAX_BUF_LEN + 1 ];
static lpt_sample_byte_type *lpt_sample_buf[] =
{
	lpt_sample_byte_array_1,
	lpt_sample_byte_array_2,
	NULL
};
static lpt_length_type lpt_cur_byte_pos = 0;
static lpt_length_type lpt_max_bytes_1 = 0;
static lpt_length_type lpt_max_bytes_2 = 0;
static lpt_length_type lpt_max_byte_pos[ 3 ];
static int toggle = 0;
static boolean lpt_output_finished = FALSE;
static boolean lpt_is_raw = TRUE;
static lpt_eof = FALSE;
static unsigned int lpt_io_address = 0x0378;


static char * hlp_msg = "*** PLAYLPT.EXE *** 01-08-93 Audio Sample Player Unit Test by John C. Howard\n"
"Plays .VOC, .WAV and .RAW and .SAM files through the LPT printer port.\n"
"Syntax example...\n"
"PLAYLPT {sample filename} [rate(default 11000)] [port address(default 378h)]";


/* function prototypes */
void lpt_service_sample_output( void );
static boolean lpt_display_sample_bytes( char * fname, lpt_sample_byte_type * lpt_file_array );
static void lpt_exit( void );
static boolean lpt_read_file( FILE * fp, char * fname, lpt_sample_byte_type * lpt_file_array,
	lpt_length_type * lpt_max_pos );
static FILE * lpt_open_file( FILE * fp, char * fname );
static boolean lpt_is_raw_file( char * fname );


void main( int argc, char *argv[] )
{
	int lpt_sample_rate = 0;
	int lpt_timer_div_rate = 605;	/* comes out to 11000Hz rate */
	char * filename = NULL;
	FILE * fp = NULL;


	/* get command-line parameters */
	if( NULL != argv[1] )
		filename = argv[1];				/* filename pointer assignment */
	else
		lpt_exit();

	if( NULL != argv[2] )				/* sample rate in Hz */
		sscanf( argv[2], "%d", &lpt_sample_rate );
	if( NULL != argv[3] )				/* lpt1 IO address in hex */
		sscanf( argv[3], "%x", &lpt_io_address );

	/* see if file is raw according to extention */
	lpt_is_raw = lpt_is_raw_file( filename );


	/* display and confirm first part of sample file */
//	if( 1 )
	if( lpt_display_sample_bytes( filename, lpt_sample_buf[ toggle ] ) )
	{
		fprintf( stdout, "Press any key to stop. (may not be immediate)" );
		/* open and read first part of file */
		if( NULL == ( fp = lpt_open_file( fp, filename ) ) )
			lpt_exit();

		/* skip reading the header is required */
		if( ! lpt_is_raw )
			fseek( fp, 32, SEEK_CUR );
		lpt_read_file( fp, filename, lpt_sample_buf[ toggle ], &lpt_max_byte_pos[ toggle ] );


		/* calculate timer divide factor from sample rate */
		if( lpt_sample_rate )
			lpt_timer_div_rate = Hz_To_InterruptFactor( lpt_sample_rate );
		/* install new timer func that calls our output routine */
		initclk_1( NULL, lpt_service_sample_output, lpt_timer_div_rate );


		/* read in more of file in alternate buffer while interrupt proc
		 *	is playing current buffer until eof or a key is pressed
		 */
		while( ! lpt_eof && ! kbhit() )
		{
			lpt_read_file( fp, filename, lpt_sample_buf[ 1 - toggle ],
			&lpt_max_byte_pos[ 1 - toggle ] );

			/* sit in loop until interrupt routine has finished outputting */
			while( !lpt_output_finished );

			/* switch buffers and reset finished flag */
			_disable();
			toggle = 1 - toggle;
			_enable();

			lpt_output_finished = FALSE;

//			_disable();
			/* Put any kind of update display here.
			 * Use enable and disable before and after because
			 * using variables for display while they may
			 * be in the process of changing in the interrupt routine
			 * WILL crash the computer.
			 */
//			_enable();
		}

		if( kbhit() )
			getch();
		/* put the timer function back where we found it */
		restclk();
	}

	/* clear the screen then exit */
	_setvideomode( _DEFAULTMODE );
	exit( 0 );
}


/* this funtion is called by the timer interrupt.
* therefore DON'T call any C funtions that use DOS's int 21h's
* lower subfunctions such as... printf().
*/
void lpt_service_sample_output( void )
{
	_outp( lpt_io_address, (int)( *(lpt_sample_buf[ toggle ]+lpt_cur_byte_pos++) ) );
	
	/* if reached end of buffer then reset to beginning */
	if( ! ( lpt_max_byte_pos[ toggle ] - lpt_cur_byte_pos ) )
	{
		lpt_cur_byte_pos = 0;
		lpt_output_finished = TRUE;
	}
}


/* The following routine is used to display the first 640 bytes
 *	on the screen graphically to inspect the result of out reading.
 * It also confirms the file's readability.
 */
static boolean lpt_display_sample_bytes( char * fname, lpt_sample_byte_type * lpt_file_array )
{
	boolean ret = TRUE;
	int i;
	char c;
	FILE * fp;


	/* open, read and then close file */
	if( NULL != ( fp = lpt_open_file( fp, fname ) ) )
	{
		/* read off header */
		if( ! lpt_is_raw )
			fseek( fp, 32, SEEK_CUR );
		lpt_read_file( fp, fname, lpt_file_array, &lpt_max_byte_pos[ toggle ] );
		fclose( fp );
	}
	else
	{
		ret = FALSE;
	}

	/* init video, position and color */
	_setvideomode( _ERESCOLOR );		/* ega for compatability sake */
	_moveto( 0, 175 );					/* halfway down screen */
	_setcolor( 4 );						/* red */
	_lineto( 640, 175 );
	
	/* re-init position and color */
	_moveto( 0, 240 );
	_setcolor( 7 );

	for( i = 0; i < 640; i++ )
	{
		_lineto( i, (int)( 175 + ( (*(lpt_file_array+i)-BYTE_SCOPE_HALF) *
			.25 ) ));
	}

	fprintf( stdout, "Press ESC to cancel, or any other key to play the sample" );
	while( ! kbhit() );
	c = getch();
	_setvideomode( _DEFAULTMODE );


	/* pressing ESCAPE causes a return false, canceling the sample output
	 *	int main()
	 */
	if( c == '\x1b' )
		ret = FALSE;

	return( ret );
}


/* the way to exit if something goes wrong */
static void lpt_exit( void )
{
	fprintf( stdout, "\n%s\n", hlp_msg );
	exit(0);
}


static FILE * lpt_open_file( FILE * fp, char * fname )
{
	return( ( NULL != ( fp = fopen( fname, "rb" ) ) ? fp : NULL ) );
}


static boolean lpt_read_file( FILE * fp, char * fname, lpt_sample_byte_type * lpt_file_array,
	lpt_length_type * lpt_max_pos )
{
	boolean ret = FALSE;
	int i;
	lpt_length_type max_fill_len = 0;

	/* read audio sample portion of file */
	if( 0 < ( max_fill_len = fread( lpt_file_array, 1, MAX_BUF_LEN, fp ) ) )
	{
		if( lpt_is_raw )
		{
			/* adjust to be over zero for raw type samples */
			for( i = 0; i < max_fill_len; i++ )
				*(lpt_file_array+i) =
					( *(lpt_file_array+i) + BYTE_SCOPE_HALF );
		}
		*(lpt_max_pos) = max_fill_len;
		ret = TRUE;
	}
	else
	{
		lpt_eof = TRUE;
	}
}

static boolean lpt_is_raw_file( char * fname )
{
	boolean ret = FALSE;

	if( ! strcmp( "SAM", strupr( ( fname+(strlen(fname)-3) ) ) ) ||
		! strcmp( "RAW", strupr( ( fname+(strlen(fname)-3) ) ) ) )
		ret = TRUE;

	return( ret );
}
