/**************************************************************************/
/**************************************************************************/
/*
main.c: This file contains the entry point for a dos program to do csx
analysis. It contains the file io functions and the top level loops.
*/
/**************************************************************************/
/*
Copyright, 1995 Atari Corporation.
Written for Atari by Highlight Software Solutions.

Modified version for csxjag. March '96 by HMK/Atari.
*/
/**************************************************************************/

#include "hdef.h"	/* this file contains the data types and constants. */


/**************************************************************************/
#include <stdlib.h>


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

HWORD	intelFlag;
HWORD	outputOption;
/**************************************************************************/
/*

		**** N O T   U S E D   A N Y M O R E ****
Main() is now is 'csxjag.c' which calls do_csx(). See below for new function!

Function: main

definition:
	This function is the main entry point for the csx
	file analysis code. This function can support mono or stereo, as
	defined in the variable mode as HMONO OR HSTEREO.
	The variable compmode is set to HCSXB or HCSXC, depending on the
	mode of compression desired.
	The function, and hence the program, expect two filenames on the
	command line, the first has the PCM file to translate into the
	CSX file named as the second argument.

	Optionally, a third parameter can be used. If used, a string
	of valid characters is used. Valid characters are:

	1 -9, a, b, c, d, e, f : Hex values of threshold calculated as
				(2 ** #) - 1 (Default: 4)
	m, s : mono or stereo (Default: Mono)

	l: lossless - CSXB (Default: CSXC)



arguments:
	argnum: Number of arguments to follow, based on the command line.
	argarr: The array with the filenames of:
		1) The PCM file that is to be translated.
		2) The CSX file to which the PCM file is translated.
		3) A string of valid characters (1-9, a-f, m, s, or l).

-------------------------------------------------------
returns:
This function returns an integer that is zero on a successful translation.
It is < 0 if there was an error.
-------------------------------------------------------
example 1:
DOS> csx filename.pcm filename.csx
This program takes the PCM file filename.pcm and translated into the CSX
file filename.csx using the default setting at the time of this writing
CSXC: Lossy, with threshold of 15, and mono.

example 2:
DOS> csx filename.pcm filename.csx s8
This program takes the PCM file filename.pcm and translated into the CSX
file filename.csx using the settings CSXC (lossy) with threshold of
255 (2 ** 8 - 1), and stereo (s).

example 3:
DOS> csx filename.pcm filename.csx l
This program takes the PCM file filename.pcm and translated into the CSX
file filename.csx using the setting CSXB (lossless) due to the l in the
third parameter.

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

/*
 * HMK THIS CODE IS NOT USED ANYMORE, main() is not in csxjag.c.
 * We call do_csx() now.
 */

#if 0

int oldcsxmain(int numarg, char *argfile[])
{
HINT i;                         /** loop counter **/
HINT retval;			/** return value **/
HINT getnum;			/** Number of PCM Elements read from file **/
HINT packbits;			/** Number of bits needed to pack elements **/

LPHFILE fin, fout;		/** input and output files **/
HINT	ch1[MAXIBLOCK], ch2[MAXIBLOCK];
				/** data buffers for input data for**/
				/** channel 1 and channel 2 **/
HWORD	ps1,ps2; 		/** preemphisis state vars for each chan. **/
HWORD fss1[MAXFILTERS+3], fss2[MAXFILTERS+3];
				/** filter state variables for each channel. **/
HWORD 	outbuf[MAXIBLOCK + 100];
				/** output data buffer **/

/** CHANGE mode FROM HMONO TO HSTEREO as Desired for mono or stereo **/
HINT	mode = HMONO;

/** CHANGE mode FROM HCSXB TO HCSXC as Desired for algorithm choice **/
HINT	compmode = HCSXC;

/** SELECT Threshold that we want to use to limit APCM. **/
HWORD	thres = (32767/2048);	/* must be int between 1 and 32K */

/** Random number seed for dither **/
HWORD	randseed = 54;


/* Open the input and output files needed */
/*===================================================================*/

if (argfile[1] == NULL) {
	printf(" Illegal or No PCM Source File Named..");
	printf("\n Example>csx file.pcm file.csx<CR> ");
	return(0);
}
if (argfile[2] == NULL) {
	printf(" Illegal or No CSX Output File Named..");
	printf("\n Example>csx file.pcm file.csx<CR> ");
	return(0);
}
printf(" Fromfile:%s ",argfile[1]);
printf(" Tofile:%s ",argfile[2]);

if (argfile[3] != NULL) {
    for (i = 0; i < 7; i++) {
	switch(argfile[3][i]) {
		case 'L':
		case 'l':
			/* lossless (CSXB) mode desired */
			compmode = HCSXB;
			break;
		case 'M':
		case 'm':
			/* Mono desired */
			mode = HMONO;
			break;
		case 'S':
		case 's':
			/* Stereo desired */
			mode = HSTEREO;
			break;
		case 'A':
		case 'a':
 			thres = 1023;
			break;
		case 'B':
		case 'b':
			thres = 2047;
			break;
		case 'C':
		case 'c':
			thres = 4095;
			break;
		case 'D':
		case 'd':
			thres = 8195;
			break;
		case 'E':
		case 'e':
			thres = (1024 * 16) - 1;
			break;
		case 'F':
		case 'f':
			thres = (1024 * 32) - 1;
			break;
		default:
			if ((argfile[3][i] > '0') && (argfile[3][i] <= '9')) {
				thres = (1 << ((argfile[3][i] - '0'))) - 1;
			}
			break;
	}
	if (argfile[3][i] == 0) break;

    }
}


if ((fin = fopen(argfile[1],"rb")) == NULL) {
	printf(" RFO ERROR ");
	return(0);
}
if ((fout = fopen(argfile[2],"wb")) == NULL) {
	printf(" WFO ERROR ");
	return(0);
}

printf("\n Creating CSX Compressed Audio File..\n");

/*** Initialize State Variables ***/
/*===================================================================*/
for (i = 0; i <= MAXFILTERS; i++) {
	fss1[i] = 0;
	fss2[i] = 0;
}
ps1 = 0;
ps2 = 0;

/* Repeat this loop until there is not an entire maxiblock read */
/*===================================================================*/

getnum = MAXIBLOCK;
while (getnum == MAXIBLOCK) {

	/* Get a maxiblock of PCM for each channel used */
	getnum = HGetPCM(fin, mode, MAXIBLOCK, (LPHWORD) ch1, (LPHWORD) ch2);

	/* Process this block into a CSX compressed block for channel 1 */
	process_block(compmode,thres,&ps1,fss1, (LPHWORD)ch1,outbuf,&randseed);
	HPutPacket(fout, (HINT)(*outbuf & LENGTHMASK), (LPHWORD) outbuf,1);

	if (mode == HSTEREO) {
		/* Process block into a CSX block for channel 2 */
		process_block(compmode,thres, &ps2,fss2,(LPHWORD)ch2,outbuf,&randseed);
		HPutPacket(fout, (*outbuf & LENGTHMASK), outbuf,1);
	}
}

/** Close the files used, and exit the program **/
/*===================================================================*/

fclose(fin);
fclose(fout);
if (numarg >= 2) printf("\n CSX File Created with File Name:%s ",argfile[2]);
else printf("\n");

/** Print Recorded Status **/
printf("\n\n CSX Recorded:");
if (compmode == HCSXC) printf (" CSXC");
else printf(" CSXB");
if (mode == HMONO) printf(" MONO");
else printf(" STEREO");
if (compmode == HCSXC) printf(" w/Threshold: %d ",thres);
printf("\n");
/** End of Print Recorded Status **/

return(0);
} /* end of main */

#endif

/************************************************************************/
/**************************************************************************/
/*
Function: process_block

definition:
	This function processes a single channel of PCM data into a csx
	compressed audio block. A maxiblock of data is processed at a time.
arguments:
HINT process_block(HINT compmode, HWORD thres, HWORD ps, LPHWORD fss,
		LPHWORD databuf, LPHWORD outbuf,LPHWORD prand)

	compmode: is HCSXB or HCSXC for algorithm chosen.
	thres: is threshold used by the coder in CSXC only.
	ps: this word ptr contains the pre-emphisis state word where pre-emp.
		is used. It returns the next pre-emp state word on return.
	fss: this array of words contains the last m elements of the
		previous maxiblock. It should be initialized to zeros
		for the first call on a channel to process_block.
		m is  the number of the highest order filter used.
	databuf: this is the array of maxiblock elements of pcm data to
		process into csx.
	outbuf: this is the output array to put the csx compressed audio
		data into.
	prand: this is a pointer to a WORD containing the seed to use for
		the random number seed needed for the maxiblock dither.
-------------------------------------------------------
returns:
An HINT that is normally zero on a successful return.
-------------------------------------------------------
example:
	process_block(HCSXB, 255, &ps,fss,pcmbuf, csxbuf,&rand);
	This function takes the PCM data in pcmbuf of length maxiblock
	and processes it into csx data in the array csxbuf. It uses
	the state variables in &ps and fss[] to process this block.
	The lossless csxb algorithm is used. The threshold, 255, is ignored
	by CSXB. The random number seed rand, is also ignored.
*/
/**************************************************************************/

/************************************************************************/
HINT process_block(HINT compmode, HWORD thres, LPHWORD pspt, LPHWORD fss, LPHWORD databuf,LPHWORD outbuf,LPHWORD prand)
{
HINT mini_in;		     	/* element that starts current miniblk */
HINT totalwords;		/* words needed to pack a miniblock */
HINT packbits;			/* number of bits needed to pack winner */
LPHINT dbuf;			/* pointer to input buffer position */
LPHINT obuf;			/* pointer to current output blk position */
HINT length;			/* length of the current block counter */
HINT miniblockcount = 0;	/* counter of miniblock number in maxiblk */
HINT	filterout[MAXIBLOCK];	/* array to store filter output elements */
HINT	filternumber;		/* filter number counter */
HINT	filterbits;		/* bits returned from filter functions */
HINT	win_bits;		/* winning filter bit requirements */
HINT	win_filter;		/* winning filter number (zero, no filter) */
HINT	i;         		/* loop counter */
HINT	prearray[MAXIBLOCK];	/* used if preemphisis is needed */
HWORD	scalefactor;		/* calculated scalefactor for APCM */
HWORD	max_value;		/* maximum absolute value found in Maxiblk */
HINT	ps;			/* current ps value */
HLONG	errval  = 0;	   	/* current error value */
HINT	rand;                   /* random number for dither */

rand = *prand;			/* set random number from parameter */


/** Initialize variables **/

ps = *pspt;
mini_in = 0;
dbuf = (LPHINT) databuf;

/** Do the pre-emphisis in CSXC **/
scalefactor = 0;
if (compmode == HCSXC) {
	/* do pre-emphisis filter */
	for (i = 0; i < MAXIBLOCK; i++) {
		prearray[i] = dbuf[i];
	}
	pre_emph((LPHWORD)&ps, MAXIBLOCK, (LPHWORD)prearray, (LPHWORD)dbuf);
	max_value = maxvalue(MAXIBLOCK, dbuf);
	if (max_value > thres) {
		scalefactor = (HWORD)(((HLONG) thres) * ((HLONG) 65536L)
			/ ((HLONG) max_value));
	}
	else {
		scalefactor = -1;	/* don't scale */
	}
	*pspt = prearray[MAXIBLOCK-1];
	if (((HINT) scalefactor) > 0) {
		/* scale the values by this multiple */
		errval = 0L;

		/* If dither desired, then use daperr, */
		/* if no dither desired, then use aperr */

		/* aperr(MAXIBLOCK, scalefactor, &errval, dbuf, dbuf); */
		daperr(MAXIBLOCK, scalefactor, &errval, dbuf, dbuf, (LPHWORD)&rand);
	}
}

/* For each miniblock in a maxiblock */
while (mini_in < MAXIBLOCK) {
	/*** Determine Winning Filter for this miniblock ***/
	/***************************************************/
	win_filter = 0;
	filternumber = 1;
	win_bits = maxbits(MINIBLOCK, &dbuf[mini_in]);
	while (filternumber <= MAXFILTERS) {
		switch(filternumber) {
			case 1:
				filterbits = filter1(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
				break;
			case 2:
				filterbits = filter2(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
				break;
			case 3:
				filterbits = filter3(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
				break;
		}
		if (filterbits < win_bits) {
			win_filter = filternumber;
			win_bits = filterbits;
		}
		filternumber++;
	}
	if (outputOption)
		printf(" %d ",win_bits);
	/** Pack winning filter and place outputs in filterout array **/
	/*****************************************************************/
	switch(win_filter) {
		case 0:
			packbits = maxbits(MINIBLOCK, &dbuf[mini_in]);
			for (i = 0; i < MINIBLOCK; i++) {
				filterout[i] = (&dbuf[mini_in])[i];
			}
			break;
		case 1:
			packbits = filter1(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
			if (packbits <= 4) packbits = 4;
			else if (packbits <=6) packbits = 6;
			else if (packbits <= 8) packbits = 8;
			else if (packbits <= 12) packbits = 12;
			else packbits = 16;			
			break;
		case 2:
			packbits = filter2(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
			if (packbits <= 4) packbits = 4;
			else if (packbits <=6) packbits = 6;
			else if (packbits <= 8) packbits = 8;
			else if (packbits <= 12) packbits = 12;
			else packbits = 16;			
			break;
		case 3:
			packbits = filter3(fss, MINIBLOCK, &dbuf[mini_in], (LPHWORD)filterout);
			if (packbits <= 4) packbits = 4;
			else if (packbits <=6) packbits = 6;
			else if (packbits <= 8) packbits = 8;
			else if (packbits <= 12) packbits = 12;
			else packbits = 16;			
			break;
	}
	/** If first miniblock in maxiblock, create CSX header structure **/
	if (!mini_in) {
		obuf = (LPHINT)(outbuf + CreateCSXHeader(outbuf,win_filter));
		CSXHeaderStates(outbuf,fss,win_filter,scalefactor,ps,*prand);
	}
	CSXHeaderParameters(outbuf,miniblockcount,win_filter,packbits);

	/** Pack the winning filter into the output buffer **/
	/****************************************************/

	totalwords = pack(packbits,(LPHINT)obuf,(LPHINT)filterout, (HINT) MINIBLOCK);
	obuf += totalwords;
	if (mini_in >= (MAXIBLOCK - MINIBLOCK)) {
		/* put length into header */
		length = (HINT) (obuf - (LPHINT)outbuf);
		*outbuf |= *outbuf | (length & 0x01FF);
	}

	/** Update State Space for next block **/
	for (i = 1; i <= MAXFILTERS; i++) {
		fss[i-1] = dbuf[mini_in + MINIBLOCK - i];
	}

	/** Update block counters **/
	mini_in += MINIBLOCK;
	miniblockcount++;
} /* end of mini_in while */

*prand = rand;		/* return new random number */

return(0);
} /* end of process_block */


/**********************************************************************************
 *
 *	 H M K's CHANGES
 */


/*
 * This is the old main function, slightly modified. It's called from CSXJag().
 * CSXJag is reading in samples according to the Sample Table Array in the file's
 * header. It then calls this function to compress them and to let it write out
 * the compressed data to the output file.
 *
 * It returns the number of bytes which were written.
 */

	int
do_csx(fout, i_stereomode, i_compmode, i_threshold, i_intelFlag)
	FILE	*fout;			/* Output file handler */
	HWORD	i_stereomode;
	HWORD	i_compmode;
	HWORD	i_threshold;
	HWORD	i_intelFlag;
{
	long n;
	HINT i;                         /** loop counter **/
	HINT retval;			/** return value **/
	HINT getnum;			/** Number of PCM Elements read from file **/
	HINT packbits;			/** Number of bits needed to pack elements **/

	HINT ch1[MAXIBLOCK], ch2[MAXIBLOCK];
					/** data buffers for input data for**/
					/** channel 1 and channel 2 **/
	HWORD ps1,ps2; 		/** preemphisis state vars for each chan. **/
	HWORD fss1[MAXFILTERS+3], fss2[MAXFILTERS+3];
					/** filter state variables for each channel. **/
	HWORD outbuf[MAXIBLOCK + 100];
					/** output data buffer **/

	/** CHANGE mode FROM HMONO TO HSTEREO as Desired for mono or stereo **/
	HINT	mode = i_stereomode;

	/** CHANGE mode FROM HCSXB TO HCSXC as Desired for algorithm choice **/
	HINT	compmode = i_compmode;

	/** SELECT Threshold that we want to use to limit APCM. **/
	HWORD	thres = i_threshold;	/* must be int between 1 and 32K */

	/** Random number seed for dither **/
	HWORD	randseed = 54;


	intelFlag = i_intelFlag;
	
	/*** Initialize State Variables ***/
	/*===================================================================*/
	for (i = 0; i <= MAXFILTERS; i++) {
		fss1[i] = 0;
		fss2[i] = 0;
	}
	ps1 = 0;
	ps2 = 0;

	/* Repeat this loop until there is not an entire maxiblock read */
	/*===================================================================*/

	getnum = MAXIBLOCK;
	n = 0;
	while (getnum == MAXIBLOCK) {

		/* Get a maxiblock of PCM for each channel used */
		getnum = MGetPCM(mode, MAXIBLOCK, (LPHWORD) ch1, (LPHWORD) ch2);

		/* Process this block into a CSX compressed block for channel 1 */
		process_block(compmode,thres,&ps1,fss1, (LPHWORD)ch1,outbuf,&randseed);
		n += HPutPacket(fout, (HINT)(*outbuf & LENGTHMASK), (LPHWORD) outbuf,1);

		if (mode == HSTEREO) {
			/* Process block into a CSX block for channel 2 */
			process_block(compmode,thres, &ps2,fss2,(LPHWORD)ch2,outbuf,&randseed);
			n += HPutPacket(fout, (*outbuf & LENGTHMASK), outbuf,1);
		}
	}
	return n * 2;	/* return number of bytes written */
}

