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

Generated on Wed Feb 20 15:45:26 2008 for SonicVisualiser by  doxygen 1.5.1