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 "notearticulationsparser.h"
24 :
25 : #include "dom/measure.h"
26 : #include "dom/note.h"
27 : #include "dom/spanner.h"
28 : #include "dom/laissezvib.h"
29 :
30 : #include "playback/utils/arrangementutils.h"
31 : #include "playback/utils/expressionutils.h"
32 :
33 : #include "internal/spannersmetaparser.h"
34 : #include "internal/symbolsmetaparser.h"
35 :
36 : using namespace mu::engraving;
37 : using namespace muse;
38 : using namespace muse::mpe;
39 :
40 1540 : void NoteArticulationsParser::buildNoteArticulationMap(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
41 : {
42 1540 : if (!note || !ctx.isValid()) {
43 0 : LOGE() << "Unable to render playback events of invalid note";
44 0 : return;
45 : }
46 :
47 1540 : parse(note, ctx, result);
48 :
49 1540 : if (result.empty()) {
50 2034 : appendArticulationData({ mpe::ArticulationType::Standard,
51 1017 : ctx.profile->pattern(mpe::ArticulationType::Standard),
52 1017 : ctx.nominalTimestamp,
53 1017 : ctx.nominalDuration,
54 : 0,
55 : 0 }, result);
56 : }
57 :
58 1540 : result.preCalculateAverageData();
59 : }
60 :
61 1540 : void NoteArticulationsParser::doParse(const EngravingItem* item, const RenderingContext& ctx, mpe::ArticulationMap& result)
62 : {
63 1540 : IF_ASSERT_FAILED(item->isNote()) {
64 0 : return;
65 : }
66 :
67 1540 : const Note* note = toNote(item);
68 :
69 1540 : if (!note || !note->play()) {
70 0 : return;
71 : }
72 :
73 1540 : parsePlayingTechnique(ctx, note->track(), result);
74 1540 : parseGhostNote(note, ctx, result);
75 1540 : parseNoteHead(note, ctx, result);
76 1540 : parseSymbols(note, ctx, result);
77 1540 : parseLaissezVibrer(note, ctx, result);
78 1540 : parseSpanners(note, ctx, result);
79 : }
80 :
81 1544 : ArticulationType NoteArticulationsParser::articulationTypeByNoteheadGroup(const NoteHeadGroup noteheadGroup)
82 : {
83 1544 : switch (noteheadGroup) {
84 44 : case NoteHeadGroup::HEAD_CROSS:
85 44 : return mpe::ArticulationType::CrossNote;
86 :
87 0 : case NoteHeadGroup::HEAD_CIRCLED_LARGE:
88 : case NoteHeadGroup::HEAD_CIRCLED:
89 0 : return mpe::ArticulationType::CircleNote;
90 :
91 0 : case NoteHeadGroup::HEAD_XCIRCLE:
92 0 : return mpe::ArticulationType::CircleCrossNote;
93 :
94 0 : case NoteHeadGroup::HEAD_TRIANGLE_DOWN:
95 0 : return mpe::ArticulationType::TriangleDownNote;
96 0 : case NoteHeadGroup::HEAD_TRIANGLE_UP:
97 0 : return mpe::ArticulationType::TriangleUpNote;
98 :
99 0 : case NoteHeadGroup::HEAD_DIAMOND:
100 : case NoteHeadGroup::HEAD_DIAMOND_OLD:
101 0 : return mpe::ArticulationType::DiamondNote;
102 :
103 0 : case NoteHeadGroup::HEAD_PLUS:
104 0 : return mpe::ArticulationType::PlusNote;
105 :
106 0 : case NoteHeadGroup::HEAD_SLASH:
107 0 : return mpe::ArticulationType::SlashNote;
108 :
109 0 : case NoteHeadGroup::HEAD_SLASHED1:
110 0 : return mpe::ArticulationType::SlashedForwardsNote;
111 :
112 0 : case NoteHeadGroup::HEAD_SLASHED2:
113 0 : return mpe::ArticulationType::SlashedBackwardsNote;
114 :
115 0 : case NoteHeadGroup::HEAD_DO:
116 0 : return mpe::ArticulationType::TriangleUpNote;
117 :
118 0 : case NoteHeadGroup::HEAD_RE:
119 0 : return mpe::ArticulationType::MoonNote;
120 :
121 0 : case NoteHeadGroup::HEAD_FA:
122 0 : return mpe::ArticulationType::TriangleRightNote;
123 :
124 0 : case NoteHeadGroup::HEAD_LA:
125 0 : return mpe::ArticulationType::SquareNote;
126 :
127 0 : case NoteHeadGroup::HEAD_TI:
128 0 : return mpe::ArticulationType::TriangleRoundDownNote;
129 :
130 1500 : default:
131 1500 : return mpe::ArticulationType::Undefined;
132 : }
133 : }
134 :
135 1544 : void NoteArticulationsParser::parsePlayingTechnique(const RenderingContext& ctx, const track_idx_t trackIdx,
136 : mpe::ArticulationMap& result, bool sustainAllowed)
137 : {
138 1544 : const int chordPosTickWithOffset = ctx.nominalPositionStartTick + ctx.positionTickOffset;
139 1544 : const std::pair<timestamp_t, PlayingTechniqueType> tech = ctx.playbackCtx->playingTechnique(trackIdx, chordPosTickWithOffset);
140 1544 : if (tech.second == PlayingTechniqueType::HandbellsLV && !sustainAllowed) {
141 1505 : return;
142 : }
143 :
144 1544 : const mpe::ArticulationType articulationType = articulationFromPlayTechType(tech.second);
145 1544 : if (articulationType == ArticulationType::Standard || articulationType == ArticulationType::Undefined) {
146 1498 : return;
147 : }
148 :
149 46 : const mpe::ArticulationPattern& pattern = ctx.profile->pattern(articulationType);
150 46 : if (pattern.empty()) {
151 7 : return;
152 : }
153 :
154 39 : timestamp_t timestamp = ctx.nominalTimestamp;
155 39 : duration_t duration = ctx.nominalDuration;
156 :
157 39 : if (tech.second == PlayingTechniqueType::HandbellsLV) {
158 12 : const timestamp_t dampTime = ctx.playbackCtx->findPlayingTechniqueTimestamp(trackIdx, PlayingTechniqueType::HandbellsDamp,
159 : chordPosTickWithOffset);
160 12 : timestamp = tech.first;
161 12 : duration = dampTime > 0 ? dampTime - timestamp : mpe::INFINITE_DURATION;
162 : }
163 :
164 39 : appendArticulationData({ articulationType,
165 : pattern,
166 : timestamp,
167 : duration,
168 : 0,
169 : 0 }, result);
170 : }
171 :
172 1544 : void NoteArticulationsParser::parseGhostNote(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
173 : {
174 1544 : if (!note->ghost() && !note->parenthesisInfo()) {
175 1544 : return;
176 : }
177 :
178 0 : appendArticulations({ mpe::ArticulationType::GhostNote }, ctx, result);
179 : }
180 :
181 1544 : void NoteArticulationsParser::parseNoteHead(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
182 : {
183 1544 : mpe::ArticulationType typeByNoteHeadGroup = articulationTypeByNoteheadGroup(note->headGroup());
184 :
185 1544 : if (typeByNoteHeadGroup != mpe::ArticulationType::Undefined) {
186 132 : appendArticulations({ typeByNoteHeadGroup }, ctx, result);
187 1500 : } else if (note->ldata()->cachedNoteheadSym.has_value()) {
188 1500 : SymId symId = note->ldata()->cachedNoteheadSym.value(); // fastest way to get the notehead symbol
189 1500 : ArticulationTypeSet types = SymbolsMetaParser::symbolToArticulations(symId);
190 1500 : appendArticulations(types, ctx, result);
191 1500 : }
192 1544 : }
193 :
194 1544 : void NoteArticulationsParser::parseSymbols(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
195 : {
196 1544 : for (const EngravingItem* item : note->el()) {
197 0 : if (item && item->isSymbol()) {
198 0 : ArticulationTypeSet types = SymbolsMetaParser::symbolToArticulations(toSymbol(item)->sym());
199 0 : appendArticulations(types, ctx, result);
200 0 : }
201 : }
202 1544 : }
203 :
204 1540 : void NoteArticulationsParser::parseLaissezVibrer(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
205 : {
206 1540 : const LaissezVib* laissezVib = note->laissezVib();
207 1540 : if (!laissezVib || !laissezVib->playSpanner()) {
208 1540 : return;
209 : }
210 :
211 0 : const mpe::ArticulationPattern& pattern = ctx.profile->pattern(mpe::ArticulationType::LaissezVibrer);
212 0 : if (pattern.empty()) {
213 0 : return;
214 : }
215 :
216 0 : const Measure* noteMeasure = note->findMeasure();
217 0 : if (!noteMeasure) {
218 0 : return;
219 : }
220 :
221 0 : const Measure* nextMeasure = noteMeasure->nextMeasure();
222 0 : const Fraction endTick = nextMeasure ? nextMeasure->endTick() : noteMeasure->endTick();
223 0 : const timestamp_t endTime = timestampFromTicks(ctx.score, endTick.ticks() + ctx.positionTickOffset);
224 :
225 0 : appendArticulationData(mpe::ArticulationMeta(mpe::ArticulationType::LaissezVibrer,
226 : pattern,
227 0 : ctx.nominalTimestamp,
228 0 : endTime - ctx.nominalTimestamp), result);
229 : }
230 :
231 1540 : void NoteArticulationsParser::parseSpanners(const Note* note, const RenderingContext& ctx, mpe::ArticulationMap& result)
232 : {
233 1567 : for (const Spanner* spanner : note->spannerFor()) {
234 27 : int spannerFrom = spanner->tick().ticks();
235 27 : int spannerTo = spannerFrom + std::abs(spanner->ticks().ticks());
236 27 : int spannerDurationTicks = spannerTo - spannerFrom;
237 :
238 27 : if (spannerDurationTicks == 0) {
239 8 : spannerDurationTicks = ctx.nominalDurationTicks;
240 : }
241 :
242 : auto spannerTnD
243 27 : = timestampAndDurationFromStartAndDurationTicks(ctx.score, spannerFrom, spannerDurationTicks, ctx.positionTickOffset);
244 :
245 27 : RenderingContext spannerContext = ctx;
246 27 : spannerContext.nominalTimestamp = spannerTnD.timestamp;
247 27 : spannerContext.nominalDuration = spannerTnD.duration;
248 27 : spannerContext.nominalPositionStartTick = spannerFrom;
249 27 : spannerContext.nominalDurationTicks = spannerDurationTicks;
250 :
251 27 : SpannersMetaParser::parse(spanner, spannerContext, result);
252 27 : }
253 1540 : }
254 :
255 1544 : void NoteArticulationsParser::appendArticulations(const mpe::ArticulationTypeSet& types, const RenderingContext& ctx,
256 : mpe::ArticulationMap& result)
257 : {
258 1588 : for (mpe::ArticulationType type : types) {
259 44 : if (type == mpe::ArticulationType::Undefined) {
260 0 : continue;
261 : }
262 :
263 44 : const mpe::ArticulationPattern& pattern = ctx.profile->pattern(type);
264 44 : if (pattern.empty()) {
265 44 : continue;
266 : }
267 :
268 0 : appendArticulationData(mpe::ArticulationMeta(type,
269 : pattern,
270 0 : ctx.nominalTimestamp,
271 0 : ctx.nominalDuration), result);
272 : }
273 1544 : }
|