bool MidiParser::parseTrack(MidiEventList &midiEventList) { char header[8]; forever { if (!readFile(header, 8)) return false; if (memcmp(header, trackID, 4) == 0) { break; } else { qDebug() << "MidiParser: Wrong MIDI track signature, skipping unknown data chunk"; quint32 dataLen = qFromBigEndian<quint32>((uchar *)&header[4]); if (file.seek(file.pos() + dataLen)) { qDebug() << "MidiParser: Error in data chunk"; return false; } } } quint32 trackLen = qFromBigEndian<quint32>((uchar *)&header[4]); char *trackData = new char[trackLen]; if (!readFile(trackData, trackLen)) { delete trackData; return false; } // Reserve memory for MIDI events, approx. 3 bytes per event midiEventList.reserve(trackLen / 3); qDebug() << "MidiParser: Memory reservation" << trackLen / 3; // Parsing actual MIDI events unsigned int runningStatus = 0; uchar *data = (uchar *)trackData; uchar sysexBuffer[MAX_SYSEX_LENGTH]; while(data < (uchar *)trackData + trackLen) { SynthTimestamp time = parseVarLenInt(data); quint32 message = 0; if (*data & 0x80) { // It's normal status byte if ((*data & 0xF0) == 0xF0) { // It's a special event if (*data == 0xF0) { // It's a sysex event sysexBuffer[0] = *(data++); quint32 sysexLength = parseVarLenInt(data); if (MAX_SYSEX_LENGTH <= sysexLength) { qDebug() << "MidiParser: too long sysex encountered:" << sysexLength; data += sysexLength; continue; } memcpy(&sysexBuffer[1], data, sysexLength); data += sysexLength; midiEventList.newMidiEvent().assignSysex(time, sysexBuffer, sysexLength + 1); continue; } else if (*data == 0xF7) { qDebug() << "MidiParser: Fragmented sysex, unsupported"; data++; quint32 len = parseVarLenInt(data); data += len; } else if (*(data++) == 0xFF) { uint metaType = *(data++); quint32 len = parseVarLenInt(data); if (metaType == 0x2F) { qDebug() << "MidiParser: End-of-track Meta-event"; runningStatus = 0x2F; break; } else if (metaType == 0x51) { uint newTempo = qFromBigEndian<quint32>(data) >> 8; midiEventList.newMidiEvent().assignSetTempoMessage(time, newTempo); qDebug() << "MidiParser: Meta-event: Set tempo:" << newTempo; data += len; continue; } else { qDebug() << "MidiParser: Meta-event code" << metaType << "unsupported"; } data += len; } else { qDebug() << "MidiParser: Unsupported event" << *(data++); } if (time > 0) { // The event is unsupported. Nevertheless, assign a special marker event to retain timing information qDebug() << "MidiParser: Adding sync event for" << time << "divisions"; midiEventList.newMidiEvent().assignSyncMessage(time); } continue; } else if ((*data & 0xE0) == 0xC0) { // It's a short message with one data byte message = qFromLittleEndian<quint16>(data); data += 2; } else { // It's a short message with two data bytes message = qFromLittleEndian<quint32>(data) & 0xFFFFFF; data += 3; } runningStatus = message & 0xFF; } else { // Handle running status if ((runningStatus & 0x80) == 0) { qDebug() << "MidiParser: First MIDI event must has status byte"; data++; continue; } if ((runningStatus & 0xE0) == 0xC0) { // It's a short message with one data byte message = runningStatus | ((quint32)*data << 8); data++; } else { // It's a short message with two data bytes message = runningStatus | ((quint32)qFromLittleEndian<quint16>(data) << 8); data += 2; } } midiEventList.newMidiEvent().assignShortMessage(time, message); } if (runningStatus != 0x2F) { qDebug() << "MidiParser: End-of-track Meta-event isn't the last event, file is probably corrupted."; } delete trackData; qDebug() << "MidiParser: Parsed" << midiEventList.count() << "MIDI events"; return true; }