svcore
1.9
|
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