Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2025 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #pragma once
24 :
25 : #include "dom/measure.h"
26 : #include "dom/note.h"
27 : #include "dom/repeatlist.h"
28 :
29 : #include "playback/renderingcontext.h"
30 :
31 : namespace mu::engraving {
32 : struct PartiallyTiedNoteInfo {
33 : const RepeatSegment* repeat = nullptr;
34 : const Note* note = nullptr;
35 :
36 36 : bool isValid() const { return repeat && note; }
37 : };
38 :
39 27 : inline PartiallyTiedNoteInfo findOutgoingNoteInPreviousRepeat(const Note* incomingNote, const RenderingContext& ctx)
40 : {
41 27 : const RepeatList& repeats = ctx.score->repeatList();
42 27 : auto currRepeatIt = repeats.findRepeatSegmentFromUTick(ctx.nominalPositionStartTick + ctx.positionTickOffset);
43 27 : if (currRepeatIt == repeats.cbegin() || currRepeatIt == repeats.cend()) {
44 12 : return PartiallyTiedNoteInfo();
45 : }
46 :
47 15 : if (ctx.nominalPositionStartTick + ctx.positionTickOffset != (*currRepeatIt)->utick) {
48 0 : return PartiallyTiedNoteInfo(); // the incoming note is not the first note in this repeat
49 : }
50 :
51 15 : auto prevRepeatIt = std::prev(currRepeatIt);
52 15 : const RepeatSegment* prevRepeat = (*prevRepeatIt);
53 15 : const Measure* lastMeasure = prevRepeat->lastMeasure();
54 15 : if (!lastMeasure) {
55 0 : return PartiallyTiedNoteInfo();
56 : }
57 :
58 15 : const ChordRest* lastChordRest = lastMeasure->lastChordRest(incomingNote->track());
59 15 : if (!lastChordRest || !lastChordRest->isChord()) {
60 0 : return PartiallyTiedNoteInfo();
61 : }
62 :
63 15 : const Note* outgoingNote = toChord(lastChordRest)->findNote(incomingNote->pitch());
64 15 : const Tie* tie = outgoingNote ? outgoingNote->tieFor() : nullptr;
65 15 : if (tie && tie->playSpanner()) {
66 15 : return { prevRepeat, outgoingNote };
67 : }
68 :
69 0 : return PartiallyTiedNoteInfo();
70 : }
71 :
72 9 : inline PartiallyTiedNoteInfo findIncomingNoteInNextRepeat(const Note* outgoingNote, const RenderingContext& ctx)
73 : {
74 9 : const RepeatList& repeats = ctx.score->repeatList();
75 9 : auto currRepeatIt = repeats.findRepeatSegmentFromUTick(ctx.nominalPositionStartTick + ctx.positionTickOffset);
76 9 : if (currRepeatIt == repeats.cend()) {
77 0 : return PartiallyTiedNoteInfo();
78 : }
79 :
80 9 : const RepeatSegment* currRepeat = (*currRepeatIt);
81 9 : const int currRepeatLastTick = currRepeat->utick + currRepeat->len();
82 9 : if (ctx.nominalPositionStartTick + ctx.positionTickOffset != currRepeatLastTick - ctx.nominalDurationTicks) {
83 1 : return PartiallyTiedNoteInfo(); // the outgoing note is not the last note in this repeat
84 : }
85 :
86 8 : auto nextRepeatIt = std::next(currRepeatIt);
87 8 : if (nextRepeatIt == repeats.cend()) {
88 2 : return PartiallyTiedNoteInfo();
89 : }
90 :
91 6 : const RepeatSegment* nextRepeat = (*nextRepeatIt);
92 6 : const Measure* firstMeasure = nextRepeat->firstMeasure();
93 6 : if (!firstMeasure) {
94 0 : return PartiallyTiedNoteInfo();
95 : }
96 :
97 6 : const ChordRest* firstChordRest = firstMeasure->firstChordRest(outgoingNote->track());
98 6 : if (!firstChordRest || !firstChordRest->isChord()) {
99 0 : return PartiallyTiedNoteInfo();
100 : }
101 :
102 6 : const Note* incomingNote = toChord(firstChordRest)->findNote(outgoingNote->pitch());
103 6 : const Tie* tie = incomingNote ? incomingNote->tieBack() : nullptr;
104 6 : if (tie && tie->playSpanner()) {
105 6 : return { nextRepeat, incomingNote };
106 : }
107 :
108 0 : return PartiallyTiedNoteInfo();
109 : }
110 :
111 38 : inline bool notesInSameRepeat(const Score* score, const Note* note1, const Note* note2, const int tickPositionOffset)
112 : {
113 38 : const RepeatList& repeats = score->repeatList();
114 38 : if (repeats.size() == 1) {
115 23 : return true;
116 : }
117 :
118 15 : const int firstNoteTick = note1->tick().ticks();
119 15 : auto repeatIt = repeats.findRepeatSegmentFromUTick(firstNoteTick + tickPositionOffset);
120 15 : if (repeatIt == repeats.end()) {
121 0 : return true;
122 : }
123 :
124 15 : const RepeatSegment* repeat = (*repeatIt);
125 15 : const int secondNoteTick = note2->tick().ticks();
126 :
127 15 : if (repeat->tick > secondNoteTick || repeat->endTick() <= secondNoteTick) {
128 4 : return false;
129 : }
130 :
131 11 : return true;
132 : }
133 : }
|