annotate data/fileio/MIDIFileReader.cpp @ 282:d9319859a4cf tip

(none)
author benoitrigolleau
date Fri, 31 Oct 2008 11:00:24 +0000
parents fc9323a41f5a
children
rev   line source
lbajardsilogic@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
lbajardsilogic@0 2
lbajardsilogic@0 3 /*
lbajardsilogic@0 4 Sonic Visualiser
lbajardsilogic@0 5 An audio file viewer and annotation editor.
lbajardsilogic@0 6 Centre for Digital Music, Queen Mary, University of London.
lbajardsilogic@0 7
lbajardsilogic@0 8 This program is free software; you can redistribute it and/or
lbajardsilogic@0 9 modify it under the terms of the GNU General Public License as
lbajardsilogic@0 10 published by the Free Software Foundation; either version 2 of the
lbajardsilogic@0 11 License, or (at your option) any later version. See the file
lbajardsilogic@0 12 COPYING included with this distribution for more information.
lbajardsilogic@0 13 */
lbajardsilogic@0 14
lbajardsilogic@0 15
lbajardsilogic@0 16 /*
lbajardsilogic@0 17 This is a modified version of a source file from the
lbajardsilogic@0 18 Rosegarden MIDI and audio sequencer and notation editor.
lbajardsilogic@0 19 This file copyright 2000-2006 Richard Bown and Chris Cannam.
lbajardsilogic@0 20 */
lbajardsilogic@0 21
lbajardsilogic@0 22
lbajardsilogic@0 23 #include <iostream>
lbajardsilogic@0 24 #include <fstream>
lbajardsilogic@0 25 #include <string>
lbajardsilogic@0 26 #include <cstdio>
lbajardsilogic@0 27 #include <algorithm>
lbajardsilogic@0 28
lbajardsilogic@0 29 #include "MIDIFileReader.h"
lbajardsilogic@0 30
lbajardsilogic@0 31 #include "model/Model.h"
lbajardsilogic@0 32 #include "base/Pitch.h"
lbajardsilogic@0 33 #include "base/RealTime.h"
lbajardsilogic@0 34 #include "model/NoteModel.h"
lbajardsilogic@0 35
lbajardsilogic@0 36 #include <QString>
lbajardsilogic@0 37 #include <QMessageBox>
lbajardsilogic@0 38 #include <QInputDialog>
lbajardsilogic@0 39
lbajardsilogic@0 40 #include <sstream>
lbajardsilogic@0 41
lbajardsilogic@0 42 using std::string;
lbajardsilogic@0 43 using std::ifstream;
lbajardsilogic@0 44 using std::stringstream;
lbajardsilogic@0 45 using std::cerr;
lbajardsilogic@0 46 using std::endl;
lbajardsilogic@0 47 using std::ends;
lbajardsilogic@0 48 using std::ios;
lbajardsilogic@0 49 using std::vector;
lbajardsilogic@0 50 using std::map;
lbajardsilogic@0 51 using std::set;
lbajardsilogic@0 52
lbajardsilogic@0 53 //#define MIDI_DEBUG 1
lbajardsilogic@0 54
lbajardsilogic@0 55 static const char *const MIDI_FILE_HEADER = "MThd";
lbajardsilogic@0 56 static const char *const MIDI_TRACK_HEADER = "MTrk";
lbajardsilogic@0 57
lbajardsilogic@0 58 static const MIDIFileReader::MIDIByte MIDI_STATUS_BYTE_MASK = 0x80;
lbajardsilogic@0 59 static const MIDIFileReader::MIDIByte MIDI_MESSAGE_TYPE_MASK = 0xF0;
lbajardsilogic@0 60 static const MIDIFileReader::MIDIByte MIDI_CHANNEL_NUM_MASK = 0x0F;
lbajardsilogic@0 61 static const MIDIFileReader::MIDIByte MIDI_NOTE_OFF = 0x80;
lbajardsilogic@0 62 static const MIDIFileReader::MIDIByte MIDI_NOTE_ON = 0x90;
lbajardsilogic@0 63 static const MIDIFileReader::MIDIByte MIDI_POLY_AFTERTOUCH = 0xA0;
lbajardsilogic@0 64 static const MIDIFileReader::MIDIByte MIDI_CTRL_CHANGE = 0xB0;
lbajardsilogic@0 65 static const MIDIFileReader::MIDIByte MIDI_PROG_CHANGE = 0xC0;
lbajardsilogic@0 66 static const MIDIFileReader::MIDIByte MIDI_CHNL_AFTERTOUCH = 0xD0;
lbajardsilogic@0 67 static const MIDIFileReader::MIDIByte MIDI_PITCH_BEND = 0xE0;
lbajardsilogic@0 68 static const MIDIFileReader::MIDIByte MIDI_SELECT_CHNL_MODE = 0xB0;
lbajardsilogic@0 69 static const MIDIFileReader::MIDIByte MIDI_SYSTEM_EXCLUSIVE = 0xF0;
lbajardsilogic@0 70 static const MIDIFileReader::MIDIByte MIDI_TC_QUARTER_FRAME = 0xF1;
lbajardsilogic@0 71 static const MIDIFileReader::MIDIByte MIDI_SONG_POSITION_PTR = 0xF2;
lbajardsilogic@0 72 static const MIDIFileReader::MIDIByte MIDI_SONG_SELECT = 0xF3;
lbajardsilogic@0 73 static const MIDIFileReader::MIDIByte MIDI_TUNE_REQUEST = 0xF6;
lbajardsilogic@0 74 static const MIDIFileReader::MIDIByte MIDI_END_OF_EXCLUSIVE = 0xF7;
lbajardsilogic@0 75 static const MIDIFileReader::MIDIByte MIDI_TIMING_CLOCK = 0xF8;
lbajardsilogic@0 76 static const MIDIFileReader::MIDIByte MIDI_START = 0xFA;
lbajardsilogic@0 77 static const MIDIFileReader::MIDIByte MIDI_CONTINUE = 0xFB;
lbajardsilogic@0 78 static const MIDIFileReader::MIDIByte MIDI_STOP = 0xFC;
lbajardsilogic@0 79 static const MIDIFileReader::MIDIByte MIDI_ACTIVE_SENSING = 0xFE;
lbajardsilogic@0 80 static const MIDIFileReader::MIDIByte MIDI_SYSTEM_RESET = 0xFF;
lbajardsilogic@0 81 static const MIDIFileReader::MIDIByte MIDI_SYSEX_NONCOMMERCIAL = 0x7D;
lbajardsilogic@0 82 static const MIDIFileReader::MIDIByte MIDI_SYSEX_NON_RT = 0x7E;
lbajardsilogic@0 83 static const MIDIFileReader::MIDIByte MIDI_SYSEX_RT = 0x7F;
lbajardsilogic@0 84 static const MIDIFileReader::MIDIByte MIDI_SYSEX_RT_COMMAND = 0x06;
lbajardsilogic@0 85 static const MIDIFileReader::MIDIByte MIDI_SYSEX_RT_RESPONSE = 0x07;
lbajardsilogic@0 86 static const MIDIFileReader::MIDIByte MIDI_MMC_STOP = 0x01;
lbajardsilogic@0 87 static const MIDIFileReader::MIDIByte MIDI_MMC_PLAY = 0x02;
lbajardsilogic@0 88 static const MIDIFileReader::MIDIByte MIDI_MMC_DEFERRED_PLAY = 0x03;
lbajardsilogic@0 89 static const MIDIFileReader::MIDIByte MIDI_MMC_FAST_FORWARD = 0x04;
lbajardsilogic@0 90 static const MIDIFileReader::MIDIByte MIDI_MMC_REWIND = 0x05;
lbajardsilogic@0 91 static const MIDIFileReader::MIDIByte MIDI_MMC_RECORD_STROBE = 0x06;
lbajardsilogic@0 92 static const MIDIFileReader::MIDIByte MIDI_MMC_RECORD_EXIT = 0x07;
lbajardsilogic@0 93 static const MIDIFileReader::MIDIByte MIDI_MMC_RECORD_PAUSE = 0x08;
lbajardsilogic@0 94 static const MIDIFileReader::MIDIByte MIDI_MMC_PAUSE = 0x08;
lbajardsilogic@0 95 static const MIDIFileReader::MIDIByte MIDI_MMC_EJECT = 0x0A;
lbajardsilogic@0 96 static const MIDIFileReader::MIDIByte MIDI_MMC_LOCATE = 0x44;
lbajardsilogic@0 97 static const MIDIFileReader::MIDIByte MIDI_FILE_META_EVENT = 0xFF;
lbajardsilogic@0 98 static const MIDIFileReader::MIDIByte MIDI_SEQUENCE_NUMBER = 0x00;
lbajardsilogic@0 99 static const MIDIFileReader::MIDIByte MIDI_TEXT_EVENT = 0x01;
lbajardsilogic@0 100 static const MIDIFileReader::MIDIByte MIDI_COPYRIGHT_NOTICE = 0x02;
lbajardsilogic@0 101 static const MIDIFileReader::MIDIByte MIDI_TRACK_NAME = 0x03;
lbajardsilogic@0 102 static const MIDIFileReader::MIDIByte MIDI_INSTRUMENT_NAME = 0x04;
lbajardsilogic@0 103 static const MIDIFileReader::MIDIByte MIDI_LYRIC = 0x05;
lbajardsilogic@0 104 static const MIDIFileReader::MIDIByte MIDI_TEXT_MARKER = 0x06;
lbajardsilogic@0 105 static const MIDIFileReader::MIDIByte MIDI_CUE_POINT = 0x07;
lbajardsilogic@0 106 static const MIDIFileReader::MIDIByte MIDI_CHANNEL_PREFIX = 0x20;
lbajardsilogic@0 107 static const MIDIFileReader::MIDIByte MIDI_CHANNEL_PREFIX_OR_PORT = 0x21;
lbajardsilogic@0 108 static const MIDIFileReader::MIDIByte MIDI_END_OF_TRACK = 0x2F;
lbajardsilogic@0 109 static const MIDIFileReader::MIDIByte MIDI_SET_TEMPO = 0x51;
lbajardsilogic@0 110 static const MIDIFileReader::MIDIByte MIDI_SMPTE_OFFSET = 0x54;
lbajardsilogic@0 111 static const MIDIFileReader::MIDIByte MIDI_TIME_SIGNATURE = 0x58;
lbajardsilogic@0 112 static const MIDIFileReader::MIDIByte MIDI_KEY_SIGNATURE = 0x59;
lbajardsilogic@0 113 static const MIDIFileReader::MIDIByte MIDI_SEQUENCER_SPECIFIC = 0x7F;
lbajardsilogic@0 114 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_BANK_MSB = 0x00;
lbajardsilogic@0 115 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_VOLUME = 0x07;
lbajardsilogic@0 116 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_BANK_LSB = 0x20;
lbajardsilogic@0 117 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_MODULATION = 0x01;
lbajardsilogic@0 118 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_PAN = 0x0A;
lbajardsilogic@0 119 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_SUSTAIN = 0x40;
lbajardsilogic@0 120 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_RESONANCE = 0x47;
lbajardsilogic@0 121 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_RELEASE = 0x48;
lbajardsilogic@0 122 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_ATTACK = 0x49;
lbajardsilogic@0 123 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_FILTER = 0x4A;
lbajardsilogic@0 124 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_REVERB = 0x5B;
lbajardsilogic@0 125 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_CHORUS = 0x5D;
lbajardsilogic@0 126 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_NRPN_1 = 0x62;
lbajardsilogic@0 127 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_NRPN_2 = 0x63;
lbajardsilogic@0 128 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_RPN_1 = 0x64;
lbajardsilogic@0 129 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_RPN_2 = 0x65;
lbajardsilogic@0 130 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_SOUNDS_OFF = 0x78;
lbajardsilogic@0 131 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_RESET = 0x79;
lbajardsilogic@0 132 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_LOCAL = 0x7A;
lbajardsilogic@0 133 static const MIDIFileReader::MIDIByte MIDI_CONTROLLER_ALL_NOTES_OFF = 0x7B;
lbajardsilogic@0 134 static const MIDIFileReader::MIDIByte MIDI_PERCUSSION_CHANNEL = 9;
lbajardsilogic@0 135
lbajardsilogic@0 136 class MIDIEvent
lbajardsilogic@0 137 {
lbajardsilogic@0 138 public:
lbajardsilogic@0 139 typedef MIDIFileReader::MIDIByte MIDIByte;
lbajardsilogic@0 140
lbajardsilogic@0 141 MIDIEvent(unsigned long deltaTime,
lbajardsilogic@0 142 MIDIByte eventCode,
lbajardsilogic@0 143 MIDIByte data1 = 0,
lbajardsilogic@0 144 MIDIByte data2 = 0) :
lbajardsilogic@0 145 m_deltaTime(deltaTime),
lbajardsilogic@0 146 m_duration(0),
lbajardsilogic@0 147 m_eventCode(eventCode),
lbajardsilogic@0 148 m_data1(data1),
lbajardsilogic@0 149 m_data2(data2),
lbajardsilogic@0 150 m_metaEventCode(0)
lbajardsilogic@0 151 { }
lbajardsilogic@0 152
lbajardsilogic@0 153 MIDIEvent(unsigned long deltaTime,
lbajardsilogic@0 154 MIDIByte eventCode,
lbajardsilogic@0 155 MIDIByte metaEventCode,
lbajardsilogic@0 156 const string &metaMessage) :
lbajardsilogic@0 157 m_deltaTime(deltaTime),
lbajardsilogic@0 158 m_duration(0),
lbajardsilogic@0 159 m_eventCode(eventCode),
lbajardsilogic@0 160 m_data1(0),
lbajardsilogic@0 161 m_data2(0),
lbajardsilogic@0 162 m_metaEventCode(metaEventCode),
lbajardsilogic@0 163 m_metaMessage(metaMessage)
lbajardsilogic@0 164 { }
lbajardsilogic@0 165
lbajardsilogic@0 166 MIDIEvent(unsigned long deltaTime,
lbajardsilogic@0 167 MIDIByte eventCode,
lbajardsilogic@0 168 const string &sysEx) :
lbajardsilogic@0 169 m_deltaTime(deltaTime),
lbajardsilogic@0 170 m_duration(0),
lbajardsilogic@0 171 m_eventCode(eventCode),
lbajardsilogic@0 172 m_data1(0),
lbajardsilogic@0 173 m_data2(0),
lbajardsilogic@0 174 m_metaEventCode(0),
lbajardsilogic@0 175 m_metaMessage(sysEx)
lbajardsilogic@0 176 { }
lbajardsilogic@0 177
lbajardsilogic@0 178 ~MIDIEvent() { }
lbajardsilogic@0 179
lbajardsilogic@0 180 void setTime(const unsigned long &time) { m_deltaTime = time; }
lbajardsilogic@0 181 void setDuration(const unsigned long& duration) { m_duration = duration;}
lbajardsilogic@0 182 unsigned long addTime(const unsigned long &time) {
lbajardsilogic@0 183 m_deltaTime += time;
lbajardsilogic@0 184 return m_deltaTime;
lbajardsilogic@0 185 }
lbajardsilogic@0 186
lbajardsilogic@0 187 MIDIByte getMessageType() const
lbajardsilogic@0 188 { return (m_eventCode & MIDI_MESSAGE_TYPE_MASK); }
lbajardsilogic@0 189
lbajardsilogic@0 190 MIDIByte getChannelNumber() const
lbajardsilogic@0 191 { return (m_eventCode & MIDI_CHANNEL_NUM_MASK); }
lbajardsilogic@0 192
lbajardsilogic@0 193 unsigned long getTime() const { return m_deltaTime; }
lbajardsilogic@0 194 unsigned long getDuration() const { return m_duration; }
lbajardsilogic@0 195
lbajardsilogic@0 196 MIDIByte getPitch() const { return m_data1; }
lbajardsilogic@0 197 MIDIByte getVelocity() const { return m_data2; }
lbajardsilogic@0 198 MIDIByte getData1() const { return m_data1; }
lbajardsilogic@0 199 MIDIByte getData2() const { return m_data2; }
lbajardsilogic@0 200 MIDIByte getEventCode() const { return m_eventCode; }
lbajardsilogic@0 201
lbajardsilogic@0 202 bool isMeta() const { return (m_eventCode == MIDI_FILE_META_EVENT); }
lbajardsilogic@0 203
lbajardsilogic@0 204 MIDIByte getMetaEventCode() const { return m_metaEventCode; }
lbajardsilogic@0 205 string getMetaMessage() const { return m_metaMessage; }
lbajardsilogic@0 206 void setMetaMessage(const string &meta) { m_metaMessage = meta; }
lbajardsilogic@0 207
lbajardsilogic@0 208 friend bool operator<(const MIDIEvent &a, const MIDIEvent &b);
lbajardsilogic@0 209
lbajardsilogic@0 210 private:
lbajardsilogic@0 211 MIDIEvent& operator=(const MIDIEvent);
lbajardsilogic@0 212
lbajardsilogic@0 213 unsigned long m_deltaTime;
lbajardsilogic@0 214 unsigned long m_duration;
lbajardsilogic@0 215 MIDIByte m_eventCode;
lbajardsilogic@0 216 MIDIByte m_data1; // or Note
lbajardsilogic@0 217 MIDIByte m_data2; // or Velocity
lbajardsilogic@0 218 MIDIByte m_metaEventCode;
lbajardsilogic@0 219 string m_metaMessage;
lbajardsilogic@0 220 };
lbajardsilogic@0 221
lbajardsilogic@0 222 // Comparator for sorting
lbajardsilogic@0 223 //
lbajardsilogic@0 224 struct MIDIEventCmp
lbajardsilogic@0 225 {
lbajardsilogic@0 226 bool operator()(const MIDIEvent &mE1, const MIDIEvent &mE2) const
lbajardsilogic@0 227 { return mE1.getTime() < mE2.getTime(); }
lbajardsilogic@0 228
lbajardsilogic@0 229 bool operator()(const MIDIEvent *mE1, const MIDIEvent *mE2) const
lbajardsilogic@0 230 { return mE1->getTime() < mE2->getTime(); }
lbajardsilogic@0 231 };
lbajardsilogic@0 232
lbajardsilogic@0 233 class MIDIException : virtual public std::exception
lbajardsilogic@0 234 {
lbajardsilogic@0 235 public:
lbajardsilogic@0 236 MIDIException(QString message) throw() : m_message(message) {
lbajardsilogic@0 237 cerr << "WARNING: MIDI exception: "
lbajardsilogic@0 238 << message.toLocal8Bit().data() << endl;
lbajardsilogic@0 239 }
lbajardsilogic@0 240 virtual ~MIDIException() throw() { }
lbajardsilogic@0 241
lbajardsilogic@0 242 virtual const char *what() const throw() {
lbajardsilogic@0 243 return m_message.toLocal8Bit().data();
lbajardsilogic@0 244 }
lbajardsilogic@0 245
lbajardsilogic@0 246 protected:
lbajardsilogic@0 247 QString m_message;
lbajardsilogic@0 248 };
lbajardsilogic@0 249
lbajardsilogic@0 250
lbajardsilogic@0 251 MIDIFileReader::MIDIFileReader(QString path,
lbajardsilogic@0 252 size_t mainModelSampleRate) :
lbajardsilogic@0 253 m_timingDivision(0),
lbajardsilogic@0 254 m_format(MIDI_FILE_BAD_FORMAT),
lbajardsilogic@0 255 m_numberOfTracks(0),
lbajardsilogic@0 256 m_trackByteCount(0),
lbajardsilogic@0 257 m_decrementCount(false),
lbajardsilogic@0 258 m_path(path),
lbajardsilogic@0 259 m_midiFile(0),
lbajardsilogic@0 260 m_fileSize(0),
lbajardsilogic@0 261 m_mainModelSampleRate(mainModelSampleRate)
lbajardsilogic@0 262 {
lbajardsilogic@0 263 if (parseFile()) {
lbajardsilogic@0 264 m_error = "";
lbajardsilogic@0 265 }
lbajardsilogic@0 266 }
lbajardsilogic@0 267
lbajardsilogic@0 268 MIDIFileReader::~MIDIFileReader()
lbajardsilogic@0 269 {
lbajardsilogic@0 270 for (MIDIComposition::iterator i = m_midiComposition.begin();
lbajardsilogic@0 271 i != m_midiComposition.end(); ++i) {
lbajardsilogic@0 272
lbajardsilogic@0 273 for (MIDITrack::iterator j = i->second.begin();
lbajardsilogic@0 274 j != i->second.end(); ++j) {
lbajardsilogic@0 275 delete *j;
lbajardsilogic@0 276 }
lbajardsilogic@0 277
lbajardsilogic@0 278 i->second.clear();
lbajardsilogic@0 279 }
lbajardsilogic@0 280
lbajardsilogic@0 281 m_midiComposition.clear();
lbajardsilogic@0 282 }
lbajardsilogic@0 283
lbajardsilogic@0 284 bool
lbajardsilogic@0 285 MIDIFileReader::isOK() const
lbajardsilogic@0 286 {
lbajardsilogic@0 287 return (m_error == "");
lbajardsilogic@0 288 }
lbajardsilogic@0 289
lbajardsilogic@0 290 QString
lbajardsilogic@0 291 MIDIFileReader::getError() const
lbajardsilogic@0 292 {
lbajardsilogic@0 293 return m_error;
lbajardsilogic@0 294 }
lbajardsilogic@0 295
lbajardsilogic@0 296 long
lbajardsilogic@0 297 MIDIFileReader::midiBytesToLong(const string& bytes)
lbajardsilogic@0 298 {
lbajardsilogic@0 299 if (bytes.length() != 4) {
lbajardsilogic@0 300 throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4));
lbajardsilogic@0 301 }
lbajardsilogic@0 302
lbajardsilogic@0 303 long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
lbajardsilogic@0 304 ((long)(((MIDIByte)bytes[1]) << 16)) |
lbajardsilogic@0 305 ((long)(((MIDIByte)bytes[2]) << 8)) |
lbajardsilogic@0 306 ((long)((MIDIByte)(bytes[3])));
lbajardsilogic@0 307
lbajardsilogic@0 308 return longRet;
lbajardsilogic@0 309 }
lbajardsilogic@0 310
lbajardsilogic@0 311 int
lbajardsilogic@0 312 MIDIFileReader::midiBytesToInt(const string& bytes)
lbajardsilogic@0 313 {
lbajardsilogic@0 314 if (bytes.length() != 2) {
lbajardsilogic@0 315 throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2));
lbajardsilogic@0 316 }
lbajardsilogic@0 317
lbajardsilogic@0 318 int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
lbajardsilogic@0 319 ((int)(((MIDIByte)bytes[1])));
lbajardsilogic@0 320 return(intRet);
lbajardsilogic@0 321 }
lbajardsilogic@0 322
lbajardsilogic@0 323
lbajardsilogic@0 324 // Gets a single byte from the MIDI byte stream. For each track
lbajardsilogic@0 325 // section we can read only a specified number of bytes held in
lbajardsilogic@0 326 // m_trackByteCount.
lbajardsilogic@0 327 //
lbajardsilogic@0 328 MIDIFileReader::MIDIByte
lbajardsilogic@0 329 MIDIFileReader::getMIDIByte()
lbajardsilogic@0 330 {
lbajardsilogic@0 331 if (!m_midiFile) {
lbajardsilogic@0 332 throw MIDIException(tr("getMIDIByte called but no MIDI file open"));
lbajardsilogic@0 333 }
lbajardsilogic@0 334
lbajardsilogic@0 335 if (m_midiFile->eof()) {
lbajardsilogic@0 336 throw MIDIException(tr("End of MIDI file encountered while reading"));
lbajardsilogic@0 337 }
lbajardsilogic@0 338
lbajardsilogic@0 339 if (m_decrementCount && m_trackByteCount <= 0) {
lbajardsilogic@0 340 throw MIDIException(tr("Attempt to get more bytes than expected on Track"));
lbajardsilogic@0 341 }
lbajardsilogic@0 342
lbajardsilogic@0 343 char byte;
lbajardsilogic@0 344 if (m_midiFile->read(&byte, 1)) {
lbajardsilogic@0 345 --m_trackByteCount;
lbajardsilogic@0 346 return (MIDIByte)byte;
lbajardsilogic@0 347 }
lbajardsilogic@0 348
lbajardsilogic@0 349 throw MIDIException(tr("Attempt to read past MIDI file end"));
lbajardsilogic@0 350 }
lbajardsilogic@0 351
lbajardsilogic@0 352
lbajardsilogic@0 353 // Gets a specified number of bytes from the MIDI byte stream. For
lbajardsilogic@0 354 // each track section we can read only a specified number of bytes
lbajardsilogic@0 355 // held in m_trackByteCount.
lbajardsilogic@0 356 //
lbajardsilogic@0 357 string
lbajardsilogic@0 358 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
lbajardsilogic@0 359 {
lbajardsilogic@0 360 if (!m_midiFile) {
lbajardsilogic@0 361 throw MIDIException(tr("getMIDIBytes called but no MIDI file open"));
lbajardsilogic@0 362 }
lbajardsilogic@0 363
lbajardsilogic@0 364 if (m_midiFile->eof()) {
lbajardsilogic@0 365 throw MIDIException(tr("End of MIDI file encountered while reading"));
lbajardsilogic@0 366 }
lbajardsilogic@0 367
lbajardsilogic@0 368 if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
lbajardsilogic@0 369 throw MIDIException(tr("Attempt to get more bytes than available on Track (%1, only have %2)").arg(numberOfBytes).arg(m_trackByteCount));
lbajardsilogic@0 370 }
lbajardsilogic@0 371
lbajardsilogic@0 372 string stringRet;
lbajardsilogic@0 373 char fileMIDIByte;
lbajardsilogic@0 374
lbajardsilogic@0 375 while (stringRet.length() < numberOfBytes &&
lbajardsilogic@0 376 m_midiFile->read(&fileMIDIByte, 1)) {
lbajardsilogic@0 377 stringRet += fileMIDIByte;
lbajardsilogic@0 378 }
lbajardsilogic@0 379
lbajardsilogic@0 380 // if we've reached the end of file without fulfilling the
lbajardsilogic@0 381 // quota then panic as our parsing has performed incorrectly
lbajardsilogic@0 382 //
lbajardsilogic@0 383 if (stringRet.length() < numberOfBytes) {
lbajardsilogic@0 384 stringRet = "";
lbajardsilogic@0 385 throw MIDIException(tr("Attempt to read past MIDI file end"));
lbajardsilogic@0 386 }
lbajardsilogic@0 387
lbajardsilogic@0 388 // decrement the byte count
lbajardsilogic@0 389 if (m_decrementCount)
lbajardsilogic@0 390 m_trackByteCount -= stringRet.length();
lbajardsilogic@0 391
lbajardsilogic@0 392 return stringRet;
lbajardsilogic@0 393 }
lbajardsilogic@0 394
lbajardsilogic@0 395
lbajardsilogic@0 396 // Get a long number of variable length from the MIDI byte stream.
lbajardsilogic@0 397 //
lbajardsilogic@0 398 long
lbajardsilogic@0 399 MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
lbajardsilogic@0 400 {
lbajardsilogic@0 401 if (!m_midiFile) {
lbajardsilogic@0 402 throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open"));
lbajardsilogic@0 403 }
lbajardsilogic@0 404
lbajardsilogic@0 405 long longRet = 0;
lbajardsilogic@0 406 MIDIByte midiByte;
lbajardsilogic@0 407
lbajardsilogic@0 408 if (firstByte >= 0) {
lbajardsilogic@0 409 midiByte = (MIDIByte)firstByte;
lbajardsilogic@0 410 } else if (m_midiFile->eof()) {
lbajardsilogic@0 411 return longRet;
lbajardsilogic@0 412 } else {
lbajardsilogic@0 413 midiByte = getMIDIByte();
lbajardsilogic@0 414 }
lbajardsilogic@0 415
lbajardsilogic@0 416 longRet = midiByte;
lbajardsilogic@0 417 if (midiByte & 0x80) {
lbajardsilogic@0 418 longRet &= 0x7F;
lbajardsilogic@0 419 do {
lbajardsilogic@0 420 midiByte = getMIDIByte();
lbajardsilogic@0 421 longRet = (longRet << 7) + (midiByte & 0x7F);
lbajardsilogic@0 422 } while (!m_midiFile->eof() && (midiByte & 0x80));
lbajardsilogic@0 423 }
lbajardsilogic@0 424
lbajardsilogic@0 425 return longRet;
lbajardsilogic@0 426 }
lbajardsilogic@0 427
lbajardsilogic@0 428
lbajardsilogic@0 429 // Seek to the next track in the midi file and set the number
lbajardsilogic@0 430 // of bytes to be read in the counter m_trackByteCount.
lbajardsilogic@0 431 //
lbajardsilogic@0 432 bool
lbajardsilogic@0 433 MIDIFileReader::skipToNextTrack()
lbajardsilogic@0 434 {
lbajardsilogic@0 435 if (!m_midiFile) {
lbajardsilogic@0 436 throw MIDIException(tr("skipToNextTrack called but no MIDI file open"));
lbajardsilogic@0 437 }
lbajardsilogic@0 438
lbajardsilogic@0 439 string buffer, buffer2;
lbajardsilogic@0 440 m_trackByteCount = -1;
lbajardsilogic@0 441 m_decrementCount = false;
lbajardsilogic@0 442
lbajardsilogic@0 443 while (!m_midiFile->eof() && (m_decrementCount == false)) {
lbajardsilogic@0 444 buffer = getMIDIBytes(4);
lbajardsilogic@0 445 if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
lbajardsilogic@0 446 m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
lbajardsilogic@0 447 m_decrementCount = true;
lbajardsilogic@0 448 }
lbajardsilogic@0 449 }
lbajardsilogic@0 450
lbajardsilogic@0 451 if (m_trackByteCount == -1) { // we haven't found a track
lbajardsilogic@0 452 return false;
lbajardsilogic@0 453 } else {
lbajardsilogic@0 454 return true;
lbajardsilogic@0 455 }
lbajardsilogic@0 456 }
lbajardsilogic@0 457
lbajardsilogic@0 458
lbajardsilogic@0 459 // Read in a MIDI file. The parsing process throws exceptions back up
lbajardsilogic@0 460 // here if we run into trouble which we can then pass back out to
lbajardsilogic@0 461 // whoever called us using a nice bool.
lbajardsilogic@0 462 //
lbajardsilogic@0 463 bool
lbajardsilogic@0 464 MIDIFileReader::parseFile()
lbajardsilogic@0 465 {
lbajardsilogic@0 466 m_error = "";
lbajardsilogic@0 467
lbajardsilogic@0 468 #ifdef MIDI_DEBUG
lbajardsilogic@0 469 cerr << "MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl;
lbajardsilogic@0 470 #endif
lbajardsilogic@0 471
lbajardsilogic@0 472 // Open the file
lbajardsilogic@0 473 m_midiFile = new ifstream(m_path.toLocal8Bit().data(),
lbajardsilogic@0 474 ios::in | ios::binary);
lbajardsilogic@0 475
lbajardsilogic@0 476 if (!*m_midiFile) {
lbajardsilogic@0 477 m_error = "File not found or not readable.";
lbajardsilogic@0 478 m_format = MIDI_FILE_BAD_FORMAT;
lbajardsilogic@0 479 delete m_midiFile;
lbajardsilogic@0 480 return false;
lbajardsilogic@0 481 }
lbajardsilogic@0 482
lbajardsilogic@0 483 bool retval = false;
lbajardsilogic@0 484
lbajardsilogic@0 485 try {
lbajardsilogic@0 486
lbajardsilogic@0 487 // Set file size so we can count it off
lbajardsilogic@0 488 //
lbajardsilogic@0 489 m_midiFile->seekg(0, ios::end);
lbajardsilogic@0 490 m_fileSize = m_midiFile->tellg();
lbajardsilogic@0 491 m_midiFile->seekg(0, ios::beg);
lbajardsilogic@0 492
lbajardsilogic@0 493 // Parse the MIDI header first. The first 14 bytes of the file.
lbajardsilogic@0 494 if (!parseHeader(getMIDIBytes(14))) {
lbajardsilogic@0 495 m_format = MIDI_FILE_BAD_FORMAT;
lbajardsilogic@0 496 m_error = "Not a MIDI file.";
lbajardsilogic@0 497 goto done;
lbajardsilogic@0 498 }
lbajardsilogic@0 499
lbajardsilogic@0 500 unsigned int i = 0;
lbajardsilogic@0 501
lbajardsilogic@0 502 for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
lbajardsilogic@0 503
lbajardsilogic@0 504 #ifdef MIDI_DEBUG
lbajardsilogic@0 505 cerr << "Parsing Track " << j << endl;
lbajardsilogic@0 506 #endif
lbajardsilogic@0 507
lbajardsilogic@0 508 if (!skipToNextTrack()) {
lbajardsilogic@0 509 #ifdef MIDI_DEBUG
lbajardsilogic@0 510 cerr << "Couldn't find Track " << j << endl;
lbajardsilogic@0 511 #endif
lbajardsilogic@0 512 m_error = "File corrupted or in non-standard format?";
lbajardsilogic@0 513 m_format = MIDI_FILE_BAD_FORMAT;
lbajardsilogic@0 514 goto done;
lbajardsilogic@0 515 }
lbajardsilogic@0 516
lbajardsilogic@0 517 #ifdef MIDI_DEBUG
lbajardsilogic@0 518 cerr << "Track has " << m_trackByteCount << " bytes" << endl;
lbajardsilogic@0 519 #endif
lbajardsilogic@0 520
lbajardsilogic@0 521 // Run through the events taking them into our internal
lbajardsilogic@0 522 // representation.
lbajardsilogic@0 523 if (!parseTrack(i)) {
lbajardsilogic@0 524 #ifdef MIDI_DEBUG
lbajardsilogic@0 525 cerr << "Track " << j << " parsing failed" << endl;
lbajardsilogic@0 526 #endif
lbajardsilogic@0 527 m_error = "File corrupted or in non-standard format?";
lbajardsilogic@0 528 m_format = MIDI_FILE_BAD_FORMAT;
lbajardsilogic@0 529 goto done;
lbajardsilogic@0 530 }
lbajardsilogic@0 531
lbajardsilogic@0 532 ++i; // j is the source track number, i the destination
lbajardsilogic@0 533 }
lbajardsilogic@0 534
lbajardsilogic@0 535 m_numberOfTracks = i;
lbajardsilogic@0 536 retval = true;
lbajardsilogic@0 537
lbajardsilogic@0 538 } catch (MIDIException e) {
lbajardsilogic@0 539
lbajardsilogic@0 540 cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
lbajardsilogic@0 541 m_error = e.what();
lbajardsilogic@0 542 }
lbajardsilogic@0 543
lbajardsilogic@0 544 done:
lbajardsilogic@0 545 m_midiFile->close();
lbajardsilogic@0 546 delete m_midiFile;
lbajardsilogic@0 547
lbajardsilogic@0 548 for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
lbajardsilogic@0 549
lbajardsilogic@0 550 // Convert the deltaTime to an absolute time since the track
lbajardsilogic@0 551 // start. The addTime method returns the sum of the current
lbajardsilogic@0 552 // MIDI Event delta time plus the argument.
lbajardsilogic@0 553
lbajardsilogic@0 554 unsigned long acc = 0;
lbajardsilogic@0 555
lbajardsilogic@0 556 for (MIDITrack::iterator i = m_midiComposition[track].begin();
lbajardsilogic@0 557 i != m_midiComposition[track].end(); ++i) {
lbajardsilogic@0 558 acc = (*i)->addTime(acc);
lbajardsilogic@0 559 }
lbajardsilogic@0 560
lbajardsilogic@0 561 if (consolidateNoteOffEvents(track)) { // returns true if some notes exist
lbajardsilogic@0 562 m_loadableTracks.insert(track);
lbajardsilogic@0 563 }
lbajardsilogic@0 564 }
lbajardsilogic@0 565
lbajardsilogic@0 566 for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
lbajardsilogic@0 567 updateTempoMap(track);
lbajardsilogic@0 568 }
lbajardsilogic@0 569
lbajardsilogic@0 570 calculateTempoTimestamps();
lbajardsilogic@0 571
lbajardsilogic@0 572 return retval;
lbajardsilogic@0 573 }
lbajardsilogic@0 574
lbajardsilogic@0 575 // Parse and ensure the MIDI Header is legitimate
lbajardsilogic@0 576 //
lbajardsilogic@0 577 bool
lbajardsilogic@0 578 MIDIFileReader::parseHeader(const string &midiHeader)
lbajardsilogic@0 579 {
lbajardsilogic@0 580 if (midiHeader.size() < 14) {
lbajardsilogic@0 581 #ifdef MIDI_DEBUG
lbajardsilogic@0 582 cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl;
lbajardsilogic@0 583 #endif
lbajardsilogic@0 584 return false;
lbajardsilogic@0 585 }
lbajardsilogic@0 586
lbajardsilogic@0 587 if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
lbajardsilogic@0 588 #ifdef MIDI_DEBUG
lbajardsilogic@0 589 cerr << "MIDIFileReader::parseHeader()"
lbajardsilogic@0 590 << "- file header not found or malformed"
lbajardsilogic@0 591 << endl;
lbajardsilogic@0 592 #endif
lbajardsilogic@0 593 return false;
lbajardsilogic@0 594 }
lbajardsilogic@0 595
lbajardsilogic@0 596 if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
lbajardsilogic@0 597 #ifdef MIDI_DEBUG
lbajardsilogic@0 598 cerr << "MIDIFileReader::parseHeader()"
lbajardsilogic@0 599 << " - header length incorrect"
lbajardsilogic@0 600 << endl;
lbajardsilogic@0 601 #endif
lbajardsilogic@0 602 return false;
lbajardsilogic@0 603 }
lbajardsilogic@0 604
lbajardsilogic@0 605 m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
lbajardsilogic@0 606 m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2));
lbajardsilogic@0 607 m_timingDivision = midiBytesToInt(midiHeader.substr(12,2));
lbajardsilogic@0 608
lbajardsilogic@0 609 if (m_format == MIDI_SEQUENTIAL_TRACK_FILE) {
lbajardsilogic@0 610 #ifdef MIDI_DEBUG
lbajardsilogic@0 611 cerr << "MIDIFileReader::parseHeader()"
lbajardsilogic@0 612 << "- can't load sequential track file"
lbajardsilogic@0 613 << endl;
lbajardsilogic@0 614 #endif
lbajardsilogic@0 615 return false;
lbajardsilogic@0 616 }
lbajardsilogic@0 617
lbajardsilogic@0 618 #ifdef MIDI_DEBUG
lbajardsilogic@0 619 if (m_timingDivision < 0) {
lbajardsilogic@0 620 cerr << "MIDIFileReader::parseHeader()"
lbajardsilogic@0 621 << " - file uses SMPTE timing"
lbajardsilogic@0 622 << endl;
lbajardsilogic@0 623 }
lbajardsilogic@0 624 #endif
lbajardsilogic@0 625
lbajardsilogic@0 626 return true;
lbajardsilogic@0 627 }
lbajardsilogic@0 628
lbajardsilogic@0 629 // Extract the contents from a MIDI file track and places it into
lbajardsilogic@0 630 // our local map of MIDI events.
lbajardsilogic@0 631 //
lbajardsilogic@0 632 bool
lbajardsilogic@0 633 MIDIFileReader::parseTrack(unsigned int &lastTrackNum)
lbajardsilogic@0 634 {
lbajardsilogic@0 635 MIDIByte midiByte, metaEventCode, data1, data2;
lbajardsilogic@0 636 MIDIByte eventCode = 0x80;
lbajardsilogic@0 637 string metaMessage;
lbajardsilogic@0 638 unsigned int messageLength;
lbajardsilogic@0 639 unsigned long deltaTime;
lbajardsilogic@0 640 unsigned long accumulatedTime = 0;
lbajardsilogic@0 641
lbajardsilogic@0 642 // The trackNum passed in to this method is the default track for
lbajardsilogic@0 643 // all events provided they're all on the same channel. If we find
lbajardsilogic@0 644 // events on more than one channel, we increment trackNum and record
lbajardsilogic@0 645 // the mapping from channel to trackNum in this channelTrackMap.
lbajardsilogic@0 646 // We then return the new trackNum by reference so the calling
lbajardsilogic@0 647 // method knows we've got more tracks than expected.
lbajardsilogic@0 648
lbajardsilogic@0 649 // This would be a vector<unsigned int> but we need -1 to indicate
lbajardsilogic@0 650 // "not yet used"
lbajardsilogic@0 651 vector<int> channelTrackMap(16, -1);
lbajardsilogic@0 652
lbajardsilogic@0 653 // This is used to store the last absolute time found on each track,
lbajardsilogic@0 654 // allowing us to modify delta-times correctly when separating events
lbajardsilogic@0 655 // out from one to multiple tracks
lbajardsilogic@0 656 //
lbajardsilogic@0 657 map<int, unsigned long> trackTimeMap;
lbajardsilogic@0 658
lbajardsilogic@0 659 // Meta-events don't have a channel, so we place them in a fixed
lbajardsilogic@0 660 // track number instead
lbajardsilogic@0 661 unsigned int metaTrack = lastTrackNum;
lbajardsilogic@0 662
lbajardsilogic@0 663 // Remember the last non-meta status byte (-1 if we haven't seen one)
lbajardsilogic@0 664 int runningStatus = -1;
lbajardsilogic@0 665
lbajardsilogic@0 666 bool firstTrack = true;
lbajardsilogic@0 667
lbajardsilogic@0 668 while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
lbajardsilogic@0 669
lbajardsilogic@0 670 if (eventCode < 0x80) {
lbajardsilogic@0 671 #ifdef MIDI_DEBUG
lbajardsilogic@0 672 cerr << "WARNING: Invalid event code " << eventCode
lbajardsilogic@0 673 << " in MIDI file" << endl;
lbajardsilogic@0 674 #endif
lbajardsilogic@0 675 throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode)));
lbajardsilogic@0 676 }
lbajardsilogic@0 677
lbajardsilogic@0 678 deltaTime = getNumberFromMIDIBytes();
lbajardsilogic@0 679
lbajardsilogic@0 680 #ifdef MIDI_DEBUG
lbajardsilogic@0 681 cerr << "read delta time " << deltaTime << endl;
lbajardsilogic@0 682 #endif
lbajardsilogic@0 683
lbajardsilogic@0 684 // Get a single byte
lbajardsilogic@0 685 midiByte = getMIDIByte();
lbajardsilogic@0 686
lbajardsilogic@0 687 if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
lbajardsilogic@0 688
lbajardsilogic@0 689 if (runningStatus < 0) {
lbajardsilogic@0 690 throw MIDIException(tr("Running status used for first event in track"));
lbajardsilogic@0 691 }
lbajardsilogic@0 692
lbajardsilogic@0 693 eventCode = (MIDIByte)runningStatus;
lbajardsilogic@0 694 data1 = midiByte;
lbajardsilogic@0 695
lbajardsilogic@0 696 #ifdef MIDI_DEBUG
lbajardsilogic@0 697 cerr << "using running status (byte " << int(midiByte) << " found)" << endl;
lbajardsilogic@0 698 #endif
lbajardsilogic@0 699 } else {
lbajardsilogic@0 700 #ifdef MIDI_DEBUG
lbajardsilogic@0 701 cerr << "have new event code " << int(midiByte) << endl;
lbajardsilogic@0 702 #endif
lbajardsilogic@0 703 eventCode = midiByte;
lbajardsilogic@0 704 data1 = getMIDIByte();
lbajardsilogic@0 705 }
lbajardsilogic@0 706
lbajardsilogic@0 707 if (eventCode == MIDI_FILE_META_EVENT) {
lbajardsilogic@0 708
lbajardsilogic@0 709 metaEventCode = data1;
lbajardsilogic@0 710 messageLength = getNumberFromMIDIBytes();
lbajardsilogic@0 711
lbajardsilogic@0 712 //#ifdef MIDI_DEBUG
lbajardsilogic@0 713 cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
lbajardsilogic@0 714 //#endif
lbajardsilogic@0 715 metaMessage = getMIDIBytes(messageLength);
lbajardsilogic@0 716
lbajardsilogic@0 717 long gap = accumulatedTime - trackTimeMap[metaTrack];
lbajardsilogic@0 718 accumulatedTime += deltaTime;
lbajardsilogic@0 719 deltaTime += gap;
lbajardsilogic@0 720 trackTimeMap[metaTrack] = accumulatedTime;
lbajardsilogic@0 721
lbajardsilogic@0 722 MIDIEvent *e = new MIDIEvent(deltaTime,
lbajardsilogic@0 723 MIDI_FILE_META_EVENT,
lbajardsilogic@0 724 metaEventCode,
lbajardsilogic@0 725 metaMessage);
lbajardsilogic@0 726
lbajardsilogic@0 727 m_midiComposition[metaTrack].push_back(e);
lbajardsilogic@0 728
lbajardsilogic@0 729 if (metaEventCode == MIDI_TRACK_NAME) {
lbajardsilogic@0 730 m_trackNames[metaTrack] = metaMessage.c_str();
lbajardsilogic@0 731 }
lbajardsilogic@0 732
lbajardsilogic@0 733 } else { // non-meta events
lbajardsilogic@0 734
lbajardsilogic@0 735 runningStatus = eventCode;
lbajardsilogic@0 736
lbajardsilogic@0 737 MIDIEvent *midiEvent;
lbajardsilogic@0 738
lbajardsilogic@0 739 int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
lbajardsilogic@0 740 if (channelTrackMap[channel] == -1) {
lbajardsilogic@0 741 if (!firstTrack) ++lastTrackNum;
lbajardsilogic@0 742 else firstTrack = false;
lbajardsilogic@0 743 channelTrackMap[channel] = lastTrackNum;
lbajardsilogic@0 744 }
lbajardsilogic@0 745
lbajardsilogic@0 746 unsigned int trackNum = channelTrackMap[channel];
lbajardsilogic@0 747
lbajardsilogic@0 748 // accumulatedTime is abs time of last event on any track;
lbajardsilogic@0 749 // trackTimeMap[trackNum] is that of last event on this track
lbajardsilogic@0 750
lbajardsilogic@0 751 long gap = accumulatedTime - trackTimeMap[trackNum];
lbajardsilogic@0 752 accumulatedTime += deltaTime;
lbajardsilogic@0 753 deltaTime += gap;
lbajardsilogic@0 754 trackTimeMap[trackNum] = accumulatedTime;
lbajardsilogic@0 755
lbajardsilogic@0 756 switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
lbajardsilogic@0 757
lbajardsilogic@0 758 case MIDI_NOTE_ON:
lbajardsilogic@0 759 case MIDI_NOTE_OFF:
lbajardsilogic@0 760 case MIDI_POLY_AFTERTOUCH:
lbajardsilogic@0 761 case MIDI_CTRL_CHANGE:
lbajardsilogic@0 762 data2 = getMIDIByte();
lbajardsilogic@0 763
lbajardsilogic@0 764 // create and store our event
lbajardsilogic@0 765 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
lbajardsilogic@0 766
lbajardsilogic@0 767 /*
lbajardsilogic@0 768 cerr << "MIDI event for channel " << channel << " (track "
lbajardsilogic@0 769 << trackNum << ")" << endl;
lbajardsilogic@0 770 midiEvent->print();
lbajardsilogic@0 771 */
lbajardsilogic@0 772
lbajardsilogic@0 773
lbajardsilogic@0 774 m_midiComposition[trackNum].push_back(midiEvent);
lbajardsilogic@0 775
lbajardsilogic@0 776 if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) {
lbajardsilogic@0 777 m_percussionTracks.insert(trackNum);
lbajardsilogic@0 778 }
lbajardsilogic@0 779
lbajardsilogic@0 780 break;
lbajardsilogic@0 781
lbajardsilogic@0 782 case MIDI_PITCH_BEND:
lbajardsilogic@0 783 data2 = getMIDIByte();
lbajardsilogic@0 784
lbajardsilogic@0 785 // create and store our event
lbajardsilogic@0 786 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
lbajardsilogic@0 787 m_midiComposition[trackNum].push_back(midiEvent);
lbajardsilogic@0 788 break;
lbajardsilogic@0 789
lbajardsilogic@0 790 case MIDI_PROG_CHANGE:
lbajardsilogic@0 791 case MIDI_CHNL_AFTERTOUCH:
lbajardsilogic@0 792 // create and store our event
lbajardsilogic@0 793 midiEvent = new MIDIEvent(deltaTime, eventCode, data1);
lbajardsilogic@0 794 m_midiComposition[trackNum].push_back(midiEvent);
lbajardsilogic@0 795 break;
lbajardsilogic@0 796
lbajardsilogic@0 797 case MIDI_SYSTEM_EXCLUSIVE:
lbajardsilogic@0 798 messageLength = getNumberFromMIDIBytes(data1);
lbajardsilogic@0 799
lbajardsilogic@0 800 #ifdef MIDI_DEBUG
lbajardsilogic@0 801 cerr << "SysEx of " << messageLength << " bytes found" << endl;
lbajardsilogic@0 802 #endif
lbajardsilogic@0 803
lbajardsilogic@0 804 metaMessage= getMIDIBytes(messageLength);
lbajardsilogic@0 805
lbajardsilogic@0 806 if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
lbajardsilogic@0 807 MIDI_END_OF_EXCLUSIVE)
lbajardsilogic@0 808 {
lbajardsilogic@0 809 #ifdef MIDI_DEBUG
lbajardsilogic@0 810 cerr << "MIDIFileReader::parseTrack() - "
lbajardsilogic@0 811 << "malformed or unsupported SysEx type"
lbajardsilogic@0 812 << endl;
lbajardsilogic@0 813 #endif
lbajardsilogic@0 814 continue;
lbajardsilogic@0 815 }
lbajardsilogic@0 816
lbajardsilogic@0 817 // chop off the EOX
lbajardsilogic@0 818 // length fixed by Pedro Lopez-Cabanillas (20030523)
lbajardsilogic@0 819 //
lbajardsilogic@0 820 metaMessage = metaMessage.substr(0, metaMessage.length()-1);
lbajardsilogic@0 821
lbajardsilogic@0 822 midiEvent = new MIDIEvent(deltaTime,
lbajardsilogic@0 823 MIDI_SYSTEM_EXCLUSIVE,
lbajardsilogic@0 824 metaMessage);
lbajardsilogic@0 825 m_midiComposition[trackNum].push_back(midiEvent);
lbajardsilogic@0 826 break;
lbajardsilogic@0 827
lbajardsilogic@0 828 case MIDI_END_OF_EXCLUSIVE:
lbajardsilogic@0 829 #ifdef MIDI_DEBUG
lbajardsilogic@0 830 cerr << "MIDIFileReader::parseTrack() - "
lbajardsilogic@0 831 << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl;
lbajardsilogic@0 832 #endif
lbajardsilogic@0 833 break;
lbajardsilogic@0 834
lbajardsilogic@0 835 default:
lbajardsilogic@0 836 #ifdef MIDI_DEBUG
lbajardsilogic@0 837 cerr << "MIDIFileReader::parseTrack()"
lbajardsilogic@0 838 << " - Unsupported MIDI Event Code: "
lbajardsilogic@0 839 << (int)eventCode << endl;
lbajardsilogic@0 840 #endif
lbajardsilogic@0 841 break;
lbajardsilogic@0 842 }
lbajardsilogic@0 843 }
lbajardsilogic@0 844 }
lbajardsilogic@0 845
lbajardsilogic@0 846 if (lastTrackNum > metaTrack) {
lbajardsilogic@0 847 for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) {
lbajardsilogic@0 848 m_trackNames[track] = QString("%1 <%2>")
lbajardsilogic@0 849 .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1);
lbajardsilogic@0 850 }
lbajardsilogic@0 851 }
lbajardsilogic@0 852
lbajardsilogic@0 853 return true;
lbajardsilogic@0 854 }
lbajardsilogic@0 855
lbajardsilogic@0 856 // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
lbajardsilogic@0 857 // reading them and modifying their relevant NOTE ONs. Return true
lbajardsilogic@0 858 // if there are some notes in this track.
lbajardsilogic@0 859 //
lbajardsilogic@0 860 bool
lbajardsilogic@0 861 MIDIFileReader::consolidateNoteOffEvents(unsigned int track)
lbajardsilogic@0 862 {
lbajardsilogic@0 863 bool notesOnTrack = false;
lbajardsilogic@0 864 bool noteOffFound;
lbajardsilogic@0 865
lbajardsilogic@0 866 for (MIDITrack::iterator i = m_midiComposition[track].begin();
lbajardsilogic@0 867 i != m_midiComposition[track].end(); i++) {
lbajardsilogic@0 868
lbajardsilogic@0 869 if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) {
lbajardsilogic@0 870
lbajardsilogic@0 871 notesOnTrack = true;
lbajardsilogic@0 872 noteOffFound = false;
lbajardsilogic@0 873
lbajardsilogic@0 874 for (MIDITrack::iterator j = i;
lbajardsilogic@0 875 j != m_midiComposition[track].end(); j++) {
lbajardsilogic@0 876
lbajardsilogic@0 877 if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) &&
lbajardsilogic@0 878 ((*j)->getPitch() == (*i)->getPitch()) &&
lbajardsilogic@0 879 ((*j)->getMessageType() == MIDI_NOTE_OFF ||
lbajardsilogic@0 880 ((*j)->getMessageType() == MIDI_NOTE_ON &&
lbajardsilogic@0 881 (*j)->getVelocity() == 0x00))) {
lbajardsilogic@0 882
lbajardsilogic@0 883 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
lbajardsilogic@0 884
lbajardsilogic@0 885 delete *j;
lbajardsilogic@0 886 m_midiComposition[track].erase(j);
lbajardsilogic@0 887
lbajardsilogic@0 888 noteOffFound = true;
lbajardsilogic@0 889 break;
lbajardsilogic@0 890 }
lbajardsilogic@0 891 }
lbajardsilogic@0 892
lbajardsilogic@0 893 // If no matching NOTE OFF has been found then set
lbajardsilogic@0 894 // Event duration to length of track
lbajardsilogic@0 895 //
lbajardsilogic@0 896 if (!noteOffFound) {
lbajardsilogic@0 897 MIDITrack::iterator j = m_midiComposition[track].end();
lbajardsilogic@0 898 --j;
lbajardsilogic@0 899 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
lbajardsilogic@0 900 }
lbajardsilogic@0 901 }
lbajardsilogic@0 902 }
lbajardsilogic@0 903
lbajardsilogic@0 904 return notesOnTrack;
lbajardsilogic@0 905 }
lbajardsilogic@0 906
lbajardsilogic@0 907 // Add any tempo events found in the given track to the global tempo map.
lbajardsilogic@0 908 //
lbajardsilogic@0 909 void
lbajardsilogic@0 910 MIDIFileReader::updateTempoMap(unsigned int track)
lbajardsilogic@0 911 {
lbajardsilogic@0 912 std::cerr << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << std::endl;
lbajardsilogic@0 913
lbajardsilogic@0 914 for (MIDITrack::iterator i = m_midiComposition[track].begin();
lbajardsilogic@0 915 i != m_midiComposition[track].end(); ++i) {
lbajardsilogic@0 916
lbajardsilogic@0 917 if ((*i)->isMeta() &&
lbajardsilogic@0 918 (*i)->getMetaEventCode() == MIDI_SET_TEMPO) {
lbajardsilogic@0 919
lbajardsilogic@0 920 MIDIByte m0 = (*i)->getMetaMessage()[0];
lbajardsilogic@0 921 MIDIByte m1 = (*i)->getMetaMessage()[1];
lbajardsilogic@0 922 MIDIByte m2 = (*i)->getMetaMessage()[2];
lbajardsilogic@0 923
lbajardsilogic@0 924 long tempo = (((m0 << 8) + m1) << 8) + m2;
lbajardsilogic@0 925
lbajardsilogic@0 926 std::cerr << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << std::endl;
lbajardsilogic@0 927
lbajardsilogic@0 928 if (tempo != 0) {
lbajardsilogic@0 929 double qpm = 60000000.0 / double(tempo);
lbajardsilogic@0 930 m_tempoMap[(*i)->getTime()] =
lbajardsilogic@0 931 TempoChange(RealTime::zeroTime, qpm);
lbajardsilogic@0 932 }
lbajardsilogic@0 933 }
lbajardsilogic@0 934 }
lbajardsilogic@0 935 }
lbajardsilogic@0 936
lbajardsilogic@0 937 void
lbajardsilogic@0 938 MIDIFileReader::calculateTempoTimestamps()
lbajardsilogic@0 939 {
lbajardsilogic@0 940 unsigned long lastMIDITime = 0;
lbajardsilogic@0 941 RealTime lastRealTime = RealTime::zeroTime;
lbajardsilogic@0 942 double tempo = 120.0;
lbajardsilogic@0 943 int td = m_timingDivision;
lbajardsilogic@0 944 if (td == 0) td = 96;
lbajardsilogic@0 945
lbajardsilogic@0 946 for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) {
lbajardsilogic@0 947
lbajardsilogic@0 948 unsigned long mtime = i->first;
lbajardsilogic@0 949 unsigned long melapsed = mtime - lastMIDITime;
lbajardsilogic@0 950 double quarters = double(melapsed) / double(td);
lbajardsilogic@0 951 double seconds = (60.0 * quarters) / tempo;
lbajardsilogic@0 952
lbajardsilogic@0 953 RealTime t = lastRealTime + RealTime::fromSeconds(seconds);
lbajardsilogic@0 954
lbajardsilogic@0 955 i->second.first = t;
lbajardsilogic@0 956
lbajardsilogic@0 957 lastRealTime = t;
lbajardsilogic@0 958 lastMIDITime = mtime;
lbajardsilogic@0 959 tempo = i->second.second;
lbajardsilogic@0 960 }
lbajardsilogic@0 961 }
lbajardsilogic@0 962
lbajardsilogic@0 963 RealTime
lbajardsilogic@0 964 MIDIFileReader::getTimeForMIDITime(unsigned long midiTime) const
lbajardsilogic@0 965 {
lbajardsilogic@0 966 unsigned long tempoMIDITime = 0;
lbajardsilogic@0 967 RealTime tempoRealTime = RealTime::zeroTime;
lbajardsilogic@0 968 double tempo = 120.0;
lbajardsilogic@0 969
lbajardsilogic@0 970 TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime);
lbajardsilogic@0 971 if (i != m_tempoMap.begin()) {
lbajardsilogic@0 972 --i;
lbajardsilogic@0 973 tempoMIDITime = i->first;
lbajardsilogic@0 974 tempoRealTime = i->second.first;
lbajardsilogic@0 975 tempo = i->second.second;
lbajardsilogic@0 976 }
lbajardsilogic@0 977
lbajardsilogic@0 978 int td = m_timingDivision;
lbajardsilogic@0 979 if (td == 0) td = 96;
lbajardsilogic@0 980
lbajardsilogic@0 981 unsigned long melapsed = midiTime - tempoMIDITime;
lbajardsilogic@0 982 double quarters = double(melapsed) / double(td);
lbajardsilogic@0 983 double seconds = (60.0 * quarters) / tempo;
lbajardsilogic@0 984
lbajardsilogic@0 985 /*
lbajardsilogic@0 986 std::cerr << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")"
lbajardsilogic@0 987 << std::endl;
lbajardsilogic@0 988 std::cerr << "timing division = " << td << std::endl;
lbajardsilogic@0 989 std::cerr << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " ("
lbajardsilogic@0 990 << tempoRealTime << ")" << std::endl;
lbajardsilogic@0 991 std::cerr << "quarters since then = " << quarters << std::endl;
lbajardsilogic@0 992 std::cerr << "tempo = " << tempo << " quarters per minute" << std::endl;
lbajardsilogic@0 993 std::cerr << "seconds since then = " << seconds << std::endl;
lbajardsilogic@0 994 std::cerr << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << std::endl;
lbajardsilogic@0 995 */
lbajardsilogic@0 996
lbajardsilogic@0 997 return tempoRealTime + RealTime::fromSeconds(seconds);
lbajardsilogic@0 998 }
lbajardsilogic@0 999
lbajardsilogic@0 1000 Model *
lbajardsilogic@0 1001 MIDIFileReader::load() const
lbajardsilogic@0 1002 {
lbajardsilogic@0 1003 if (!isOK()) return 0;
lbajardsilogic@0 1004
lbajardsilogic@0 1005 if (m_loadableTracks.empty()) {
lbajardsilogic@0 1006 QMessageBox::critical(0, tr("No notes in MIDI file"),
lbajardsilogic@0 1007 tr("MIDI file \"%1\" has no notes in any track")
lbajardsilogic@0 1008 .arg(m_path));
lbajardsilogic@0 1009 return 0;
lbajardsilogic@0 1010 }
lbajardsilogic@0 1011
lbajardsilogic@0 1012 std::set<unsigned int> tracksToLoad;
lbajardsilogic@0 1013
lbajardsilogic@0 1014 if (m_loadableTracks.size() == 1) {
lbajardsilogic@0 1015
lbajardsilogic@0 1016 tracksToLoad.insert(*m_loadableTracks.begin());
lbajardsilogic@0 1017
lbajardsilogic@0 1018 } else {
lbajardsilogic@0 1019
lbajardsilogic@0 1020 QStringList available;
lbajardsilogic@0 1021 QString allTracks = tr("Merge all tracks");
lbajardsilogic@0 1022 QString allNonPercussion = tr("Merge all non-percussion tracks");
lbajardsilogic@0 1023
lbajardsilogic@0 1024 int nonTrackItems = 1;
lbajardsilogic@0 1025
lbajardsilogic@0 1026 available << allTracks;
lbajardsilogic@0 1027
lbajardsilogic@0 1028 if (!m_percussionTracks.empty() &&
lbajardsilogic@0 1029 (m_percussionTracks.size() < m_loadableTracks.size())) {
lbajardsilogic@0 1030 available << allNonPercussion;
lbajardsilogic@0 1031 ++nonTrackItems;
lbajardsilogic@0 1032 }
lbajardsilogic@0 1033
lbajardsilogic@0 1034 for (set<unsigned int>::const_iterator i = m_loadableTracks.begin();
lbajardsilogic@0 1035 i != m_loadableTracks.end(); ++i) {
lbajardsilogic@0 1036
lbajardsilogic@0 1037 unsigned int trackNo = *i;
lbajardsilogic@0 1038 QString label;
lbajardsilogic@0 1039
lbajardsilogic@0 1040 QString perc;
lbajardsilogic@0 1041 if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) {
lbajardsilogic@0 1042 perc = tr(" - uses GM percussion channel");
lbajardsilogic@0 1043 }
lbajardsilogic@0 1044
lbajardsilogic@0 1045 if (m_trackNames.find(trackNo) != m_trackNames.end()) {
lbajardsilogic@0 1046 label = tr("Track %1 (%2)%3")
lbajardsilogic@0 1047 .arg(trackNo).arg(m_trackNames.find(trackNo)->second)
lbajardsilogic@0 1048 .arg(perc);
lbajardsilogic@0 1049 } else {
lbajardsilogic@0 1050 label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc);
lbajardsilogic@0 1051 }
lbajardsilogic@0 1052 available << label;
lbajardsilogic@0 1053 }
lbajardsilogic@0 1054
lbajardsilogic@0 1055 bool ok = false;
lbajardsilogic@0 1056 QString selected = QInputDialog::getItem
lbajardsilogic@0 1057 (0, tr("Select track or tracks to import"),
lbajardsilogic@0 1058 tr("You can only import this file as a single annotation layer,\nbut the file contains more than one track,\nor notes on more than one channel.\n\nPlease select the track or merged tracks you wish to import:"),
lbajardsilogic@0 1059 available, 0, false, &ok);
lbajardsilogic@0 1060
lbajardsilogic@0 1061 if (!ok || selected.isEmpty()) return 0;
lbajardsilogic@0 1062
lbajardsilogic@0 1063 if (selected == allTracks || selected == allNonPercussion) {
lbajardsilogic@0 1064
lbajardsilogic@0 1065 for (set<unsigned int>::const_iterator i = m_loadableTracks.begin();
lbajardsilogic@0 1066 i != m_loadableTracks.end(); ++i) {
lbajardsilogic@0 1067
lbajardsilogic@0 1068 if (selected == allTracks ||
lbajardsilogic@0 1069 m_percussionTracks.find(*i) == m_percussionTracks.end()) {
lbajardsilogic@0 1070
lbajardsilogic@0 1071 tracksToLoad.insert(*i);
lbajardsilogic@0 1072 }
lbajardsilogic@0 1073 }
lbajardsilogic@0 1074
lbajardsilogic@0 1075 } else {
lbajardsilogic@0 1076
lbajardsilogic@0 1077 int j = nonTrackItems;
lbajardsilogic@0 1078
lbajardsilogic@0 1079 for (set<unsigned int>::const_iterator i = m_loadableTracks.begin();
lbajardsilogic@0 1080 i != m_loadableTracks.end(); ++i) {
lbajardsilogic@0 1081
lbajardsilogic@0 1082 if (selected == available[j]) {
lbajardsilogic@0 1083 tracksToLoad.insert(*i);
lbajardsilogic@0 1084 break;
lbajardsilogic@0 1085 }
lbajardsilogic@0 1086
lbajardsilogic@0 1087 ++j;
lbajardsilogic@0 1088 }
lbajardsilogic@0 1089 }
lbajardsilogic@0 1090 }
lbajardsilogic@0 1091
lbajardsilogic@0 1092 if (tracksToLoad.empty()) return 0;
lbajardsilogic@0 1093
lbajardsilogic@0 1094 size_t n = tracksToLoad.size(), count = 0;
lbajardsilogic@0 1095 Model *model = 0;
lbajardsilogic@0 1096
lbajardsilogic@0 1097 for (std::set<unsigned int>::iterator i = tracksToLoad.begin();
lbajardsilogic@0 1098 i != tracksToLoad.end(); ++i) {
lbajardsilogic@0 1099
lbajardsilogic@0 1100 int minProgress = (100 * count) / n;
lbajardsilogic@0 1101 int progressAmount = 100 / n;
lbajardsilogic@0 1102
lbajardsilogic@0 1103 model = loadTrack(*i, model, minProgress, progressAmount);
lbajardsilogic@0 1104
lbajardsilogic@0 1105 ++count;
lbajardsilogic@0 1106 }
lbajardsilogic@0 1107
lbajardsilogic@0 1108 if (dynamic_cast<NoteModel *>(model)) {
lbajardsilogic@0 1109 dynamic_cast<NoteModel *>(model)->setCompletion(100);
lbajardsilogic@0 1110 }
lbajardsilogic@0 1111
lbajardsilogic@0 1112 return model;
lbajardsilogic@0 1113 }
lbajardsilogic@0 1114
lbajardsilogic@0 1115 Model *
lbajardsilogic@0 1116 MIDIFileReader::loadTrack(unsigned int trackToLoad,
lbajardsilogic@0 1117 Model *existingModel,
lbajardsilogic@0 1118 int minProgress,
lbajardsilogic@0 1119 int progressAmount) const
lbajardsilogic@0 1120 {
lbajardsilogic@0 1121 if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) {
lbajardsilogic@0 1122 return 0;
lbajardsilogic@0 1123 }
lbajardsilogic@0 1124
lbajardsilogic@0 1125 NoteModel *model = 0;
lbajardsilogic@0 1126
lbajardsilogic@0 1127 if (existingModel) {
lbajardsilogic@0 1128 model = dynamic_cast<NoteModel *>(existingModel);
lbajardsilogic@0 1129 if (!model) {
lbajardsilogic@0 1130 std::cerr << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << std::endl;
lbajardsilogic@0 1131 }
lbajardsilogic@0 1132 }
lbajardsilogic@0 1133
lbajardsilogic@0 1134 if (!model) {
lbajardsilogic@0 1135 model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false);
lbajardsilogic@0 1136 model->setValueQuantization(1.0);
lbajardsilogic@0 1137 }
lbajardsilogic@0 1138
lbajardsilogic@0 1139 const MIDITrack &track = m_midiComposition.find(trackToLoad)->second;
lbajardsilogic@0 1140
lbajardsilogic@0 1141 size_t totalEvents = track.size();
lbajardsilogic@0 1142 size_t count = 0;
lbajardsilogic@0 1143
lbajardsilogic@0 1144 bool minorKey = false;
lbajardsilogic@0 1145 bool sharpKey = true;
lbajardsilogic@0 1146
lbajardsilogic@0 1147 for (MIDITrack::const_iterator i = track.begin(); i != track.end(); ++i) {
lbajardsilogic@0 1148
lbajardsilogic@0 1149 RealTime rt = getTimeForMIDITime((*i)->getTime());
lbajardsilogic@0 1150
lbajardsilogic@0 1151 // We ignore most of these event types for now, though in
lbajardsilogic@0 1152 // theory some of the text ones could usefully be incorporated
lbajardsilogic@0 1153
lbajardsilogic@0 1154 if ((*i)->isMeta()) {
lbajardsilogic@0 1155
lbajardsilogic@0 1156 switch((*i)->getMetaEventCode()) {
lbajardsilogic@0 1157
lbajardsilogic@0 1158 case MIDI_KEY_SIGNATURE:
lbajardsilogic@0 1159 minorKey = (int((*i)->getMetaMessage()[1]) != 0);
lbajardsilogic@0 1160 sharpKey = (int((*i)->getMetaMessage()[0]) >= 0);
lbajardsilogic@0 1161 break;
lbajardsilogic@0 1162
lbajardsilogic@0 1163 case MIDI_TEXT_EVENT:
lbajardsilogic@0 1164 case MIDI_LYRIC:
lbajardsilogic@0 1165 case MIDI_TEXT_MARKER:
lbajardsilogic@0 1166 case MIDI_COPYRIGHT_NOTICE:
lbajardsilogic@0 1167 case MIDI_TRACK_NAME:
lbajardsilogic@0 1168 // The text events that we could potentially use
lbajardsilogic@0 1169 break;
lbajardsilogic@0 1170
lbajardsilogic@0 1171 case MIDI_SET_TEMPO:
lbajardsilogic@0 1172 // Already dealt with in a separate pass previously
lbajardsilogic@0 1173 break;
lbajardsilogic@0 1174
lbajardsilogic@0 1175 case MIDI_TIME_SIGNATURE:
lbajardsilogic@0 1176 // Not yet!
lbajardsilogic@0 1177 break;
lbajardsilogic@0 1178
lbajardsilogic@0 1179 case MIDI_SEQUENCE_NUMBER:
lbajardsilogic@0 1180 case MIDI_CHANNEL_PREFIX_OR_PORT:
lbajardsilogic@0 1181 case MIDI_INSTRUMENT_NAME:
lbajardsilogic@0 1182 case MIDI_CUE_POINT:
lbajardsilogic@0 1183 case MIDI_CHANNEL_PREFIX:
lbajardsilogic@0 1184 case MIDI_SEQUENCER_SPECIFIC:
lbajardsilogic@0 1185 case MIDI_SMPTE_OFFSET:
lbajardsilogic@0 1186 default:
lbajardsilogic@0 1187 break;
lbajardsilogic@0 1188 }
lbajardsilogic@0 1189
lbajardsilogic@0 1190 } else {
lbajardsilogic@0 1191
lbajardsilogic@0 1192 switch ((*i)->getMessageType()) {
lbajardsilogic@0 1193
lbajardsilogic@0 1194 case MIDI_NOTE_ON:
lbajardsilogic@0 1195
lbajardsilogic@0 1196 if ((*i)->getVelocity() == 0) break; // effective note-off
lbajardsilogic@0 1197 else {
lbajardsilogic@0 1198 RealTime endRT = getTimeForMIDITime((*i)->getTime() +
lbajardsilogic@0 1199 (*i)->getDuration());
lbajardsilogic@0 1200
lbajardsilogic@0 1201 long startFrame = RealTime::realTime2Frame
lbajardsilogic@0 1202 (rt, model->getSampleRate());
lbajardsilogic@0 1203
lbajardsilogic@0 1204 long endFrame = RealTime::realTime2Frame
lbajardsilogic@0 1205 (endRT, model->getSampleRate());
lbajardsilogic@0 1206
lbajardsilogic@0 1207 QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(),
lbajardsilogic@0 1208 0,
lbajardsilogic@0 1209 !sharpKey);
lbajardsilogic@0 1210
lbajardsilogic@0 1211 QString noteLabel = tr("%1 - vel %2")
lbajardsilogic@0 1212 .arg(pitchLabel).arg(int((*i)->getVelocity()));
lbajardsilogic@0 1213
lbajardsilogic@0 1214 Note note(startFrame, (*i)->getPitch(),
lbajardsilogic@0 1215 endFrame - startFrame, noteLabel);
lbajardsilogic@0 1216
lbajardsilogic@0 1217 // std::cerr << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << std::endl;
lbajardsilogic@0 1218
lbajardsilogic@0 1219 model->addPoint(note);
lbajardsilogic@0 1220 break;
lbajardsilogic@0 1221 }
lbajardsilogic@0 1222
lbajardsilogic@0 1223 case MIDI_PITCH_BEND:
lbajardsilogic@0 1224 // I guess we could make some use of this...
lbajardsilogic@0 1225 break;
lbajardsilogic@0 1226
lbajardsilogic@0 1227 case MIDI_NOTE_OFF:
lbajardsilogic@0 1228 case MIDI_PROG_CHANGE:
lbajardsilogic@0 1229 case MIDI_CTRL_CHANGE:
lbajardsilogic@0 1230 case MIDI_SYSTEM_EXCLUSIVE:
lbajardsilogic@0 1231 case MIDI_POLY_AFTERTOUCH:
lbajardsilogic@0 1232 case MIDI_CHNL_AFTERTOUCH:
lbajardsilogic@0 1233 break;
lbajardsilogic@0 1234
lbajardsilogic@0 1235 default:
lbajardsilogic@0 1236 break;
lbajardsilogic@0 1237 }
lbajardsilogic@0 1238 }
lbajardsilogic@0 1239
lbajardsilogic@0 1240 model->setCompletion(minProgress +
lbajardsilogic@0 1241 (count * progressAmount) / totalEvents);
lbajardsilogic@0 1242 ++count;
lbajardsilogic@0 1243 }
lbajardsilogic@0 1244
lbajardsilogic@0 1245 return model;
lbajardsilogic@0 1246 }
lbajardsilogic@0 1247
lbajardsilogic@0 1248