svcore  1.9
MIDIFileReader.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7 
8  This program is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 2 of the
11  License, or (at your option) any later version. See the file
12  COPYING included with this distribution for more information.
13 */
14 
15 
16 /*
17  This is a modified version of a source file from the
18  Rosegarden MIDI and audio sequencer and notation editor.
19  This file copyright 2000-2006 Richard Bown and Chris Cannam.
20 */
21 
22 
23 #include <iostream>
24 #include <fstream>
25 #include <string>
26 #include <cstdio>
27 #include <algorithm>
28 
29 #include "MIDIFileReader.h"
30 
31 #include "data/midi/MIDIEvent.h"
32 
33 #include "model/Model.h"
34 #include "base/Pitch.h"
35 #include "base/RealTime.h"
36 #include "model/NoteModel.h"
37 
38 #include <QString>
39 
40 #include <sstream>
41 
42 #include "base/Debug.h"
43 
44 using std::string;
45 using std::ifstream;
46 using std::stringstream;
47 using std::ends;
48 using std::ios;
49 using std::vector;
50 using std::map;
51 using std::set;
52 
53 using namespace MIDIConstants;
54 
55 //#define MIDI_SVDEBUG 1
56 
57 
60  int mainModelSampleRate) :
61  m_smpte(false),
62  m_timingDivision(0),
63  m_fps(0),
64  m_subframes(0),
65  m_format(MIDI_FILE_BAD_FORMAT),
66  m_numberOfTracks(0),
67  m_trackByteCount(0),
68  m_decrementCount(false),
69  m_path(path),
70  m_midiFile(0),
71  m_fileSize(0),
72  m_mainModelSampleRate(mainModelSampleRate),
73  m_acquirer(acquirer)
74 {
75  if (parseFile()) {
76  m_error = "";
77  }
78 }
79 
81 {
82  for (MIDIComposition::iterator i = m_midiComposition.begin();
83  i != m_midiComposition.end(); ++i) {
84 
85  for (MIDITrack::iterator j = i->second.begin();
86  j != i->second.end(); ++j) {
87  delete *j;
88  }
89 
90  i->second.clear();
91  }
92 
93  m_midiComposition.clear();
94 }
95 
96 bool
98 {
99  return (m_error == "");
100 }
101 
102 QString
104 {
105  return m_error;
106 }
107 
108 long
110 {
111  if (bytes.length() != 4) {
112  throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4));
113  }
114 
115  long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
116  ((long)(((MIDIByte)bytes[1]) << 16)) |
117  ((long)(((MIDIByte)bytes[2]) << 8)) |
118  ((long)((MIDIByte)(bytes[3])));
119 
120  return longRet;
121 }
122 
123 int
124 MIDIFileReader::midiBytesToInt(const string& bytes)
125 {
126  if (bytes.length() != 2) {
127  throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2));
128  }
129 
130  int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
131  ((int)(((MIDIByte)bytes[1])));
132  return(intRet);
133 }
134 
135 
136 // Gets a single byte from the MIDI byte stream. For each track
137 // section we can read only a specified number of bytes held in
138 // m_trackByteCount.
139 //
140 MIDIByte
142 {
143  if (!m_midiFile) {
144  throw MIDIException(tr("getMIDIByte called but no MIDI file open"));
145  }
146 
147  if (m_midiFile->eof()) {
148  throw MIDIException(tr("End of MIDI file encountered while reading"));
149  }
150 
151  if (m_decrementCount && m_trackByteCount <= 0) {
152  throw MIDIException(tr("Attempt to get more bytes than expected on Track"));
153  }
154 
155  char byte;
156  if (m_midiFile->read(&byte, 1)) {
158  return (MIDIByte)byte;
159  }
160 
161  throw MIDIException(tr("Attempt to read past MIDI file end"));
162 }
163 
164 
165 // Gets a specified number of bytes from the MIDI byte stream. For
166 // each track section we can read only a specified number of bytes
167 // held in m_trackByteCount.
168 //
169 string
170 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
171 {
172  if (!m_midiFile) {
173  throw MIDIException(tr("getMIDIBytes called but no MIDI file open"));
174  }
175 
176  if (m_midiFile->eof()) {
177  throw MIDIException(tr("End of MIDI file encountered while reading"));
178  }
179 
180  if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
181  throw MIDIException(tr("Attempt to get more bytes than available on Track (%1, only have %2)").arg(numberOfBytes).arg(m_trackByteCount));
182  }
183 
184  string stringRet;
185  char fileMIDIByte;
186 
187  while (stringRet.length() < numberOfBytes &&
188  m_midiFile->read(&fileMIDIByte, 1)) {
189  stringRet += fileMIDIByte;
190  }
191 
192  // if we've reached the end of file without fulfilling the
193  // quota then panic as our parsing has performed incorrectly
194  //
195  if (stringRet.length() < numberOfBytes) {
196  stringRet = "";
197  throw MIDIException(tr("Attempt to read past MIDI file end"));
198  }
199 
200  // decrement the byte count
201  if (m_decrementCount)
202  m_trackByteCount -= stringRet.length();
203 
204  return stringRet;
205 }
206 
207 
208 // Get a long number of variable length from the MIDI byte stream.
209 //
210 long
212 {
213  if (!m_midiFile) {
214  throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open"));
215  }
216 
217  long longRet = 0;
218  MIDIByte midiByte;
219 
220  if (firstByte >= 0) {
221  midiByte = (MIDIByte)firstByte;
222  } else if (m_midiFile->eof()) {
223  return longRet;
224  } else {
225  midiByte = getMIDIByte();
226  }
227 
228  longRet = midiByte;
229  if (midiByte & 0x80) {
230  longRet &= 0x7F;
231  do {
232  midiByte = getMIDIByte();
233  longRet = (longRet << 7) + (midiByte & 0x7F);
234  } while (!m_midiFile->eof() && (midiByte & 0x80));
235  }
236 
237  return longRet;
238 }
239 
240 
241 // Seek to the next track in the midi file and set the number
242 // of bytes to be read in the counter m_trackByteCount.
243 //
244 bool
246 {
247  if (!m_midiFile) {
248  throw MIDIException(tr("skipToNextTrack called but no MIDI file open"));
249  }
250 
251  string buffer, buffer2;
252  m_trackByteCount = -1;
253  m_decrementCount = false;
254 
255  while (!m_midiFile->eof() && (m_decrementCount == false)) {
256  buffer = getMIDIBytes(4);
257  if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
259  m_decrementCount = true;
260  }
261  }
262 
263  if (m_trackByteCount == -1) { // we haven't found a track
264  return false;
265  } else {
266  return true;
267  }
268 }
269 
270 
271 // Read in a MIDI file. The parsing process throws exceptions back up
272 // here if we run into trouble which we can then pass back out to
273 // whoever called us using a nice bool.
274 //
275 bool
277 {
278  m_error = "";
279 
280 #ifdef MIDI_DEBUG
281  SVDEBUG << "MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl;
282 #endif
283 
284  // Open the file
285  m_midiFile = new ifstream(m_path.toLocal8Bit().data(),
286  ios::in | ios::binary);
287 
288  if (!*m_midiFile) {
289  m_error = "File not found or not readable.";
291  delete m_midiFile;
292  m_midiFile = 0;
293  return false;
294  }
295 
296  bool retval = false;
297 
298  try {
299 
300  // Set file size so we can count it off
301  //
302  m_midiFile->seekg(0, ios::end);
303  m_fileSize = m_midiFile->tellg();
304  m_midiFile->seekg(0, ios::beg);
305 
306  // Parse the MIDI header first. The first 14 bytes of the file.
307  if (!parseHeader(getMIDIBytes(14))) {
309  m_error = "Not a MIDI file.";
310  goto done;
311  }
312 
313  unsigned int i = 0;
314 
315  for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
316 
317 #ifdef MIDI_DEBUG
318  SVDEBUG << "Parsing Track " << j << endl;
319 #endif
320 
321  if (!skipToNextTrack()) {
322 #ifdef MIDI_DEBUG
323  cerr << "Couldn't find Track " << j << endl;
324 #endif
325  m_error = "File corrupted or in non-standard format?";
327  goto done;
328  }
329 
330 #ifdef MIDI_DEBUG
331  cerr << "Track has " << m_trackByteCount << " bytes" << endl;
332 #endif
333 
334  // Run through the events taking them into our internal
335  // representation.
336  if (!parseTrack(i)) {
337 #ifdef MIDI_DEBUG
338  cerr << "Track " << j << " parsing failed" << endl;
339 #endif
340  m_error = "File corrupted or in non-standard format?";
342  goto done;
343  }
344 
345  ++i; // j is the source track number, i the destination
346  }
347 
348  m_numberOfTracks = i;
349  retval = true;
350 
351  } catch (MIDIException e) {
352 
353  SVDEBUG << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
354  m_error = e.what();
355  }
356 
357 done:
358  m_midiFile->close();
359  delete m_midiFile;
360 
361  for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
362 
363  // Convert the deltaTime to an absolute time since the track
364  // start. The addTime method returns the sum of the current
365  // MIDI Event delta time plus the argument.
366 
367  unsigned long acc = 0;
368 
369  for (MIDITrack::iterator i = m_midiComposition[track].begin();
370  i != m_midiComposition[track].end(); ++i) {
371  acc = (*i)->addTime(acc);
372  }
373 
374  if (consolidateNoteOffEvents(track)) { // returns true if some notes exist
375  m_loadableTracks.insert(track);
376  }
377  }
378 
379  for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
380  updateTempoMap(track);
381  }
382 
384 
385  return retval;
386 }
387 
388 // Parse and ensure the MIDI Header is legitimate
389 //
390 bool
391 MIDIFileReader::parseHeader(const string &midiHeader)
392 {
393  if (midiHeader.size() < 14) {
394 #ifdef MIDI_DEBUG
395  SVDEBUG << "MIDIFileReader::parseHeader() - file header undersized" << endl;
396 #endif
397  return false;
398  }
399 
400  if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
401 #ifdef MIDI_DEBUG
402  SVDEBUG << "MIDIFileReader::parseHeader()"
403  << "- file header not found or malformed"
404  << endl;
405 #endif
406  return false;
407  }
408 
409  if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
410 #ifdef MIDI_DEBUG
411  SVDEBUG << "MIDIFileReader::parseHeader()"
412  << " - header length incorrect"
413  << endl;
414 #endif
415  return false;
416  }
417 
418  m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
419  m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
420  m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
421 
422  if (m_timingDivision >= 32768) {
423  m_smpte = true;
424  m_fps = 256 - (m_timingDivision >> 8);
425  m_subframes = (m_timingDivision & 0xff);
426  } else {
427  m_smpte = false;
428  }
429 
430  return true;
431 }
432 
433 // Extract the contents from a MIDI file track and places it into
434 // our local map of MIDI events.
435 //
436 bool
437 MIDIFileReader::parseTrack(unsigned int &lastTrackNum)
438 {
439  MIDIByte midiByte, metaEventCode, data1, data2;
440  MIDIByte eventCode = 0x80;
441  string metaMessage;
442  unsigned int messageLength;
443  unsigned long deltaTime;
444  unsigned long accumulatedTime = 0;
445 
446  // The trackNum passed in to this method is the default track for
447  // all events provided they're all on the same channel. If we find
448  // events on more than one channel, we increment trackNum and record
449  // the mapping from channel to trackNum in this channelTrackMap.
450  // We then return the new trackNum by reference so the calling
451  // method knows we've got more tracks than expected.
452 
453  // This would be a vector<unsigned int> but we need -1 to indicate
454  // "not yet used"
455  vector<int> channelTrackMap(16, -1);
456 
457  // This is used to store the last absolute time found on each track,
458  // allowing us to modify delta-times correctly when separating events
459  // out from one to multiple tracks
460  //
461  map<int, unsigned long> trackTimeMap;
462 
463  // Meta-events don't have a channel, so we place them in a fixed
464  // track number instead
465  unsigned int metaTrack = lastTrackNum;
466 
467  // Remember the last non-meta status byte (-1 if we haven't seen one)
468  int runningStatus = -1;
469 
470  bool firstTrack = true;
471 
472  while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
473 
474  if (eventCode < 0x80) {
475 #ifdef MIDI_DEBUG
476  cerr << "WARNING: Invalid event code " << eventCode
477  << " in MIDI file" << endl;
478 #endif
479  throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode)));
480  }
481 
482  deltaTime = getNumberFromMIDIBytes();
483 
484 #ifdef MIDI_DEBUG
485  cerr << "read delta time " << deltaTime << endl;
486 #endif
487 
488  // Get a single byte
489  midiByte = getMIDIByte();
490 
491  if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
492 
493  if (runningStatus < 0) {
494  throw MIDIException(tr("Running status used for first event in track"));
495  }
496 
497  eventCode = (MIDIByte)runningStatus;
498  data1 = midiByte;
499 
500 #ifdef MIDI_DEBUG
501  SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl;
502 #endif
503  } else {
504 #ifdef MIDI_DEBUG
505  cerr << "have new event code " << int(midiByte) << endl;
506 #endif
507  eventCode = midiByte;
508  data1 = getMIDIByte();
509  }
510 
511  if (eventCode == MIDI_FILE_META_EVENT) {
512 
513  metaEventCode = data1;
514  messageLength = getNumberFromMIDIBytes();
515 
516 //#ifdef MIDI_DEBUG
517  cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
518 //#endif
519  metaMessage = getMIDIBytes(messageLength);
520 
521  long gap = accumulatedTime - trackTimeMap[metaTrack];
522  accumulatedTime += deltaTime;
523  deltaTime += gap;
524  trackTimeMap[metaTrack] = accumulatedTime;
525 
526  MIDIEvent *e = new MIDIEvent(deltaTime,
528  metaEventCode,
529  metaMessage);
530 
531  m_midiComposition[metaTrack].push_back(e);
532 
533  if (metaEventCode == MIDI_TRACK_NAME) {
534  m_trackNames[metaTrack] = metaMessage.c_str();
535  }
536 
537  } else { // non-meta events
538 
539  runningStatus = eventCode;
540 
541  MIDIEvent *midiEvent;
542 
543  int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
544  if (channelTrackMap[channel] == -1) {
545  if (!firstTrack) ++lastTrackNum;
546  else firstTrack = false;
547  channelTrackMap[channel] = lastTrackNum;
548  }
549 
550  unsigned int trackNum = channelTrackMap[channel];
551 
552  // accumulatedTime is abs time of last event on any track;
553  // trackTimeMap[trackNum] is that of last event on this track
554 
555  long gap = accumulatedTime - trackTimeMap[trackNum];
556  accumulatedTime += deltaTime;
557  deltaTime += gap;
558  trackTimeMap[trackNum] = accumulatedTime;
559 
560  switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
561 
562  case MIDI_NOTE_ON:
563  case MIDI_NOTE_OFF:
565  case MIDI_CTRL_CHANGE:
566  data2 = getMIDIByte();
567 
568  // create and store our event
569  midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
570 
571  /*
572  cerr << "MIDI event for channel " << channel << " (track "
573  << trackNum << ")" << endl;
574  midiEvent->print();
575  */
576 
577 
578  m_midiComposition[trackNum].push_back(midiEvent);
579 
580  if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) {
581  m_percussionTracks.insert(trackNum);
582  }
583 
584  break;
585 
586  case MIDI_PITCH_BEND:
587  data2 = getMIDIByte();
588 
589  // create and store our event
590  midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
591  m_midiComposition[trackNum].push_back(midiEvent);
592  break;
593 
594  case MIDI_PROG_CHANGE:
596  // create and store our event
597  midiEvent = new MIDIEvent(deltaTime, eventCode, data1);
598  m_midiComposition[trackNum].push_back(midiEvent);
599  break;
600 
602  messageLength = getNumberFromMIDIBytes(data1);
603 
604 #ifdef MIDI_DEBUG
605  cerr << "SysEx of " << messageLength << " bytes found" << endl;
606 #endif
607 
608  metaMessage= getMIDIBytes(messageLength);
609 
610  if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
612  {
613 #ifdef MIDI_DEBUG
614  SVDEBUG << "MIDIFileReader::parseTrack() - "
615  << "malformed or unsupported SysEx type"
616  << endl;
617 #endif
618  continue;
619  }
620 
621  // chop off the EOX
622  // length fixed by Pedro Lopez-Cabanillas (20030523)
623  //
624  metaMessage = metaMessage.substr(0, metaMessage.length()-1);
625 
626  midiEvent = new MIDIEvent(deltaTime,
628  metaMessage);
629  m_midiComposition[trackNum].push_back(midiEvent);
630  break;
631 
632  default:
633 #ifdef MIDI_DEBUG
634  SVDEBUG << "MIDIFileReader::parseTrack()"
635  << " - Unsupported MIDI Event Code: "
636  << (int)eventCode << endl;
637 #endif
638  break;
639  }
640  }
641  }
642 
643  if (lastTrackNum > metaTrack) {
644  for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) {
645  m_trackNames[track] = QString("%1 <%2>")
646  .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1);
647  }
648  }
649 
650  return true;
651 }
652 
653 // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
654 // reading them and modifying their relevant NOTE ONs. Return true
655 // if there are some notes in this track.
656 //
657 bool
659 {
660  bool notesOnTrack = false;
661  bool noteOffFound;
662 
663  for (MIDITrack::iterator i = m_midiComposition[track].begin();
664  i != m_midiComposition[track].end(); i++) {
665 
666  if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) {
667 
668  notesOnTrack = true;
669  noteOffFound = false;
670 
671  for (MIDITrack::iterator j = i;
672  j != m_midiComposition[track].end(); j++) {
673 
674  if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) &&
675  ((*j)->getPitch() == (*i)->getPitch()) &&
676  ((*j)->getMessageType() == MIDI_NOTE_OFF ||
677  ((*j)->getMessageType() == MIDI_NOTE_ON &&
678  (*j)->getVelocity() == 0x00))) {
679 
680  (*i)->setDuration((*j)->getTime() - (*i)->getTime());
681 
682  delete *j;
683  m_midiComposition[track].erase(j);
684 
685  noteOffFound = true;
686  break;
687  }
688  }
689 
690  // If no matching NOTE OFF has been found then set
691  // Event duration to length of track
692  //
693  if (!noteOffFound) {
694  MIDITrack::iterator j = m_midiComposition[track].end();
695  --j;
696  (*i)->setDuration((*j)->getTime() - (*i)->getTime());
697  }
698  }
699  }
700 
701  return notesOnTrack;
702 }
703 
704 // Add any tempo events found in the given track to the global tempo map.
705 //
706 void
708 {
709  cerr << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl;
710 
711  for (MIDITrack::iterator i = m_midiComposition[track].begin();
712  i != m_midiComposition[track].end(); ++i) {
713 
714  if ((*i)->isMeta() &&
715  (*i)->getMetaEventCode() == MIDI_SET_TEMPO) {
716 
717  MIDIByte m0 = (*i)->getMetaMessage()[0];
718  MIDIByte m1 = (*i)->getMetaMessage()[1];
719  MIDIByte m2 = (*i)->getMetaMessage()[2];
720 
721  long tempo = (((m0 << 8) + m1) << 8) + m2;
722 
723  cerr << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl;
724 
725  if (tempo != 0) {
726  double qpm = 60000000.0 / double(tempo);
727  m_tempoMap[(*i)->getTime()] =
729  }
730  }
731  }
732 }
733 
734 void
736 {
737  unsigned long lastMIDITime = 0;
738  RealTime lastRealTime = RealTime::zeroTime;
739  double tempo = 120.0;
740  int td = m_timingDivision;
741  if (td == 0) td = 96;
742 
743  for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) {
744 
745  unsigned long mtime = i->first;
746  unsigned long melapsed = mtime - lastMIDITime;
747  double quarters = double(melapsed) / double(td);
748  double seconds = (60.0 * quarters) / tempo;
749 
750  RealTime t = lastRealTime + RealTime::fromSeconds(seconds);
751 
752  i->second.first = t;
753 
754  lastRealTime = t;
755  lastMIDITime = mtime;
756  tempo = i->second.second;
757  }
758 }
759 
760 RealTime
761 MIDIFileReader::getTimeForMIDITime(unsigned long midiTime) const
762 {
763  unsigned long tempoMIDITime = 0;
764  RealTime tempoRealTime = RealTime::zeroTime;
765  double tempo = 120.0;
766 
767  TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime);
768  if (i != m_tempoMap.begin()) {
769  --i;
770  tempoMIDITime = i->first;
771  tempoRealTime = i->second.first;
772  tempo = i->second.second;
773  }
774 
775  int td = m_timingDivision;
776  if (td == 0) td = 96;
777 
778  unsigned long melapsed = midiTime - tempoMIDITime;
779  double quarters = double(melapsed) / double(td);
780  double seconds = (60.0 * quarters) / tempo;
781 
782 /*
783  SVDEBUG << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")"
784  << endl;
785  SVDEBUG << "timing division = " << td << endl;
786  cerr << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " ("
787  << tempoRealTime << ")" << endl;
788  cerr << "quarters since then = " << quarters << endl;
789  cerr << "tempo = " << tempo << " quarters per minute" << endl;
790  cerr << "seconds since then = " << seconds << endl;
791  SVDEBUG << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << endl;
792 */
793 
794  return tempoRealTime + RealTime::fromSeconds(seconds);
795 }
796 
797 Model *
799 {
800  if (!isOK()) return 0;
801 
802  if (m_loadableTracks.empty()) {
803  if (m_acquirer) {
805  (tr("MIDI file \"%1\" has no notes in any track").arg(m_path));
806  }
807  return 0;
808  }
809 
810  std::set<unsigned int> tracksToLoad;
811 
812  if (m_loadableTracks.size() == 1) {
813 
814  tracksToLoad.insert(*m_loadableTracks.begin());
815 
816  } else {
817 
818  QStringList displayNames;
819 
820  for (set<unsigned int>::iterator i = m_loadableTracks.begin();
821  i != m_loadableTracks.end(); ++i) {
822 
823  unsigned int trackNo = *i;
824  QString label;
825 
826  QString perc;
827  if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) {
828  perc = tr(" - uses GM percussion channel");
829  }
830 
831  if (m_trackNames.find(trackNo) != m_trackNames.end()) {
832  label = tr("Track %1 (%2)%3")
833  .arg(trackNo).arg(m_trackNames.find(trackNo)->second)
834  .arg(perc);
835  } else {
836  label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc);
837  }
838 
839  displayNames << label;
840  }
841 
842  QString singleTrack;
843 
844  bool haveSomePercussion =
845  (!m_percussionTracks.empty() &&
846  (m_percussionTracks.size() < m_loadableTracks.size()));
847 
849 
850  if (m_acquirer) {
851  pref = m_acquirer->getTrackImportPreference(displayNames,
852  haveSomePercussion,
853  singleTrack);
854  } else {
856  }
857 
859 
862 
863  for (set<unsigned int>::iterator i = m_loadableTracks.begin();
864  i != m_loadableTracks.end(); ++i) {
865 
867  m_percussionTracks.find(*i) == m_percussionTracks.end()) {
868 
869  tracksToLoad.insert(*i);
870  }
871  }
872 
873  } else {
874 
875  int j = 0;
876 
877  for (set<unsigned int>::iterator i = m_loadableTracks.begin();
878  i != m_loadableTracks.end(); ++i) {
879 
880  if (singleTrack == displayNames[j]) {
881  tracksToLoad.insert(*i);
882  break;
883  }
884 
885  ++j;
886  }
887  }
888  }
889 
890  if (tracksToLoad.empty()) return 0;
891 
892  int n = tracksToLoad.size(), count = 0;
893  Model *model = 0;
894 
895  for (std::set<unsigned int>::iterator i = tracksToLoad.begin();
896  i != tracksToLoad.end(); ++i) {
897 
898  int minProgress = (100 * count) / n;
899  int progressAmount = 100 / n;
900 
901  model = loadTrack(*i, model, minProgress, progressAmount);
902 
903  ++count;
904  }
905 
906  if (dynamic_cast<NoteModel *>(model)) {
907  dynamic_cast<NoteModel *>(model)->setCompletion(100);
908  }
909 
910  return model;
911 }
912 
913 Model *
914 MIDIFileReader::loadTrack(unsigned int trackToLoad,
915  Model *existingModel,
916  int minProgress,
917  int progressAmount) const
918 {
919  if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) {
920  return 0;
921  }
922 
923  NoteModel *model = 0;
924 
925  if (existingModel) {
926  model = dynamic_cast<NoteModel *>(existingModel);
927  if (!model) {
928  cerr << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl;
929  }
930  }
931 
932  if (!model) {
933  model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false);
934  model->setValueQuantization(1.0);
935  }
936 
937  const MIDITrack &track = m_midiComposition.find(trackToLoad)->second;
938 
939  int totalEvents = track.size();
940  int count = 0;
941 
942  bool sharpKey = true;
943 
944  for (MIDITrack::const_iterator i = track.begin(); i != track.end(); ++i) {
945 
946  RealTime rt;
947  unsigned long midiTime = (*i)->getTime();
948 
949  if (m_smpte) {
950  rt = RealTime::frame2RealTime(midiTime, m_fps * m_subframes);
951  } else {
952  rt = getTimeForMIDITime(midiTime);
953  }
954 
955  // We ignore most of these event types for now, though in
956  // theory some of the text ones could usefully be incorporated
957 
958  if ((*i)->isMeta()) {
959 
960  switch((*i)->getMetaEventCode()) {
961 
962  case MIDI_KEY_SIGNATURE:
963  // minorKey = (int((*i)->getMetaMessage()[1]) != 0);
964  sharpKey = (int((*i)->getMetaMessage()[0]) >= 0);
965  break;
966 
967  case MIDI_TEXT_EVENT:
968  case MIDI_LYRIC:
969  case MIDI_TEXT_MARKER:
971  case MIDI_TRACK_NAME:
972  // The text events that we could potentially use
973  break;
974 
975  case MIDI_SET_TEMPO:
976  // Already dealt with in a separate pass previously
977  break;
978 
979  case MIDI_TIME_SIGNATURE:
980  // Not yet!
981  break;
982 
986  case MIDI_CUE_POINT:
987  case MIDI_CHANNEL_PREFIX:
989  case MIDI_SMPTE_OFFSET:
990  default:
991  break;
992  }
993 
994  } else {
995 
996  switch ((*i)->getMessageType()) {
997 
998  case MIDI_NOTE_ON:
999 
1000  if ((*i)->getVelocity() == 0) break; // effective note-off
1001  else {
1002  RealTime endRT;
1003  unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration();
1004  if (m_smpte) {
1005  endRT = RealTime::frame2RealTime(endMidiTime, m_fps * m_subframes);
1006  } else {
1007  endRT = getTimeForMIDITime(endMidiTime);
1008  }
1009 
1010  long startFrame = RealTime::realTime2Frame
1011  (rt, model->getSampleRate());
1012 
1013  long endFrame = RealTime::realTime2Frame
1014  (endRT, model->getSampleRate());
1015 
1016  QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(),
1017  0,
1018  !sharpKey);
1019 
1020  QString noteLabel = tr("%1 - vel %2")
1021  .arg(pitchLabel).arg(int((*i)->getVelocity()));
1022 
1023  float level = float((*i)->getVelocity()) / 128.f;
1024 
1025  Note note(startFrame, (*i)->getPitch(),
1026  endFrame - startFrame, level, noteLabel);
1027 
1028 // SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl;
1029 
1030  model->addPoint(note);
1031  break;
1032  }
1033 
1034  case MIDI_PITCH_BEND:
1035  // I guess we could make some use of this...
1036  break;
1037 
1038  case MIDI_NOTE_OFF:
1039  case MIDI_PROG_CHANGE:
1040  case MIDI_CTRL_CHANGE:
1041  case MIDI_SYSTEM_EXCLUSIVE:
1042  case MIDI_POLY_AFTERTOUCH:
1043  case MIDI_CHNL_AFTERTOUCH:
1044  break;
1045 
1046  default:
1047  break;
1048  }
1049  }
1050 
1051  model->setCompletion(minProgress +
1052  (count * progressAmount) / totalEvents);
1053  ++count;
1054  }
1055 
1056  return model;
1057 }
1058 
1059 
std::set< unsigned int > m_percussionTracks
static const MIDIByte MIDI_KEY_SIGNATURE
Definition: MIDIEvent.h:92
Model * loadTrack(unsigned int trackNum, Model *existingModel=0, int minProgress=0, int progressAmount=100) const
static const MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT
Definition: MIDIEvent.h:87
static RealTime frame2RealTime(long frame, unsigned int sampleRate)
Convert a sample frame at the given sample rate into a RealTime.
Definition: RealTime.cpp:450
static const MIDIByte MIDI_COPYRIGHT_NOTICE
Definition: MIDIEvent.h:80
TempoMap m_tempoMap
std::pair< RealTime, double > TempoChange
static const MIDIByte MIDI_CHANNEL_PREFIX
Definition: MIDIEvent.h:86
MIDIComposition m_midiComposition
static const MIDIByte MIDI_CHANNEL_NUM_MASK
Definition: MIDIEvent.h:39
virtual int getSampleRate() const
Return the frame rate in frames per second.
Definition: SparseModel.h:53
unsigned int m_numberOfTracks
static const char *const MIDI_TRACK_HEADER
Definition: MIDIEvent.h:35
NoteModel – a concrete IntervalModel for notes.
Definition: NoteModel.h:38
static RealTime fromSeconds(double sec)
Definition: RealTime.cpp:60
virtual QString getError() const
bool parseHeader(const std::string &midiHeader)
static const MIDIByte MIDI_TRACK_NAME
Definition: MIDIEvent.h:81
MIDIFileFormatType m_format
long midiBytesToLong(const std::string &bytes)
MIDIFileReader(QString path, MIDIFileImportPreferenceAcquirer *pref, int mainModelSampleRate)
static const MIDIByte MIDI_TEXT_MARKER
Definition: MIDIEvent.h:84
MIDIByte getMIDIByte()
static const MIDIByte MIDI_SEQUENCE_NUMBER
Definition: MIDIEvent.h:78
static const MIDIByte MIDI_TEXT_EVENT
Definition: MIDIEvent.h:79
long getNumberFromMIDIBytes(int firstByte=-1)
static const MIDIByte MIDI_SMPTE_OFFSET
Definition: MIDIEvent.h:90
static QString getPitchLabel(int midiPitch, float centsOffset=0, bool useFlats=false)
Return a string describing the given MIDI pitch, with optional cents offset.
Definition: Pitch.cpp:100
RealTime getTimeForMIDITime(unsigned long midiTime) const
static const MIDIByte MIDI_PERCUSSION_CHANNEL
Definition: MIDIEvent.h:114
std::ifstream * m_midiFile
MIDIFileImportPreferenceAcquirer * m_acquirer
static const MIDIByte MIDI_LYRIC
Definition: MIDIEvent.h:83
int midiBytesToInt(const std::string &bytes)
static const MIDIByte MIDI_CUE_POINT
Definition: MIDIEvent.h:85
virtual TrackPreference getTrackImportPreference(QStringList trackNames, bool haveSomePercussion, QString &singleTrack) const =0
static const MIDIByte MIDI_FILE_META_EVENT
Definition: MIDIEvent.h:77
static const MIDIByte MIDI_SET_TEMPO
Definition: MIDIEvent.h:89
void setValueQuantization(float q)
Definition: NoteModel.h:126
static const MIDIByte MIDI_INSTRUMENT_NAME
Definition: MIDIEvent.h:82
static const MIDIByte MIDI_SEQUENCER_SPECIFIC
Definition: MIDIEvent.h:93
static const MIDIByte MIDI_STATUS_BYTE_MASK
Definition: MIDIEvent.h:37
virtual void addPoint(const PointType &point)
Add a point.
bool parseTrack(unsigned int &trackNum)
virtual const char * what() const
Definition: MIDIEvent.h:221
Model is the base class for all data models that represent any sort of data on a time scale based on ...
Definition: Model.h:35
bool consolidateNoteOffEvents(unsigned int track)
static const MIDIByte MIDI_PITCH_BEND
Definition: MIDIEvent.h:47
void calculateTempoTimestamps()
MIDIByte getChannelNumber() const
Definition: MIDIEvent.h:169
std::map< int, QString > m_trackNames
#define SVDEBUG
Definition: Debug.h:42
void updateTempoMap(unsigned int track)
static long realTime2Frame(const RealTime &r, unsigned int sampleRate)
Convert a RealTime into a sample frame at the given sample rate.
Definition: RealTime.cpp:442
virtual bool isOK() const
Return true if the file appears to be of the correct type.
virtual void showError(QString error)=0
virtual ~MIDIFileReader()
static const MIDIByte MIDI_CTRL_CHANGE
Definition: MIDIEvent.h:44
static const MIDIByte MIDI_POLY_AFTERTOUCH
Definition: MIDIEvent.h:43
static const MIDIByte MIDI_SYSTEM_EXCLUSIVE
Definition: MIDIEvent.h:49
std::string getMIDIBytes(unsigned long bytes)
static const MIDIByte MIDI_NOTE_OFF
Definition: MIDIEvent.h:41
static const char *const MIDI_FILE_HEADER
Definition: MIDIEvent.h:34
static const MIDIByte MIDI_PROG_CHANGE
Definition: MIDIEvent.h:45
virtual void setCompletion(int completion, bool update=true)
Definition: SparseModel.h:761
virtual Model * load() const
Read the file and return the corresponding data model.
static const MIDIByte MIDI_TIME_SIGNATURE
Definition: MIDIEvent.h:91
static const RealTime zeroTime
Definition: RealTime.h:159
std::set< unsigned int > m_loadableTracks
static const MIDIByte MIDI_MESSAGE_TYPE_MASK
Definition: MIDIEvent.h:38
std::vector< MIDIEvent * > MIDITrack
static const MIDIByte MIDI_NOTE_ON
Definition: MIDIEvent.h:42
unsigned char MIDIByte
static const MIDIByte MIDI_END_OF_EXCLUSIVE
Definition: MIDIEvent.h:54
static const MIDIByte MIDI_CHNL_AFTERTOUCH
Definition: MIDIEvent.h:46
RealTime represents time values to nanosecond precision with accurate arithmetic and frame-rate conve...
Definition: RealTime.h:35