void SwingDetector::add(ChordRest *cr) { if (elements.empty()) { if (ReducedFraction(cr->globalDuration()) >= FULL_LEN) return; const int tickInBar = cr->tick() - cr->measure()->tick(); if (tickInBar % MScore::division == 0) append(cr); } else { if (sumLen + ReducedFraction(cr->globalDuration()) > FULL_LEN) { reset(); return; } append(cr); if (sumLen == FULL_LEN) { // check for swing patterns switch (swingType) { case MidiOperation::Swing::SWING: checkNormalSwing(); break; case MidiOperation::Swing::SHUFFLE: checkShuffle(); break; default: break; } reset(); } } }
void addTitleIfAny(const std::multimap<ReducedFraction, std::string> &lyricTrack, Score *score) { int textCounter = 0; for (const auto &lyric: lyricTrack) { if (lyric.first == ReducedFraction(0, 1)) { QString text = MidiCharset::convertToCharset(lyric.second); if (isTitlePrefix(text)) { ++textCounter; addTitleToScore(score, text, textCounter); } } else if (lyric.first > ReducedFraction(0, 1)) { break; } } }
void SwingDetector::append(ChordRest *cr) { if (cr->type() == Element::CHORD || cr->type() == Element::REST) { elements.push_back(cr); sumLen += ReducedFraction(cr->globalDuration()); } }
void SwingDetector::append(ChordRest *cr) { if (cr->isChord() || cr->isRest()) { elements.push_back(cr); sumLen += ReducedFraction(cr->globalTicks()); } }
void removeOverlappingNotes(std::multimap<int, MTrack> &tracks) { for (auto &track: tracks) { auto &chords = track.second.chords; for (auto i1 = chords.begin(); i1 != chords.end(); ++i1) { auto &chord1 = i1->second; const auto &onTime1 = i1->first; for (auto ¬e1: chord1.notes) { for (auto i2 = std::next(i1); i2 != chords.end(); ++i2) { const auto &onTime2 = i2->first; if (onTime2 >= note1.offTime) break; auto &chord2 = i2->second; if (chord1.voice != chord2.voice) continue; for (auto ¬e2: chord2.notes) { if (note2.pitch != note1.pitch) continue; qDebug("Midi import: overlapping events: %d+%d %d+%d", onTime1.ticks(), note1.offTime.ticks(), onTime2.ticks(), note2.offTime.ticks()); note1.offTime = onTime2; i2 = std::prev(chords.end()); break; } } if (note1.offTime <= ReducedFraction(0, 1)) { qDebug("removeOverlappingNotes: duration <= 0: drop note at %d", onTime1.ticks()); continue; } } } // for note1 } }
ReducedFraction ReducedFraction::reduced() const { const int tmp = gcd(numerator_, denominator_); checkDivisionOverflow(numerator_, tmp); checkDivisionOverflow(denominator_, tmp); return ReducedFraction(numerator_ / tmp, denominator_ / tmp); }
void quantizeChords(std::multimap<ReducedFraction, MidiChord> &chords, const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tupletEvents, const TimeSigMap *sigmap) { std::multimap<ReducedFraction, MidiChord> quantizedChords; for (auto &chordEvent: chords) { MidiChord chord = chordEvent.second; // copy chord auto onTime = chordEvent.first; const auto barStart = findBarStart(onTime, sigmap); if (chord.quantizedOnTime == ReducedFraction(-1, 1)) { const auto raster = findQuantRaster(onTime, chord.voice, tupletEvents, chords, sigmap); onTime = barStart + Quantize::quantizeValue(onTime - barStart, raster); } else { onTime = chord.quantizedOnTime; chord.quantizedOnTime = {-1, 1}; } for (auto it = chord.notes.begin(); it != chord.notes.end(); ) { MidiNote ¬e = *it; if (note.quantizedOffTime == ReducedFraction(-1, 1)) { auto offTime = note.offTime; auto raster = findQuantRaster(offTime, chord.voice, tupletEvents, chords, sigmap); if (Meter::isSimpleNoteDuration(raster)) // offTime is not inside tuplet raster = reduceRasterIfDottedNote(note.offTime - chordEvent.first, raster); offTime = barStart + Quantize::quantizeValue(offTime - barStart, raster); note.offTime = offTime; } else { note.offTime = note.quantizedOffTime; note.quantizedOffTime = {-1, 1}; } if (note.offTime - onTime < MChord::minAllowedDuration()) { it = chord.notes.erase(it); qDebug() << "quantizeChords: note was removed due to its short length"; continue; } ++it; } if (!chord.notes.isEmpty()) quantizedChords.insert({onTime, chord}); } std::swap(chords, quantizedChords); }
bool areDurationsEqual( const QList<std::pair<ReducedFraction, TDuration> > &durations, const ReducedFraction &desiredLen) { ReducedFraction sum(0, 1); for (const auto &d: durations) sum += ReducedFraction(d.second.fraction()) / d.first; return desiredLen == desiredLen; }
ReducedFraction ReducedFraction::reduced() const { const int tmp = gcd(numerator_, denominator_); Q_ASSERT_X(!isDivisionOverflow(numerator_, tmp), "ReducedFraction::reduced", "Division overflow"); Q_ASSERT_X(!isDivisionOverflow(denominator_, tmp), "ReducedFraction::reduced", "Division overflow"); return ReducedFraction(numerator_ / tmp, denominator_ / tmp); }
ReducedFraction quantizeValue(const ReducedFraction &value, const ReducedFraction &raster) { const auto valueReduced = value.reduced(); const auto rasterReduced = raster.reduced(); int valNum = valueReduced.numerator() * rasterReduced.denominator(); const int rastNum = rasterReduced.numerator() * valueReduced.denominator(); const int commonDen = valueReduced.denominator() * rasterReduced.denominator(); valNum = ((valNum + rastNum / 2) / rastNum) * rastNum; return ReducedFraction(valNum, commonDen).reduced(); }
void minimizeNumberOfRests( std::multimap<ReducedFraction, MidiChord> &chords, const TimeSigMap *sigmap, const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tuplets) { for (auto it = chords.begin(); it != chords.end(); ++it) { for (MidiNote ¬e: it->second.notes) { const auto barStart = MidiBar::findBarStart(note.offTime, sigmap); const auto barFraction = ReducedFraction( sigmap->timesig(barStart.ticks()).timesig()); auto durationStart = (it->first > barStart) ? it->first : barStart; if (it->second.isInTuplet) { const auto &tuplet = it->second.tuplet->second; if (note.offTime >= tuplet.onTime + tuplet.len) durationStart = tuplet.onTime + tuplet.len; } auto endTime = (barStart == note.offTime) ? barStart : barStart + barFraction; if (note.isInTuplet) { const auto &tuplet = note.tuplet->second; if (note.offTime == tuplet.onTime + tuplet.len) continue; endTime = barStart + Quantize::quantizeToLarge( note.offTime - barStart, tuplet.len / tuplet.tupletNumber); } const auto beatLen = Meter::beatLength(barFraction); const auto beatTime = barStart + Quantize::quantizeToLarge( note.offTime - barStart, beatLen); if (endTime > beatTime) endTime = beatTime; auto next = std::next(it); while (next != chords.end() && (next->second.voice != it->second.voice || next->first < note.offTime)) { ++next; } if (next != chords.end()) { if (next->first < endTime) endTime = next->first; if (next->second.isInTuplet && !note.isInTuplet) { const auto &tuplet = next->second.tuplet->second; if (tuplet.onTime < endTime) endTime = tuplet.onTime; } } lengthenNote(note, it->second.voice, it->first, durationStart, endTime, barStart, barFraction, tuplets); } } }
ReducedFraction findMinDuration(const ReducedFraction &onTime, const QList<MidiChord> &midiChords, const ReducedFraction &length) { ReducedFraction len = length; for (const auto &chord: midiChords) { for (const auto ¬e: chord.notes) { if ((note.offTime - onTime < len) && (note.offTime - onTime != ReducedFraction(0, 1))) len = note.offTime - onTime; } } return len; }
void addLyricsToScore(const std::multimap<ReducedFraction, std::string> &lyricTrack, const Staff *staffAddTo) { Score *score = staffAddTo->score(); int textCounter = 0; for (const auto &e: lyricTrack) { const auto &tick = e.first; QString str = MidiCharset::convertToCharset(e.second); if (tick == ReducedFraction(0, 1)) { addTitle(score, str, &textCounter); } else { QString text = removeSlashes(str); score->addLyrics(tick.ticks(), staffAddTo->idx(), text); } } }
ReducedFraction findQuantRaster( const ReducedFraction &time, int voice, const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tupletEvents, const std::multimap<ReducedFraction, MidiChord> &chords, const TimeSigMap *sigmap) { ReducedFraction raster; const auto tupletIt = MidiTuplet::findTupletContainsTime(voice, time, tupletEvents); if (tupletIt != tupletEvents.end() && time > tupletIt->first) raster = tupletIt->second.tupletQuant; // quantize onTime with tuplet quant else { // quantize onTime with regular quant const auto startBarTick = findBarStart(time, sigmap); const auto endBarTick = startBarTick + ReducedFraction(sigmap->timesig(startBarTick.ticks()).timesig()); const auto startBarChordIt = MChord::findFirstChordInRange( chords, startBarTick, endBarTick); raster = findRegularQuantRaster(startBarChordIt, chords.end(), endBarTick); } return raster; }
void addLyricsToScore( const std::multimap<ReducedFraction, std::string> &lyricTrack, const std::vector<std::pair<ReducedFraction, ReducedFraction>> &matchedLyricTimes, const Staff *staffAddTo) { Score *score = staffAddTo->score(); addTitleIfAny(lyricTrack, score); for (const auto &timePair: matchedLyricTimes) { const auto quantizedTime = timePair.first; const auto originalTime = timePair.second; const auto it = lyricTrack.find(originalTime); Q_ASSERT_X(it != lyricTrack.end(), "MidiLyrics::addLyricsToScore", "Lyric time not found"); QString text = MidiCharset::convertToCharset(it->second); if (originalTime != ReducedFraction(0, 1) || !isTitlePrefix(text)) { // not title score->addLyrics(quantizedTime.ticks(), staffAddTo->idx(), removeSlashes(text).toHtmlEscaped()); } } }
ReducedFraction ReducedFraction::fromTicks(int ticks) { return ReducedFraction(ticks, MScore::division * 4).reduced(); }
void setToNegative(ReducedFraction &v1, ReducedFraction &v2, ReducedFraction &v3) { v1 = ReducedFraction(-1, 1); v2 = ReducedFraction(-1, 1); v3 = ReducedFraction(-1, 1); }
void SwingDetector::reset() { elements.clear(); sumLen = ReducedFraction(0); }
void createInstruments(Score *score, QList<MTrack> &tracks) { const auto& opers = preferences.midiImportOperations; const auto &instrListOption = opers.data()->trackOpers.msInstrList; const int ntracks = tracks.size(); for (int idx = 0; idx < ntracks; ++idx) { MTrack& track = tracks[idx]; Part* part = new Part(score); const auto &instrList = instrListOption.value(track.indexOfOperation); const InstrumentTemplate *instr = nullptr; if (!instrList.empty()) { const int instrIndex = opers.data()->trackOpers.msInstrIndex.value( track.indexOfOperation); instr = instrList[instrIndex]; if (instr) part->initFromInstrTemplate(instr); } if (areNext3OrganStaff(idx, tracks)) part->setStaves(3); else if (areNext2GrandStaff(idx, tracks)) part->setStaves(2); else part->setStaves(1); if (part->nstaves() == 1) { if (track.mtrack->drumTrack()) { part->staff(0)->setStaffType(0, StaffType::preset(StaffTypes::PERC_DEFAULT)); if (!instr) { part->instrument()->setDrumset(smDrumset); } } } else { if (!instr) { part->staff(0)->setBarLineSpan(2); part->staff(0)->setBracket(0, BracketType::BRACE); } else { part->staff(0)->setBarLineSpan(instr->barlineSpan[0]); part->staff(0)->setBracket(0, instr->bracket[0]); } part->staff(0)->setBracketSpan(0, 2); } if (instr) { for (int i = 0; i != part->nstaves(); ++i) { if (instr->staffTypePreset) part->staff(i)->setStaffType(0, instr->staffTypePreset); part->staff(i)->setLines(0, instr->staffLines[i]); part->staff(i)->setSmall(0, instr->smallStaff[i]); part->staff(i)->setDefaultClefType(instr->clefTypes[i]); } } for (int i = 0; i != part->nstaves(); ++i) { if (i > 0) ++idx; tracks[idx].staff = part->staff(i); } // only importing a single volume per track here, skip when multiple volumes // are defined, or the single volume is not defined on tick 0. if (track.volumes.size() == 1) { for (auto &i: track.volumes) { if (i.first == ReducedFraction(0, 1)) { part->instrument()->channel(0)->volume = i.second; } } } score->appendPart(part); } }
void lengthenNote( MidiNote ¬e, int voice, const ReducedFraction ¬eOnTime, const ReducedFraction &durationStart, const ReducedFraction &endTime, const ReducedFraction &barStart, const ReducedFraction &barFraction, const std::multimap<ReducedFraction, MidiTuplet::TupletData> &tuplets) { if (endTime <= note.offTime) return; const auto &opers = preferences.midiImportOperations.data()->trackOpers; const int currentTrack = preferences.midiImportOperations.currentTrack(); const bool useDots = opers.useDots.value(currentTrack); const auto tupletsForDuration = MidiTuplet::findTupletsInBarForDuration( voice, barStart, note.offTime, endTime - note.offTime, tuplets); Q_ASSERT_X(note.quant != ReducedFraction(-1, 1), "Simplify::lengthenNote", "Note quant value was not set"); const auto origNoteDurations = Meter::toDurationList( durationStart - barStart, note.offTime - barStart, barFraction, tupletsForDuration, Meter::DurationType::NOTE, useDots, false); const auto origRestDurations = Meter::toDurationList( note.offTime - barStart, endTime - barStart, barFraction, tupletsForDuration, Meter::DurationType::REST, useDots, false); Q_ASSERT_X(areDurationsEqual(origNoteDurations, note.offTime - durationStart), "Simplify::lengthenNote", "Too short note durations remaining"); Q_ASSERT_X(areDurationsEqual(origRestDurations, endTime - note.offTime), "Simplify::lengthenNote", "Too short rest durations remaining"); // double - because can be + 0.5 for dots double minNoteDurationCount = MidiDuration::durationCount(origNoteDurations); double minRestDurationCount = MidiDuration::durationCount(origRestDurations); ReducedFraction bestOffTime(-1, 1); for (ReducedFraction offTime = note.offTime + note.quant; offTime <= endTime; offTime += note.quant) { double noteDurationCount = 0; double restDurationCount = 0; const auto noteDurations = Meter::toDurationList( durationStart - barStart, offTime - barStart, barFraction, tupletsForDuration, Meter::DurationType::NOTE, useDots, false); Q_ASSERT_X(areDurationsEqual(noteDurations, offTime - durationStart), "Simplify::lengthenNote", "Too short note durations remaining"); noteDurationCount += MidiDuration::durationCount(noteDurations); if (offTime < endTime) { const auto restDurations = Meter::toDurationList( offTime - barStart, endTime - barStart, barFraction, tupletsForDuration, Meter::DurationType::REST, useDots, false); Q_ASSERT_X(areDurationsEqual(restDurations, endTime - offTime), "Simplify::lengthenNote", "Too short rest durations remaining"); restDurationCount += MidiDuration::durationCount(restDurations); } if (noteDurationCount + restDurationCount < minNoteDurationCount + minRestDurationCount) { if (opers.isHumanPerformance.value() || noteDurationCount <= 1.5) { minNoteDurationCount = noteDurationCount; minRestDurationCount = restDurationCount; bestOffTime = offTime; } } } if (bestOffTime == ReducedFraction(-1, 1)) return; // check for staccato: // don't apply staccato if note is tied // (case noteOnTime != durationStart - another bar, for example) bool hasLossOfAccuracy = false; const double addedPart = ((bestOffTime - note.offTime) / (bestOffTime - durationStart)).toDouble(); const double STACCATO_TOL = 0.3; if (addedPart >= STACCATO_TOL) { if (noteOnTime == durationStart && minNoteDurationCount <= 1.5) note.staccato = true; else hasLossOfAccuracy = true; } // if the difference is only in one note/rest and there is some loss of accuracy - // discard change because it silently reduces duration accuracy // without significant improvement of readability if (!opers.isHumanPerformance.value() && (origNoteDurations.size() + origRestDurations.size()) - (minNoteDurationCount + minRestDurationCount) <= 1 && !hasComplexBeamedDurations(origNoteDurations) && !hasComplexBeamedDurations(origRestDurations) && hasLossOfAccuracy) { return; } note.offTime = bestOffTime; }
ReducedFraction ReducedFraction::absValue() const { return ReducedFraction(qAbs(numerator_), qAbs(denominator_)); }
ReducedFraction userTimeSigToFraction( MidiOperations::TimeSigNumerator timeSigNumerator, MidiOperations::TimeSigDenominator timeSigDenominator) { int numerator = 4; int denominator = 4; switch (timeSigNumerator) { case MidiOperations::TimeSigNumerator::_2: numerator = 2; break; case MidiOperations::TimeSigNumerator::_3: numerator = 3; break; case MidiOperations::TimeSigNumerator::_4: numerator = 4; break; case MidiOperations::TimeSigNumerator::_5: numerator = 5; break; case MidiOperations::TimeSigNumerator::_6: numerator = 6; break; case MidiOperations::TimeSigNumerator::_7: numerator = 7; break; case MidiOperations::TimeSigNumerator::_9: numerator = 9; break; case MidiOperations::TimeSigNumerator::_12: numerator = 12; break; case MidiOperations::TimeSigNumerator::_15: numerator = 15; break; case MidiOperations::TimeSigNumerator::_21: numerator = 21; break; default: break; } switch (timeSigDenominator) { case MidiOperations::TimeSigDenominator::_2: denominator = 2; break; case MidiOperations::TimeSigDenominator::_4: denominator = 4; break; case MidiOperations::TimeSigDenominator::_8: denominator = 8; break; case MidiOperations::TimeSigDenominator::_16: denominator = 16; break; case MidiOperations::TimeSigDenominator::_32: denominator = 32; break; default: break; } return ReducedFraction(numerator, denominator); }
TupletInfo findTupletApproximation( const ReducedFraction &tupletLen, int tupletNumber, const ReducedFraction &basicQuant, const ReducedFraction &startTupletTime, const std::multimap<ReducedFraction, MidiChord>::iterator &startChordIt, const std::multimap<ReducedFraction, MidiChord>::iterator &endChordIt) { TupletInfo tupletInfo; tupletInfo.tupletNumber = tupletNumber; tupletInfo.onTime = startTupletTime; tupletInfo.len = tupletLen; const auto tupletNoteLen = tupletLen / tupletNumber; struct Error { bool operator<(const Error &e) const { if (tupletError < e.tupletError) return true; if (tupletError > e.tupletError) return false; return (tupletRegularDiff < e.tupletRegularDiff); } ReducedFraction tupletError; ReducedFraction tupletRegularDiff; }; struct Candidate { int posIndex; std::multimap<ReducedFraction, MidiChord>::iterator chord; ReducedFraction regularError; }; std::multimap<Error, Candidate> chordCandidates; for (int posIndex = 0; posIndex != tupletNumber; ++posIndex) { const auto tupletNotePos = startTupletTime + tupletNoteLen * posIndex; for (auto it = startChordIt; it != endChordIt; ++it) { if (it->first < tupletNotePos - tupletNoteLen / 2) continue; if (it->first > tupletNotePos + tupletNoteLen / 2) break; const auto tupletError = (it->first - tupletNotePos).absValue(); const auto regularError = Quantize::findOnTimeQuantError(*it, basicQuant); const auto diff = tupletError - regularError; chordCandidates.insert({{tupletError, diff}, {posIndex, it, regularError}}); } } std::set<std::pair<const ReducedFraction, MidiChord> *> usedChords; std::set<int> usedPosIndexes; ReducedFraction diffSum; int firstChordIndex = 0; for (const auto &candidate: chordCandidates) { if (diffSum + candidate.first.tupletRegularDiff > ReducedFraction(0, 1)) break; const Candidate &c = candidate.second; if (usedPosIndexes.find(c.posIndex) != usedPosIndexes.end()) continue; if (usedChords.find(&*c.chord) != usedChords.end()) continue; usedChords.insert(&*c.chord); usedPosIndexes.insert(c.posIndex); diffSum += candidate.first.tupletRegularDiff; tupletInfo.chords.insert({c.chord->first, c.chord}); tupletInfo.tupletSumError += candidate.first.tupletError; tupletInfo.regularSumError += c.regularError; // if chord was inserted to the beginning - remember its pos index if (c.chord->first == tupletInfo.chords.begin()->first) firstChordIndex = c.posIndex; } tupletInfo.firstChordIndex = firstChordIndex; return tupletInfo; }