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 0000000000000..c502315025e90 Binary files /dev/null and b/src/importexport/guitarpro/tests/guitarbendimporter_data/slight_bend_chord.gp differ 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 0000000000000..ac3e64da3feb7 Binary files /dev/null and b/src/importexport/guitarpro/tests/guitarbendimporter_data/tied_bend_chord.gp differ diff --git a/src/importexport/guitarpro/tests/guitarbendimporter_tests.cpp b/src/importexport/guitarpro/tests/guitarbendimporter_tests.cpp index c26f1680e5ac7..cbbda76717bdc 100644 --- a/src/importexport/guitarpro/tests/guitarbendimporter_tests.cpp +++ b/src/importexport/guitarpro/tests/guitarbendimporter_tests.cpp @@ -106,4 +106,12 @@ TEST_F(GuitarBendImporter_Tests, gpTiedBend) { TEST_F(GuitarBendImporter_Tests, gp5TiedBend) { gpReadTest(u"tied_bend", u"gp5"); } + +TEST_F(GuitarBendImporter_Tests, gpTiedBendChord) { + gpReadTest(u"tied_bend_chord", u"gp"); +} + +TEST_F(GuitarBendImporter_Tests, gpSlightBendChord) { + gpReadTest(u"slight_bend_chord", u"gp"); +} }