From 971e76c563ad1abe74ed4e16e8fe621dcfb6e2af Mon Sep 17 00:00:00 2001 From: Alexander Pavlov Date: Sat, 4 Jan 2025 16:18:08 +0200 Subject: [PATCH] guitar bend import: part 3 - splitting chord durations --- src/importexport/guitarpro/CMakeLists.txt | 2 + .../bendchorddurationsplitter.cpp | 211 +++++ .../bendchorddurationsplitter.h | 35 + .../guitarbendimport/benddatacollector.cpp | 191 +++- .../guitarbendimport/benddatacollector.h | 2 +- .../guitarbendimport/benddataprocessor.cpp | 74 +- .../bend+release_1-gp.mscx | 315 +++++++ .../guitarbendimporter_data/bend+release_1.gp | Bin 0 -> 8644 bytes .../guitarbendimporter_data/bend_1-gp.mscx | 606 ++++++++++++ .../tests/guitarbendimporter_data/bend_1.gp | Bin 0 -> 8813 bytes .../guitarbendimporter_data/bend_2-gp.mscx | 891 ++++++++++++++++++ .../tests/guitarbendimporter_data/bend_2.gp | Bin 0 -> 8888 bytes .../guitarbendimporter_data/bend_3-gp.mscx | 404 ++++++++ .../tests/guitarbendimporter_data/bend_3.gp | Bin 0 -> 8903 bytes .../prebend+bend_1-gp.mscx | 327 +++++++ .../guitarbendimporter_data/prebend+bend_1.gp | Bin 0 -> 8685 bytes .../tests/guitarbendimporter_tests.cpp | 30 +- 17 files changed, 3012 insertions(+), 76 deletions(-) create mode 100644 src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.cpp create mode 100644 src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.h create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1.gp create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1.gp create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2.gp create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_3-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/bend_3.gp create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1.gp diff --git a/src/importexport/guitarpro/CMakeLists.txt b/src/importexport/guitarpro/CMakeLists.txt index c4c0bb592ad18..1c6625a5e2d34 100644 --- a/src/importexport/guitarpro/CMakeLists.txt +++ b/src/importexport/guitarpro/CMakeLists.txt @@ -47,6 +47,8 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/benddatacontext.h ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/benddatacollector.cpp ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/benddatacollector.h + ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/bendchorddurationsplitter.cpp + ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/bendchorddurationsplitter.h ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/benddataprocessor.cpp ${CMAKE_CURRENT_LIST_DIR}/internal/guitarbendimport/benddataprocessor.h diff --git a/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.cpp b/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.cpp new file mode 100644 index 0000000000000..b094649dda70c --- /dev/null +++ b/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.cpp @@ -0,0 +1,211 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "bendchorddurationsplitter.h" + +#include "log.h" + +using namespace mu::engraving; + +namespace mu::iex::guitarpro { +constexpr int MAX_DENOMINATOR = 64; +constexpr Fraction ONE_DOT_DURATION_MULTIPLIER(3, 2); +constexpr Fraction TWO_DOTS_DURATION_MULTIPLIER(7, 4); + +static int findClosestNumerator(const Fraction& fraction, int requestedDenominator, bool skipFirstBest); +static Fraction findClosestDisplayableDuration(const Fraction& totalDuration, const Fraction& targetDuration, int maxDenominator, + bool skipFirstBest); +static std::vector fillDurationsByProportions(const Fraction& totalDuration, const std::vector& proportions, + int maxDenominator, bool skipFirstBest); +static bool canBeRepresentedAsDottedNote(const Fraction& duration, int maxDenominator); +static std::vector bruteForceSplit(const Fraction& totalDuration, const std::vector& proportions, int maxDenominator); +static std::vector bruteForceSplitInTwo(const Fraction& target, const std::vector& proportions, + const std::vector& durations); +static std::vector bruteForceSplitInThree(const Fraction& target, const std::vector& durations); + +static std::vector generateDurations(int maxDenominator); + +std::vector BendChordDurationSplitter::findValidNoteSplit(const Fraction& totalDuration, const std::vector& proportions, + int maxDenominator) +{ + if (proportions.size() == 1) { + std::vector unchangedDurations; + unchangedDurations.push_back(totalDuration); + return unchangedDurations; + } + + bool splitReached = false; + bool skipFirstBest = false; + std::vector durations; + + do { + durations = fillDurationsByProportions(totalDuration, proportions, maxDenominator, skipFirstBest); + + Fraction accumulatedDuration = std::accumulate(durations.begin(), durations.end(), Fraction(0, 1)); + Fraction lastDuration = totalDuration - accumulatedDuration; + + if (lastDuration.isNotZero() && !lastDuration.negative() && canBeRepresentedAsDottedNote(lastDuration, maxDenominator)) { + durations.push_back(lastDuration); + splitReached = true; + } else if (!skipFirstBest) { + skipFirstBest = true; + } else { + durations = bruteForceSplit(totalDuration, proportions, maxDenominator); + splitReached = true; + } + } while (!splitReached); + + return durations; +} + +static std::vector fillDurationsByProportions(const Fraction& totalDuration, const std::vector& proportions, + int maxDenominator, bool skipFirstBest) +{ + std::vector durations; + + for (size_t i = 0; i < proportions.size() - 1; i++) { + Fraction targetDuration = totalDuration * proportions[i]; + Fraction closestDuration = findClosestDisplayableDuration(totalDuration, targetDuration, maxDenominator, + i == 0 ? skipFirstBest : false); + durations.push_back(closestDuration); + } + + return durations; +} + +static std::vector bruteForceSplit(const Fraction& totalDuration, const std::vector& proportions, int maxDenominator) +{ + auto durations = generateDurations(maxDenominator); + std::sort(durations.begin(), durations.end()); + + if (proportions.size() == 2) { + return bruteForceSplitInTwo(totalDuration, proportions, durations); + } + + return bruteForceSplitInThree(totalDuration, durations); +} + +static std::vector bruteForceSplitInTwo(const Fraction& target, const std::vector& proportions, + const std::vector& durations) +{ + if (proportions.front() < proportions.back()) { + for (auto it1 = durations.begin(); it1 != durations.end(); ++it1) { + for (auto it2 = it1; it2 != durations.end(); ++it2) { + if (*it1 + *it2 == target) { + return { *it1, *it2 }; + } + } + } + } else { + for (auto it1 = durations.rbegin(); it1 != durations.rend(); ++it1) { + for (auto it2 = it1; it2 != durations.rend(); ++it2) { + if (*it1 + *it2 == target) { + return { *it1, *it2 }; + } + } + } + } + + return {}; +} + +static std::vector bruteForceSplitInThree(const Fraction& target, const std::vector& durations) +{ + for (auto it1 = durations.begin(); it1 != durations.end(); ++it1) { + for (auto it2 = it1; it2 != durations.end(); ++it2) { + for (auto it3 = it2; it3 != durations.end(); ++it3) { + if (*it1 + *it2 + *it3 == target) { + return { *it1, *it2, *it3 }; + } + } + } + } + + return {}; +} + +static std::vector generateDurations(int maxDenominator) +{ + std::vector durations; + for (int denom = 1; denom <= std::min(maxDenominator, MAX_DENOMINATOR); denom *= 2) { + durations.emplace_back(1, denom); + durations.emplace_back(ONE_DOT_DURATION_MULTIPLIER * denom); + durations.emplace_back(TWO_DOTS_DURATION_MULTIPLIER * denom); + } + return durations; +} + +static Fraction findClosestDisplayableDuration(const Fraction& totalDuration, const Fraction& targetDuration, int maxDenominator, + bool skipFirstBest) +{ + int numerator = findClosestNumerator(targetDuration, maxDenominator, skipFirstBest); + Fraction currentChordTicks = Fraction(numerator, maxDenominator); + currentChordTicks = std::max(currentChordTicks, Fraction(1, maxDenominator)); + currentChordTicks = std::min(currentChordTicks, totalDuration - Fraction(1, maxDenominator)); + currentChordTicks.reduce(); + + return currentChordTicks; +} + +static int findClosestNumerator(const Fraction& fraction, int requestedDenominator, bool skipFirstBest) +{ + int closestNumerator = 0; + double minDifference = std::numeric_limits::max(); + int numerator = fraction.numerator(); + int denominator = fraction.denominator(); + + bool firstBestSkipped = false; + + for (int candidateNumerator = 1; candidateNumerator < requestedDenominator; candidateNumerator++) { + double originalFraction = static_cast(numerator) / denominator; + double candidateFraction = static_cast(candidateNumerator) / requestedDenominator; + + double difference = std::fabs(originalFraction - candidateFraction); + + if (difference < minDifference + && canBeRepresentedAsDottedNote(Fraction(candidateNumerator, requestedDenominator), requestedDenominator)) { + if (skipFirstBest && !firstBestSkipped) { + firstBestSkipped = true; + } else { + minDifference = difference; + closestNumerator = candidateNumerator; + } + } + } + + return closestNumerator; +} + +static bool canBeRepresentedAsDottedNote(const Fraction& duration, int maxDenominator) +{ + int denom = 1; + while (denom <= std::min(maxDenominator, MAX_DENOMINATOR)) { + Fraction base(1, denom); + if (duration == base || duration == base * ONE_DOT_DURATION_MULTIPLIER || duration == base * TWO_DOTS_DURATION_MULTIPLIER) { + return true; + } + + denom *= 2; + } + + return false; +} +} // namespace mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.h b/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.h new file mode 100644 index 0000000000000..fc94f70bf8027 --- /dev/null +++ b/src/importexport/guitarpro/internal/guitarbendimport/bendchorddurationsplitter.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2024 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +namespace mu::iex::guitarpro { +class BendChordDurationSplitter +{ +public: + static std::vector findValidNoteSplit(const mu::engraving::Fraction& totalDuration, + const std::vector& proportions, + int maxDenominator); +}; +} // mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp index c678b943e8154..3b0046778a7f6 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp @@ -26,14 +26,18 @@ #include #include +#include "bendchorddurationsplitter.h" + using namespace mu::engraving; namespace mu::iex::guitarpro { constexpr int BEND_DIVISIONS = 60; -static void fillBendDataContextForNote(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo); +static void fillChordDurationsFromBendDiagram(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo); +static void fillBendDataForNote(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo); static std::vector bendSegmentsFromPitchValues(const PitchValues& pitchValues, bool noteTiedBack); -BendDataCollector::ImportedBendInfo fillBendInfo(const Note* note, const PitchValues& pitchValues); +static bool isSlightBend(const BendDataCollector::ImportedBendInfo& importedInfo); +static BendDataCollector::ImportedBendInfo fillBendInfo(const Note* note, const PitchValues& pitchValues); void BendDataCollector::storeBendData(mu::engraving::Note* note, const mu::engraving::PitchValues& pitchValues) { @@ -46,10 +50,19 @@ BendDataContext BendDataCollector::collectBendDataContext() { BendDataContext bendDataCtx; + for (const auto& [track, trackInfo] : m_bendInfoForNote) { + for (const auto& [tick, tickInfo] : trackInfo) { + if (!tickInfo.empty()) { + // TODO: now using the top note of chord to fill durations + fillChordDurationsFromBendDiagram(bendDataCtx, tickInfo.begin()->second); + } + } + } + for (const auto& [track, trackInfo] : m_bendInfoForNote) { for (const auto& [tick, tickInfo] : trackInfo) { for (const auto& [pitch, importedBendInfo] : tickInfo) { - fillBendDataContextForNote(bendDataCtx, importedBendInfo); + fillBendDataForNote(bendDataCtx, importedBendInfo); } } } @@ -156,7 +169,7 @@ static bool isSlightBend(const BendDataCollector::ImportedBendInfo& importedInfo return false; } -void fillSlightBendDataContext(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) +void fillSlightBendData(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) { const Note* note = importedInfo.note; const Chord* chord = note->chord(); @@ -191,7 +204,7 @@ static bool isFirstPrebend(const BendDataCollector::ImportedBendInfo& importedIn return firstSeg.startTime == firstSeg.endTime; } -static void fillPrebendDataContext(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) +static void fillPrebendData(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) { const Note* note = importedInfo.note; Fraction tick = note->tick(); @@ -205,61 +218,153 @@ static void fillPrebendDataContext(BendDataContext& bendDataCtx, const BendDataC prebendNoteData.quarterTones = firstSeg.pitchDiff / 25; prebendChordData.noteDataByPitch[note->pitch()] = std::move(prebendNoteData); +} + +static void fillNormalBendData(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo, + size_t startIndex) +{ + if (startIndex >= importedInfo.segments.size()) { + return; + } + + const Note* note = importedInfo.note; + const Chord* chord = note->chord(); + const Fraction chordStartTick = chord->tick(); + const auto& durations = bendDataCtx.bendChordDurations; + + size_t currentIndex = startIndex; + if (durations.find(chord->track()) == durations.end()) { + LOGE() << "bend import error : no information about chord duration for track " << chord->track(); + return; + } + + const auto& trackDurations = durations.at(chord->track()); + if (trackDurations.find(chordStartTick.ticks()) == trackDurations.end()) { + LOGE() << "bend import error : no information about chord duration for track " << chord->track() << " tick " << + chordStartTick.ticks(); + return; + } + + const auto& tickDurations = trackDurations.at(chordStartTick.ticks()); + + Fraction currentTick = chordStartTick; + + for (const auto& tickDuration : tickDurations) { + if (currentIndex >= importedInfo.segments.size()) { + break; + } + + const auto& seg = importedInfo.segments[currentIndex]; + + BendDataContext::BendChordData& bendChordData = bendDataCtx.bendDataByEndTick[note->track()][(currentTick + tickDuration).ticks()]; + bendChordData.startTick = currentTick; + BendDataContext::BendNoteData bendNoteData; + bendNoteData.type = GuitarBendType::BEND; + bendNoteData.quarterTones = seg.pitchDiff / 25; + bendChordData.noteDataByPitch[note->pitch()] = std::move(bendNoteData); + + currentTick += tickDuration; + currentIndex++; + } +} + +static void addFullChordDuration(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) +{ + const Chord* chord = importedInfo.note->chord(); + Fraction tick = chord->tick(); std::vector bendDurations; - Fraction duration = note->chord()->actualTicks(); + Fraction duration = chord->actualTicks(); bendDurations.push_back(duration); + bendDataCtx.bendChordDurations[chord->track()][tick.ticks()] = std::move(bendDurations); +} + +static int getMaxDenominatorForSplit(const Fraction& duration) +{ + int denominator = 1; + + while (Fraction(1, denominator * 2) > duration) { + denominator *= 2; + } - bendDataCtx.bendChordDurations[note->track()][note->tick().ticks()] = std::move(bendDurations); + return denominator * 2; } -static void fillNormalBendDataContext(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo, - size_t startIndex) +static std::vector splittedDurations(const BendDataCollector::ImportedBendInfo& importedInfo, size_t startIndex) { - // TODO: fill chords durations in proportion to bend diagram - if (startIndex >= importedInfo.segments.size()) { - return; + const Note* note = importedInfo.note; + const Chord* chord = note->chord(); + const auto& bendSegments = importedInfo.segments; + + std::vector proportions; + + // TODO: Fraction duration = chord->endTickIncludingTied() - chord->tick(); + Fraction duration = chord->actualTicks(); + const int maxDenominator = getMaxDenominatorForSplit(duration / 4); + + for (size_t i = startIndex; i < bendSegments.size(); i++) { + const auto& seg = bendSegments[i]; + if (seg.endTime != seg.startTime) { + Fraction targetProportion(seg.endTime - seg.startTime, BEND_DIVISIONS); + targetProportion.reduce(); + proportions.push_back(targetProportion); + } } - const auto& firstSeg = importedInfo.segments.at(startIndex); - if (firstSeg.pitchDiff == 0) { + Fraction proportionsSum = std::accumulate(proportions.begin(), proportions.end(), Fraction(0, 1)); + + if (proportionsSum < Fraction(1, 1)) { + Fraction lastProportion = Fraction(1, 1) - proportionsSum; + proportions.push_back(lastProportion); + } + + return BendChordDurationSplitter::findValidNoteSplit(chord->actualTicks(), proportions, maxDenominator); +} + +static void splitBendChordDurations(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo, + size_t startIndex) +{ + if (startIndex >= importedInfo.segments.size()) { return; } + const Chord* chord = importedInfo.note->chord(); + bendDataCtx.bendChordDurations[chord->track()][chord->tick().ticks()] = splittedDurations(importedInfo, startIndex); +} + +static void fillChordDurationsFromBendDiagram(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) +{ const Note* note = importedInfo.note; - Fraction tick = note->tick(); - BendDataContext::BendChordData& bendChordData = bendDataCtx.bendDataByEndTick[note->track()][note->chord()->actualTicks().ticks()]; - bendChordData.startTick = tick; + IF_ASSERT_FAILED(note) { + LOGE() << "couldn't fill chord durations: note is NULL"; + return; + } - BendDataContext::BendNoteData bendNoteData; - bendNoteData.type = GuitarBendType::BEND; - bendNoteData.quarterTones = firstSeg.pitchDiff / 25; + const auto& bendSegments = importedInfo.segments; + IF_ASSERT_FAILED(!bendSegments.empty()) { + LOGE() << "couldn't fill chord durations: bend segments are empty"; + return; + } - bendChordData.noteDataByPitch[note->pitch()] = std::move(bendNoteData); + if (isSlightBend(importedInfo)) { + addFullChordDuration(bendDataCtx, importedInfo); + return; + } - std::vector bendDurations; - Fraction duration = note->chord()->actualTicks(); - bendDurations.push_back(duration); + size_t startIndex = 0; - // currently not adding new chords - so checking if END chord for new bend already exists - // it's a test code - { - const Score* score = note->score(); - Fraction endChordTick = note->tick() + duration; - const Measure* endChordMeasure = score->tick2measure(endChordTick); - - if (endChordMeasure) { - const Chord* endChord = endChordMeasure->findChord(endChordTick, note->track()); - if (endChord) { - bendDurations.push_back(endChord->actualTicks()); - } + if (isFirstPrebend(importedInfo)) { + addFullChordDuration(bendDataCtx, importedInfo); + startIndex = 1; + if (importedInfo.segments.size() > 1 && importedInfo.segments[1].pitchDiff == 0) { + startIndex = 2; } } - bendDataCtx.bendChordDurations[note->track()][note->tick().ticks()] = std::move(bendDurations); + splitBendChordDurations(bendDataCtx, importedInfo, startIndex); } -static void fillBendDataContextForNote(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) +static void fillBendDataForNote(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) { const Note* note = importedInfo.note; IF_ASSERT_FAILED(note) { @@ -273,19 +378,21 @@ static void fillBendDataContextForNote(BendDataContext& bendDataCtx, const BendD return; } - std::vector bendDurations; if (isSlightBend(importedInfo)) { - fillSlightBendDataContext(bendDataCtx, importedInfo); + fillSlightBendData(bendDataCtx, importedInfo); return; } size_t startIndex = 0; if (isFirstPrebend(importedInfo)) { - fillPrebendDataContext(bendDataCtx, importedInfo); + fillPrebendData(bendDataCtx, importedInfo); startIndex = 1; + if (importedInfo.segments.size() > 1 && importedInfo.segments[1].pitchDiff == 0) { + startIndex = 2; + } } - fillNormalBendDataContext(bendDataCtx, importedInfo, startIndex); + fillNormalBendData(bendDataCtx, importedInfo, startIndex); } } // namespace mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h index 4e465e0d58fd2..372f58ef99332 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h @@ -55,6 +55,6 @@ class BendDataCollector }; private: - std::unordered_map > > m_bendInfoForNote; + std::unordered_map > > m_bendInfoForNote; }; } // mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp b/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp index 1a6992e812d6c..2bf6037d39905 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp @@ -33,7 +33,7 @@ using namespace mu::engraving; namespace mu::iex::guitarpro { -static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engraving::Chord* chord); +static void createGuitarBends(const BendDataContext& bendDataCtx, mu::engraving::Chord* chord); BendDataProcessor::BendDataProcessor(mu::engraving::Score* score) : m_score(score) @@ -57,73 +57,82 @@ void BendDataProcessor::processBends(const BendDataContext& bendDataCtx) return; } - const Chord* mainChord = mainChordMeasure->findChord(mainTickFr, track); - + Chord* mainChord = mainChordMeasure->findChord(mainTickFr, track); if (!mainChord) { LOGE() << "bend import error : no valid chord for track " << track << ", tick " << mainTick; return; } + Fraction newMainChordDuration = chordsDurations.front(); + mainChord->setTicks(newMainChordDuration); + mainChord->setDurationType(TDuration(newMainChordDuration)); Fraction currentTick = mainChord->tick() + mainChord->ticks(); createGuitarBends(bendDataCtx, mainChord); for (size_t i = 1; i < chordsDurations.size(); i++) { - const Measure* currentMeasure = m_score->tick2measure(currentTick); - const Segment* curSegment = currentMeasure->findSegment(SegmentType::ChordRest, currentTick); + Measure* currentMeasure = m_score->tick2measure(currentTick); + if (!currentMeasure) { + LOGE() << "bend import error : no valid measure for track " << track << ", tick " << currentTick; + return; + } + Segment* curSegment = currentMeasure->findSegment(SegmentType::ChordRest, currentTick); + Chord* currentChord = nullptr; + Fraction currentChordDuration = chordsDurations[i]; if (curSegment) { - // adding bend for existing chord - const Measure* startMeasure = m_score->tick2measure(currentTick); - - if (!startMeasure) { - LOGE() << "bend import error : no valid measure for track " << track << ", tick " << currentTick; - return; - } - - const Chord* existingChord = startMeasure->findChord(currentTick, track); - - if (!existingChord) { + currentChord = currentMeasure->findChord(currentTick, track); + if (!currentChord) { LOGE() << "bend import error : no valid chord for track " << track << ", tick " << currentTick; return; } - - createGuitarBends(bendDataCtx, existingChord); - currentTick += existingChord->ticks(); } else { - // TODO: create new segment and new chord on it - LOGE() << "bend wasn't added!"; + curSegment = currentMeasure->getSegment(SegmentType::ChordRest, currentTick); + curSegment->setTicks(currentChordDuration); + + currentChord = Factory::createChord(m_score->dummy()->segment()); + currentChord->setTrack(track); + currentChord->setTicks(currentChordDuration); + currentChord->setDurationType(currentChordDuration); + curSegment->add(currentChord); + + for (Note* note : mainChord->notes()) { + Note* newChordNote = Factory::createNote(currentChord); + newChordNote->setTrack(track); + currentChord->add(newChordNote); + newChordNote->setPitch(note->pitch()); + newChordNote->setTpcFromPitch(); + } } + + createGuitarBends(bendDataCtx, currentChord); + currentTick += currentChordDuration; } } } } -static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engraving::Chord* chord) +static void createGuitarBends(const BendDataContext& bendDataCtx, mu::engraving::Chord* chord) { Score* score = chord->score(); int chordTicks = chord->tick().ticks(); if (bendDataCtx.bendDataByEndTick.find(chord->track()) == bendDataCtx.bendDataByEndTick.end()) { - LOGE() << "bend import error: bends data on track " << chord->track() << " doesn't exist"; return; } const auto& currentTrackData = bendDataCtx.bendDataByEndTick.at(chord->track()); if (currentTrackData.find(chordTicks) == currentTrackData.end()) { - LOGE() << "bend import error: bends data on track " << chord->track() << " doesn't exist for tick " << chordTicks; return; } const BendDataContext::BendChordData& bendChordData = currentTrackData.at(chordTicks); const Measure* startMeasure = score->tick2measure(bendChordData.startTick); - if (!startMeasure) { LOGE() << "bend import error : no valid measure for track " << chord->track() << ", tick " << bendChordData.startTick.ticks(); return; } - const Chord* startChord = startMeasure->findChord(bendChordData.startTick, chord->track()); - + Chord* startChord = startMeasure->findChord(bendChordData.startTick, chord->track()); if (!startChord) { LOGE() << "bend import error : no valid chord for track " << chord->track() << ", tick " << bendChordData.startTick.ticks(); return; @@ -146,13 +155,16 @@ static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engr return l->pitch() < r->pitch(); }); + auto bendInfoIt = bendChordData.noteDataByPitch.begin(); for (size_t noteIndex = 0; noteIndex < endChordNotes.size(); noteIndex++) { Note* note = endChordNotes[noteIndex]; - if (bendChordData.noteDataByPitch.find(note->pitch()) == bendChordData.noteDataByPitch.end()) { - continue; + + if (bendChordData.noteDataByPitch.size() <= noteIndex) { + LOGE() << "bend import error : bend data filled incorrectly, not all bends will be created"; + return; } - const BendDataContext::BendNoteData& bendNoteData = bendChordData.noteDataByPitch.at(note->pitch()); + const BendDataContext::BendNoteData& bendNoteData = bendInfoIt->second; Note* startNote = startChordNotes[noteIndex]; int pitch = bendNoteData.quarterTones / 2; @@ -212,6 +224,8 @@ static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engr tiedNote = tie->endNote(); } + + bendInfoIt = std::next(bendInfoIt); } } } // namespace mu::iex::guitarpro diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1-gp.mscx new file mode 100644 index 0000000000000..2a6dd93b737c9 --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1-gp.mscx @@ -0,0 +1,315 @@ + + + + 480 + 1 + 1 + 1 + 0 + + + + + + + + + + + + + 2 + + stdNormal + + + + 1 + + tab6StrSimple + 6 + 1.5 + 1 + 1 + 0 + 0 + MuseScore Tab Modern + 15 + 0 + MuseScore Tab Sans + 9 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + + + Electric Guitar + + Electric Guitar + el.guit. + + -7 + -12 + pluck.guitar.electric + 0 + + 24 + 52 + 57 + 62 + 67 + 71 + 76 + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + 0 + major + + + 4 + 4 + + + mf + 80 + + + 2 + metNoteQuarterUp = 120 + + + 38 + 38 + + + + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + + 16th + + + 62 + 16 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + -1/16 + + + + + + + + eighth + + + 60 + 14 + 1 + 1 + + + + -1/16 + + + + + + + + quarter + + + + half + + + + + + + + + + + 0 + major + + + + + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + -1/16 + + + + + + + + + eighth + + + + 1 + + noteheadParenthesisLeft + + + noteheadParenthesisRight + + 60 + 14 + 1 + 1 + 1 + + + + -1/16 + + + + + + + + + quarter + + + + + half + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend+release_1.gp new file mode 100644 index 0000000000000000000000000000000000000000..409eeed431838de466a5445ef826add2ff050717 GIT binary patch literal 8644 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 literal 0 HcmV?d00001 diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1-gp.mscx new file mode 100644 index 0000000000000..be2ca4c7b11cb --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1-gp.mscx @@ -0,0 +1,606 @@ + + + + 480 + 1 + 1 + 1 + 0 + + + + + + + + + + + + + 2 + + stdNormal + + + + 1 + + tab6StrSimple + 6 + 1.5 + 1 + 1 + 0 + 0 + MuseScore Tab Modern + 15 + 0 + MuseScore Tab Sans + 9 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + + + Electric Guitar + + Electric Guitar + el.guit. + + -7 + -12 + pluck.guitar.electric + 0 + + 24 + 52 + 57 + 62 + 67 + 71 + 76 + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + 0 + major + + + 4 + 4 + + + mf + 80 + + + 2 + metNoteQuarterUp = 120 + + + 34 + 32 + + + + eighth + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/8 + + + + + + + + eighth + + + 62 + 16 + 1 + 1 + + + + -1/8 + + + + + + + 38 + 36 + + + + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + + 1 + eighth + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + 38 + 36 + + + + 1 + eighth + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 3/16 + + + + + + + + 16th + + + 62 + 16 + 1 + 1 + + + + -3/16 + + + + + + + 44 + 44 + + + + 1 + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 3/32 + + + + + + + + 32nd + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + 32nd + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/32 + + + + + + + + 1 + 16th + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + + + + + + + + + 0 + major + + + + + eighth + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/8 + + + + + + + + + eighth + + + + 62 + 16 + 1 + 1 + + + + -1/8 + + + + + + + + + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + + + 1 + eighth + + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + + 1 + eighth + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 3/16 + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + + -3/16 + + + + + + + + + 1 + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 3/32 + + + + + + + + + 32nd + + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + + 32nd + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/32 + + + + + + + + + 1 + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_1.gp new file mode 100644 index 0000000000000000000000000000000000000000..211fd8a2559c58db69e5876895c0662abf7df6e6 GIT binary patch literal 8813 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 literal 0 HcmV?d00001 diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2-gp.mscx new file mode 100644 index 0000000000000..7e70116037d4a --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2-gp.mscx @@ -0,0 +1,891 @@ + + + + 480 + 1 + 1 + 1 + 0 + + + + + + + + + + + + + 2 + + stdNormal + + + + 1 + + tab6StrSimple + 6 + 1.5 + 1 + 1 + 0 + 0 + MuseScore Tab Modern + 15 + 0 + MuseScore Tab Sans + 9 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + + + Electric Guitar + + Electric Guitar + el.guit. + + -7 + -12 + pluck.guitar.electric + 0 + + 24 + 52 + 57 + 62 + 67 + 71 + 76 + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + 0 + major + + + 4 + 4 + + + mf + 80 + + + 2 + metNoteQuarterUp = 120 + + + + no + eighth + + 1 + + + + noteheadParenthesisLeft + + + noteheadParenthesisRight + + 60 + 14 + 1 + 1 + + + 1 + 0 + 1 + + 3 + + + + + + + + + + 44 + 44 + + + + eighth + + + 62 + 16 + 1 + 1 + + + + 0 + + + + + + + + 32nd + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/32 + + + + + + + + 1 + 16th + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + 44 + 44 + + + + 32nd + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/32 + + + + + + + + 1 + 16th + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + + 16th + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + 38 + 38 + + + + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + + 16th + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + 1 + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 3/32 + + + + + + + + 32nd + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + 38 + 38 + + + + 1 + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 3/32 + + + + + + + + 32nd + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + 1 + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 3/32 + + + + + + + + 32nd + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + + + + + + + + 0 + major + + + + + no + eighth + + 1 + + + + 60 + 14 + 1 + 1 + + + 1 + 0 + 1 + + + 3 + + + + + + + + + + + + eighth + + + + 62 + 16 + 1 + 1 + + + + 0 + + + + + + + + + 32nd + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/32 + + + + + + + + + 1 + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + + + 32nd + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/32 + + + + + + + + + 1 + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/32 + + + + + + + + + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + + 1 + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 3/32 + + + + + + + + + 32nd + + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + + 1 + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 3/32 + + + + + + + + + 32nd + + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + + 1 + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 3/32 + + + + + + + + + 32nd + + + + 62 + 16 + 1 + 1 + + + + -3/32 + + + + + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_2.gp new file mode 100644 index 0000000000000000000000000000000000000000..67df17c5dc060009cc8228e5e317f2f396903e2e GIT binary patch literal 8888 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 + + + 480 + 1 + 1 + 1 + 0 + + + + + + + + + + + + + 2 + + stdNormal + + + + 1 + + tab6StrSimple + 6 + 1.5 + 1 + 1 + 0 + 0 + MuseScore Tab Modern + 15 + 0 + MuseScore Tab Sans + 9 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + + + Electric Guitar + + Electric Guitar + el.guit. + + -7 + -12 + pluck.guitar.electric + 0 + + 24 + 52 + 57 + 62 + 67 + 71 + 76 + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + 0 + major + + + 6 + 4 + + + mf + 80 + + + 2 + metNoteQuarterUp = 120 + + + 38 + 36 + + + + eighth + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/8 + + + + + + + + 16th + + + 62 + 16 + 1 + 1 + + + + -1/8 + + + + + + + + 16th + + + + quarter + + + + half + + + + half + + + + + + + + + + 38 + 36 + + + + 16th + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + + eighth + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + 16th + + + + quarter + + + + half + + + + half + + + + + + + + + + + 0 + major + + + + + eighth + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/8 + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + + -1/8 + + + + + + + + + 16th + + + + + quarter + + + + + half + + + + + half + + + + + + + + + + + + + 16th + + + + 60 + 14 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + + + eighth + + + + 62 + 16 + 1 + 1 + + + + -1/16 + + + + + + + + + 16th + + + + + quarter + + + + + half + + + + + half + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_3.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/bend_3.gp new file mode 100644 index 0000000000000000000000000000000000000000..2f0d94e3a98653224e511c4ed94af3ff251bc960 GIT binary patch literal 8903 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 BPU`>w literal 0 HcmV?d00001 diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1-gp.mscx new file mode 100644 index 0000000000000..fa82509e3f441 --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1-gp.mscx @@ -0,0 +1,327 @@ + + + + 480 + 1 + 1 + 1 + 0 + + + + + + + + + + + + + 2 + + stdNormal + + + + 1 + + tab6StrSimple + 6 + 1.5 + 1 + 1 + 0 + 0 + MuseScore Tab Modern + 15 + 0 + MuseScore Tab Sans + 9 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 0 + 1 + + + Electric Guitar + + Electric Guitar + el.guit. + + -7 + -12 + pluck.guitar.electric + 0 + + 24 + 52 + 57 + 62 + 67 + 71 + 76 + + + + + + + + + + 10 + + + + + + + + + + + + + + + + + + 0 + major + + + 4 + 4 + + + mf + 80 + + + 2 + metNoteQuarterUp = 120 + + + + no + eighth + + 1 + + + + noteheadParenthesisLeft + + + noteheadParenthesisRight + + 60 + 14 + 1 + 1 + + + 1 + 0 + 1 + + 3 + + + + + + + + + + 32 + 30 + + + + 16th + + + 62 + 16 + 1 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/16 + + + + + + + 0 + + + + + + + + 1 + eighth + + + + 1 + accidentalFlat + + 63 + 11 + 3 + 1 + + + + -1/16 + + + + + + + + quarter + + + + half + + + + + + + + + + + 0 + major + + + + + no + eighth + + 1 + + + + 60 + 14 + 1 + 1 + + + 1 + 0 + 1 + + + 3 + + + + + + + + + + + + 16th + + + + 62 + 16 + 1 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/16 + + + + + + + 0 + + + + + + + + + 1 + eighth + + + + + 1 + accidentalFlat + + 63 + 11 + 1 + 1 + + + + -1/16 + + + + + + + + + quarter + + + + + half + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/prebend+bend_1.gp new file mode 100644 index 0000000000000000000000000000000000000000..e294bbbb03a9a5612d729d0302ab158847521926 GIT binary patch literal 8685 zcmaKybx<8ko5qpg7Th)1!3hq*-3jjQ?r?Am7UUqoJ-E9D*MpPb9^Bn^!*1Q(yLb2d z-l?vcwx_!1nLmE>Rx8Ouzr%v~{{sF`qo{+utC_tkv(kT#f`@o(``>~0r~20@5i5Hm zXP}BJ(ALbw(#*^?Q&-l3NW;C-K1PycAzoU==d0@BR4qG6SR6NG5ZQ;CbhJ1~0LD?0 zl{dPa+wJj5WN8_%+6=`K{d2%O_#m?Ic0>@vN9llfHMn&tUvRa!1CUL*(iwut${2#Q z`m|t_#(ihon-s8@>f-t;2l;P5b)53;a&B{Pf9H$tL*ccb1`<%)Z+z+Y$^dl-;(|hN z0X(h~?cJ72t4}4H%l55|Yw{B4pPSr!i{%v_HuD9~7Tr8!H4Jc#bFy{5Vz-{+kw`OkZk4y(9)szhTxFh~6`)pgD&NjTEk zvmUfh^sHPXI|k_!{mQHs-|hdZyM{J_D(SZ&UafwVm_%suu-=N>M=7)SjN&1b7PsVw zddOXX^kWNVR5sRtcU*aAyzhg~_dOVZV9=nE(R|MLR-7RUsD9oFL&C#RKvslaL`Qx~v@4{cjRK{lxoQ}Z? zZkf+B#Z2LLCmA%9dmz;2P*Rb5Fb7%|O{=Cn>Hm zq4tD9x_+)_2ZZ<;A)%XqzHOGHfVJQ(#bl2RkNyNOZ2;QgZ=S$UJ{G$$2($a7%aH&J z^}E)e1RA9c2|Iq@wK8X)Pnsm4i|Q#>-9Z!A@!;lk{206bI4$htgg4zlX>39 za7L;{x5cR9CtEQ^L$+)cJ&s#)uzFyxDvdq^_oYr>6k-;rIfP7Ni?$) zo%v-^k87}?^U?Uh`+N6f_?{a2lq25HX=7~W1iDbMEpb0hzo&1Ta+lr-=pp=Z9(+w- zm^Uad7QvtEQM!IANLdK(;C3GPFSw<%{bJYvjZL;n# z;K>#2!-lYi5WuEA2lGeoUDPMRvh)oJZG#G-uNJ!rf@QQ&^-BHC81+s1OAYam#Cug+BS z{luw6$mI0QhWmZ2vhIdsX20e66*``x{Ld`E%R!7T9i_o2H{I;yWkvn=hn>h}j1M5d zu6%^^*Ntx(dZGDqmp$58Uo7_Z(~*= zO0_ynm5tEcFRbt=D-CE)$QZ+M$fTGNB_YF_WRC@aj7TgS(imBB;kmm|+fA^6P`ac& zooT<%-m@$s4L6WiS7nl#o2v1MXPw%_v~NwE)yCinIVS(?uMEG%b{dK=_mvrs%%f%F zE&yM?kh7f;;{$#YTzg@$Y4p>4PdRfVsLqAOZ^erJ{orkU-QiqO`#K@O*Lc{LJaWj}RmHf#we; zV|kJxuCMYX`IDpvp~-%ok)?*iX|BJ z1sKJFEAyfd$B8cfqDA9fA1P@*WXf3Ep!!j~9nAGxDFhO2OCGym@5&|==CM!w+fzV6 z8x;CyJ(f%{f>I6}x1__l>tmO*N#ZjO>dt%-%*h-8P=ZVqJcoKPfm)_~#VJt{G8=~= z^ovtKp2tD0DJDixV8Oq3u6*1HD{?0*T0z@pb?Mzlh0<+vurx z%($@d>?Wy6pRpCFY8cq|3yqshWzTSvj$@()nx z46B)Yamv&6vU!2&&1n^acl#N%rfDHotVP#~NZex`SJJzr`TdoGM{OvG=hbznOHo^V zvRDr0Z7_>K+gM>@Pdlkss&YQ#tKN_HQ$GoJm~d;AixkMLn7SM0MyP+y_EI9i#oN-C z+@>vjGGJfsJnk1=BOalo?d-RufShJ5-ViTSVPi~mAPDIS(CL`-L(L&0fZ9D&`w)=V z&i=U}FDO1EfN@MO0uP4pOlKwy!ifXj z)Rh4&1cwf6o*LMDf+Sv*af-(l=O$y>MIFGRxOB{baz7p$rS}UeNe`^HJ1>0g> z|2aaos2(f3M+S`i4G|MxNXl&{dYr-q!c#E%x4S-8-Z*}P-c#a`rNj` zKEebio*?0X?R;HKxG7Wr4K&CY`UZWyH;WLZCqTi}9Qd=oPbq2FyyzE@pHUW1GsXYm zxM{5;X~=71pdB46d6ic8VNNUI$AV+n{;ZBxo256F=qrpn8z$NO&aXiTU>tpLt6R_* zOIyd13dh;q`V3b%G_Njv^>qwXB+yIZ13+Sz$k=;)*x$-oICS@9UJK51OsPds?U!&5k!i41ZcI`Rnn|o+nRH9wJD0Z+aP{h|i)|!P9WKIoae?r6NbuE}wuG@w2>sB&4pin+$ z*k3X76j=|t4a#>$L*A;0I%t2tC_1CR3BdF&YZVwmoR%;C?NO&P}+6!`NNqup;&dL4zsf4IW)^l&?GoMQj(~1{Lf@+EF3bfo7Sanwkia;?Y zHV4(Fa5kHWMY)m^rbK=1ed%$LYfbivW7plnbopfgc_6vxH zJD(UyCAN~MuSWoY02%sc-P+uVqvNYBtX-^esLB~q?Nyn)vldo+K2S0b$z)?_p?=!Z zL0?EhzP_M8YXVQb8O025@`gp`LG%SV0g1wV7moQksd$zu)=?Z-?IJc668^jaVDrll zf-Vzoh)f_~hES;>iY9P|_A?aU0seRl(bc=orQ>nyI@Sfk0ywQAdjrMD*i{Q%0Dvd+ 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