diff --git a/src/engraving/dom/chord.cpp b/src/engraving/dom/chord.cpp index 8711377e2918b..e18597259df2b 100644 --- a/src/engraving/dom/chord.cpp +++ b/src/engraving/dom/chord.cpp @@ -2572,6 +2572,15 @@ GraceNotesGroup& Chord::graceNotesAfter(bool filterUnplayable) const return m_graceNotesAfter; } +Chord* Chord::graceNoteAt(size_t idx) const +{ + if (idx > m_graceNotes.size()) { + return nullptr; + } + + return m_graceNotes.at(idx); +} + //--------------------------------------------------------- // setShowStemSlashInAdvance //--------------------------------------------------------- diff --git a/src/engraving/dom/chord.h b/src/engraving/dom/chord.h index 544d65ad436a4..2c313e0bb7be8 100644 --- a/src/engraving/dom/chord.h +++ b/src/engraving/dom/chord.h @@ -193,6 +193,8 @@ class Chord final : public ChordRest size_t graceIndex() const { return m_graceIndex; } void setGraceIndex(size_t val) { m_graceIndex = val; } + Chord* graceNoteAt(size_t idx) const; + int upLine() const override; int downLine() const override; PointF stemPos() const override; ///< page coordinates diff --git a/src/engraving/dom/excerpt.cpp b/src/engraving/dom/excerpt.cpp index 1f44b2cd84df8..967281e3005d5 100644 --- a/src/engraving/dom/excerpt.cpp +++ b/src/engraving/dom/excerpt.cpp @@ -767,6 +767,45 @@ static void processLinkedClone(EngravingItem* ne, Score* score, track_idx_t stra ne->setScore(score); } +static void addTies(Note* originalNote, Note* newNote, TieMap& tieMap, Score* score) +{ + if (originalNote->tieFor()) { + Tie* tie = toTie(originalNote->tieFor()->linkedClone()); + tie->setScore(score); + newNote->setTieFor(tie); + tie->setStartNote(newNote); + tie->setTrack(newNote->track()); + tieMap.add(originalNote->tieFor(), tie); + } + if (originalNote->tieBack()) { + Tie* tie = tieMap.findNew(originalNote->tieBack()); + if (tie) { + newNote->setTieBack(tie); + tie->setEndNote(newNote); + } else { + LOGD("addTiesToMap: cannot find tie"); + } + } +} + +static void addGraceNoteTies(GraceNotesGroup& originalGraceNotes, Chord* newChord, TieMap& tieMap, Score* score) +{ + for (Chord* oldGrace : originalGraceNotes) { + Chord* newGrace = newChord->graceNoteAt(oldGrace->graceIndex()); + if (!newGrace) { + continue; + } + + size_t notes = oldGrace->notes().size(); + for (size_t i = 0; i < notes; ++i) { + Note* originalNote = oldGrace->notes().at(i); + Note* newNote = newGrace->notes().at(i); + + addTies(originalNote, newNote, tieMap, score); + } + } +} + static MeasureBase* cloneMeasure(MeasureBase* mb, Score* score, const Score* oscore, const std::vector& sourceStavesIndexes, const TracksMap& trackList, TieMap& tieMap) @@ -934,28 +973,13 @@ static MeasureBase* cloneMeasure(MeasureBase* mb, Score* score, const Score* osc if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); - + addGraceNoteTies(och->graceNotesBefore(), nch, tieMap, score); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); - if (on->tieFor()) { - Tie* tie = toTie(on->tieFor()->linkedClone()); - tie->setScore(score); - nn->setTieFor(tie); - tie->setStartNote(nn); - tie->setTrack(nn->track()); - tieMap.add(on->tieFor(), tie); - } - if (on->tieBack()) { - Tie* tie = tieMap.findNew(on->tieBack()); - if (tie) { - nn->setTieBack(tie); - tie->setEndNote(nn); - } else { - LOGD("cloneStaves: cannot find tie"); - } - } + + addTies(on, nn, tieMap, score); // add back spanners (going back from end to start spanner element // makes sure the 'other' spanner anchor element is already set up) // 'on' is the old spanner end note and 'nn' is the new spanner end note @@ -988,6 +1012,7 @@ static MeasureBase* cloneMeasure(MeasureBase* mb, Score* score, const Score* osc } } } + addGraceNoteTies(och->graceNotesAfter(), nch, tieMap, score); // two note tremolo if (och->tremoloTwoChord()) { if (och == och->tremoloTwoChord()->chord1()) { @@ -1298,27 +1323,12 @@ void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff, bool cloneSpanners) if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); + addGraceNoteTies(och->graceNotesBefore(), nch, tieMap, score); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); - if (on->tieFor()) { - Tie* tie = toTie(on->tieFor()->linkedClone()); - tie->setScore(score); - nn->setTieFor(tie); - tie->setStartNote(nn); - tie->setTrack(nn->track()); - tieMap.add(on->tieFor(), tie); - } - if (on->tieBack()) { - Tie* tie = tieMap.findNew(on->tieBack()); - if (tie) { - nn->setTieBack(tie); - tie->setEndNote(nn); - } else { - LOGD("cloneStaff: cannot find tie"); - } - } + addTies(on, nn, tieMap, score); // add back spanners (going back from end to start spanner element // makes sure the 'other' spanner anchor element is already set up) // 'on' is the old spanner end note and 'nn' is the new spanner end note @@ -1333,6 +1343,7 @@ void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff, bool cloneSpanners) } } } + addGraceNoteTies(och->graceNotesAfter(), nch, tieMap, score); // two note tremolo if (och->tremoloTwoChord()) { if (och == och->tremoloTwoChord()->chord1()) { @@ -1569,27 +1580,12 @@ void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& star if (oe->isChord()) { Chord* och = toChord(ocr); Chord* nch = toChord(ncr); + addGraceNoteTies(och->graceNotesBefore(), nch, tieMap, score); size_t n = och->notes().size(); for (size_t i = 0; i < n; ++i) { Note* on = och->notes().at(i); Note* nn = nch->notes().at(i); - if (on->tieFor()) { - Tie* tie = toTie(on->tieFor()->linkedClone()); - tie->setScore(score); - nn->setTieFor(tie); - tie->setStartNote(nn); - tie->setTrack(nn->track()); - tieMap.add(on->tieFor(), tie); - } - if (on->tieBack()) { - Tie* tie = tieMap.findNew(on->tieBack()); - if (tie) { - nn->setTieBack(tie); - tie->setEndNote(nn); - } else { - LOGD("cloneStaff2: cannot find tie"); - } - } + addTies(on, nn, tieMap, score); // add back spanners (going back from end to start spanner element // makes sure the 'other' spanner anchor element is already set up) // 'on' is the old spanner end note and 'nn' is the new spanner end note @@ -1629,6 +1625,7 @@ void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& star nn->addSpannerFor(newBend); } } + addGraceNoteTies(och->graceNotesAfter(), nch, tieMap, score); // two note tremolo if (och->tremoloTwoChord()) { if (och == och->tremoloTwoChord()->chord1()) { diff --git a/src/engraving/tests/parts_data/linked-ties-1.mscx b/src/engraving/tests/parts_data/linked-ties-1.mscx new file mode 100644 index 0000000000000..897571c3322f9 --- /dev/null +++ b/src/engraving/tests/parts_data/linked-ties-1.mscx @@ -0,0 +1,389 @@ + + + + 480 + + 1 + 1 + 1 + 0 + + Composer / arranger + + + + + + Subtitle + + + Untitled score + + Orchestra + + Flutes + +
+ flutes + oboes + clarinets + saxophones + bassoons + +
+
+ horns + trumpets + cornets + flugelhorns + trombones + tubas + +
+
+ timpani +
+
+ keyboard-percussion + + drums + unpitched-metal-percussion + unpitched-wooden-percussion + other-percussion + +
+ keyboards + harps + organs + synths + + +
+ voices + voice-groups +
+
+ orchestral-strings +
+
+ + + 2 + + stdNormal + + + + 1 + + stdNormal + + + Flute + + Flute + Fl. + Flute + 59 + 98 + 60 + 93 + wind.flutes.flute + + + + + + + + + + + 0 + + + 4 + 4 + + + + quarter + + + + + + + + + 1/4 + + + + 71 + 19 + + + + + quarter + + + + + + -1/4 + + + + 71 + 19 + + + + + half + + + + + + + + eighth + + + + + + + + + + + + + 71 + 19 + + + + + quarter + + + + + + 0 + + + + 71 + 19 + + + + + quarter + + + + eighth + + + + + + + + + + 71 + 19 + + + + + quarter + + + + + + + + + 0 + + + + 71 + 19 + + + + + quarter + + + + + + + + + + + 0 + + + 4 + 4 + + + + + quarter + + + + + + + + + + + 1/4 + + + + 71 + 19 + + + + + + quarter + + + + + + + -1/4 + + + + 71 + 19 + + + + + + half + + + + + + + + + eighth + + + + + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + eighth + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + +
+
diff --git a/src/engraving/tests/parts_data/linked-ties-parts.mscx b/src/engraving/tests/parts_data/linked-ties-parts.mscx new file mode 100644 index 0000000000000..803e4f6419235 --- /dev/null +++ b/src/engraving/tests/parts_data/linked-ties-parts.mscx @@ -0,0 +1,769 @@ + + + + 480 + + 1 + 1 + 1 + 0 + + Composer / arranger + + + + + + Subtitle + + + Untitled score + + Orchestra + + Flutes + +
+ flutes + oboes + clarinets + saxophones + bassoons + +
+
+ horns + trumpets + cornets + flugelhorns + trombones + tubas + +
+
+ timpani +
+
+ keyboard-percussion + + drums + unpitched-metal-percussion + unpitched-wooden-percussion + other-percussion + +
+ keyboards + harps + organs + synths + + +
+ voices + voice-groups +
+
+ orchestral-strings +
+
+ + + 2 + + stdNormal + + + + 1 + + stdNormal + + + Flute + + Flute + Fl. + Flute + 59 + 98 + 60 + 93 + wind.flutes.flute + + + + + + + + 10 + + + + + + + 0 + + + + 4 + 4 + + + + quarter + + + + + + + + + 1/4 + + + + 71 + 19 + + + + + quarter + + + + + + -1/4 + + + + 71 + 19 + + + + + half + + + + + + + + eighth + + + + + + + + + + + + + 71 + 19 + + + + + quarter + + + + + + 0 + + + + 71 + 19 + + + + + quarter + + + + eighth + + + + + + + + + + 71 + 19 + + + + + quarter + + + + + + + + + 0 + + + + 71 + 19 + + + + + quarter + + + + + + + + + + 1 + + 0 + + + + 4 + 4 + + + + 3 + + quarter + + + 3 + + + + + 3 + + + + + 1/4 + + + + 71 + 19 + + + + + + quarter + + + + + + + -1/4 + + + + 71 + 19 + + + + + + half + + + + + + + + + eighth + + + + + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + eighth + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + + Flute + 480 + + 1 + 1 + 1 + 0 + Flute + + + 1 + 2 + + stdNormal + + + + 1 + 2 + + stdNormal + + + Flute + + Flute + Fl. + Flute + 59 + 98 + 60 + 93 + wind.flutes.flute + + + + + + + + 10 + + + + + Flute + + + + + + + + 0 + + + + + 4 + 4 + + + + + quarter + + + + + + + + + + + 1/4 + + + + 71 + 19 + + + + + + quarter + + + + + + + -1/4 + + + + 71 + 19 + + + + + + half + + + + + + + + + eighth + + + + + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + eighth + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + + + + + + 1 + + 0 + + + + + 1 + + + 4 + 4 + + + + 3 + + quarter + + + 3 + + + + + 3 + + + + + 1/4 + + + + 71 + 19 + + + + + + quarter + + + + + + + -1/4 + + + + 71 + 19 + + + + + + half + + + + + + + + + eighth + + + + + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + eighth + + + + + + + + + + + 71 + 19 + + + + + + quarter + + + + + + + + + + + 0 + + + + 71 + 19 + + + + + + quarter + + + + + +
+
diff --git a/src/engraving/tests/parts_data/linked-ties.mscx b/src/engraving/tests/parts_data/linked-ties.mscx new file mode 100755 index 0000000000000..c423a773a19b3 --- /dev/null +++ b/src/engraving/tests/parts_data/linked-ties.mscx @@ -0,0 +1,212 @@ + + + 4.4.0 + + + 480 + 1 + 1 + 1 + 0 + 1 + + Composer / arranger + + 2024-08-05 + + + + Apple Macintosh + + Subtitle + + + Untitled score + + Orchestra + + Flutes + +
+ flutes + oboes + clarinets + saxophones + bassoons + +
+
+ horns + trumpets + cornets + flugelhorns + trombones + tubas + +
+
+ timpani +
+
+ keyboard-percussion + + drums + unpitched-metal-percussion + unpitched-wooden-percussion + other-percussion + +
+ keyboards + harps + organs + synths + + +
+ voices + voice-groups +
+
+ orchestral-strings +
+
+ + + + stdNormal + + + Flute + + Flute + Fl. + Flute + 59 + 98 + 60 + 93 + wind.flutes.flute + + + Fluid + + + + + + + + 0 + + + 4 + 4 + + + quarter + + + + + + + 1/4 + + + + 71 + 19 + + + + quarter + + + + + -1/4 + + + + 71 + 19 + + + + half + + + + + + + eighth + + + + + + + + + + + 71 + 19 + + + + quarter + + + + + 0 + + + + 71 + 19 + + + + quarter + + + eighth + + + + + + + + + 71 + 19 + + + + quarter + + + + + + + 0 + + + + 71 + 19 + + + + quarter + + + + +
+
diff --git a/src/engraving/tests/parts_tests.cpp b/src/engraving/tests/parts_tests.cpp index a0eab73c7b512..cd25f7d709d0c 100644 --- a/src/engraving/tests/parts_tests.cpp +++ b/src/engraving/tests/parts_tests.cpp @@ -38,6 +38,7 @@ #include "dom/part.h" #include "dom/segment.h" #include "dom/spanner.h" +#include "dom/staff.h" #include "utils/scorerw.h" #include "utils/scorecomp.h" @@ -53,6 +54,7 @@ class Engraving_PartsTests : public ::testing::Test Score* createPart(MasterScore* score); void createParts(MasterScore* score); void testPartCreation(const String& test); + void createLinkedStaff(MasterScore* score); MasterScore* doAddBreath(); MasterScore* doRemoveBreath(); @@ -131,6 +133,19 @@ void Engraving_PartsTests::createParts(MasterScore* masterScore) masterScore->setExcerptsChanged(true); } +void Engraving_PartsTests::createLinkedStaff(MasterScore* masterScore) +{ + masterScore->startCmd(); + Staff* sourceStaff = masterScore->staff(0); + EXPECT_TRUE(sourceStaff); + Staff* linkedStaff = Factory::createStaff(sourceStaff->part()); + linkedStaff->setPart(sourceStaff->part()); + masterScore->undoInsertStaff(linkedStaff, 1, false); + Excerpt::cloneStaff(sourceStaff, linkedStaff); + masterScore->endCmd(); + EXPECT_TRUE(masterScore->staff(1)); +} + //--------------------------------------------------------- // voicesExcerpt //--------------------------------------------------------- @@ -1270,6 +1285,17 @@ TEST_F(Engraving_PartsTests, partSpanners) MScore::useRead302InTestMode = useRead302; } +TEST_F(Engraving_PartsTests, partTies) { + const String test = u"linked-ties"; + MasterScore* score = ScoreRW::readScore(PARTS_DATA_DIR + test + u".mscx"); + ASSERT_TRUE(score); + createLinkedStaff(score); + EXPECT_TRUE(ScoreComp::saveCompareScore(score, test + u"-1.mscx", PARTS_DATA_DIR + test + u"-1.mscx")); + createPart(score); + EXPECT_TRUE(ScoreComp::saveCompareScore(score, test + u"-parts.mscx", PARTS_DATA_DIR + test + u"-parts.mscx")); + delete score; +} + TEST_F(Engraving_PartsTests, partVisibleTracks) { Score* score = ScoreRW::readScore(PARTS_DATA_DIR + u"part-visible-tracks.mscx"); EXPECT_TRUE(score);