46 using std::stringstream;
60 int mainModelSampleRate) :
65 m_format(MIDI_FILE_BAD_FORMAT),
68 m_decrementCount(false),
72 m_mainModelSampleRate(mainModelSampleRate),
85 for (MIDITrack::iterator j = i->second.begin();
86 j != i->second.end(); ++j) {
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));
115 long longRet = ((long)(((
MIDIByte)bytes[0]) << 24)) |
116 ((
long)(((
MIDIByte)bytes[1]) << 16)) |
117 ((long)(((
MIDIByte)bytes[2]) << 8)) |
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));
130 int intRet = ((int)(((
MIDIByte)bytes[0]) << 8)) |
144 throw MIDIException(tr(
"getMIDIByte called but no MIDI file open"));
148 throw MIDIException(tr(
"End of MIDI file encountered while reading"));
152 throw MIDIException(tr(
"Attempt to get more bytes than expected on Track"));
161 throw MIDIException(tr(
"Attempt to read past MIDI file end"));
173 throw MIDIException(tr(
"getMIDIBytes called but no MIDI file open"));
177 throw MIDIException(tr(
"End of MIDI file encountered while reading"));
187 while (stringRet.length() < numberOfBytes &&
189 stringRet += fileMIDIByte;
195 if (stringRet.length() < numberOfBytes) {
197 throw MIDIException(tr(
"Attempt to read past MIDI file end"));
214 throw MIDIException(tr(
"getNumberFromMIDIBytes called but no MIDI file open"));
220 if (firstByte >= 0) {
229 if (midiByte & 0x80) {
233 longRet = (longRet << 7) + (midiByte & 0x7F);
234 }
while (!
m_midiFile->eof() && (midiByte & 0x80));
248 throw MIDIException(tr(
"skipToNextTrack called but no MIDI file open"));
251 string buffer, buffer2;
281 SVDEBUG <<
"MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl;
286 ios::in | ios::binary);
289 m_error =
"File not found or not readable.";
318 SVDEBUG <<
"Parsing Track " << j << endl;
323 cerr <<
"Couldn't find Track " << j << endl;
325 m_error =
"File corrupted or in non-standard format?";
338 cerr <<
"Track " << j <<
" parsing failed" << endl;
340 m_error =
"File corrupted or in non-standard format?";
353 SVDEBUG <<
"MIDIFileReader::open() - caught exception - " << e.
what() << endl;
367 unsigned long acc = 0;
371 acc = (*i)->addTime(acc);
393 if (midiHeader.size() < 14) {
395 SVDEBUG <<
"MIDIFileReader::parseHeader() - file header undersized" << endl;
402 SVDEBUG <<
"MIDIFileReader::parseHeader()" 403 <<
"- file header not found or malformed" 411 SVDEBUG <<
"MIDIFileReader::parseHeader()" 412 <<
" - header length incorrect" 439 MIDIByte midiByte, metaEventCode, data1, data2;
442 unsigned int messageLength;
443 unsigned long deltaTime;
444 unsigned long accumulatedTime = 0;
455 vector<int> channelTrackMap(16, -1);
461 map<int, unsigned long> trackTimeMap;
465 unsigned int metaTrack = lastTrackNum;
468 int runningStatus = -1;
470 bool firstTrack =
true;
474 if (eventCode < 0x80) {
476 cerr <<
"WARNING: Invalid event code " << eventCode
477 <<
" in MIDI file" << endl;
479 throw MIDIException(tr(
"Invalid event code %1 found").arg(
int(eventCode)));
485 cerr <<
"read delta time " << deltaTime << endl;
493 if (runningStatus < 0) {
494 throw MIDIException(tr(
"Running status used for first event in track"));
497 eventCode = (
MIDIByte)runningStatus;
501 SVDEBUG <<
"using running status (byte " << int(midiByte) <<
" found)" << endl;
505 cerr <<
"have new event code " << int(midiByte) << endl;
507 eventCode = midiByte;
513 metaEventCode = data1;
517 cerr <<
"Meta event of type " << int(metaEventCode) <<
" and " << messageLength <<
" bytes found, putting on track " << metaTrack << endl;
521 long gap = accumulatedTime - trackTimeMap[metaTrack];
522 accumulatedTime += deltaTime;
524 trackTimeMap[metaTrack] = accumulatedTime;
539 runningStatus = eventCode;
544 if (channelTrackMap[channel] == -1) {
545 if (!firstTrack) ++lastTrackNum;
546 else firstTrack =
false;
547 channelTrackMap[channel] = lastTrackNum;
550 unsigned int trackNum = channelTrackMap[channel];
555 long gap = accumulatedTime - trackTimeMap[trackNum];
556 accumulatedTime += deltaTime;
558 trackTimeMap[trackNum] = accumulatedTime;
569 midiEvent =
new MIDIEvent(deltaTime, eventCode, data1, data2);
590 midiEvent =
new MIDIEvent(deltaTime, eventCode, data1, data2);
597 midiEvent =
new MIDIEvent(deltaTime, eventCode, data1);
605 cerr <<
"SysEx of " << messageLength <<
" bytes found" << endl;
610 if (
MIDIByte(metaMessage[metaMessage.length() - 1]) !=
614 SVDEBUG <<
"MIDIFileReader::parseTrack() - " 615 <<
"malformed or unsupported SysEx type" 624 metaMessage = metaMessage.substr(0, metaMessage.length()-1);
634 SVDEBUG <<
"MIDIFileReader::parseTrack()" 635 <<
" - Unsupported MIDI Event Code: " 636 << (int)eventCode << endl;
643 if (lastTrackNum > metaTrack) {
644 for (
unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) {
646 .arg(
m_trackNames[metaTrack]).arg(track - metaTrack + 1);
660 bool notesOnTrack =
false;
666 if ((*i)->getMessageType() ==
MIDI_NOTE_ON && (*i)->getVelocity() > 0) {
669 noteOffFound =
false;
671 for (MIDITrack::iterator j = i;
674 if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) &&
675 ((*j)->getPitch() == (*i)->getPitch()) &&
678 (*j)->getVelocity() == 0x00))) {
680 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
696 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
709 cerr <<
"updateTempoMap for track " << track <<
" (" <<
m_midiComposition[track].size() <<
" events)" << endl;
714 if ((*i)->isMeta() &&
717 MIDIByte m0 = (*i)->getMetaMessage()[0];
718 MIDIByte m1 = (*i)->getMetaMessage()[1];
719 MIDIByte m2 = (*i)->getMetaMessage()[2];
721 long tempo = (((m0 << 8) + m1) << 8) + m2;
723 cerr <<
"updateTempoMap: have tempo, it's " << tempo <<
" at " << (*i)->getTime() << endl;
726 double qpm = 60000000.0 / double(tempo);
737 unsigned long lastMIDITime = 0;
739 double tempo = 120.0;
741 if (td == 0) td = 96;
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;
755 lastMIDITime = mtime;
756 tempo = i->second.second;
763 unsigned long tempoMIDITime = 0;
765 double tempo = 120.0;
767 TempoMap::const_iterator i =
m_tempoMap.lower_bound(midiTime);
770 tempoMIDITime = i->first;
771 tempoRealTime = i->second.first;
772 tempo = i->second.second;
776 if (td == 0) td = 96;
778 unsigned long melapsed = midiTime - tempoMIDITime;
779 double quarters = double(melapsed) / double(td);
780 double seconds = (60.0 * quarters) / tempo;
800 if (!
isOK())
return 0;
805 (tr(
"MIDI file \"%1\" has no notes in any track").arg(
m_path));
810 std::set<unsigned int> tracksToLoad;
818 QStringList displayNames;
823 unsigned int trackNo = *i;
828 perc = tr(
" - uses GM percussion channel");
832 label = tr(
"Track %1 (%2)%3")
836 label = tr(
"Track %1 (untitled)%3").arg(trackNo).arg(perc);
839 displayNames << label;
844 bool haveSomePercussion =
869 tracksToLoad.insert(*i);
880 if (singleTrack == displayNames[j]) {
881 tracksToLoad.insert(*i);
890 if (tracksToLoad.empty())
return 0;
892 int n = tracksToLoad.size(), count = 0;
895 for (std::set<unsigned int>::iterator i = tracksToLoad.begin();
896 i != tracksToLoad.end(); ++i) {
898 int minProgress = (100 * count) / n;
899 int progressAmount = 100 / n;
901 model =
loadTrack(*i, model, minProgress, progressAmount);
906 if (dynamic_cast<NoteModel *>(model)) {
907 dynamic_cast<NoteModel *>(model)->setCompletion(100);
915 Model *existingModel,
917 int progressAmount)
const 926 model = dynamic_cast<NoteModel *>(existingModel);
928 cerr <<
"WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl;
939 int totalEvents = track.size();
942 bool sharpKey =
true;
944 for (MIDITrack::const_iterator i = track.begin(); i != track.end(); ++i) {
947 unsigned long midiTime = (*i)->getTime();
958 if ((*i)->isMeta()) {
960 switch((*i)->getMetaEventCode()) {
964 sharpKey = (int((*i)->getMetaMessage()[0]) >= 0);
996 switch ((*i)->getMessageType()) {
1000 if ((*i)->getVelocity() == 0)
break;
1003 unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration();
1020 QString noteLabel = tr(
"%1 - vel %2")
1021 .arg(pitchLabel).arg(
int((*i)->getVelocity()));
1023 float level = float((*i)->getVelocity()) / 128.f;
1025 Note note(startFrame, (*i)->getPitch(),
1026 endFrame - startFrame, level, noteLabel);
1052 (count * progressAmount) / totalEvents);
std::set< unsigned int > m_percussionTracks
static const MIDIByte MIDI_KEY_SIGNATURE
Model * loadTrack(unsigned int trackNum, Model *existingModel=0, int minProgress=0, int progressAmount=100) const
static const MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT
static RealTime frame2RealTime(long frame, unsigned int sampleRate)
Convert a sample frame at the given sample rate into a RealTime.
static const MIDIByte MIDI_COPYRIGHT_NOTICE
std::pair< RealTime, double > TempoChange
static const MIDIByte MIDI_CHANNEL_PREFIX
MIDIComposition m_midiComposition
static const MIDIByte MIDI_CHANNEL_NUM_MASK
virtual int getSampleRate() const
Return the frame rate in frames per second.
unsigned int m_numberOfTracks
static const char *const MIDI_TRACK_HEADER
NoteModel – a concrete IntervalModel for notes.
static RealTime fromSeconds(double sec)
virtual QString getError() const
bool parseHeader(const std::string &midiHeader)
static const MIDIByte MIDI_TRACK_NAME
MIDIFileFormatType m_format
long midiBytesToLong(const std::string &bytes)
MIDIFileReader(QString path, MIDIFileImportPreferenceAcquirer *pref, int mainModelSampleRate)
static const MIDIByte MIDI_TEXT_MARKER
static const MIDIByte MIDI_SEQUENCE_NUMBER
static const MIDIByte MIDI_TEXT_EVENT
long getNumberFromMIDIBytes(int firstByte=-1)
static const MIDIByte MIDI_SMPTE_OFFSET
static QString getPitchLabel(int midiPitch, float centsOffset=0, bool useFlats=false)
Return a string describing the given MIDI pitch, with optional cents offset.
RealTime getTimeForMIDITime(unsigned long midiTime) const
static const MIDIByte MIDI_PERCUSSION_CHANNEL
int m_mainModelSampleRate
std::ifstream * m_midiFile
MIDIFileImportPreferenceAcquirer * m_acquirer
static const MIDIByte MIDI_LYRIC
int midiBytesToInt(const std::string &bytes)
static const MIDIByte MIDI_CUE_POINT
virtual TrackPreference getTrackImportPreference(QStringList trackNames, bool haveSomePercussion, QString &singleTrack) const =0
static const MIDIByte MIDI_FILE_META_EVENT
static const MIDIByte MIDI_SET_TEMPO
void setValueQuantization(float q)
static const MIDIByte MIDI_INSTRUMENT_NAME
static const MIDIByte MIDI_SEQUENCER_SPECIFIC
static const MIDIByte MIDI_STATUS_BYTE_MASK
virtual void addPoint(const PointType &point)
Add a point.
bool parseTrack(unsigned int &trackNum)
virtual const char * what() const
Model is the base class for all data models that represent any sort of data on a time scale based on ...
bool consolidateNoteOffEvents(unsigned int track)
static const MIDIByte MIDI_PITCH_BEND
void calculateTempoTimestamps()
MIDIByte getChannelNumber() const
std::map< int, QString > m_trackNames
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.
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
static const MIDIByte MIDI_POLY_AFTERTOUCH
static const MIDIByte MIDI_SYSTEM_EXCLUSIVE
std::string getMIDIBytes(unsigned long bytes)
static const MIDIByte MIDI_NOTE_OFF
static const char *const MIDI_FILE_HEADER
static const MIDIByte MIDI_PROG_CHANGE
virtual void setCompletion(int completion, bool update=true)
virtual Model * load() const
Read the file and return the corresponding data model.
static const MIDIByte MIDI_TIME_SIGNATURE
static const RealTime zeroTime
std::set< unsigned int > m_loadableTracks
static const MIDIByte MIDI_MESSAGE_TYPE_MASK
std::vector< MIDIEvent * > MIDITrack
static const MIDIByte MIDI_NOTE_ON
static const MIDIByte MIDI_END_OF_EXCLUSIVE
static const MIDIByte MIDI_CHNL_AFTERTOUCH
RealTime represents time values to nanosecond precision with accurate arithmetic and frame-rate conve...