annotate midiFileReader/MIDIFileReader.cpp @ 36:5a1b0c6fa1fb

Added class to read in the csv Annotation file, then write out the respective difference between the performed piece as followed here, and the annotation of RWC by Ewert and Muller
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Thu, 15 Dec 2011 02:28:49 +0000
parents b299a65a3ad0
children
rev   line source
andrew@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
andrew@0 2
andrew@0 3 /*
andrew@0 4 This is a modified version of a source file from the
andrew@0 5 Rosegarden MIDI and audio sequencer and notation editor.
andrew@0 6 This file copyright 2000-2010 Richard Bown and Chris Cannam.
andrew@0 7
andrew@0 8 Permission is hereby granted, free of charge, to any person
andrew@0 9 obtaining a copy of this software and associated documentation
andrew@0 10 files (the "Software"), to deal in the Software without
andrew@0 11 restriction, including without limitation the rights to use, copy,
andrew@0 12 modify, merge, publish, distribute, sublicense, and/or sell copies
andrew@0 13 of the Software, and to permit persons to whom the Software is
andrew@0 14 furnished to do so, subject to the following conditions:
andrew@0 15
andrew@0 16 The above copyright notice and this permission notice shall be
andrew@0 17 included in all copies or substantial portions of the Software.
andrew@0 18
andrew@0 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
andrew@0 20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
andrew@0 21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
andrew@0 22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
andrew@0 23 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
andrew@0 24 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
andrew@0 25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
andrew@0 26
andrew@0 27 Except as contained in this notice, the names of the authors
andrew@0 28 shall not be used in advertising or otherwise to promote the sale,
andrew@0 29 use or other dealings in this Software without prior written
andrew@0 30 authorization.
andrew@0 31 */
andrew@0 32
andrew@0 33
andrew@0 34 #include <iostream>
andrew@0 35 #include <fstream>
andrew@0 36 #include <string>
andrew@0 37 #include <cstdio>
andrew@0 38
andrew@0 39 #include "MIDIFileReader.h"
andrew@0 40 #include "MIDIEvent.h"
andrew@0 41
andrew@0 42 #include <sstream>
andrew@0 43
andrew@0 44 using std::string;
andrew@0 45 using std::ifstream;
andrew@0 46 using std::stringstream;
andrew@0 47 using std::cerr;
andrew@0 48 using std::endl;
andrew@0 49 using std::ends;
andrew@0 50 using std::ios;
andrew@0 51
andrew@0 52 using namespace MIDIConstants;
andrew@0 53
andrew@0 54 //#define DEBUG_MIDI_FILE_READER 1
andrew@0 55
andrew@0 56 #define throw_exception(...) do { \
andrew@0 57 char message[128]; \
andrew@0 58 snprintf(message, 128, __VA_ARGS__); \
andrew@0 59 throw MIDIException(std::string(message)); \
andrew@0 60 } while (0)
andrew@0 61
andrew@0 62
andrew@0 63
andrew@0 64 MIDIFileReader::MIDIFileReader(std::string path) :
andrew@0 65 m_timingDivision(0),
andrew@0 66 m_format(MIDI_FILE_BAD_FORMAT),
andrew@0 67 m_numberOfTracks(0),
andrew@0 68 m_trackByteCount(0),
andrew@0 69 m_decrementCount(false),
andrew@0 70 m_path(path),
andrew@0 71 m_midiFile(0),
andrew@0 72 m_fileSize(0)
andrew@0 73 {
andrew@0 74 if (parseFile()) {
andrew@0 75 m_error = "";
andrew@0 76 }
andrew@0 77 }
andrew@0 78
andrew@0 79 MIDIFileReader::~MIDIFileReader()
andrew@0 80 {
andrew@0 81 }
andrew@0 82
andrew@0 83 bool
andrew@0 84 MIDIFileReader::isOK() const
andrew@0 85 {
andrew@0 86 return (m_error == "");
andrew@0 87 }
andrew@0 88
andrew@0 89 std::string
andrew@0 90 MIDIFileReader::getError() const
andrew@0 91 {
andrew@0 92 return m_error;
andrew@0 93 }
andrew@0 94
andrew@0 95 long
andrew@0 96 MIDIFileReader::midiBytesToLong(const string& bytes)
andrew@0 97 {
andrew@0 98 if (bytes.length() != 4) {
andrew@0 99 throw_exception("Wrong length for long data in MIDI stream (%d, should be %d)", (int)bytes.length(), 4);
andrew@0 100 }
andrew@0 101
andrew@0 102 long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
andrew@0 103 ((long)(((MIDIByte)bytes[1]) << 16)) |
andrew@0 104 ((long)(((MIDIByte)bytes[2]) << 8)) |
andrew@0 105 ((long)((MIDIByte)(bytes[3])));
andrew@0 106
andrew@0 107 return longRet;
andrew@0 108 }
andrew@0 109
andrew@0 110 int
andrew@0 111 MIDIFileReader::midiBytesToInt(const string& bytes)
andrew@0 112 {
andrew@0 113 if (bytes.length() != 2) {
andrew@0 114 throw_exception("Wrong length for int data in MIDI stream (%d, should be %d)", (int)bytes.length(), 2);
andrew@0 115 }
andrew@0 116
andrew@0 117 int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
andrew@0 118 ((int)(((MIDIByte)bytes[1])));
andrew@0 119 return(intRet);
andrew@0 120 }
andrew@0 121
andrew@0 122
andrew@0 123 // Gets a single byte from the MIDI byte stream. For each track
andrew@0 124 // section we can read only a specified number of bytes held in
andrew@0 125 // m_trackByteCount.
andrew@0 126 //
andrew@0 127 MIDIByte
andrew@0 128 MIDIFileReader::getMIDIByte()
andrew@0 129 {
andrew@0 130 if (!m_midiFile) {
andrew@0 131 throw_exception("getMIDIByte called but no MIDI file open");
andrew@0 132 }
andrew@0 133
andrew@0 134 if (m_midiFile->eof()) {
andrew@0 135 throw_exception("End of MIDI file encountered while reading");
andrew@0 136 }
andrew@0 137
andrew@0 138 if (m_decrementCount && m_trackByteCount <= 0) {
andrew@0 139 throw_exception("Attempt to get more bytes than expected on Track");
andrew@0 140 }
andrew@0 141
andrew@0 142 char byte;
andrew@0 143 if (m_midiFile->read(&byte, 1)) {
andrew@0 144 --m_trackByteCount;
andrew@0 145 return (MIDIByte)byte;
andrew@0 146 }
andrew@0 147
andrew@0 148 throw_exception("Attempt to read past MIDI file end");
andrew@0 149 }
andrew@0 150
andrew@0 151
andrew@0 152 // Gets a specified number of bytes from the MIDI byte stream. For
andrew@0 153 // each track section we can read only a specified number of bytes
andrew@0 154 // held in m_trackByteCount.
andrew@0 155 //
andrew@0 156 string
andrew@0 157 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
andrew@0 158 {
andrew@0 159 if (!m_midiFile) {
andrew@0 160 throw_exception("getMIDIBytes called but no MIDI file open");
andrew@0 161 }
andrew@0 162
andrew@0 163 if (m_midiFile->eof()) {
andrew@0 164 throw_exception("End of MIDI file encountered while reading");
andrew@0 165 }
andrew@0 166
andrew@0 167 if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
andrew@0 168 throw_exception("Attempt to get more bytes than available on Track (%lu, only have %ld)", numberOfBytes, m_trackByteCount);
andrew@0 169 }
andrew@0 170
andrew@0 171 string stringRet;
andrew@0 172 char fileMIDIByte;
andrew@0 173
andrew@0 174 while (stringRet.length() < numberOfBytes &&
andrew@0 175 m_midiFile->read(&fileMIDIByte, 1)) {
andrew@0 176 stringRet += fileMIDIByte;
andrew@0 177 }
andrew@0 178
andrew@0 179 // if we've reached the end of file without fulfilling the
andrew@0 180 // quota then panic as our parsing has performed incorrectly
andrew@0 181 //
andrew@0 182 if (stringRet.length() < numberOfBytes) {
andrew@0 183 stringRet = "";
andrew@0 184 throw_exception("Attempt to read past MIDI file end");
andrew@0 185 }
andrew@0 186
andrew@0 187 // decrement the byte count
andrew@0 188 if (m_decrementCount)
andrew@0 189 m_trackByteCount -= stringRet.length();
andrew@0 190
andrew@0 191 return stringRet;
andrew@0 192 }
andrew@0 193
andrew@0 194
andrew@0 195 // Get a long number of variable length from the MIDI byte stream.
andrew@0 196 //
andrew@0 197 long
andrew@0 198 MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
andrew@0 199 {
andrew@0 200 if (!m_midiFile) {
andrew@0 201 throw_exception("getNumberFromMIDIBytes called but no MIDI file open");
andrew@0 202 }
andrew@0 203
andrew@0 204 long longRet = 0;
andrew@0 205 MIDIByte midiByte;
andrew@0 206
andrew@0 207 if (firstByte >= 0) {
andrew@0 208 midiByte = (MIDIByte)firstByte;
andrew@0 209 } else if (m_midiFile->eof()) {
andrew@0 210 return longRet;
andrew@0 211 } else {
andrew@0 212 midiByte = getMIDIByte();
andrew@0 213 }
andrew@0 214
andrew@0 215 longRet = midiByte;
andrew@0 216 if (midiByte & 0x80) {
andrew@0 217 longRet &= 0x7F;
andrew@0 218 do {
andrew@0 219 midiByte = getMIDIByte();
andrew@0 220 longRet = (longRet << 7) + (midiByte & 0x7F);
andrew@0 221 } while (!m_midiFile->eof() && (midiByte & 0x80));
andrew@0 222 }
andrew@0 223
andrew@0 224 return longRet;
andrew@0 225 }
andrew@0 226
andrew@0 227
andrew@0 228 // Seek to the next track in the midi file and set the number
andrew@0 229 // of bytes to be read in the counter m_trackByteCount.
andrew@0 230 //
andrew@0 231 bool
andrew@0 232 MIDIFileReader::skipToNextTrack()
andrew@0 233 {
andrew@0 234 if (!m_midiFile) {
andrew@0 235 throw_exception("skipToNextTrack called but no MIDI file open");
andrew@0 236 }
andrew@0 237
andrew@0 238 string buffer, buffer2;
andrew@0 239 m_trackByteCount = -1;
andrew@0 240 m_decrementCount = false;
andrew@0 241
andrew@0 242 while (!m_midiFile->eof() && (m_decrementCount == false)) {
andrew@0 243 buffer = getMIDIBytes(4);
andrew@0 244 if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
andrew@0 245 m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
andrew@0 246 m_decrementCount = true;
andrew@0 247 }
andrew@0 248 }
andrew@0 249
andrew@0 250 if (m_trackByteCount == -1) { // we haven't found a track
andrew@0 251 return false;
andrew@0 252 } else {
andrew@0 253 return true;
andrew@0 254 }
andrew@0 255 }
andrew@0 256
andrew@0 257
andrew@0 258 // Read in a MIDI file. The parsing process throws exceptions back up
andrew@0 259 // here if we run into trouble which we can then pass back out to
andrew@0 260 // whoever called us using a nice bool.
andrew@0 261 //
andrew@0 262 bool
andrew@0 263 MIDIFileReader::parseFile()
andrew@0 264 {
andrew@0 265 m_error = "";
andrew@0 266
andrew@0 267 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 268 cerr << "MIDIFileReader::open() : fileName = " << m_path.toStdString() << endl;
andrew@0 269 #endif
andrew@0 270
andrew@0 271 // Open the file
andrew@0 272 m_midiFile = new ifstream(m_path.c_str(), ios::in | ios::binary);
andrew@0 273
andrew@0 274 if (!*m_midiFile) {
andrew@0 275 m_error = "File not found or not readable.";
andrew@0 276 m_format = MIDI_FILE_BAD_FORMAT;
andrew@0 277 delete m_midiFile;
andrew@0 278 m_midiFile = 0;
andrew@0 279 return false;
andrew@0 280 }
andrew@0 281
andrew@0 282 bool retval = false;
andrew@0 283
andrew@0 284 try {
andrew@0 285
andrew@0 286 // Set file size so we can count it off
andrew@0 287 //
andrew@0 288 m_midiFile->seekg(0, ios::end);
andrew@0 289 m_fileSize = m_midiFile->tellg();
andrew@0 290 m_midiFile->seekg(0, ios::beg);
andrew@0 291
andrew@0 292 // Parse the MIDI header first. The first 14 bytes of the file.
andrew@0 293 if (!parseHeader(getMIDIBytes(14))) {
andrew@0 294 m_format = MIDI_FILE_BAD_FORMAT;
andrew@0 295 m_error = "Not a MIDI file.";
andrew@0 296 goto done;
andrew@0 297 }
andrew@0 298
andrew@0 299 for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
andrew@0 300
andrew@0 301 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 302 cerr << "Parsing Track " << j << endl;
andrew@0 303 #endif
andrew@0 304
andrew@0 305 if (!skipToNextTrack()) {
andrew@0 306 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 307 cerr << "Couldn't find Track " << j << endl;
andrew@0 308 #endif
andrew@0 309 m_error = "File corrupted or in non-standard format?";
andrew@0 310 m_format = MIDI_FILE_BAD_FORMAT;
andrew@0 311 goto done;
andrew@0 312 }
andrew@0 313
andrew@0 314 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 315 cerr << "Track has " << m_trackByteCount << " bytes" << endl;
andrew@0 316 #endif
andrew@0 317
andrew@0 318 // Run through the events taking them into our internal
andrew@0 319 // representation.
andrew@0 320 if (!parseTrack(j)) {
andrew@0 321 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 322 cerr << "Track " << j << " parsing failed" << endl;
andrew@0 323 #endif
andrew@0 324 m_error = "File corrupted or in non-standard format?";
andrew@0 325 m_format = MIDI_FILE_BAD_FORMAT;
andrew@0 326 goto done;
andrew@0 327 }
andrew@0 328 }
andrew@0 329
andrew@0 330 retval = true;
andrew@0 331
andrew@0 332 } catch (MIDIException e) {
andrew@0 333
andrew@0 334 cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
andrew@0 335 m_error = e.what();
andrew@0 336 }
andrew@0 337
andrew@0 338 done:
andrew@0 339 m_midiFile->close();
andrew@0 340 delete m_midiFile;
andrew@0 341
andrew@0 342 for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
andrew@0 343
andrew@0 344 // Convert the deltaTime to an absolute time since the track
andrew@0 345 // start. The addTime method returns the sum of the current
andrew@0 346 // MIDI Event delta time plus the argument.
andrew@0 347
andrew@0 348 unsigned long acc = 0;
andrew@0 349
andrew@0 350 for (MIDITrack::iterator i = m_midiComposition[track].begin();
andrew@0 351 i != m_midiComposition[track].end(); ++i) {
andrew@0 352 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 353 cerr << "converting delta time " << i->getTime();
andrew@0 354 #endif
andrew@0 355 acc = i->addTime(acc);
andrew@0 356 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 357 cerr << " to " << i->getTime() << endl;
andrew@0 358 #endif
andrew@0 359 }
andrew@0 360
andrew@0 361 consolidateNoteOffEvents(track);
andrew@0 362 }
andrew@0 363
andrew@0 364 return retval;
andrew@0 365 }
andrew@0 366
andrew@0 367 // Parse and ensure the MIDI Header is legitimate
andrew@0 368 //
andrew@0 369 bool
andrew@0 370 MIDIFileReader::parseHeader(const string &midiHeader)
andrew@0 371 {
andrew@0 372 if (midiHeader.size() < 14) {
andrew@0 373 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 374 cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl;
andrew@0 375 #endif
andrew@0 376 return false;
andrew@0 377 }
andrew@0 378
andrew@0 379 if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
andrew@0 380 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 381 cerr << "MIDIFileReader::parseHeader()"
andrew@0 382 << "- file header not found or malformed"
andrew@0 383 << endl;
andrew@0 384 #endif
andrew@0 385 return false;
andrew@0 386 }
andrew@0 387
andrew@0 388 if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
andrew@0 389 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 390 cerr << "MIDIFileReader::parseHeader()"
andrew@0 391 << " - header length incorrect"
andrew@0 392 << endl;
andrew@0 393 #endif
andrew@0 394 return false;
andrew@0 395 }
andrew@0 396
andrew@0 397 m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
andrew@0 398 m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
andrew@0 399 m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
andrew@0 400
andrew@0 401 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 402 if (m_timingDivision < 0) {
andrew@0 403 cerr << "MIDIFileReader::parseHeader()"
andrew@0 404 << " - file uses SMPTE timing"
andrew@0 405 << endl;
andrew@0 406 }
andrew@0 407 #endif
andrew@0 408
andrew@0 409 return true;
andrew@0 410 }
andrew@0 411
andrew@0 412 // Extract the contents from a MIDI file track and places it into
andrew@0 413 // our local map of MIDI events.
andrew@0 414 //
andrew@0 415 bool
andrew@0 416 MIDIFileReader::parseTrack(unsigned int trackNum)
andrew@0 417 {
andrew@0 418 MIDIByte midiByte, metaEventCode, data1, data2;
andrew@0 419 MIDIByte eventCode = 0x80;
andrew@0 420 string metaMessage;
andrew@0 421 unsigned int messageLength;
andrew@0 422 unsigned long deltaTime;
andrew@0 423 unsigned long accumulatedTime = 0;
andrew@0 424
andrew@0 425 // Remember the last non-meta status byte (-1 if we haven't seen one)
andrew@0 426 int runningStatus = -1;
andrew@0 427
andrew@0 428 while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
andrew@0 429
andrew@0 430 if (eventCode < 0x80) {
andrew@0 431 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 432 cerr << "WARNING: Invalid event code " << eventCode
andrew@0 433 << " in MIDI file" << endl;
andrew@0 434 #endif
andrew@0 435 throw_exception("Invalid event code %d found", int(eventCode));
andrew@0 436 }
andrew@0 437
andrew@0 438 deltaTime = getNumberFromMIDIBytes();
andrew@0 439
andrew@0 440 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 441 cerr << "read delta time " << deltaTime << endl;
andrew@0 442 #endif
andrew@0 443
andrew@0 444 // Get a single byte
andrew@0 445 midiByte = getMIDIByte();
andrew@0 446
andrew@0 447 if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
andrew@0 448
andrew@0 449 if (runningStatus < 0) {
andrew@0 450 throw_exception("Running status used for first event in track");
andrew@0 451 }
andrew@0 452
andrew@0 453 eventCode = (MIDIByte)runningStatus;
andrew@0 454 data1 = midiByte;
andrew@0 455
andrew@0 456 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 457 cerr << "using running status (byte " << int(midiByte) << " found)" << endl;
andrew@0 458 #endif
andrew@0 459 } else {
andrew@0 460 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 461 cerr << "have new event code " << int(midiByte) << endl;
andrew@0 462 #endif
andrew@0 463 eventCode = midiByte;
andrew@0 464 data1 = getMIDIByte();
andrew@0 465 }
andrew@0 466
andrew@0 467 if (eventCode == MIDI_FILE_META_EVENT) {
andrew@0 468
andrew@0 469 metaEventCode = data1;
andrew@0 470 messageLength = getNumberFromMIDIBytes();
andrew@0 471
andrew@0 472 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 473 cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
andrew@0 474 #endif
andrew@0 475 metaMessage = getMIDIBytes(messageLength);
andrew@0 476
andrew@0 477 accumulatedTime += deltaTime;
andrew@0 478
andrew@0 479 MIDIEvent e(deltaTime,
andrew@0 480 MIDI_FILE_META_EVENT,
andrew@0 481 metaEventCode,
andrew@0 482 metaMessage);
andrew@0 483
andrew@0 484 m_midiComposition[trackNum].push_back(e);
andrew@0 485
andrew@0 486 if (metaEventCode == MIDI_TRACK_NAME) {
andrew@0 487 m_trackNames[trackNum] = metaMessage.c_str();
andrew@0 488 }
andrew@0 489
andrew@0 490 } else { // non-meta events
andrew@0 491
andrew@0 492 runningStatus = eventCode;
andrew@0 493
andrew@0 494 int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
andrew@0 495
andrew@0 496 accumulatedTime += deltaTime;
andrew@0 497
andrew@0 498 switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
andrew@0 499
andrew@0 500 case MIDI_NOTE_ON:
andrew@0 501 case MIDI_NOTE_OFF:
andrew@0 502 case MIDI_POLY_AFTERTOUCH:
andrew@0 503 case MIDI_CTRL_CHANGE:
andrew@0 504 data2 = getMIDIByte();
andrew@0 505
andrew@0 506 {
andrew@0 507 // create and store our event
andrew@0 508 MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
andrew@0 509
andrew@0 510 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 511 cerr << "MIDI event for channel " << channel << " (track "
andrew@0 512 << trackNum << ") with delta time " << deltaTime << endl;
andrew@0 513 #endif
andrew@0 514
andrew@0 515 m_midiComposition[trackNum].push_back(midiEvent);
andrew@0 516 }
andrew@0 517 break;
andrew@0 518
andrew@0 519 case MIDI_PITCH_BEND:
andrew@0 520 data2 = getMIDIByte();
andrew@0 521
andrew@0 522 {
andrew@0 523 // create and store our event
andrew@0 524 MIDIEvent midiEvent(deltaTime, eventCode, data1, data2);
andrew@0 525 m_midiComposition[trackNum].push_back(midiEvent);
andrew@0 526 }
andrew@0 527 break;
andrew@0 528
andrew@0 529 case MIDI_PROG_CHANGE:
andrew@0 530 case MIDI_CHNL_AFTERTOUCH:
andrew@0 531
andrew@0 532 {
andrew@0 533 // create and store our event
andrew@0 534 MIDIEvent midiEvent(deltaTime, eventCode, data1);
andrew@0 535 m_midiComposition[trackNum].push_back(midiEvent);
andrew@0 536 }
andrew@0 537 break;
andrew@0 538
andrew@0 539 case MIDI_SYSTEM_EXCLUSIVE:
andrew@0 540 messageLength = getNumberFromMIDIBytes(data1);
andrew@0 541
andrew@0 542 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 543 cerr << "SysEx of " << messageLength << " bytes found" << endl;
andrew@0 544 #endif
andrew@0 545
andrew@0 546 metaMessage= getMIDIBytes(messageLength);
andrew@0 547
andrew@0 548 if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
andrew@0 549 MIDI_END_OF_EXCLUSIVE)
andrew@0 550 {
andrew@0 551 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 552 cerr << "MIDIFileReader::parseTrack() - "
andrew@0 553 << "malformed or unsupported SysEx type"
andrew@0 554 << endl;
andrew@0 555 #endif
andrew@0 556 continue;
andrew@0 557 }
andrew@0 558
andrew@0 559 // chop off the EOX
andrew@0 560 // length fixed by Pedro Lopez-Cabanillas (20030523)
andrew@0 561 //
andrew@0 562 metaMessage = metaMessage.substr(0, metaMessage.length()-1);
andrew@0 563
andrew@0 564 {
andrew@0 565 MIDIEvent midiEvent(deltaTime,
andrew@0 566 MIDI_SYSTEM_EXCLUSIVE,
andrew@0 567 metaMessage);
andrew@0 568 m_midiComposition[trackNum].push_back(midiEvent);
andrew@0 569 }
andrew@0 570 break;
andrew@0 571
andrew@0 572 case MIDI_END_OF_EXCLUSIVE:
andrew@0 573 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 574 cerr << "MIDIFileReader::parseTrack() - "
andrew@0 575 << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl;
andrew@0 576 #endif
andrew@0 577 break;
andrew@0 578
andrew@0 579 default:
andrew@0 580 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 581 cerr << "MIDIFileReader::parseTrack()"
andrew@0 582 << " - Unsupported MIDI Event Code: "
andrew@0 583 << (int)eventCode << endl;
andrew@0 584 #endif
andrew@0 585 break;
andrew@0 586 }
andrew@0 587 }
andrew@0 588 }
andrew@0 589
andrew@0 590 return true;
andrew@0 591 }
andrew@0 592
andrew@0 593 // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
andrew@0 594 // reading them and modifying their relevant NOTE ONs. Return true
andrew@0 595 // if there are some notes in this track.
andrew@0 596 //
andrew@0 597 bool
andrew@0 598 MIDIFileReader::consolidateNoteOffEvents(unsigned int track)
andrew@0 599 {
andrew@0 600 bool notesOnTrack = false;
andrew@0 601 bool noteOffFound;
andrew@0 602
andrew@0 603 MIDITrack &t = m_midiComposition[track];
andrew@0 604
andrew@0 605 for (MIDITrack::iterator i = t.begin(); i != t.end(); ++i) {
andrew@0 606
andrew@0 607 if (i->getMessageType() == MIDI_NOTE_ON && i->getVelocity() > 0) {
andrew@0 608
andrew@0 609 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 610 cerr << "Looking for note-offs for note at " << i->getTime() << " (pitch " << (int)i->getPitch() << ")" << endl;
andrew@0 611 #endif
andrew@0 612
andrew@0 613 notesOnTrack = true;
andrew@0 614 noteOffFound = false;
andrew@0 615
andrew@0 616 for (MIDITrack::iterator j = i; j != t.end(); ++j) {
andrew@0 617
andrew@0 618 if ((j->getChannelNumber() == i->getChannelNumber()) &&
andrew@0 619 (j->getPitch() == i->getPitch()) &&
andrew@0 620 (j->getMessageType() == MIDI_NOTE_OFF ||
andrew@0 621 (j->getMessageType() == MIDI_NOTE_ON &&
andrew@0 622 j->getVelocity() == 0x00))) {
andrew@0 623
andrew@0 624 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 625 cerr << "Found note-off at " << j->getTime() << " for note at " << i->getTime() << endl;
andrew@0 626 #endif
andrew@0 627
andrew@0 628 i->setDuration(j->getTime() - i->getTime());
andrew@0 629
andrew@0 630 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 631 cerr << "Duration is now " << i->getDuration() << endl;
andrew@0 632 #endif
andrew@0 633
andrew@0 634 t.erase(j);
andrew@0 635
andrew@0 636 noteOffFound = true;
andrew@0 637 break;
andrew@0 638 }
andrew@0 639 }
andrew@0 640
andrew@0 641 // If no matching NOTE OFF has been found then set
andrew@0 642 // Event duration to length of track
andrew@0 643 //
andrew@0 644 if (!noteOffFound) {
andrew@0 645 #ifdef DEBUG_MIDI_FILE_READER
andrew@0 646 cerr << "Failed to find note-off for note at " << i->getTime() << endl;
andrew@0 647 #endif
andrew@0 648 MIDITrack::iterator j = t.end();
andrew@0 649 --j;
andrew@0 650 i->setDuration(j->getTime() - i->getTime());
andrew@0 651 }
andrew@0 652 }
andrew@0 653 }
andrew@0 654
andrew@0 655 return notesOnTrack;
andrew@0 656 }
andrew@0 657
andrew@0 658 MIDIComposition
andrew@0 659 MIDIFileReader::load() const
andrew@0 660 {
andrew@0 661 return m_midiComposition;
andrew@0 662 }
andrew@0 663
andrew@0 664