annotate midiFileReader/MIDIFileReader.cpp @ 52:13194a9dca77 tip

Added exporting of image and text data
author Andrew N Robertson <andrew.robertson@eecs.qmul.ac.uk>
date Tue, 17 Jul 2012 22:13:10 +0100
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