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 : #include "continiouselementsbuilder.h"
23 :
24 : #include "engraving/dom/trill.h"
25 : #include "engraving/dom/score.h"
26 : #include "engraving/dom/ottava.h"
27 : #include "engraving/dom/chordrest.h"
28 : #include "engraving/dom/hairpin.h"
29 : #include "engraving/dom/textline.h"
30 : #include "engraving/dom/vibrato.h"
31 : #include "engraving/dom/factory.h"
32 :
33 : using namespace mu::engraving;
34 :
35 : namespace mu::iex::guitarpro {
36 295 : ContiniousElementsBuilder::ContiniousElementsBuilder(Score* score)
37 295 : : m_score(score)
38 : {
39 295 : }
40 :
41 33447 : static mu::engraving::ElementType muTypeFromImportType(ContiniousElementsBuilder::ImportType importType)
42 : {
43 : using import_t = ContiniousElementsBuilder::ImportType;
44 :
45 33447 : switch (importType) {
46 2392 : case import_t::LET_RING:
47 2392 : return ElementType::LET_RING;
48 2392 : case import_t::PALM_MUTE:
49 2392 : return ElementType::PALM_MUTE;
50 2050 : case import_t::RASGUEADO:
51 2050 : return ElementType::RASGUEADO;
52 1595 : case import_t::PICK_SCRAPE:
53 1595 : return ElementType::PICK_SCRAPE;
54 :
55 10831 : case import_t::HARMONIC_ARTIFICIAL:
56 : case import_t::HARMONIC_PINCH:
57 : case import_t::HARMONIC_TAP:
58 : case import_t::HARMONIC_SEMI:
59 : case import_t::HARMONIC_FEEDBACK:
60 10831 : return ElementType::HARMONIC_MARK;
61 :
62 60 : case import_t::OTTAVA_MA15:
63 : case import_t::OTTAVA_VA8:
64 : case import_t::OTTAVA_VB8:
65 : case import_t::OTTAVA_MB15:
66 60 : return ElementType::OTTAVA;
67 :
68 2309 : case import_t::TRILL:
69 2309 : return ElementType::TRILL;
70 714 : case import_t::HAMMER_ON_PULL_OFF:
71 714 : return ElementType::HAMMER_ON_PULL_OFF;
72 :
73 13 : case import_t::HAIRPIN_CRESCENDO:
74 : case import_t::HAIRPIN_DIMINUENDO:
75 13 : return ElementType::HAIRPIN;
76 :
77 7974 : case import_t::VIBRATO_LEFT_HAND_SLIGHT:
78 : case import_t::VIBRATO_LEFT_HAND_WIDE:
79 : case import_t::VIBRATO_W_TREM_BAR_SLIGHT:
80 : case import_t::VIBRATO_W_TREM_BAR_WIDE:
81 7974 : return ElementType::VIBRATO;
82 :
83 3117 : case import_t::NONE:
84 3117 : return ElementType::INVALID;
85 : }
86 :
87 0 : return ElementType::INVALID;
88 : }
89 :
90 51 : static String harmonicText(ContiniousElementsBuilder::ImportType type)
91 : {
92 : static const std::unordered_map<ContiniousElementsBuilder::ImportType, String> names {
93 0 : { ContiniousElementsBuilder::ImportType::HARMONIC_ARTIFICIAL, u"AH" },
94 0 : { ContiniousElementsBuilder::ImportType::HARMONIC_PINCH, u"PH" },
95 0 : { ContiniousElementsBuilder::ImportType::HARMONIC_TAP, u"TH" },
96 0 : { ContiniousElementsBuilder::ImportType::HARMONIC_SEMI, u"SH" },
97 0 : { ContiniousElementsBuilder::ImportType::HARMONIC_FEEDBACK, u"Fdbk" },
98 58 : };
99 :
100 51 : auto it = names.find(type);
101 51 : if (it != names.end()) {
102 51 : return it->second;
103 : }
104 :
105 0 : LOGE() << "wrong harmonic type";
106 0 : return String();
107 1 : }
108 :
109 30 : static mu::engraving::VibratoType vibratoTypeFromImportType(ContiniousElementsBuilder::ImportType type)
110 : {
111 : static const std::unordered_map<ContiniousElementsBuilder::ImportType, VibratoType> names {
112 : { ContiniousElementsBuilder::ImportType::VIBRATO_LEFT_HAND_SLIGHT, VibratoType::GUITAR_VIBRATO },
113 : { ContiniousElementsBuilder::ImportType::VIBRATO_LEFT_HAND_WIDE, VibratoType::GUITAR_VIBRATO_WIDE },
114 : { ContiniousElementsBuilder::ImportType::VIBRATO_W_TREM_BAR_SLIGHT, VibratoType::VIBRATO_SAWTOOTH },
115 : { ContiniousElementsBuilder::ImportType::VIBRATO_W_TREM_BAR_WIDE, VibratoType::VIBRATO_SAWTOOTH_WIDE }
116 32 : };
117 :
118 30 : auto it = names.find(type);
119 30 : if (it != names.end()) {
120 30 : return it->second;
121 : }
122 :
123 0 : LOGE() << "wrong vibrato type";
124 0 : return VibratoType::GUITAR_VIBRATO;
125 : }
126 :
127 60 : static std::pair<bool, mu::engraving::OttavaType> ottavaType(ContiniousElementsBuilder::ImportType type)
128 : {
129 : static const std::unordered_map<ContiniousElementsBuilder::ImportType, mu::engraving::OttavaType> types {
130 : { ContiniousElementsBuilder::ImportType::OTTAVA_VA8, mu::engraving::OttavaType::OTTAVA_8VA },
131 : { ContiniousElementsBuilder::ImportType::OTTAVA_VB8, mu::engraving::OttavaType::OTTAVA_8VB },
132 : { ContiniousElementsBuilder::ImportType::OTTAVA_MA15, mu::engraving::OttavaType::OTTAVA_15MA },
133 : { ContiniousElementsBuilder::ImportType::OTTAVA_MB15, mu::engraving::OttavaType::OTTAVA_15MB }
134 62 : };
135 :
136 60 : auto it = types.find(type);
137 60 : if (it != types.end()) {
138 60 : return { true, it->second };
139 : }
140 :
141 0 : return { false, mu::engraving::OttavaType::OTTAVA_8VA };
142 : }
143 :
144 : /// whenever the continious line elements should end on rest or rest should be included in it
145 42819 : static bool shouldSplitByRests(mu::engraving::ElementType muType)
146 : {
147 42819 : switch (muType) {
148 29939 : case ElementType::PICK_SCRAPE:
149 : case ElementType::TRILL:
150 : case ElementType::HARMONIC_MARK:
151 : case ElementType::VIBRATO:
152 : case ElementType::HAMMER_ON_PULL_OFF:
153 29939 : return true;
154 :
155 12880 : default:
156 12880 : return false;
157 : }
158 : }
159 :
160 42819 : void ContiniousElementsBuilder::buildContiniousElement(ChordRest* cr, ElementType muType, ImportType importType, bool elemExists,
161 : sub_type_t subType)
162 : {
163 124 : auto setStartCR = [](Spanner* elem, ChordRest* cr) {
164 124 : elem->setTick(cr->tick());
165 124 : elem->setStartElement(cr);
166 124 : };
167 :
168 208 : auto setEndCR = [](Spanner* elem, ChordRest* cr) {
169 208 : elem->setTick2(cr->endTick());
170 208 : elem->setEndElement(cr);
171 208 : };
172 :
173 42819 : bool splitByRests = shouldSplitByRests(muType);
174 :
175 42819 : track_idx_t track = cr->track();
176 :
177 42819 : auto& elements = m_elementsByType[importType];
178 48881 : while (elements.size() < track + 1) {
179 6062 : elements.push_back(nullptr);
180 : }
181 :
182 42819 : auto& elem = elements[track];
183 42819 : auto& lastTypeForTrack = m_lastImportTypes[muType][track][subType];
184 :
185 42819 : ContiniousElementState state = calculateState(cr->isRest(), elemExists, splitByRests, lastTypeForTrack != importType);
186 :
187 : /// handling the continious element according to its state
188 42819 : switch (state) {
189 33239 : case ContiniousElementState::CHORD_NO_ELEMENT:
190 33239 : lastTypeForTrack = ImportType::NONE;
191 33239 : elem = nullptr;
192 33239 : break;
193 :
194 6516 : case ContiniousElementState::REST_BREAK:
195 6516 : lastTypeForTrack = ImportType::NONE;
196 6516 : elem = nullptr;
197 6516 : break;
198 :
199 2856 : case ContiniousElementState::REST_CONTINUE:
200 : case ContiniousElementState::ELEMENT_ON_REST: // currently it's mistake: we cannot indicate Rest with any of continious elements in guitar pro
201 2856 : if (lastTypeForTrack != ImportType::NONE) {
202 34 : m_elementsToAddToScore[track][lastTypeForTrack].endedOnRest = true;
203 : }
204 :
205 2856 : break;
206 :
207 84 : case ContiniousElementState::CONTINUE_CURRENT_LINE:
208 : {
209 84 : ChordRest* lastCR = elem->endCR();
210 84 : if (lastCR == cr) {
211 0 : break;
212 : }
213 :
214 84 : if (elem->tick2() < cr->tick()) {
215 14 : if (lastTypeForTrack != ImportType::NONE) {
216 14 : auto& lastTypeElementsToAdd = m_elementsToAddToScore[track][lastTypeForTrack];
217 :
218 : /// removing info about the Rest and updating last element's ticks
219 14 : if (lastTypeElementsToAdd.endedOnRest) {
220 13 : lastTypeElementsToAdd.endedOnRest = false;
221 13 : Spanner* prevElem = lastTypeElementsToAdd.elements.back();
222 13 : if (!prevElem) {
223 0 : LOGE() << "error while importing";
224 9372 : return;
225 : }
226 :
227 13 : elem = prevElem;
228 13 : setEndCR(elem, cr);
229 : } else {
230 : // Simile mark case
231 1 : setEndCR(elem, cr);
232 : }
233 : }
234 : } else {
235 70 : setEndCR(elem, cr);
236 : }
237 :
238 84 : break;
239 : }
240 :
241 124 : case ContiniousElementState::CREATE_NEW_ITEM:
242 : {
243 124 : EngravingItem* engItem = Factory::createItem(muType, m_score->dummy());
244 :
245 124 : Spanner* newElem = dynamic_cast<Spanner*>(engItem);
246 124 : IF_ASSERT_FAILED(newElem) {
247 0 : return;
248 : }
249 :
250 124 : elem = newElem;
251 :
252 124 : setStartCR(newElem, cr);
253 124 : setEndCR(newElem, cr);
254 :
255 124 : newElem->setTrack(track);
256 124 : newElem->setTrack2(track);
257 :
258 124 : m_elementsToAddToScore[track][importType].elements.push_back(newElem);
259 124 : m_orderedAddedElements.push_back(newElem);
260 124 : lastTypeForTrack = importType;
261 124 : break;
262 : }
263 :
264 0 : default:
265 0 : break;
266 : }
267 :
268 42819 : if (!cr->isChord()) {
269 9372 : return;
270 : }
271 :
272 33447 : setupAddedElement(cr->track(), importType);
273 : }
274 :
275 182 : void ContiniousElementsBuilder::notifyUncompletedMeasure()
276 : {
277 260 : for (auto& trackMaps : m_elementsToAddToScore) {
278 236 : for (auto& typeMaps : trackMaps.second) {
279 158 : typeMaps.second.endedOnRest = true;
280 : }
281 : }
282 182 : }
283 :
284 3 : static Chord* searchEndChord(Chord* startChord)
285 : {
286 3 : ChordRest* nextCr = nullptr;
287 3 : if (startChord->isGrace()) {
288 : //! this case when start note is a grace note so end note can be next note in grace notes
289 : //! or parent note of grace notes
290 0 : Chord* parentGrace = toChord(startChord->parent());
291 :
292 0 : auto it = parentGrace->graceNotes().begin();
293 0 : for (; it != parentGrace->graceNotes().end(); ++it) {
294 0 : if (*it == startChord) {
295 0 : break;
296 : }
297 : }
298 :
299 0 : if (it == parentGrace->graceNotes().end()) {
300 0 : nextCr = nullptr;
301 0 : } else if (std::next(it) == parentGrace->graceNotes().end()) {
302 0 : nextCr = parentGrace;
303 : } else {
304 0 : nextCr = *(++it);
305 : }
306 : } else {
307 3 : nextCr = startChord->segment()->next1()->nextChordRest(startChord->track());
308 3 : if (!nextCr) {
309 0 : return nullptr;
310 : }
311 :
312 3 : if (nextCr->isChord() && !toChord(nextCr)->graceNotes().empty()) {
313 0 : nextCr = toChord(nextCr)->graceNotes().front();
314 : }
315 : }
316 :
317 3 : return (nextCr && nextCr->isChord()) ? toChord(nextCr) : nullptr;
318 : }
319 :
320 294 : void ContiniousElementsBuilder::addElementsToScore()
321 : {
322 296 : for (auto& trackMaps: m_spannersWithoutEndElement) {
323 4 : for (auto& typeMaps : trackMaps.second) {
324 5 : for (Spanner* elem : typeMaps.second) {
325 3 : elem->setEndElement(searchEndChord(elem->endChord()));
326 : }
327 : }
328 : }
329 :
330 418 : for (Spanner* elem : m_orderedAddedElements) {
331 124 : m_score->addElement(elem);
332 : }
333 294 : }
334 :
335 : /// indicating the type of behaviour for continious element
336 42819 : ContiniousElementsBuilder::ContiniousElementState ContiniousElementsBuilder::calculateState(bool isRest, bool elemExists, bool splitByRests,
337 : bool importTypeChanged) const
338 : {
339 42819 : ContiniousElementState state = ContiniousElementState::UNDEFINED;
340 :
341 42819 : if (isRest && splitByRests) {
342 6516 : state = ContiniousElementState::REST_BREAK;
343 : } else {
344 36303 : if (!elemExists) {
345 36092 : if (!isRest) {
346 33239 : state = ContiniousElementState::CHORD_NO_ELEMENT;
347 : } else {
348 2853 : state = ContiniousElementState::REST_CONTINUE;
349 : }
350 : } else {
351 : // element exists on current beat
352 211 : if (isRest) {
353 3 : state = ContiniousElementState::ELEMENT_ON_REST;
354 208 : } else if (importTypeChanged) {
355 124 : state = ContiniousElementState::CREATE_NEW_ITEM;
356 : } else {
357 84 : state = ContiniousElementState::CONTINUE_CURRENT_LINE;
358 : }
359 : }
360 : }
361 :
362 42819 : return state;
363 : }
364 :
365 33447 : void ContiniousElementsBuilder::setupAddedElement(track_idx_t trackIdx, ImportType importType)
366 : {
367 33447 : ElementType muType = muTypeFromImportType(importType);
368 :
369 33447 : Spanner* lineElem = m_elementsByType[importType][trackIdx];
370 33447 : if (!lineElem) {
371 33239 : return;
372 : }
373 :
374 208 : switch (muType) {
375 3 : case ElementType::TRILL:
376 : {
377 3 : if (lineElem->isTrill()) {
378 3 : toTrill(lineElem)->setTrillType(TrillType::TRILL_LINE);
379 : }
380 :
381 3 : break;
382 : }
383 13 : case ElementType::HAIRPIN:
384 : {
385 13 : if (lineElem->isHairpin()) {
386 13 : toHairpin(lineElem)->setHairpinType(
387 13 : importType == ImportType::HAIRPIN_CRESCENDO ? HairpinType::CRESC_HAIRPIN : HairpinType::DIM_HAIRPIN);
388 : }
389 :
390 13 : break;
391 : }
392 51 : case ElementType::HARMONIC_MARK:
393 : {
394 51 : if (lineElem->isTextLineBase()) {
395 51 : const String& text = harmonicText(importType);
396 51 : toTextLineBase(lineElem)->setBeginText(text);
397 51 : toTextLineBase(lineElem)->setContinueText(text);
398 51 : }
399 :
400 51 : break;
401 : }
402 60 : case ElementType::OTTAVA:
403 : {
404 60 : const auto& [foundOttava, muOttavaType] = ottavaType(importType);
405 60 : if (!foundOttava) {
406 0 : return;
407 : }
408 :
409 60 : if (lineElem->isOttava()) {
410 60 : toOttava(lineElem)->setOttavaType(muOttavaType);
411 : }
412 :
413 60 : break;
414 : }
415 30 : case ElementType::VIBRATO:
416 30 : if (lineElem->isVibrato()) {
417 30 : toVibrato(lineElem)->setVibratoType(vibratoTypeFromImportType(importType));
418 : }
419 :
420 30 : break;
421 4 : case ElementType::HAMMER_ON_PULL_OFF:
422 4 : m_spannersWithoutEndElement[trackIdx][importType].insert(lineElem);
423 4 : break;
424 47 : default:
425 47 : break;
426 : }
427 : }
428 : } // namespace mu::iex::guitarpro
|