From bd673e8d8083da0c3d3c4c1ed992faccf5f08efa Mon Sep 17 00:00:00 2001 From: Alexander Pavlov Date: Mon, 23 Dec 2024 15:00:42 +0200 Subject: [PATCH] guitar bend import: part 2 - treating separate notes of chord --- .../guitarbendimport/benddatacollector.cpp | 83 ++-- .../guitarbendimport/benddatacollector.h | 3 +- .../guitarbendimport/benddatacontext.h | 10 +- .../guitarbendimport/benddataprocessor.cpp | 146 ++++--- .../slight_bend_chord-gp.mscx | 316 +++++++++++++++ .../slight_bend_chord.gp | Bin 0 -> 8757 bytes .../tied_bend_chord-gp.mscx | 373 ++++++++++++++++++ .../tied_bend_chord.gp | Bin 0 -> 8846 bytes .../tests/guitarbendimporter_tests.cpp | 8 + 9 files changed, 828 insertions(+), 111 deletions(-) create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord.gp create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord-gp.mscx create mode 100644 src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord.gp diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp index f4840f1cc5c53..c678b943e8154 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.cpp @@ -38,7 +38,7 @@ BendDataCollector::ImportedBendInfo fillBendInfo(const Note* note, const PitchVa void BendDataCollector::storeBendData(mu::engraving::Note* note, const mu::engraving::PitchValues& pitchValues) { if (!pitchValues.empty()) { - m_bendInfoForNote[note->track()][note->tick().ticks()] = fillBendInfo(note, pitchValues); + m_bendInfoForNote[note->track()][note->tick().ticks()][note->pitch()] = fillBendInfo(note, pitchValues); } } @@ -47,8 +47,10 @@ BendDataContext BendDataCollector::collectBendDataContext() BendDataContext bendDataCtx; for (const auto& [track, trackInfo] : m_bendInfoForNote) { - for (const auto& [tick, importedBendInfo] : trackInfo) { - fillBendDataContextForNote(bendDataCtx, importedBendInfo); + for (const auto& [tick, tickInfo] : trackInfo) { + for (const auto& [pitch, importedBendInfo] : tickInfo) { + fillBendDataContextForNote(bendDataCtx, importedBendInfo); + } } } @@ -126,27 +128,8 @@ std::vector bendSegmentsFromPitchValues(const Pi BendDataCollector::ImportedBendInfo fillBendInfo(const Note* note, const PitchValues& pitchValues) { - PitchValues adaptedPitchValues; - adaptedPitchValues.reserve(pitchValues.size() + 2); - - if (pitchValues.front().time != 0) { - PitchValue firstPv = pitchValues.front(); - firstPv.time = 0; - adaptedPitchValues.push_back(firstPv); - } - - for (const auto& pv : pitchValues) { - adaptedPitchValues.push_back(pv); - } - - if (pitchValues.back().time != BEND_DIVISIONS) { - PitchValue lastPv = pitchValues.back(); - lastPv.time = BEND_DIVISIONS; - adaptedPitchValues.push_back(lastPv); - } - BendDataCollector::ImportedBendInfo info; - info.segments = bendSegmentsFromPitchValues(adaptedPitchValues, note->tieBack()); + info.segments = bendSegmentsFromPitchValues(pitchValues, note->tieBack()); info.note = note; for (const auto& bs : info.segments) { @@ -160,7 +143,7 @@ BendDataCollector::ImportedBendInfo fillBendInfo(const Note* note, const PitchVa static bool isSlightBend(const BendDataCollector::ImportedBendInfo& importedInfo) { - if (importedInfo.pitchChangesAmount != 1 || importedInfo.note->tieFor() || importedInfo.segments.size() != 2) { + if (importedInfo.pitchChangesAmount != 1 || importedInfo.note->tieFor()) { return false; } @@ -185,18 +168,21 @@ void fillSlightBendDataContext(BendDataContext& bendDataCtx, const BendDataColle bendDataCtx.bendChordDurations[note->track()][tick.ticks()] = std::move(bendDurations); - BendDataContext::BendData slightBendData; - slightBendData.quarterTones = 1; + BendDataContext::BendChordData& slightBendChordData = bendDataCtx.bendDataByEndTick[note->track()][tick.ticks()]; + slightBendChordData.startTick = tick; + + BendDataContext::BendNoteData slightBendNoteData; + slightBendNoteData.quarterTones = 1; const auto& firstSeg = importedInfo.segments.front(); if (firstSeg.middleTime != -1) { - slightBendData.startFactor = (double)firstSeg.middleTime / BEND_DIVISIONS; + slightBendNoteData.startFactor = (double)firstSeg.middleTime / BEND_DIVISIONS; } - slightBendData.endFactor = (double)(firstSeg.endTime + 1) / BEND_DIVISIONS; - slightBendData.type = GuitarBendType::SLIGHT_BEND; - slightBendData.startTick = tick; - bendDataCtx.bendDataByEndTick[note->track()][tick.ticks()] = std::move(slightBendData); + slightBendNoteData.endFactor = (double)(firstSeg.endTime + 1) / BEND_DIVISIONS; + slightBendNoteData.type = GuitarBendType::SLIGHT_BEND; + + slightBendChordData.noteDataByPitch[note->pitch()] = std::move(slightBendNoteData); } static bool isFirstPrebend(const BendDataCollector::ImportedBendInfo& importedInfo) @@ -208,14 +194,17 @@ static bool isFirstPrebend(const BendDataCollector::ImportedBendInfo& importedIn static void fillPrebendDataContext(BendDataContext& bendDataCtx, const BendDataCollector::ImportedBendInfo& importedInfo) { const Note* note = importedInfo.note; - BendDataContext::BendData prebendData; + Fraction tick = note->tick(); + BendDataContext::BendChordData& prebendChordData = bendDataCtx.bendDataByEndTick[note->track()][tick.ticks()]; + prebendChordData.startTick = tick; const auto& firstSeg = importedInfo.segments.front(); - prebendData.type = GuitarBendType::PRE_BEND; - prebendData.startTick = note->tick(); - prebendData.quarterTones = firstSeg.pitchDiff / 25; - bendDataCtx.bendDataByEndTick[note->track()][note->tick().ticks()] = std::move(prebendData); + BendDataContext::BendNoteData prebendNoteData; + prebendNoteData.type = GuitarBendType::PRE_BEND; + prebendNoteData.quarterTones = firstSeg.pitchDiff / 25; + + prebendChordData.noteDataByPitch[note->pitch()] = std::move(prebendNoteData); std::vector bendDurations; Fraction duration = note->chord()->actualTicks(); @@ -228,19 +217,25 @@ static void fillNormalBendDataContext(BendDataContext& bendDataCtx, const BendDa size_t startIndex) { // TODO: fill chords durations in proportion to bend diagram - const Note* note = importedInfo.note; - BendDataContext::BendData bendData; - - if (startIndex >= importedInfo.segments.size() - 1) { + if (startIndex >= importedInfo.segments.size()) { return; } const auto& firstSeg = importedInfo.segments.at(startIndex); - bendData.type = GuitarBendType::BEND; - bendData.startTick = note->tick(); - bendData.quarterTones = firstSeg.pitchDiff / 25; + if (firstSeg.pitchDiff == 0) { + return; + } + + const Note* note = importedInfo.note; + Fraction tick = note->tick(); + BendDataContext::BendChordData& bendChordData = bendDataCtx.bendDataByEndTick[note->track()][note->chord()->actualTicks().ticks()]; + bendChordData.startTick = tick; + + BendDataContext::BendNoteData bendNoteData; + bendNoteData.type = GuitarBendType::BEND; + bendNoteData.quarterTones = firstSeg.pitchDiff / 25; - bendDataCtx.bendDataByEndTick[note->track()][note->chord()->actualTicks().ticks()] = std::move(bendData); + bendChordData.noteDataByPitch[note->pitch()] = std::move(bendNoteData); std::vector bendDurations; Fraction duration = note->chord()->actualTicks(); diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h index ff092103a8254..4e465e0d58fd2 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddatacollector.h @@ -55,7 +55,6 @@ class BendDataCollector }; private: - // todo: here storing 1 note for "track+tick". TODO: adapt for chord - std::unordered_map > m_bendInfoForNote; + std::unordered_map > > m_bendInfoForNote; }; } // mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddatacontext.h b/src/importexport/guitarpro/internal/guitarbendimport/benddatacontext.h index 0511e8e448403..026fbc86be95e 100644 --- a/src/importexport/guitarpro/internal/guitarbendimport/benddatacontext.h +++ b/src/importexport/guitarpro/internal/guitarbendimport/benddatacontext.h @@ -31,15 +31,19 @@ class Chord; namespace mu::iex::guitarpro { struct BendDataContext { - struct BendData { - mu::engraving::Fraction startTick; + struct BendNoteData { double startFactor = 0.0; double endFactor = 1.0; int quarterTones = 0; mu::engraving::GuitarBendType type = mu::engraving::GuitarBendType::BEND; }; + struct BendChordData { + mu::engraving::Fraction startTick; + std::map noteDataByPitch; + }; + std::unordered_map > > bendChordDurations; - std::unordered_map > bendDataByEndTick; + std::unordered_map > bendDataByEndTick; }; } // mu::iex::guitarpro diff --git a/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp b/src/importexport/guitarpro/internal/guitarbendimport/benddataprocessor.cpp index 72ee754aa5580..1a6992e812d6c 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, mu::engraving::Note* note); +static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engraving::Chord* chord); BendDataProcessor::BendDataProcessor(mu::engraving::Score* score) : m_score(score) @@ -65,7 +65,7 @@ void BendDataProcessor::processBends(const BendDataContext& bendDataCtx) } Fraction currentTick = mainChord->tick() + mainChord->ticks(); - createGuitarBends(bendDataCtx, mainChord->upNote()); + createGuitarBends(bendDataCtx, mainChord); for (size_t i = 1; i < chordsDurations.size(); i++) { const Measure* currentMeasure = m_score->tick2measure(currentTick); @@ -87,8 +87,7 @@ void BendDataProcessor::processBends(const BendDataContext& bendDataCtx) return; } - Note* existingNote = existingChord->upNote(); // TODO: different notes - createGuitarBends(bendDataCtx, existingNote); + createGuitarBends(bendDataCtx, existingChord); currentTick += existingChord->ticks(); } else { // TODO: create new segment and new chord on it @@ -99,13 +98,11 @@ void BendDataProcessor::processBends(const BendDataContext& bendDataCtx) } } -static void createGuitarBends(const BendDataContext& bendDataCtx, mu::engraving::Note* note) +static void createGuitarBends(const BendDataContext& bendDataCtx, const mu::engraving::Chord* chord) { - Score* score = note->score(); - const Chord* chord = toChord(note->parent()); + Score* score = chord->score(); int chordTicks = chord->tick().ticks(); - // TODO: distinguish notes from same chord if (bendDataCtx.bendDataByEndTick.find(chord->track()) == bendDataCtx.bendDataByEndTick.end()) { LOGE() << "bend import error: bends data on track " << chord->track() << " doesn't exist"; return; @@ -117,79 +114,104 @@ static void createGuitarBends(const BendDataContext& bendDataCtx, mu::engraving: return; } - const BendDataContext::BendData& bendData = currentTrackData.at(chordTicks); - const Measure* startMeasure = score->tick2measure(bendData.startTick); + 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 " << bendData.startTick.ticks(); + LOGE() << "bend import error : no valid measure for track " << chord->track() << ", tick " << bendChordData.startTick.ticks(); return; } - const Chord* startChord = startMeasure->findChord(bendData.startTick, chord->track()); + const Chord* startChord = startMeasure->findChord(bendChordData.startTick, chord->track()); if (!startChord) { - LOGE() << "bend import error : no valid chord for track " << chord->track() << ", tick " << bendData.startTick.ticks(); + LOGE() << "bend import error : no valid chord for track " << chord->track() << ", tick " << bendChordData.startTick.ticks(); return; } - Note* startNote = startChord->upNote(); // TODO: separate notes in the same chord - int pitch = bendData.quarterTones / 2; - - if (bendData.type == GuitarBendType::PRE_BEND) { - int pitch = bendData.quarterTones / 2; - note->setPitch(note->pitch() + pitch); - note->setTpcFromPitch(); - GuitarBend* bend = score->addGuitarBend(bendData.type, note); - QuarterOffset quarterOff = bendData.quarterTones % 2 ? QuarterOffset::QUARTER_SHARP : QuarterOffset::NONE; - bend->setEndNotePitch(note->pitch(), quarterOff); - Note* startNote = bend->startNote(); - if (startNote) { - startNote->setPitch(note->pitch() - pitch); - startNote->setTpcFromPitch(); - } - } else if (bendData.type == GuitarBendType::SLIGHT_BEND) { - GuitarBend* bend = score->addGuitarBend(bendData.type, note); - bend->setStartTimeFactor(bendData.startFactor); - bend->setEndTimeFactor(bendData.endFactor); - } else { - if (startChord == chord) { - LOGE() << "bend import error : start and end chords are the same for track " << chord->track() << ", tick " << - bendData.startTick.ticks(); - return; - } + std::vector startChordNotes = startChord->notes(); + std::vector endChordNotes = chord->notes(); + + IF_ASSERT_FAILED(startChordNotes.size() == endChordNotes.size()) { + LOGE() << "bend import error: start and end chord sizes don't match for track " << chord->track() << ", tick " << + bendChordData.startTick.ticks(); + return; + } - GuitarBend* bend = score->addGuitarBend(bendData.type, startNote, note); - if (!bend) { - LOGE() << "bend wasn't created for track " << chord->track() << ", tick " << startChord->tick().ticks(); - return; + std::sort(startChordNotes.begin(), startChordNotes.end(), [](Note* l, Note* r) { + return l->pitch() < r->pitch(); + }); + + std::sort(endChordNotes.begin(), endChordNotes.end(), [](Note* l, Note* r) { + return l->pitch() < r->pitch(); + }); + + for (size_t noteIndex = 0; noteIndex < endChordNotes.size(); noteIndex++) { + Note* note = endChordNotes[noteIndex]; + if (bendChordData.noteDataByPitch.find(note->pitch()) == bendChordData.noteDataByPitch.end()) { + continue; } - QuarterOffset quarterOff = bendData.quarterTones % 2 ? QuarterOffset::QUARTER_SHARP : QuarterOffset::NONE; - bend->setEndNotePitch(startNote->pitch() + pitch, quarterOff); - bend->setStartTimeFactor(bendData.startFactor); - bend->setEndTimeFactor(bendData.endFactor); - } + const BendDataContext::BendNoteData& bendNoteData = bendChordData.noteDataByPitch.at(note->pitch()); + Note* startNote = startChordNotes[noteIndex]; + int pitch = bendNoteData.quarterTones / 2; + + if (bendNoteData.type == GuitarBendType::PRE_BEND) { + int pitch = bendNoteData.quarterTones / 2; + note->setPitch(note->pitch() + pitch); + note->setTpcFromPitch(); + GuitarBend* bend = score->addGuitarBend(bendNoteData.type, note); + QuarterOffset quarterOff = bendNoteData.quarterTones % 2 ? QuarterOffset::QUARTER_SHARP : QuarterOffset::NONE; + bend->setEndNotePitch(note->pitch(), quarterOff); + Note* startNote = bend->startNote(); + if (startNote) { + startNote->setPitch(note->pitch() - pitch); + startNote->setTpcFromPitch(); + } + } else if (bendNoteData.type == GuitarBendType::SLIGHT_BEND) { + GuitarBend* bend = score->addGuitarBend(bendNoteData.type, note); + bend->setStartTimeFactor(bendNoteData.startFactor); + bend->setEndTimeFactor(bendNoteData.endFactor); + } else { + if (startChord == chord) { + LOGE() << "bend import error : start and end chords are the same for track " << chord->track() << ", tick " << + bendChordData.startTick.ticks(); + return; + } - int newPitch = note->pitch(); - Note* tiedNote = nullptr; + GuitarBend* bend = score->addGuitarBend(bendNoteData.type, startNote, note); + if (!bend) { + LOGE() << "bend wasn't created for track " << chord->track() << ", tick " << startChord->tick().ticks(); + return; + } - Tie* tieFor = startNote->tieFor(); - if (tieFor) { - tiedNote = tieFor->endNote(); - if (bendData.type != GuitarBendType::PRE_BEND) { - startNote->remove(tieFor); + QuarterOffset quarterOff = bendNoteData.quarterTones % 2 ? QuarterOffset::QUARTER_SHARP : QuarterOffset::NONE; + bend->setEndNotePitch(startNote->pitch() + pitch, quarterOff); + bend->setStartTimeFactor(bendNoteData.startFactor); + bend->setEndTimeFactor(bendNoteData.endFactor); } - } - while (tiedNote) { - tiedNote->setPitch(newPitch); - tiedNote->setTpcFromPitch(); - Tie* tie = tiedNote->tieFor(); - if (!tie) { - break; + int newPitch = note->pitch(); + Note* tiedNote = nullptr; + + Tie* tieFor = startNote->tieFor(); + if (tieFor) { + tiedNote = tieFor->endNote(); + if (bendNoteData.type != GuitarBendType::PRE_BEND) { + startNote->remove(tieFor); + } } - tiedNote = tie->endNote(); + while (tiedNote) { + tiedNote->setPitch(newPitch); + tiedNote->setTpcFromPitch(); + Tie* tie = tiedNote->tieFor(); + if (!tie) { + break; + } + + tiedNote = tie->endNote(); + } } } } // namespace mu::iex::guitarpro diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord-gp.mscx new file mode 100644 index 0000000000000..88ac26a70eff2 --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord-gp.mscx @@ -0,0 +1,316 @@ + + + + 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 + + + + quarter + + + 60 + 14 + 1 + 1 + + + 3 + 0 + 1 + + 3 + + + + + + + + + + + + + + + + 64 + 18 + 0 + 0 + + + 3 + 0 + 0.5 + + 3 + + + + + + + + + + + + + + + + 67 + 15 + 12 + 2 + + + 3 + 0 + 0.216667 + + 3 + + + + + + + + + + + + + + + + + quarter + + + + half + + + + + + + + + + + 0 + major + + + + + quarter + + + + 60 + 14 + 1 + 1 + + + 3 + 0 + 1 + + + 3 + + + + + + + + + + + + + + + + + 64 + 18 + 0 + 0 + + + 3 + 0 + 1 + + + 3 + + + + + + + + + + + + + + + + + 67 + 15 + 12 + 2 + + + 3 + 0 + 1 + + + 3 + + + + + + + + + + + + + + + + + + quarter + + + + + half + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord.gp new file mode 100644 index 0000000000000000000000000000000000000000..c502315025e902d58acac68c82b17dd3dfad71cd GIT binary patch literal 8757 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 literal 0 HcmV?d00001 diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord-gp.mscx b/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord-gp.mscx new file mode 100644 index 0000000000000..fbc307d5ade41 --- /dev/null +++ b/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord-gp.mscx @@ -0,0 +1,373 @@ + + + + 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 + + + + quarter + + + 57 + 17 + 2 + 2 + + + 0 + 0 + 1 + + 3 + + + + 1/4 + + + + + + + + + + + + + 1/4 + + + + 60 + 14 + 1 + 1 + + + + 64 + 18 + 5 + 1 + + + 0 + 0 + 1 + + 3 + + + + 1/4 + + + + + + + + quarter + + + + 1 + accidentalQuarterToneSharpArrowDown + + 57 + 17 + 2 + 2 + + + + -1/4 + + + + + + + + + + -1/4 + + + + 60 + 14 + 1 + 1 + + + + 67 + 15 + 5 + 1 + + + + -1/4 + + + + + + + + half + + + + + + + + + + + 0 + major + + + + + quarter + + + + 57 + 17 + 2 + 2 + + + 0 + 0 + 1 + + + 3 + + + + 1/4 + + + + + + + + + + + + + + + 1/4 + + + + 60 + 14 + 1 + 1 + + + + + 64 + 18 + 5 + 1 + + + 0 + 0 + 1 + + + 3 + + + + 1/4 + + + + + + + + + quarter + + + + + 1 + accidentalQuarterToneSharpArrowDown + + 57 + 17 + 2 + 2 + + + + -1/4 + + + + + + + + + + + -1/4 + + + + 60 + 14 + 1 + 1 + + + + + 67 + 15 + 5 + 1 + + + + -1/4 + + + + + + + + + half + + + + + + diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord.gp b/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord.gp new file mode 100644 index 0000000000000000000000000000000000000000..ac3e64da3feb742bde1705d8b47f9c6e0b522574 GIT binary patch literal 8846 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 t#~l1~{i9lsN