/* ------------------------------------------------------------------
                        MML compiler [M2]
               programmed by S.Yamamoto (SHINNOSUKE)
                 m2comp.c  --  MML compile Main
------------------------------------------------------------------ */

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>
#include	<limits.h>
#include	"m2.h"

#define	SUCCSESS	0
#define	FAILURE	(-1)
#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)
#define	YES	1
#define	NO	0

#define	PART_MAX	16
#define	ARAY_MAX	16
#define	EXCL_MAX	128

#define	EXCM	0xf0
#define	TEMPO	0xf7
#define	SIGNATURE	0xf8
#define	MEMO	0xf1
#define	WORDS	0xf2
#define	F7EXCM	0xf3
#define	DIRECT	0xf4

#define	ABS(x)	((x)>0?(x):-(x))
#define	CHECKSUM(x)	(((x)%128!=0)?128-(x)%128:0) /* EXCLUSIVE SUM */

#define	RUNNING	1
#define	SHIFTST	2
#define	HISPCMP	4
#define	CHECKMD	8

char	*EMG_v = "velocity";
char	*EMG_o = "octave";
char	*EMG_q = "q";
char	*EMG_l = "length";

char	*rp;
char	*exclPt[EXCL_MAX];	/* Exclusive pointer */

char	key_spart[16] = {0,0,0,0 ,0,0,0,0 ,0,0,0,0 ,0,0,0,0};
int	key_shift = 0;

int	dlt_v[PART_MAX][ARAY_MAX];
int	dlt_o[PART_MAX][ARAY_MAX];
int	dlt_q[PART_MAX][ARAY_MAX];
int	dlt_l[PART_MAX][ARAY_MAX];

int	tie_flag;
int	tie_note;
int	tie_etct;	/* タイコマンド用 */

int	exclCt;		/* Exclusive count */
int	nowPart;
int	nowAray;
int	dltAray[PART_MAX];

int	evntCt;

int option;	/* option status */

unsigned	short	maxTCt;
unsigned	short	setTime[128];

WORKMEM	*evntBuf;
FLGDAT	fg;

static	int	ctrlMacro[][7] =
{/* Num,Mode,Min,Max,+(para default),*,default */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{0,0,0,127,0,1,0},	/* b/Bank Select */
	{10,1,ERR,ERR,64,1,ERR},	/* c/Panpot Center */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{11,0,0,127,0,1,127},	/* e/Expression */
	{69,0,0,1,0,127,0},	/* f/freeze */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{64,0,0,1,0,127,0},	/* h/Hold1 */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{10,0,0,64,64,-1,63},	/* l/Panpot Left */
	{1,0,0,127,0,1,0},	/* m/Modulation depth */
	{66,0,0,1,0,127,0},	/* n/Sostenuto */
	{93,0,0,127,0,1,0},	/* o/chorus send level(ef3) */
	{65,0,0,1,0,127,0},	/* p/Portamento */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{10,0,0,63,64,1,63},	/* r/Panpot Right */
	{67,0,0,1,0,127,0},	/* s/Soft pedal */
	{5,0,0,127,0,1,0},	/* t/portamento time */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{7,0,0,127,0,1,100},	/* v/volume */
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR},
	{ERR,ERR,ERR,ERR,ERR,ERR,ERR}
};

void	dltSet( void )	/* 省略値の初期化 */
{
	int	i;
	int	j;

	for( i=0;i<PART_MAX;i++ ) {
		for( j=0;j<ARAY_MAX;j++ ) {
			dlt_v[i][j] = 127;
			dlt_o[i][j] = 4;
			dlt_q[i][j] = 8;
			dlt_l[i][j] = 4;
		}
	}
	return;
}

/* データ読み込み・書き込みなど */

int	readData( void )	/* 文字の読み込み */
{
	int	ret;

	skipLine( &rp );
	ret = *rp;
	return( ret );
}

int	readPara( int min ,int max ,int d ,char *m )	/* 数字の読み込み */
{
	int	ret;

	skipLine( &rp );
	ret = strToInt( &rp ,min ,max ,d ,m );
	return( ret );
}

int	blockName( void )	/* ブロックネームを読み飛ばす */
{
	int	b;
	int	flag = 0;

	for(;;) {
		b = readData();
		if( b == '{' )
			break;
		if( b == '\0' )
			return( FAILURE );
		rp++;
		if( b< ' ' || b > '~' )
			continue;
		if( flag == 0 )
			putDisp( "process:" );
		putDisp( "%c",b );
		flag = 1;
	}
	rp++;
	if( flag == 1 )
		putDisp("\n");
	return( SUCCSESS );
}

void	getPart( void )	/* パート＆配列番号を得る */
{
	nowPart = readPara( 1 ,PART_MAX ,-1 ,"(part number)" ) - 1;

	if( readData() == '[' ) {
		rp++;
		dltAray[nowPart] = readPara( 0 ,ARAY_MAX-1 ,dltAray[nowPart]
			,"(array number)" );
		if( readData() != ']' )
			errMsg( MSG_syntax ,"array" );
		rp++;
	};

	nowAray = dltAray[nowPart];
	dltAray[nowPart]++;
	if( dltAray[nowPart] >= ARAY_MAX )
		dltAray[nowPart]=0;

	if( readData() != '=' )
		errMsg( MSG_misopd ,"not found '='" );
	rp++;
	return;
}

void	exclBank( unsigned short t ,int m ,int l ,char *s )
{
	int	i;

	evntPut( t ,m ,exclCt ,l);
	if(( exclPt[exclCt] = malloc( l )) == NULL)
		errMsg( MSG_cntalc ,"excl/word" );

	for( i=0;i<l;i++ ) {
		exclPt[exclCt][i] = *s;
		s++;
	}
	exclCt++;
	return;
}

/* イベント処理系 */

void	evntPut(unsigned short c ,int d1 ,int d2 ,int d3 )	/* 記憶 */
{
	if( evntCt >= fg.wksize )
		errMsg( MSG_outwka ,NULL );

	evntBuf[evntCt].num = (unsigned short) evntCt;
	evntBuf[evntCt].ct = (unsigned short) c;
	evntBuf[evntCt].dat[0] = (unsigned char) d1;
	evntBuf[evntCt].dat[1] = (unsigned char) d2;
	evntBuf[evntCt].dat[2] = (unsigned char) d3;
	evntCt++;
	return;
}

void	evntSort( void )	/* イベントをソート */
{
/*  基本選択法  */
	int	i;
	int	j;
	int	s;

	unsigned	short	min;
	WORKMEM	dummy;
	
	for( i=0;i<evntCt-1;i++ ) {
		min = evntBuf[i].ct;
		s = i;
		for( j=i+1;j<evntCt;j++ ) {
			if( evntBuf[j].ct < min || (evntBuf[j].ct == min
			&& evntBuf[j].num < evntBuf[s].num )) {
				min = evntBuf[j].ct;
				s = j;
			}
		}
		dummy = evntBuf[i];
		evntBuf[i] = evntBuf[s];
		evntBuf[s] = dummy;
	}
	return;
}

void	evntSort2( void )	/* イベントをソート（高速モード） */
{
/*  ヒープソート  
    動作チェックで，完全に安定したソートができていないことがわかった。
*/
	int	i;
	int	j;
	int	k;
	int	n = evntCt;

	WORKMEM	x;

	for ( k=n/2;k>=1;k-- ) {
		i = k;
		x = evntBuf[i-1];
		while((j = 2 * i) <= n) {
			if (j< n && evntBuf[j-1].ct < evntBuf[j].ct) j++;
			if (evntBuf[j-1].ct == evntBuf[j].ct) {
				if(j < n && evntBuf[j-1].num <= evntBuf[j].num )
					j++;
			}
			if (x.ct > evntBuf[j-1].ct) break;
			if (x.ct == evntBuf[j-1].ct) {
				if(x.num >= evntBuf[j-1].num)	break;
			}
			evntBuf[i-1] = evntBuf[j-1];
			i = j;
		}
		evntBuf[i-1] = x;
	}
	while (n > 1) {
		x = evntBuf[n-1];
		evntBuf[n-1] = evntBuf[0];
		n--;
		i = 1;
		while ((j = 2 * i) <= n) {
			if (j < n && evntBuf[j-1].ct < evntBuf[j].ct) j++;
			if (evntBuf[j-1].ct == evntBuf[j].ct) {
				if(j < n && evntBuf[j-1].num <= evntBuf[j].num) j++;
			}
			if (x.ct > evntBuf[j-1].ct) break;
			if (x.ct == evntBuf[j-1].ct) {
				if(x.num >= evntBuf[j-1].num)	break;
			}
			evntBuf[i-1] = evntBuf[j-1];
			i = j;
		}
		evntBuf[i-1] = x;
	}
	return;
}

void	evntCheck( void )	/* イベントをチェック */
{
	unsigned	int	i;
	unsigned	int	c = 0;
	unsigned	int	n = 0;

	printf( "No.   Time  Pri.  MIDI Para.\n" );

	for( i=0;i<evntCt;i++ ) {
		printf( "%05u %05u %05u %02X %03u %03u\n",i ,evntBuf[i].ct
			,evntBuf[i].num ,evntBuf[i].dat[0]
			,evntBuf[i].dat[1] ,evntBuf[i].dat[2] );

		if ( i != 0 ) {
			if( c == evntBuf[i].ct ) {
				if( n > evntBuf[i].num )
					printf( "Warning:Sort error\n" );
			}
		}
		c = evntBuf[i].ct;
		n = evntBuf[i].num; 
	}
	return;
}

void	evntRtch( void )	/* イベントを修正 */
{
	int	e;
	int	i;
	int	flag = FALSE;

	unsigned	short	max;
	unsigned	short	t1;
	unsigned	short	t2;

	max = evntBuf[evntCt-1].ct;
	for( i=0;i<evntCt-2;i++ ) {
		e = (evntBuf[i].dat[0]) & 0xf0;
		if( e==0xb0 || e==0xc0 || e==0xe0 || e==0xf0 ) {
			if( flag == FALSE ) {
				t1 = evntBuf[i].ct;
				t2 = evntBuf[i].ct;
				flag = TRUE;
				continue;
			}
			if( t1 == evntBuf[i].ct ) {
				t2++;
				if( t2 <= max )	evntBuf[i].ct = t2;
				continue;
			}
			t1 = evntBuf[i].ct;
			if( t1 > t2 )	t2 = evntBuf[i].ct;
		}
	}
	evntSort();
	return;
}

void	evntWrite(void)	/* イベントをディスクに出力 */
{
	int	ev;
	int	i;
	int	j;
	int	dummy;
	int	running = -1;

	unsigned	long	dt;

	for( i=0;i<evntCt;i++ ) {
		dt = ( i==0 ) ? 0 : evntBuf[i].ct-evntBuf[i-1].ct;
		dt = dt * fg.div /48;	/* 分解能変換 */
		varNum( dt );	/* ＴＩＭＥ */
		ev = evntBuf[i].dat[0];
		if( ev >= 0x80 && ev <= 0xef) {
			if(( running!=ev ) || (( option & RUNNING ) != RUNNING)) {
				putData( 1,ev );
				running = ev;
			}
			if(( ev&0xf0 ) == 0xc0 ) {
				putData( 1 ,evntBuf[i].dat[1] );
				continue;
			}
			putData( 2 ,evntBuf[i].dat[1] ,evntBuf[i].dat[2] );
			continue;
		}
		running = -1;
		if( ev == 0xff ) {
			for( j=0;j<3;j++ )
				putData( 1 ,evntBuf[i].dat[j] );
			continue;
		}
		switch( ev ) {
		case EXCM:	/* exclusive message */
			putData( 1 ,0xf0 );
			dummy = (int) evntBuf[i].dat[1];
			varNum( (unsigned long) evntBuf[i].dat[2] + 1 );
			for( j=0;j<(int)evntBuf[i].dat[2];j++ )
				putData( 1 ,(int)exclPt[dummy][j] );
			putData( 1 ,0xf7 );
			break;
		case F7EXCM:
			putData( 1 ,0xf7 );
			dummy = (int) evntBuf[i].dat[1];
			varNum( (unsigned long) evntBuf[i].dat[2] );
			for( j=0;j<(int)evntBuf[i].dat[2];j++ )
				putData( 1 ,(int) exclPt[dummy][j] );
			break;
		case DIRECT:
			dummy = (int) evntBuf[i].dat[1];
			for( j=0;j<(int)evntBuf[i].dat[2];j++ )
				putData( 1 ,(int)exclPt[dummy][j] );
			break;
		case MEMO:	/* text */
		case WORDS:	/* words */
			dummy = ( ev==WORDS ) ? 5 : 1;
			putData( 2,0xff,dummy );
			dummy = (int) evntBuf[i].dat[1];
			varNum( (unsigned long) evntBuf[i].dat[2] );
			for( j=0;j<(int)evntBuf[i].dat[2];j++ )
				putData( 1 ,(int)exclPt[dummy][j] );
			break;
		case TEMPO:	/* tempo */
			putTempo( evntBuf[i].dat[1] * 256 + evntBuf[i].dat[2] );
			break;
		case SIGNATURE:	/* signature */
			putSignature( evntBuf[i].dat[1] ,evntBuf[i].dat[2] );
		case 0xf9:	/* tyou */
			break;
		}
	}
	return;
}

/*  ＭＭＬコマンド類 */

void	mmlNote( unsigned short *tc ,int note )	/* ノートオン・オフ／休符 */
{
	int	d;
	int	n;
	int	len;
	int	vel;
	int	q;
	int	step;
	int	dummy;

	unsigned	short	timeCt;
	unsigned	short	tDummy;

	timeCt = *tc;
	n = note;

	if( n == 12 ) {
		note = readPara( 0 ,127 ,-1 ,"'n' number" );
		if( readData() == ',' )	rp++;
	}

	if( n<12 ) {
		if( (d = readData()) == '+' || d == '#' ){
			note++;
			rp++;
		}
		if( d=='-' ){
			note--;
			rp++;
		}
		note += (dlt_o[nowPart][nowAray]+1) * 12;
		if(key_spart[nowPart] == 0)	note = note + key_shift;
		if(note<0 || note>127)	errMsg(MSG_syntax,"over note");
	}

	step = 0;
	for(;;){
		len = readPara( 1 ,192 ,dlt_l[nowPart][nowAray]
			,"(note:length)" );
		dummy = 192 / len;
		if( readData() == '.' ) {
			rp++;
			dummy = dummy * 1.5;
		}
		step += dummy;
		if( readData() =='^' )	rp++;
		else	break;
	}

	if( n != 13 ){
		if( readData() == ',' ) {
			rp++;
			vel = readPara( 1 ,127 ,dlt_v[nowPart][nowAray]
				,"(note:velocity)" );
		}else	vel = dlt_v[nowPart][nowAray];
		if( readData() == ',' ) {
			rp++;
			q = readPara( 1 ,8 ,dlt_q[nowPart][nowAray]
				,"(note:q)");
		}else	q=dlt_q[nowPart][nowAray];

		if( tie_flag == TRUE ) {
			if( tie_note == note )
				evntBuf[tie_etct].ct = (unsigned short)( timeCt+step*q/8 );
			else {
				if( tie_note != ERR )
					evntBuf[tie_etct].ct = (unsigned short)(timeCt);
				q = 8;
				tie_flag = FALSE;
			}
		}
		if( tie_flag != TRUE ) {
			evntPut( timeCt ,0x90+fg.part[nowPart] ,note ,vel );
			tie_note = note;
			tie_etct = evntCt;
			tDummy = step*q/8-1;
			if( tDummy < 1 )	tDummy = 1;
			evntPut( timeCt+tDummy ,0x80+fg.part[nowPart]
				,note ,0x00 );
		}
	}
	timeCt += step;
	*tc = timeCt;
	tie_flag = FALSE;
	return;
}
void	mmlSub1( unsigned short timeCt ,int cm )	/* ＭＭＬコマンド１ */
{
	char	*msg_tempo = "tempo";

	int	dummy;
	int	para;

	switch( cm )
	{
		case 'v':
			dlt_v[nowPart][nowAray] = readPara( 0,15,15,EMG_v )*8+7;
			break;
		case 'o':
			dlt_o[nowPart][nowAray] = readPara( 1,8,4,EMG_o);
			break;
		case 'q':
			dlt_q[nowPart][nowAray] = readPara( 1,8,8,EMG_q );
			break;
		case 'l':
			dlt_l[nowPart][nowAray] = readPara( 1,192,4,EMG_l );
			break;
		case '>':
			dlt_o[nowPart][nowAray] += 2;
		case '<':
			dlt_o[nowPart][nowAray] --;
			dummy = dlt_o[nowPart][nowAray];
			if( dummy < 1 || dummy > 8 )
				errMsg( MSG_illfnc ,"'>or<' octave over" );
			break;
		case ']':
		case '[':
			dummy = readPara( 0,127,8,EMG_v )*((cm==']')?1:-1);
			dummy += dlt_v[nowPart][nowAray];
			if( dummy<0 || dummy>127 )
				errMsg( MSG_illfnc ,"']or[' velocity over" );
			dlt_v[nowPart][nowAray] = dummy;
			break;
		case 'u':
			para = readPara( -8192,8191,0,"pitch bend" )+8192;
			evntPut( timeCt,0xE0+fg.part[nowPart],para&0x7f,(para&0x3f80)>>7 );
			break;
		case 't':
			dummy = readData();
			if( dummy=='+' || dummy=='-' ) {
			rp++;
			para = readPara( 0,256,5,msg_tempo );
			fg.tempo += para*(( dummy=='+' )?1:-1);
			} else
				fg.tempo = readPara( 30,600,120,msg_tempo );
			evntPut( timeCt,TEMPO,(fg.tempo&0xff00)>>8,fg.tempo&0xff );
			break;
		default:
			errMsg( MSG_unperr ,NULL );
	}
	return;
}

void	mmlSub2( unsigned short timeCt )	/* ＭＭＬコマンド２ */
{
	char	*msg_c = "control change";
	char	*msg_s = "signature";
	char	*msg_x = "exclusive";
	char	*msg_m = "memo or words";

	char	ex[256];

	int	cm2;
	int	para;
	int	para2;
	int	dummy;
	int	i;
	int	j;

	cm2 = tolower(readData());
	rp++;
	switch( cm2 ) {
		case 'a':
			para = readPara( 0,127,0,NULL );
			break;
		case 'c':
			para = readPara( 0,127,-1,msg_c );
			if( readData() != '/' )
				errMsg( MSG_syntax,msg_c );
				rp++;
			para2 = readPara( 0,127,-1,msg_c );
			evntPut( timeCt,0xB0+fg.part[nowPart],para,para2 );
			break;
		case 'v':
			dlt_v[nowPart][nowAray] = readPara( 0,127,127,EMG_v );
			break;
		case 's':
			para = readPara( 1,64,-1,msg_s );
			if( readData() != '/' )
				errMsg( MSG_syntax ,msg_s );
			rp++;
			para2 = readPara( 1,64,-1,msg_s );
			evntPut( timeCt,SIGNATURE,para,para2 );
			fg.signature[0] = para;
			fg.signature[1] = para2;
			break;
		case 'd':
		case 'r':
		case 'f':
		case 'x':
			if( readData() != '[' )
				errMsg( MSG_syntax,msg_x );
			rp++;
			i=0;
			for(;;) {
				dummy = readData();
				if( dummy == ']' )	break;
				if( dummy == '\0' ) {
					skipLine( &rp );
					continue;
				}
				para = 0;
				for( j=0;j<2;j++ ) {
					dummy = readData();
					rp++;
					if( dummy == '%' ) {
						if( j != 0 )
							errMsg( MSG_syntax,msg_x );
						para=readPara( 0,255,-1,msg_x );
						if( readData() != '%' )
							errMsg( MSG_syntax,msg_x );
						rp++;
						break;
					}
					if( toupper(dummy) == 'X' ) {
						dummy=fg.part[nowPart];
						dummy=(dummy != 9)?dummy:-1;
						if(dummy<9)	dummy++;
					}
					else if( toupper(dummy) == 'P' ) {
						dummy = fg.part[nowPart];
					}
					else
						dummy = instr("0123456789ABCDEF",toupper(dummy));
					if( dummy == ERR )
						errMsg( MSG_syntax,msg_x );
					para = para*16+dummy;
				}
				ex[i] = para;
				if( ++i > 255 )
					errMsg( MSG_syntax,msg_x );
			}
			rp++;
			if ( cm2 == 'r' ) {	/* ROLAND EX. CHECK SUM */
				if( i == 255 )
					errMsg(MSG_syntax,msg_x);
				dummy = 0;
				for( j=4;j<i;j++ )	dummy += ex[j];
				dummy = CHECKSUM(dummy);
				ex[i++] = dummy;
			}
			if( cm2 == 'f' )	dummy = F7EXCM;
			else if( cm2 == 'd' )	dummy = DIRECT;
			else dummy = EXCM;
			exclBank( timeCt,dummy,i,ex );
			break;
		case 'm':	/* メモ・歌詞はエクスクルーシブ領域を代用する */
		case 'w':
			if( readData() != '"' )
				errMsg( MSG_syntax,msg_m );
			rp++;
			i = 0;
			for(;;) {
				dummy=*rp;
				if( dummy=='"' )	break;
				if( dummy=='\0' )	{
					skipLine( &rp );
					continue;
				}
				rp++;
				ex[i] = dummy;
				if( ++i > 255 )	break;
			}
			rp++;
			dummy = (cm2=='w')?WORDS:MEMO;
			exclBank( timeCt,dummy,i,ex );
			break;
		case 'h':
			fg.part[nowPart] = readPara( 1,16,0,"'@H'" )-1;
			break;
		case 'k':
			dummy = toupper(readData());
			rp++;
			if(  dummy == 'S' ) {
				para = readPara( 1,16,0,"'@KS'")-1;
				key_spart[para]=1;
			} else 
			if( dummy == 'U' || dummy == 'D' )
				key_shift = readPara(0,24,-1,"'@KU/@KD'") * ((dummy == 'U')?1:-1);
			else	errMsg(MSG_syntax,"Key Shift");
			break;
		default:
			rp--;
			dummy = readPara( 0,128,-1,"program change" );
			para = fg.program[dummy]-1;
			if( para<0 || para >127 )
				errMsg( MSG_illfnc,"program change" );
			evntPut( timeCt,0xC0+fg.part[nowPart],para,0x00 );
			break;
	}
	return;
}
void	mmlSub3( unsigned int t )	/* ”％”コマンド */
{
	int	comNum;
	int	clChg;
	int	mode;
	int	min;
	int	max;
	int	a;
	int	b;
	int	defl;
	int	para = 0;

	comNum = toupper( readData() );

	if( comNum ==ERR || comNum < 'A' || comNum > 'Z' )
		errMsg( MSG_syntax ,"('%')" );
	rp++;

	comNum = comNum - 'A';
	clChg = ctrlMacro[comNum][0];
	mode = ctrlMacro[comNum][1];
	min = ctrlMacro[comNum][2];
	max = ctrlMacro[comNum][3];
	a = ctrlMacro[comNum][4];
	b = ctrlMacro[comNum][5];
	defl = ctrlMacro[comNum][6];

	if( clChg == ERR )
		errMsg( MSG_syntax ,"('%')" );
	if( mode != 1 )
		para = readPara( min ,max ,defl ,"('%' para)" );
	para = para * b + a;
	evntPut( t ,0xB0+fg .part[nowPart] ,clChg ,para );
	return;
}

void	mmlSub4( unsigned short *tc )	/* タイムポインター系 */
{
	int	c;
	int	d;
	unsigned	short	timeCt;

	timeCt = *tc;
	c = tolower( readData() );
	rp++;

	switch( c ) {
		case 's':
			d = readPara( 1 ,128 ,0 ,"MARK" ) - 1;
			setTime[d] = timeCt;
			break;
		case 'm':
			timeCt = ( readPara( 1,256,0,"MEAS" ) -1 ) * fg.signature[0] * 192 / fg.signature[1];
			break;
		case 'i':
			printf( "info.  :Time/%u O/%d L/%d @V/%d Q/%d\n",timeCt,
				dlt_o[nowPart][nowAray] ,dlt_l[nowPart][nowAray],
				dlt_v[nowPart][nowAray] ,dlt_q[nowPart][nowAray]);
			break;
		default:
			rp--;
			d = readPara( 1,128,0,"MARK" ) - 1;
			timeCt = setTime[d];
			break;
	}
	*tc = timeCt;
	return;
}

void	mmlRhythm( unsigned short *tc ,int cm )	/* リズム用サブコマンド */
{
	char	*msg = "Rhythm Track";
	char	buf[64];

	int	num = 1;
	int	len;
	int	step;
	int	dummy;
	int	note;
	int	vel;
	int	i;

	buf[0] = cm;

	for(;;) {
		cm = tolower( readData() );

		if( cm == '!' ) {
			rp++;
			continue;
		}

		if( cm == 'r' )
			errMsg( MSG_syntax ,msg );

		if( cm >= 'a' && cm <= 'z' ) {
			buf[num] = cm;
			num++;
			if( num >= 64 )
				errMsg( MSG_syntax ,msg );
			rp++;
			continue;
		}
		break;
	}

	step = 0;

	for(;;) {
		len = readPara( 1 ,192 ,0 ,msg );
		dummy = 192 / len;

		if( readData()=='.') {
			rp++;
			dummy = dummy * 1.5;
		}
		step += dummy;
		if( readData() == '^' )
			rp++;
		else	break;
	}

	for( i=0;i<num;i++ ) {
		dummy = buf[i] - 'a';
		note = fg.rnote[dummy][0];
		vel = fg.rnote[dummy][1];
		if( note == ERR )
			errMsg( MSG_undmml ,"Rhythm" );
		evntPut( *tc ,0x90+fg .part[nowPart] ,note ,vel );
		evntPut( *tc+1 ,0x80+fg .part[nowPart] ,note ,0x00 );
	}
	*tc += step;
	return;
}

void	mmlComp( void )	/* ＭＭＬコマンド分岐 */
{
	char	buf[32];

	int	cm;
	int	note;
	int	maxSTC = 0;

	unsigned	short	timeCt = 0;
	unsigned	short	sTimeCt[32][2];

	tie_flag = FALSE;
	tie_note = ERR;

	for(;;) {
		if( timeCt > maxTCt )
			maxTCt = timeCt;
		cm = tolower( readData() );
		if( cm == '\0' )
			errMsg( MSG_freerr ,"MML not end" );
		if( cm == '}' ) {
			printf( "Warning:" );
			printf( MSG_misopd ,"';'" );
			break;
		}
		rp++;
		if( cm == ';' )
			break;
		if( fg.rpart == nowPart ) {
			if( cm >= 'a' && cm <= 'z' && cm != 'r' ) {
				mmlRhythm( &timeCt ,cm );
				continue;
			}
		}
		if(( note = instr( "c d ef g a bnr" ,cm )) != ERR ) {
			mmlNote( &timeCt ,note );
			continue;
		}
		if( instr( "voql><[]ut" ,cm ) != ERR ) {
			mmlSub1( timeCt ,cm );
			continue;
		}
		if( cm == '@' ) {
			mmlSub2( timeCt );
			continue;
		}
		if( cm == '%' ) {
			mmlSub3( timeCt );
			continue;
		}
		if( cm == '*' ) {
			mmlSub4( &timeCt );
			continue;
		}
		if( cm == '&' ) {
			tie_flag=TRUE;
			continue;
		}
		if( cm == '_' ) {
			itlMacro( &rp ,&timeCt ,&exclCt ,fg.part[nowPart] );
			continue;
		}
		switch( cm ) {
			case '(':
				if( maxSTC >= 32 )
					errMsg( MSG_toonst ,"Chord" );
				sTimeCt[maxSTC][0] = timeCt;
				sTimeCt[maxSTC][1] = timeCt;
				maxSTC++;
				break;
			case '|':
				if( maxSTC == 0 )
					errMsg( MSG_syntax ,"chord:'|'" );
				if( timeCt > sTimeCt[maxSTC-1][1] )
					sTimeCt[maxSTC-1][1] = timeCt;
				timeCt = sTimeCt[maxSTC-1][0];
				break;
			case ')':
				if( maxSTC == 0 )
					errMsg( MSG_syntax ,"chord:')'" );
				maxSTC--;
				timeCt = sTimeCt[maxSTC][1];
				break;
		}
		if( instr( "(|)" ,cm ) != ERR )
			continue;

		sprintf( buf ,"undefined MML '%c'" ,cm );
		errMsg( MSG_syntax ,buf );
	}
	if( maxSTC != 0 ) {
		printf( "Warning:" );
		printf( MSG_misopd ,"chord" );
	}
	if( timeCt > maxTCt )
		maxTCt = timeCt;
	return;
}

int	mmlLine( FLGDAT fgDummy ,WORKMEM *w ,int orf )	/* ＭＭＬ行メイン */
{
	int	i;

	option = orf;
	evntBuf = w;
	fg = fgDummy;

	if(( rp = Pre_fgets() ) == NULL )
		errMsg( MSG_freerr ,"No MML data" );
	dltSet();

	for( i=0;i<128;i++ )
		setTime[i] = 0;

	for(;;) {
		if( blockName() == FAILURE )
			break;
		for( i=0;i<PART_MAX;i++ )
			dltAray[i] = 0;	/* 配列初期化 */
		evntCt = 0;
		exclCt = 0;
		maxTCt = 0;
		for(;;) {
			if( readData() == '}' ) {
				rp++;
				break;
			}
			getPart();
			mmlComp();
		}
		evntPut( maxTCt ,0xff ,0x06 ,0x00 );

		if(( option & HISPCMP ) != HISPCMP )
			evntSort();
		else	evntSort2();
		if(( option & SHIFTST ) == SHIFTST )
			evntRtch();
		evntWrite();
		if(( option & CHECKMD ) == CHECKMD )
			evntCheck();
		for( i=0;i<exclCt;i++ )
			free( exclPt[i] );
	}
	return( SUCCSESS );
}
