// chord |<--fudge time-->| // ------x-------------------------------|----------------|---------------------|------ // |<-----------------thresh time------------------>|<--thresh ext time-->| // void collectChords( MTrack &track, const ReducedFraction &humanTolCoeff, const ReducedFraction &nonHumanTolCoeff) { auto &chords = track.chords; if (chords.empty()) return; Q_ASSERT_X(areNotesLongEnough(chords), "MChord::collectChords", "There are too short notes"); const auto &opers = preferences.midiImportOperations.data()->trackOpers; const auto minAllowedDur = minAllowedDuration(); const auto threshTime = (opers.isHumanPerformance.value()) ? minAllowedDur * humanTolCoeff : minAllowedDur * nonHumanTolCoeff; const auto fudgeTime = threshTime / 4; const auto threshExtTime = threshTime / 2; ReducedFraction currentChordStart; ReducedFraction curThreshTime; // if note onTime goes after max chord offTime // then this is not a chord but arpeggio ReducedFraction maxOffTime; setToNegative(currentChordStart, curThreshTime, maxOffTime); // invalidate for (auto it = chords.begin(); it != chords.end(); ) { if (it->second.isInTuplet) { setToNegative(currentChordStart, curThreshTime, maxOffTime); ++it; continue; } const auto maxNoteOffTime = MChord::maxNoteOffTime(it->second.notes); if (it->first < currentChordStart + curThreshTime) { // this branch should not be executed when it == chords.begin() Q_ASSERT_X(it != chords.begin(), "MChord: collectChords", "it == chords.begin()"); if (it->first <= maxOffTime - minAllowedDur) { // add current note to the previous chord auto chordAddTo = std::prev(it); if (it->second.voice != chordAddTo->second.voice) { setToNegative(currentChordStart, curThreshTime, maxOffTime); ++it; continue; } if (!hasNotesWithEqualPitch(chordAddTo->second, it->second)) { for (const auto ¬e: it->second.notes) chordAddTo->second.notes.push_back(note); if (maxNoteOffTime > maxOffTime) maxOffTime = maxNoteOffTime; } if (it->first >= currentChordStart + curThreshTime - fudgeTime && curThreshTime == threshTime) { curThreshTime += threshExtTime; } it = chords.erase(it); continue; } } currentChordStart = it->first; maxOffTime = maxNoteOffTime; curThreshTime = threshTime; ++it; } Q_ASSERT_X(areOnTimeValuesDifferent(chords), "MChord: collectChords", "onTime values of chords are equal but should be different"); }
void collectChords(std::multimap<int, MTrack> &tracks) { for (auto &track: tracks) { auto &chords = track.second.chords; if (chords.empty()) continue; const auto &opers = preferences.midiImportOperations.data()->trackOpers; const auto minAllowedDur = minAllowedDuration(); const auto threshTime = (opers.isHumanPerformance.value()) ? minAllowedDur * 2 : minAllowedDur / 2; const auto fudgeTime = threshTime / 4; const auto threshExtTime = threshTime / 2; ReducedFraction currentChordStart(-1, 1); // invalid ReducedFraction curThreshTime(-1, 1); // if note onTime goes after max chord offTime // then this is not a chord but arpeggio ReducedFraction maxOffTime(-1, 1); // chords here should consist of a single note // because notes are not united into chords yet Q_ASSERT_X(areSingleNoteChords(chords), "MChord: collectChords", "Some chords have more than one note"); for (auto it = chords.begin(); it != chords.end(); ) { const auto ¬e = it->second.notes[0]; // short events with len < minAllowedDuration must be cleaned up Q_ASSERT_X(note.offTime - it->first >= minAllowedDuration(), "MChord: collectChords", "Note length is less than min allowed duration"); if (it->first < currentChordStart + curThreshTime) { // this branch should not be executed when it == chords.begin() Q_ASSERT_X(it != chords.begin(), "MChord: collectChords", "it == chords.begin()"); if (it->first <= maxOffTime - minAllowedDur) { // add current note to the previous chord auto prev = std::prev(it); bool hasNoteWithThisPitch = false; for (const auto &n: prev->second.notes) { if (n.pitch == note.pitch) { hasNoteWithThisPitch = true; break; } } if (!hasNoteWithThisPitch) { prev->second.notes.push_back(note); if (note.offTime > maxOffTime) maxOffTime = note.offTime; } if (it->first >= currentChordStart + curThreshTime - fudgeTime && curThreshTime == threshTime) { curThreshTime += threshExtTime; } it = chords.erase(it); continue; } } currentChordStart = it->first; maxOffTime = note.offTime; curThreshTime = threshTime; ++it; } Q_ASSERT_X(areOnTimeValuesDifferent(chords), "MChord: collectChords", "onTime values of chords are equal but should be different"); Q_ASSERT_X(areNotesLongEnough(chords), "MChord::collectChords", "There are too short notes"); } }