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