#include <osbind.h>

struct {
	unsigned char	ident[10];
	unsigned char	ainam[15][10];
	unsigned char	aiset[15][8];
	unsigned char	cinam[15][10];
	unsigned char	ciset1[15];
	unsigned char	ciset2[15];
	long	fptr[5];
	unsigned char	title[32];
} mushdr;

struct muevnt {
	long	rltime;
	long	deltime;
	char	etype;
	char	inst;
	char	frq;
	char	fill;
} evnt[2500];

unsigned char tmpbuf[16384];

char *keys[] = { "none","C","G","D","A","E","B","F#","C#","F","Bb","Eb","Ab",
		"Db","Gb","Cb" };
char *beat[] = { "none","2/2","3/2","2/4","3/4","4/4","5/4","6/8" };

/* sharping and flatting for key:
 c     d     e  f     g     a     b  */
int	keyfix[16][12] = {
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0,
 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0,
 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0,
 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0,
 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-1,
 0, 0, 0, 0,-1, 0, 0, 0, 0, 0, 0,-1,
 0, 0, 0, 0,-1, 0, 0, 0, 0,-1, 0,-1,
 0, 0,-1, 0,-1, 0, 0, 0, 0,-1, 0,-1,
 0, 0,-1, 0,-1, 0, 0,-1, 0,-1, 0,-1,
-1, 0,-1, 0,-1, 0, 0,-1, 0,-1, 0,-1,
-1, 0,-1, 0,-1,-1, 0,-1, 0,-1, 0,-1
};

/* 96 pulses per quarter note timing */
int ppq[] = {
/*    1/32      1/16      1/8 (3)---1/4-----(.)  1/2           1            2*/
6,8,9, 12,16,18, 24,32,36, 48,64,72, 96,128,144, 192,256,288, 384,512,576, 768,
1024,1152, 1536,2048,2304, 3072,4096,4608, 6144,8192
};

/*************************/
main( argc , argv )
int	argc;
char	*argv[];
{
int	handl;
int	i,j,k;
int	tempo;
int	evntptr;
long	tmpo,tmpo1;
register int	keyf,pitch;
register long	realtime;
int	noteplay[3];

handl = Fopen( argv[1] , 0 );
if( handl < 0 ) return;
Fread( handl , 512L , &mushdr );

/* NOTES **********************/
for( i = 0 ; i < 16384 ; i++ ) tmpbuf[i] = 0;
if( mushdr.fptr[0] )
	Fread( handl , (unsigned long)(mushdr.fptr[0] - 512L) , tmpbuf );
else
	Fread( handl , 16384L , tmpbuf );

/* NOW, FOR THE SOUNDS */
tempo = 60;
tmpo1 = 0;
realtime = 0;
evntptr = 0;

printf( "PROCESSING\n" );

/* for each byte, but may jump for variable record lengths */
for( i = 8 ; tmpbuf[i] != 0xff ; i++ ) {
	switch( tmpbuf[i] ) {
	case 0x00:	/* begin/end record delimiter */
realtime += tmpo1;
tmpo1 = 0;
		break;
	case 0x80:	/* grand staff and key signature, what does [1] do? */
keyf = tmpbuf[i+2];
		i += 2;
		break;
	case 0x81:	/* tempo (MM) */
tempo = tmpbuf[i+1];
		i++;
		break;
	case 0x82:	/* vertical bar across grand staff */
		break;
	case 0x83:	/* time signature */
		i++;
		break;
	case 0x84:	/* loudness */
		i+=2;
		break;
	case 0x85:	/* repeat (n times) marker ||: */
/*		printf( " %d*[" , tmpbuf[i+1] );
*/		i++;
		break;
	case 0x86:	/* repeat ends here :|| */
/*		printf( "]" );
*/		break;
	default:	/* note subrecord, 3 bytes, instr:note */
/* the printed value is the base note, pitch is the actual to play */
pitch = tmpbuf[i+2];
/* should be +/-12 per octave (oct2=0) offset for instrument */


		if( ( tmpbuf[i+1] & 0xc0 ) == 0xc0 )	/* flat */
			pitch--;
		if( ( tmpbuf[i+1] & 0xc0 ) == 0x80 )	/* sharp */
			pitch++;

/*		if( ( tmpbuf[i+1] & 0xc0 ) == 0x40 )	   natural */

if( !( tmpbuf[i+1] & 0xc0 ) )		/* adjust note value for key */
	pitch += keyfix[keyf][pitch%12];	/* only if no accidental */

/* Note duration */

/* the player does not yet do differing lengths,
	should be minimum between 00 records */
tmpo = 1250L;		/* for .5 milliseconds, 125 for 200Hz tics */
tmpo = ppq[ tmpbuf[i+1] & 31 ];		/* *= */
/* tmpo /= tempo; */
if( !tmpo1 || tmpo < tmpo1 )
	tmpo1 = tmpo;

k = 0;
if( tmpbuf[i] & 0x40 )	/* begin tie */
	k |= 4;
if( tmpbuf[i] & 0x20 )	/* end tie (valid to begin too */
	k |= 2;
if( tmpbuf[i+1] & 0x20 )	/* accent */
	k |= 8;

if( !( tmpbuf[i] & 0x10 ) ) {	/* rest */
	evnt[ evntptr ].rltime = realtime;
	evnt[ evntptr+1 ].rltime = realtime + tmpo;
	evnt[ evntptr ].deltime = tmpo;
	evnt[ evntptr+1 ].deltime = tmpo;
	evnt[ evntptr ].etype = k;
	evnt[ evntptr+1 ].etype = k|1;
	evnt[ evntptr ].inst = tmpbuf[i]&15;
	evnt[ evntptr+1 ].inst = tmpbuf[i]&15;
	evnt[ evntptr ].frq = pitch;
	evnt[ evntptr+1 ].frq = pitch;
	evntptr += 2;
	}

		i+=2;
		break;
		}

	}
/* unfold event table */
printf( "SORTING\n" );
heapsort( evnt , evntptr );

evnt[ evntptr ].rltime = 0x7fffffffL;

printf( "UNFOLDING-\n" );
/* look for end of tie/beginning of tie pairs, and expand first note */

printf( "RESULTS:\n" );
initsnd();
tmpo1 = 0;
k = 0;
noteplay[0]=0;
noteplay[1]=0;
noteplay[2]=0;
for( i = 0 ; i < evntptr ; i++ ) {
	if( tmpo1 < evnt[i].rltime ) {
printf( "%10D\n" , evnt[i].rltime );

	while( k < i ) {
		if( !( evnt[k].etype & 1 ) ) {
if( !noteplay[0] )play(0,evnt[k].frq),noteplay[0]=evnt[k].frq;
else if( !noteplay[1] )play(1,evnt[k].frq),noteplay[1]=evnt[k].frq;
else if( !noteplay[2] )play(2,evnt[k].frq),noteplay[2]=evnt[k].frq;
			}
		k++;
		}

		p_pause( (long)( (evnt[i].rltime - tmpo1) * 625L / tempo ) );
		tmpo1 = evnt[i].rltime;
		}

/*	printf( "%10D %6D %2d %2d %3d\n" ,
evnt[i].rltime, evnt[i].deltime, evnt[i].etype, evnt[i].inst, evnt[i].frq );
*/
	if( evnt[i].etype & 1 ) {
/*		if( !( evnt[i].etype & 4 ) ) {		only if matching &2*/
if( noteplay[0] == evnt[i].frq )quiet(0),noteplay[0]=0;
else if( noteplay[1] == evnt[i].frq )quiet(1),noteplay[1]=0;
else if( noteplay[2] == evnt[i].frq )quiet(2),noteplay[2]=0;
/*			}*/
		}

	}

printf( "\n" );
Fclose( handl );

}

/*** HOW TO PLAY NOTES ***/

int	notedivs[12] = {	3822, 3608, 3405, 3214, 3034, 2863,
				2703, 2551, 2408, 2273, 2145, 2025 };
/*****/
initsnd()
{
int	val;

Giaccess( 0 , 8+128 );
Giaccess( 0 , 9+128 );
Giaccess( 0 , 10+128 );

val = Giaccess( 0 , 7 );
val &= 0xc0;
val |= 0x38;
Giaccess( val , 7+128 );
}

/*****/
quiet( channel )
register int	channel;
{
Giaccess( 0 , channel + 8+128 );
}

/*****/
play( channel , note )
register int	channel,note;
{
register int val;
note -= 24;

if( note < 0 )
	return;

val = notedivs[ note % 12 ] >> (note / 12);

Giaccess( 0x08 ,	channel + 8+128 );
Giaccess( val & 0xff ,	channel + channel + 0+128 );
Giaccess( val >> 8 ,	channel + channel + 1+128 );

}

/*************************/
heapsort( tbl , len )
struct muevnt	*tbl;
int	len;
{
int	l;
int	r;
int	i,j;

struct muevnt	ktbl;

/*1*/
l = len/2 + 1;
r = len;

while( 1 ) {
/*2*/
	if( l > 1 ) {
		l--;
		ktbl = tbl[ l ];
		}
	else	{
		ktbl = tbl[ r ];
		tbl[ r ] = tbl[ 1 ];
		r--;
		if( r == 1 ) {
			tbl[ 1 ] = ktbl;
			break;
			}
		}
/*3*/
	j = l;

	while( 1 ) {
/*4*/
		i = j;
		j = j+j;
		if( j > r )
			break;
		if( j < r && tbl[ j ].rltime < tbl[ j+1 ].rltime )
			j++;
/*6*/
		if( ktbl.rltime >= tbl[ j ].rltime )
			break;
/*7*/
		tbl[i] = tbl[j];
		}
/*8*/
	tbl[i] = ktbl;
	}

}
