comparison data/fileio/MIDIFileReader.cpp @ 1429:48e9f538e6e9

Untabify
author Chris Cannam
date Thu, 01 Mar 2018 18:02:22 +0000
parents 39271c98cbdd
children cee1be4fb8c1
comparison
equal deleted inserted replaced
1428:87ae75da6527 1429:48e9f538e6e9
56 //#define MIDI_DEBUG 1 56 //#define MIDI_DEBUG 1
57 57
58 58
59 MIDIFileReader::MIDIFileReader(QString path, 59 MIDIFileReader::MIDIFileReader(QString path,
60 MIDIFileImportPreferenceAcquirer *acquirer, 60 MIDIFileImportPreferenceAcquirer *acquirer,
61 sv_samplerate_t mainModelSampleRate) : 61 sv_samplerate_t mainModelSampleRate) :
62 m_smpte(false), 62 m_smpte(false),
63 m_timingDivision(0), 63 m_timingDivision(0),
64 m_fps(0), 64 m_fps(0),
65 m_subframes(0), 65 m_subframes(0),
66 m_format(MIDI_FILE_BAD_FORMAT), 66 m_format(MIDI_FILE_BAD_FORMAT),
72 m_fileSize(0), 72 m_fileSize(0),
73 m_mainModelSampleRate(mainModelSampleRate), 73 m_mainModelSampleRate(mainModelSampleRate),
74 m_acquirer(acquirer) 74 m_acquirer(acquirer)
75 { 75 {
76 if (parseFile()) { 76 if (parseFile()) {
77 m_error = ""; 77 m_error = "";
78 } 78 }
79 } 79 }
80 80
81 MIDIFileReader::~MIDIFileReader() 81 MIDIFileReader::~MIDIFileReader()
82 { 82 {
83 for (MIDIComposition::iterator i = m_midiComposition.begin(); 83 for (MIDIComposition::iterator i = m_midiComposition.begin();
84 i != m_midiComposition.end(); ++i) { 84 i != m_midiComposition.end(); ++i) {
85 85
86 for (MIDITrack::iterator j = i->second.begin(); 86 for (MIDITrack::iterator j = i->second.begin();
87 j != i->second.end(); ++j) { 87 j != i->second.end(); ++j) {
88 delete *j; 88 delete *j;
89 } 89 }
90 90
91 i->second.clear(); 91 i->second.clear();
92 } 92 }
93 93
94 m_midiComposition.clear(); 94 m_midiComposition.clear();
95 } 95 }
96 96
108 108
109 long 109 long
110 MIDIFileReader::midiBytesToLong(const string& bytes) 110 MIDIFileReader::midiBytesToLong(const string& bytes)
111 { 111 {
112 if (bytes.length() != 4) { 112 if (bytes.length() != 4) {
113 throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4)); 113 throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4));
114 } 114 }
115 115
116 long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) | 116 long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) |
117 ((long)(((MIDIByte)bytes[1]) << 16)) | 117 ((long)(((MIDIByte)bytes[1]) << 16)) |
118 ((long)(((MIDIByte)bytes[2]) << 8)) | 118 ((long)(((MIDIByte)bytes[2]) << 8)) |
123 123
124 int 124 int
125 MIDIFileReader::midiBytesToInt(const string& bytes) 125 MIDIFileReader::midiBytesToInt(const string& bytes)
126 { 126 {
127 if (bytes.length() != 2) { 127 if (bytes.length() != 2) {
128 throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2)); 128 throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2));
129 } 129 }
130 130
131 int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) | 131 int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) |
132 ((int)(((MIDIByte)bytes[1]))); 132 ((int)(((MIDIByte)bytes[1])));
133 return(intRet); 133 return(intRet);
140 // 140 //
141 MIDIByte 141 MIDIByte
142 MIDIFileReader::getMIDIByte() 142 MIDIFileReader::getMIDIByte()
143 { 143 {
144 if (!m_midiFile) { 144 if (!m_midiFile) {
145 throw MIDIException(tr("getMIDIByte called but no MIDI file open")); 145 throw MIDIException(tr("getMIDIByte called but no MIDI file open"));
146 } 146 }
147 147
148 if (m_midiFile->eof()) { 148 if (m_midiFile->eof()) {
149 throw MIDIException(tr("End of MIDI file encountered while reading")); 149 throw MIDIException(tr("End of MIDI file encountered while reading"));
150 } 150 }
153 throw MIDIException(tr("Attempt to get more bytes than expected on Track")); 153 throw MIDIException(tr("Attempt to get more bytes than expected on Track"));
154 } 154 }
155 155
156 char byte; 156 char byte;
157 if (m_midiFile->read(&byte, 1)) { 157 if (m_midiFile->read(&byte, 1)) {
158 --m_trackByteCount; 158 --m_trackByteCount;
159 return (MIDIByte)byte; 159 return (MIDIByte)byte;
160 } 160 }
161 161
162 throw MIDIException(tr("Attempt to read past MIDI file end")); 162 throw MIDIException(tr("Attempt to read past MIDI file end"));
163 } 163 }
164 164
169 // 169 //
170 string 170 string
171 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes) 171 MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes)
172 { 172 {
173 if (!m_midiFile) { 173 if (!m_midiFile) {
174 throw MIDIException(tr("getMIDIBytes called but no MIDI file open")); 174 throw MIDIException(tr("getMIDIBytes called but no MIDI file open"));
175 } 175 }
176 176
177 if (m_midiFile->eof()) { 177 if (m_midiFile->eof()) {
178 throw MIDIException(tr("End of MIDI file encountered while reading")); 178 throw MIDIException(tr("End of MIDI file encountered while reading"));
179 } 179 }
210 // 210 //
211 long 211 long
212 MIDIFileReader::getNumberFromMIDIBytes(int firstByte) 212 MIDIFileReader::getNumberFromMIDIBytes(int firstByte)
213 { 213 {
214 if (!m_midiFile) { 214 if (!m_midiFile) {
215 throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open")); 215 throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open"));
216 } 216 }
217 217
218 long longRet = 0; 218 long longRet = 0;
219 MIDIByte midiByte; 219 MIDIByte midiByte;
220 220
221 if (firstByte >= 0) { 221 if (firstByte >= 0) {
222 midiByte = (MIDIByte)firstByte; 222 midiByte = (MIDIByte)firstByte;
223 } else if (m_midiFile->eof()) { 223 } else if (m_midiFile->eof()) {
224 return longRet; 224 return longRet;
225 } else { 225 } else {
226 midiByte = getMIDIByte(); 226 midiByte = getMIDIByte();
227 } 227 }
228 228
229 longRet = midiByte; 229 longRet = midiByte;
230 if (midiByte & 0x80) { 230 if (midiByte & 0x80) {
231 longRet &= 0x7F; 231 longRet &= 0x7F;
232 do { 232 do {
233 midiByte = getMIDIByte(); 233 midiByte = getMIDIByte();
234 longRet = (longRet << 7) + (midiByte & 0x7F); 234 longRet = (longRet << 7) + (midiByte & 0x7F);
235 } while (!m_midiFile->eof() && (midiByte & 0x80)); 235 } while (!m_midiFile->eof() && (midiByte & 0x80));
236 } 236 }
237 237
238 return longRet; 238 return longRet;
239 } 239 }
240 240
244 // 244 //
245 bool 245 bool
246 MIDIFileReader::skipToNextTrack() 246 MIDIFileReader::skipToNextTrack()
247 { 247 {
248 if (!m_midiFile) { 248 if (!m_midiFile) {
249 throw MIDIException(tr("skipToNextTrack called but no MIDI file open")); 249 throw MIDIException(tr("skipToNextTrack called but no MIDI file open"));
250 } 250 }
251 251
252 string buffer, buffer2; 252 string buffer, buffer2;
253 m_trackByteCount = -1; 253 m_trackByteCount = -1;
254 m_decrementCount = false; 254 m_decrementCount = false;
255 255
256 while (!m_midiFile->eof() && (m_decrementCount == false)) { 256 while (!m_midiFile->eof() && (m_decrementCount == false)) {
257 buffer = getMIDIBytes(4); 257 buffer = getMIDIBytes(4);
258 if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) { 258 if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) {
259 m_trackByteCount = midiBytesToLong(getMIDIBytes(4)); 259 m_trackByteCount = midiBytesToLong(getMIDIBytes(4));
260 m_decrementCount = true; 260 m_decrementCount = true;
261 } 261 }
262 } 262 }
263 263
264 if (m_trackByteCount == -1) { // we haven't found a track 264 if (m_trackByteCount == -1) { // we haven't found a track
265 return false; 265 return false;
266 } else { 266 } else {
282 SVDEBUG << "MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl; 282 SVDEBUG << "MIDIFileReader::open() : fileName = " << m_fileName.c_str() << endl;
283 #endif 283 #endif
284 284
285 // Open the file 285 // Open the file
286 m_midiFile = new ifstream(m_path.toLocal8Bit().data(), 286 m_midiFile = new ifstream(m_path.toLocal8Bit().data(),
287 ios::in | ios::binary); 287 ios::in | ios::binary);
288 288
289 if (!*m_midiFile) { 289 if (!*m_midiFile) {
290 m_error = "File not found or not readable."; 290 m_error = "File not found or not readable.";
291 m_format = MIDI_FILE_BAD_FORMAT; 291 m_format = MIDI_FILE_BAD_FORMAT;
292 delete m_midiFile; 292 delete m_midiFile;
293 m_midiFile = 0; 293 m_midiFile = 0;
294 return false; 294 return false;
295 } 295 }
296 296
297 bool retval = false; 297 bool retval = false;
298 298
299 try { 299 try {
300 300
301 // Set file size so we can count it off 301 // Set file size so we can count it off
302 // 302 //
303 m_midiFile->seekg(0, ios::end); 303 m_midiFile->seekg(0, ios::end);
304 std::streamoff off = m_midiFile->tellg(); 304 std::streamoff off = m_midiFile->tellg();
305 m_fileSize = 0; 305 m_fileSize = 0;
306 if (off > 0) m_fileSize = off; 306 if (off > 0) m_fileSize = off;
307 m_midiFile->seekg(0, ios::beg); 307 m_midiFile->seekg(0, ios::beg);
308 308
309 // Parse the MIDI header first. The first 14 bytes of the file. 309 // Parse the MIDI header first. The first 14 bytes of the file.
310 if (!parseHeader(getMIDIBytes(14))) { 310 if (!parseHeader(getMIDIBytes(14))) {
311 m_format = MIDI_FILE_BAD_FORMAT; 311 m_format = MIDI_FILE_BAD_FORMAT;
312 m_error = "Not a MIDI file."; 312 m_error = "Not a MIDI file.";
313 goto done; 313 goto done;
314 } 314 }
315 315
316 unsigned int i = 0; 316 unsigned int i = 0;
317 317
318 for (unsigned int j = 0; j < m_numberOfTracks; ++j) { 318 for (unsigned int j = 0; j < m_numberOfTracks; ++j) {
319 319
320 #ifdef MIDI_DEBUG 320 #ifdef MIDI_DEBUG
321 SVDEBUG << "Parsing Track " << j << endl; 321 SVDEBUG << "Parsing Track " << j << endl;
322 #endif 322 #endif
323 323
324 if (!skipToNextTrack()) { 324 if (!skipToNextTrack()) {
325 #ifdef MIDI_DEBUG 325 #ifdef MIDI_DEBUG
326 SVDEBUG << "Couldn't find Track " << j << endl; 326 SVDEBUG << "Couldn't find Track " << j << endl;
327 #endif 327 #endif
328 m_error = "File corrupted or in non-standard format?"; 328 m_error = "File corrupted or in non-standard format?";
329 m_format = MIDI_FILE_BAD_FORMAT; 329 m_format = MIDI_FILE_BAD_FORMAT;
330 goto done; 330 goto done;
331 } 331 }
332 332
333 #ifdef MIDI_DEBUG 333 #ifdef MIDI_DEBUG
334 SVDEBUG << "Track has " << m_trackByteCount << " bytes" << endl; 334 SVDEBUG << "Track has " << m_trackByteCount << " bytes" << endl;
335 #endif 335 #endif
336 336
337 // Run through the events taking them into our internal 337 // Run through the events taking them into our internal
338 // representation. 338 // representation.
339 if (!parseTrack(i)) { 339 if (!parseTrack(i)) {
340 #ifdef MIDI_DEBUG 340 #ifdef MIDI_DEBUG
341 SVDEBUG << "Track " << j << " parsing failed" << endl; 341 SVDEBUG << "Track " << j << " parsing failed" << endl;
342 #endif 342 #endif
343 m_error = "File corrupted or in non-standard format?"; 343 m_error = "File corrupted or in non-standard format?";
344 m_format = MIDI_FILE_BAD_FORMAT; 344 m_format = MIDI_FILE_BAD_FORMAT;
345 goto done; 345 goto done;
346 } 346 }
347 347
348 ++i; // j is the source track number, i the destination 348 ++i; // j is the source track number, i the destination
349 } 349 }
350 350
351 m_numberOfTracks = i; 351 m_numberOfTracks = i;
352 retval = true; 352 retval = true;
353 353
354 } catch (MIDIException e) { 354 } catch (MIDIException e) {
355 355
356 SVDEBUG << "MIDIFileReader::open() - caught exception - " << e.what() << endl; 356 SVDEBUG << "MIDIFileReader::open() - caught exception - " << e.what() << endl;
357 m_error = e.what(); 357 m_error = e.what();
358 } 358 }
359 359
360 done: 360 done:
361 m_midiFile->close(); 361 m_midiFile->close();
362 delete m_midiFile; 362 delete m_midiFile;
365 365
366 // Convert the deltaTime to an absolute time since the track 366 // Convert the deltaTime to an absolute time since the track
367 // start. The addTime method returns the sum of the current 367 // start. The addTime method returns the sum of the current
368 // MIDI Event delta time plus the argument. 368 // MIDI Event delta time plus the argument.
369 369
370 unsigned long acc = 0; 370 unsigned long acc = 0;
371 371
372 for (MIDITrack::iterator i = m_midiComposition[track].begin(); 372 for (MIDITrack::iterator i = m_midiComposition[track].begin();
373 i != m_midiComposition[track].end(); ++i) { 373 i != m_midiComposition[track].end(); ++i) {
374 acc = (*i)->addTime(acc); 374 acc = (*i)->addTime(acc);
375 } 375 }
376 376
377 if (consolidateNoteOffEvents(track)) { // returns true if some notes exist 377 if (consolidateNoteOffEvents(track)) { // returns true if some notes exist
378 m_loadableTracks.insert(track); 378 m_loadableTracks.insert(track);
379 } 379 }
380 } 380 }
381 381
382 for (unsigned int track = 0; track < m_numberOfTracks; ++track) { 382 for (unsigned int track = 0; track < m_numberOfTracks; ++track) {
383 updateTempoMap(track); 383 updateTempoMap(track);
384 } 384 }
400 return false; 400 return false;
401 } 401 }
402 402
403 if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) { 403 if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) {
404 #ifdef MIDI_DEBUG 404 #ifdef MIDI_DEBUG
405 SVDEBUG << "MIDIFileReader::parseHeader()" 405 SVDEBUG << "MIDIFileReader::parseHeader()"
406 << "- file header not found or malformed" 406 << "- file header not found or malformed"
407 << endl; 407 << endl;
408 #endif 408 #endif
409 return false; 409 return false;
410 } 410 }
411 411
412 if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) { 412 if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) {
413 #ifdef MIDI_DEBUG 413 #ifdef MIDI_DEBUG
414 SVDEBUG << "MIDIFileReader::parseHeader()" 414 SVDEBUG << "MIDIFileReader::parseHeader()"
415 << " - header length incorrect" 415 << " - header length incorrect"
416 << endl; 416 << endl;
417 #endif 417 #endif
418 return false; 418 return false;
419 } 419 }
420 420
421 m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2)); 421 m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8,2));
472 472
473 bool firstTrack = true; 473 bool firstTrack = true;
474 474
475 while (!m_midiFile->eof() && (m_trackByteCount > 0)) { 475 while (!m_midiFile->eof() && (m_trackByteCount > 0)) {
476 476
477 if (eventCode < 0x80) { 477 if (eventCode < 0x80) {
478 #ifdef MIDI_DEBUG 478 #ifdef MIDI_DEBUG
479 SVDEBUG << "WARNING: Invalid event code " << eventCode 479 SVDEBUG << "WARNING: Invalid event code " << eventCode
480 << " in MIDI file" << endl; 480 << " in MIDI file" << endl;
481 #endif 481 #endif
482 throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode))); 482 throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode)));
483 } 483 }
484 484
485 deltaTime = getNumberFromMIDIBytes(); 485 deltaTime = getNumberFromMIDIBytes();
486 486
487 #ifdef MIDI_DEBUG 487 #ifdef MIDI_DEBUG
488 SVDEBUG << "read delta time " << deltaTime << endl; 488 SVDEBUG << "read delta time " << deltaTime << endl;
489 #endif 489 #endif
490 490
491 // Get a single byte 491 // Get a single byte
492 midiByte = getMIDIByte(); 492 midiByte = getMIDIByte();
493 493
494 if (!(midiByte & MIDI_STATUS_BYTE_MASK)) { 494 if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
495 495
496 if (runningStatus < 0) { 496 if (runningStatus < 0) {
497 throw MIDIException(tr("Running status used for first event in track")); 497 throw MIDIException(tr("Running status used for first event in track"));
498 } 498 }
499 499
500 eventCode = (MIDIByte)runningStatus; 500 eventCode = (MIDIByte)runningStatus;
501 data1 = midiByte; 501 data1 = midiByte;
502 502
503 #ifdef MIDI_DEBUG 503 #ifdef MIDI_DEBUG
504 SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl; 504 SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl;
505 #endif 505 #endif
506 } else { 506 } else {
507 #ifdef MIDI_DEBUG 507 #ifdef MIDI_DEBUG
508 SVDEBUG << "have new event code " << int(midiByte) << endl; 508 SVDEBUG << "have new event code " << int(midiByte) << endl;
509 #endif 509 #endif
510 eventCode = midiByte; 510 eventCode = midiByte;
511 data1 = getMIDIByte(); 511 data1 = getMIDIByte();
512 } 512 }
513 513
514 if (eventCode == MIDI_FILE_META_EVENT) { 514 if (eventCode == MIDI_FILE_META_EVENT) {
515 515
516 metaEventCode = data1; 516 metaEventCode = data1;
517 messageLength = getNumberFromMIDIBytes(); 517 messageLength = getNumberFromMIDIBytes();
518 518
519 //#ifdef MIDI_DEBUG 519 //#ifdef MIDI_DEBUG
520 SVDEBUG << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl; 520 SVDEBUG << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl;
521 //#endif 521 //#endif
522 metaMessage = getMIDIBytes(messageLength); 522 metaMessage = getMIDIBytes(messageLength);
523 523
524 long gap = accumulatedTime - trackTimeMap[metaTrack]; 524 long gap = accumulatedTime - trackTimeMap[metaTrack];
525 accumulatedTime += deltaTime; 525 accumulatedTime += deltaTime;
526 deltaTime += gap; 526 deltaTime += gap;
527 trackTimeMap[metaTrack] = accumulatedTime; 527 trackTimeMap[metaTrack] = accumulatedTime;
528 528
529 MIDIEvent *e = new MIDIEvent(deltaTime, 529 MIDIEvent *e = new MIDIEvent(deltaTime,
530 MIDI_FILE_META_EVENT, 530 MIDI_FILE_META_EVENT,
531 metaEventCode, 531 metaEventCode,
532 metaMessage); 532 metaMessage);
533 533
534 m_midiComposition[metaTrack].push_back(e); 534 m_midiComposition[metaTrack].push_back(e);
535 535
536 if (metaEventCode == MIDI_TRACK_NAME) { 536 if (metaEventCode == MIDI_TRACK_NAME) {
537 m_trackNames[metaTrack] = metaMessage.c_str(); 537 m_trackNames[metaTrack] = metaMessage.c_str();
538 } 538 }
539 539
540 } else { // non-meta events 540 } else { // non-meta events
541 541
542 runningStatus = eventCode; 542 runningStatus = eventCode;
543 543
544 MIDIEvent *midiEvent; 544 MIDIEvent *midiEvent;
545 545
546 int channel = (eventCode & MIDI_CHANNEL_NUM_MASK); 546 int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
547 if (channelTrackMap[channel] == -1) { 547 if (channelTrackMap[channel] == -1) {
548 if (!firstTrack) ++lastTrackNum; 548 if (!firstTrack) ++lastTrackNum;
549 else firstTrack = false; 549 else firstTrack = false;
550 channelTrackMap[channel] = lastTrackNum; 550 channelTrackMap[channel] = lastTrackNum;
551 } 551 }
552 552
553 unsigned int trackNum = channelTrackMap[channel]; 553 unsigned int trackNum = channelTrackMap[channel];
554 554
555 // accumulatedTime is abs time of last event on any track; 555 // accumulatedTime is abs time of last event on any track;
556 // trackTimeMap[trackNum] is that of last event on this track 556 // trackTimeMap[trackNum] is that of last event on this track
557 557
558 long gap = accumulatedTime - trackTimeMap[trackNum]; 558 long gap = accumulatedTime - trackTimeMap[trackNum];
559 accumulatedTime += deltaTime; 559 accumulatedTime += deltaTime;
560 deltaTime += gap; 560 deltaTime += gap;
561 trackTimeMap[trackNum] = accumulatedTime; 561 trackTimeMap[trackNum] = accumulatedTime;
562 562
563 switch (eventCode & MIDI_MESSAGE_TYPE_MASK) { 563 switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
564 564
565 case MIDI_NOTE_ON: 565 case MIDI_NOTE_ON:
566 case MIDI_NOTE_OFF: 566 case MIDI_NOTE_OFF:
570 570
571 // create and store our event 571 // create and store our event
572 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2); 572 midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2);
573 573
574 /* 574 /*
575 SVDEBUG << "MIDI event for channel " << channel << " (track " 575 SVDEBUG << "MIDI event for channel " << channel << " (track "
576 << trackNum << ")" << endl; 576 << trackNum << ")" << endl;
577 midiEvent->print(); 577 midiEvent->print();
578 */ 578 */
579 579
580 580
581 m_midiComposition[trackNum].push_back(midiEvent); 581 m_midiComposition[trackNum].push_back(midiEvent);
582 582
583 if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) { 583 if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) {
584 m_percussionTracks.insert(trackNum); 584 m_percussionTracks.insert(trackNum);
585 } 585 }
586 586
587 break; 587 break;
588 588
589 case MIDI_PITCH_BEND: 589 case MIDI_PITCH_BEND:
590 data2 = getMIDIByte(); 590 data2 = getMIDIByte();
603 603
604 case MIDI_SYSTEM_EXCLUSIVE: 604 case MIDI_SYSTEM_EXCLUSIVE:
605 messageLength = getNumberFromMIDIBytes(data1); 605 messageLength = getNumberFromMIDIBytes(data1);
606 606
607 #ifdef MIDI_DEBUG 607 #ifdef MIDI_DEBUG
608 SVDEBUG << "SysEx of " << messageLength << " bytes found" << endl; 608 SVDEBUG << "SysEx of " << messageLength << " bytes found" << endl;
609 #endif 609 #endif
610 610
611 metaMessage= getMIDIBytes(messageLength); 611 metaMessage= getMIDIBytes(messageLength);
612 612
613 if (MIDIByte(metaMessage[metaMessage.length() - 1]) != 613 if (MIDIByte(metaMessage[metaMessage.length() - 1]) !=
642 } 642 }
643 } 643 }
644 } 644 }
645 645
646 if (lastTrackNum > metaTrack) { 646 if (lastTrackNum > metaTrack) {
647 for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) { 647 for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) {
648 m_trackNames[track] = QString("%1 <%2>") 648 m_trackNames[track] = QString("%1 <%2>")
649 .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1); 649 .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1);
650 } 650 }
651 } 651 }
652 652
653 return true; 653 return true;
654 } 654 }
655 655
662 { 662 {
663 bool notesOnTrack = false; 663 bool notesOnTrack = false;
664 bool noteOffFound; 664 bool noteOffFound;
665 665
666 for (MIDITrack::iterator i = m_midiComposition[track].begin(); 666 for (MIDITrack::iterator i = m_midiComposition[track].begin();
667 i != m_midiComposition[track].end(); i++) { 667 i != m_midiComposition[track].end(); i++) {
668 668
669 if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) { 669 if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) {
670 670
671 notesOnTrack = true; 671 notesOnTrack = true;
672 noteOffFound = false; 672 noteOffFound = false;
673 673
674 for (MIDITrack::iterator j = i; 674 for (MIDITrack::iterator j = i;
675 j != m_midiComposition[track].end(); j++) { 675 j != m_midiComposition[track].end(); j++) {
676 676
677 if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) && 677 if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) &&
678 ((*j)->getPitch() == (*i)->getPitch()) && 678 ((*j)->getPitch() == (*i)->getPitch()) &&
679 ((*j)->getMessageType() == MIDI_NOTE_OFF || 679 ((*j)->getMessageType() == MIDI_NOTE_OFF ||
680 ((*j)->getMessageType() == MIDI_NOTE_ON && 680 ((*j)->getMessageType() == MIDI_NOTE_ON &&
681 (*j)->getVelocity() == 0x00))) { 681 (*j)->getVelocity() == 0x00))) {
682 682
683 (*i)->setDuration((*j)->getTime() - (*i)->getTime()); 683 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
692 692
693 // If no matching NOTE OFF has been found then set 693 // If no matching NOTE OFF has been found then set
694 // Event duration to length of track 694 // Event duration to length of track
695 // 695 //
696 if (!noteOffFound) { 696 if (!noteOffFound) {
697 MIDITrack::iterator j = m_midiComposition[track].end(); 697 MIDITrack::iterator j = m_midiComposition[track].end();
698 --j; 698 --j;
699 (*i)->setDuration((*j)->getTime() - (*i)->getTime()); 699 (*i)->setDuration((*j)->getTime() - (*i)->getTime());
700 } 700 }
701 } 701 }
702 } 702 }
703 703
704 return notesOnTrack; 704 return notesOnTrack;
705 } 705 }
710 MIDIFileReader::updateTempoMap(unsigned int track) 710 MIDIFileReader::updateTempoMap(unsigned int track)
711 { 711 {
712 SVDEBUG << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl; 712 SVDEBUG << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl;
713 713
714 for (MIDITrack::iterator i = m_midiComposition[track].begin(); 714 for (MIDITrack::iterator i = m_midiComposition[track].begin();
715 i != m_midiComposition[track].end(); ++i) { 715 i != m_midiComposition[track].end(); ++i) {
716 716
717 if ((*i)->isMeta() && 717 if ((*i)->isMeta() &&
718 (*i)->getMetaEventCode() == MIDI_SET_TEMPO) { 718 (*i)->getMetaEventCode() == MIDI_SET_TEMPO) {
719 719
720 MIDIByte m0 = (*i)->getMetaMessage()[0]; 720 MIDIByte m0 = (*i)->getMetaMessage()[0];
721 MIDIByte m1 = (*i)->getMetaMessage()[1]; 721 MIDIByte m1 = (*i)->getMetaMessage()[1];
722 MIDIByte m2 = (*i)->getMetaMessage()[2]; 722 MIDIByte m2 = (*i)->getMetaMessage()[2];
723 723
724 long tempo = (((m0 << 8) + m1) << 8) + m2; 724 long tempo = (((m0 << 8) + m1) << 8) + m2;
725 725
726 SVDEBUG << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl; 726 SVDEBUG << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl;
727 727
728 if (tempo != 0) { 728 if (tempo != 0) {
729 double qpm = 60000000.0 / double(tempo); 729 double qpm = 60000000.0 / double(tempo);
730 m_tempoMap[(*i)->getTime()] = 730 m_tempoMap[(*i)->getTime()] =
731 TempoChange(RealTime::zeroTime, qpm); 731 TempoChange(RealTime::zeroTime, qpm);
732 } 732 }
733 } 733 }
734 } 734 }
735 } 735 }
736 736
737 void 737 void
742 double tempo = 120.0; 742 double tempo = 120.0;
743 int td = m_timingDivision; 743 int td = m_timingDivision;
744 if (td == 0) td = 96; 744 if (td == 0) td = 96;
745 745
746 for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) { 746 for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) {
747 747
748 unsigned long mtime = i->first; 748 unsigned long mtime = i->first;
749 unsigned long melapsed = mtime - lastMIDITime; 749 unsigned long melapsed = mtime - lastMIDITime;
750 double quarters = double(melapsed) / double(td); 750 double quarters = double(melapsed) / double(td);
751 double seconds = (60.0 * quarters) / tempo; 751 double seconds = (60.0 * quarters) / tempo;
752 752
753 RealTime t = lastRealTime + RealTime::fromSeconds(seconds); 753 RealTime t = lastRealTime + RealTime::fromSeconds(seconds);
754 754
755 i->second.first = t; 755 i->second.first = t;
756 756
757 lastRealTime = t; 757 lastRealTime = t;
758 lastMIDITime = mtime; 758 lastMIDITime = mtime;
759 tempo = i->second.second; 759 tempo = i->second.second;
760 } 760 }
761 } 761 }
762 762
763 RealTime 763 RealTime
764 MIDIFileReader::getTimeForMIDITime(unsigned long midiTime) const 764 MIDIFileReader::getTimeForMIDITime(unsigned long midiTime) const
767 RealTime tempoRealTime = RealTime::zeroTime; 767 RealTime tempoRealTime = RealTime::zeroTime;
768 double tempo = 120.0; 768 double tempo = 120.0;
769 769
770 TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime); 770 TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime);
771 if (i != m_tempoMap.begin()) { 771 if (i != m_tempoMap.begin()) {
772 --i; 772 --i;
773 tempoMIDITime = i->first; 773 tempoMIDITime = i->first;
774 tempoRealTime = i->second.first; 774 tempoRealTime = i->second.first;
775 tempo = i->second.second; 775 tempo = i->second.second;
776 } 776 }
777 777
778 int td = m_timingDivision; 778 int td = m_timingDivision;
779 if (td == 0) td = 96; 779 if (td == 0) td = 96;
780 780
782 double quarters = double(melapsed) / double(td); 782 double quarters = double(melapsed) / double(td);
783 double seconds = (60.0 * quarters) / tempo; 783 double seconds = (60.0 * quarters) / tempo;
784 784
785 /* 785 /*
786 SVDEBUG << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")" 786 SVDEBUG << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")"
787 << endl; 787 << endl;
788 SVDEBUG << "timing division = " << td << endl; 788 SVDEBUG << "timing division = " << td << endl;
789 SVDEBUG << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " (" 789 SVDEBUG << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " ("
790 << tempoRealTime << ")" << endl; 790 << tempoRealTime << ")" << endl;
791 SVDEBUG << "quarters since then = " << quarters << endl; 791 SVDEBUG << "quarters since then = " << quarters << endl;
792 SVDEBUG << "tempo = " << tempo << " quarters per minute" << endl; 792 SVDEBUG << "tempo = " << tempo << " quarters per minute" << endl;
793 SVDEBUG << "seconds since then = " << seconds << endl; 793 SVDEBUG << "seconds since then = " << seconds << endl;
794 SVDEBUG << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << endl; 794 SVDEBUG << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << endl;
795 */ 795 */
805 if (m_loadableTracks.empty()) { 805 if (m_loadableTracks.empty()) {
806 if (m_acquirer) { 806 if (m_acquirer) {
807 m_acquirer->showError 807 m_acquirer->showError
808 (tr("MIDI file \"%1\" has no notes in any track").arg(m_path)); 808 (tr("MIDI file \"%1\" has no notes in any track").arg(m_path));
809 } 809 }
810 return 0; 810 return 0;
811 } 811 }
812 812
813 std::set<unsigned int> tracksToLoad; 813 std::set<unsigned int> tracksToLoad;
814 814
815 if (m_loadableTracks.size() == 1) { 815 if (m_loadableTracks.size() == 1) {
816 816
817 tracksToLoad.insert(*m_loadableTracks.begin()); 817 tracksToLoad.insert(*m_loadableTracks.begin());
818 818
819 } else { 819 } else {
820 820
821 QStringList displayNames; 821 QStringList displayNames;
822 822
823 for (set<unsigned int>::iterator i = m_loadableTracks.begin(); 823 for (set<unsigned int>::iterator i = m_loadableTracks.begin();
824 i != m_loadableTracks.end(); ++i) { 824 i != m_loadableTracks.end(); ++i) {
825 825
826 unsigned int trackNo = *i; 826 unsigned int trackNo = *i;
827 QString label; 827 QString label;
828 828
829 QString perc; 829 QString perc;
830 if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) { 830 if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) {
831 perc = tr(" - uses GM percussion channel"); 831 perc = tr(" - uses GM percussion channel");
832 } 832 }
833 833
834 if (m_trackNames.find(trackNo) != m_trackNames.end()) { 834 if (m_trackNames.find(trackNo) != m_trackNames.end()) {
835 label = tr("Track %1 (%2)%3") 835 label = tr("Track %1 (%2)%3")
836 .arg(trackNo).arg(m_trackNames.find(trackNo)->second) 836 .arg(trackNo).arg(m_trackNames.find(trackNo)->second)
837 .arg(perc); 837 .arg(perc);
838 } else { 838 } else {
839 label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc); 839 label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc);
840 } 840 }
841 841
842 displayNames << label; 842 displayNames << label;
843 } 843 }
844 844
845 QString singleTrack; 845 QString singleTrack;
846 846
847 bool haveSomePercussion = 847 bool haveSomePercussion =
848 (!m_percussionTracks.empty() && 848 (!m_percussionTracks.empty() &&
864 pref == MIDIFileImportPreferenceAcquirer::MergeAllNonPercussionTracks) { 864 pref == MIDIFileImportPreferenceAcquirer::MergeAllNonPercussionTracks) {
865 865
866 for (set<unsigned int>::iterator i = m_loadableTracks.begin(); 866 for (set<unsigned int>::iterator i = m_loadableTracks.begin();
867 i != m_loadableTracks.end(); ++i) { 867 i != m_loadableTracks.end(); ++i) {
868 868
869 if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks || 869 if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks ||
870 m_percussionTracks.find(*i) == m_percussionTracks.end()) { 870 m_percussionTracks.find(*i) == m_percussionTracks.end()) {
871 871
872 tracksToLoad.insert(*i); 872 tracksToLoad.insert(*i);
873 } 873 }
874 } 874 }
875 875
876 } else { 876 } else {
877 877
878 int j = 0; 878 int j = 0;
879 879
880 for (set<unsigned int>::iterator i = m_loadableTracks.begin(); 880 for (set<unsigned int>::iterator i = m_loadableTracks.begin();
881 i != m_loadableTracks.end(); ++i) { 881 i != m_loadableTracks.end(); ++i) {
882 882
883 if (singleTrack == displayNames[j]) { 883 if (singleTrack == displayNames[j]) {
884 tracksToLoad.insert(*i); 884 tracksToLoad.insert(*i);
885 break; 885 break;
886 } 886 }
887 887
888 ++j; 888 ++j;
889 } 889 }
890 } 890 }
891 } 891 }
892 892
893 if (tracksToLoad.empty()) return 0; 893 if (tracksToLoad.empty()) return 0;
894 894
895 int n = int(tracksToLoad.size()), count = 0; 895 int n = int(tracksToLoad.size()), count = 0;
896 Model *model = 0; 896 Model *model = 0;
897 897
898 for (std::set<unsigned int>::iterator i = tracksToLoad.begin(); 898 for (std::set<unsigned int>::iterator i = tracksToLoad.begin();
899 i != tracksToLoad.end(); ++i) { 899 i != tracksToLoad.end(); ++i) {
900 900
901 int minProgress = (100 * count) / n; 901 int minProgress = (100 * count) / n;
902 int progressAmount = 100 / n; 902 int progressAmount = 100 / n;
903 903
904 model = loadTrack(*i, model, minProgress, progressAmount); 904 model = loadTrack(*i, model, minProgress, progressAmount);
905 905
906 ++count; 906 ++count;
907 } 907 }
908 908
909 if (dynamic_cast<NoteModel *>(model)) { 909 if (dynamic_cast<NoteModel *>(model)) {
910 dynamic_cast<NoteModel *>(model)->setCompletion(100); 910 dynamic_cast<NoteModel *>(model)->setCompletion(100);
911 } 911 }
912 912
913 return model; 913 return model;
914 } 914 }
915 915
916 Model * 916 Model *
917 MIDIFileReader::loadTrack(unsigned int trackToLoad, 917 MIDIFileReader::loadTrack(unsigned int trackToLoad,
918 Model *existingModel, 918 Model *existingModel,
919 int minProgress, 919 int minProgress,
920 int progressAmount) const 920 int progressAmount) const
921 { 921 {
922 if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) { 922 if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) {
923 return 0; 923 return 0;
924 } 924 }
925 925
926 NoteModel *model = 0; 926 NoteModel *model = 0;
927 927
928 if (existingModel) { 928 if (existingModel) {
929 model = dynamic_cast<NoteModel *>(existingModel); 929 model = dynamic_cast<NoteModel *>(existingModel);
930 if (!model) { 930 if (!model) {
931 SVDEBUG << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl; 931 SVDEBUG << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl;
932 } 932 }
933 } 933 }
934 934
935 if (!model) { 935 if (!model) {
936 model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false); 936 model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false);
937 model->setValueQuantization(1.0); 937 model->setValueQuantization(1.0);
938 model->setObjectName(QFileInfo(m_path).fileName()); 938 model->setObjectName(QFileInfo(m_path).fileName());
939 } 939 }
940 940
941 const MIDITrack &track = m_midiComposition.find(trackToLoad)->second; 941 const MIDITrack &track = m_midiComposition.find(trackToLoad)->second;
942 942
954 rt = RealTime::frame2RealTime(midiTime, m_fps * m_subframes); 954 rt = RealTime::frame2RealTime(midiTime, m_fps * m_subframes);
955 } else { 955 } else {
956 rt = getTimeForMIDITime(midiTime); 956 rt = getTimeForMIDITime(midiTime);
957 } 957 }
958 958
959 // We ignore most of these event types for now, though in 959 // We ignore most of these event types for now, though in
960 // theory some of the text ones could usefully be incorporated 960 // theory some of the text ones could usefully be incorporated
961 961
962 if ((*i)->isMeta()) { 962 if ((*i)->isMeta()) {
963 963
964 switch((*i)->getMetaEventCode()) { 964 switch((*i)->getMetaEventCode()) {
965 965
966 case MIDI_KEY_SIGNATURE: 966 case MIDI_KEY_SIGNATURE:
967 // minorKey = (int((*i)->getMetaMessage()[1]) != 0); 967 // minorKey = (int((*i)->getMetaMessage()[1]) != 0);
968 sharpKey = (int((*i)->getMetaMessage()[0]) >= 0); 968 sharpKey = (int((*i)->getMetaMessage()[0]) >= 0);
969 break; 969 break;
970 970
971 case MIDI_TEXT_EVENT: 971 case MIDI_TEXT_EVENT:
972 case MIDI_LYRIC: 972 case MIDI_LYRIC:
973 case MIDI_TEXT_MARKER: 973 case MIDI_TEXT_MARKER:
974 case MIDI_COPYRIGHT_NOTICE: 974 case MIDI_COPYRIGHT_NOTICE:
975 case MIDI_TRACK_NAME: 975 case MIDI_TRACK_NAME:
976 // The text events that we could potentially use 976 // The text events that we could potentially use
977 break; 977 break;
978 978
979 case MIDI_SET_TEMPO: 979 case MIDI_SET_TEMPO:
980 // Already dealt with in a separate pass previously 980 // Already dealt with in a separate pass previously
981 break; 981 break;
982 982
983 case MIDI_TIME_SIGNATURE: 983 case MIDI_TIME_SIGNATURE:
984 // Not yet! 984 // Not yet!
985 break; 985 break;
986 986
987 case MIDI_SEQUENCE_NUMBER: 987 case MIDI_SEQUENCE_NUMBER:
988 case MIDI_CHANNEL_PREFIX_OR_PORT: 988 case MIDI_CHANNEL_PREFIX_OR_PORT:
989 case MIDI_INSTRUMENT_NAME: 989 case MIDI_INSTRUMENT_NAME:
990 case MIDI_CUE_POINT: 990 case MIDI_CUE_POINT:
991 case MIDI_CHANNEL_PREFIX: 991 case MIDI_CHANNEL_PREFIX:
992 case MIDI_SEQUENCER_SPECIFIC: 992 case MIDI_SEQUENCER_SPECIFIC:
993 case MIDI_SMPTE_OFFSET: 993 case MIDI_SMPTE_OFFSET:
994 default: 994 default:
995 break; 995 break;
996 } 996 }
997 997
998 } else { 998 } else {
999 999
1000 switch ((*i)->getMessageType()) { 1000 switch ((*i)->getMessageType()) {
1001 1001
1002 case MIDI_NOTE_ON: 1002 case MIDI_NOTE_ON:
1003 1003
1004 if ((*i)->getVelocity() == 0) break; // effective note-off 1004 if ((*i)->getVelocity() == 0) break; // effective note-off
1005 else { 1005 else {
1006 RealTime endRT; 1006 RealTime endRT;
1007 unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration(); 1007 unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration();
1008 if (m_smpte) { 1008 if (m_smpte) {
1009 endRT = RealTime::frame2RealTime(endMidiTime, m_fps * m_subframes); 1009 endRT = RealTime::frame2RealTime(endMidiTime, m_fps * m_subframes);
1010 } else { 1010 } else {
1011 endRT = getTimeForMIDITime(endMidiTime); 1011 endRT = getTimeForMIDITime(endMidiTime);
1012 } 1012 }
1013 1013
1014 long startFrame = RealTime::realTime2Frame 1014 long startFrame = RealTime::realTime2Frame
1015 (rt, model->getSampleRate()); 1015 (rt, model->getSampleRate());
1016 1016
1017 long endFrame = RealTime::realTime2Frame 1017 long endFrame = RealTime::realTime2Frame
1018 (endRT, model->getSampleRate()); 1018 (endRT, model->getSampleRate());
1019 1019
1020 QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(), 1020 QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(),
1021 0, 1021 0,
1022 !sharpKey); 1022 !sharpKey);
1023 1023
1024 QString noteLabel = tr("%1 - vel %2") 1024 QString noteLabel = tr("%1 - vel %2")
1025 .arg(pitchLabel).arg(int((*i)->getVelocity())); 1025 .arg(pitchLabel).arg(int((*i)->getVelocity()));
1026 1026
1027 float level = float((*i)->getVelocity()) / 128.f; 1027 float level = float((*i)->getVelocity()) / 128.f;
1028 1028
1029 Note note(startFrame, (*i)->getPitch(), 1029 Note note(startFrame, (*i)->getPitch(),
1030 endFrame - startFrame, level, noteLabel); 1030 endFrame - startFrame, level, noteLabel);
1031 1031
1032 // SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl; 1032 // SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl;
1033 1033
1034 model->addPoint(note); 1034 model->addPoint(note);
1035 break; 1035 break;
1036 } 1036 }
1037 1037
1038 case MIDI_PITCH_BEND: 1038 case MIDI_PITCH_BEND:
1039 // I guess we could make some use of this... 1039 // I guess we could make some use of this...
1040 break; 1040 break;
1041 1041
1042 case MIDI_NOTE_OFF: 1042 case MIDI_NOTE_OFF:
1043 case MIDI_PROG_CHANGE: 1043 case MIDI_PROG_CHANGE:
1044 case MIDI_CTRL_CHANGE: 1044 case MIDI_CTRL_CHANGE:
1048 break; 1048 break;
1049 1049
1050 default: 1050 default:
1051 break; 1051 break;
1052 } 1052 }
1053 } 1053 }
1054 1054
1055 model->setCompletion(minProgress + 1055 model->setCompletion(minProgress +
1056 (count * progressAmount) / totalEvents); 1056 (count * progressAmount) / totalEvents);
1057 ++count; 1057 ++count;
1058 } 1058 }
1059 1059
1060 return model; 1060 return model;
1061 } 1061 }
1062 1062