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) 2021 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 : #include "chordarticulationsparser.h"
24 :
25 : #include "dom/arpeggio.h"
26 : #include "dom/breath.h"
27 : #include "dom/chord.h"
28 : #include "dom/chordline.h"
29 : #include "dom/score.h"
30 : #include "dom/segment.h"
31 : #include "dom/spanner.h"
32 : #include "dom/tremolosinglechord.h"
33 : #include "dom/tremolotwochord.h"
34 : #include "dom/tapping.h"
35 :
36 : #include "playback/utils/arrangementutils.h"
37 : #include "playback/filters/spannerfilter.h"
38 :
39 : #include "internal/spannersmetaparser.h"
40 : #include "internal/symbolsmetaparser.h"
41 : #include "internal/annotationsmetaparser.h"
42 : #include "internal/tremolometaparser.h"
43 : #include "internal/arpeggiometaparser.h"
44 : #include "internal/gracenotesmetaparser.h"
45 : #include "internal/chordlinemetaparser.h"
46 :
47 : using namespace mu::engraving;
48 : using namespace muse;
49 : using namespace muse::mpe;
50 :
51 1139 : void ChordArticulationsParser::buildChordArticulationMap(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
52 : {
53 1139 : if (!chord || !ctx.isValid()) {
54 0 : LOGE() << "Unable to render playback events of invalid chord";
55 0 : return;
56 : }
57 :
58 1139 : parse(chord, ctx, result);
59 :
60 1226 : for (const auto& pair : result) {
61 87 : if (isSingleNoteArticulation(pair.first)) {
62 52 : continue;
63 : }
64 :
65 35 : duration_percentage_t occupiedFrom = occupiedPercentage(ctx.nominalTimestamp - pair.second.meta.timestamp,
66 35 : pair.second.meta.overallDuration);
67 35 : duration_percentage_t occupiedTo = occupiedPercentage(ctx.nominalTimestamp + ctx.nominalDuration - pair.second.meta.timestamp,
68 35 : pair.second.meta.overallDuration);
69 :
70 35 : result.updateOccupiedRange(pair.first, occupiedFrom, occupiedTo);
71 : }
72 :
73 1139 : result.preCalculateAverageData();
74 : }
75 :
76 1139 : void ChordArticulationsParser::doParse(const EngravingItem* item, const RenderingContext& ctx, mpe::ArticulationMap& result)
77 : {
78 1139 : IF_ASSERT_FAILED(item->isChord()) {
79 0 : return;
80 : }
81 :
82 1139 : const Chord* chord = toChord(item);
83 :
84 1139 : parseSpanners(chord, ctx, result);
85 1139 : parseAnnotations(chord, ctx, result);
86 1139 : parseTremolo(chord, ctx, result);
87 1139 : parseArpeggio(chord, ctx, result);
88 1139 : parseGraceNotes(chord, ctx, result);
89 1139 : parseChordLine(chord, ctx, result);
90 1139 : parseArticulationSymbols(chord, ctx, result);
91 1139 : parseTapping(chord, ctx, result);
92 1139 : parseBreath(chord, ctx, result);
93 : }
94 :
95 1139 : void ChordArticulationsParser::parseSpanners(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
96 : {
97 1139 : const SpannerMap& spannerMap = ctx.score->spannerMap();
98 1139 : if (spannerMap.empty()) {
99 1048 : return;
100 : }
101 :
102 182 : auto intervals = spannerMap.findOverlapping(ctx.nominalPositionStartTick,
103 91 : ctx.nominalPositionEndTick,
104 91 : /*excludeCollisions*/ true);
105 :
106 158 : for (const auto& interval : intervals) {
107 67 : const Spanner* spanner = interval.value;
108 :
109 67 : if (!SpannersMetaParser::isAbleToParse(spanner)) {
110 48 : continue;
111 : }
112 :
113 28 : if (SpannerFilter::isMultiStaffSpanner(spanner)) {
114 17 : if (spanner->part() != chord->part()) {
115 0 : continue;
116 : }
117 : } else {
118 11 : if (spanner->staffIdx() != chord->staffIdx()) {
119 0 : continue;
120 : }
121 : }
122 :
123 28 : if (!SpannerFilter::isItemPlayable(spanner, ctx)) {
124 9 : continue;
125 : }
126 :
127 19 : RenderingContext spannerContext = ctx;
128 19 : spannerContext.nominalTimestamp = timestampFromTicks(ctx.score, interval.start + ctx.positionTickOffset);
129 19 : spannerContext.nominalPositionStartTick = interval.start;
130 19 : spannerContext.nominalDurationTicks = SpannerFilter::spannerActualDurationTicks(spanner, interval.stop - interval.start);
131 19 : spannerContext.nominalPositionEndTick = spannerContext.nominalPositionStartTick + spannerContext.nominalDurationTicks;
132 :
133 19 : SpannersMetaParser::parse(spanner, spannerContext, result);
134 19 : }
135 91 : }
136 :
137 1139 : void ChordArticulationsParser::parseArticulationSymbols(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
138 : {
139 1153 : for (const Articulation* articulation : chord->articulations()) {
140 14 : SymbolsMetaParser::parse(articulation, ctx, result);
141 : }
142 1139 : }
143 :
144 1139 : void ChordArticulationsParser::parseAnnotations(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
145 : {
146 1185 : for (const EngravingItem* annotation : chord->segment()->annotations()) {
147 46 : if (annotation->staffIdx() != chord->staffIdx()) {
148 4 : continue;
149 : }
150 :
151 42 : AnnotationsMetaParser::parse(annotation, ctx, result);
152 : }
153 1139 : }
154 :
155 1139 : void ChordArticulationsParser::parseTremolo(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
156 : {
157 : // single chord
158 : {
159 1139 : const TremoloSingleChord* tremoloSingle = chord->tremoloSingleChord();
160 1139 : if (tremoloSingle && tremoloSingle->playTremolo()) {
161 28 : TremoloSingleMetaParser::parse(tremoloSingle, ctx, result);
162 : }
163 : }
164 :
165 : // two chord
166 : {
167 1139 : const TremoloTwoChord* tremoloTwo = chord->tremoloTwoChord();
168 1139 : if (tremoloTwo && tremoloTwo->playTremolo()) {
169 1 : TremoloTwoMetaParser::parse(tremoloTwo, ctx, result);
170 : }
171 : }
172 1139 : }
173 :
174 1139 : void ChordArticulationsParser::parseArpeggio(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
175 : {
176 1139 : const Arpeggio* arpeggio = chord->arpeggio();
177 1139 : if (!arpeggio || arpeggio->isChordBracket()) {
178 1126 : return;
179 : }
180 :
181 13 : if (chord->notes().empty()) {
182 0 : return;
183 : }
184 :
185 13 : ArpeggioMetaParser::parse(arpeggio, ctx, result);
186 : }
187 :
188 1139 : void ChordArticulationsParser::parseGraceNotes(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
189 : {
190 1159 : for (const Chord* graceChord : chord->graceNotes()) {
191 20 : GraceNotesMetaParser::parse(graceChord, ctx, result);
192 : }
193 1139 : }
194 :
195 1139 : void ChordArticulationsParser::parseChordLine(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
196 : {
197 1139 : const ChordLine* chordLine = chord->chordLine();
198 1139 : if (!chordLine || !chordLine->playChordLine()) {
199 1135 : return;
200 : }
201 :
202 4 : ChordLineMetaParser::parse(chordLine, ctx, result);
203 : }
204 :
205 1139 : void ChordArticulationsParser::parseTapping(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
206 : {
207 1139 : const Tapping* tapping = chord->tapping();
208 1139 : if (!tapping || !tapping->playArticulation()) {
209 1139 : return;
210 : }
211 :
212 0 : mpe::ArticulationType type = mpe::ArticulationType::Undefined;
213 :
214 0 : switch (tapping->hand()) {
215 0 : case TappingHand::LEFT:
216 0 : type = mpe::ArticulationType::LeftHandTapping;
217 0 : break;
218 0 : case TappingHand::RIGHT:
219 0 : type = mpe::ArticulationType::RightHandTapping;
220 0 : break;
221 0 : case TappingHand::INVALID:
222 0 : break;
223 : }
224 :
225 0 : if (type == mpe::ArticulationType::Undefined) {
226 0 : return;
227 : }
228 :
229 0 : const mpe::ArticulationPattern& pattern = ctx.profile->pattern(type);
230 0 : if (pattern.empty()) {
231 0 : return;
232 : }
233 :
234 0 : appendArticulationData(mpe::ArticulationMeta(type, pattern, ctx.nominalTimestamp, ctx.nominalDuration), result);
235 : }
236 :
237 1139 : void ChordArticulationsParser::parseBreath(const Chord* chord, const RenderingContext& ctx, mpe::ArticulationMap& result)
238 : {
239 1139 : const mpe::ArticulationPattern& pattern = ctx.profile->pattern(ArticulationType::Breath);
240 1139 : if (pattern.empty()) {
241 1139 : return;
242 : }
243 :
244 0 : const Breath* breath = chord->hasBreathMark();
245 0 : if (!breath) {
246 0 : return;
247 : }
248 :
249 0 : const mpe::timestamp_t timestamp = timestampFromTicks(ctx.score, breath->tick().ticks() + ctx.positionTickOffset);
250 0 : const mpe::duration_t duration = breath->pause() * 1000000;
251 0 : appendArticulationData(mpe::ArticulationMeta(ArticulationType::Breath, pattern, timestamp, duration), result);
252 : }
|