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 "ornament.h"
24 :
25 : #include "accidental.h"
26 : #include "chord.h"
27 : #include "engravingitem.h"
28 : #include "factory.h"
29 : #include "key.h"
30 : #include "note.h"
31 : #include "score.h"
32 : #include "shape.h"
33 : #include "staff.h"
34 : #include "utils.h"
35 :
36 : #include "editing/editchord.h"
37 : #include "editing/editparentheses.h"
38 : #include "editing/transaction/transaction.h"
39 : #include "editing/transpose.h"
40 :
41 : using namespace mu::engraving;
42 :
43 281 : Ornament::Ornament(DummyParentOr<ChordRest> parent)
44 281 : : Articulation(parent, ElementType::ORNAMENT)
45 : {
46 281 : _intervalAbove = DEFAULT_ORNAMENT_INTERVAL;
47 281 : _intervalBelow = DEFAULT_ORNAMENT_INTERVAL;
48 281 : _showAccidental = OrnamentShowAccidental::DEFAULT;
49 281 : _startOnUpperNote = false;
50 281 : }
51 :
52 4 : Ornament::Ornament(const Ornament& o)
53 4 : : Articulation(o)
54 : {
55 4 : _intervalAbove = o._intervalAbove;
56 4 : _intervalBelow = o._intervalBelow;
57 4 : _showAccidental = o._showAccidental;
58 4 : _startOnUpperNote = o._startOnUpperNote;
59 4 : m_showCueNote = o.m_showCueNote;
60 :
61 4 : if (o.m_cueNoteChord) {
62 4 : m_cueNoteChord = o.m_cueNoteChord->clone();
63 : }
64 :
65 24 : for (size_t i = 0; i < m_accidentalsAboveAndBelow.size(); ++i) {
66 8 : Accidental* oldAccidental = o.m_accidentalsAboveAndBelow[i];
67 8 : if (!oldAccidental) {
68 8 : continue;
69 : }
70 0 : Accidental* newAccidental = oldAccidental->clone();
71 0 : newAccidental->setOwnershipParent(this);
72 0 : m_accidentalsAboveAndBelow[i] = newAccidental;
73 : }
74 4 : }
75 :
76 538 : Ornament::~Ornament()
77 : {
78 807 : std::fill(std::begin(m_notesAboveAndBelow), std::end(m_notesAboveAndBelow), nullptr);
79 807 : std::fill(std::begin(m_accidentalsAboveAndBelow), std::end(m_accidentalsAboveAndBelow), nullptr);
80 :
81 269 : if (m_cueNoteChord && m_cueNoteChord->notes().size()) {
82 7 : m_cueNoteChord->notes().clear();
83 : }
84 :
85 269 : m_cueNoteChord = nullptr;
86 538 : }
87 :
88 0 : void Ornament::remove(EngravingItem* e)
89 : {
90 0 : if (e->isAccidental()) {
91 0 : for (Accidental*& acc : m_accidentalsAboveAndBelow) {
92 0 : if (e == acc) {
93 0 : acc = nullptr;
94 0 : e->removed();
95 : }
96 : }
97 : }
98 0 : }
99 :
100 0 : muse::TranslatableString Ornament::typeUserName() const
101 : {
102 0 : if (textType() != ArticulationTextType::NO_TEXT) {
103 0 : return TranslatableString("engraving", "Ornament text");
104 : }
105 :
106 0 : return TranslatableString("engraving", "Ornament");
107 : }
108 :
109 518 : void Ornament::setTrack(track_idx_t val)
110 : {
111 1554 : for (Note* note : m_notesAboveAndBelow) {
112 1036 : if (note) {
113 12 : note->setTrack(val);
114 : }
115 : }
116 518 : if (m_cueNoteChord) {
117 14 : m_cueNoteChord->setTrack(val);
118 : }
119 518 : m_track = val;
120 518 : }
121 :
122 12 : void Ornament::scanElements(std::function<void(EngravingItem*)> func)
123 : {
124 12 : func(this);
125 36 : for (Accidental* accidental : m_accidentalsAboveAndBelow) {
126 24 : if (accidental) {
127 2 : func(accidental);
128 : }
129 : }
130 12 : if (m_cueNoteChord) {
131 0 : m_cueNoteChord->scanElements(func);
132 : }
133 12 : }
134 :
135 2502 : PropertyValue Ornament::getProperty(Pid propertyId) const
136 : {
137 2502 : switch (propertyId) {
138 146 : case Pid::INTERVAL_ABOVE:
139 146 : return _intervalAbove;
140 146 : case Pid::INTERVAL_BELOW:
141 146 : return _intervalBelow;
142 146 : case Pid::ORNAMENT_SHOW_ACCIDENTAL:
143 146 : return _showAccidental;
144 146 : case Pid::ORNAMENT_SHOW_CUE_NOTE:
145 146 : return m_showCueNote;
146 146 : case Pid::START_ON_UPPER_NOTE:
147 146 : return _startOnUpperNote;
148 1772 : default:
149 1772 : return Articulation::getProperty(propertyId);
150 : }
151 : }
152 :
153 152 : Sid Ornament::getPropertyStyle(Pid propertyId) const
154 : {
155 152 : switch (propertyId) {
156 3 : case Pid::ARTICULATION_ANCHOR:
157 3 : return Sid::articulationAnchorDefault;
158 149 : default:
159 149 : return Articulation::getPropertyStyle(propertyId);
160 : }
161 : }
162 :
163 2789 : PropertyValue Ornament::propertyDefault(Pid id) const
164 : {
165 2789 : switch (id) {
166 292 : case Pid::INTERVAL_ABOVE:
167 : case Pid::INTERVAL_BELOW:
168 292 : return DEFAULT_ORNAMENT_INTERVAL;
169 146 : case Pid::ORNAMENT_SHOW_ACCIDENTAL:
170 292 : return OrnamentShowAccidental::DEFAULT;
171 146 : case Pid::ORNAMENT_SHOW_CUE_NOTE:
172 292 : return AutoOnOff::AUTO;
173 146 : case Pid::START_ON_UPPER_NOTE:
174 146 : return false;
175 307 : case Pid::ARTICULATION_ANCHOR:
176 307 : return static_cast<int>(ArticulationAnchor::AUTO);
177 1752 : default:
178 1752 : return Articulation::propertyDefault(id);
179 : }
180 : }
181 :
182 153 : bool Ornament::setProperty(Pid propertyId, const PropertyValue& v)
183 : {
184 153 : switch (propertyId) {
185 19 : case Pid::INTERVAL_ABOVE:
186 19 : setIntervalAbove(v.value<OrnamentInterval>());
187 19 : break;
188 8 : case Pid::INTERVAL_BELOW:
189 8 : setIntervalBelow(v.value<OrnamentInterval>());
190 8 : break;
191 0 : case Pid::ORNAMENT_SHOW_ACCIDENTAL:
192 0 : setShowAccidental(v.value<OrnamentShowAccidental>());
193 0 : break;
194 0 : case Pid::ORNAMENT_SHOW_CUE_NOTE:
195 0 : setShowCueNote(v.value<AutoOnOff>());
196 0 : break;
197 0 : case Pid::START_ON_UPPER_NOTE:
198 0 : setStartOnUpperNote(v.toBool());
199 0 : break;
200 126 : default:
201 126 : return Articulation::setProperty(propertyId, v);
202 : }
203 27 : triggerLayout();
204 27 : return true;
205 : }
206 :
207 916 : bool Ornament::hasIntervalAbove() const
208 : {
209 916 : SymId id = symId();
210 : return id == SymId::ornamentTurnInverted
211 846 : || id == SymId::ornamentTrill
212 554 : || id == SymId::ornamentTurnSlash
213 544 : || id == SymId::ornamentTurn
214 452 : || id == SymId::ornamentShortTrill
215 382 : || id == SymId::ornamentTremblement
216 352 : || id == SymId::ornamentPrallMordent
217 322 : || id == SymId::ornamentUpPrall
218 292 : || id == SymId::ornamentPrecompMordentUpperPrefix
219 262 : || id == SymId::ornamentUpMordent
220 1762 : || id == SymId::ornamentPrallUp;
221 : }
222 :
223 916 : bool Ornament::hasIntervalBelow() const
224 : {
225 916 : SymId id = symId();
226 : return id == SymId::ornamentTurnInverted
227 846 : || id == SymId::ornamentMordent
228 766 : || id == SymId::ornamentTurnSlash
229 756 : || id == SymId::ornamentTurn
230 664 : || id == SymId::ornamentTremblement
231 634 : || id == SymId::ornamentPrallMordent
232 604 : || id == SymId::ornamentDownMordent
233 574 : || id == SymId::ornamentPrallDown
234 554 : || id == SymId::ornamentPrallMordent
235 1762 : || id == SymId::ornamentPrallMordent;
236 : }
237 :
238 1648 : bool Ornament::hasFullIntervalChoice() const
239 : {
240 1648 : SymId id = symId();
241 1648 : return id == SymId::ornamentTrill;
242 : }
243 :
244 1648 : bool Ornament::showCueNote()
245 : {
246 1648 : if (m_showCueNote == AutoOnOff::AUTO) {
247 1648 : return (hasFullIntervalChoice() && style().styleB(Sid::trillAlwaysShowCueNote)) || _intervalAbove.step != IntervalStep::SECOND;
248 : }
249 :
250 0 : return m_showCueNote == AutoOnOff::ON;
251 : }
252 :
253 421 : void Ornament::computeNotesAboveAndBelow(AccidentalState* accState)
254 : {
255 421 : ChordRest* parentChordRest = chordRest();
256 421 : Chord* parentChord = parentChordRest && parentChordRest->isChord() ? toChord(parentChordRest) : nullptr;
257 :
258 421 : const Note* mainNote = parentChord ? parentChord->upNote() : nullptr;
259 :
260 421 : if (!mainNote) {
261 0 : return;
262 : }
263 :
264 421 : if (m_cueNoteChord && !m_cueNoteChord->ownershipParent()) {
265 7 : m_cueNoteChord->setOwnershipParent(parentChord->segment());
266 : }
267 :
268 2526 : for (size_t i = 0; i < m_notesAboveAndBelow.size(); ++i) {
269 842 : bool above = (i == 0);
270 842 : bool hasIntAbove = hasIntervalAbove();
271 842 : bool hasIntBelow = hasIntervalBelow();
272 :
273 842 : if ((above && !hasIntAbove) || (!above && !hasIntBelow)) {
274 356 : continue;
275 : }
276 :
277 486 : Note*& note = m_notesAboveAndBelow.at(i);
278 486 : if (!note && above && m_cueNoteChord) {
279 1 : note = m_cueNoteChord->upNote();
280 : }
281 :
282 486 : if (!note) {
283 308 : note = mainNote->clone();
284 308 : Tie* tie = note->tieFor();
285 308 : if (tie) {
286 6 : score()->undoRemoveElement((EngravingItem*)tie);
287 : }
288 : } else {
289 178 : note->setTpc1(mainNote->tpc1());
290 178 : note->setTpc2(mainNote->tpc2());
291 178 : note->setPitch(mainNote->pitch());
292 : }
293 486 : note->setTrack(track());
294 :
295 486 : if (Accidental::isMicrotonal(note->accidentalType())) {
296 : // If mainNote has microtonal accidental, don't clone it to the ornament note because microtonal intervals are not supported.
297 0 : note->setAccidentalType(Accidental::value2subtype(tpc2alter(note->tpc())));
298 : }
299 :
300 486 : bool autoMode = (above && _intervalAbove.type == IntervalType::AUTO) || (!above && _intervalBelow.type == IntervalType::AUTO);
301 486 : if (autoMode) {
302 : // NOTE: In AUTO mode, the ornament note should match not only any alteration from the
303 : // key signature, but also any alteration present in the measure before this point.
304 430 : int intervalSteps = above ? static_cast<int>(_intervalAbove.step) : -static_cast<int>(_intervalBelow.step);
305 430 : note->transposeDiatonic(intervalSteps, false, true);
306 430 : if (m_trillOldCompatAccidental) {
307 0 : mapOldTrillAccidental(note, mainNote);
308 0 : m_trillOldCompatAccidental = nullptr;
309 : } else {
310 430 : int pitchLine = absStep(note->tpc(), note->epitch());
311 430 : AccidentalVal accidentalVal = accState->accidentalVal(pitchLine);
312 430 : AccidentalVal noteAccidentalVal = tpc2alter(note->tpc());
313 430 : int accidentalDiff = static_cast<int>(accidentalVal) - static_cast<int>(noteAccidentalVal);
314 430 : note->transpose(Interval(0, accidentalDiff), true);
315 : }
316 : } else {
317 56 : Interval interval = Interval::fromOrnamentInterval(above ? _intervalAbove : _intervalBelow);
318 56 : if (!above) {
319 15 : interval.flip();
320 : }
321 56 : note->transpose(interval, true);
322 : }
323 :
324 486 : AccidentalState copyOfAccState = *accState;
325 486 : note->updateAccidental(©OfAccState);
326 486 : if (note->accidental()) {
327 55 : int pitchLine = absStep(note->tpc(), note->epitch());
328 55 : accState->setForceRestateAccidental(pitchLine, true);
329 : }
330 :
331 486 : manageAccidentalVisibilityRules(note);
332 : }
333 :
334 421 : updateAccidentalsAboveAndBelow();
335 421 : updateCueNote();
336 : }
337 :
338 486 : void Ornament::manageAccidentalVisibilityRules(Note* note)
339 : {
340 486 : if (_showAccidental == OrnamentShowAccidental::DEFAULT) {
341 486 : return;
342 : }
343 :
344 0 : AccidentalVal accidentalValue = tpc2alter(note->tpc());
345 0 : AccidentalType accidentalType = Accidental::value2subtype(accidentalValue);
346 0 : if (accidentalType == AccidentalType::NONE) {
347 0 : accidentalType = AccidentalType::NATURAL;
348 : }
349 0 : if (note->accidentalType() == accidentalType) {
350 0 : return;
351 : }
352 :
353 0 : bool show = false;
354 0 : if (_showAccidental == OrnamentShowAccidental::ALWAYS) {
355 0 : show = true;
356 0 : } else if (_showAccidental == OrnamentShowAccidental::ANY_ALTERATION) {
357 0 : Key key = staff()->key(tick());
358 0 : AccidentalState unalteredState;
359 0 : unalteredState.init(key);
360 0 : int pitchLine = absStep(note->tpc(), note->epitch() + note->ottaveCapoFret());
361 0 : AccidentalVal unalteredAccidentalVal = unalteredState.accidentalVal(pitchLine);
362 0 : show = accidentalValue != unalteredAccidentalVal;
363 : }
364 :
365 0 : if (show) {
366 0 : note->setAccidentalType(accidentalType);
367 0 : note->accidental()->setRole(AccidentalRole::AUTO);
368 : }
369 : }
370 :
371 421 : void Ornament::updateAccidentalsAboveAndBelow()
372 : {
373 2526 : for (size_t i = 0; i < m_notesAboveAndBelow.size(); ++i) {
374 842 : Note* note = m_notesAboveAndBelow[i];
375 842 : Accidental* accidental = note ? note->accidental() : nullptr;
376 842 : Accidental*& curAccidental = m_accidentalsAboveAndBelow[i];
377 842 : if (showCueNote() || !accidental) {
378 803 : if (curAccidental) {
379 0 : delete curAccidental;
380 0 : curAccidental = nullptr;
381 : }
382 803 : continue;
383 : }
384 39 : if (accidental) {
385 39 : if (!curAccidental) {
386 0 : curAccidental = accidental->clone();
387 0 : curAccidental->setOwnershipParent(this);
388 0 : curAccidental->setPlacement(i == 0 ? PlacementV::ABOVE : PlacementV::BELOW);
389 : } else {
390 39 : curAccidental->setAccidentalType(accidental->accidentalType());
391 39 : curAccidental->setRole(accidental->role());
392 : }
393 : }
394 : }
395 421 : }
396 :
397 421 : void Ornament::updateCueNote()
398 : {
399 421 : ChordRest* parentChordRest = chordRest();
400 421 : Chord* parentChord = parentChordRest && parentChordRest->isChord() ? toChord(parentChordRest) : nullptr;
401 :
402 421 : if (!showCueNote()) {
403 405 : if (noteAbove() && ownershipParent()) {
404 304 : noteAbove()->setOwnershipParent(parentChord);
405 : }
406 405 : if (m_cueNoteChord) {
407 0 : m_cueNoteChord->notes().clear();
408 0 : delete m_cueNoteChord;
409 0 : m_cueNoteChord = nullptr;
410 : }
411 405 : return;
412 : }
413 :
414 16 : if (!parentChord) {
415 0 : return;
416 : }
417 :
418 16 : Note* cueNote = noteAbove();
419 : // If needed, create cue note
420 16 : if (!m_cueNoteChord) {
421 0 : m_cueNoteChord = Factory::createChord(parentChord->segment());
422 0 : m_cueNoteChord->setSmall(true);
423 0 : cueNote->setHeadType(NoteHeadType::HEAD_QUARTER);
424 0 : m_cueNoteChord->add(cueNote);
425 0 : cueNote->setOwnershipParent(m_cueNoteChord);
426 :
427 0 : Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
428 0 : EditParentheses::addParenthesesToNotes(tx, { cueNote });
429 : }
430 16 : m_cueNoteChord->setTrack(track());
431 16 : m_cueNoteChord->setOwnershipParent(parentChord->segment());
432 16 : m_cueNoteChord->setStaffMove(parentChord->staffMove());
433 16 : cueNote->updateLine();
434 16 : cueNote->setIsTrillCueNote(true);
435 : }
436 :
437 67 : SymId Ornament::fromTrillType(TrillType trillType)
438 : {
439 67 : switch (trillType) {
440 54 : case TrillType::TRILL_LINE:
441 54 : return SymId::ornamentTrill;
442 4 : case TrillType::UPPRALL_LINE:
443 4 : return SymId::ornamentUpPrall;
444 4 : case TrillType::DOWNPRALL_LINE:
445 4 : return SymId::ornamentDownMordent;
446 5 : case TrillType::PRALLPRALL_LINE:
447 5 : return SymId::ornamentTrill;
448 0 : default:
449 0 : return SymId::noSym;
450 : }
451 : }
452 :
453 0 : void Ornament::mapOldTrillAccidental(Note* note, const Note* mainNote)
454 : {
455 : // Compatibility with trills pre-4.1
456 0 : AccidentalVal oldCompatValue = Accidental::subtype2value(m_trillOldCompatAccidental->accidentalType());
457 0 : AccidentalVal noteAccidentalVal = tpc2alter(note->tpc());
458 0 : int accidentalDiff = static_cast<int>(oldCompatValue) - static_cast<int>(noteAccidentalVal);
459 0 : note->transpose(Interval(0, accidentalDiff), true);
460 0 : int semitones = note->pitch() - mainNote->pitch();
461 0 : switch (semitones) {
462 0 : case 0:
463 0 : _intervalAbove.type = IntervalType::DIMINISHED;
464 0 : break;
465 0 : case 1:
466 0 : _intervalAbove.type = IntervalType::MINOR;
467 0 : break;
468 0 : case 2:
469 0 : _intervalAbove.type = IntervalType::MAJOR;
470 0 : break;
471 0 : case 3:
472 0 : _intervalAbove.type = IntervalType::AUGMENTED;
473 0 : break;
474 0 : default:
475 0 : break;
476 : }
477 0 : }
|