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) 2024 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 : #include "bendbuilder.h"
23 :
24 : #include <algorithm>
25 :
26 : #include <engraving/dom/chord.h>
27 : #include <engraving/dom/guitarbend.h>
28 : #include <engraving/dom/factory.h>
29 : #include <engraving/dom/measure.h>
30 : #include <engraving/dom/note.h>
31 : #include <engraving/dom/score.h>
32 : #include <engraving/dom/part.h>
33 : #include <engraving/dom/staff.h>
34 : #include <engraving/dom/stringdata.h>
35 : #include <engraving/dom/tie.h>
36 : #include <engraving/dom/tuplet.h>
37 :
38 : #include "../utils.h"
39 :
40 : #include "global/log.h"
41 :
42 : using namespace mu::engraving;
43 :
44 : namespace mu::iex::guitarpro {
45 : static std::vector<Chord*> createGraceChords(Chord* chord, const grace_segment_map_t& bendInfo);
46 : static void reindexGraceNotes(Chord* chord);
47 : static void createSlightBends(const BendDataContext& bendDataCtx, Score* score);
48 : static void createPreBends(const BendDataContext& bendDataCtx, Score* score);
49 : static void createPreDives(const DiveDataContext& diveDataCtx, Score* score);
50 : static void createGraceAfterNotes(const GraceAfterTrackMap& data, GuitarBendType type, Score* score);
51 : static void createTiedSpans(const TiedNotesTrackMap& data, GuitarBendType type, Score* score);
52 :
53 294 : void BendBuilder::addElementsToScore(Score* score, const BendDataContext& bendCtx, const DiveDataContext& diveCtx)
54 : {
55 294 : createPreBends(bendCtx, score);
56 294 : createSlightBends(bendCtx, score);
57 294 : createGraceAfterNotes(bendCtx.graceAfterBendData, GuitarBendType::BEND, score);
58 294 : createTiedSpans(bendCtx.tiedNotesBendsData, GuitarBendType::BEND, score);
59 294 : createPreDives(diveCtx, score);
60 294 : createTiedSpans(diveCtx.tiedNotesDivesData, GuitarBendType::DIVE, score);
61 294 : createGraceAfterNotes(diveCtx.graceAfterDiveData, GuitarBendType::DIVE, score);
62 :
63 294 : if (bendCtx.splitChordCtx) {
64 0 : m_bendDataProcessorSplitChord = std::make_unique<BendDataProcessorSplitChord>(score);
65 0 : m_bendDataProcessorSplitChord->processBends(*bendCtx.splitChordCtx);
66 : }
67 294 : }
68 :
69 294 : static void createSlightBends(const BendDataContext& bendDataCtx, mu::engraving::Score* score)
70 : {
71 298 : for (const auto& [track, trackInfo] : bendDataCtx.slightBendData) {
72 10 : for (const auto& [tick, tickInfo] : trackInfo) {
73 6 : Chord* chord = utils::getLocatedChord(score, tick, track);
74 14 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
75 8 : if (!muse::contains(tickInfo, noteIndex)) {
76 0 : continue;
77 : }
78 :
79 8 : Note* note = chord->notes()[noteIndex];
80 8 : const auto& noteBendData = tickInfo.at(noteIndex);
81 8 : GuitarBend* bend = chord->score()->addGuitarBend(GuitarBendType::SLIGHT_BEND, note);
82 8 : IF_ASSERT_FAILED(bend) {
83 0 : LOGE() << "bend wasn't created for track " << chord->track() << ", tick " << chord->tick().ticks();
84 0 : continue;
85 : }
86 :
87 8 : bend->setStartTimeFactor(noteBendData.startFactor);
88 8 : bend->setEndTimeFactor(noteBendData.endFactor);
89 : }
90 : }
91 : }
92 294 : }
93 :
94 294 : static void createPreBends(const BendDataContext& bendDataCtx, mu::engraving::Score* score)
95 : {
96 307 : for (const auto& [track, trackInfo] : bendDataCtx.prebendData) {
97 37 : for (const auto& [tick, tickInfo] : trackInfo) {
98 24 : Chord* chord = utils::getLocatedChord(score, tick, track);
99 55 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
100 31 : if (!muse::contains(tickInfo, noteIndex)) {
101 7 : continue;
102 : }
103 :
104 24 : Note* note = chord->notes()[noteIndex];
105 : // NOTE: addGuitarBend will create grace note and transpose it down. We need the pitch of current note instead to properly fret the note.
106 24 : note->transposeDiatonic(1, true, false);
107 24 : const auto& noteBendData = tickInfo.at(noteIndex);
108 24 : GuitarBend* bend = chord->score()->addGuitarBend(GuitarBendType::PRE_BEND, note);
109 24 : IF_ASSERT_FAILED(bend) {
110 0 : LOGE() << "prebend wasn't created for track " << chord->track() << ", tick " << chord->tick().ticks();
111 0 : continue;
112 : }
113 :
114 : // NOTE: returning pitch.
115 24 : note->transposeDiatonic(-1, true, false);
116 24 : bend->setStartTimeFactor(noteBendData.startFactor);
117 24 : bend->setEndTimeFactor(noteBendData.endFactor);
118 :
119 24 : const int pitch = noteBendData.quarterTones / 2;
120 24 : note->setPitch(note->pitch() + pitch);
121 24 : note->setTpcFromPitch();
122 24 : int quarterOff = noteBendData.quarterTones % 2;
123 24 : bend->setEndNotePitch(note->pitch(), quarterOff);
124 24 : Note* startNote = bend->startNote();
125 24 : if (!startNote) {
126 0 : return;
127 : }
128 :
129 24 : startNote->setPitch(note->pitch() - pitch);
130 24 : startNote->setTpcFromPitch();
131 24 : if (note->displayFret() == Note::DisplayFretOption::Hide) {
132 0 : startNote->setDisplayFret(Note::DisplayFretOption::Hide);
133 : }
134 :
135 24 : int newPitch = note->pitch();
136 24 : Note* tiedNote = nullptr;
137 :
138 24 : Tie* tieFor = startNote->tieFor();
139 24 : if (tieFor) {
140 0 : tiedNote = tieFor->endNote();
141 0 : startNote->remove(tieFor);
142 : }
143 :
144 24 : while (tiedNote) {
145 0 : tiedNote->setPitch(newPitch);
146 0 : tiedNote->setTpcFromPitch();
147 0 : Tie* tie = tiedNote->tieFor();
148 0 : if (!tie) {
149 0 : break;
150 : }
151 :
152 0 : tiedNote = tie->endNote();
153 : }
154 : }
155 : }
156 : }
157 : }
158 :
159 124 : static void reindexGraceNotes(Chord* chord)
160 : {
161 124 : int gi = 0;
162 324 : for (Chord* c : chord->graceNotes()) {
163 200 : c->setGraceIndex(gi++);
164 : }
165 124 : }
166 :
167 137 : static Chord* createGraceAfterChord(Chord* parent)
168 : {
169 137 : Chord* graceChord = Factory::createChord(parent->score()->dummy());
170 137 : graceChord->setTrack(parent->track());
171 137 : graceChord->setNoteType(NoteType::GRACE8_AFTER);
172 137 : graceChord->setNoStem(true);
173 137 : graceChord->setBeamMode(BeamMode::NONE);
174 :
175 137 : TDuration dur;
176 137 : dur.setVal(mu::engraving::Constants::DIVISION / 2);
177 137 : graceChord->setDurationType(dur);
178 137 : graceChord->setTicks(dur.fraction());
179 137 : parent->add(graceChord);
180 :
181 137 : return graceChord;
182 : }
183 :
184 110 : static std::vector<Chord*> createGraceChords(Chord* chord, const grace_segment_map_t& bendInfo)
185 : {
186 110 : std::vector<Chord*> graceChords;
187 :
188 110 : size_t maxGraceAmount = 0;
189 236 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
190 126 : if (!muse::contains(bendInfo, noteIndex)) {
191 2 : continue;
192 : }
193 124 : maxGraceAmount = std::max(maxGraceAmount, bendInfo.at(noteIndex).data.size());
194 : }
195 :
196 247 : for (size_t i = 0; i < maxGraceAmount; i++) {
197 137 : graceChords.push_back(createGraceAfterChord(chord));
198 : }
199 :
200 110 : return graceChords;
201 0 : }
202 :
203 588 : static void createGraceAfterNotes(const GraceAfterTrackMap& data, GuitarBendType type, Score* score)
204 : {
205 647 : for (const auto& [track, trackInfo] : data) {
206 169 : for (const auto& [tick, tickEntry] : trackInfo) {
207 110 : Chord* chord = utils::getLocatedChord(score, tick, track);
208 110 : std::vector<Chord*> graceChords = createGraceChords(chord, tickEntry);
209 :
210 236 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
211 126 : if (!muse::contains(tickEntry, noteIndex)) {
212 2 : continue;
213 : }
214 :
215 124 : Note* mainNote = chord->notes()[noteIndex];
216 124 : const auto& graceData = tickEntry.at(noteIndex);
217 124 : const auto& lastGraceData = graceData.lastNoteData;
218 :
219 124 : const bool isDive = (type == GuitarBendType::DIVE);
220 :
221 124 : Note* currentNote = mainNote;
222 278 : for (size_t graceIndex = 0; graceIndex < graceData.data.size(); graceIndex++) {
223 154 : const auto& noteData = graceData.data[graceIndex];
224 154 : Chord* graceChord = graceChords[graceIndex];
225 :
226 154 : Note* graceNote = Factory::createNote(graceChord);
227 154 : const int gracePitch = currentNote->pitch() + noteData.quarterTones / 2;
228 154 : graceNote->setPitch(gracePitch);
229 154 : graceNote->setTpcFromPitch();
230 154 : graceChord->add(graceNote);
231 :
232 154 : if (isDive) {
233 81 : graceNote->setString(mainNote->string());
234 81 : const Staff* staff = mainNote->staff();
235 81 : const StringData* sd = mainNote->part()->stringData(mainNote->tick(), staff->idx());
236 81 : const int offset = staff->pitchOffset(mainNote->tick());
237 81 : const int fret = sd->fret(gracePitch + offset, mainNote->string(), staff);
238 81 : if (fret >= 0) {
239 67 : graceNote->setFret(fret);
240 : }
241 : }
242 :
243 154 : GuitarBend* bend = score->addGuitarBend(type, currentNote, graceNote);
244 154 : IF_ASSERT_FAILED(bend) {
245 0 : LOGE() << "grace-after spanner not created for track " << track
246 0 : << ", tick " << tick.ticks();
247 0 : break;
248 : }
249 :
250 : // Bend quarterTones are absolute from chain start, dive quarterTones are deltas
251 : const int endPitch = isDive
252 154 : ? currentNote->pitch() + noteData.quarterTones / 2
253 73 : : bend->startNoteOfChain()->pitch() + noteData.quarterTones / 2;
254 :
255 154 : bend->setEndNotePitch(endPitch, noteData.quarterTones % 2);
256 154 : bend->setStartTimeFactor(noteData.startFactor);
257 154 : bend->setEndTimeFactor(noteData.endFactor);
258 :
259 154 : currentNote = graceNote;
260 : }
261 :
262 124 : reindexGraceNotes(mainNote->chord());
263 :
264 124 : if (lastGraceData.shouldMoveTie) {
265 5 : Tie* tieFor = mainNote->tieFor();
266 5 : if (tieFor && tieFor->endNote()) {
267 5 : Note* tiedNote = tieFor->endNote();
268 5 : mainNote->remove(tieFor);
269 5 : GuitarBend* tieBend = score->addGuitarBend(type, currentNote, tiedNote);
270 5 : if (tieBend) {
271 5 : tieBend->setEndTimeFactor(lastGraceData.endFactor);
272 : }
273 : }
274 : }
275 : }
276 110 : }
277 : }
278 588 : }
279 :
280 294 : static void createPreDives(const DiveDataContext& diveDataCtx, Score* score)
281 : {
282 322 : for (const auto& [track, trackInfo] : diveDataCtx.preDiveData) {
283 67 : for (const auto& [tick, tickInfo] : trackInfo) {
284 39 : Chord* chord = utils::getLocatedChord(score, tick, track);
285 39 : if (!chord) {
286 0 : continue;
287 : }
288 :
289 81 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
290 42 : if (!muse::contains(tickInfo, noteIndex)) {
291 0 : continue;
292 : }
293 :
294 42 : Note* note = chord->notes()[noteIndex];
295 42 : const SegmentData& pd = tickInfo.at(noteIndex);
296 :
297 42 : const int writtenPitch = note->pitch();
298 42 : const int originalString = note->string();
299 42 : const int originalFret = note->fret();
300 42 : const int attackPitch = writtenPitch - pd.quarterTones / 2;
301 :
302 42 : note->transposeDiatonic(-1, true, false);
303 42 : GuitarBend* preDive = score->addGuitarBend(GuitarBendType::PRE_DIVE, note);
304 42 : note->transposeDiatonic(1, true, false);
305 :
306 42 : IF_ASSERT_FAILED(preDive) {
307 0 : LOGE() << "pre-dive not created for track " << track << ", tick " << tick.ticks();
308 0 : continue;
309 : }
310 :
311 42 : Note* ghostNote = preDive->startNote();
312 42 : if (ghostNote) {
313 42 : ghostNote->setPitch(writtenPitch);
314 42 : ghostNote->setTpcFromPitch();
315 42 : ghostNote->setString(originalString);
316 42 : ghostNote->setFret(originalFret);
317 : }
318 :
319 42 : note->setPitch(attackPitch);
320 42 : note->setTpcFromPitch();
321 42 : note->setString(originalString);
322 42 : note->setFret(originalFret);
323 : }
324 : }
325 : }
326 294 : }
327 :
328 588 : static void createTiedSpans(const TiedNotesTrackMap& data, GuitarBendType type, Score* score)
329 : {
330 588 : const bool isDive = (type == GuitarBendType::DIVE);
331 :
332 620 : for (const auto& [track, trackInfo] : data) {
333 85 : for (const auto& [tick, tickEntry] : trackInfo) {
334 53 : Chord* chord = utils::getLocatedChord(score, tick, track);
335 53 : if (!chord) {
336 0 : continue;
337 : }
338 :
339 118 : for (size_t noteIndex = 0; noteIndex < chord->notes().size(); noteIndex++) {
340 65 : if (!muse::contains(tickEntry, noteIndex)) {
341 4 : continue;
342 : }
343 :
344 61 : const auto& noteInfo = tickEntry.at(noteIndex);
345 61 : Note* startNote = chord->notes()[noteIndex];
346 61 : Note* endNote = startNote->tieFor() ? startNote->tieFor()->endNote() : nullptr;
347 61 : if (!endNote) {
348 2 : LOGE() << "bend/dive import error: not found tied note for track " << track << ", tick " << tick.ticks();
349 2 : continue;
350 : }
351 :
352 59 : Tie* tie = startNote->tieFor();
353 59 : if (tie) {
354 59 : startNote->remove(tie);
355 : }
356 :
357 59 : GuitarBend* bend = score->addGuitarBend(type, startNote, endNote);
358 59 : IF_ASSERT_FAILED(bend) {
359 0 : LOGE() << "bend/dive wasn't created for track " << chord->track() << ", tick " << chord->tick().ticks();
360 0 : continue;
361 : }
362 :
363 59 : if (isDive && startNote->tieBack()) {
364 1 : startNote->setPitch(startNote->tieBack()->startNote()->pitch());
365 1 : startNote->setTpcFromPitch();
366 : }
367 :
368 : int rootPitch = isDive
369 59 : ? startNote->pitch()
370 43 : : bend->startNoteOfChain()->pitch();
371 :
372 59 : bend->setEndNotePitch(rootPitch + noteInfo.quarterTones / 2, noteInfo.quarterTones % 2);
373 59 : bend->setStartTimeFactor(noteInfo.startFactor);
374 59 : bend->setEndTimeFactor(noteInfo.endFactor);
375 59 : endNote->setParenthesesMode(ParenthesesMode::BOTH);
376 :
377 59 : Tie* endTie = endNote->tieFor();
378 81 : while (endTie) {
379 22 : Note* nextNote = endTie->endNote();
380 22 : IF_ASSERT_FAILED(nextNote) {
381 0 : LOGE() << "bend/dive import error: not found tied note for track " << track << ", tick " << tick.ticks();
382 0 : break;
383 : }
384 22 : nextNote->setPitch(endNote->pitch());
385 22 : nextNote->setTpcFromPitch();
386 22 : nextNote->setParenthesesMode(ParenthesesMode::BOTH);
387 22 : endTie = nextNote->tieFor();
388 : }
389 : }
390 : }
391 : }
392 588 : }
393 : } // namespace mu::iex::guitarpro
|