bool MD_MFTrack::getNextEvent(MD_MIDIFile *mf, uint16_t tickCount) // track_event = <time:v> + [<midi_event> | <meta_event> | <sysex_event>] { uint32_t deltaT; // is there anything to process? if (_endOfTrack) return(false); // move the file pointer to where we left off mf->_fd.seekSet(_startOffset+_currOffset); // Work out new total elapsed ticks - include the overshoot from // last event. _elapsedTicks += tickCount; // Get the DeltaT from the file in order to see if enough ticks have // passed for the event to be active. deltaT = readVarLen(&mf->_fd); // If not enough ticks, just return without saving the file pointer and // we will go back to the same spot next time. if (_elapsedTicks < deltaT) return(false); // Adjust the total elapsed time to the error against actual DeltaT to avoid // accumulation of errors, as we only check for _elapsedTicks being >= ticks, // giving positive biased errors every time. _elapsedTicks -= deltaT; DUMP("\ndT: ", deltaT); DUMP(" + ", _elapsedTicks); DUMPS("\t"); parseEvent(mf); // remember the offset for next time _currOffset = mf->_fd.curPosition() - _startOffset; // catch end of track when there is no META event _endOfTrack = _endOfTrack || (_currOffset >= _length); if (_endOfTrack) DUMPS(" - OUT OF TRACK"); return(true); }
int event () { int i; char b[80]; int data; int event; int velocity; unsigned long length; char c; unsigned long deltime; int channel; deltime = readVarLen(); event = *fp++; event &= 0xff; data = *fp++; data &= 0xff; if (deltime) count++; if (brief) { printf("\nd-t %3d %3x ", count, deltime); } else printf("d-time %3d %3x ", count, deltime); // DATA BYTE - first bit not set if (!(event & 0x80)) { /* if first bit not set it is a data byte */ *fp--; /* here we have many of the same events strung together */ *fp--; length = old_length; printf(" continued data bytes "); while (length--) { data = (*fp++)&0xFF; printf(" %x ", data); } printf("\n"); event = old_event; /* where only the first is specified */ } // CHANNEL EVENTS ? 8 -> E if (((event & 0xf0) >= 0x80) && ((event & 0xf0) <= 0xE0)) { channel = event & 0x0f; /* if (event & 0x0f) (fprintf(stderr, " non zero channel set %x\n", event & 0x0f )); */ if (!brief) printf ("channel %d ", channel); event &= 0xf0; old_event = event; } else old_event = event; switch (event) { case 0x80: velocity = *fp++; if (brief) printf("OFF %2x ", data); else printf("Note OFF note %2x %s velocity %x \n", data, note_to_text(data), velocity); on[data]= 0; break; case 0x90: velocity = *fp++; if (brief) printf("On %2x ", data); else printf("Note ON note %2x %s velocity %x \n", data, note_to_text(data), velocity); on[data]= 1; break; case 0xa0: /* AFTERTOUCH */ printf("Polyphonic Key Pressure data %d length %d\n", data, length); break; case 0xb0: /* MODE Message */ length = *fp++&0xFF; printf("Controller Change %d %d\n", data, length); break; case 0xc0: printf("Program Change %x\n", data ); break; case 0xd0: /* AFTERTOUCH */ printf("Channel Key Pressure %x\n", data ); break; case 0xe0: /* PITCH BEND */ { int f; f=*fp++&0xFF; printf("Pitch Bend %x %x ", data, f ); /* extreme hack */ /* for (i=0; i< (f >> 4); i++ ) printf(" %x ", *fp++); */ printf("\n"); } break; case 0xf0: printf("Sysex event %x\n", data); if (data & 0x80) fprintf(stderr, "Sysex event, length not handled\n"); else { length = data; for (i=0; i< data; i++ ) printf(" %x ", *fp++ & 0xff); printf("\n"); } break; case 0xff: if (!(data & 0xf0)) length = readVarLen(fp); else length = *fp++; switch (data) { case 0: printf("Sequence Number"); { int seq; if (length != 2) fprintf(stderr, "Sequence Number length %x ERROR\n", length); for (i=0; i< length; i++ ) b[i] = *fp++; seq = ((b[0]&0xff)<<8) + (b[1]&0xff); printf(" %x\n", seq); } break; case 1: printf("Text Event length %d\n", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 2: printf("Copyright length %d\n", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 3: printf("Sequence/Track Name length %d ", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 4: printf("Instrument Name length %d ", length); while (length--) printf("%c", *fp++); printf("\n"); /* fprintf(stderr, "NOT Implimented Yet\n"); */ break; case 5: printf("Lyric length %d ", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 6: printf("Marker length %d ", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 7: printf("Cue Point length %d ", length); while (length--) printf("%c", *fp++); printf("\n"); break; case 0x20: printf("MIDI Channel Prefix channel "); /* *fp++; */ printf("%x \n", *fp++); break; case 0x21: printf("MIDI Port (obsolete) length %x port number ", length); while (length--) printf("%x", *fp++); printf("\n"); break; case 0x51: { int i=0; int tmp=0; printf("Set Tempo microseconds/quarter note "); if (length != 3) fprintf(stderr, "Set Tempo length ERROR\n"); { int v; while (length--) { v = *fp++&0xff; tmp += (v << (length * 8 )); /* printf("%02x ", v); */ } printf(" %x %d \n", tmp, tmp); } } break; case 0x53: printf("Unknown Meta Event 0x53 %d ", length); fprintf(stderr, "NOT Implimented Yet\n"); break; case 0x54: printf("SMPTE Offset length %x ", length); while (length--) printf("%x ", *fp++); printf("\n"); break; case 0x58: { int num, denom, clocks, bb; printf("Time Signature "); if (length != 4) fprintf(stderr, "Time Signature length ERROR %d\n", length); num = *fp++&0xff; denom = *fp++&0xff; clocks = *fp++&0xff; bb = *fp++&0xff; printf( "%d/%d %x clocks per metronome tick, %x 32nd notes per 24 midi clocks\n", num, (int)pow(2.0, (double)denom), clocks, bb); } break; case 0x59: { int sf, mi; printf("Key Signature "); if (length != 2) fprintf(stderr, "Key Signature length ERROR\n"); sf = *fp++&0xff; mi = *fp++&0xff; printf( "num sharps or flats %d major or minor %x\n", sf, mi); } break; case 0x7f: printf("Sequencer Specific Meta-Event "); { int v; while (length--) { v = *fp++&0xff; printf(" %x ", v); } printf("\n"); } break; case 0x2f: printf("End of Track\n", length); return (0); break; } break; default: fprintf(stderr, "UNHANDLED EVENT data %x length %x event %x\n", data, length, event); exit(0); break; } return (1); }
int parseEvents(uint8_t * data, uint32_t size) { //for debugging chunks int total = 0; int pos = 0; //remember last event type uint8_t lastEvent; while(pos < size) { int unknown = 0; uint32_t delta = readVarLen(data + pos, &pos); uint8_t type = *(data + pos) & 0xF0; uint8_t chan = *(data+pos) & 0x0F; //check if the MSB of the type is set if(type & 0x80)//then we have a new event { lastEvent = type | chan; pos++; printf("==Parsing MIDI event==\n"); printf("delta = %d, pos = 0x%X\n", delta, pos); printf("Type = 0x%02X, Channel = 0x%02X\n", type, chan); } else //same event { type = lastEvent & 0xF0; chan = lastEvent & 0x0F; printf("Same event detected: Type = 0x%02X, Channel = 0x%02X\n", type, chan); } switch (type) { uint8_t param1; uint8_t param2; uint8_t m_type; uint32_t m_len; uint8_t * m_data; case 0x80: //TODO NOTE OFF event param1 = *(uint8_t *)(data + pos++); param2 = *(uint8_t *)(data + pos++); printf("Note OFF: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta); if(!chan && !firstNote) { if(delta) { printf("%d/%d\n", sumNote, numNote); int note = sumNote / numNote; //printf("play(%d, %d)\n", note, delta); printf("play(%d, %d)\n", //(int)(220 * pow(2, (note-57)/12.0 )), ((1.0/(220 * pow(2, (note-57)/12.0 )))/(64.0/20000000.0)) - 1, (int)( (0.0029585799 * delta) * (220 * pow(2, (note-57)/12.0 ) ) ) ); sumNote -= param1; numNote--; } else { sumNote -= param1; numNote--; } } break; case 0x90: //TODO NOTE ON event param1 = *(uint8_t *)(data + pos++); param2 = *(uint8_t *)(data + pos++); printf("Note ON: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta); _d(data + pos - 3, 3); if(!chan) { //printf("Note ON: Note=%d, Velocity=%d, delta=%d\n", param1, param2, delta); if(firstNote) { sumNote += param1; numNote++; firstNote = 0; } else { sumNote += param1; numNote++; } } break; case 0xA0: param1 = *(uint8_t *)(data + pos++); param2 = *(uint8_t *)(data + pos++); printf("Note Aftertouch: Note=%d, Amount=%d\n", param1, param2); _d(data + pos - 3, 3); break; case 0xB0: param1 = *(uint8_t *)(data + pos++); param2 = *(uint8_t *)(data + pos++); printf("Controller Event: Type=%d, Value=%d\n", param1, param2); break; case 0xC0: param1 = *(uint8_t *)(data + pos++); printf("Program Change Event: Program Numbe=%d\n", param1); break; case 0xF0://SysEx event //SysEx parsed below META //case 0xFF://META event if(chan == 0x0F)//META event { m_type = *(data + pos); pos++; m_len = readVarLen(data + pos, &pos); //now each meta event switch (m_type) { case 0x01: //experimental //if(strlen(data+pos) > m_len) // m_len = strlen(data+pos)+1; // m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Text [%s]\n", m_data); _d(m_data, m_len); free(m_data); break; case 0x02: m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Copyright notice [%s]\n", m_data); free(m_data); break; case 0x03: m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Track Name [%s]\n", m_data); free(m_data); break; case 0x07: m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Cue Point [%s]\n", m_data); free(m_data); break; case 0x2F: printf("\tMETA: End of Track.\n"); break; case 0x51: //TODO: this may be important m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: MPQN = %d\n", ( *(uint8_t *)(m_data) << 16) + ( *(uint8_t *)(m_data+1) << 8) + *(uint8_t *)(m_data+2) ); free(m_data); break; case 0x58: //TODO time signature m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Time Signature: Number=%d, Denom=%d, Metro=%d 32nds=%d\n", *(uint8_t *)(m_data), *(uint8_t *)(m_data+1), *(uint8_t *)(m_data+2), *(uint8_t *)(m_data+3) ); free(m_data); break; case 0x59: //TODO key signature m_data = (uint8_t *)malloc(m_len); memcpy(m_data, data+pos, m_len); pos += m_len; printf("\tMETA: Key:%d, Scale:%d\n", *(uint8_t *)(m_data), *(uint8_t *)(m_data+1) ); free(m_data); break; default: unknown = 1; printf("\tUnknown Meta-Event... [%02X]\n", m_type); } break;//end META event } //SysEx event m_len = readVarLen(data + pos, &pos); m_data = (uint8_t *)malloc(m_len); pos += m_len; printf("\tSysEx: read %d bytes... skipping.\n", m_len); free(m_data); break; default: unknown = 1; printf("Unknown Event... [0x%02X]\n", type); } if(unknown) break; } }
// // Got a lot of good ideas from pykaraoke by Kelvin Lawson ([email protected]). Thanks! // void CKaraokeLyricsTextKAR::parseMIDI() { m_midiOffset = 0; // Bytes 0-4: header unsigned int header = readDword(); // If we get MS RIFF header, skip it if ( header == 0x52494646 ) { setPos( currentPos() + 16 ); header = readDword(); } // MIDI header if ( header != 0x4D546864 ) throw( "Not a MIDI file" ); // Bytes 5-8: header length unsigned int header_length = readDword(); // Bytes 9-10: format unsigned short format = readWord(); if ( format > 2 ) throw( "Unsupported format" ); // Bytes 11-12: tracks unsigned short tracks = readWord(); // Bytes 13-14: divisious unsigned short divisions = readWord(); if ( divisions > 32768 ) throw( "Unsupported division" ); // Number of tracks is always 1 if format is 0 if ( format == 0 ) tracks = 1; // Parsed per-channel info std::vector<MidiLyrics> lyrics; std::vector<MidiTempo> tempos; std::vector<MidiChannelInfo> channels; channels.resize( tracks ); // Set up default tempo MidiTempo te; te.clocks = 0; te.tempo = 500000; tempos.push_back( te ); int preferred_lyrics_track = -1; int lastchannel = 0; int laststatus = 0; unsigned int firstNoteClocks = 1000000000; // arbitrary large value unsigned int next_line_flag = 0; // Point to first byte after MIDI header setPos( 8 + header_length ); // Parse all tracks for ( int track = 0; track < tracks; track++ ) { char tempbuf[1024]; unsigned int clocks = 0; channels[track].total_lyrics = 0; channels[track].total_lyrics_space = 0; // Skip malformed files if ( readDword() != 0x4D54726B ) throw( "Malformed track header" ); // Next track position int tracklen = readDword(); unsigned int nexttrackstart = tracklen + currentPos(); // Parse track until end of track event while ( currentPos() < nexttrackstart ) { // field length clocks += readVarLen(); unsigned char msgtype = readByte(); // // Meta event // if ( msgtype == 0xFF ) { unsigned char metatype = readByte(); unsigned int metalength = readVarLen(); if ( metatype == 3 ) { // Track title metatype if ( metalength >= sizeof( tempbuf ) ) throw( "Meta event too long" ); readData( tempbuf, metalength ); tempbuf[metalength] = '\0'; if ( !strcmp( tempbuf, "Words" ) ) preferred_lyrics_track = track; } else if ( metatype == 5 || metatype == 1 ) { // Lyrics metatype if ( metalength >= sizeof( tempbuf ) ) throw( "Meta event too long" ); readData( tempbuf, metalength ); tempbuf[metalength] = '\0'; if ( (tempbuf[0] == '@' && tempbuf[1] >= 'A' && tempbuf[1] <= 'Z') || strstr( tempbuf, " SYX" ) || strstr( tempbuf, "Track-" ) || strstr( tempbuf, "%-" ) || strstr( tempbuf, "%+" ) ) { // Keywords if ( tempbuf[0] == '@' && tempbuf[1] == 'T' && strlen( tempbuf + 2 ) > 0 ) { if ( m_songName.empty() ) m_songName = convertText( tempbuf + 2 ); else { if ( !m_artist.empty() ) m_artist += "[CR]"; m_artist += convertText( tempbuf + 2 ); } } } else { MidiLyrics lyric; lyric.clocks = clocks; lyric.track = track; lyric.flags = next_line_flag; if ( tempbuf[0] == '\\' ) { lyric.flags = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH; lyric.text = convertText( tempbuf + 1 ); } else if ( tempbuf[0] == '/' ) { lyric.flags = CKaraokeLyricsText::LYRICS_NEW_LINE; lyric.text = convertText( tempbuf + 1 ); } else if ( tempbuf[1] == '\0' && (tempbuf[0] == '\n' || tempbuf[0] == '\r' ) ) { // An empty line; do not add it but set the flag if ( next_line_flag == CKaraokeLyricsText::LYRICS_NEW_LINE ) next_line_flag = CKaraokeLyricsText::LYRICS_NEW_PARAGRAPH; else next_line_flag = CKaraokeLyricsText::LYRICS_NEW_LINE; } else { next_line_flag = (strchr(tempbuf, '\n') || strchr(tempbuf, '\r')) ? CKaraokeLyricsText::LYRICS_NEW_LINE : CKaraokeLyricsText::LYRICS_NONE; lyric.text = convertText( tempbuf ); } lyrics.push_back( lyric ); // Calculate the number of spaces in current syllable for ( unsigned int j = 0; j < metalength; j++ ) { channels[ track ].total_lyrics++; if ( tempbuf[j] == 0x20 ) channels[ track ].total_lyrics_space++; } } } else if ( metatype == 0x51 ) { // Set tempo event if ( metalength != 3 ) throw( "Invalid tempo" ); unsigned char a1 = readByte(); unsigned char a2 = readByte(); unsigned char a3 = readByte(); unsigned int tempo = (a1 << 16) | (a2 << 8) | a3; // MIDI spec says tempo could only be on the first track... // but some MIDI editors still put it on second. Shouldn't break anything anyway, but let's see //if ( track != 0 ) // throw( "Invalid tempo track" ); // Check tempo array. If previous tempo has higher clocks, abort. if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks > clocks ) throw( "Invalid tempo" ); // If previous tempo has the same clocks value, override it. Otherwise add new. if ( tempos.size() > 0 && tempos[ tempos.size() - 1 ].clocks == clocks ) tempos[ tempos.size() - 1 ].tempo = tempo; else { MidiTempo mt; mt.clocks = clocks; mt.tempo = tempo; tempos.push_back( mt ); } } else { // Skip the event completely setPos( currentPos() + metalength ); } } else if ( msgtype== 0xF0 || msgtype == 0xF7 ) { // SysEx event unsigned int length = readVarLen(); setPos( currentPos() + length ); } else { // Regular MIDI event if ( msgtype & 0x80 ) { // Status byte laststatus = ( msgtype >> 4) & 0x07; lastchannel = msgtype & 0x0F; if ( laststatus != 0x07 ) msgtype = readByte() & 0x7F; } switch ( laststatus ) { case 0: // Note off readByte(); break; case 1: // Note on if ( (readByte() & 0x7F) != 0 ) // this would be in fact Note off { // Remember the time the first note played if ( firstNoteClocks > clocks ) firstNoteClocks = clocks; } break; case 2: // Key Pressure case 3: // Control change case 6: // Pitch wheel readByte(); break; case 4: // Program change case 5: // Channel pressure break; default: // case 7: Ignore this event if ( (lastchannel & 0x0F) == 2 ) // Sys Com Song Position Pntr readWord(); else if ( (lastchannel & 0x0F) == 3 ) // Sys Com Song Select(Song #) readByte(); break; } } } }
void MD_MFTrack::parseEvent(MD_MIDIFile *mf) // process the event from the physical file { uint8_t eType; uint32_t eLen, mLen; sysex_event sev; // used for sysex callback function // now we have to process this event eType = mf->_fd.read(); switch (eType) { // ---------------------------- MIDI // midi_event = any MIDI channel message, including running status // Midi events (status bytes 0x8n - 0xEn) The standard Channel MIDI messages, where 'n' is the MIDI channel (0 - 15). // This status byte will be followed by 1 or 2 data bytes, as is usual for the particular MIDI message. // Any valid Channel MIDI message can be included in a MIDI file. case 0x80 ... 0xBf: // MIDI message with 2 parameters case 0xe0 ... 0xef: _mev.size = 3; _mev.data[0] = eType; _mev.channel = _mev.data[0] & 0xf; // mask off the channel _mev.data[0] = _mev.data[0] & 0xf0; // just the command byte _mev.data[1] = mf->_fd.read(); _mev.data[2] = mf->_fd.read(); DUMP("[MID2] Ch: ", _mev.channel); DUMPX(" Data: ", _mev.data[0]); DUMPX(" ", _mev.data[1]); DUMPX(" ", _mev.data[2]); #if !DUMP_DATA if (mf->_midiHandler != NULL) (mf->_midiHandler)(&_mev); #endif // !DUMP_DATA break; case 0xc0 ... 0xdf: // MIDI message with 1 parameter _mev.size = 2; _mev.data[0] = eType; _mev.channel = _mev.data[0] & 0xf; // mask off the channel _mev.data[0] = _mev.data[0] & 0xf0; // just the command byte _mev.data[1] = mf->_fd.read(); DUMP("[MID1] Ch: ", _mev.channel); DUMPX(" Data: ", _mev.data[0]); DUMPX(" ", _mev.data[1]); #if !DUMP_DATA if (mf->_midiHandler != NULL) (mf->_midiHandler)(&_mev); #endif break; case 0x00 ... 0x7f: // MIDI run on message { // If the first (status) byte is less than 128 (0x80), this implies that MIDI // running status is in effect, and that this byte is actually the first data byte // (the status carrying over from the previous MIDI event). // This can only be the case if the immediately previous event was also a MIDI event, // ie SysEx and Meta events clear running status. This means that the _mev structure // should contain the info from the previous message in the structure's channel member // and data[0] (for the MIDI command). // Hence start saving the data at byte data[1] with the byte we have just read (eType) // and use the size member to determine how large the message is (ie, same as before). _mev.data[1] = eType; for (uint8_t i = 2; i < _mev.size; i++) { _mev.data[i] = mf->_fd.read(); // next byte } DUMP("[MID+] Ch: ", _mev.channel); DUMPS(" Data:"); for (uint8_t i = 0; i<_mev.size; i++) { DUMPX(" ", _mev.data[i]); } #if !DUMP_DATA if (mf->_midiHandler != NULL) (mf->_midiHandler)(&_mev); #endif } break; // ---------------------------- SYSEX case 0xf0: // sysex_event = 0xF0 + <len:1> + <data_bytes> + 0xF7 case 0xf7: // sysex_event = 0xF7 + <len:1> + <data_bytes> + 0xF7 { uint8_t c, i; sysex_event sev; // collect all the bytes until the 0xf7 - boundaries are included in the message sev.track = _trackId; sev.data[0] = eType; sev.size = mf->_fd.read(); // The length parameter includes the 0xF7 but not the start boundary. // However, it may be bigger than our buffer will allow us to store. sev.size = (sev.size > BUF_SIZE(sev.data) - 2 ? BUF_SIZE(sev.data) - 2 : sev.size + 1); for (i = 1; (i < sev.size) && (c != 0xf7); i++) { c = mf->_fd.read(); // next char sev.data[i] = c; } // check if we had an overflow if (c != 0xf7) { while ((c = mf->_fd.read()) != 0xf7) ; // skip read all data sev.data[sev.size] = 0xf7; // terminate properly - data is probably nuked anyway } DUMPS("[SYSX] Data:"); for (uint8_t i = 0; i<sev.size; i++) { DUMPX(" ", sev.data[i]); } #if !DUMP_DATA if (mf->_sysexHandler != NULL) (mf->_sysexHandler)(&sev); #endif } break; // ---------------------------- META case 0xff: // meta_event = 0xFF + <meta_type:1> + <length:v> + <event_data_bytes> eType = mf->_fd.read(); mLen = readVarLen(&mf->_fd); DUMPX("[META] Type: 0x", eType); DUMP("\tLen: ", mLen); DUMPS("\t"); switch (eType) { case 0x2f: // End of track _endOfTrack = true; DUMPS("END OF TRACK"); break; case 0x51: // set Tempo - really the microseconds per tick mf->setMicrosecondPerQuarterNote(readMultiByte(&mf->_fd, MB_TRYTE)); DUMP("SET TEMPO to ", mf->getTickTime()); DUMP(" us/tick or ", mf->getTempo()); DUMPS(" beats/min"); break; case 0x58: // time signature mf->setTimeSignature(mf->_fd.read(), (1 << mf->_fd.read())); // denominator is 2^n mf->_fd.seekCur(mLen-2); DUMP("SET TIME SIGNATURE to ", mf->getTimeSignature()>>8); DUMP("/", mf->getTimeSignature()&0xf); break; #if SHOW_UNUSED_META case 0x0: // Sequence Number DUMP("SEQUENCE NUMBER ", readMultiByte(&mf->_fd, MB_WORD)); break; case 0x1: // Text DUMPS("TEXT "); for (int i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x2: // Copyright Notice DUMPS("COPYRIGHT "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x3: // Sequence or Track Name DUMPS("SEQ/TRK NAME "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x4: // Instrument Name DUMPS("INSTRUMENT "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x5: // Lyric DUMPS("LYRIC "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x6: // Marker DUMPS("MARKER "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x7: // Cue Point DUMPS("CUE POINT "); for (uint8_t i=0; i<mLen; i++) DUMP("", (char)mf->_fd.read()); break; case 0x20:// Channel Prefix DUMP("CHANNEL PREFIX ", readMultiByte(&mf->_fd, MB_BYTE)); break; case 0x21:// Port Prefix DUMP("PORT PREFIX ", readMultiByte(&mf->_fd, MB_BYTE)); break; case 0x54:// SMPTE Offset DUMPS("SMPTE OFFSET"); for (uint8_t i=0; i<mLen; i++) { DUMP(" ", mf->_fd.read()); } break; case 0x59:// Key Signature DUMPS("KEY SIGNATURE"); for (uint8_t i=0; i<mLen; i++) { DUMP(" ", mf->_fd.read()); } break; case 0x7F: // Sequencer Specific Metadata DUMPS("SEQ SPECIFIC"); for (uint8_t i=0; i<mLen; i++) { DUMPX(" ", mf->_fd.read()); } break; #endif // SHOW_UNUSED_META default: mf->_fd.seekCur(mLen); DUMPS("IGNORED"); } break; // ---------------------------- UNKNOWN default: // stop playing this track as we cannot identify the eType _endOfTrack = true; DUMPX("[UKNOWN 0x", eType); DUMPS("] Track aborted"); } }