/*
 *      NEW MIDI PARSER for ATARI JAGUAR
 *      Copyright 1993, 1994, 1995 ATARI Corp.
 *
 *      Version 1.1 - August 7, 1995
 *
 *	Written by Paul Foster & Hans-Martin Krober
 */

char	*name;
char	version[] = "version 1.1 (beta 0.5)";

/*
 * This is a program to parse a type 0 or type 1 MIDI file.
 *
 * The following things need to be done in this order:
 * 1) Check the file(?) header
 * 2) Check the track(?) header
 * 3) Separate events
 * 4) Handle events
 * 5) Detect end of file
 *
 * The meaning of step four now means print out the meaning of each event.
 *
 * Check the File (?) Header steps:
 * 1) Read the first four bytes, confirm that they are Mthd, error/exit if not.
 * 2) Read the next four bytes, they are 00 00 00 06, error/exit if not.
 * 3) Read next 2 bytes, this is the format
 * 4) Read the next 2 bytes, this is the number of tracks
 * 5) Read the next two bytes, this is the resolution in ppqn
 *
 * Check the Track(?) Header steps:
 * 1) Read the first four bytes, confirm that they are Mtrk, error/exit if not.
 * 2) Read the next four bytes, it is the length of the track.
 *
 * Separate events:
 * An event is one of three things:
 *      Meta-Event
 *      Note Event
 *      Sysex Event
 * Each event starts with a delta time from the last event. This is a variable
 * length item. Each byte is a 7 bit quantity, the high bit is set if there is
 * another chunk.
 *
 * These deltas will be turned into absolute times.
 *
 * The next part of an event is a byte with the high bit set. The are eight kinds:
 *
 * In all cases X is 4 bits for MIDI channel
 * 8X   Note off; followed by optional velocity
 * 9X   Note on; followed by velocity, v=0 => off
 * AX   Aftertouch
 * BX   Controller; 7 bits for controller number, 7 bits for value
 * CX   Patch change; 7 bits for patch number
 * DX   Aftertouch
 * EX   Pitch bend; confusing number of bytes
 * FX   Meta event; FF / System Exclusive in real time version (!!!)
 *
 * Note: In the case of Note on events if the next event byte has its high bit
 * clear it is another note on event. Note also that note on of velocity zero
 * is really a note off.
 *
 *
 */

/* ---------------------------------------------------------------------------
 *      Include Files
 */

/**************************************************************************/
/* Redefine Atari GEMDOS function names to MSDOS equivalents for file I/O */
/**************************************************************************/

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#if __DOS__

#include <dos.h>
#include <fcntl.h>
#include <io.h>

#define Fopen(a,b) open(a,(b)|O_BINARY)         /* OR 'O_BINARY' into the mode to make sure no text translation is done. */
#define Fcreate(a,b) creat(a,b)
#define Fwrite(a,b,c) write(a,c,(int)b)
#define Fread(a,b,c) read(a,c,(int)b)
#define Fclose(a) close(a)
#define Fseek(a,b,c) lseek(b,a,c)

#else  /* Atari TOS/GEMDOS */
#include <osbind.h>
#endif


		

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

/* ---------------------------------------------------------------------------
 *      Defines
 */

#ifndef TRUE
#define TRUE		(1)
#define FALSE 		(0)
#endif

#define MAX_CHAN 	(32)

#define MAX_VOLSCALE 	(16384)

#define MAX_TRACKS	(256)

/* ---------------------------------------------------------------------------
 *      Global Variables
 */

extern int      optind;         // output by getopt() function 
extern char     *optarg;

extern unsigned short	freq[];

/*
 *	Parser Options
 */
short           msg;            // Flag - show messages?
short           debugmsg;	// Debug messages
short           koption;	// Kill event option
short           poption;	// Pitch bend effects patch option
short		nostealing;	// Note stealing option
short           novoldown;
short           volscale;

/*
 *	File Handling
 */
FILE            *outfile;	// Output file
FILE            *infile;	// Input file
unsigned char   inbuf[128];	// buffer

/*
 *	Multiple Track Handling
 */
char		*track[MAX_TRACKS];	// pointers to tracks 
long		tsize[MAX_TRACKS];	// track sizes 
long		tindex[MAX_TRACKS];	// file index into tracks

#define EVBUFSIZ	16

typedef struct _mevent MEVENT;

struct _mevent {
	long	time;			// time
	char	data[EVBUFSIZ];		// midi event data
	MEVENT	*prev;			// previous event
	MEVENT	*next;			// next event
} mheads[MAX_TRACKS+1];

long	mspqn;
short	tpqn;
short	ntrk;		 		// track handling
short	c12, c13, c14;

/*
 *	Parsing
 */

long            count;
short           n_ch;   		// # of channels to use (default is 12) 
short           ch_off; 		// channel offset (default is 0)  
short           voice_flag[MAX_CHAN];	// voice allocation table
long            voice_time[MAX_CHAN];	// voice (note on) time table
long		pitch_amount;		// pitch bend
short           last_voice;		// last voice used
float		factor;			// time factor

short           vols[MAX_CHAN]; 	// current volume (velocity) of 
					// each voice 
long		timepos;


int             note_on_err;		// # note on  failures
int             note_off_err;		// # note off failures

char		parsecnf[_MAX_PATH];	// Parse cnf file name
char		notescnf[_MAX_PATH];	// Notes cnf file name

/*      pitchpatch
 *      this data structure stores information
 *      for changing a pitch range to a patch
 */

#define NRANGE (50)             /* nr of possible pitch changes / channel */

struct _pitchpatch {
	int     translate;        /* translation flag (true/false) */
	int     n;                /* number of ranges */
	int     start[NRANGE];    /* pitch range start */
	int     end[NRANGE];      /* pitch range end */
	int     patch[NRANGE];    /* patch to translate to */
	int     offset[NRANGE];   /* pitch offset */
	int     priority[NRANGE]; /* pathc's priority */
} pitchpatch[MAX_CHAN];

#include "nproto.h"				// Prototypes 

/* ---------------------------------------------------------------------------
 *      Procedures
 */

/* 
 * Read a 16-bit big-endian 'short' or 'int' from specified file. 
 */
	int 
readshort(fhand, shortval)
	FILE	*fhand; 
	short	*shortval;
{
	short s;
	int errcode;

	errcode = fread(&s, 2, 1, fhand);
#ifdef __DOS__
	s = ((s << 8) & 0xff00) | ((s >> 8) & 0x00ff);
#endif
	*shortval = s;

	return errcode;
}

/* 
 * Read a 32-bit big-endian 'short' or 'int' from specified file. 
 */
	int 
readlong(fhand, lval)
	FILE *fhand;
	long *lval;
{
	int errcode;
	unsigned long l;
#ifdef __DOS__
	unsigned long a, b, c, d;
#endif

	errcode = fread( &l, 4, 1, fhand);
#ifdef __DOS__
	a = (l >> 24) & 0x000000ffL;
	b = (l >> 8) & 0x0000ff00L;
	c = (l << 8) & 0x00ff0000L;
	d = (l << 24) & 0xff000000L;
	*lval = ( a | b | c | d );
#else
	*lval = l;
#endif
	return errcode;
}

/*
 *	MIDI EVENTS
 */

/*
 *      Check if we want to translate a pitch range into a patch
 */
	int
do_pitchpatch(channel, pitch, priority)
	short *channel;
	short *pitch;
	short *priority;
{
	int i, old;
	int found = FALSE;

	if (pitchpatch[*channel].translate == TRUE)
	{
		for (i = 0; i < pitchpatch[*channel].n; i++)
		{
			if ((pitchpatch[*channel].start[i] <= *pitch) && (*pitch <= pitchpatch[*channel].end[i]))
			{
				old = *channel;
				*channel = (short) pitchpatch[old].patch[i];
				*pitch += (short) pitchpatch[old].offset[i];
				*priority = (short) pitchpatch[old].priority[i];
				if (msg)        
					printf("\t---> (Translate channel %d to patch %d, new pitch is %d)\n", old, (int) *channel, (int) *pitch);
				found = TRUE;
				break;
			}
		}
	}
	if (!found) { 
		if (!koption) {
			printf("\nWARNING: Channel %d not specified in PARSE.CNF. EVENT DROPPED.\n", *channel);
			return -1;
		} else
			printf("\nWARNING: Channel %d not specified in PARSE.CNF\n", *channel);		
	}
	return 0;
}



/*	NOTE ON EVENT
 *      This procedure keeps track of what voice to use
 */
	void
note_on(meventp, channel, pitch, vol, ctime)
	MEVENT *meventp;
	short channel;
	unsigned short pitch, vol;
	long ctime;
{
	unsigned long out;
	long timestamp;
	short priority;

	if (do_pitchpatch((short*)&channel, (short*)&pitch, &priority) == -1)
		return;


			
	/*
	 * Create Synth Event
	 */
	pitch = freq[pitch];

	out =  ((unsigned long) vol << 24);
	out |= ((unsigned long) pitch << 7);
	out |= ((unsigned long) channel);
		
	out |= 0x80000000L;

	count += 2;

	vols[channel] = vol; 		/* save volume */

	timestamp = (((long) ctime & 0x1ffffffL) << 4L) | ((long) priority << 29);
	/*
	 *	Write time stamp to file
	 */
	fprintf(outfile,"\tdc.l\t$%08lx\n", timestamp);
	/*
	 *	Write event to file
	 */
	fprintf(outfile,"\tdc.l\t$%08lx", out );

	if (debugmsg)
		fprintf(outfile, "\t; Note ON,  patch=%d, pri.=%d, freq=%d, vol=%d\n",
			channel, priority, pitch, vol);
	else
		fprintf(outfile, "\n");

}
/*	NOTE OFF EVENT
 *      This procedure checks to see what voice was used
 */
	void 
note_off(channel, pitch, ctime)
	short channel, pitch;
	long ctime;
{
	unsigned long out;
	short priority;
	long timestamp;

	if (do_pitchpatch(&channel, &pitch, &priority) == -1)
		return;

	/*
	 * Create the event and write it to the file
	 */	
	pitch = freq[pitch];
	out = ((unsigned long) pitch << 7);
	out |= ((unsigned long) channel);

	count += 2;
	
	timestamp = (((long) ctime & 0x1ffffffL) << 4L);
	fprintf(outfile,"\tdc.l\t$%08lx\n", (long)timestamp);
	
	fprintf(outfile,"\tdc.l\t$%08lx", out);
	if (debugmsg)
			fprintf(outfile, "\t; Note OFF, ch=%d, freq=%d\n", channel, pitch);
	else
			fprintf(outfile, "\n");
}
/*
 *	PAN Controller
 */

	void
pan_value(c_channel, value, ctime)
	short c_channel, value;
	long ctime;
{
	short channel;
	register short j;
	unsigned long out;
		
	/*
	 * 	Do it for the patches
	 */
	for (j = 0; j < pitchpatch[c_channel].n; j++) {
		ctime = (((long) ctime & 0x1ffffffL) << 4L);
		fprintf(outfile,"\tdc.l\t$%08lx\n", (long)ctime);
		count += 1;

		channel = pitchpatch[c_channel].patch[j];
		out  = 0x40000000L;		// event code
		out |= (1L << 21L);		// PAN == 10
		out |= ((long) value << 7L);
		out |= ((long) channel);

		count += 1;

		fprintf(outfile,"\tdc.l\t$%08lx", out);
		if (debugmsg)
			fprintf(outfile,"\t; PAN patch=%d, value=%d\n",
				channel, value);
		else
			fprintf(outfile, "\n");
		if (poption) {
			out  |= 0x10000000L;		// event code
			fprintf(outfile,"\tdc.l\t$%08lx\n", (long)ctime);
			count += 1;
			fprintf(outfile,"\tdc.l\t$%08lx", out);
			count += 1;
			if (debugmsg)
				fprintf(outfile,"\t; PAN master patch=%d, value=%d\n",
					channel, value);
			else
				fprintf(outfile, "\n");
		}
	}
}

	void
vol_value(c_channel, value, ctime)
	short c_channel, value;
	long ctime;
{
	short channel;
	register short j;
	unsigned long out;

	/*
	 * 	Do it for the patches
	 */
	for (j = 0; j < pitchpatch[c_channel].n; j++) {
		ctime = (((long) ctime & 0x1ffffffL) << 4L);
		fprintf(outfile,"\tdc.l\t$%08lx\n", (long)ctime);
		count += 1;

		channel = pitchpatch[c_channel].patch[j];
		out  = 0x40000000L;		// event code
		out |= (2L << 21L);		// VOLUME == 2
		out |= ((long) value << 7L);
		out |= ((long) channel);

		count += 1;

		fprintf(outfile,"\tdc.l\t$%08lx", out);
		if (debugmsg)
			fprintf(outfile,"\t; VOLUME patch=%d, value=%d\n",
				channel, value);
		else
			fprintf(outfile, "\n");
		if (poption) {
			out  |= 0x10000000L;		// event code
			fprintf(outfile,"\tdc.l\t$%08lx\n", (long)ctime);
			count += 1;
			fprintf(outfile,"\tdc.l\t$%08lx", out);
			count += 1;
			if (debugmsg)
				fprintf(outfile,"\t; VOLUME master patch=%d, value=%d\n",
					channel, value);
			else
				fprintf(outfile, "\n");
		}
	}

}

	void
pitch_bend(c_channel, value, ctime)
	short c_channel, value;
	long ctime;
{
	short channel;
	short j;
	unsigned long out;

	/*
	 * 	Do it for the patches
	 */
	for (j = 0; j < pitchpatch[c_channel].n; j++) {
		ctime = (((long) ctime & 0x1ffffffL) << 4L);
		fprintf(outfile,"\tdc.l\t$%08lx\n", (long)ctime);
		count += 1;

		channel = pitchpatch[c_channel].patch[j];
		out  = 0x40000000L;		// event code
		out |= (0L << 21L);		// PITCH BEND = 0
		out |= ((long) value << 7L);
		out |= ((long) channel);

		count += 1;

		fprintf(outfile,"\tdc.l\t$%08lx", out);
		if (debugmsg)
			fprintf(outfile,"\t; PITCH BEND patch=%d, value=%d\n",
				channel, value);
		else
			fprintf(outfile, "\n");
		/*
		 * pitch bend is only done on current voices - DOES NOT
		 * update the patch itself!!!
		 */
	}

}
	void	
patch_change(short channel, short patch)
{

}

/*
 *      Get time delta
 */
	void
get_time(handle, eventp2)
	int 	handle;
	MEVENT 	*eventp2;
{
	char litbuf;
	long temp;

	tread(handle, 1L, &litbuf);
	temp = litbuf & 0x7f;

	while ((litbuf & 0x80) != 0) {
		tread(handle, 1L, &litbuf);
		temp = (temp << 7) + (litbuf & 0x7f);
	}
	eventp2->time = temp + eventp2->prev->time;
}

/*
 *	Get time code of next event
 */
	int
get_event(handle, eventp2)
	int	handle;
	register MEVENT	*eventp2;
{
	unsigned char	mbyte1[3], 	// input vars reading from MIDI tracks
			mbyte2[3],
			mbyte3[3],
			inbuf[128];			
	register unsigned char	cmd;
	int		eot = FALSE;
	register int	i, l;

	/*
	 *	Read event 
	 */
	tread(handle, 1L, mbyte1);
	/* 
	 * Event type is contained in high nybble of byte, 
	 * channel number in low nybble 
	 */
	eventp2->data[0] = mbyte1[0];
	cmd = mbyte1[0] & 0xf0;
	switch (cmd) {
	case 0x80:			// Note Off
	case 0x90:			// Note On
	case 0xa0:			// Aftertouch Poly
	case 0xb0:			// Controller Change
	case 0xe0:			// Pitch Bend
		tread(handle, 2L, mbyte2);
		eventp2->data[1] = mbyte2[0];
		eventp2->data[2] = mbyte2[1];
		eventp2->data[3] = 0;
		break;

	case 0xc0:			// Program Change
	case 0xd0:			// Aftertouch Channel
		tread(handle, 1L, mbyte2);
		eventp2->data[1] = mbyte2[0];
		eventp2->data[2] = 0;
		eventp2->data[3] = 0;
		break;
	case 0xf0:			// Meta		
		tread(handle, 1L, mbyte2);
		eventp2->data[1] = mbyte2[0];
		switch (mbyte2[0]) {
		case 0x2f:		// End of Track
			eot = TRUE;
			break;
		case 0x51:		// Set Tempo
		case 0x58:		// Time Signature
		case 0x54:		// SMPTE Offset
			tread(handle, 1L, mbyte3);
			tread(handle, (long)mbyte3[0], inbuf);
			eventp2->data[2] = mbyte3[0];
			l = (mbyte3[0] > EVBUFSIZ)  ? EVBUFSIZ : mbyte3[0];
			for (i = 0; i < l; i++) {
				eventp2->data[3+i] = inbuf[i];
			}
			break;
		case 0x20:		// Channel Prefix
			tread(handle, 2L, mbyte3);
			eventp2->data[2] = mbyte3[0];
			eventp2->data[3] = mbyte3[1];
			break;
		default:
			if ((mbyte2[0] > 0) && (mbyte2[0] < 16)) {
				tread(handle, 1L, mbyte3);
				tread(handle, (long)mbyte3[0], inbuf);
				eventp2->data[2] = 1; // Text Event	
				break;
			} else {
				printf("Unknown event.\n");
			}
			break;
		}
		break;
	default:
		printf("Unknown event!\n");
		break;

	}
	return eot;
}


	void
sort_lists(ntrk)
	register int ntrk;
{
	register int i;
	register MEVENT *masterp;
	int done;
	register int minindex;
	long mintime;

	masterp    = &mheads[ntrk]; // mheads of ntrk is the master track head

	done = FALSE;
	do {
		minindex = -1;
		mintime = 0x7fffffffL;
		for  (i = 0; i < ntrk; i++) {
			if ((mheads[i].next != NULL) && 
			  (mheads[i].next->time <= mintime)) {
				mintime = mheads[i].next->time;
				minindex = i;
			}						
		}
		if (minindex == -1) {
			done = TRUE;
		} else {
			masterp->next = mheads[minindex].next;
			masterp = masterp->next;		
			mheads[minindex].next = mheads[minindex].next->next;
		}
	} while (!done);
	masterp->next = NULL;
}
	void
parse_list(void)
{
	register MEVENT *meventp, *p;
	long target, marker[128];	// loop targets & marker
	unsigned short event;
	register short i;
	char num1;			// input vars reading from MIDI tracks
	short channel, vol;
	short cnum, value, backup;
	long time;
	long lvalue;

	meventp = mheads[ntrk].next;
	
	while (meventp) { 
		num1    = meventp->data[0];
		event   = num1 & 0xf0;
		channel = num1 & 0x0f;
		time    = meventp->time;
		meventp->time = (long)((float)meventp->time * factor);

		if ((c13 > c14) && (event != 0xB0)) {	// check loops
			fprintf(stdout, 
				"WARNING: \'Loop count\' (Ctrl 13) should immediatly be followed by a \'Loop to\'.\n");
		}
		p = meventp->next;

		/*
		 * Parse single MIDI event
		 */
		switch (event)
		{
/* Note off */
		case 0x80:
			if (msg)
				printf("Note %d off channel %d\n", meventp->data[1], channel);

			note_off(channel, meventp->data[1], meventp->time);
			break;
/* Note on */                           
		case 0x90:
			if (msg)
				printf("Note %d ", meventp->data[1]);
			vol = meventp->data[2];
			if (vol != 0) {
				if (msg) {
					printf("on, channel %d velocity %d\n",
					 channel, vol);
				}
				note_on(meventp, channel, meventp->data[1], vol, 
					meventp->time);
			} else {       
				if (msg)
					printf("off channel %d\n", channel);

				note_off(channel, meventp->data[1],
					meventp->time);
			}
			break;

/* Polyphonic aftertouch */                             
		case 0xA0:
			if (msg)
				printf("Ignoring Polyphonic Aftertouch\n");
			break;

/* Controller Change */
		case 0xB0:

			cnum  = meventp->data[1];
			value = meventp->data[2];

			if (msg) {
				printf("MIDI Channel %d ", channel);
				printf("Change on controller %d value %d\n", 
				 cnum, value);
			}

// Controller 7 is volume
			if (cnum == 7) {
			  if (koption || (pitchpatch[channel].translate 
			  == TRUE)) {
				vol_value(channel, value, meventp->time);
			  } else {
				printf(
"WARNING: Channel for volume not specified in PARSE.CNF. Event dropped\n");
			  }
			}
// Controller 10 is pan
			if (cnum == 10) {
			  if (koption || (pitchpatch[channel].translate 
			  == TRUE)) {
				pan_value(channel, value, meventp->time);

			  } else {
				printf(
"WARNING: Channel for pant not specified in PARSE.CNF. Event dropped\n");
			  }
			}

// Controller 12 marks loop targets
			if (cnum == 12) {
 			  if (koption || (pitchpatch[channel].translate 
			  == TRUE)) {
				c12++;
				marker[value] = count;
				fprintf(outfile, "; Loop target #%d\n", value);
				fprintf(outfile, "\tdc.l\t$%l0x\n",
				 (long)meventp->time);
				fprintf(outfile, "\tdc.l\t$40018000\n");
				count += 2;
			  } else {
				printf(
"WARNING: Channel for loop target not specified in PARSE.CNF. Event dropped\n");
			  }
			}

// Controller 13 selects loop targets

			if (cnum == 13) {
 			  if (koption || (pitchpatch[channel].translate 
			  == TRUE)) {
				c13++;
				if (c13 > c12) {
					printf(
"WARNING: Loop target (controller #12) %d is missing.\n", value);
					continue;       
				}
				target = marker[value];
				fprintf(outfile,
				 "; Loop to target #%d ", value);
			  } else  {
				printf(
"WARNING: Channel for loop target not specified in PARSE.CNF. Event dropped\n");
			  }
			}

// Controller 14 gives loop count

			if (cnum == 14) {
 			  if (koption || (pitchpatch[channel].translate 
			  == TRUE)) {
				c14++;
				if (c14 > c12) {
					printf("WARNING: Loop target (controller #12) %d is missing.\n", value);
					continue;
				}
				if (c14 > c13) {
					printf("WARNING: Loop specifier (controller #13) is missing.\n");
					continue;
				}
				if (value == 127) {
					fprintf(outfile,"infinite times\n");
				} else {
					fprintf(outfile,"%d times\n", value);
				}
				count += 2;
				fprintf(outfile, "\tdc.l\t%ld\n",
				 (long)meventp->time);

				if (value == 127) /* infinite loop */
					fprintf(outfile, 
					 "\tdc.l\t$30000000|((((-%ld/2)&$3ffff)<<10)", 
					 count - target);
				else {
					fprintf(outfile, 
					 "\tdc.l\t$20000000|((((-%ld/2)&$3ffff)<<10)",
					 count - target);

					backup = value & 0x1f;
					lvalue = (backup << 5)|(value & 0x1f);
					fprintf(outfile, "%d\n", 
					 lvalue & 0x3ffL);
				}
			  } else
				printf(
"WARNING: Channel for loop count not specified in PARSE.CNF. Event dropped\n");
			}
			break;
/* Patch Change */
		case 0xC0:
			if (msg) {
				printf("MIDI Channel %d ",channel);
				printf("Patch change to program %d \n", num1);
			}
			patch_change(channel, meventp->data[1]);
			break;
/* Aftertouch */
		case 0xD0:
			if (msg)
				printf("Ignoring Aftertouch\n");
			break;

/* Pitch Bend */
		case 0xE0:
			cnum  = meventp->data[1];
			value = meventp->data[2];

			if (msg) {
				printf("MIDI Channel %d ", channel);
				printf("Pitch bend; value %d\n", (short)cnum + (short)(value << 7));
			}
			if (koption || (pitchpatch[channel].translate == TRUE)) {
				pitch_bend(channel, (short)cnum + (short)(value << 7), meventp->time);
			} else {
					printf("WARNING: Channel for pitch bend not specified in PARSE.CNF. Event dropped\n");
			}
			break;

/* End of track */                              
		case 0xF0:
			num1 = meventp->data[1];
			if (num1 == 0x2f)
			{
				if (msg)
					printf("End of track\n");

				count += 1;
//				fprintf(outfile, "\tdc.l\t$7fffffff\n");
				break;
			}
			if ((num1 > 0) && (num1 < 16)) {
				if (msg)
					printf("General text event: ");
				if (msg) {
				    for (i = 0 ; i < meventp->data[1] ; i++)
					printf("%c", meventp->data[2+i]);

					printf("\n");
				}
				break;
			}
			if (num1 == 0x51)  {
				if (msg)
					printf("Set Tempo, ");
				mspqn = 0;
				for (i = 0 ; i < meventp->data[2] ; ++i)
					mspqn = (mspqn << 8) +
						(meventp->data[3+i] & 0xFF);
				if (msg)
					printf("Microseconds per quarter-note is %ld\n", mspqn);
				factor = ((float)mspqn / (float)tpqn) / 1000.;
				break;
			}
			if (num1 == 0x58) {
				if (msg)
					printf("Set Time Signature\n");
				break;
			}
			if (num1 == 0x54) {
				if (msg)
					printf("Set Key Signature\n");
				break;
			}
			if (msg) {
				printf("Meta-Event ");
				printf("type %x ", meventp->data[1]);
			}
			if (msg) {
			    for (i = 0 ; i < meventp->data[1] ; i++)
				printf("%x ", meventp->data[2+i] & 0xFF);
			    printf("\n");
			}
			break;
		default:
			if (msg)
				printf("\n");
			break;

		} // switch
		meventp = meventp->next;	
	} 
}
/*
 * 	Track Read
 */
	long
tread(handle, size, ptr)
	int	handle;
	long	size;
	char	*ptr;
{
	memcpy(ptr, &track[handle][tindex[handle]], size);
	tindex[handle] += size;

	return size;
}

/*
 * 	Track Seek
 */
	long
tseek(pos, handle, mode)
	long	pos;
	int	handle;
	int	mode;
{
	switch (mode) {
	case 0:
		tindex[handle] = pos;
		break;
	case 1:
		tindex[handle] += pos;
		break;
	case 2:
		tindex[handle] = tsize[handle] - pos;
		break;
	}
	return tindex[handle];
}

/*
 * read the notes config file
 */
	void
read_notes()
{
	FILE *fp;
	static char fbuf[512];
	int note_n;
	char note_str[64];
	int n;
	short a, b, c;

	if ((fp = fopen(notescnf, "r")) == NULL)
		return;

	while (fgets(fbuf, 512, fp) != NULL)
	{
		if (sscanf(fbuf, "#define %s %d", note_str, &note_n) == 2)
		{
			switch (note_str[5])
			{
				case 'C': a = 0; break;
				case 'D': a = 2; break;
				case 'E': a = 4; break;
				case 'F': a = 5; break;
				case 'G': a = 7; break;
				case 'A': a = 9; break;
				case 'B': a = 11; break;
				default:
					printf("Error while reading notes.cnf.  " );
					printf("Note %s must contain letter [C|D|E|F|G|A|B]\n", note_str);
					break;
			}

			b = (note_str[6] == '_') ? 0 : note_str[6] - '0' + 1;
			c = (note_str[strlen(note_str) - 1] == 'S') ? 1 : 0;
			n = 12 + b * 12 + a + c;

			if (n > 137)
			{
				printf("Wrong note: %s\n", note_str);
			}
			else
			{
				freq[12 + b*12 + a+c] = (unsigned short) note_n;
			}
		}
	}
	fclose(fp);

}


/*
 * Read the config file with pitch to patch translations
 */
	void
read_cnf()
{
	FILE *fp;
	int channel, start, end, patch, offset;
	static char fbuf[512];
	int n;
	int nch;
	int priority;
	char flag;
	

/* If PARSE.CNF file not in default directory, search the PATH for it... */

	fp = fopen(parsecnf, "r");
	if( fp == NULL )
	{
#if __DOS__
	char parse_cnf_path[_MAX_PATH];

		printf( "Searching PATH for PARSE.CNF...\n" );
		_searchenv( "parse.cnf", "PATH", parse_cnf_path );
		if( parse_cnf_path[0] != '\0' )
		  fp = fopen( parse_cnf_path, "r" );
#else
	char parse_cnf_path[200];
	
		fp = fopene( "parse.cnf", "r", parse_cnf_path );
#endif
		if( fp )
		  printf( "Using configuration file %s\n", parse_cnf_path );
	}

/* If we still cannot find the PARSE.CNF file, then bail out */

	if( fp == NULL )
	{
		printf( "PARSE.CNF file not found.\n" );
		return;
	}

	while (fgets(fbuf, 512, fp) != NULL)
	{
	int numscanned;

/* If not a comment or blank line... */

		if( fbuf[0] != '*' && fbuf[0] != ';' && fbuf[0] != '#' &&
		 fbuf[0] != 0x0a && fbuf[0] != 0x0d )
		{
		char *bufptr;
		int once;

			once = 0;
			bufptr = fbuf;

/* Make sure line is all either numbers or whitespace */
			flag = 'p';
			while( *bufptr )
			{

				switch( *bufptr )
				{
				case '-':       /* to     "0  0 127 5 0" */
					if (once)	/* do it only once otherwise we delete the minus sign of the pitch offset */
						break;
					once = 1;
				case 'n':       /* Change "n = 8" to just "    8" */
					flag = 'n';
				case 'p':
				case '=':
				case ':':       /* Change "0:<tab>0 - 127<tab>5 0" */
				case 0x09:
					*bufptr = ' ';
					break;
				}
				bufptr++;
			}
/* Now scan the numbers out of the line */

			numscanned = sscanf(fbuf, "%d %d %d %d %d %d", &channel, &start, &end, &patch, &offset, &priority);
	
			if ( numscanned == 6 )
			{
				if (channel > MAX_CHAN)
				{
					printf("Error in translation file (parse.cnf): channel number is greater than %d\n", MAX_CHAN);
				}
				else
				{
					if (msg)
						printf("Will do translation: channel %d: %d - %d patch %d offset %d\n", channel, start, end, patch, offset);
	
					pitchpatch[channel].translate = TRUE;
					n = pitchpatch[channel].n++;
					pitchpatch[channel].start[n] = start;
					pitchpatch[channel].end[n] = end;
					pitchpatch[channel].patch[n] = patch;
					pitchpatch[channel].offset[n] = offset;
					pitchpatch[channel].priority[n] = priority;
				}
			}
			else if (numscanned == 1)
			{
				if (flag == 'n') {
					nch = channel;          /* obtained earlier by sscanf() */

					printf( "nch = %d\n", nch );
					n_ch = (short) nch;
					if (n_ch > MAX_CHAN)
					{
						printf("Maximum number of channels is %d\n", MAX_CHAN);
						n_ch = MAX_CHAN;
					}
					printf("Set number of channels to %d\n", n_ch);
				} else {
					pitch_amount = (long) channel;
				}
					
			}
		} /* if( fbuf[0] ... ) */
	} /* while */
	fclose(fp);
}

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

void usage(void)
{
	printf( "Usage: %s [-q] [-d] [-k] [-v] [-x chnl_off] [-n max_chnl] [-z n] [-p] [-c <parse.cnf>] [-f <notes.cnf> [-o<outputname>] [inputname]\n", name);
}

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

/*
 * THE MAIN PROGRAM
 */
	void
main(argc, argv)
	int	argc;
	char	**argv;
{
	char *iname, *oname;		// input, output filename 
	register short i;
	short n1_ch;
	char c;
	short temp;
	register short tcount;
	register MEVENT *eventp;
	short eot;
	long ltemp;

printf("xxx=%d\n",sizeof(int));
	/*
	 * Get the program's name
	 */
	name = argv[0];

	/*
	 * Initialize variables
	 */


	koption  = 0;   /* kill events if not in PARSE.CNF 		*/
	poption  = 1;	/* allows pitch bend to effect also the patch 	*/
	debugmsg = 0;	/* No debug msgs 				*/
	msg      = 1;	/* Output msgs					*/
	
	for (i = 0; i < MAX_CHAN; i++)
	{
		/* init pitchpatch stuff (default: no translations */

		pitchpatch[i].translate = FALSE;
		pitchpatch[i].n = 0;
		vols[i] = 127;			// init to max volume 
	}

	count		= 0;
	mspqn		= 500000L;
	tpqn		= 60;
	factor		= 1.0;
	last_voice	= -1;

	for (i = 0 ; i < MAX_CHAN ; ++i)
	{
		voice_flag[i] = -1;
		voice_time[i] = 0L;
	}

	oname		= NULL;	// output file name 

	n1_ch 		= -1;
	novoldown 	= 0;
	volscale 	= 256;  // equal to shift of 8 

	note_on_err 	= 0;	// number of notes on errors 		
	note_off_err 	= 0;	// number of notes off errors 	

	n_ch 		= 12;   // default number of channels 	12 now!!
	ch_off 		= 0;	// channel offset	      

	c12 = c13 = c14 = 0;    // counter for controller events 


	strcpy(notescnf, "notes.cnf");
	strcpy(parsecnf, "parse.cnf");

	/*
	 * Parse command line options
	 */
	while ((c = getopt(argc, argv, "f:c:vdkqpz:o:n:x:")) != (char)-1)
	{
		switch (c)
		{
		case 'v':
			printf("ATARI JAGUAR NEW MIDI Parser, %s\n", version);
			break;
		case 'd':
			debugmsg = 1;
			break;
		case 'q':
			msg = 0;
			break;
		case 'p':
			poption = 0;
			break;
		case 'k':
			koption = 1;
			break;
		case 'o':
			oname = optarg;
			break;
		case 'c':
			strcpy(parsecnf, optarg);
			break;
		case 'f':
			strcpy(notescnf, optarg);
			break;
		case 'x':
			ch_off = (short) atoi(optarg);
			ch_off = (ch_off < 0) ? -ch_off : ch_off;
			if (n1_ch > MAX_CHAN)
			{
				printf("%s: maximum offset is %d\n", name, MAX_CHAN);
				exit(-1);
			}
			break;
		case 'n':
			n1_ch = (short) atoi(optarg);
			n1_ch = (n1_ch < 0) ? -n1_ch : n1_ch;
			if (n1_ch > MAX_CHAN)
			{
				printf("%s: maximum number of channels is %d\n", name, MAX_CHAN);
				exit(-1);
			}
			break;
		case 'z':
			volscale = (short) atoi(optarg);
			volscale = (ch_off < 0) ? -volscale  : volscale;
			volscale = (ch_off == 0) ? 1  : volscale;

			volscale = (volscale) ? volscale : 1;   /* make sure it's non-zero */

			if( volscale > MAX_VOLSCALE ) {
				printf( "%s: maximum value for volume scale down is %ld\n",
					name, (long)MAX_VOLSCALE );
				exit(-1);
			}
			break;
		case '?':
			usage();
			exit(0);
			break;
		}
	}
	/* 
	 * read configuration file for pitch-patch translations 
	 */
	read_cnf();     

	if (n1_ch != -1) {        /* overwrite .cnf */
		 n_ch = n1_ch;
		printf(
"Command line option overwrites \'PARSE.CNF\', number of channels set to %d.\n",
		n_ch);
	}

	/*
	 * Input & output file name
	 */
	if (optind < argc)
		iname = argv[optind];
	else
		iname = "test.mid";

	if(msg)
		printf( "Opening '%s' for input\n", iname );

	infile = fopen(iname, "rb");

	if (infile == NULL) {
		perror(iname);
		exit(-1);
	}
	if (oname == NULL)
		oname = "test.out";

	if ((outfile = fopen(oname, "w")) == NULL) {
		perror(oname);
		exit(-1);
	}

	/*
	 * Write Header
	 */ 
	fprintf(outfile, "; *** Created by NEW MIDI file parser %s\n", version);
	fprintf(outfile, "\t.globl\tscoretab\n");
	fprintf(outfile, "scoretab:\n");

	/*
	 * Read MIDI file header
	 */
	fread(inbuf, 1, 4, infile);
	inbuf[4] = 0;
printf("inbuf=%s\n",inbuf);
	if (strcmp((char *)inbuf, "MThd") != 0)
		printf("Not a legal file header\n");

	fread(inbuf, 1, 4, infile);
	if (strcmp((char *)inbuf, "\0\0\0\6") !=0)
		printf("Not a legal file header size\n");

	readshort(infile, &temp);
	if (msg)
		printf("MIDI file type %d\n", temp);

	readshort(infile, &ntrk );
	if (msg)
		printf("Number of tracks %d\n", ntrk);

	readshort(infile, &temp);
	if (msg)
		printf("Resolution is ppqn %d\n", temp);
	tpqn = temp;

	/*
	 * Read all tracks into memory
	 */
	for (tcount = 0; tcount < ntrk ; ++tcount)
	{
		readlong(infile, (long*)inbuf);
		inbuf[4] = 0;
		if (strcmp((char *)inbuf, "MTrk") != 0)
			printf("Not a legal track header\n");

		readlong(infile, (long*)&ltemp);
		if (msg)
			printf("Size of track %ld\n", ltemp);

		if ((track[tcount] = malloc(ltemp)) == NULL) {
			printf("(1) Out of memory.\n");
			exit(-1);
		}
		tsize[tcount]  = ltemp;
		tindex[tcount] = 0L;
		if (fread(track[tcount], ltemp, 1, infile) != 1) {
			perror(iname);
			exit(-1);
		}
	}

	/*	
	 * Build linked lists of tracks
	 */
	for (i = 0; i < ntrk; i++) {
		// assign head
		mheads[i].time = 0L;
		eventp = &mheads[i];
		do {
			if ((eventp->next = (MEVENT*) malloc(sizeof(MEVENT)))
			 == NULL) {
				printf("fish: Out of memory.\n");
				exit(-1);
			}
			eventp->next->prev = eventp;
			eventp = eventp->next;
			get_time(i, eventp);
			eot = get_event(i, eventp);
		} while (!eot);
		eventp->prev->next = NULL; 
	}


	sort_lists(ntrk);

	parse_list();


	fprintf(outfile, "\tdc.l\t$0fffffe0\n");
	fprintf(outfile, "\tdc.l\t$3fffffff\t; loop to itself\n");

	fclose(infile);
	fclose(outfile);
	exit(0);
}
