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 : #include "arpeggiorenderer.h"
24 :
25 : #include "dom/chord.h"
26 : #include "dom/arpeggio.h"
27 :
28 : #include "noterenderer.h"
29 :
30 : using namespace mu::engraving;
31 : using namespace muse;
32 : using namespace muse::mpe;
33 :
34 10 : static bool isDirectionUp(const mpe::ArticulationType type)
35 : {
36 10 : switch (type) {
37 2 : case mpe::ArticulationType::ArpeggioDown:
38 : case mpe::ArticulationType::ArpeggioStraightDown:
39 2 : return false;
40 8 : case mpe::ArticulationType::Arpeggio:
41 : case mpe::ArticulationType::ArpeggioUp:
42 : case mpe::ArticulationType::ArpeggioStraightUp:
43 8 : return true;
44 0 : default:
45 0 : return false;
46 : }
47 : }
48 :
49 10 : static usecs_t timestampOffsetStep(const RenderingContext& ctx, int stepCount)
50 : {
51 10 : constexpr int MAX_TIMESTAMP_OFFSET_STEP = 60000;
52 10 : const int offsetStep = ctx.nominalDuration / stepCount;
53 :
54 10 : return std::min(offsetStep, MAX_TIMESTAMP_OFFSET_STEP);
55 : }
56 :
57 10 : static std::map<int /*pitch*/, const Note*> arpeggioNotes(const Chord* chord)
58 : {
59 10 : std::map<int, const Note*> result;
60 35 : for (const Note* note : chord->notes()) {
61 25 : result.emplace(note->pitch(), note);
62 : }
63 :
64 10 : return result;
65 0 : }
66 :
67 32 : const ArticulationTypeSet& ArpeggioRenderer::supportedTypes()
68 : {
69 : static const mpe::ArticulationTypeSet types = {
70 : mpe::ArticulationType::Arpeggio, mpe::ArticulationType::ArpeggioDown,
71 : mpe::ArticulationType::ArpeggioStraightDown, mpe::ArticulationType::ArpeggioStraightUp,
72 : mpe::ArticulationType::ArpeggioUp
73 34 : };
74 :
75 32 : return types;
76 : }
77 :
78 10 : void ArpeggioRenderer::doRender(const EngravingItem* item, const mpe::ArticulationType preferredType,
79 : const RenderingContext& ctx, mpe::PlaybackEventList& result)
80 : {
81 10 : const Chord* chord = toChord(item);
82 10 : IF_ASSERT_FAILED(chord) {
83 0 : return;
84 : }
85 :
86 10 : const Arpeggio* arpeggio = chord->arpeggio();
87 10 : IF_ASSERT_FAILED(arpeggio) {
88 0 : return;
89 : }
90 :
91 10 : const int stepsCount = static_cast<int>(chord->notes().size());
92 10 : const usecs_t offsetStep = timestampOffsetStep(ctx, stepsCount);
93 10 : const double stretch = arpeggio->Stretch();
94 :
95 25 : auto renderEvent = [&](const Note* note, const int stepNumber) {
96 25 : const timestamp_t offset = offsetStep * stepNumber * stretch;
97 :
98 25 : RenderingContext noteCtx(ctx);
99 25 : noteCtx.nominalTimestamp += offset;
100 25 : noteCtx.nominalDuration -= offset;
101 :
102 25 : NoteRenderer::render(note, noteCtx, result);
103 25 : };
104 :
105 10 : const std::map<int, const Note*> notes = arpeggioNotes(chord);
106 :
107 10 : if (isDirectionUp(preferredType)) {
108 27 : for (auto it = notes.begin(); it != notes.end(); ++it) {
109 38 : renderEvent(it->second, std::distance(notes.begin(), it));
110 : }
111 : } else {
112 8 : for (auto it = notes.rbegin(); it != notes.rend(); ++it) {
113 12 : renderEvent(it->second, std::distance(notes.rbegin(), it));
114 : }
115 : }
116 10 : }
|