Skip to content

Commit bc1de3d

Browse files
committed
Add support for note spanners (tie, glissandos...)
1 parent d0e97e9 commit bc1de3d

2 files changed

Lines changed: 226 additions & 31 deletions

File tree

src/engraving/rendering/score/stavesharinglayout.cpp

Lines changed: 221 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -250,14 +250,21 @@ bool StaveSharingLayout::isUnison(track_idx_t prevTrack, track_idx_t nextTrack,
250250
}
251251

252252
for (size_t i = 0; i < notes1.size(); ++i) {
253-
if (!notes1[i]->isExactUnison(notes2[i])) {
253+
Note* n1 = notes1[i];
254+
Note* n2 = notes2[i];
255+
256+
if (!n1->isExactUnison(n2)) {
257+
return false;
258+
}
259+
260+
if (!checkNoteSpannersForUnison(n1, n2)) {
254261
return false;
255262
}
256263
}
257264
}
258265

259266
for (Segment* segment : ctx.allSegments) {
260-
if (!checkAnnotationsForSameVoice(segment, prevTrack, nextTrack, ctx)) {
267+
if (!checkAnnotationsForSameVoice(segment, prevTrack, nextTrack)) {
261268
return false;
262269
}
263270
}
@@ -295,40 +302,52 @@ bool StaveSharingLayout::canGoToSameVoice(track_idx_t prevTrack, track_idx_t nex
295302

296303
Chord* c1 = toChord(cr1);
297304
Chord* c2 = toChord(cr2);
298-
for (Note* n1 : c1->notes()) {
299-
for (Note* n2 : c2->notes()) {
300-
if (n2->pitch() > n1->pitch()) {
305+
306+
const std::vector<Note*>& notes1 = c1->notes();
307+
const std::vector<Note*>& notes2 = c2->notes();
308+
if (notes1.size() != notes2.size()) {
309+
return false;
310+
}
311+
312+
for (size_t i = 0; i < notes1.size(); ++i) {
313+
Note* n1 = notes1[i];
314+
Note* n2 = notes2[i];
315+
316+
if (n2->pitch() > n1->pitch()) {
317+
return false;
318+
}
319+
320+
if (!n2->isExactUnison(n1)) {
321+
if (n2->pitch() == n1->pitch() || muse::contains(localUnisonNotes, n1)) {
301322
return false;
302323
}
303324

304-
if (!n2->isExactUnison(n1)) {
305-
if (n2->pitch() == n1->pitch() || muse::contains(localUnisonNotes, n1)) {
306-
return false;
307-
}
325+
continue;
326+
}
308327

328+
if (!checkNoteSpannersForUnison(n1, n2)) {
329+
return false;
330+
}
331+
332+
for (track_idx_t track : curTrackGroup) {
333+
if (track == prevTrack) {
309334
continue;
310335
}
311-
312-
for (track_idx_t track : curTrackGroup) {
313-
if (track == prevTrack) {
314-
continue;
315-
}
316-
if (ChordRest* cr = toChordRest(segment->element(track)); cr && cr->isChord()) {
317-
for (Note* n : toChord(cr)->notes()) {
318-
if (!n2->isExactUnison(n)) {
319-
return false;
320-
}
336+
if (ChordRest* cr = toChordRest(segment->element(track)); cr && cr->isChord()) {
337+
for (Note* n : toChord(cr)->notes()) {
338+
if (!n2->isExactUnison(n)) {
339+
return false;
321340
}
322341
}
323342
}
324-
325-
potentialUnisonNotes.push_back(n2);
326343
}
344+
345+
potentialUnisonNotes.push_back(n2);
327346
}
328347
}
329348

330349
for (Segment* segment : ctx.allSegments) {
331-
if (!checkAnnotationsForSameVoice(segment, prevTrack, nextTrack, ctx)) {
350+
if (!checkAnnotationsForSameVoice(segment, prevTrack, nextTrack)) {
332351
return false;
333352
}
334353
}
@@ -340,8 +359,7 @@ bool StaveSharingLayout::canGoToSameVoice(track_idx_t prevTrack, track_idx_t nex
340359
return true;
341360
}
342361

343-
bool StaveSharingLayout::checkAnnotationsForSameVoice(Segment* segment, track_idx_t prevTrack, track_idx_t nextTrack,
344-
StaveSharingContext& ctx)
362+
bool StaveSharingLayout::checkAnnotationsForSameVoice(Segment* segment, track_idx_t prevTrack, track_idx_t nextTrack)
345363
{
346364
std::multimap<ElementType, EngravingItem*> annotationsOnPrevTrack;
347365
std::multimap<ElementType, EngravingItem*> annotationsOnNextTrack;
@@ -379,6 +397,65 @@ bool StaveSharingLayout::checkAnnotationsForSameVoice(Segment* segment, track_id
379397
return true;
380398
}
381399

400+
bool StaveSharingLayout::checkNoteSpannersForUnison(const Note* note1, const Note* note2)
401+
{
402+
const Tie* tieBack1 = note1->tieBack();
403+
const Tie* tieBack2 = note2->tieBack();
404+
if (bool(tieBack1) != bool(tieBack2)) {
405+
return false;
406+
}
407+
if (tieBack1 && bool(tieBack1->startElement()) != bool(tieBack2->startElement())) {
408+
return false;
409+
}
410+
411+
const Tie* tieFor1 = note1->tieFor();
412+
const Tie* tieFor2 = note2->tieFor();
413+
if (bool(tieFor1) != bool(tieFor2)) {
414+
return false;
415+
}
416+
if (tieFor1 && bool(tieFor1->startElement()) != bool(tieFor2->startElement())) {
417+
return false;
418+
}
419+
420+
const std::vector<Spanner*>& spannerBack1 = note1->spannerBack();
421+
const std::vector<Spanner*>& spannerBack2 = note2->spannerBack();
422+
if (spannerBack1.size() != spannerBack2.size()) {
423+
return false;
424+
}
425+
426+
for (Spanner* sp1 : spannerBack1) {
427+
auto i = std::find_if(spannerBack2.begin(), spannerBack2.end(), [sp1](Spanner* sp2) { return sp2->type() == sp1->type(); });
428+
if (i == spannerBack2.end()) {
429+
return false;
430+
}
431+
432+
Spanner* sp2 = *i;
433+
if (bool(sp1->startElement()) != bool(sp2->startElement())) {
434+
return false;
435+
}
436+
}
437+
438+
const std::vector<Spanner*>& spannerFor1 = note1->spannerFor();
439+
const std::vector<Spanner*>& spannerFor2 = note2->spannerFor();
440+
if (spannerFor1.size() != spannerFor2.size()) {
441+
return false;
442+
}
443+
444+
for (Spanner* sp1 : spannerFor1) {
445+
auto i = std::find_if(spannerFor2.begin(), spannerFor2.end(), [sp1](Spanner* sp2) { return sp2->type() == sp1->type(); });
446+
if (i == spannerFor2.end()) {
447+
return false;
448+
}
449+
450+
Spanner* sp2 = *i;
451+
if (bool(sp1->endElement()) != bool(sp2->endElement())) {
452+
return false;
453+
}
454+
}
455+
456+
return true;
457+
}
458+
382459
bool StaveSharingLayout::canGoToSameStave(track_idx_t prevTrack, track_idx_t nextTrack,
383460
StaveSharingContext& ctx)
384461
{
@@ -474,6 +551,24 @@ void StaveSharingLayout::disconnectAll(SharedPart* p, StaveSharingContext& ctx)
474551
if (cr->isChord()) {
475552
for (Note* note : toChord(cr)->notes()) {
476553
EngravingItem::disconnectAllOriginItems(note);
554+
555+
if (Tie* tieFor = note->tieFor(); tieFor && !tieFor->endNote()) { // if it does have endNote it will be disconnected when we process that note
556+
EngravingItem::disconnectAllOriginItems(tieFor);
557+
}
558+
559+
for (Spanner* spannerFor : note->spannerFor()) {
560+
if (!spannerFor->endElement()) {
561+
EngravingItem::disconnectAllOriginItems(spannerFor); // if it does have endElement it will be disconnected when we process that note
562+
}
563+
}
564+
565+
if (Tie* tieBack = note->tieBack()) {
566+
EngravingItem::disconnectAllOriginItems(tieBack);
567+
}
568+
569+
for (Spanner* spannerBack : note->spannerBack()) {
570+
EngravingItem::disconnectAllOriginItems(spannerBack);
571+
}
477572
}
478573
}
479574
}
@@ -567,7 +662,7 @@ void StaveSharingLayout::makeSharedChordRests(SharedPart* p, StaveSharingContext
567662
Chord* sharedChord = toChord(sharedCR);
568663
Note* sharedNote = nullptr;
569664
for (Note* n : sharedChord->notes()) {
570-
if (n->pitch() == originNote->pitch()) {
665+
if (n->isExactUnison(originNote)) {
571666
sharedNote = n;
572667
break;
573668
}
@@ -581,7 +676,88 @@ void StaveSharingLayout::makeSharedChordRests(SharedPart* p, StaveSharingContext
581676
}
582677

583678
EngravingItem::connectSharedItem(sharedNote, originNote);
679+
680+
makeSharedTiesAndNoteSpanners(originNote, sharedNote);
681+
}
682+
}
683+
}
684+
685+
void StaveSharingLayout::makeSharedTiesAndNoteSpanners(Note* originNote, Note* sharedNote)
686+
{
687+
Score* score = originNote->score();
688+
689+
Tie* tieBack = originNote->tieBack();
690+
if (tieBack) {
691+
Tie* sharedTieBack = sharedNote->tieBack();
692+
if (!sharedTieBack) {
693+
sharedTieBack = toTie(tieBack->clone());
694+
Note* sharedStartNote = tieBack->startNote() ? toNote(tieBack->startNote()->sharedItem()) : nullptr;
695+
sharedTieBack->setNoteSpan(sharedStartNote, sharedNote);
696+
697+
score->undoAddElement(sharedTieBack);
698+
}
699+
700+
EngravingItem::connectSharedItem(sharedTieBack, tieBack);
701+
}
702+
703+
Tie* tieFor = originNote->tieFor();
704+
if (tieFor && !tieFor->endNote()) { // If it does have an end note we add it when we process the end note
705+
Tie* sharedTieFor = sharedNote->tieFor();
706+
if (!sharedTieFor) {
707+
sharedTieFor = toTie(sharedTieFor->clone());
708+
sharedTieFor->setNoteSpan(sharedNote, nullptr);
709+
710+
score->undoAddElement(sharedTieFor);
711+
}
712+
713+
EngravingItem::connectSharedItem(sharedTieFor, tieFor);
714+
}
715+
716+
const std::vector<Spanner*>& originSpannerBack = originNote->spannerBack();
717+
const std::vector<Spanner*>& sharedSpannerBack = sharedNote->spannerBack();
718+
719+
for (Spanner* spanner : originSpannerBack) {
720+
Spanner* sharedSpanner = nullptr;
721+
for (Spanner* sp : sharedSpannerBack) {
722+
if (sp && sp->type() == spanner->type()) {
723+
sharedSpanner = sp;
724+
break;
725+
}
584726
}
727+
728+
if (!sharedSpanner) {
729+
sharedSpanner = toSpanner(spanner->clone());
730+
Note* startNote = spanner->startElement() ? toNote(spanner->startElement()->sharedItem()) : nullptr;
731+
sharedSpanner->setNoteSpan(startNote, sharedNote);
732+
733+
score->undoAddElement(sharedSpanner);
734+
}
735+
736+
EngravingItem::connectSharedItem(sharedSpanner, spanner);
737+
}
738+
739+
for (Spanner* spanner : originNote->spannerFor()) {
740+
if (spanner->endElement()) { // If it does have an end note we add it when we process the end note
741+
continue;
742+
}
743+
744+
Spanner* sharedSpanner = nullptr;
745+
for (Spanner* sp : sharedNote->spannerFor()) {
746+
if (sp->type() == spanner->type()) {
747+
sharedSpanner = sp;
748+
break;
749+
}
750+
}
751+
752+
if (!sharedSpanner) {
753+
sharedSpanner = toSpanner(spanner->clone());
754+
sharedSpanner->setTrack(sharedNote->track());
755+
sharedSpanner->setStartElement(sharedNote);
756+
757+
score->undoAddElement(sharedSpanner);
758+
}
759+
760+
EngravingItem::connectSharedItem(sharedSpanner, spanner);
585761
}
586762
}
587763

@@ -663,10 +839,6 @@ void StaveSharingLayout::makeSharedAnnotations(SharedPart* p, StaveSharingContex
663839
}
664840
}
665841

666-
bool StaveSharingLayout::annotationRefersToBothVoices(EngravingItem* sharedAnnotation, StaveSharingContext& ctx)
667-
{
668-
}
669-
670842
void StaveSharingLayout::cleanup(SharedPart* p, StaveSharingContext& ctx)
671843
{
672844
Score* score = ctx.score;
@@ -714,6 +886,26 @@ void StaveSharingLayout::cleanup(SharedPart* p, StaveSharingContext& ctx)
714886
Chord* c = toChord(cr);
715887
std::vector<Note*> notes = c->notes(); // copy because may be removed
716888
for (Note* note : notes) {
889+
if (Tie* tieBack = note->tieBack(); tieBack && tieBack->originItems().empty()) {
890+
score->undoRemoveElement(tieBack);
891+
}
892+
893+
for (Spanner* sp : note->spannerBack()) {
894+
if (sp->originItems().empty()) {
895+
score->undoRemoveElement(sp);
896+
}
897+
}
898+
899+
if (Tie* tieFor = note->tieFor(); tieFor && !tieFor->endElement() && tieFor->originItems().empty()) {
900+
score->undoRemoveElement(tieFor);
901+
}
902+
903+
for (Spanner* sp : note->spannerFor()) {
904+
if (!sp->endElement() && sp->originItems().empty()) {
905+
score->undoRemoveElement(sp);
906+
}
907+
}
908+
717909
if (note->originItems().empty()) {
718910
score->undoRemoveElement(note);
719911
}

src/engraving/rendering/score/stavesharinglayout.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ class StaveSharingLayout
6161
static bool isUnison(track_idx_t prevTrack, track_idx_t nextTrack, StaveSharingContext& ctx);
6262
static bool canGoToSameVoice(track_idx_t prevTrack, track_idx_t nextTrack, StaveSharingContext& ctx, const TrackGroup& curTrackGroup,
6363
std::unordered_set<Note*>& localUnisonNotes);
64-
static bool checkAnnotationsForSameVoice(Segment* segment, track_idx_t prevTrack, track_idx_t nextTrack, StaveSharingContext& ctx);
64+
65+
static bool checkAnnotationsForSameVoice(Segment* segment, track_idx_t prevTrack, track_idx_t nextTrack);
66+
static bool checkNoteSpannersForUnison(const Note* note1, const Note* note2);
67+
6568
static bool canGoToSameStave(track_idx_t prevTrack, track_idx_t nextTrack, StaveSharingContext& ctx);
6669

6770
static void updateNotation(SharedPart* p, StaveSharingContext& ctx);
@@ -70,8 +73,8 @@ class StaveSharingLayout
7073

7174
static void makeSharedNotation(SharedPart* p, StaveSharingContext& ctx);
7275
static void makeSharedChordRests(SharedPart* p, StaveSharingContext& ctx);
76+
static void makeSharedTiesAndNoteSpanners(Note* originNote, Note* sharedNote);
7377
static void makeSharedAnnotations(SharedPart* p, StaveSharingContext& ctx);
74-
static bool annotationRefersToBothVoices(EngravingItem* sharedAnnotation, StaveSharingContext& ctx);
7578

7679
static void cleanup(SharedPart* p, StaveSharingContext& ctx);
7780
};

0 commit comments

Comments
 (0)