andrew@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ andrew@0: andrew@0: /* andrew@0: This is a modified version of a source file from the andrew@0: Rosegarden MIDI and audio sequencer and notation editor. andrew@0: This file copyright 2000-2010 Richard Bown and Chris Cannam. andrew@0: andrew@0: Permission is hereby granted, free of charge, to any person andrew@0: obtaining a copy of this software and associated documentation andrew@0: files (the "Software"), to deal in the Software without andrew@0: restriction, including without limitation the rights to use, copy, andrew@0: modify, merge, publish, distribute, sublicense, and/or sell copies andrew@0: of the Software, and to permit persons to whom the Software is andrew@0: furnished to do so, subject to the following conditions: andrew@0: andrew@0: The above copyright notice and this permission notice shall be andrew@0: included in all copies or substantial portions of the Software. andrew@0: andrew@0: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, andrew@0: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF andrew@0: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND andrew@0: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR andrew@0: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF andrew@0: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION andrew@0: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. andrew@0: andrew@0: Except as contained in this notice, the names of the authors andrew@0: shall not be used in advertising or otherwise to promote the sale, andrew@0: use or other dealings in this Software without prior written andrew@0: authorization. andrew@0: */ andrew@0: andrew@0: andrew@0: #include andrew@0: #include andrew@0: #include andrew@0: #include andrew@0: andrew@0: #include "MIDIFileReader.h" andrew@0: #include "MIDIEvent.h" andrew@0: andrew@0: #include andrew@0: andrew@0: using std::string; andrew@0: using std::ifstream; andrew@0: using std::stringstream; andrew@0: using std::cerr; andrew@0: using std::endl; andrew@0: using std::ends; andrew@0: using std::ios; andrew@0: andrew@0: using namespace MIDIConstants; andrew@0: andrew@0: //#define DEBUG_MIDI_FILE_READER 1 andrew@0: andrew@0: #define throw_exception(...) do { \ andrew@0: char message[128]; \ andrew@0: snprintf(message, 128, __VA_ARGS__); \ andrew@0: throw MIDIException(std::string(message)); \ andrew@0: } while (0) andrew@0: andrew@0: andrew@0: andrew@0: MIDIFileReader::MIDIFileReader(std::string path) : andrew@0: m_timingDivision(0), andrew@0: m_format(MIDI_FILE_BAD_FORMAT), andrew@0: m_numberOfTracks(0), andrew@0: m_trackByteCount(0), andrew@0: m_decrementCount(false), andrew@0: m_path(path), andrew@0: m_midiFile(0), andrew@0: m_fileSize(0) andrew@0: { andrew@0: if (parseFile()) { andrew@0: m_error = ""; andrew@0: } andrew@0: } andrew@0: andrew@0: MIDIFileReader::~MIDIFileReader() andrew@0: { andrew@0: } andrew@0: andrew@0: bool andrew@0: MIDIFileReader::isOK() const andrew@0: { andrew@0: return (m_error == ""); andrew@0: } andrew@0: andrew@0: std::string andrew@0: MIDIFileReader::getError() const andrew@0: { andrew@0: return m_error; andrew@0: } andrew@0: andrew@0: long andrew@0: MIDIFileReader::midiBytesToLong(const string& bytes) andrew@0: { andrew@0: if (bytes.length() != 4) { andrew@0: throw_exception("Wrong length for long data in MIDI stream (%d, should be %d)", (int)bytes.length(), 4); andrew@0: } andrew@0: andrew@0: long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) | andrew@0: ((long)(((MIDIByte)bytes[1]) << 16)) | andrew@0: ((long)(((MIDIByte)bytes[2]) << 8)) | andrew@0: ((long)((MIDIByte)(bytes[3]))); andrew@0: andrew@0: return longRet; andrew@0: } andrew@0: andrew@0: int andrew@0: MIDIFileReader::midiBytesToInt(const string& bytes) andrew@0: { andrew@0: if (bytes.length() != 2) { andrew@0: throw_exception("Wrong length for int data in MIDI stream (%d, should be %d)", (int)bytes.length(), 2); andrew@0: } andrew@0: andrew@0: int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) | andrew@0: ((int)(((MIDIByte)bytes[1]))); andrew@0: return(intRet); andrew@0: } andrew@0: andrew@0: andrew@0: // Gets a single byte from the MIDI byte stream. For each track andrew@0: // section we can read only a specified number of bytes held in andrew@0: // m_trackByteCount. andrew@0: // andrew@0: MIDIByte andrew@0: MIDIFileReader::getMIDIByte() andrew@0: { andrew@0: if (!m_midiFile) { andrew@0: throw_exception("getMIDIByte called but no MIDI file open"); andrew@0: } andrew@0: andrew@0: if (m_midiFile->eof()) { andrew@0: throw_exception("End of MIDI file encountered while reading"); andrew@0: } andrew@0: andrew@0: if (m_decrementCount && m_trackByteCount <= 0) { andrew@0: throw_exception("Attempt to get more bytes than expected on Track"); andrew@0: } andrew@0: andrew@0: char byte; andrew@0: if (m_midiFile->read(&byte, 1)) { andrew@0: --m_trackByteCount; andrew@0: return (MIDIByte)byte; andrew@0: } andrew@0: andrew@0: throw_exception("Attempt to read past MIDI file end"); andrew@0: } andrew@0: andrew@0: andrew@0: // Gets a specified number of bytes from the MIDI byte stream. For andrew@0: // each track section we can read only a specified number of bytes andrew@0: // held in m_trackByteCount. andrew@0: // andrew@0: string andrew@0: MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes) andrew@0: { andrew@0: if (!m_midiFile) { andrew@0: throw_exception("getMIDIBytes called but no MIDI file open"); andrew@0: } andrew@0: andrew@0: if (m_midiFile->eof()) { andrew@0: throw_exception("End of MIDI file encountered while reading"); andrew@0: } andrew@0: andrew@0: if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) { andrew@0: throw_exception("Attempt to get more bytes than available on Track (%lu, only have %ld)", numberOfBytes, m_trackByteCount); andrew@0: } andrew@0: andrew@0: string stringRet; andrew@0: char fileMIDIByte; andrew@0: andrew@0: while (stringRet.length() < numberOfBytes && andrew@0: m_midiFile->read(&fileMIDIByte, 1)) { andrew@0: stringRet += fileMIDIByte; andrew@0: } andrew@0: andrew@0: // if we've reached the end of file without fulfilling the andrew@0: // quota then panic as our parsing has performed incorrectly andrew@0: // andrew@0: if (stringRet.length() < numberOfBytes) { andrew@0: stringRet = ""; andrew@0: throw_exception("Attempt to read past MIDI file end"); andrew@0: } andrew@0: andrew@0: // decrement the byte count andrew@0: if (m_decrementCount) andrew@0: m_trackByteCount -= stringRet.length(); andrew@0: andrew@0: return stringRet; andrew@0: } andrew@0: andrew@0: andrew@0: // Get a long number of variable length from the MIDI byte stream. andrew@0: // andrew@0: long andrew@0: MIDIFileReader::getNumberFromMIDIBytes(int firstByte) andrew@0: { andrew@0: if (!m_midiFile) { andrew@0: throw_exception("getNumberFromMIDIBytes called but no MIDI file open"); andrew@0: } andrew@0: andrew@0: long longRet = 0; andrew@0: MIDIByte midiByte; andrew@0: andrew@0: if (firstByte >= 0) { andrew@0: midiByte = (MIDIByte)firstByte; andrew@0: } else if (m_midiFile->eof()) { andrew@0: return longRet; andrew@0: } else { andrew@0: midiByte = getMIDIByte(); andrew@0: } andrew@0: andrew@0: longRet = midiByte; andrew@0: if (midiByte & 0x80) { andrew@0: longRet &= 0x7F; andrew@0: do { andrew@0: midiByte = getMIDIByte(); andrew@0: longRet = (longRet << 7) + (midiByte & 0x7F); andrew@0: } while (!m_midiFile->eof() && (midiByte & 0x80)); andrew@0: } andrew@0: andrew@0: return longRet; andrew@0: } andrew@0: andrew@0: andrew@0: // Seek to the next track in the midi file and set the number andrew@0: // of bytes to be read in the counter m_trackByteCount. andrew@0: // andrew@0: bool andrew@0: MIDIFileReader::skipToNextTrack() andrew@0: { andrew@0: if (!m_midiFile) { andrew@0: throw_exception("skipToNextTrack called but no MIDI file open"); andrew@0: } andrew@0: andrew@0: string buffer, buffer2; andrew@0: m_trackByteCount = -1; andrew@0: m_decrementCount = false; andrew@0: andrew@0: while (!m_midiFile->eof() && (m_decrementCount == false)) { andrew@0: buffer = getMIDIBytes(4); andrew@0: if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) { andrew@0: m_trackByteCount = midiBytesToLong(getMIDIBytes(4)); andrew@0: m_decrementCount = true; andrew@0: } andrew@0: } andrew@0: andrew@0: if (m_trackByteCount == -1) { // we haven't found a track andrew@0: return false; andrew@0: } else { andrew@0: return true; andrew@0: } andrew@0: } andrew@0: andrew@0: andrew@0: // Read in a MIDI file. The parsing process throws exceptions back up andrew@0: // here if we run into trouble which we can then pass back out to andrew@0: // whoever called us using a nice bool. andrew@0: // andrew@0: bool andrew@0: MIDIFileReader::parseFile() andrew@0: { andrew@0: m_error = ""; andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::open() : fileName = " << m_path.toStdString() << endl; andrew@0: #endif andrew@0: andrew@0: // Open the file andrew@0: m_midiFile = new ifstream(m_path.c_str(), ios::in | ios::binary); andrew@0: andrew@0: if (!*m_midiFile) { andrew@0: m_error = "File not found or not readable."; andrew@0: m_format = MIDI_FILE_BAD_FORMAT; andrew@0: delete m_midiFile; andrew@0: m_midiFile = 0; andrew@0: return false; andrew@0: } andrew@0: andrew@0: bool retval = false; andrew@0: andrew@0: try { andrew@0: andrew@0: // Set file size so we can count it off andrew@0: // andrew@0: m_midiFile->seekg(0, ios::end); andrew@0: m_fileSize = m_midiFile->tellg(); andrew@0: m_midiFile->seekg(0, ios::beg); andrew@0: andrew@0: // Parse the MIDI header first. The first 14 bytes of the file. andrew@0: if (!parseHeader(getMIDIBytes(14))) { andrew@0: m_format = MIDI_FILE_BAD_FORMAT; andrew@0: m_error = "Not a MIDI file."; andrew@0: goto done; andrew@0: } andrew@0: andrew@0: for (unsigned int j = 0; j < m_numberOfTracks; ++j) { andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Parsing Track " << j << endl; andrew@0: #endif andrew@0: andrew@0: if (!skipToNextTrack()) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Couldn't find Track " << j << endl; andrew@0: #endif andrew@0: m_error = "File corrupted or in non-standard format?"; andrew@0: m_format = MIDI_FILE_BAD_FORMAT; andrew@0: goto done; andrew@0: } andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Track has " << m_trackByteCount << " bytes" << endl; andrew@0: #endif andrew@0: andrew@0: // Run through the events taking them into our internal andrew@0: // representation. andrew@0: if (!parseTrack(j)) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Track " << j << " parsing failed" << endl; andrew@0: #endif andrew@0: m_error = "File corrupted or in non-standard format?"; andrew@0: m_format = MIDI_FILE_BAD_FORMAT; andrew@0: goto done; andrew@0: } andrew@0: } andrew@0: andrew@0: retval = true; andrew@0: andrew@0: } catch (MIDIException e) { andrew@0: andrew@0: cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl; andrew@0: m_error = e.what(); andrew@0: } andrew@0: andrew@0: done: andrew@0: m_midiFile->close(); andrew@0: delete m_midiFile; andrew@0: andrew@0: for (unsigned int track = 0; track < m_numberOfTracks; ++track) { andrew@0: andrew@0: // Convert the deltaTime to an absolute time since the track andrew@0: // start. The addTime method returns the sum of the current andrew@0: // MIDI Event delta time plus the argument. andrew@0: andrew@0: unsigned long acc = 0; andrew@0: andrew@0: for (MIDITrack::iterator i = m_midiComposition[track].begin(); andrew@0: i != m_midiComposition[track].end(); ++i) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "converting delta time " << i->getTime(); andrew@0: #endif andrew@0: acc = i->addTime(acc); andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << " to " << i->getTime() << endl; andrew@0: #endif andrew@0: } andrew@0: andrew@0: consolidateNoteOffEvents(track); andrew@0: } andrew@0: andrew@0: return retval; andrew@0: } andrew@0: andrew@0: // Parse and ensure the MIDI Header is legitimate andrew@0: // andrew@0: bool andrew@0: MIDIFileReader::parseHeader(const string &midiHeader) andrew@0: { andrew@0: if (midiHeader.size() < 14) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl; andrew@0: #endif andrew@0: return false; andrew@0: } andrew@0: andrew@0: if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseHeader()" andrew@0: << "- file header not found or malformed" andrew@0: << endl; andrew@0: #endif andrew@0: return false; andrew@0: } andrew@0: andrew@0: if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseHeader()" andrew@0: << " - header length incorrect" andrew@0: << endl; andrew@0: #endif andrew@0: return false; andrew@0: } andrew@0: andrew@0: m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2)); andrew@0: m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2)); andrew@0: m_timingDivision = midiBytesToInt(midiHeader.substr(12,2)); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: if (m_timingDivision < 0) { andrew@0: cerr << "MIDIFileReader::parseHeader()" andrew@0: << " - file uses SMPTE timing" andrew@0: << endl; andrew@0: } andrew@0: #endif andrew@0: andrew@0: return true; andrew@0: } andrew@0: andrew@0: // Extract the contents from a MIDI file track and places it into andrew@0: // our local map of MIDI events. andrew@0: // andrew@0: bool andrew@0: MIDIFileReader::parseTrack(unsigned int trackNum) andrew@0: { andrew@0: MIDIByte midiByte, metaEventCode, data1, data2; andrew@0: MIDIByte eventCode = 0x80; andrew@0: string metaMessage; andrew@0: unsigned int messageLength; andrew@0: unsigned long deltaTime; andrew@0: unsigned long accumulatedTime = 0; andrew@0: andrew@0: // Remember the last non-meta status byte (-1 if we haven't seen one) andrew@0: int runningStatus = -1; andrew@0: andrew@0: while (!m_midiFile->eof() && (m_trackByteCount > 0)) { andrew@0: andrew@0: if (eventCode < 0x80) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "WARNING: Invalid event code " << eventCode andrew@0: << " in MIDI file" << endl; andrew@0: #endif andrew@0: throw_exception("Invalid event code %d found", int(eventCode)); andrew@0: } andrew@0: andrew@0: deltaTime = getNumberFromMIDIBytes(); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "read delta time " << deltaTime << endl; andrew@0: #endif andrew@0: andrew@0: // Get a single byte andrew@0: midiByte = getMIDIByte(); andrew@0: andrew@0: if (!(midiByte & MIDI_STATUS_BYTE_MASK)) { andrew@0: andrew@0: if (runningStatus < 0) { andrew@0: throw_exception("Running status used for first event in track"); andrew@0: } andrew@0: andrew@0: eventCode = (MIDIByte)runningStatus; andrew@0: data1 = midiByte; andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "using running status (byte " << int(midiByte) << " found)" << endl; andrew@0: #endif andrew@0: } else { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "have new event code " << int(midiByte) << endl; andrew@0: #endif andrew@0: eventCode = midiByte; andrew@0: data1 = getMIDIByte(); andrew@0: } andrew@0: andrew@0: if (eventCode == MIDI_FILE_META_EVENT) { andrew@0: andrew@0: metaEventCode = data1; andrew@0: messageLength = getNumberFromMIDIBytes(); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl; andrew@0: #endif andrew@0: metaMessage = getMIDIBytes(messageLength); andrew@0: andrew@0: accumulatedTime += deltaTime; andrew@0: andrew@0: MIDIEvent e(deltaTime, andrew@0: MIDI_FILE_META_EVENT, andrew@0: metaEventCode, andrew@0: metaMessage); andrew@0: andrew@0: m_midiComposition[trackNum].push_back(e); andrew@0: andrew@0: if (metaEventCode == MIDI_TRACK_NAME) { andrew@0: m_trackNames[trackNum] = metaMessage.c_str(); andrew@0: } andrew@0: andrew@0: } else { // non-meta events andrew@0: andrew@0: runningStatus = eventCode; andrew@0: andrew@0: int channel = (eventCode & MIDI_CHANNEL_NUM_MASK); andrew@0: andrew@0: accumulatedTime += deltaTime; andrew@0: andrew@0: switch (eventCode & MIDI_MESSAGE_TYPE_MASK) { andrew@0: andrew@0: case MIDI_NOTE_ON: andrew@0: case MIDI_NOTE_OFF: andrew@0: case MIDI_POLY_AFTERTOUCH: andrew@0: case MIDI_CTRL_CHANGE: andrew@0: data2 = getMIDIByte(); andrew@0: andrew@0: { andrew@0: // create and store our event andrew@0: MIDIEvent midiEvent(deltaTime, eventCode, data1, data2); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDI event for channel " << channel << " (track " andrew@0: << trackNum << ") with delta time " << deltaTime << endl; andrew@0: #endif andrew@0: andrew@0: m_midiComposition[trackNum].push_back(midiEvent); andrew@0: } andrew@0: break; andrew@0: andrew@0: case MIDI_PITCH_BEND: andrew@0: data2 = getMIDIByte(); andrew@0: andrew@0: { andrew@0: // create and store our event andrew@0: MIDIEvent midiEvent(deltaTime, eventCode, data1, data2); andrew@0: m_midiComposition[trackNum].push_back(midiEvent); andrew@0: } andrew@0: break; andrew@0: andrew@0: case MIDI_PROG_CHANGE: andrew@0: case MIDI_CHNL_AFTERTOUCH: andrew@0: andrew@0: { andrew@0: // create and store our event andrew@0: MIDIEvent midiEvent(deltaTime, eventCode, data1); andrew@0: m_midiComposition[trackNum].push_back(midiEvent); andrew@0: } andrew@0: break; andrew@0: andrew@0: case MIDI_SYSTEM_EXCLUSIVE: andrew@0: messageLength = getNumberFromMIDIBytes(data1); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "SysEx of " << messageLength << " bytes found" << endl; andrew@0: #endif andrew@0: andrew@0: metaMessage= getMIDIBytes(messageLength); andrew@0: andrew@0: if (MIDIByte(metaMessage[metaMessage.length() - 1]) != andrew@0: MIDI_END_OF_EXCLUSIVE) andrew@0: { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseTrack() - " andrew@0: << "malformed or unsupported SysEx type" andrew@0: << endl; andrew@0: #endif andrew@0: continue; andrew@0: } andrew@0: andrew@0: // chop off the EOX andrew@0: // length fixed by Pedro Lopez-Cabanillas (20030523) andrew@0: // andrew@0: metaMessage = metaMessage.substr(0, metaMessage.length()-1); andrew@0: andrew@0: { andrew@0: MIDIEvent midiEvent(deltaTime, andrew@0: MIDI_SYSTEM_EXCLUSIVE, andrew@0: metaMessage); andrew@0: m_midiComposition[trackNum].push_back(midiEvent); andrew@0: } andrew@0: break; andrew@0: andrew@0: case MIDI_END_OF_EXCLUSIVE: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseTrack() - " andrew@0: << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl; andrew@0: #endif andrew@0: break; andrew@0: andrew@0: default: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "MIDIFileReader::parseTrack()" andrew@0: << " - Unsupported MIDI Event Code: " andrew@0: << (int)eventCode << endl; andrew@0: #endif andrew@0: break; andrew@0: } andrew@0: } andrew@0: } andrew@0: andrew@0: return true; andrew@0: } andrew@0: andrew@0: // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after andrew@0: // reading them and modifying their relevant NOTE ONs. Return true andrew@0: // if there are some notes in this track. andrew@0: // andrew@0: bool andrew@0: MIDIFileReader::consolidateNoteOffEvents(unsigned int track) andrew@0: { andrew@0: bool notesOnTrack = false; andrew@0: bool noteOffFound; andrew@0: andrew@0: MIDITrack &t = m_midiComposition[track]; andrew@0: andrew@0: for (MIDITrack::iterator i = t.begin(); i != t.end(); ++i) { andrew@0: andrew@0: if (i->getMessageType() == MIDI_NOTE_ON && i->getVelocity() > 0) { andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Looking for note-offs for note at " << i->getTime() << " (pitch " << (int)i->getPitch() << ")" << endl; andrew@0: #endif andrew@0: andrew@0: notesOnTrack = true; andrew@0: noteOffFound = false; andrew@0: andrew@0: for (MIDITrack::iterator j = i; j != t.end(); ++j) { andrew@0: andrew@0: if ((j->getChannelNumber() == i->getChannelNumber()) && andrew@0: (j->getPitch() == i->getPitch()) && andrew@0: (j->getMessageType() == MIDI_NOTE_OFF || andrew@0: (j->getMessageType() == MIDI_NOTE_ON && andrew@0: j->getVelocity() == 0x00))) { andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Found note-off at " << j->getTime() << " for note at " << i->getTime() << endl; andrew@0: #endif andrew@0: andrew@0: i->setDuration(j->getTime() - i->getTime()); andrew@0: andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Duration is now " << i->getDuration() << endl; andrew@0: #endif andrew@0: andrew@0: t.erase(j); andrew@0: andrew@0: noteOffFound = true; andrew@0: break; andrew@0: } andrew@0: } andrew@0: andrew@0: // If no matching NOTE OFF has been found then set andrew@0: // Event duration to length of track andrew@0: // andrew@0: if (!noteOffFound) { andrew@0: #ifdef DEBUG_MIDI_FILE_READER andrew@0: cerr << "Failed to find note-off for note at " << i->getTime() << endl; andrew@0: #endif andrew@0: MIDITrack::iterator j = t.end(); andrew@0: --j; andrew@0: i->setDuration(j->getTime() - i->getTime()); andrew@0: } andrew@0: } andrew@0: } andrew@0: andrew@0: return notesOnTrack; andrew@0: } andrew@0: andrew@0: MIDIComposition andrew@0: MIDIFileReader::load() const andrew@0: { andrew@0: return m_midiComposition; andrew@0: } andrew@0: andrew@0: