00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
00134
00135
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
00163
00164
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
00190
00191
00192 if (stringRet.length() < numberOfBytes) {
00193 stringRet = "";
00194 throw MIDIException(tr("Attempt to read past MIDI file end"));
00195 }
00196
00197
00198 if (m_decrementCount)
00199 m_trackByteCount -= stringRet.length();
00200
00201 return stringRet;
00202 }
00203
00204
00205
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
00239
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) {
00261 return false;
00262 } else {
00263 return true;
00264 }
00265 }
00266
00267
00268
00269
00270
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
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
00298
00299 m_midiFile->seekg(0, ios::end);
00300 m_fileSize = m_midiFile->tellg();
00301 m_midiFile->seekg(0, ios::beg);
00302
00303
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
00332
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;
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
00361
00362
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)) {
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
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
00440
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
00453
00454
00455
00456
00457
00458
00459
00460
00461 vector<int> channelTrackMap(16, -1);
00462
00463
00464
00465
00466
00467 map<int, unsigned long> trackTimeMap;
00468
00469
00470
00471 unsigned int metaTrack = lastTrackNum;
00472
00473
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
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
00523 cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
00524
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 {
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
00559
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
00575 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
00576
00577
00578
00579
00580
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
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
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
00628
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
00667
00668
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
00704
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
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
00797
00798
00799
00800
00801
00802
00803
00804
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
00962
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
00979 break;
00980
00981 case MIDI_SET_TEMPO:
00982
00983 break;
00984
00985 case MIDI_TIME_SIGNATURE:
00986
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;
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
01030
01031 model->addPoint(note);
01032 break;
01033 }
01034
01035 case MIDI_PITCH_BEND:
01036
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