/* Functions to be called while processing the MIDI file. */
int (*Mf_getc)() = NULLFUNC;
int (*Mf_error)() = NULLFUNC;
int (*Mf_header)() = NULLFUNC;
int (*Mf_trackstart)() = NULLFUNC;
int (*Mf_trackend)() = NULLFUNC;
int (*Mf_noteon)() = NULLFUNC;
int (*Mf_noteoff)() = NULLFUNC;
int (*Mf_pressure)() = NULLFUNC;
int (*Mf_parameter)() = NULLFUNC;
int (*Mf_pitchbend)() = NULLFUNC;
int (*Mf_program)() = NULLFUNC;
int (*Mf_chanpressure)() = NULLFUNC;
int (*Mf_sysex)() = NULLFUNC;
int (*Mf_arbitrary)() = NULLFUNC;
int (*Mf_metamisc)() = NULLFUNC;
int (*Mf_seqnum)() = NULLFUNC;
int (*Mf_eot)() = NULLFUNC;
int (*Mf_smpte)() = NULLFUNC;
int (*Mf_tempo)() = NULLFUNC;
int (*Mf_timesig)() = NULLFUNC;
int (*Mf_keysig)() = NULLFUNC;
int (*Mf_seqspecific)() = NULLFUNC;
int (*Mf_text)() = NULLFUNC;
int Mf_nomerge = 0; /* 1 => continue'ed system exclusives are */
/* not collapsed. */
long Mf_currtime = 0L; /* current time in delta-time units */
/* private stuff */
static long Mf_toberead = 0L;
static long Mf_numbyteswritten = 0L;
static long readvarinum();
static long read32bit();
static long to32bit();
static int read16bit();
static int to16bit();
static char *msg();
static int format, ntrks, division;
static
readmt(s) /* read through the "MThd" or "MTrk" header string */
char *s;
{
int n = 0;
char *p = s;
int c;
while ( n++<4 && (c=(*Mf_getc)()) != EOF ) {
if ( c != *p++ ) {
char buff[32];
(void) strcpy(buff,"expecting ");
(void) strcat(buff,s);
mferror(buff);
}
}
return(c);
}
static
egetc() /* read a single character and abort on EOF */
{
int c = (*Mf_getc)();
if ( c == EOF )
mferror("premature EOF");
Mf_toberead--;
return(c);
}
static
readheader() /* read a header chunk */
{
if ( readmt("MThd") == EOF )
return;
if ( Mf_header )
(*Mf_header)(format,ntrks,division);
/* flush any extra stuff, in case the length of header is not 6 */
while ( Mf_toberead > 0L )
(void) egetc();
}
static
readtrack() /* read a track chunk */
{
/* This array is indexed by the high half of a status byte. It's */
/* value is either the number of bytes needed (1 or 2) for a channel */
/* message, or 0 (meaning it's not a channel message). */
static int chantype[] = {
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
};
long lookfor;
int c, c1, type;
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
int running = 0; /* 1 when running status used */
int status = 0; /* status value (e.g. 0x90==note-on) */
int needed;
if ( readmt("MTrk") == EOF )
return(0);
Mf_toberead = read32bit();
Mf_currtime = 0L;
if ( Mf_trackstart )
(*Mf_trackstart)();
while ( Mf_toberead > 0L ) {
Mf_currtime += readvarinum(); /* delta time */
c = egetc();
if ( sysexcontinue && c != 0xf7 )
mferror("didn't find expected continuation of a sysex");
if ( (c & 0x80) == 0 ) { /* running status? */
if ( status == 0 )
mferror("unexpected running status");
running = 1;
}
else {
status = c;
running = 0;
}
needed = chantype[ (status>>4) & 0xf ];
if ( needed ) { /* ie. is it a channel message? */
switch ( type ) {
case 0x00:
if ( Mf_seqnum )
(*Mf_seqnum)(to16bit(m[0],m[1]));
break;
case 0x01: /* Text event */
case 0x02: /* Copyright notice */
case 0x03: /* Sequence/Track name */
case 0x04: /* Instrument name */
case 0x05: /* Lyric */
case 0x06: /* Marker */
case 0x07: /* Cue point */
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
/* These are all text events */
if ( Mf_text )
(*Mf_text)(type,leng,m);
break;
case 0x2f: /* End of Track */
if ( Mf_eot )
(*Mf_eot)();
break;
case 0x51: /* Set tempo */
if ( Mf_tempo )
(*Mf_tempo)(to32bit(0,m[0],m[1],m[2]));
break;
case 0x54:
if ( Mf_smpte )
(*Mf_smpte)(m[0],m[1],m[2],m[3],m[4]);
break;
case 0x58:
if ( Mf_timesig )
(*Mf_timesig)(m[0],m[1],m[2],m[3]);
break;
case 0x59:
if ( Mf_keysig )
(*Mf_keysig)(m[0],m[1]);
break;
case 0x7f:
if ( Mf_seqspecific )
(*Mf_seqspecific)(leng,m);
break;
default:
if ( Mf_metamisc )
(*Mf_metamisc)(type,leng,m);
}
}
static
sysex()
{
if ( Mf_sysex )
(*Mf_sysex)(msgleng(),msg());
}
static
chanmessage(status,c1,c2)
int status;
int c1, c2;
{
int chan = status & 0xf;
switch ( status & 0xf0 ) {
case 0x80:
if ( Mf_noteoff )
(*Mf_noteoff)(chan,c1,c2);
break;
case 0x90:
if ( Mf_noteon )
(*Mf_noteon)(chan,c1,c2);
break;
case 0xa0:
if ( Mf_pressure )
(*Mf_pressure)(chan,c1,c2);
break;
case 0xb0:
if ( Mf_parameter )
(*Mf_parameter)(chan,c1,c2);
break;
case 0xe0:
if ( Mf_pitchbend )
(*Mf_pitchbend)(chan,c1,c2);
break;
case 0xc0:
if ( Mf_program )
(*Mf_program)(chan,c1);
break;
case 0xd0:
if ( Mf_chanpressure )
(*Mf_chanpressure)(chan,c1);
break;
}
}
/* readvarinum - read a varying-length number, and return the */
/* number of characters it took. */
static long
readvarinum()
{
long value;
int c;
c = egetc();
value = (long)c;
if ( c & 0x80 ) {
value &= 0x7FL;
do {
c = egetc();
value = (value << 7) + (long)(c & 0x7f);
} while (c & 0x80);
}
return (value);
}
static long
to32bit(c1,c2,c3,c4)
{
long value = 0L;
value = (c1 & 0xff);
value = (value<<8) + (c2 & 0xff);
value = (value<<8) + (c3 & 0xff);
value = (value<<8) + (c4 & 0xff);
return (value);
}
/* The code below allows collection of a system exclusive message of */
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
#define MSGINCREMENT 128
static char *Msgbuff = NULL; /* message buffer */
static int Msgsize = 0; /* Size of currently allocated Msg */
static int Msgindex = 0; /* index of next available location in Msg */
static
msginit()
{
Msgindex = 0;
}
static char *
msg()
{
return(Msgbuff);
}
static
msgleng()
{
return(Msgindex);
}
static
msgadd(c)
int c;
{
/* If necessary, allocate larger message buffer. */
if ( Msgindex >= Msgsize )
biggermsg();
Msgbuff[Msgindex++] = c;
}