#include    <stdio.h>
#include    <stdlib.h>

#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)

#define	INT(p)	*((int *)(p))

static	unsigned long	getdword(char *p)
{
    unsigned long l;

    l  = (unsigned char)(*(p++)); l <<= 8;
    l |= (unsigned char)(*(p++)); l <<= 8;
    l |= (unsigned char)(*(p++)); l <<= 8;
    l |= (unsigned char)(*(p++));
    return l;
}
static	unsigned long	get3byte(char *p)
{
    unsigned long l;

    l  = (unsigned char)(*(p++)); l <<= 8;
    l |= (unsigned char)(*(p++)); l <<= 8;
    l |= (unsigned char)(*(p++));
    return l;
}
static	int	getword(char *p)
{
    int i;

    i  = (unsigned char)(*(p++)); i <<= 8;
    i |= (unsigned char)(*(p++));
    return i;
}
static	int	getdelta(char *p, unsigned long *delta)
{
    int n = 0;

    *delta = 0;
    while ( (p[n] & 0x80) != 0 ) {
	*delta |= (p[n++] & 0x7F);
	*delta = (*delta << 7);
    }
    *delta |= p[n++];
    return n;
}
int	PLAY_midi(char *file)
{
    int n, i, o;
    int sw,x,y;
    FILE *fp;
    unsigned long file_size;
    unsigned long size;
    unsigned long delta;
    unsigned long clk;
    unsigned long step, next;
    char *top, *ptr, *btm;
    int format, ntrks, division;
    int port, track, tempo, ofs, st;
    struct {
	char		*ptr;
	char		*btm;
	char		cmd;
	unsigned long	clk;
    } trtab[32];
    char tmp[4];
    char *eup;
    int pos, max, list;

    DSP_mos(2);
    if( (fp = fopen(file, "rb")) == NULL ) {
	DSP_mos(0);
	kakunin("MIDIファイルのオープンに失敗しました");
	return ERR;
    }

    fseek(fp, 0L, SEEK_END);
    file_size = ftell(fp);
    rewind(fp);

    if( (top = (char *)malloc((int)file_size)) == NULL ) {
	DSP_mos(0);
	fclose(fp);
	kakunin("メモリが足りませんでした");
	return ERR;
    }

    if ( fread(top, (int)file_size, 1, fp) != 1 ) {
	DSP_mos(0);
	kakunin("MIDIファイルの読み込みに失敗しました");
	return ERR;
    }
    fclose(fp);

    ptr = top;
    btm = top + file_size;

    if ( strncmp(ptr, "MThd", 4) != 0 &&
	 file_size > (128 + 8) &&
	 strncmp(ptr + 128, "MThd", 4) == 0 ) {
	ptr += 128;
    }

    while ( strncmp(ptr, "MThd", 4) != 0 ) {
	size = getdword(ptr + 4);
	if ( (ptr += (8 + size)) >= btm )
	    goto ERROR;
    }

    size = getdword(ptr + 4);
    ptr += 8;
    format   = getword(ptr + 0);
    ntrks    = getword(ptr + 2);
    division = getword(ptr + 4);
    ptr += size;

    if ( ntrks <= 0 || division <= 0 )
	goto ERROR;

    for ( track = 0 ; track < 32 && track < ntrks && ptr < btm ; track++ ) {
	while ( strncmp(ptr, "MTrk", 4) != 0 ) {
	    size = getdword(ptr + 4);
	    if ( (ptr += (8 + size)) >= btm )
		goto ERROR;
	}

	size = getdword(ptr + 4);
	ptr += 8;

	trtab[track].btm = ptr + size;
	trtab[track].ptr = ptr + getdelta(ptr, &delta);
	trtab[track].clk = delta;
	trtab[track].cmd = 0x00;

	ptr += size;
    }
    ntrks = track;

    port = 0;
    tempo = 120;
    st = FALSE;
    step = 0;
    next = division * 4;

    list = 0;
    max = (64 * 1024);

    if ( (eup = (char *)malloc(max)) == NULL )
	goto MEMERR;

    pos = 4;
    eup[pos++] = 0x33;
    eup[pos++] = tempo - 30;

    while ( st == FALSE ) {
	st = TRUE;

	clk = 0xFFFFFFFFL;
	for ( track = 0 ; track < ntrks ; track++ ) {
	    if ( trtab[track].ptr < trtab[track].btm &&
		 trtab[track].clk < clk )
		clk = trtab[track].clk;
	}

	if ( clk == 0xFFFFFFFFL )
	    break;

	while ( clk >= next ) {
	    if ( (pos + 12) >= max ) {
		max += (4 * 1024);
		if ( (eup = (char *)realloc(eup, max)) == NULL )
		    goto MEMERR;
	    }
	    eup[pos++] = 0xF2;
	    eup[pos++] = 0x33;
	    eup[pos++] = 384 % 128;
	    eup[pos++] = 384 / 128;
	    eup[pos++] = 0xFF;
	    eup[pos++] = 0xFF;

	    step += (division * 4);
	    next += (division * 4);
	}

	for ( track = 0 ; track < ntrks ; track++ ) {
	    ptr = trtab[track].ptr;
	    while ( ptr < trtab[track].btm &&
		    trtab[track].clk <= clk ) {

	        if ( (pos + 12) >= max ) {
		    max += (4 * 1024);
		    if ( (eup = (char *)realloc(eup, max)) == NULL )
		        goto MEMERR;
	        }

		ofs = (trtab[track].clk - step) * 96 / division;

		if ( (*ptr & 0x80) == 0 )
		    *(--ptr) = trtab[track].cmd;

		switch(*(ptr) & 0xF0) {
		case 0x90:
		    trtab[track].cmd = *ptr;
		    ptr[1] &= 0x7F;
		    ptr[2] &= 0x7F;
		    if ( ptr[2] == 0 )
			goto NOTE_OFF;
		    eup[pos + 0] = ptr[0];
		    eup[pos + 1] = ptr[1];
		    eup[pos + 2] = ptr[2];
		    INT(eup + pos + 4) = trtab[track].clk * 96 / division;
		    INT(eup + pos + 8) = list;
		    list = pos;
		    pos += 12;
		    ptr += 3;
		    break;

		case 0x80:
		    trtab[track].cmd = *ptr;
		    ptr[1] &= 0x7F;
		    ptr[2] &= 0x7F;
		NOTE_OFF:
		    for ( n = list ; n != 0 ; ) {
			i = INT(eup + n + 8);
			if ( (ptr[0] & 0x0F) == (eup[n + 0] & 0x0F) &&
			     ptr[1] == eup[n + 1] ) {

			    delta = INT(eup + n + 4);
			    eup[n + 4] = eup[n + 1];
			    eup[n + 5] = eup[n + 2];
			    eup[n + 1] = 0;
			    eup[n + 2] = (delta % 384) % 128;
			    eup[n + 3] = (delta % 384) / 128;
			    eup[n + 6] = 0x80 | (eup[n + 0] & 0x0F);
			    delta = (trtab[track].clk * 96 / division) - delta;
			    eup[n + 7]  = delta & 0x0F; delta >>= 4;
			    eup[n + 8]  = delta & 0x0F; delta >>= 4;
			    eup[n + 9]  = delta & 0x0F; delta >>= 4;
			    eup[n + 10] = delta & 0x0F; delta >>= 4;
			    eup[n + 11] = ptr[2];

			    if ( n == list )
				list = i;
			    else
				INT(eup + o + 8) = i;
			    n = i;
			} else {
			    o = n;
			    n = i;
			}
		    }
		    ptr += 3;
		    break;

		case 0xA0: case 0xB0: case 0xE0:
		    trtab[track].cmd = *ptr;
		    eup[pos++] = ptr[0];
		    eup[pos++] = 0;
		    eup[pos++] = ofs % 128;
		    eup[pos++] = ofs / 128;
		    eup[pos++] = ptr[1];
		    eup[pos++] = ptr[2];
		    ptr += 3;
		    break;

		case 0xC0: case 0xD0:
		    trtab[track].cmd = *ptr;
		    eup[pos++] = ptr[0];
		    eup[pos++] = 0;
		    eup[pos++] = ofs % 128;
		    eup[pos++] = ofs / 128;
		    eup[pos++] = ptr[1];
		    eup[pos++] = 0xFF;
		    ptr += 2;
		    break;

		case 0xF0:
		    trtab[track].cmd = 0x00;
		    switch(*(ptr) & 0xFF) {
		    case 0xF0:
		    case 0xF7:
			eup[pos++] = 0xF0;
			eup[pos++] = 0;
			eup[pos++] = ofs % 128;
			eup[pos++] = ofs / 128;
			ptr += 1;
			ptr += getdelta(ptr, &delta);
			eup[pos++] = delta % 128;
			eup[pos++] = delta / 128;
			for ( n = 0 ; n < delta ; n += 6 ) {
	        	    if ( (pos + 12) >= max ) {
			        max += (4 * 1024);
			        if ( (eup = (char *)realloc(eup, max)) == NULL )
			            goto MEMERR;
	        	    }
			    if ( (i = delta - n) > 6 )
				i = 6;
			    memcpy(eup + pos, ptr + n, i);
			    while ( i < 6 )
				eup[pos + i++] = 0xFF;
			    pos += 6;
			}
			ptr += delta;
			eup[pos++] = 0xF7;
			eup[pos++] = 0xFF;
			eup[pos++] = 0xFF;
			eup[pos++] = 0xFF;
			eup[pos++] = 0xFF;
			eup[pos++] = 0xFF;
			break;

		    case 0xF2:
			ptr += 2;
			break;

		    case 0xF3:
			ptr += 3;
			break;

		    case 0xF1:
		    case 0xF4:
		    case 0xF5:
		    case 0xF6:
		    case 0xF8:
		    case 0xFA:
		    case 0xFB:
		    case 0xFC:
		    case 0xFD:
		    case 0xFE:
			ptr += 1;
			break;

		    case 0xFF:
			switch(*(ptr + 1) & 0xFF) {
			case 0x2F:		/* end of */
			    ptr = trtab[track].btm;
			    break;

			case 0x51:		/* tempo */
			    delta = get3byte(ptr + 3);
			    if ( delta > 0 ) {
				if ( (tempo = 60000000L / delta) < 30 )
				    tempo = 30;
				else if ( tempo > 280 )
				    tempo = 280;
			    }
			    eup[pos++] = 0xF8;
			    eup[pos++] = 0xFF;
			    eup[pos++] = ofs % 128;
			    eup[pos++] = ofs / 128;
			    eup[pos++] = (tempo - 30) % 128;
			    eup[pos++] = (tempo - 30) / 128;
			    break;
			}
			ptr += 2;
			ptr += getdelta(ptr, &delta);
			ptr += delta;
			break;
		    }
		    break;

		default:
		    ptr++;
		    break;
		}
		ptr += getdelta(ptr, &delta);
		trtab[track].clk += delta;
	    }
	    if ( (trtab[track].ptr = ptr) < trtab[track].btm )
		st = FALSE;
	}
    }

    for ( n = list ; n != 0 ; ) {
	i = INT(eup + n + 8);
	memset(eup + n, 0xFF, 12);
	n = i;
    }

    if ( (pos + 6) >= max ) {
        max += 6;
	if ( (eup = (char *)realloc(eup, max)) == NULL )
	    goto MEMERR;
    }

    eup[pos++] = 0xFE;
    eup[pos++] = 0x33;
    eup[pos++] = 384 % 128;
    eup[pos++] = 384 / 128;
    eup[pos++] = 0xFF;
    eup[pos++] = 0xFF;

    INT(eup) = pos;

    DSP_mos(0);

    SND_eup_mute_set(0, 0xFF);
    SND_eup_port_set(0, 0);
    SND_eup_midi_ch_set(0, 0xFF);
    SND_eup_bias_set(0, 0);
    SND_eup_transpose_set(0, 0);

    SND_eup_loop_set(0);
    SND_eup_tempo_set(120 - 30);
    SND_eup_relative_tempo_set(0);
    SND_eup_play_start(eup + 6, pos - 6, 0x33);

    EUP_open(file);
    while ( SND_eup_stat_flag() ) {
        if ( (n = EUP_chk(SND_eup_stat_ptr())) == ERR )
	    break;
	else if ( n == TRUE ) {
	    SND_eup_play_stop();
	    SND_eup_play_start(eup + 6, pos - 6, 0x33);
	}
    }
    EUP_close();

    SND_eup_play_stop();

    free(eup);
    free(top);
    return FALSE;

ERROR:
    free(top);
    DSP_mos(0);
    kakunin("対応していないMIDIファイルです");
    return ERR;

MEMERR:
    free(top);
    DSP_mos(0);
    kakunin("メモリ不足です");
    return ERR;
}
