static void MidiTracktoStream(void) { DWORD atime,len; byte event,type,a,b,c; byte laststatus,lastchan; CurrentPos=0; laststatus=0; lastchan=0; atime=0; for (;;) { if (CurrentPos>=mididata.track[CurrentTrack].len) return; atime+=getvl(); event=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; if(event==0xF0 || event == 0xF7) /* SysEx event */ { len=getvl(); CurrentPos+=len; } else if(event==0xFF) /* Meta event */ { type=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; len=getvl(); switch(type) { case 0x2f: return; case 0x51: /* Tempo */ a=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; c=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; AddEvent(atime, MEVT_TEMPO, c, b, a); break; default: CurrentPos+=len; break; } } else { a=event; if (a & 0x80) /* status byte */ { lastchan=a & 0x0F; laststatus=(a>>4) & 0x07; a=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; a &= 0x7F; } switch(laststatus) { case 0: /* Note off */ b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b &= 0x7F; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, b); break; case 1: /* Note on */ b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b &= 0x7F; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, b); break; case 2: /* Key Pressure */ b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b &= 0x7F; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, b); break; case 3: /* Control change */ b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b &= 0x7F; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, b); break; case 4: /* Program change */ a &= 0x7f; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, 0); break; case 5: /* Channel pressure */ a &= 0x7f; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, 0); break; case 6: /* Pitch wheel */ b=mididata.track[CurrentTrack].data[CurrentPos]; CurrentPos++; b &= 0x7F; AddEvent(atime, MEVT_SHORTMSG, (byte)((laststatus<<4)+lastchan+0x80), a, b); break; default: break; } } }
/* Read a MIDI event, returning a freshly allocated element that can be linked to the event list */ static MidiEventList *read_midi_event(struct md *d) { static uint8 laststatus, lastchan; static uint8 nrpn=0, rpn_msb[MAXCHAN], rpn_lsb[MAXCHAN]; /* one per channel */ uint8 me, type, a,b,c; int32 len, i; MidiEventList *newev; for (;;) { #ifdef tplus if ( (len=getvl(d)) < 0) return 0; d->at+= len; #else d->at+=getvl(d); #endif #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } me = (uint8)i; #else if (fread(&me,1,1,d->fp)!=1) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, sys_errlist[errno]); return 0; } #endif if(me==0xF0 || me == 0xF7) /* SysEx event */ { int32 sret; uint8 sysa=0, sysb=0, syschan=0; #ifdef tplus if ( (len=getvl(d)) < 0) return 0; #else len=getvl(d); #endif sret=sysex(len, &syschan, &sysa, &sysb, d); if (sret) { MIDIEVENT(d->at, sret, syschan, sysa, sysb); } } else if(me==0xFF) /* Meta event */ { #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } type = (uint8)i; #else fread(&type,1,1,d->fp); #endif #ifdef tplus if ( (len=getvl(d)) < 0) return 0; #else len=getvl(d); #endif if (type>0 && type<16) { static const char *label[]={ "text: ", "text: ", "Copyright: ", "track: ", "instrument: ", "lyric: ", "marker: ", "cue point: "}; dumpstring(len, label[(type>7) ? 0 : type], type, d); } else switch(type) { case 0x21: /* MIDI port number */ if(len == 1) { fread(&d->midi_port_number,1,1,d->fp); if(d->midi_port_number == EOF) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } d->midi_port_number &= 0x0f; if (d->midi_port_number) ctl->cmsg(CMSG_INFO, VERB_VERBOSE, "(MIDI port number %d)", d->midi_port_number); d->midi_port_number &= 0x03; } else skip(d->fp, len); break; case 0x2F: /* End of Track */ return MAGIC_EOT; case 0x51: /* Tempo */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } a = (uint8)i; if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } c = (uint8)i; #else fread(&a,1,1,d->fp); fread(&b,1,1,d->fp); fread(&c,1,1,d->fp); #endif MIDIEVENT(d->at, ME_TEMPO, c, a, b); default: ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(Meta event type 0x%02x, length %ld)", type, len); skip(d->fp, len); break; } } else { a=me; if (a & 0x80) /* status byte */ { lastchan = MERGE_CHANNEL_PORT(a & 0x0F); laststatus=(a>>4) & 0x07; #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } a = (uint8)i; #else fread(&a, 1,1, d->fp); #endif a &= 0x7F; } switch(laststatus) { case 0: /* Note off */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; #else fread(&b, 1,1, d->fp); #endif b &= 0x7F; /*MIDIEVENT(d->at, ME_NOTEOFF, lastchan, a,b);*/ MIDIEVENT(d->at, ME_NOTEON, lastchan, a,0); case 1: /* Note on */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; #else fread(&b, 1,1, d->fp); #endif b &= 0x7F; MIDIEVENT(d->at, ME_NOTEON, lastchan, a,b); if (d->curr_track == d->curr_title_track && d->track_info > 1) d->title[0] = '\0'; case 2: /* Key Pressure */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; #else fread(&b, 1,1, d->fp); #endif b &= 0x7F; MIDIEVENT(d->at, ME_KEYPRESSURE, lastchan, a, b); case 3: /* Control change */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; #else fread(&b, 1,1, d->fp); #endif b &= 0x7F; { int control=255; switch(a) { #ifdef tplus case 1: control=ME_MODULATION_WHEEL; break; case 5: control=ME_PORTAMENTO_TIME_MSB; break; case 37: control=ME_PORTAMENTO_TIME_LSB; break; case 65: control=ME_PORTAMENTO; break; #endif case 7: control=ME_MAINVOLUME; break; case 10: control=ME_PAN; break; case 11: control=ME_EXPRESSION; break; #ifdef tplus case 64: control=ME_SUSTAIN; b = (b >= 64); break; #else case 64: control=ME_SUSTAIN; break; #endif case 71: control=ME_HARMONICCONTENT; break; case 72: control=ME_RELEASETIME; break; case 73: control=ME_ATTACKTIME; break; case 74: control=ME_BRIGHTNESS; break; case 91: control=ME_REVERBERATION; break; case 93: control=ME_CHORUSDEPTH; break; #ifdef CHANNEL_EFFECT case 94: control=ME_CELESTE; break; ctl->cmsg(CMSG_INFO, VERB_NORMAL, "CELESTE"); case 95: control=ME_PHASER; break; ctl->cmsg(CMSG_INFO, VERB_NORMAL, "PHASER"); #endif case 120: control=ME_ALL_SOUNDS_OFF; break; case 121: control=ME_RESET_CONTROLLERS; break; case 123: control=ME_ALL_NOTES_OFF; break; /* These should be the SCC-1 tone bank switch commands. I don't know why there are two, or why the latter only allows switching to bank 0. Also, some MIDI files use 0 as some sort of continuous controller. This will cause lots of warnings about undefined tone banks. */ case 0: if (d->XG_System_On) control=ME_TONE_KIT; else control=ME_TONE_BANK; break; case 32: if (d->XG_System_On) control=ME_TONE_BANK; break; case 100: nrpn=0; rpn_msb[lastchan]=b; break; case 101: nrpn=0; rpn_lsb[lastchan]=b; break; case 98: nrpn=1; rpn_lsb[lastchan]=b; break; case 99: nrpn=1; rpn_msb[lastchan]=b; break; case 6: if (nrpn) { if (rpn_msb[lastchan]==1) switch (rpn_lsb[lastchan]) { #ifdef tplus case 0x08: control=ME_VIBRATO_RATE; break; case 0x09: control=ME_VIBRATO_DEPTH; break; case 0x0a: control=ME_VIBRATO_DELAY; break; #endif case 0x20: control=ME_BRIGHTNESS; break; case 0x21: control=ME_HARMONICCONTENT; break; /* case 0x63: envelope attack rate case 0x64: envelope decay rate case 0x66: envelope release rate */ } else switch (rpn_msb[lastchan]) { /* case 0x14: filter cutoff frequency case 0x15: filter resonance case 0x16: envelope attack rate case 0x17: envelope decay rate case 0x18: pitch coarse case 0x19: pitch fine */ case 0x1a: d->drumvolume[lastchan][0x7f & rpn_lsb[lastchan]] = b; break; case 0x1c: if (!b) b=(int) (127.0*rand()/(RAND_MAX)); d->drumpanpot[lastchan][0x7f & rpn_lsb[lastchan]] = b; break; case 0x1d: d->drumreverberation[lastchan][0x7f & rpn_lsb[lastchan]] = b; break; case 0x1e: d->drumchorusdepth[lastchan][0x7f & rpn_lsb[lastchan]] = b; break; /* case 0x1f: variation send level */ } ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(Data entry (MSB) for NRPN %02x,%02x: %ld)", rpn_msb[lastchan], rpn_lsb[lastchan], b); break; } switch((rpn_msb[lastchan]<<8) | rpn_lsb[lastchan]) { case 0x0000: /* Pitch bend sensitivity */ control=ME_PITCH_SENS; break; #ifdef tplus case 0x0001: control=ME_FINE_TUNING; break; case 0x0002: control=ME_COARSE_TUNING; break; #endif case 0x7F7F: /* RPN reset */ /* reset pitch bend sensitivity to 2 */ MIDIEVENT(d->at, ME_PITCH_SENS, lastchan, 2, 0); default: ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(Data entry (MSB) for RPN %02x,%02x: %ld)", rpn_msb[lastchan], rpn_lsb[lastchan], b); break; } break; default: ctl->cmsg(CMSG_INFO, VERB_DEBUG, "(Control %d: %d)", a, b); break; } if (control != 255) { MIDIEVENT(d->at, control, lastchan, b, 0); } } break; case 4: /* Program change */ a &= 0x7f; MIDIEVENT(d->at, ME_PROGRAM, lastchan, a, 0); #ifdef tplus case 5: /* Channel pressure */ MIDIEVENT(d->at, ME_CHANNEL_PRESSURE, lastchan, a, 0); #else case 5: /* Channel pressure - NOT IMPLEMENTED */ break; #endif case 6: /* Pitch wheel */ #ifdef tplus if((i = tf_getc(d)) == EOF) { if(errno) ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "\"%s\": read_midi_event: %s", d->midi_name, strerror(errno)); else ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Warning: \"%s\": Short midi file.", d->midi_name); return 0; } b = (uint8)i; #else fread(&b, 1,1, d->fp); #endif b &= 0x7F; MIDIEVENT(d->at, ME_PITCHWHEEL, lastchan, a, b); case 7: break; default: ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "*** Can't happen: status 0x%02X, channel 0x%02X", laststatus, lastchan); break; } } }