bool isTupletAllowed(const TupletInfo &tupletInfo) { { // special check for duplets and triplets const std::vector<int> nums = {2, 3}; // for duplet: if note first and single - only 1/2*tupletLen duration is allowed // for triplet: if note first and single - only 1/3*tupletLen duration is allowed for (int num: nums) { if (tupletInfo.tupletNumber == num && tupletInfo.chords.size() == 1 && tupletInfo.firstChordIndex == 0) { const auto &chordEventIt = tupletInfo.chords.begin()->second; const auto tupletNoteLen = tupletInfo.len / num; for (const auto ¬e: chordEventIt->second.notes) { if ((note.offTime - chordEventIt->first - tupletNoteLen).absValue() > tupletNoteLen / 2) return false; } } } } // for all tuplets const auto &opers = midiImportOperations.data()->trackOpers; const int minAllowedNoteCount = (opers.isHumanPerformance.value()) ? tupletLimits(tupletInfo.tupletNumber).minNoteCountHuman : tupletLimits(tupletInfo.tupletNumber).minNoteCount; if ((int)tupletInfo.chords.size() < minAllowedNoteCount) return false; // allow duplets and quadruplets with error == regular error // because tuplet notation is simpler in that case if (tupletInfo.tupletNumber == 2 || tupletInfo.tupletNumber == 4) { if (tupletInfo.tupletSumError > tupletInfo.regularSumError) return false; } else { if (tupletInfo.tupletSumError >= tupletInfo.regularSumError) return false; } // at least one note has to have len >= (half tuplet note len) const auto tupletNoteLen = tupletInfo.len / tupletInfo.tupletNumber; for (const auto &tupletChord: tupletInfo.chords) { for (const auto ¬e: tupletChord.second->second.notes) { if (note.offTime - tupletChord.first >= tupletNoteLen / 2) return true; } } return false; }
void createTupletNotes( Staff *staff, const std::multimap<ReducedFraction, TupletData> &tuplets) { Score* score = staff->score(); const int track = staff->idx() * VOICES; for (const auto &tupletEvent: tuplets) { const auto &tupletData = tupletEvent.second; if (tupletData.elements.empty()) continue; Tuplet* tuplet = new Tuplet(score); const auto &tupletRatio = tupletLimits(tupletData.tupletNumber).ratio; tuplet->setRatio(tupletRatio.fraction()); tuplet->setDuration(tupletData.len.fraction()); const TDuration baseLen = tupletData.len.fraction() / tupletRatio.denominator(); tuplet->setBaseLen(baseLen); tuplet->setTrack(track); tuplet->setTick(tupletData.onTime.ticks()); tuplet->setVoice(tupletData.voice); Measure* measure = score->tick2measure(tupletData.onTime.ticks()); tuplet->setParent(measure); for (DurationElement *el: tupletData.elements) { tuplet->add(el); el->setTuplet(tuplet); } } }
ReducedFraction findSumLengthOfRests( const TupletInfo &tupletInfo, const ReducedFraction &startBarTick) { auto beg = tupletInfo.onTime; const auto tupletEndTime = tupletInfo.onTime + tupletInfo.len; const auto tupletNoteLen = tupletInfo.len / tupletInfo.tupletNumber; ReducedFraction sumLen = {0, 1}; const auto &opers = midiImportOperations.data()->trackOpers; const int currentTrack = midiImportOperations.currentTrack(); for (const auto &chord: tupletInfo.chords) { const auto staccatoIt = (opers.simplifyDurations.value(currentTrack)) ? tupletInfo.staccatoChords.find(chord.first) : tupletInfo.staccatoChords.end(); const MidiChord &midiChord = chord.second->second; const auto &chordOnTime = (chord.second->first < startBarTick) ? startBarTick : Quantize::findQuantizedTupletChordOnTime(*chord.second, tupletInfo.len, tupletLimits(tupletInfo.tupletNumber).ratio, startBarTick); if (beg < chordOnTime) sumLen += (chordOnTime - beg); ReducedFraction maxOffTime(0, 1); for (int i = 0; i != midiChord.notes.size(); ++i) { auto noteOffTime = midiChord.notes[i].offTime; if (staccatoIt != tupletInfo.staccatoChords.end() && i == staccatoIt->second) noteOffTime = chordOnTime + tupletNoteLen; if (noteOffTime > maxOffTime) maxOffTime = noteOffTime; } beg = Quantize::findQuantizedTupletNoteOffTime(chord.first, maxOffTime, tupletInfo.len, tupletLimits(tupletInfo.tupletNumber).ratio, startBarTick).first; if (beg >= tupletEndTime) break; } if (beg < tupletEndTime) sumLen += (tupletEndTime - beg); return sumLen; }
void detectStaccato(TupletInfo &tuplet) { if ((int)tuplet.chords.size() >= tupletLimits(tuplet.tupletNumber).minNoteCountStaccato) { const auto tupletNoteLen = tuplet.len / tuplet.tupletNumber; for (auto &chord: tuplet.chords) { MidiChord &midiChord = chord.second->second; for (int i = 0; i != midiChord.notes.size(); ++i) { if (midiChord.notes[i].offTime - chord.first < tupletNoteLen / 2) { // later if chord have one or more notes // with staccato -> entire chord is staccato // don't mark note as staccato here, only remember it // because different tuplets may contain this note, // it will be resolved after tuplet filtering tuplet.staccatoChords.insert({chord.first, i}); } } } } }