To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / MIDIFileReader.cpp
History | View | Annotate | Download (17.5 KB)
| 1 | 1:3e65e0344413 | cannam | /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
|---|---|---|---|
| 2 | |||
| 3 | /*
|
||
| 4 | 4:a98a66b43882 | Chris | This is a modified version of a source file from the
|
| 5 | Rosegarden MIDI and audio sequencer and notation editor.
|
||
| 6 | This file copyright 2000-2010 Richard Bown and Chris Cannam.
|
||
| 7 | |||
| 8 | Permission is hereby granted, free of charge, to any person
|
||
| 9 | obtaining a copy of this software and associated documentation
|
||
| 10 | files (the "Software"), to deal in the Software without
|
||
| 11 | restriction, including without limitation the rights to use, copy,
|
||
| 12 | modify, merge, publish, distribute, sublicense, and/or sell copies
|
||
| 13 | of the Software, and to permit persons to whom the Software is
|
||
| 14 | furnished to do so, subject to the following conditions:
|
||
| 15 | |||
| 16 | The above copyright notice and this permission notice shall be
|
||
| 17 | included in all copies or substantial portions of the Software.
|
||
| 18 | |||
| 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
| 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
| 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
| 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
|
||
| 23 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||
| 24 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
| 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
| 26 | |||
| 27 | Except as contained in this notice, the names of the authors
|
||
| 28 | shall not be used in advertising or otherwise to promote the sale,
|
||
| 29 | use or other dealings in this Software without prior written
|
||
| 30 | authorization.
|
||
| 31 | 1:3e65e0344413 | cannam | */
|
| 32 | |||
| 33 | |||
| 34 | #include <iostream> |
||
| 35 | #include <fstream> |
||
| 36 | #include <string> |
||
| 37 | #include <cstdio> |
||
| 38 | |||
| 39 | #include "MIDIFileReader.h" |
||
| 40 | #include "MIDIEvent.h" |
||
| 41 | |||
| 42 | #include <sstream> |
||
| 43 | |||
| 44 | using std::string; |
||
| 45 | using std::ifstream;
|
||
| 46 | using std::stringstream;
|
||
| 47 | using std::cerr;
|
||
| 48 | using std::endl;
|
||
| 49 | using std::ends;
|
||
| 50 | using std::ios;
|
||
| 51 | |||
| 52 | using namespace MIDIConstants; |
||
| 53 | |||
| 54 | //#define DEBUG_MIDI_FILE_READER 1
|
||
| 55 | |||
| 56 | 5:7fde3cc109dc | Chris | #define throw_exception(...) do { \ |
| 57 | char message[128]; \ |
||
| 58 | snprintf(message, 128, __VA_ARGS__); \
|
||
| 59 | throw MIDIException(std::string(message)); \ |
||
| 60 | } while (0) |
||
| 61 | |||
| 62 | 1:3e65e0344413 | cannam | |
| 63 | 5:7fde3cc109dc | Chris | |
| 64 | MIDIFileReader::MIDIFileReader(std::string path) :
|
||
| 65 | 1:3e65e0344413 | cannam | m_timingDivision(0),
|
| 66 | m_format(MIDI_FILE_BAD_FORMAT), |
||
| 67 | m_numberOfTracks(0),
|
||
| 68 | m_trackByteCount(0),
|
||
| 69 | m_decrementCount(false),
|
||
| 70 | m_path(path), |
||
| 71 | m_midiFile(0),
|
||
| 72 | m_fileSize(0)
|
||
| 73 | {
|
||
| 74 | if (parseFile()) {
|
||
| 75 | m_error = "";
|
||
| 76 | } |
||
| 77 | } |
||
| 78 | |||
| 79 | MIDIFileReader::~MIDIFileReader() |
||
| 80 | {
|
||
| 81 | } |
||
| 82 | |||
| 83 | bool
|
||
| 84 | MIDIFileReader::isOK() const
|
||
| 85 | {
|
||
| 86 | return (m_error == ""); |
||
| 87 | } |
||
| 88 | |||
| 89 | 5:7fde3cc109dc | Chris | std::string
|
| 90 | 1:3e65e0344413 | cannam | MIDIFileReader::getError() const
|
| 91 | {
|
||
| 92 | return m_error;
|
||
| 93 | } |
||
| 94 | |||
| 95 | long
|
||
| 96 | MIDIFileReader::midiBytesToLong(const string& bytes) |
||
| 97 | {
|
||
| 98 | if (bytes.length() != 4) { |
||
| 99 | 5:7fde3cc109dc | Chris | throw_exception("Wrong length for long data in MIDI stream (%d, should be %d)", (int)bytes.length(), 4); |
| 100 | 1:3e65e0344413 | cannam | } |
| 101 | |||
| 102 | long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) | |
||
| 103 | ((long)(((MIDIByte)bytes[1]) << 16)) | |
||
| 104 | ((long)(((MIDIByte)bytes[2]) << 8)) | |
||
| 105 | ((long)((MIDIByte)(bytes[3]))); |
||
| 106 | |||
| 107 | return longRet;
|
||
| 108 | } |
||
| 109 | |||
| 110 | int
|
||
| 111 | MIDIFileReader::midiBytesToInt(const string& bytes) |
||
| 112 | {
|
||
| 113 | if (bytes.length() != 2) { |
||
| 114 | 5:7fde3cc109dc | Chris | throw_exception("Wrong length for int data in MIDI stream (%d, should be %d)", (int)bytes.length(), 2); |
| 115 | 1:3e65e0344413 | cannam | } |
| 116 | |||
| 117 | int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) | |
||
| 118 | ((int)(((MIDIByte)bytes[1]))); |
||
| 119 | return(intRet);
|
||
| 120 | } |
||
| 121 | |||
| 122 | |||
| 123 | // Gets a single byte from the MIDI byte stream. For each track
|
||
| 124 | // section we can read only a specified number of bytes held in
|
||
| 125 | // m_trackByteCount.
|
||
| 126 | //
|
||
| 127 | MIDIByte |
||
| 128 | MIDIFileReader::getMIDIByte() |
||
| 129 | {
|
||
| 130 | if (!m_midiFile) {
|
||
| 131 | 5:7fde3cc109dc | Chris | throw_exception("getMIDIByte called but no MIDI file open");
|
| 132 | 1:3e65e0344413 | cannam | } |
| 133 | |||
| 134 | if (m_midiFile->eof()) {
|
||
| 135 | 5:7fde3cc109dc | Chris | throw_exception("End of MIDI file encountered while reading");
|
| 136 | 1:3e65e0344413 | cannam | } |
| 137 | |||
| 138 | if (m_decrementCount && m_trackByteCount <= 0) { |
||
| 139 | 5:7fde3cc109dc | Chris | throw_exception("Attempt to get more bytes than expected on Track");
|
| 140 | 1:3e65e0344413 | cannam | } |
| 141 | |||
| 142 | char byte;
|
||
| 143 | if (m_midiFile->read(&byte, 1)) { |
||
| 144 | --m_trackByteCount; |
||
| 145 | return (MIDIByte)byte;
|
||
| 146 | } |
||
| 147 | |||
| 148 | 5:7fde3cc109dc | Chris | throw_exception("Attempt to read past MIDI file end");
|
| 149 | 1:3e65e0344413 | cannam | } |
| 150 | |||
| 151 | |||
| 152 | // Gets a specified number of bytes from the MIDI byte stream. For
|
||
| 153 | // each track section we can read only a specified number of bytes
|
||
| 154 | // held in m_trackByteCount.
|
||
| 155 | //
|
||
| 156 | string
|
||
| 157 | MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes) |
||
| 158 | {
|
||
| 159 | if (!m_midiFile) {
|
||
| 160 | 5:7fde3cc109dc | Chris | throw_exception("getMIDIBytes called but no MIDI file open");
|
| 161 | 1:3e65e0344413 | cannam | } |
| 162 | |||
| 163 | if (m_midiFile->eof()) {
|
||
| 164 | 5:7fde3cc109dc | Chris | throw_exception("End of MIDI file encountered while reading");
|
| 165 | 1:3e65e0344413 | cannam | } |
| 166 | |||
| 167 | if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) { |
||
| 168 | 5:7fde3cc109dc | Chris | throw_exception("Attempt to get more bytes than available on Track (%lu, only have %ld)", numberOfBytes, m_trackByteCount);
|
| 169 | 1:3e65e0344413 | cannam | } |
| 170 | |||
| 171 | string stringRet;
|
||
| 172 | char fileMIDIByte;
|
||
| 173 | |||
| 174 | while (stringRet.length() < numberOfBytes &&
|
||
| 175 | m_midiFile->read(&fileMIDIByte, 1)) {
|
||
| 176 | stringRet += fileMIDIByte; |
||
| 177 | } |
||
| 178 | |||
| 179 | // if we've reached the end of file without fulfilling the
|
||
| 180 | // quota then panic as our parsing has performed incorrectly
|
||
| 181 | //
|
||
| 182 | if (stringRet.length() < numberOfBytes) {
|
||
| 183 | stringRet = "";
|
||
| 184 | 5:7fde3cc109dc | Chris | throw_exception("Attempt to read past MIDI file end");
|
| 185 | 1:3e65e0344413 | cannam | } |
| 186 | |||
| 187 | // decrement the byte count
|
||
| 188 | if (m_decrementCount)
|
||
| 189 | m_trackByteCount -= stringRet.length(); |
||
| 190 | |||
| 191 | return stringRet;
|
||
| 192 | } |
||
| 193 | |||
| 194 | |||
| 195 | // Get a long number of variable length from the MIDI byte stream.
|
||
| 196 | //
|
||
| 197 | long
|
||
| 198 | MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
|
||
| 199 | {
|
||
| 200 | if (!m_midiFile) {
|
||
| 201 | 5:7fde3cc109dc | Chris | throw_exception("getNumberFromMIDIBytes called but no MIDI file open");
|
| 202 | 1:3e65e0344413 | cannam | } |
| 203 | |||
| 204 | long longRet = 0; |
||
| 205 | MIDIByte midiByte; |
||
| 206 | |||
| 207 | if (firstByte >= 0) { |
||
| 208 | midiByte = (MIDIByte)firstByte; |
||
| 209 | } else if (m_midiFile->eof()) { |
||
| 210 | return longRet;
|
||
| 211 | } else {
|
||
| 212 | midiByte = getMIDIByte(); |
||
| 213 | } |
||
| 214 | |||
| 215 | longRet = midiByte; |
||
| 216 | if (midiByte & 0x80) { |
||
| 217 | longRet &= 0x7F;
|
||
| 218 | do {
|
||
| 219 | midiByte = getMIDIByte(); |
||
| 220 | longRet = (longRet << 7) + (midiByte & 0x7F); |
||
| 221 | } while (!m_midiFile->eof() && (midiByte & 0x80)); |
||
| 222 | } |
||
| 223 | |||
| 224 | return longRet;
|
||
| 225 | } |
||
| 226 | |||
| 227 | |||
| 228 | // Seek to the next track in the midi file and set the number
|
||
| 229 | // of bytes to be read in the counter m_trackByteCount.
|
||
| 230 | //
|
||
| 231 | bool
|
||
| 232 | MIDIFileReader::skipToNextTrack() |
||
| 233 | {
|
||
| 234 | if (!m_midiFile) {
|
||
| 235 | 5:7fde3cc109dc | Chris | throw_exception("skipToNextTrack called but no MIDI file open");
|
| 236 | 1:3e65e0344413 | cannam | } |
| 237 | |||
| 238 | string buffer, buffer2;
|
||
| 239 | m_trackByteCount = -1;
|
||
| 240 | m_decrementCount = false;
|
||
| 241 | |||
| 242 | while (!m_midiFile->eof() && (m_decrementCount == false)) { |
||
| 243 | buffer = getMIDIBytes(4);
|
||
| 244 | if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) { |
||
| 245 | m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
|
||
| 246 | m_decrementCount = true;
|
||
| 247 | } |
||
| 248 | } |
||
| 249 | |||
| 250 | if (m_trackByteCount == -1) { // we haven't found a track |
||
| 251 | return false; |
||
| 252 | } else {
|
||
| 253 | return true; |
||
| 254 | } |
||
| 255 | } |
||
| 256 | |||
| 257 | |||
| 258 | // Read in a MIDI file. The parsing process throws exceptions back up
|
||
| 259 | // here if we run into trouble which we can then pass back out to
|
||
| 260 | // whoever called us using a nice bool.
|
||
| 261 | //
|
||
| 262 | bool
|
||
| 263 | MIDIFileReader::parseFile() |
||
| 264 | {
|
||
| 265 | m_error = "";
|
||
| 266 | |||
| 267 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 268 | 7:b9a2f08e2c62 | Chris | cerr << "MIDIFileReader::open() : fileName = " << m_path << endl;
|
| 269 | 1:3e65e0344413 | cannam | #endif
|
| 270 | |||
| 271 | // Open the file
|
||
| 272 | 5:7fde3cc109dc | Chris | m_midiFile = new ifstream(m_path.c_str(), ios::in | ios::binary);
|
| 273 | 1:3e65e0344413 | cannam | |
| 274 | if (!*m_midiFile) {
|
||
| 275 | m_error = "File not found or not readable.";
|
||
| 276 | m_format = MIDI_FILE_BAD_FORMAT; |
||
| 277 | delete m_midiFile;
|
||
| 278 | m_midiFile = 0;
|
||
| 279 | return false; |
||
| 280 | } |
||
| 281 | |||
| 282 | bool retval = false; |
||
| 283 | |||
| 284 | try {
|
||
| 285 | |||
| 286 | // Set file size so we can count it off
|
||
| 287 | //
|
||
| 288 | m_midiFile->seekg(0, ios::end);
|
||
| 289 | m_fileSize = m_midiFile->tellg(); |
||
| 290 | m_midiFile->seekg(0, ios::beg);
|
||
| 291 | |||
| 292 | // Parse the MIDI header first. The first 14 bytes of the file.
|
||
| 293 | if (!parseHeader(getMIDIBytes(14))) { |
||
| 294 | m_format = MIDI_FILE_BAD_FORMAT; |
||
| 295 | m_error = "Not a MIDI file.";
|
||
| 296 | goto done;
|
||
| 297 | } |
||
| 298 | |||
| 299 | for (unsigned int j = 0; j < m_numberOfTracks; ++j) { |
||
| 300 | |||
| 301 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 302 | cerr << "Parsing Track " << j << endl;
|
||
| 303 | #endif
|
||
| 304 | |||
| 305 | if (!skipToNextTrack()) {
|
||
| 306 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 307 | cerr << "Couldn't find Track " << j << endl;
|
||
| 308 | #endif
|
||
| 309 | m_error = "File corrupted or in non-standard format?";
|
||
| 310 | m_format = MIDI_FILE_BAD_FORMAT; |
||
| 311 | goto done;
|
||
| 312 | } |
||
| 313 | |||
| 314 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 315 | cerr << "Track has " << m_trackByteCount << " bytes" << endl; |
||
| 316 | #endif
|
||
| 317 | |||
| 318 | // Run through the events taking them into our internal
|
||
| 319 | // representation.
|
||
| 320 | if (!parseTrack(j)) {
|
||
| 321 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 322 | cerr << "Track " << j << " parsing failed" << endl; |
||
| 323 | #endif
|
||
| 324 | m_error = "File corrupted or in non-standard format?";
|
||
| 325 | m_format = MIDI_FILE_BAD_FORMAT; |
||
| 326 | goto done;
|
||
| 327 | } |
||
| 328 | } |
||
| 329 | |||
| 330 | retval = true;
|
||
| 331 | |||
| 332 | } catch (MIDIException e) {
|
||
| 333 | |||
| 334 | cerr << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
|
||
| 335 | m_error = e.what(); |
||
| 336 | } |
||
| 337 | |||
| 338 | done:
|
||
| 339 | m_midiFile->close(); |
||
| 340 | delete m_midiFile;
|
||
| 341 | |||
| 342 | for (unsigned int track = 0; track < m_numberOfTracks; ++track) { |
||
| 343 | |||
| 344 | // Convert the deltaTime to an absolute time since the track
|
||
| 345 | // start. The addTime method returns the sum of the current
|
||
| 346 | // MIDI Event delta time plus the argument.
|
||
| 347 | |||
| 348 | unsigned long acc = 0; |
||
| 349 | |||
| 350 | for (MIDITrack::iterator i = m_midiComposition[track].begin();
|
||
| 351 | i != m_midiComposition[track].end(); ++i) {
|
||
| 352 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 353 | cerr << "converting delta time " << i->getTime();
|
||
| 354 | #endif
|
||
| 355 | acc = i->addTime(acc); |
||
| 356 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 357 | cerr << " to " << i->getTime() << endl;
|
||
| 358 | #endif
|
||
| 359 | } |
||
| 360 | |||
| 361 | consolidateNoteOffEvents(track); |
||
| 362 | } |
||
| 363 | |||
| 364 | return retval;
|
||
| 365 | } |
||
| 366 | |||
| 367 | // Parse and ensure the MIDI Header is legitimate
|
||
| 368 | //
|
||
| 369 | bool
|
||
| 370 | MIDIFileReader::parseHeader(const string &midiHeader) |
||
| 371 | {
|
||
| 372 | if (midiHeader.size() < 14) { |
||
| 373 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 374 | cerr << "MIDIFileReader::parseHeader() - file header undersized" << endl;
|
||
| 375 | #endif
|
||
| 376 | return false; |
||
| 377 | } |
||
| 378 | |||
| 379 | if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) { |
||
| 380 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 381 | cerr << "MIDIFileReader::parseHeader()"
|
||
| 382 | << "- file header not found or malformed"
|
||
| 383 | << endl; |
||
| 384 | #endif
|
||
| 385 | return false; |
||
| 386 | } |
||
| 387 | |||
| 388 | if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) { |
||
| 389 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 390 | cerr << "MIDIFileReader::parseHeader()"
|
||
| 391 | << " - header length incorrect"
|
||
| 392 | << endl; |
||
| 393 | #endif
|
||
| 394 | return false; |
||
| 395 | } |
||
| 396 | |||
| 397 | m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2)); |
||
| 398 | m_numberOfTracks = midiBytesToInt(midiHeader.substr(10,2)); |
||
| 399 | m_timingDivision = midiBytesToInt(midiHeader.substr(12,2)); |
||
| 400 | |||
| 401 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 402 | if (m_timingDivision < 0) { |
||
| 403 | cerr << "MIDIFileReader::parseHeader()"
|
||
| 404 | << " - file uses SMPTE timing"
|
||
| 405 | << endl; |
||
| 406 | } |
||
| 407 | #endif
|
||
| 408 | |||
| 409 | return true; |
||
| 410 | } |
||
| 411 | |||
| 412 | // Extract the contents from a MIDI file track and places it into
|
||
| 413 | // our local map of MIDI events.
|
||
| 414 | //
|
||
| 415 | bool
|
||
| 416 | MIDIFileReader::parseTrack(unsigned int trackNum) |
||
| 417 | {
|
||
| 418 | MIDIByte midiByte, metaEventCode, data1, data2; |
||
| 419 | MIDIByte eventCode = 0x80;
|
||
| 420 | string metaMessage;
|
||
| 421 | unsigned int messageLength; |
||
| 422 | unsigned long deltaTime; |
||
| 423 | unsigned long accumulatedTime = 0; |
||
| 424 | |||
| 425 | // Remember the last non-meta status byte (-1 if we haven't seen one)
|
||
| 426 | int runningStatus = -1; |
||
| 427 | |||
| 428 | while (!m_midiFile->eof() && (m_trackByteCount > 0)) { |
||
| 429 | |||
| 430 | if (eventCode < 0x80) { |
||
| 431 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 432 | cerr << "WARNING: Invalid event code " << eventCode
|
||
| 433 | << " in MIDI file" << endl;
|
||
| 434 | #endif
|
||
| 435 | 5:7fde3cc109dc | Chris | throw_exception("Invalid event code %d found", int(eventCode)); |
| 436 | 1:3e65e0344413 | cannam | } |
| 437 | |||
| 438 | deltaTime = getNumberFromMIDIBytes(); |
||
| 439 | |||
| 440 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 441 | cerr << "read delta time " << deltaTime << endl;
|
||
| 442 | #endif
|
||
| 443 | |||
| 444 | // Get a single byte
|
||
| 445 | midiByte = getMIDIByte(); |
||
| 446 | |||
| 447 | if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
|
||
| 448 | |||
| 449 | if (runningStatus < 0) { |
||
| 450 | 5:7fde3cc109dc | Chris | throw_exception("Running status used for first event in track");
|
| 451 | 1:3e65e0344413 | cannam | } |
| 452 | |||
| 453 | eventCode = (MIDIByte)runningStatus; |
||
| 454 | data1 = midiByte; |
||
| 455 | |||
| 456 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 457 | cerr << "using running status (byte " << int(midiByte) << " found)" << endl; |
||
| 458 | #endif
|
||
| 459 | } else {
|
||
| 460 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 461 | cerr << "have new event code " << int(midiByte) << endl; |
||
| 462 | #endif
|
||
| 463 | eventCode = midiByte; |
||
| 464 | data1 = getMIDIByte(); |
||
| 465 | } |
||
| 466 | |||
| 467 | if (eventCode == MIDI_FILE_META_EVENT) {
|
||
| 468 | |||
| 469 | metaEventCode = data1; |
||
| 470 | messageLength = getNumberFromMIDIBytes(); |
||
| 471 | |||
| 472 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 473 | 7:b9a2f08e2c62 | Chris | cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found" << endl; |
| 474 | 1:3e65e0344413 | cannam | #endif
|
| 475 | metaMessage = getMIDIBytes(messageLength); |
||
| 476 | |||
| 477 | accumulatedTime += deltaTime; |
||
| 478 | |||
| 479 | MIDIEvent e(deltaTime, |
||
| 480 | MIDI_FILE_META_EVENT, |
||
| 481 | metaEventCode, |
||
| 482 | metaMessage); |
||
| 483 | |||
| 484 | m_midiComposition[trackNum].push_back(e); |
||
| 485 | |||
| 486 | if (metaEventCode == MIDI_TRACK_NAME) {
|
||
| 487 | m_trackNames[trackNum] = metaMessage.c_str(); |
||
| 488 | } |
||
| 489 | |||
| 490 | } else { // non-meta events |
||
| 491 | |||
| 492 | runningStatus = eventCode; |
||
| 493 | |||
| 494 | int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
|
||
| 495 | |||
| 496 | accumulatedTime += deltaTime; |
||
| 497 | |||
| 498 | switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
|
||
| 499 | |||
| 500 | case MIDI_NOTE_ON:
|
||
| 501 | case MIDI_NOTE_OFF:
|
||
| 502 | case MIDI_POLY_AFTERTOUCH:
|
||
| 503 | case MIDI_CTRL_CHANGE:
|
||
| 504 | data2 = getMIDIByte(); |
||
| 505 | |||
| 506 | {
|
||
| 507 | // create and store our event
|
||
| 508 | MIDIEvent midiEvent(deltaTime, eventCode, data1, data2); |
||
| 509 | |||
| 510 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 511 | cerr << "MIDI event for channel " << channel << " (track " |
||
| 512 | << trackNum << ") with delta time " << deltaTime << endl;
|
||
| 513 | #endif
|
||
| 514 | |||
| 515 | m_midiComposition[trackNum].push_back(midiEvent); |
||
| 516 | } |
||
| 517 | break;
|
||
| 518 | |||
| 519 | case MIDI_PITCH_BEND:
|
||
| 520 | data2 = getMIDIByte(); |
||
| 521 | |||
| 522 | {
|
||
| 523 | // create and store our event
|
||
| 524 | MIDIEvent midiEvent(deltaTime, eventCode, data1, data2); |
||
| 525 | m_midiComposition[trackNum].push_back(midiEvent); |
||
| 526 | } |
||
| 527 | break;
|
||
| 528 | |||
| 529 | case MIDI_PROG_CHANGE:
|
||
| 530 | case MIDI_CHNL_AFTERTOUCH:
|
||
| 531 | |||
| 532 | {
|
||
| 533 | // create and store our event
|
||
| 534 | MIDIEvent midiEvent(deltaTime, eventCode, data1); |
||
| 535 | m_midiComposition[trackNum].push_back(midiEvent); |
||
| 536 | } |
||
| 537 | break;
|
||
| 538 | |||
| 539 | case MIDI_SYSTEM_EXCLUSIVE:
|
||
| 540 | messageLength = getNumberFromMIDIBytes(data1); |
||
| 541 | |||
| 542 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 543 | cerr << "SysEx of " << messageLength << " bytes found" << endl; |
||
| 544 | #endif
|
||
| 545 | |||
| 546 | metaMessage= getMIDIBytes(messageLength); |
||
| 547 | |||
| 548 | if (MIDIByte(metaMessage[metaMessage.length() - 1]) != |
||
| 549 | MIDI_END_OF_EXCLUSIVE) |
||
| 550 | {
|
||
| 551 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 552 | cerr << "MIDIFileReader::parseTrack() - "
|
||
| 553 | << "malformed or unsupported SysEx type"
|
||
| 554 | << endl; |
||
| 555 | #endif
|
||
| 556 | continue;
|
||
| 557 | } |
||
| 558 | |||
| 559 | // chop off the EOX
|
||
| 560 | // length fixed by Pedro Lopez-Cabanillas (20030523)
|
||
| 561 | //
|
||
| 562 | metaMessage = metaMessage.substr(0, metaMessage.length()-1); |
||
| 563 | |||
| 564 | {
|
||
| 565 | MIDIEvent midiEvent(deltaTime, |
||
| 566 | MIDI_SYSTEM_EXCLUSIVE, |
||
| 567 | metaMessage); |
||
| 568 | m_midiComposition[trackNum].push_back(midiEvent); |
||
| 569 | } |
||
| 570 | break;
|
||
| 571 | |||
| 572 | case MIDI_END_OF_EXCLUSIVE:
|
||
| 573 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 574 | cerr << "MIDIFileReader::parseTrack() - "
|
||
| 575 | << "Found a stray MIDI_END_OF_EXCLUSIVE" << endl;
|
||
| 576 | #endif
|
||
| 577 | break;
|
||
| 578 | |||
| 579 | default:
|
||
| 580 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 581 | cerr << "MIDIFileReader::parseTrack()"
|
||
| 582 | << " - Unsupported MIDI Event Code: "
|
||
| 583 | << (int)eventCode << endl;
|
||
| 584 | #endif
|
||
| 585 | break;
|
||
| 586 | } |
||
| 587 | } |
||
| 588 | } |
||
| 589 | |||
| 590 | return true; |
||
| 591 | } |
||
| 592 | |||
| 593 | // Delete dead NOTE OFF and NOTE ON/Zero Velocity Events after
|
||
| 594 | // reading them and modifying their relevant NOTE ONs. Return true
|
||
| 595 | // if there are some notes in this track.
|
||
| 596 | //
|
||
| 597 | bool
|
||
| 598 | MIDIFileReader::consolidateNoteOffEvents(unsigned int track) |
||
| 599 | {
|
||
| 600 | bool notesOnTrack = false; |
||
| 601 | bool noteOffFound;
|
||
| 602 | |||
| 603 | MIDITrack &t = m_midiComposition[track]; |
||
| 604 | |||
| 605 | for (MIDITrack::iterator i = t.begin(); i != t.end(); ++i) {
|
||
| 606 | |||
| 607 | if (i->getMessageType() == MIDI_NOTE_ON && i->getVelocity() > 0) { |
||
| 608 | |||
| 609 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 610 | cerr << "Looking for note-offs for note at " << i->getTime() << " (pitch " << (int)i->getPitch() << ")" << endl; |
||
| 611 | #endif
|
||
| 612 | |||
| 613 | notesOnTrack = true;
|
||
| 614 | noteOffFound = false;
|
||
| 615 | |||
| 616 | for (MIDITrack::iterator j = i; j != t.end(); ++j) {
|
||
| 617 | |||
| 618 | if ((j->getChannelNumber() == i->getChannelNumber()) &&
|
||
| 619 | (j->getPitch() == i->getPitch()) && |
||
| 620 | (j->getMessageType() == MIDI_NOTE_OFF || |
||
| 621 | (j->getMessageType() == MIDI_NOTE_ON && |
||
| 622 | j->getVelocity() == 0x00))) {
|
||
| 623 | |||
| 624 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 625 | cerr << "Found note-off at " << j->getTime() << " for note at " << i->getTime() << endl; |
||
| 626 | #endif
|
||
| 627 | |||
| 628 | i->setDuration(j->getTime() - i->getTime()); |
||
| 629 | |||
| 630 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 631 | cerr << "Duration is now " << i->getDuration() << endl;
|
||
| 632 | #endif
|
||
| 633 | |||
| 634 | t.erase(j); |
||
| 635 | |||
| 636 | noteOffFound = true;
|
||
| 637 | break;
|
||
| 638 | } |
||
| 639 | } |
||
| 640 | |||
| 641 | // If no matching NOTE OFF has been found then set
|
||
| 642 | // Event duration to length of track
|
||
| 643 | //
|
||
| 644 | if (!noteOffFound) {
|
||
| 645 | #ifdef DEBUG_MIDI_FILE_READER
|
||
| 646 | cerr << "Failed to find note-off for note at " << i->getTime() << endl;
|
||
| 647 | #endif
|
||
| 648 | MIDITrack::iterator j = t.end(); |
||
| 649 | --j; |
||
| 650 | i->setDuration(j->getTime() - i->getTime()); |
||
| 651 | } |
||
| 652 | } |
||
| 653 | } |
||
| 654 | |||
| 655 | return notesOnTrack;
|
||
| 656 | } |
||
| 657 | |||
| 658 | MIDIComposition |
||
| 659 | MIDIFileReader::load() const
|
||
| 660 | {
|
||
| 661 | return m_midiComposition;
|
||
| 662 | } |
||
| 663 |