From 3d193d494b01400b60442dae76306e321c205d32 Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Mon, 16 Dec 2013 19:23:04 +0400 Subject: [PATCH 1/7] Remove unnecessary note overlapping check and add Q_ASSERT instead --- mscore/importmidi.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/mscore/importmidi.cpp b/mscore/importmidi.cpp index 0f94acb0caa3e..a55cf431f6cd6 100644 --- a/mscore/importmidi.cpp +++ b/mscore/importmidi.cpp @@ -852,6 +852,39 @@ QList getTracksMeta(const QList &tracks, return tracksMeta; } +//---------------------------------------------------------------------------------------- +// DEBUG function + +bool doNotesOverlap(const std::multimap &tracks) + { + for (const auto &track: tracks) { + const auto &chords = track.second.chords; + for (auto it = chords.begin(); it != chords.end(); ++it) { + const auto &firstChord = it->second; + const auto &firstOnTime = it->first; + for (const auto ¬e1: firstChord.notes) { + auto ii = std::next(it); + for (; ii != chords.end(); ++ii) { + const auto &secondChord = ii->second; + if (firstChord.voice != secondChord.voice) + continue; + const auto &secondOnTime = ii->first; + for (const auto ¬e2: secondChord.notes) { + if (note2.pitch != note1.pitch) + continue; + if (secondOnTime >= (firstOnTime + note1.len)) + continue; + return true; + } + } + } + } // for note1 + } + return false; + } + +//---------------------------------------------------------------------------------------- + void convertMidi(Score *score, const MidiFile *mf) { ReducedFraction lastTick; @@ -862,7 +895,10 @@ void convertMidi(Score *score, const MidiFile *mf) MChord::collectChords(tracks); MChord::removeOverlappingNotes(tracks); quantizeAllTracks(tracks, sigmap, lastTick); - MChord::removeOverlappingNotes(tracks); + + Q_ASSERT_X(!doNotesOverlap(tracks), + "convertMidi:", "There are overlapping notes of the same voice that is incorrect"); + MChord::mergeChordsWithEqualOnTimeAndVoice(tracks); LRHand::splitIntoLeftRightHands(tracks); MidiDrum::splitDrumVoices(tracks); From 5c74b6997b9377b5359027c927104724f19f4ecf Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Sat, 21 Dec 2013 03:00:20 +0400 Subject: [PATCH 2/7] Fix too many tuplet voices (> VOICES) --- mscore/importmidi_tuplet.cpp | 89 ++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/mscore/importmidi_tuplet.cpp b/mscore/importmidi_tuplet.cpp index 139cd1943c337..5ca2c60006322 100644 --- a/mscore/importmidi_tuplet.cpp +++ b/mscore/importmidi_tuplet.cpp @@ -32,6 +32,19 @@ const std::map& tupletRatios() return ratios; } +int voiceLimit() + { + const auto operations = preferences.midiImportOperations.currentTrackOperations(); + return (operations.useMultipleVoices) ? VOICES : 1; + } + +int tupletVoiceLimit() + { + const auto operations = preferences.midiImportOperations.currentTrackOperations(); + // for multiple voices: one voice is reserved for non-tuplet chords + return (operations.useMultipleVoices) ? VOICES - 1 : 1; + } + bool isTupletAllowed(const TupletInfo &tupletInfo) { // special check for duplets and triplets @@ -272,18 +285,10 @@ TupletInfo findTupletApproximation(const ReducedFraction &tupletLen, return tupletInfo; } -bool isMoreVoicesAllowed(int voicesInUse, int availableVoices) +bool isMoreTupletVoicesAllowed(int voicesInUse, int availableVoices) { - const auto operations = preferences.midiImportOperations.currentTrackOperations(); - - if (!operations.useMultipleVoices && voicesInUse >= 1) + if (voicesInUse >= availableVoices || voicesInUse >= tupletVoiceLimit()) return false; - // VOICES - 1 limit because one voice is reserved for non-tuplet chords - if (voicesInUse >= availableVoices || voicesInUse >= VOICES - 1) { - // need to choose next tuplet candidate - no more available voices - // one voice is reserved for non-tuplet chords - return false; - } return true; } @@ -330,7 +335,7 @@ bool areTupletChordsInUse( // check are first tuplet notes all in use (1 note - 1 voice) const auto ii = usedFirstTupletNotes.find(&*(i->second)); if (ii != usedFirstTupletNotes.end()) { - if (!isMoreVoicesAllowed(ii->second, i->second->second.notes.size())) + if (!isMoreTupletVoicesAllowed(ii->second, i->second->second.notes.size())) return true; } } @@ -454,7 +459,7 @@ bool validateSelectedTuplets(const std::list &bestIndexes, usedFirstTupletNotes.insert({&*tupletChord.second, 1}).first; } else { - if (!isMoreVoicesAllowed(ii->second, tupletChord.second->second.notes.size())) + if (!isMoreTupletVoicesAllowed(ii->second, tupletChord.second->second.notes.size())) return false; else ++(ii->second); // increase chord note counter @@ -1019,21 +1024,23 @@ chordInterval(const std::pair *chord, } void setTupletVoices(std::vector &tuplets, - std::map>> &intervals, + std::map>> &tupletIntervals, + const std::vector> &nonTupletIntervals, std::set &pendingTuplets, const ReducedFraction ®ularRaster) { - const auto operations = preferences.midiImportOperations.currentTrackOperations(); - const int voiceLimit = (operations.useMultipleVoices) ? VOICES : 1; + int limit = tupletVoiceLimit(); int voice = 0; - - while (!pendingTuplets.empty() && voice < voiceLimit) { + while (!pendingTuplets.empty() && voice < limit) { for (auto it = pendingTuplets.begin(); it != pendingTuplets.end(); ) { int i = *it; - auto interval = tupletInterval(tuplets[i], regularRaster); - if (!haveIntersection(interval, intervals[voice])) { + const auto interval = tupletInterval(tuplets[i], regularRaster); + if (!haveIntersection(interval, tupletIntervals[voice])) { + // forbid intersection with non-tuplets when !useMultipleVoices + if (limit == 1 && haveIntersection(interval, nonTupletIntervals)) + continue; setTupletVoice(tuplets[i].chords, voice); - intervals[voice].push_back(interval); + tupletIntervals[voice].push_back(interval); it = pendingTuplets.erase(it); continue; } @@ -1044,18 +1051,16 @@ void setTupletVoices(std::vector &tuplets, } void setNonTupletVoices(std::set *> &pendingNonTuplets, - std::map>> &intervals, + const std::map>> &intervals, const ReducedFraction ®ularRaster) { - const auto operations = preferences.midiImportOperations.currentTrackOperations(); - const int voiceLimit = (operations.useMultipleVoices) ? VOICES : 1; int voice = 0; - - while (!pendingNonTuplets.empty() && voice < voiceLimit) { + while (!pendingNonTuplets.empty() && voice < voiceLimit()) { for (auto it = pendingNonTuplets.begin(); it != pendingNonTuplets.end(); ) { auto chord = *it; - auto interval = chordInterval(chord, regularRaster); - if (!haveIntersection(interval, intervals[voice])) { + const auto interval = chordInterval(chord, regularRaster); + const auto fit = intervals.find(voice); + if (fit == intervals.end() || !haveIntersection(interval, fit->second)) { chord->second.voice = voice; it = pendingNonTuplets.erase(it); // don't insert chord interval here @@ -1078,7 +1083,7 @@ void setTiedTupletVoice(std::vector &tuplets, if (&*chord.second == firstTiedTuplet.chord) { setTupletVoice(tuplets[i].chords, firstTiedTuplet.voice); pendingTuplets.erase(i); - auto interval = tupletInterval(tuplets[i], regularRaster); + const auto interval = tupletInterval(tuplets[i], regularRaster); intervals[firstTiedTuplet.voice].push_back(interval); i = tuplets.size() - 1; break; @@ -1181,6 +1186,17 @@ bool doTupletsHaveCommonChords(const std::vector &tuplets) //---------------------------------------------------------------------------------------- +std::vector > +findNonTupletIntervals(const ReducedFraction ®ularRaster, + const std::list::iterator> &nonTuplets) + { + std::vector> nonTupletIntervals; + for (const auto &nonTuplet: nonTuplets) + nonTupletIntervals.push_back(chordInterval(&*nonTuplet, regularRaster)); + + return nonTupletIntervals; + } + void assignVoices(std::multimap &chords, std::vector &tuplets, std::list::iterator> &nonTuplets, @@ -1189,7 +1205,8 @@ void assignVoices(std::multimap &chords, const ReducedFraction ®ularRaster) { // - std::map>> intervals; + std::map>> tupletIntervals; + std::set pendingTuplets; // tuplet indexes for (int i = 0; i != (int)tuplets.size(); ++i) pendingTuplets.insert(i); @@ -1203,19 +1220,23 @@ void assignVoices(std::multimap &chords, setTupletVoice(tuplets[t.tupletIndex].chords, t.voice); pendingTuplets.erase(t.tupletIndex); auto interval = tupletInterval(tuplets[t.tupletIndex], regularRaster); - intervals[t.voice].push_back(interval); + tupletIntervals[t.voice].push_back(interval); if (t.chord->first >= startBarTick) { t.chord->second.voice = t.voice; - intervals[t.voice].push_back(chordInterval(t.chord, regularRaster)); + // add interval of tied chord to tuplet intervals + tupletIntervals[t.voice].push_back(chordInterval(t.chord, regularRaster)); if (pendingNonTuplets.find(t.chord) == pendingNonTuplets.end()) - setTiedTupletVoice(tuplets, intervals, pendingTuplets, t, regularRaster); + setTiedTupletVoice(tuplets, tupletIntervals, pendingTuplets, t, regularRaster); else pendingNonTuplets.erase(t.chord); } } - setTupletVoices(tuplets, intervals, pendingTuplets, regularRaster); - setNonTupletVoices(pendingNonTuplets, intervals, regularRaster); + auto nonTupletIntervals = findNonTupletIntervals(regularRaster, nonTuplets); + + setTupletVoices(tuplets, tupletIntervals, nonTupletIntervals, + pendingTuplets, regularRaster); + setNonTupletVoices(pendingNonTuplets, tupletIntervals, regularRaster); removeUnusedTuplets(tuplets, pendingTuplets); Q_ASSERT_X(!haveTupletsEmptyChords(tuplets), From fdb231e5c01cf97b5b8ca0dc8a401b5b509617ec Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Sat, 28 Dec 2013 20:17:11 +0400 Subject: [PATCH 3/7] Fix multiple voices for drum tracks --- mscore/importmidi_operations.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mscore/importmidi_operations.cpp b/mscore/importmidi_operations.cpp index 6347e4124c9a6..21a968cd6eedd 100644 --- a/mscore/importmidi_operations.cpp +++ b/mscore/importmidi_operations.cpp @@ -55,11 +55,14 @@ QString MidiImportOperations::charset() const void MidiImportOperations::adaptForPercussion(int trackIndex, bool isDrumTrack) { // small hack: don't use multiple voices for tuplets in percussion tracks - if (isValidIndex(trackIndex)) - operations_[trackIndex].useMultipleVoices = !isDrumTrack; - else + if (isValidIndex(trackIndex)) { + if (isDrumTrack) + operations_[trackIndex].useMultipleVoices = false; + } + else { defaultOpers.useMultipleVoices = isDrumTrack ? false : TrackOperations().useMultipleVoices; + } } void MidiImportOperations::addTrackLyrics(const std::multimap &trackLyrics) From ec025a45c8ec3b93c6ebf273ce021788169bfdaf Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Mon, 30 Dec 2013 01:01:57 +0400 Subject: [PATCH 4/7] Fix !useMultipleVoices case for tied tuplets --- mscore/importmidi_tuplet.cpp | 321 ++++++++++++++++++++++++++--------- 1 file changed, 241 insertions(+), 80 deletions(-) diff --git a/mscore/importmidi_tuplet.cpp b/mscore/importmidi_tuplet.cpp index 5ca2c60006322..1272b092803ec 100644 --- a/mscore/importmidi_tuplet.cpp +++ b/mscore/importmidi_tuplet.cpp @@ -805,11 +805,17 @@ void splitFirstTupletChords(std::vector &tuplets, } } +bool haveIntersection(const std::pair &interval1, + const std::pair &interval2) + { + return interval1.second > interval2.first && interval1.first < interval2.second; + } + bool haveIntersection(const std::pair &interval, const std::vector> &intervals) { for (const auto &i: intervals) { - if (i.second > interval.first && i.first < interval.second) + if (haveIntersection(i, interval)) return true; } return false; @@ -852,12 +858,12 @@ struct TiedTuplet { int tupletIndex; int voice; - std::pair *chord; + std::pair *chord; // chord the tuplet is tied with }; std::vector findBackTiedTuplets(const std::multimap &chords, - std::vector &tuplets, + const std::vector &tuplets, const ReducedFraction &prevBarStart) { std::vector tiedTuplets; @@ -1024,9 +1030,8 @@ chordInterval(const std::pair *chord, } void setTupletVoices(std::vector &tuplets, - std::map>> &tupletIntervals, - const std::vector> &nonTupletIntervals, std::set &pendingTuplets, + std::map>> &tupletIntervals, const ReducedFraction ®ularRaster) { int limit = tupletVoiceLimit(); @@ -1036,9 +1041,6 @@ void setTupletVoices(std::vector &tuplets, int i = *it; const auto interval = tupletInterval(tuplets[i], regularRaster); if (!haveIntersection(interval, tupletIntervals[voice])) { - // forbid intersection with non-tuplets when !useMultipleVoices - if (limit == 1 && haveIntersection(interval, nonTupletIntervals)) - continue; setTupletVoice(tuplets[i].chords, voice); tupletIntervals[voice].push_back(interval); it = pendingTuplets.erase(it); @@ -1051,16 +1053,17 @@ void setTupletVoices(std::vector &tuplets, } void setNonTupletVoices(std::set *> &pendingNonTuplets, - const std::map>> &intervals, + const std::map>> &tupletIntervals, const ReducedFraction ®ularRaster) { + const int limit = voiceLimit(); int voice = 0; - while (!pendingNonTuplets.empty() && voice < voiceLimit()) { + while (!pendingNonTuplets.empty() && voice < limit) { for (auto it = pendingNonTuplets.begin(); it != pendingNonTuplets.end(); ) { auto chord = *it; const auto interval = chordInterval(chord, regularRaster); - const auto fit = intervals.find(voice); - if (fit == intervals.end() || !haveIntersection(interval, fit->second)) { + const auto fit = tupletIntervals.find(voice); + if (fit == tupletIntervals.end() || !haveIntersection(interval, fit->second)) { chord->second.voice = voice; it = pendingNonTuplets.erase(it); // don't insert chord interval here @@ -1072,37 +1075,28 @@ void setNonTupletVoices(std::set *> } } -void setTiedTupletVoice(std::vector &tuplets, - std::map>> &intervals, - std::set &pendingTuplets, - const TiedTuplet &firstTiedTuplet, - const ReducedFraction ®ularRaster) +void removeUnusedTuplets(std::vector &tuplets, + std::list::iterator> &nonTuplets, + std::set &pendingTuplets, + std::set *> &pendingNonTuplets) { + if (pendingTuplets.empty()) + return; + + std::vector newTuplets; for (int i = 0; i != (int)tuplets.size(); ++i) { - for (const auto &chord: tuplets[i].chords) { - if (&*chord.second == firstTiedTuplet.chord) { - setTupletVoice(tuplets[i].chords, firstTiedTuplet.voice); - pendingTuplets.erase(i); - const auto interval = tupletInterval(tuplets[i], regularRaster); - intervals[firstTiedTuplet.voice].push_back(interval); - i = tuplets.size() - 1; - break; - } + if (pendingTuplets.find(i) == pendingTuplets.end()) { + newTuplets.push_back(tuplets[i]); } - } - } - -void removeUnusedTuplets(std::vector &tuplets, - const std::set &pendingTuplets) - { - if (!pendingTuplets.empty()) { - std::vector newTuplets; - for (int i = 0; i != (int)tuplets.size(); ++i) { - if (pendingTuplets.find(i) == pendingTuplets.end()) - newTuplets.push_back(tuplets[i]); + else { + for (const auto &chord: tuplets[i].chords) { + nonTuplets.push_back(chord.second); + pendingNonTuplets.insert(&*chord.second); + } } - std::swap(tuplets, newTuplets); } + pendingTuplets.clear(); + std::swap(tuplets, newTuplets); } //---------------------------------------------------------------------------------------- @@ -1133,10 +1127,10 @@ bool doTupletChordsHaveSameVoice(const std::vector &tuplets) // back tied tuplets are not checked here -bool areTupletVoicesOk(const std::list::iterator> &nonTuplets, - const std::vector &tuplets, - const ReducedFraction ®ularRaster, - const std::vector &tiedTuplets) +bool haveOverlappingVoices(const std::list::iterator> &nonTuplets, + const std::vector &tuplets, + const ReducedFraction ®ularRaster, + const std::vector &backTiedTuplets = std::vector()) { // std::map>> intervals; @@ -1145,7 +1139,7 @@ bool areTupletVoicesOk(const std::list const int voice = tuplet.chords.begin()->second->second.voice; const auto interval = std::make_pair(tuplet.onTime, tuplet.onTime + tuplet.len); if (haveIntersection(interval, intervals[voice])) - return false; + return true; else intervals[voice].push_back(interval); } @@ -1155,18 +1149,18 @@ bool areTupletVoicesOk(const std::list const auto interval = chordInterval(&*chord, regularRaster); if (haveIntersection(interval, intervals[voice])) { bool flag = false; // if chord is tied then it can intersect tuplet - for (const TiedTuplet &tiedTuplet: tiedTuplets) { + for (const TiedTuplet &tiedTuplet: backTiedTuplets) { if (tiedTuplet.chord == (&*chord) && tiedTuplet.voice == voice) { flag = true; break; } } if (!flag) - return false; + return true; } } - return true; + return false; } bool doTupletsHaveCommonChords(const std::vector &tuplets) @@ -1187,16 +1181,155 @@ bool doTupletsHaveCommonChords(const std::vector &tuplets) //---------------------------------------------------------------------------------------- std::vector > -findNonTupletIntervals(const ReducedFraction ®ularRaster, - const std::list::iterator> &nonTuplets) +findNonTupletIntervals(const std::list::iterator> &nonTuplets, + const ReducedFraction ®ularRaster) { std::vector> nonTupletIntervals; - for (const auto &nonTuplet: nonTuplets) + for (const auto &nonTuplet: nonTuplets) { nonTupletIntervals.push_back(chordInterval(&*nonTuplet, regularRaster)); - + } return nonTupletIntervals; } +std::set findPendingTuplets(const std::vector &tuplets) + { + std::set pendingTuplets; // tuplet indexes + for (int i = 0; i != (int)tuplets.size(); ++i) { + pendingTuplets.insert(i); + } + return pendingTuplets; + } + +std::set *> +findPendingNonTuplets(const std::list::iterator> &nonTuplets) + { + std::set *> pendingNonTuplets; + for (const auto &c: nonTuplets) { + pendingNonTuplets.insert(&*c); + } + return pendingNonTuplets; + } + +int findTupletWithChord(const MidiChord &midiChord, + const std::vector &tuplets) + { + for (int i = 0; i != (int)tuplets.size(); ++i) { + for (const auto &chord: tuplets[i].chords) { + if (&(chord.second->second) == &midiChord) + return i; + } + } + return -1; + } + +std::vector > +findForTiedTuplets(const std::vector &tuplets, + const std::vector &tiedTuplets, + const std::set *> &pendingNonTuplets, + const ReducedFraction &startBarTick) + { + std::vector> forTiedTuplets; // + + for (const TiedTuplet &tuplet: tiedTuplets) { + if (tuplet.chord->first < startBarTick) + continue; // only for chords in the current bar + if (pendingNonTuplets.find(tuplet.chord) == pendingNonTuplets.end()) { + const int i = findTupletWithChord(tuplet.chord->second, tuplets); + if (i != -1) + forTiedTuplets.push_back({i, tuplet.voice}); + } + } + return forTiedTuplets; + } + + +//---------------------------------------------------------------------------------------- +// DEBUG functions + +bool areAllElementsUnique(const std::list::iterator> &nonTuplets) + { + std::set *> chords; + for (const auto &chord: nonTuplets) { + if (chords.find(&*chord) == chords.end()) + chords.insert(&*chord); + else + return false; + } + return true; + } + +size_t chordCount(const std::vector &tuplets, + const std::list::iterator> &nonTuplets) + { + size_t sum = nonTuplets.size(); + for (const auto &tuplet: tuplets) { + sum += tuplet.chords.size(); + } + return sum; + } + +//---------------------------------------------------------------------------------------- + + +// for the case !useMultipleVoices + +void excludeExtraVoiceTuplets( + std::vector &tuplets, + std::list::iterator> &nonTuplets, + const ReducedFraction ®ularRaster) + { + // remove overlapping tuplets + size_t sz = tuplets.size(); + if (sz == 0) + return; + while (true) { + bool change = false; + for (size_t i = 0; i < sz - 1; ++i) { + const auto interval1 = tupletInterval(tuplets[i], regularRaster); + for (size_t j = i + 1; j < sz; ++j) { + const auto interval2 = tupletInterval(tuplets[j], regularRaster); + if (haveIntersection(interval1, interval2)) { + --sz; + if (j < sz) + tuplets[j] = tuplets[sz]; + --sz; + if (i < sz) + tuplets[i] = tuplets[sz]; + change = true; + break; + } + } + if (change) + break; + } + if (!change || sz == 0) + break; + } + + if (sz > 0) { // remove tuplets that are overlapped with non-tuplets + const auto nonTupletIntervals = findNonTupletIntervals(nonTuplets, regularRaster); + + for (size_t i = 0; i < sz; ) { + const auto interval = tupletInterval(tuplets[i], regularRaster); + if (haveIntersection(interval, nonTupletIntervals)) { + for (const auto &chord: tuplets[i].chords) + nonTuplets.push_back(chord.second); + --sz; + if (i < sz) { + tuplets[i] = tuplets[sz]; + continue; + } + } + ++i; + } + } + + Q_ASSERT_X(areAllElementsUnique(nonTuplets), + "MIDI tuplets: excludeExtraVoiceTuplets", "non unique chords in non-tuplets"); + + tuplets.resize(sz); + } + void assignVoices(std::multimap &chords, std::vector &tuplets, std::list::iterator> &nonTuplets, @@ -1204,47 +1337,66 @@ void assignVoices(std::multimap &chords, const ReducedFraction &endBarTick, const ReducedFraction ®ularRaster) { - // +#ifdef QT_DEBUG + size_t oldChordCount = chordCount(tuplets, nonTuplets); +#endif + Q_ASSERT_X(!haveTupletsEmptyChords(tuplets), + "MIDI tuplets: assignVoices", "Empty tuplet chords"); + + auto pendingTuplets = findPendingTuplets(tuplets); + auto pendingNonTuplets = findPendingNonTuplets(nonTuplets); + const auto prevBarStart = findPrevBarStart(startBarTick, endBarTick - startBarTick); + const auto backTiedTuplets = findBackTiedTuplets(chords, tuplets, prevBarStart); + const auto forTiedTuplets = findForTiedTuplets(tuplets, backTiedTuplets, pendingNonTuplets, + startBarTick); std::map>> tupletIntervals; - std::set pendingTuplets; // tuplet indexes - for (int i = 0; i != (int)tuplets.size(); ++i) - pendingTuplets.insert(i); - std::set *> pendingNonTuplets; - for (const auto &c: nonTuplets) - pendingNonTuplets.insert(&*c); - auto prevBarStart = findPrevBarStart(startBarTick, endBarTick - startBarTick); - - auto tiedTuplets = findBackTiedTuplets(chords, tuplets, prevBarStart); - for (const TiedTuplet &t: tiedTuplets) { - setTupletVoice(tuplets[t.tupletIndex].chords, t.voice); - pendingTuplets.erase(t.tupletIndex); - auto interval = tupletInterval(tuplets[t.tupletIndex], regularRaster); - tupletIntervals[t.voice].push_back(interval); - if (t.chord->first >= startBarTick) { - t.chord->second.voice = t.voice; - // add interval of tied chord to tuplet intervals - tupletIntervals[t.voice].push_back(chordInterval(t.chord, regularRaster)); - if (pendingNonTuplets.find(t.chord) == pendingNonTuplets.end()) - setTiedTupletVoice(tuplets, tupletIntervals, pendingTuplets, t, regularRaster); - else - pendingNonTuplets.erase(t.chord); - } + for (const auto &t: forTiedTuplets) { + const int i = t.first; + const int voice = t.second; + setTupletVoice(tuplets[i].chords, voice); + // remove tuplets with already set voices + pendingTuplets.erase(i); + tupletIntervals[voice].push_back(tupletInterval(tuplets[i], regularRaster)); + } + + for (const TiedTuplet &tuplet: backTiedTuplets) { + setTupletVoice(tuplets[tuplet.tupletIndex].chords, tuplet.voice); + pendingTuplets.erase(tuplet.tupletIndex); + tupletIntervals[tuplet.voice].push_back( + tupletInterval(tuplets[tuplet.tupletIndex], regularRaster)); + // set for-tied chords + // some chords can be the same as in forTiedTuplets + if (tuplet.chord->first < startBarTick) + continue; // only for chords in the current bar + tuplet.chord->second.voice = tuplet.voice; + // remove chords with already set voices + pendingNonTuplets.erase(tuplet.chord); } - auto nonTupletIntervals = findNonTupletIntervals(regularRaster, nonTuplets); + { + setTupletVoices(tuplets, pendingTuplets, tupletIntervals, regularRaster); + + Q_ASSERT_X((voiceLimit() == 1) ? pendingTuplets.empty() : true, + "MIDI tuplets: assignVoices", "Unused tuplets for the case !useMultipleVoices"); - setTupletVoices(tuplets, tupletIntervals, nonTupletIntervals, - pendingTuplets, regularRaster); + removeUnusedTuplets(tuplets, nonTuplets, pendingTuplets, pendingNonTuplets); setNonTupletVoices(pendingNonTuplets, tupletIntervals, regularRaster); - removeUnusedTuplets(tuplets, pendingTuplets); + } + Q_ASSERT_X(pendingNonTuplets.empty(), + "MIDI tuplets: assignVoices", "Unused non-tuplets"); Q_ASSERT_X(!haveTupletsEmptyChords(tuplets), "MIDI tuplets: assignVoices", "Empty tuplet chords"); Q_ASSERT_X(doTupletChordsHaveSameVoice(tuplets), "MIDI tuplets: assignVoices", "Tuplet chords have different voices"); - Q_ASSERT_X(areTupletVoicesOk(nonTuplets, tuplets, regularRaster, tiedTuplets), + Q_ASSERT_X(!haveOverlappingVoices(nonTuplets, tuplets, regularRaster, backTiedTuplets), "MIDI tuplets: assignVoices", "Overlapping tuplets of the same voice"); +#ifdef QT_DEBUG + size_t newChordCount = chordCount(tuplets, nonTuplets); +#endif + Q_ASSERT_X(oldChordCount == newChordCount, + "MIDI tuplets: assignVoices", "Chord count is not preserved"); } std::vector convertToData(const std::vector &tuplets) @@ -1351,12 +1503,16 @@ std::vector findTuplets(const ReducedFraction &startBarTick, } filterTuplets(tuplets); - resetTupletVoices(tuplets); // because of tol some chords may have non-zero voices auto nonTuplets = findNonTupletChords(tuplets, startBarChordIt, endBarChordIt); + if (tupletVoiceLimit() == 1) + excludeExtraVoiceTuplets(tuplets, nonTuplets, regularRaster); + resetTupletVoices(tuplets); // because of tol some chords may have non-zero voices + addChordsBetweenTupletNotes(tuplets, nonTuplets); sortNotesByPitch(startBarChordIt, endBarChordIt); sortTupletsByAveragePitch(tuplets); + if (operations.useMultipleVoices) { splitFirstTupletChords(tuplets, chords); minimizeOffTimeError(tuplets, chords, nonTuplets); @@ -1364,6 +1520,11 @@ std::vector findTuplets(const ReducedFraction &startBarTick, Q_ASSERT_X(!doTupletsHaveCommonChords(tuplets), "MIDI tuplets: findTuplets", "Tuplets have common chords but they shouldn't"); + Q_ASSERT_X((voiceLimit() == 1) + ? !haveOverlappingVoices(nonTuplets, tuplets, regularRaster) + : true, + "MIDI tuplets: findTuplets", + "Overlapping tuplet and non-tuplet voices for the case !useMultipleVoices"); assignVoices(chords, tuplets, nonTuplets, startBarTick, endBarTick, regularRaster); From e3b79ee0bc3abe21357442a8c6782705aa0d5f5c Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Tue, 31 Dec 2013 13:26:09 +0400 Subject: [PATCH 5/7] Enclose assert functions by #ifdef QT_DEBUG --- mscore/importmidi.cpp | 7 ++++--- mscore/importmidi_chord.cpp | 7 ++++--- mscore/importmidi_drum.cpp | 7 ++++--- mscore/importmidi_tuplet.cpp | 19 ++++++++++--------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/mscore/importmidi.cpp b/mscore/importmidi.cpp index a55cf431f6cd6..288ad3f07cf10 100644 --- a/mscore/importmidi.cpp +++ b/mscore/importmidi.cpp @@ -852,8 +852,8 @@ QList getTracksMeta(const QList &tracks, return tracksMeta; } -//---------------------------------------------------------------------------------------- -// DEBUG function + +#ifdef QT_DEBUG bool doNotesOverlap(const std::multimap &tracks) { @@ -883,7 +883,8 @@ bool doNotesOverlap(const std::multimap &tracks) return false; } -//---------------------------------------------------------------------------------------- +#endif + void convertMidi(Score *score, const MidiFile *mf) { diff --git a/mscore/importmidi_chord.cpp b/mscore/importmidi_chord.cpp index 4c7f78b649f87..e93be0802c051 100644 --- a/mscore/importmidi_chord.cpp +++ b/mscore/importmidi_chord.cpp @@ -90,8 +90,8 @@ void removeOverlappingNotes(std::multimap &tracks) } } -//---------------------------------------------------------------------------------------- -// DEBUG function + +#ifdef QT_DEBUG bool areOnTimeValuesDifferent(const std::multimap &chords) { @@ -105,7 +105,8 @@ bool areOnTimeValuesDifferent(const std::multimap &c return true; } -//---------------------------------------------------------------------------------------- +#endif + // based on quickthresh algorithm // diff --git a/mscore/importmidi_drum.cpp b/mscore/importmidi_drum.cpp index b8511a6f56624..83bb674b0f52b 100644 --- a/mscore/importmidi_drum.cpp +++ b/mscore/importmidi_drum.cpp @@ -16,8 +16,8 @@ extern Preferences preferences; namespace MidiDrum { -//---------------------------------------------------------------------------------------- -// DEBUG functions + +#ifdef QT_DEBUG bool areOnTimeValuesDifferent(const std::multimap &chords) { @@ -40,7 +40,8 @@ bool areVoicesNonZero(const std::multimap &chords) return true; } -//---------------------------------------------------------------------------------------- +#endif + void splitDrumVoices(std::multimap &tracks) { diff --git a/mscore/importmidi_tuplet.cpp b/mscore/importmidi_tuplet.cpp index 1272b092803ec..933bd017b2f73 100644 --- a/mscore/importmidi_tuplet.cpp +++ b/mscore/importmidi_tuplet.cpp @@ -428,8 +428,8 @@ validateTuplets(std::list &indexes, return findTupletError(indexes, tuplets, excludedChords); } -//---------------------------------------------------------------------------------------- -// DEBUG function + +#ifdef QT_DEBUG bool validateSelectedTuplets(const std::list &bestIndexes, const std::vector &tuplets) @@ -469,7 +469,8 @@ bool validateSelectedTuplets(const std::list &bestIndexes, return true; } -//---------------------------------------------------------------------------------------- +#endif + // Try different permutations of tuplets (as indexes) to minimize quantization error. // Because one tuplet can use the same chord as another tuplet - @@ -1099,8 +1100,8 @@ void removeUnusedTuplets(std::vector &tuplets, std::swap(tuplets, newTuplets); } -//---------------------------------------------------------------------------------------- -// DEBUG functions + +#ifdef QT_DEBUG bool haveTupletsEmptyChords(const std::vector &tuplets) { @@ -1178,7 +1179,8 @@ bool doTupletsHaveCommonChords(const std::vector &tuplets) return false; } -//---------------------------------------------------------------------------------------- +#endif + std::vector > findNonTupletIntervals(const std::list::iterator> &nonTuplets, @@ -1243,8 +1245,7 @@ findForTiedTuplets(const std::vector &tuplets, } -//---------------------------------------------------------------------------------------- -// DEBUG functions +#ifdef QT_DEBUG bool areAllElementsUnique(const std::list::iterator> &nonTuplets) { @@ -1268,7 +1269,7 @@ size_t chordCount(const std::vector &tuplets, return sum; } -//---------------------------------------------------------------------------------------- +#endif // for the case !useMultipleVoices From 825f225e21b23ca8c6585b517dd00589d7ef312d Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Thu, 2 Jan 2014 23:12:57 +0400 Subject: [PATCH 6/7] Remove incorrect comment --- mscore/importmidi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mscore/importmidi.cpp b/mscore/importmidi.cpp index 288ad3f07cf10..ec782b9648564 100644 --- a/mscore/importmidi.cpp +++ b/mscore/importmidi.cpp @@ -878,7 +878,7 @@ bool doNotesOverlap(const std::multimap &tracks) } } } - } // for note1 + } } return false; } From c8ba5dc94b8b4eec3a2f6a852b8cc5927e79281d Mon Sep 17 00:00:00 2001 From: "Andrey M. Tokarev" Date: Thu, 2 Jan 2014 23:42:51 +0400 Subject: [PATCH 7/7] Add note overlap check after removeOverlappingNotes function --- mscore/importmidi.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mscore/importmidi.cpp b/mscore/importmidi.cpp index ec782b9648564..2b54e3cca5e6c 100644 --- a/mscore/importmidi.cpp +++ b/mscore/importmidi.cpp @@ -895,6 +895,10 @@ void convertMidi(Score *score, const MidiFile *mf) cleanUpMidiEvents(tracks); MChord::collectChords(tracks); MChord::removeOverlappingNotes(tracks); + + Q_ASSERT_X(!doNotesOverlap(tracks), + "convertMidi:", "There are overlapping notes of the same voice that is incorrect"); + quantizeAllTracks(tracks, sigmap, lastTick); Q_ASSERT_X(!doNotesOverlap(tracks),