svcore  1.9
MIDIFileReader.cpp
Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     
00008     This program is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU General Public License as
00010     published by the Free Software Foundation; either version 2 of the
00011     License, or (at your option) any later version.  See the file
00012     COPYING included with this distribution for more information.
00013 */
00014 
00015 
00016 /*
00017    This is a modified version of a source file from the 
00018    Rosegarden MIDI and audio sequencer and notation editor.
00019    This file copyright 2000-2006 Richard Bown and Chris Cannam.
00020 */
00021 
00022 
00023 #include <iostream>
00024 #include <fstream>
00025 #include <string>
00026 #include <cstdio>
00027 #include <algorithm>
00028 
00029 #include "MIDIFileReader.h"
00030 
00031 #include "data/midi/MIDIEvent.h"
00032 
00033 #include "model/Model.h"
00034 #include "base/Pitch.h"
00035 #include "base/RealTime.h"
00036 #include "model/NoteModel.h"
00037 
00038 #include <QString>
00039 
00040 #include <sstream>
00041 
00042 #include "base/Debug.h"
00043 
00044 using std::string;
00045 using std::ifstream;
00046 using std::stringstream;
00047 using std::ends;
00048 using std::ios;
00049 using std::vector;
00050 using std::map;
00051 using std::set;
00052 
00053 using namespace MIDIConstants;
00054 
00055 //#define MIDI_SVDEBUG 1
00056 
00057 
00058 MIDIFileReader::MIDIFileReader(QString path,
00059                                MIDIFileImportPreferenceAcquirer *acquirer,
00060                                int mainModelSampleRate) :
00061     m_smpte(false),
00062     m_timingDivision(0),
00063     m_fps(0),
00064     m_subframes(0),
00065     m_format(MIDI_FILE_BAD_FORMAT),
00066     m_numberOfTracks(0),
00067     m_trackByteCount(0),
00068     m_decrementCount(false),
00069     m_path(path),
00070     m_midiFile(0),
00071     m_fileSize(0),
00072     m_mainModelSampleRate(mainModelSampleRate),
00073     m_acquirer(acquirer)
00074 {
00075     if (parseFile()) {
00076         m_error = "";
00077     }
00078 }
00079 
00080 MIDIFileReader::~MIDIFileReader()
00081 {
00082     for (MIDIComposition::iterator i = m_midiComposition.begin();
00083          i != m_midiComposition.end(); ++i) {
00084         
00085         for (MIDITrack::iterator j = i->second.begin();
00086              j != i->second.end(); ++j) {
00087             delete *j;
00088         }
00089 
00090         i->second.clear();
00091     }
00092 
00093     m_midiComposition.clear();
00094 }
00095 
00096 bool
00097 MIDIFileReader::isOK() const
00098 {
00099     return (m_error == "");
00100 }
00101 
00102 QString
00103 MIDIFileReader::getError() const
00104 {
00105     return m_error;
00106 }
00107 
00108 long
00109 MIDIFileReader::midiBytesToLong(const string& bytes)
00110 {
00111     if (bytes.length() != 4) {
00112         throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4));
00113     }
00114 
00115     long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
00116                    ((long)(((MIDIByte)bytes[1]) << 16)) |
00117                    ((long)(((MIDIByte)bytes[2]) << 8)) |
00118                    ((long)((MIDIByte)(bytes[3])));
00119 
00120     return longRet;
00121 }
00122 
00123 int
00124 MIDIFileReader::midiBytesToInt(const string& bytes)
00125 {
00126     if (bytes.length() != 2) {
00127         throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2));
00128     }
00129 
00130     int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
00131                  ((int)(((MIDIByte)bytes[1])));
00132     return(intRet);
00133 }
00134 
00135 
00136 // Gets a single byte from the MIDI byte stream.  For each track
00137 // section we can read only a specified number of bytes held in
00138 // m_trackByteCount.
00139 //
00140 MIDIByte
00141 MIDIFileReader::getMIDIByte()
00142 {
00143     if (!m_midiFile) {
00144         throw MIDIException(tr("getMIDIByte called but no MIDI file open"));
00145     }
00146 
00147     if (m_midiFile->eof()) {
00148         throw MIDIException(tr("End of MIDI file encountered while reading"));
00149     }
00150 
00151     if (m_decrementCount && m_trackByteCount <= 0) {
00152         throw MIDIException(tr("Attempt to get more bytes than expected on Track"));
00153     }
00154 
00155     char byte;
00156     if (m_midiFile->read(&byte, 1)) {
00157         --m_trackByteCount;
00158         return (MIDIByte)byte;
00159     }
00160 
00161     throw MIDIException(tr("Attempt to read past MIDI file end"));
00162 }
00163 
00164 
00165 // Gets a specified number of bytes from the MIDI byte stream.  For
00166 // each track section we can read only a specified number of bytes
00167 // held in m_trackByteCount.
00168 //
00169 string
00170 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
00171 {
00172     if (!m_midiFile) {
00173         throw MIDIException(tr("getMIDIBytes called but no MIDI file open"));
00174     }
00175 
00176     if (m_midiFile->eof()) {
00177         throw MIDIException(tr("End of MIDI file encountered while reading"));
00178     }
00179 
00180     if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
00181         throw MIDIException(tr("Attempt to get more bytes than available on Track (%1, only have %2)").arg(numberOfBytes).arg(m_trackByteCount));
00182     }
00183 
00184     string stringRet;
00185     char fileMIDIByte;
00186 
00187     while (stringRet.length() < numberOfBytes &&
00188            m_midiFile->read(&fileMIDIByte, 1)) {
00189         stringRet += fileMIDIByte;
00190     }
00191 
00192     // if we've reached the end of file without fulfilling the
00193     // quota then panic as our parsing has performed incorrectly
00194     //
00195     if (stringRet.length() < numberOfBytes) {
00196         stringRet = "";
00197         throw MIDIException(tr("Attempt to read past MIDI file end"));
00198     }
00199 
00200     // decrement the byte count
00201     if (m_decrementCount)
00202         m_trackByteCount -= stringRet.length();
00203 
00204     return stringRet;
00205 }
00206 
00207 
00208 // Get a long number of variable length from the MIDI byte stream.
00209 //
00210 long
00211 MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
00212 {
00213     if (!m_midiFile) {
00214         throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open"));
00215     }
00216 
00217     long longRet = 0;
00218     MIDIByte midiByte;
00219 
00220     if (firstByte >= 0) {
00221         midiByte = (MIDIByte)firstByte;
00222     } else if (m_midiFile->eof()) {
00223         return longRet;
00224     } else {
00225         midiByte = getMIDIByte();
00226     }
00227 
00228     longRet = midiByte;
00229     if (midiByte & 0x80) {
00230         longRet &= 0x7F;
00231         do {
00232             midiByte = getMIDIByte();
00233             longRet = (longRet << 7) + (midiByte & 0x7F);
00234         } while (!m_midiFile->eof() && (midiByte & 0x80));
00235     }
00236 
00237     return longRet;
00238 }
00239 
00240 
00241 // Seek to the next track in the midi file and set the number
00242 // of bytes to be read in the counter m_trackByteCount.
00243 //
00244 bool
00245 MIDIFileReader::skipToNextTrack()
00246 {
00247     if (!m_midiFile) {
00248         throw MIDIException(tr("skipToNextTrack called but no MIDI file open"));
00249     }
00250 
00251     string buffer, buffer2;
00252     m_trackByteCount = -1;
00253     m_decrementCount = false;
00254 
00255     while (!m_midiFile->eof() && (m_decrementCount == false)) {
00256         buffer = getMIDIBytes(4); 
00257         if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
00258             m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
00259             m_decrementCount = true;
00260         }
00261     }
00262 
00263     if (m_trackByteCount == -1) { // we haven't found a track
00264         return false;
00265     } else {
00266         return true;
00267     }
00268 }
00269 
00270 
00271 // Read in a MIDI file.  The parsing process throws exceptions back up
00272 // here if we run into trouble which we can then pass back out to
00273 // whoever called us using a nice bool.
00274 //
00275 bool
00276 MIDIFileReader::parseFile()
00277 {
00278     m_error = "";
00279 
00280 #ifdef MIDI_DEBUG
00281     SVDEBUG << "MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl;
00282 #endif
00283 
00284     // Open the file
00285     m_midiFile = new ifstream(m_path.toLocal8Bit().data(),
00286                               ios::in | ios::binary);
00287 
00288     if (!*m_midiFile) {
00289         m_error = "File not found or not readable.";
00290         m_format = MIDI_FILE_BAD_FORMAT;
00291         delete m_midiFile;
00292         m_midiFile = 0;
00293         return false;
00294     }
00295 
00296     bool retval = false;
00297 
00298     try {
00299 
00300         // Set file size so we can count it off
00301         //
00302         m_midiFile->seekg(0, ios::end);
00303         m_fileSize = m_midiFile->tellg();
00304         m_midiFile->seekg(0, ios::beg);
00305 
00306         // Parse the MIDI header first.  The first 14 bytes of the file.
00307         if (!parseHeader(getMIDIBytes(14))) {
00308             m_format = MIDI_FILE_BAD_FORMAT;
00309             m_error = "Not a MIDI file.";
00310             goto done;
00311         }
00312 
00313         unsigned int i = 0;
00314 
00315         for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
00316 
00317 #ifdef MIDI_DEBUG
00318             SVDEBUG << "Parsing Track " << j << endl;
00319 #endif
00320 
00321             if (!skipToNextTrack()) {
00322 #ifdef MIDI_DEBUG
00323                 cerr << "Couldn't find Track " << j << endl;
00324 #endif
00325                 m_error = "File corrupted or in non-standard format?";
00326                 m_format = MIDI_FILE_BAD_FORMAT;
00327                 goto done;
00328             }
00329 
00330 #ifdef MIDI_DEBUG
00331             cerr << "Track has " << m_trackByteCount << " bytes" << endl;
00332 #endif
00333 
00334             // Run through the events taking them into our internal
00335             // representation.
00336             if (!parseTrack(i)) {
00337 #ifdef MIDI_DEBUG
00338                 cerr << "Track " << j << " parsing failed" << endl;
00339 #endif
00340                 m_error = "File corrupted or in non-standard format?";
00341                 m_format = MIDI_FILE_BAD_FORMAT;
00342                 goto done;
00343             }
00344 
00345             ++i; // j is the source track number, i the destination
00346         }
00347         
00348         m_numberOfTracks = i;
00349         retval = true;
00350 
00351     } catch (MIDIException e) {
00352 
00353         SVDEBUG << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
00354         m_error = e.what();
00355     }
00356     
00357 done:
00358     m_midiFile->close();
00359     delete m_midiFile;
00360 
00361     for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
00362 
00363         // Convert the deltaTime to an absolute time since the track
00364         // start.  The addTime method returns the sum of the current
00365         // MIDI Event delta time plus the argument.
00366 
00367         unsigned long acc = 0;
00368 
00369         for (MIDITrack::iterator i = m_midiComposition[track].begin();
00370              i != m_midiComposition[track].end(); ++i) {
00371             acc = (*i)->addTime(acc);
00372         }
00373 
00374         if (consolidateNoteOffEvents(track)) { // returns true if some notes exist
00375             m_loadableTracks.insert(track);
00376         }
00377     }
00378 
00379     for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
00380         updateTempoMap(track);
00381     }
00382 
00383     calculateTempoTimestamps();
00384 
00385     return retval;
00386 }
00387 
00388 // Parse and ensure the MIDI Header is legitimate
00389 //
00390 bool
00391 MIDIFileReader::parseHeader(const string &midiHeader)
00392 {
00393     if (midiHeader.size() < 14) {
00394 #ifdef MIDI_DEBUG
00395         SVDEBUG << "MIDIFileReader::parseHeader() - file header undersized" << endl;
00396 #endif
00397         return false;
00398     }
00399 
00400     if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
00401 #ifdef MIDI_DEBUG
00402         SVDEBUG << "MIDIFileReader::parseHeader()"
00403              << "- file header not found or malformed"
00404              << endl;
00405 #endif
00406         return false;
00407     }
00408 
00409     if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
00410 #ifdef MIDI_DEBUG
00411         SVDEBUG << "MIDIFileReader::parseHeader()"
00412              << " - header length incorrect"
00413              << endl;
00414 #endif
00415         return false;
00416     }
00417 
00418     m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
00419     m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
00420     m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
00421 
00422     if (m_timingDivision >= 32768) {
00423         m_smpte = true;
00424         m_fps = 256 - (m_timingDivision >> 8);
00425         m_subframes = (m_timingDivision & 0xff);
00426     } else {
00427         m_smpte = false;
00428     }
00429 
00430     return true; 
00431 }
00432 
00433 // Extract the contents from a MIDI file track and places it into
00434 // our local map of MIDI events.
00435 //
00436 bool
00437 MIDIFileReader::parseTrack(unsigned int &lastTrackNum)
00438 {
00439     MIDIByte midiByte, metaEventCode, data1, data2;
00440     MIDIByte eventCode = 0x80;
00441     string metaMessage;
00442     unsigned int messageLength;
00443     unsigned long deltaTime;
00444     unsigned long accumulatedTime = 0;
00445 
00446     // The trackNum passed in to this method is the default track for
00447     // all events provided they're all on the same channel.  If we find
00448     // events on more than one channel, we increment trackNum and record
00449     // the mapping from channel to trackNum in this channelTrackMap.
00450     // We then return the new trackNum by reference so the calling
00451     // method knows we've got more tracks than expected.
00452 
00453     // This would be a vector<unsigned int> but we need -1 to indicate
00454     // "not yet used"
00455     vector<int> channelTrackMap(16, -1);
00456 
00457     // This is used to store the last absolute time found on each track,
00458     // allowing us to modify delta-times correctly when separating events
00459     // out from one to multiple tracks
00460     //
00461     map<int, unsigned long> trackTimeMap;
00462 
00463     // Meta-events don't have a channel, so we place them in a fixed
00464     // track number instead
00465     unsigned int metaTrack = lastTrackNum;
00466 
00467     // Remember the last non-meta status byte (-1 if we haven't seen one)
00468     int runningStatus = -1;
00469 
00470     bool firstTrack = true;
00471 
00472     while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
00473 
00474         if (eventCode < 0x80) {
00475 #ifdef MIDI_DEBUG
00476             cerr << "WARNING: Invalid event code " << eventCode
00477                  << " in MIDI file" << endl;
00478 #endif
00479             throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode)));
00480         }
00481 
00482         deltaTime = getNumberFromMIDIBytes();
00483 
00484 #ifdef MIDI_DEBUG
00485         cerr << "read delta time " << deltaTime << endl;
00486 #endif
00487 
00488         // Get a single byte
00489         midiByte = getMIDIByte();
00490 
00491         if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
00492 
00493             if (runningStatus < 0) {
00494                 throw MIDIException(tr("Running status used for first event in track"));
00495             }
00496 
00497             eventCode = (MIDIByte)runningStatus;
00498             data1 = midiByte;
00499 
00500 #ifdef MIDI_DEBUG
00501             SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl;
00502 #endif
00503         } else {
00504 #ifdef MIDI_DEBUG
00505             cerr << "have new event code " << int(midiByte) << endl;
00506 #endif
00507             eventCode = midiByte;
00508             data1 = getMIDIByte();
00509         }
00510 
00511         if (eventCode == MIDI_FILE_META_EVENT) {
00512 
00513             metaEventCode = data1;
00514             messageLength = getNumberFromMIDIBytes();
00515 
00516 //#ifdef MIDI_DEBUG
00517                 cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
00518 //#endif
00519             metaMessage = getMIDIBytes(messageLength);
00520 
00521             long gap = accumulatedTime - trackTimeMap[metaTrack];
00522             accumulatedTime += deltaTime;
00523             deltaTime += gap;
00524             trackTimeMap[metaTrack] = accumulatedTime;
00525 
00526             MIDIEvent *e = new MIDIEvent(deltaTime,
00527                                          MIDI_FILE_META_EVENT,
00528                                          metaEventCode,
00529                                          metaMessage);
00530 
00531             m_midiComposition[metaTrack].push_back(e);
00532 
00533             if (metaEventCode == MIDI_TRACK_NAME) {
00534                 m_trackNames[metaTrack] = metaMessage.c_str();
00535             }
00536 
00537         } else { // non-meta events
00538 
00539             runningStatus = eventCode;
00540 
00541             MIDIEvent *midiEvent;
00542 
00543             int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
00544             if (channelTrackMap[channel] == -1) {
00545                 if (!firstTrack) ++lastTrackNum;
00546                 else firstTrack = false;
00547                 channelTrackMap[channel] = lastTrackNum;
00548             }
00549 
00550             unsigned int trackNum = channelTrackMap[channel];
00551             
00552             // accumulatedTime is abs time of last event on any track;
00553             // trackTimeMap[trackNum] is that of last event on this track
00554             
00555             long gap = accumulatedTime - trackTimeMap[trackNum];
00556             accumulatedTime += deltaTime;
00557             deltaTime += gap;
00558             trackTimeMap[trackNum] = accumulatedTime;
00559 
00560             switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
00561 
00562             case MIDI_NOTE_ON:
00563             case MIDI_NOTE_OFF:
00564             case MIDI_POLY_AFTERTOUCH:
00565             case MIDI_CTRL_CHANGE:
00566                 data2 = getMIDIByte();
00567 
00568                 // create and store our event
00569                 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
00570 
00571                 /*
00572                 cerr << "MIDI event for channel " << channel << " (track "
00573                           << trackNum << ")" << endl;
00574                 midiEvent->print();
00575                           */
00576 
00577 
00578                 m_midiComposition[trackNum].push_back(midiEvent);
00579 
00580                 if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) {
00581                     m_percussionTracks.insert(trackNum);
00582                 }
00583 
00584                 break;
00585 
00586             case MIDI_PITCH_BEND:
00587                 data2 = getMIDIByte();
00588 
00589                 // create and store our event
00590                 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
00591                 m_midiComposition[trackNum].push_back(midiEvent);
00592                 break;
00593 
00594             case MIDI_PROG_CHANGE:
00595             case MIDI_CHNL_AFTERTOUCH:
00596                 // create and store our event
00597                 midiEvent = new MIDIEvent(deltaTime, eventCode, data1);
00598                 m_midiComposition[trackNum].push_back(midiEvent);
00599                 break;
00600 
00601             case MIDI_SYSTEM_EXCLUSIVE:
00602                 messageLength = getNumberFromMIDIBytes(data1);
00603 
00604 #ifdef MIDI_DEBUG
00605                 cerr << "SysEx of " << messageLength << " bytes found" << endl;
00606 #endif
00607 
00608                 metaMessage= getMIDIBytes(messageLength);
00609 
00610                 if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
00611                         MIDI_END_OF_EXCLUSIVE)
00612                 {
00613 #ifdef MIDI_DEBUG
00614                     SVDEBUG << "MIDIFileReader::parseTrack() - "
00615                               << "malformed or unsupported SysEx type"
00616                               << endl;
00617 #endif
00618                     continue;
00619                 }
00620 
00621                 // chop off the EOX 
00622                 // length fixed by Pedro Lopez-Cabanillas (20030523)
00623                 //
00624                 metaMessage = metaMessage.substr(0, metaMessage.length()-1);
00625 
00626                 midiEvent = new MIDIEvent(deltaTime,
00627                                           MIDI_SYSTEM_EXCLUSIVE,
00628                                           metaMessage);
00629                 m_midiComposition[trackNum].push_back(midiEvent);
00630                 break;
00631 
00632             default:
00633 #ifdef MIDI_DEBUG
00634                 SVDEBUG << "MIDIFileReader::parseTrack()" 
00635                           << " - Unsupported MIDI Event Code:  "
00636                           << (int)eventCode << endl;
00637 #endif
00638                 break;
00639             } 
00640         }
00641     }
00642 
00643     if (lastTrackNum > metaTrack) {
00644         for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) {
00645             m_trackNames[track] = QString("%1 <%2>")
00646                 .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1);
00647         }
00648     }
00649 
00650     return true;
00651 }
00652 
00653 // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
00654 // reading them and modifying their relevant NOTE ONs.  Return true
00655 // if there are some notes in this track.
00656 //
00657 bool
00658 MIDIFileReader::consolidateNoteOffEvents(unsigned int track)
00659 {
00660     bool notesOnTrack = false;
00661     bool noteOffFound;
00662 
00663     for (MIDITrack::iterator i = m_midiComposition[track].begin();
00664          i != m_midiComposition[track].end(); i++) {
00665 
00666         if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) {
00667 
00668             notesOnTrack = true;
00669             noteOffFound = false;
00670 
00671             for (MIDITrack::iterator j = i;
00672                  j != m_midiComposition[track].end(); j++) {
00673 
00674                 if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) &&
00675                     ((*j)->getPitch() == (*i)->getPitch()) &&
00676                     ((*j)->getMessageType() == MIDI_NOTE_OFF ||
00677                     ((*j)->getMessageType() == MIDI_NOTE_ON &&
00678                      (*j)->getVelocity() == 0x00))) {
00679 
00680                     (*i)->setDuration((*j)->getTime() - (*i)->getTime());
00681 
00682                     delete *j;
00683                     m_midiComposition[track].erase(j);
00684 
00685                     noteOffFound = true;
00686                     break;
00687                 }
00688             }
00689 
00690             // If no matching NOTE OFF has been found then set
00691             // Event duration to length of track
00692             //
00693             if (!noteOffFound) {
00694                 MIDITrack::iterator j = m_midiComposition[track].end();
00695                 --j;
00696                 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
00697             }
00698         }
00699     }
00700 
00701     return notesOnTrack;
00702 }
00703 
00704 // Add any tempo events found in the given track to the global tempo map.
00705 //
00706 void
00707 MIDIFileReader::updateTempoMap(unsigned int track)
00708 {
00709     cerr << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl;
00710 
00711     for (MIDITrack::iterator i = m_midiComposition[track].begin();
00712          i != m_midiComposition[track].end(); ++i) {
00713 
00714         if ((*i)->isMeta() &&
00715             (*i)->getMetaEventCode() == MIDI_SET_TEMPO) {
00716 
00717             MIDIByte m0 = (*i)->getMetaMessage()[0];
00718             MIDIByte m1 = (*i)->getMetaMessage()[1];
00719             MIDIByte m2 = (*i)->getMetaMessage()[2];
00720             
00721             long tempo = (((m0 << 8) + m1) << 8) + m2;
00722 
00723             cerr << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl;
00724 
00725             if (tempo != 0) {
00726                 double qpm = 60000000.0 / double(tempo);
00727                 m_tempoMap[(*i)->getTime()] =
00728                     TempoChange(RealTime::zeroTime, qpm);
00729             }
00730         }
00731     }
00732 }
00733 
00734 void
00735 MIDIFileReader::calculateTempoTimestamps()
00736 {
00737     unsigned long lastMIDITime = 0;
00738     RealTime lastRealTime = RealTime::zeroTime;
00739     double tempo = 120.0;
00740     int td = m_timingDivision;
00741     if (td == 0) td = 96;
00742 
00743     for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) {
00744         
00745         unsigned long mtime = i->first;
00746         unsigned long melapsed = mtime - lastMIDITime;
00747         double quarters = double(melapsed) / double(td);
00748         double seconds = (60.0 * quarters) / tempo;
00749 
00750         RealTime t = lastRealTime + RealTime::fromSeconds(seconds);
00751 
00752         i->second.first = t;
00753 
00754         lastRealTime = t;
00755         lastMIDITime = mtime;
00756         tempo = i->second.second;
00757     }
00758 }
00759 
00760 RealTime
00761 MIDIFileReader::getTimeForMIDITime(unsigned long midiTime) const
00762 {
00763     unsigned long tempoMIDITime = 0;
00764     RealTime tempoRealTime = RealTime::zeroTime;
00765     double tempo = 120.0;
00766 
00767     TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime);
00768     if (i != m_tempoMap.begin()) {
00769         --i;
00770         tempoMIDITime = i->first;
00771         tempoRealTime = i->second.first;
00772         tempo = i->second.second;
00773     }
00774 
00775     int td = m_timingDivision;
00776     if (td == 0) td = 96;
00777 
00778     unsigned long melapsed = midiTime - tempoMIDITime;
00779     double quarters = double(melapsed) / double(td);
00780     double seconds = (60.0 * quarters) / tempo;
00781 
00782 /*
00783     SVDEBUG << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")"
00784               << endl;
00785     SVDEBUG << "timing division = " << td << endl;
00786     cerr << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " ("
00787               << tempoRealTime << ")" << endl;
00788     cerr << "quarters since then = " << quarters << endl;
00789     cerr << "tempo = " << tempo << " quarters per minute" << endl;
00790     cerr << "seconds since then = " << seconds << endl;
00791     SVDEBUG << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << endl;
00792 */
00793 
00794     return tempoRealTime + RealTime::fromSeconds(seconds);
00795 }
00796 
00797 Model *
00798 MIDIFileReader::load() const
00799 {
00800     if (!isOK()) return 0;
00801 
00802     if (m_loadableTracks.empty()) {
00803         if (m_acquirer) {
00804             m_acquirer->showError
00805                 (tr("MIDI file \"%1\" has no notes in any track").arg(m_path));
00806         }
00807         return 0;
00808     }
00809 
00810     std::set<unsigned int> tracksToLoad;
00811 
00812     if (m_loadableTracks.size() == 1) {
00813 
00814         tracksToLoad.insert(*m_loadableTracks.begin());
00815 
00816     } else {
00817 
00818         QStringList displayNames;
00819 
00820         for (set<unsigned int>::iterator i = m_loadableTracks.begin();
00821              i != m_loadableTracks.end(); ++i) {
00822 
00823             unsigned int trackNo = *i;
00824             QString label;
00825 
00826             QString perc;
00827             if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) {
00828                 perc = tr(" - uses GM percussion channel");
00829             }
00830 
00831             if (m_trackNames.find(trackNo) != m_trackNames.end()) {
00832                 label = tr("Track %1 (%2)%3")
00833                     .arg(trackNo).arg(m_trackNames.find(trackNo)->second)
00834                     .arg(perc);
00835             } else {
00836                 label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc);
00837             }
00838 
00839             displayNames << label;
00840         }
00841 
00842         QString singleTrack;
00843 
00844         bool haveSomePercussion = 
00845             (!m_percussionTracks.empty() &&
00846              (m_percussionTracks.size() < m_loadableTracks.size()));
00847 
00848         MIDIFileImportPreferenceAcquirer::TrackPreference pref;
00849 
00850         if (m_acquirer) {
00851             pref = m_acquirer->getTrackImportPreference(displayNames,
00852                                                         haveSomePercussion,
00853                                                         singleTrack);
00854         } else {
00855             pref = MIDIFileImportPreferenceAcquirer::MergeAllTracks;
00856         }
00857 
00858         if (pref == MIDIFileImportPreferenceAcquirer::ImportNothing) return 0;
00859 
00860         if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks ||
00861             pref == MIDIFileImportPreferenceAcquirer::MergeAllNonPercussionTracks) {
00862             
00863             for (set<unsigned int>::iterator i = m_loadableTracks.begin();
00864                  i != m_loadableTracks.end(); ++i) {
00865                 
00866                 if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks ||
00867                     m_percussionTracks.find(*i) == m_percussionTracks.end()) {
00868                     
00869                     tracksToLoad.insert(*i);
00870                 }
00871             }
00872 
00873         } else {
00874             
00875             int j = 0;
00876 
00877             for (set<unsigned int>::iterator i = m_loadableTracks.begin();
00878                  i != m_loadableTracks.end(); ++i) {
00879                 
00880                 if (singleTrack == displayNames[j]) {
00881                     tracksToLoad.insert(*i);
00882                     break;
00883                 }
00884                 
00885                 ++j;
00886             }
00887         }
00888     }
00889 
00890     if (tracksToLoad.empty()) return 0;
00891 
00892     int n = tracksToLoad.size(), count = 0;
00893     Model *model = 0;
00894 
00895     for (std::set<unsigned int>::iterator i = tracksToLoad.begin();
00896          i != tracksToLoad.end(); ++i) {
00897 
00898         int minProgress = (100 * count) / n;
00899         int progressAmount = 100 / n;
00900 
00901         model = loadTrack(*i, model, minProgress, progressAmount);
00902 
00903         ++count;
00904     }
00905 
00906     if (dynamic_cast<NoteModel *>(model)) {
00907         dynamic_cast<NoteModel *>(model)->setCompletion(100);
00908     }
00909 
00910     return model;
00911 }
00912 
00913 Model *
00914 MIDIFileReader::loadTrack(unsigned int trackToLoad,
00915                           Model *existingModel,
00916                           int minProgress,
00917                           int progressAmount) const
00918 {
00919     if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) {
00920         return 0;
00921     }
00922 
00923     NoteModel *model = 0;
00924 
00925     if (existingModel) {
00926         model = dynamic_cast<NoteModel *>(existingModel);
00927         if (!model) {
00928             cerr << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl;
00929         }
00930     }
00931 
00932     if (!model) {
00933         model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false);
00934         model->setValueQuantization(1.0);
00935     }
00936 
00937     const MIDITrack &track = m_midiComposition.find(trackToLoad)->second;
00938 
00939     int totalEvents = track.size();
00940     int count = 0;
00941 
00942     bool sharpKey = true;
00943 
00944     for (MIDITrack::const_iterator i = track.begin(); i != track.end(); ++i) {
00945 
00946         RealTime rt;
00947         unsigned long midiTime = (*i)->getTime();
00948 
00949         if (m_smpte) {
00950             rt = RealTime::frame2RealTime(midiTime, m_fps * m_subframes);
00951         } else {
00952             rt = getTimeForMIDITime(midiTime);
00953         }
00954 
00955         // We ignore most of these event types for now, though in
00956         // theory some of the text ones could usefully be incorporated
00957 
00958         if ((*i)->isMeta()) {
00959 
00960             switch((*i)->getMetaEventCode()) {
00961 
00962             case MIDI_KEY_SIGNATURE:
00963                 // minorKey = (int((*i)->getMetaMessage()[1]) != 0);
00964                 sharpKey = (int((*i)->getMetaMessage()[0]) >= 0);
00965                 break;
00966 
00967             case MIDI_TEXT_EVENT:
00968             case MIDI_LYRIC:
00969             case MIDI_TEXT_MARKER:
00970             case MIDI_COPYRIGHT_NOTICE:
00971             case MIDI_TRACK_NAME:
00972                 // The text events that we could potentially use
00973                 break;
00974 
00975             case MIDI_SET_TEMPO:
00976                 // Already dealt with in a separate pass previously
00977                 break;
00978 
00979             case MIDI_TIME_SIGNATURE:
00980                 // Not yet!
00981                 break;
00982 
00983             case MIDI_SEQUENCE_NUMBER:
00984             case MIDI_CHANNEL_PREFIX_OR_PORT:
00985             case MIDI_INSTRUMENT_NAME:
00986             case MIDI_CUE_POINT:
00987             case MIDI_CHANNEL_PREFIX:
00988             case MIDI_SEQUENCER_SPECIFIC:
00989             case MIDI_SMPTE_OFFSET:
00990             default:
00991                 break;
00992             }
00993 
00994         } else {
00995 
00996             switch ((*i)->getMessageType()) {
00997 
00998             case MIDI_NOTE_ON:
00999 
01000                 if ((*i)->getVelocity() == 0) break; // effective note-off
01001                 else {
01002                     RealTime endRT;
01003                     unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration();
01004                     if (m_smpte) {
01005                         endRT = RealTime::frame2RealTime(endMidiTime, m_fps * m_subframes);
01006                     } else {
01007                         endRT = getTimeForMIDITime(endMidiTime);
01008                     }
01009 
01010                     long startFrame = RealTime::realTime2Frame
01011                         (rt, model->getSampleRate());
01012 
01013                     long endFrame = RealTime::realTime2Frame
01014                         (endRT, model->getSampleRate());
01015 
01016                     QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(),
01017                                                               0, 
01018                                                               !sharpKey);
01019 
01020                     QString noteLabel = tr("%1 - vel %2")
01021                         .arg(pitchLabel).arg(int((*i)->getVelocity()));
01022 
01023                     float level = float((*i)->getVelocity()) / 128.f;
01024 
01025                     Note note(startFrame, (*i)->getPitch(),
01026                               endFrame - startFrame, level, noteLabel);
01027 
01028 //                  SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl;
01029 
01030                     model->addPoint(note);
01031                     break;
01032                 }
01033 
01034             case MIDI_PITCH_BEND:
01035                 // I guess we could make some use of this...
01036                 break;
01037 
01038             case MIDI_NOTE_OFF:
01039             case MIDI_PROG_CHANGE:
01040             case MIDI_CTRL_CHANGE:
01041             case MIDI_SYSTEM_EXCLUSIVE:
01042             case MIDI_POLY_AFTERTOUCH:
01043             case MIDI_CHNL_AFTERTOUCH:
01044                 break;
01045 
01046             default:
01047                 break;
01048             }
01049         }
01050 
01051         model->setCompletion(minProgress +
01052                              (count * progressAmount) / totalEvents);
01053         ++count;
01054     }
01055 
01056     return model;
01057 }
01058 
01059