46 , pEventList(eventlist)
50 , mVerbosity(verbosity)
74 if (
mVerbosity >= 3) qDebug() <<
"parsing data file with" << data.length() <<
"bytes";
79 {
reportError(
"Could not find MThd header");
return false; }
81 {
reportError(
"Could not read MThd header");
return false; }
87 if (format > 2) {
reportError(
"Unknown format in MThd header");
return false; }
88 if (format == 2) {
reportError(
"Sequential track format not supported");
return false; }
89 if (tracks == 0) {
reportError(
"No tracks in MIDI file");
return false; }
90 if (tracks >= 256) {
reportError(
"Too many tracks");
return false; }
91 SMPTE = ((division & 0x8000U) != 0);
95 qDebug() <<
"Detected" << (format==0 ?
"single" :
"multi") <<
"channel MIDI data with" 96 << tracks <<
"tracks in" << (
SMPTE ?
"absolute SMPTE-based" :
"tempo-based") <<
"division";
101 qint8 negFrames = division>>8;
102 int frames = abs(negFrames);
103 int ticksPerFrame = division & 0xFFU;
104 double msecPerDeltaTick = 1000.0 / frames / ticksPerFrame;
106 if (
mVerbosity >= 3) qDebug() <<
"SMPTE: frames =" << frames <<
"/ Tick per frame" 107 << ticksPerFrame <<
"/ msec per tick =" << msecPerDeltaTick;
111 int ticksPerQuarterNote = division;
112 if (
mVerbosity >= 3) qDebug() <<
"Midi file is tempo-based with" 113 << ticksPerQuarterNote <<
"ticks per quarter note";
118 for (
int track=0; track<tracks; track++)
121 if (length == 0) {
reportError(
"Could not read MThd header");
return false; }
123 {
reportError(
"Could not read track " + QString::number(track)); }
148 QByteArray pattern(tag.toStdString().c_str());
149 int position = data.indexOf(pattern,
mCursor);
150 if (position<0 or position>=data.size()-8)
return 0;
154 qDebug() <<
"Chunk" << tag <<
"found at position" 155 << position <<
"with" << length <<
"bytes";
174 {
reportError(
"Reading int32 beyond EOF");
return 0; }
175 quint32 byte0 =(quint8)data[
mCursor-1];
176 quint32 byte1 =(quint8)data[
mCursor-2];
177 quint32 byte2 =(quint8)data[
mCursor-3];
178 quint32 byte3 =(quint8)data[
mCursor-4];
179 quint32 result = byte0 + (byte1<<8) + (byte2<<16) + (byte3<<24);
198 {
reportError(
"Reading int16 beyond EOF");
return 0; }
199 quint16 lsb = (quint8)data[
mCursor-1];
200 quint16 msb = (quint8)data[
mCursor-2];
201 quint16 result = lsb + (msb<<8);
227 if (
mCursor > data.size())
return 0;
229 number += (data[
mCursor-1] & 0x7FU);
231 while ((data[
mCursor-1] & 0x80U) != 0);
255 int finalPosition =
mCursor + length;
256 double cumulativeDelta = 0;
261 cumulativeDelta += delta + 0.0000001;
262 if (not
parseEvent(data, track, delta, cumulativeDelta))
return false;
263 if (
mCursor > finalPosition)
return false;
265 while (
mCursor < finalPosition);
287 quint32 delta,
double cumulativeDelta)
290 if (
mCursor >= data.size())
return false;
291 quint8 command = data[
mCursor];
305 enum commandType {undefined,ignore,onebyte,twobytes,sysex,meta};
306 commandType type = undefined;
307 if (command <= 0xBFU) type = twobytes;
308 else if (command <= 0xDFU) type = onebyte;
309 else if (command <= 0xEFU) type = twobytes;
310 else if (command == 0xF0U) type = sysex;
311 else if (command == 0xF2U) type = twobytes;
312 else if (command == 0xF3U) type = onebyte;
313 else if (command == 0xFFU) type = meta;
314 else if (
mVerbosity >= 3) qDebug() <<
"MidiParser: Unrecognized command code =" << command
315 <<
" ... assuming it to be 1-byte dummy code";
321 if (
mVerbosity >= 2) qWarning() <<
"WARNING: MidiParser: Unrecognized command code =" << command;
322 for (
int i=-10; i<10; i++) qDebug() << (quint8)(data[
mCursor+i]);
328 if (
mCursor >= data.size())
return false;
336 if (
mCursor+1 >= data.size())
return false;
343 qDebug() <<
"SYSEX NOT YET IMPLEMENTED";
366 if (
mCursor >= data.size())
return false;
367 quint8 command = data[
mCursor];
370 if (command == 0x2FU and length == 0)
372 if (
mVerbosity >= 3) qDebug() <<
"MidiPlayerParser: End of track detected as meta event";
374 else if (command >= 0x01U and command <= 0x07U)
376 QByteArray sub = data.mid(
mCursor,length);
377 if (
mVerbosity >= 3) qDebug() <<
"MidiPlayerParser: Track info:\n" << sub.data();
379 else if (command == 0x51 and length == 3)
381 quint32 byte0 = (quint8)data[
mCursor+2];
382 quint32 byte1 = (quint8)data[
mCursor+1];
383 quint32 byte2 = (quint8)data[
mCursor];
384 quint32 result = byte0 + (byte1<<8) + (byte2<<16);
386 int millisecondsPerQuarterNote = result / 1000;
387 if (
mVerbosity >= 3) qDebug() <<
"MidiPlayerParser: Tempo:" 388 << millisecondsPerQuarterNote <<
"msec per quarter note";
392 millisecondsPerQuarterNote >> 8,
393 millisecondsPerQuarterNote & 0xFF);
398 if (
mVerbosity >= 4) qDebug() <<
"Skipping meta event";
401 if (
mCursor > data.size())
return false;
quint32 parseInt32(QByteArray &data)
Parse a 32-bit (4-byte) integer and advance the cursor.
bool parseEvent(QByteArray &data, quint8 track, quint32 delta, double cumulativeDelta)
Parse a single event In the Midi specifications three types of events are defined, namely, ordinary Midi events, sysex events, and meta events. This function detects the event type and takes action accordingly.
void reportError(QString msg)
Report an error.
void receiveTempoData(bool smpte, double parameter)
Receive tempo data from the parser.
bool parseTrack(QByteArray &data, int length, quint8 track)
Parse an entire track.
Structure used internally in the MidiPlayer to hold a Midi event.
bool SMPTE
Flag indicating the time format.
int mCursor
Cursor from where the events are being played.
int mVerbosity
Level of verbosity of qDebug() messages.
MidiPlayerParser(MidiPlayer *player, MidiPlayerEventList *eventlist, int verbosity=0)
Constructor.
quint8 mLastMidiCommand
Variable storing the last Midi command.
void reportError(QString msg) const
Report an error back to MidiPlayer.
bool parse(QByteArray &data)
Parse a Midi file and store it in the EventList.
Class managing the EventList in the MidiPlayer.
quint32 parseVariableLengthNumber(QByteArray &data)
Parse a variable-length integer.
int findNextChunk(QByteArray &data, QString tag)
Find the next chunk tag.
void insert(double time, const MidiPlayerEvent &event)
Insert a new event in the time-ordered list of events.
MidiPlayer * pMidiPlayer
Pointer back to the MidiPlayer.
MidiPlayerEventList * pEventList
Pointer to event list held by MidiPlayer.
bool parseMetaEvent(QByteArray &data, quint8 track, double cumulativeDelta)
Parse a meta event and display info in qDebug.
quint16 parseInt16(QByteArray &data)
Parse a 16bit (2-byte) integer and advance the cursor.