Line data Source code
1 : #include "gpconverter.h"
2 :
3 : #include "engraving/dom/pitchspelling.h"
4 : #include "translation.h"
5 :
6 : #include "gpdommodel.h"
7 :
8 : #include "engraving/dom/arpeggio.h"
9 : #include "engraving/dom/bend.h"
10 : #include "engraving/dom/box.h"
11 : #include "engraving/dom/bracketitem.h"
12 : #include "engraving/editing/editstaffbrackets.h"
13 : #include "engraving/dom/chord.h"
14 : #include "engraving/dom/chordline.h"
15 : #include "engraving/dom/clef.h"
16 : #include "engraving/dom/deadslapped.h"
17 : #include "engraving/dom/drumset.h"
18 : #include "engraving/dom/dynamic.h"
19 : #include "engraving/dom/factory.h"
20 : #include "engraving/dom/fermata.h"
21 : #include "engraving/dom/fingering.h"
22 : #include "engraving/dom/fret.h"
23 : #include "engraving/dom/glissando.h"
24 : #include "engraving/dom/guitarbend.h"
25 : #include "engraving/dom/gradualtempochange.h"
26 : #include "engraving/dom/hammeronpulloff.h"
27 : #include "engraving/dom/harmony.h"
28 : #include "engraving/dom/instrchange.h"
29 : #include "engraving/dom/jump.h"
30 : #include "engraving/dom/keysig.h"
31 : #include "engraving/dom/lyrics.h"
32 : #include "engraving/dom/marker.h"
33 : #include "engraving/dom/measure.h"
34 : #include "engraving/dom/measurerepeat.h"
35 : #include "engraving/dom/note.h"
36 : #include "engraving/dom/ottava.h"
37 : #include "engraving/dom/part.h"
38 : #include "engraving/dom/rehearsalmark.h"
39 : #include "engraving/dom/rest.h"
40 : #include "engraving/dom/score.h"
41 : #include "engraving/dom/slur.h"
42 : #include "engraving/dom/spanner.h"
43 : #include "engraving/dom/staff.h"
44 : #include "engraving/dom/stafftext.h"
45 : #include "engraving/dom/tapping.h"
46 : #include "engraving/dom/tempotext.h"
47 : #include "engraving/dom/text.h"
48 : #include "engraving/dom/tie.h"
49 : #include "engraving/dom/timesig.h"
50 : #include "engraving/dom/tremolosinglechord.h"
51 : #include "engraving/dom/tripletfeel.h"
52 : #include "engraving/dom/tuplet.h"
53 : #include "engraving/dom/volta.h"
54 : #include "engraving/dom/capo.h"
55 : #include "engraving/dom/stringtunings.h"
56 : #include "engraving/types/symid.h"
57 :
58 : #include "engraving/editing/editchord.h"
59 :
60 : #include "../utils.h"
61 : #include "../guitarprodrumset.h"
62 :
63 : #include "log.h"
64 :
65 : using namespace mu::engraving;
66 :
67 : namespace mu::iex::guitarpro {
68 22 : static mu::engraving::JumpType jumpType(const String& typeString)
69 : {
70 : static const std::map<String, JumpType> types {
71 0 : { u"DaCapo", JumpType::DC },
72 0 : { u"DaSegno", JumpType::DS },
73 0 : { u"DaCapoAlFine", JumpType::DC_AL_FINE },
74 0 : { u"DaCapoAlCoda", JumpType::DC_AL_CODA },
75 0 : { u"DaCapoAlDoubleCoda", JumpType::DC_AL_DBLCODA },
76 0 : { u"DaSegnoAlCoda", JumpType::DS_AL_CODA },
77 0 : { u"DaSegnoAlDoubleCoda", JumpType::DS_AL_DBLCODA },
78 0 : { u"DaSegnoAlFine", JumpType::DS_AL_FINE },
79 0 : { u"DaSegnoSegno", JumpType::DSS },
80 0 : { u"DaSegnoSegnoAlCoda", JumpType::DSS_AL_CODA },
81 0 : { u"DaSegnoSegnoAlDoubleCoda", JumpType::DSS_AL_DBLCODA },
82 0 : { u"DaSegnoSegnoAlFine", JumpType::DSS_AL_FINE },
83 36 : };
84 :
85 22 : auto it = types.find(typeString);
86 22 : if (it != types.end()) {
87 22 : return it->second;
88 : }
89 :
90 0 : LOGE() << "wrong jump type";
91 0 : return JumpType::USER;
92 1 : }
93 :
94 14 : static mu::engraving::MarkerType markerType(const String& typeString)
95 : {
96 : static const std::map<String, MarkerType> types {
97 0 : { u"Segno", MarkerType::SEGNO },
98 0 : { u"SegnoSegno", MarkerType::VARSEGNO },
99 0 : { u"Coda", MarkerType::CODA },
100 0 : { u"DoubleCoda", MarkerType::VARCODA },
101 0 : { u"Fine", MarkerType::FINE },
102 0 : { u"DaCoda", MarkerType::DA_CODA },
103 0 : { u"DaDoubleCoda", MarkerType::DA_DBLCODA },
104 23 : };
105 :
106 14 : auto it = types.find(typeString);
107 14 : if (it != types.end()) {
108 14 : return it->second;
109 : }
110 :
111 0 : LOGE() << "wrong direction marker type";
112 0 : return MarkerType::USER;
113 1 : }
114 :
115 0 : static mu::engraving::TripletFeelType tripletFeelType(GPMasterBar::TripletFeelType tf)
116 : {
117 : static const std::map<GPMasterBar::TripletFeelType, TripletFeelType> types {
118 : { GPMasterBar::TripletFeelType::Triplet8th, TripletFeelType::TRIPLET_8TH },
119 : { GPMasterBar::TripletFeelType::Triplet16th, TripletFeelType::TRIPLET_16TH },
120 : { GPMasterBar::TripletFeelType::Dotted8th, TripletFeelType::DOTTED_8TH },
121 : { GPMasterBar::TripletFeelType::Dotted16th, TripletFeelType::DOTTED_16TH },
122 : { GPMasterBar::TripletFeelType::Scottish8th, TripletFeelType::SCOTTISH_8TH },
123 : { GPMasterBar::TripletFeelType::Scottish16th, TripletFeelType::SCOTTISH_16TH },
124 : { GPMasterBar::TripletFeelType::None, TripletFeelType::NONE }
125 0 : };
126 :
127 0 : auto it = types.find(tf);
128 0 : if (it != types.end()) {
129 0 : return it->second;
130 : }
131 :
132 0 : return TripletFeelType::NONE;
133 : }
134 :
135 1631 : static std::pair<bool, mu::engraving::OttavaType> ottavaType(GPBeat::OttavaType t)
136 : {
137 : static const std::map<GPBeat::OttavaType, mu::engraving::OttavaType> types {
138 : { GPBeat::OttavaType::va8, OttavaType::OTTAVA_8VA },
139 : { GPBeat::OttavaType::vb8, OttavaType::OTTAVA_8VB },
140 : { GPBeat::OttavaType::ma15, OttavaType::OTTAVA_15MA },
141 : { GPBeat::OttavaType::mb15, OttavaType::OTTAVA_15MB }
142 1633 : };
143 :
144 1631 : auto it = types.find(t);
145 1631 : if (it != types.end()) {
146 60 : return { true, it->second };
147 : }
148 :
149 1571 : return { false, OttavaType::OTTAVA_8VA };
150 : }
151 :
152 46 : static GPBeat::HarmonicMarkType harmonicTypeNoteToBeat(GPNote::Harmonic::Type t)
153 : {
154 : static const std::map<GPNote::Harmonic::Type, GPBeat::HarmonicMarkType> types {
155 : { GPNote::Harmonic::Type::Artificial, GPBeat::HarmonicMarkType::Artificial },
156 : { GPNote::Harmonic::Type::Pinch, GPBeat::HarmonicMarkType::Pinch },
157 : { GPNote::Harmonic::Type::Tap, GPBeat::HarmonicMarkType::Tap },
158 : { GPNote::Harmonic::Type::Semi, GPBeat::HarmonicMarkType::Semi },
159 : { GPNote::Harmonic::Type::FeedBack, GPBeat::HarmonicMarkType::FeedBack }
160 48 : };
161 :
162 46 : auto it = types.find(t);
163 46 : if (it != types.end()) {
164 46 : return it->second;
165 : }
166 :
167 0 : return GPBeat::HarmonicMarkType::None;
168 : }
169 :
170 2095 : static ContiniousElementsBuilder::ImportType ottavaToImportType(GPBeat::OttavaType t)
171 : {
172 : static const std::map<GPBeat::OttavaType, ContiniousElementsBuilder::ImportType> types {
173 : { GPBeat::OttavaType::ma15, ContiniousElementsBuilder::ImportType::OTTAVA_MA15 },
174 : { GPBeat::OttavaType::va8, ContiniousElementsBuilder::ImportType::OTTAVA_VA8 },
175 : { GPBeat::OttavaType::vb8, ContiniousElementsBuilder::ImportType::OTTAVA_VB8 },
176 : { GPBeat::OttavaType::mb15, ContiniousElementsBuilder::ImportType::OTTAVA_MB15 }
177 2097 : };
178 :
179 2095 : auto it = types.find(t);
180 2095 : if (it != types.end()) {
181 60 : return it->second;
182 : }
183 :
184 2035 : return ContiniousElementsBuilder::ImportType::NONE;
185 : }
186 :
187 2095 : static ContiniousElementsBuilder::ImportType hairpinToImportType(GPBeat::Hairpin t)
188 : {
189 : static const std::map<GPBeat::Hairpin, ContiniousElementsBuilder::ImportType> types {
190 : { GPBeat::Hairpin::Crescendo, ContiniousElementsBuilder::ImportType::HAIRPIN_CRESCENDO },
191 : { GPBeat::Hairpin::Decrescendo, ContiniousElementsBuilder::ImportType::HAIRPIN_DIMINUENDO }
192 2097 : };
193 :
194 2095 : auto it = types.find(t);
195 2095 : if (it != types.end()) {
196 16 : return it->second;
197 : }
198 :
199 2079 : return ContiniousElementsBuilder::ImportType::NONE;
200 : }
201 :
202 60 : static void setPitchByOttavaType(mu::engraving::Note* note, mu::engraving::OttavaType type)
203 : {
204 60 : note->setPitch(clampPitchOctaved(note->pitch() - ottavaDefault[int(type)].shift));
205 60 : }
206 :
207 : static std::unordered_map<uint64_t, mu::engraving::StringData> stringDatas;
208 :
209 221 : GPConverter::GPConverter(Score* score, std::unique_ptr<GPDomModel>&& gpDom, const muse::modularity::ContextPtr& iocCtx)
210 1105 : : muse::Contextable(iocCtx), _score(score), _gpDom(std::move(gpDom))
211 : {
212 221 : _drumExtension = {
213 : { 91, 40 }, //Snare(rim shot)
214 : { 92, 42 }, //Hi Hat (half)
215 : { 93, 51 }, //Ride (edje)
216 : { 94, 59 }, //Ride (choke)
217 : { 95, 55 }, //Splash (choke)
218 : { 96, 52 }, //Chine(choke)
219 : { 97, 49 }, //Crash high (choke)
220 : { 98, 57 }, //Crash medium (choke)
221 : { 99, 56 }, //Cowbell low (hit)
222 : { 100, 56 },//Cowbell low (tip)
223 : { 101, 56 },//Cowbell medium (tip)
224 : { 102, 56 },//Cowbell high (hit)
225 : { 103, 56 },//Cowbell high (tip)
226 : { 104, 60 },//Hand (mute)
227 : { 105, 60 },//Hand (slap)
228 : { 106, 61 },//Hand (mute)
229 : { 107, 61 },//Hand (slap)
230 : { 108, 64 },//Conga low (slap)
231 : { 109, 64 },//Conga low (mute)
232 : { 110, 64 },//Conga high (slap)
233 : { 111, 54 },//Tambourine (return)
234 : { 112, 54 },//Tambourine (roll)
235 : { 113, 54 },//Tambourine (hand)
236 : { 114, 43 },//Grancassa (hit)
237 : { 115, 49 },//Piatti (hit)
238 : { 116, 49 },//Piatti (hand)
239 : { 117, 69 },//Cabasa (return)
240 : { 118, 70 },//Left Maraca (return)
241 : { 119, 70 },//Right Maraca (hit)
242 : { 120, 70 },//Right Maraca (return)
243 : { 122, 82 },//Shaker (return)
244 : { 123, 84 },//Bell Tree (return)
245 : { 124, 62 },//Golpe (thumb)
246 : { 125, 62 },//Golpe (finger)
247 : { 126, 59 },//Ride (middle)
248 : { 127, 59 }//Ride (bell)
249 221 : };
250 :
251 221 : _drumResolver = std::make_unique<GPDrumSetResolver>();
252 221 : _drumResolver->initGPDrum();
253 221 : m_continiousElementsBuilder = std::make_unique<ContiniousElementsBuilder>(_score);
254 221 : m_guitarBendImporter = std::make_unique<GuitarBendImporter>(_score);
255 221 : }
256 :
257 0 : const std::unique_ptr<GPDomModel>& GPConverter::gpDom() const
258 : {
259 0 : return _gpDom;
260 : }
261 :
262 221 : void GPConverter::convertGP()
263 : {
264 221 : setUpGPScore(_gpDom->score());
265 221 : setUpTracks(_gpDom->tracks());
266 221 : collectTempoMap(_gpDom->masterTracks());
267 :
268 221 : convert(_gpDom->masterBars());
269 :
270 221 : clearDefectedSpanner();
271 221 : fixPercussion();
272 221 : addCapos();
273 221 : utils::addPlayCountTexts(_score);
274 221 : }
275 :
276 221 : void GPConverter::fixPercussion()
277 : {
278 : // After reading some of the GP files, there're
279 : // parts, which contain only one percussion instrument.
280 : // In this case the program value for this instrument will
281 : // be set to the pitch value of the notes of the instrument,
282 : // thus we need to reset this value to 0.
283 460 : for (size_t i = 0; i < _score->parts().size(); ++i) {
284 239 : mu::engraving::Part* pPart = _score->parts()[i];
285 239 : IF_ASSERT_FAILED(!!pPart) {
286 0 : continue;
287 : }
288 :
289 239 : mu::engraving::Instrument* pInstrument = pPart->instrument();
290 239 : if (!pInstrument) {
291 0 : continue;
292 : }
293 :
294 239 : if (!pInstrument->useDrumset()) {
295 236 : continue;
296 : }
297 :
298 3 : if (pPart->midiChannel() == PERC_CHANNEL) {
299 3 : pPart->setMidiProgram(0, 128);
300 : }
301 : }
302 221 : }
303 :
304 12 : void GPConverter::fillTuplet()
305 : {
306 12 : if (m_nextTupletInfo.elements.empty() || !m_nextTupletInfo.tuplet) {
307 0 : LOGE() << "not able to add elements to tuplet";
308 0 : return;
309 : }
310 :
311 12 : Tuplet* tuplet = m_nextTupletInfo.tuplet;
312 12 : ChordRest* firstCr = m_nextTupletInfo.elements.front();
313 12 : tuplet->setTrack(firstCr->track());
314 12 : tuplet->setOwnershipParent(firstCr->measure());
315 12 : tuplet->setTick(firstCr->tick());
316 12 : tuplet->setBaseLen(Fraction(1, m_nextTupletInfo.lowestBase));
317 12 : tuplet->setTicks(m_nextTupletInfo.duration);
318 12 : setupTupletStyle(tuplet);
319 :
320 62 : for (ChordRest* elem : m_nextTupletInfo.elements) {
321 50 : tuplet->add(elem);
322 : }
323 :
324 : /// TODO: solve correctly within engraving
325 : /// avoiding brackets on single note
326 12 : if (tuplet->elements().size() == 1) {
327 0 : tuplet->setBracketType(TupletBracketType::SHOW_NO_BRACKET);
328 : }
329 :
330 12 : m_nextTupletInfo.elements.clear();
331 12 : m_nextTupletInfo.tuplet = nullptr;
332 12 : m_nextTupletInfo.duration = Fraction();
333 12 : m_nextTupletInfo.lowestBase = NextTupletInfo::LOWEST_BASE;
334 : }
335 :
336 221 : void GPConverter::convert(const std::vector<std::unique_ptr<GPMasterBar> >& masterBars)
337 : {
338 916 : for (uint32_t mi = 0; mi < masterBars.size(); ++mi) {
339 695 : Context ctx;
340 695 : ctx.masterBarIndex = mi;
341 695 : convertMasterBar(masterBars.at(mi).get(), ctx);
342 : }
343 :
344 : // glueing line segment elements separated with rests
345 221 : m_continiousElementsBuilder->addElementsToScore();
346 :
347 221 : addTempoMap();
348 221 : addInstrumentChanges();
349 221 : m_guitarBendImporter->addElementsToScore();
350 :
351 221 : addFermatas();
352 221 : addContinuousSlideHammerOn();
353 221 : addTuning();
354 221 : }
355 :
356 695 : void GPConverter::convertMasterBar(const GPMasterBar* mB, Context ctx)
357 : {
358 695 : Measure* measure = addMeasure(mB);
359 :
360 695 : addTimeSig(mB, measure);
361 695 : addKeySig(mB, measure);
362 695 : addBarline(mB, measure, ctx.masterBarIndex);
363 695 : addRepeat(mB, measure);
364 695 : collectFermatas(mB, measure);
365 695 : convertBars(mB->bars(), ctx);
366 695 : addTripletFeel(mB, measure);
367 695 : addSection(mB, measure);
368 695 : addDirection(mB, measure);
369 :
370 695 : fixEmptyMeasures();
371 695 : }
372 :
373 695 : void GPConverter::fixEmptyMeasures()
374 : {
375 : // Get all ChordRest elems and sort them by staves
376 : // Also store root Segment ptr will need it later to delete some rest elems
377 695 : std::map<track_idx_t, std::vector<std::pair<Segment*, EngravingItem*> > > elems;
378 :
379 695 : size_t ntracks = _score->ntracks();
380 695 : SegmentType type = SegmentType::ChordRest;
381 695 : Measure* lastMeasure = _score->lastMeasure();
382 :
383 2722 : for (Segment* s = lastMeasure->segments().first(type); s; s = s->next(type)) {
384 10767 : for (track_idx_t i = 0; i < ntracks; ++i) {
385 8740 : auto e = s->element(i);
386 8740 : if (!e) {
387 6425 : continue;
388 : }
389 2315 : elems[i / VOICES].push_back({ s, e });
390 : }
391 : }
392 :
393 1473 : for (const auto& [staffIdx, segItemPairs] : elems) {
394 778 : bool shouldClear = true;
395 1100 : for (const auto& p : segItemPairs) {
396 : // Don't need to do anything if there is not "rest" elem on a staff
397 909 : if (!p.second->isRest()) {
398 587 : shouldClear = false;
399 587 : break;
400 : }
401 : }
402 778 : if (shouldClear) {
403 : // Keep only one rest element in a bar and make its duration V_MEASURE
404 : // that way layout can recognize this bar as "empty"
405 : // and properly render mmrests
406 191 : if (segItemPairs.empty()) {
407 0 : continue;
408 : }
409 210 : for (size_t i = 1; i < segItemPairs.size(); ++i) {
410 19 : Rest* rest = toRest(segItemPairs.at(i).second);
411 19 : if (Tuplet* tuplet = rest->tuplet()) {
412 1 : tuplet->remove(rest);
413 : }
414 :
415 19 : segItemPairs.at(i).first->remove(rest);
416 : }
417 :
418 191 : Rest* rest = toRest(segItemPairs.at(0).second);
419 191 : rest->setTicks(lastMeasure->ticks());
420 191 : rest->setDurationType(DurationType::V_MEASURE);
421 191 : if (Tuplet* tuplet = rest->tuplet()) {
422 1 : tuplet->remove(rest);
423 1 : if (tuplet->elements().empty()) {
424 1 : if (tuplet->tuplet()) {
425 0 : tuplet->tuplet()->remove(tuplet);
426 : }
427 :
428 1 : delete tuplet;
429 : }
430 : }
431 : }
432 : }
433 695 : }
434 :
435 695 : void GPConverter::convertBars(const std::vector<std::unique_ptr<GPBar> >& bars, Context ctx)
436 : {
437 695 : ctx.curTrack = 0;
438 1473 : for (const auto& bar : bars) {
439 778 : m_chordExistsInBar = false;
440 778 : m_chordExistsForVoice.fill(false);
441 778 : convertBar(bar.get(), ctx);
442 778 : ctx.curTrack += VOICES;
443 : }
444 695 : }
445 :
446 778 : void GPConverter::convertBar(const GPBar* bar, Context ctx)
447 : {
448 778 : addClef(bar, static_cast<int>(ctx.curTrack));
449 :
450 778 : if (addSimileMark(bar, static_cast<int>(ctx.curTrack))) {
451 3 : return;
452 : }
453 :
454 775 : convertVoices(bar->voices(), ctx);
455 775 : hideRestsInEmptyMeasures(ctx.curTrack, ctx.curTrack + VOICES);
456 : }
457 :
458 695 : void GPConverter::addBarline(const GPMasterBar* mB, Measure* measure, int32_t masterBarIndex)
459 : {
460 : static bool insideFreeTime = false;
461 695 : size_t staves = _score->staves().size();
462 695 : bool lastMeasure = (masterBarIndex == static_cast<int32_t>(_gpDom->masterBars().size() - 1));
463 :
464 695 : if (!lastMeasure && mB->barlineType() == GPMasterBar::BarlineType::NORMAL) {
465 1003 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
466 532 : measure->setEndBarLineType(mu::engraving::BarLineType::NORMAL, staffIdx * VOICES);
467 : }
468 : }
469 :
470 695 : if (!lastMeasure && mB->barlineType() == GPMasterBar::BarlineType::DOUBLE) {
471 7 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
472 4 : measure->setEndBarLineType(mu::engraving::BarLineType::DOUBLE, staffIdx * VOICES);
473 : }
474 : }
475 :
476 695 : GPMasterBar::TimeSig sig = mB->timeSig();
477 695 : auto scoreTimeSig = Fraction(sig.numerator, sig.denominator);
478 :
479 695 : if (mB->freeTime()) {
480 8 : if (!lastMeasure && mB->barlineType() != GPMasterBar::BarlineType::DOUBLE) {
481 13 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
482 7 : measure->setEndBarLineType(mu::engraving::BarLineType::BROKEN, staffIdx * VOICES);
483 : }
484 : }
485 8 : if (!insideFreeTime) {
486 5 : insideFreeTime = true;
487 :
488 : // Free time text
489 12 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
490 7 : Segment* s = measure->getSegment(SegmentType::TimeSig, measure->tick());
491 7 : StaffText* st = Factory::createStaffText(s);
492 7 : st->setTrack(staffIdx * VOICES);
493 7 : st->setPlainText(muse::mtrc("iex_guitarpro", "Free time", "time signature"));
494 7 : s->add(st);
495 :
496 : // if timeSig is different, it was added before, here we handle "freetime"
497 7 : if (_lastTimeSig.numerator != sig.numerator
498 7 : || _lastTimeSig.denominator != sig.denominator) {
499 0 : return;
500 : }
501 7 : TimeSig* ts = Factory::createTimeSig(s);
502 7 : ts->setSig(scoreTimeSig);
503 7 : ts->setTrack(staffIdx * VOICES);
504 7 : ts->setLargeParentheses(true);
505 7 : s->add(ts);
506 : }
507 : }
508 : } else {
509 687 : insideFreeTime = false;
510 : }
511 695 : _lastTimeSig.numerator = sig.numerator;
512 695 : _lastTimeSig.denominator = sig.denominator;
513 : }
514 :
515 775 : void GPConverter::convertVoices(const std::vector<std::unique_ptr<GPVoice> >& voices, Context ctx)
516 : {
517 775 : if (voices.empty()) {
518 101 : ctx.curTick = _score->lastMeasure()->tick();
519 101 : fillUncompletedMeasure(ctx);
520 : }
521 :
522 775 : track_idx_t currentTrackFirstVoice = ctx.curTrack;
523 1499 : for (const auto& voice : voices) {
524 724 : ctx.curTrack = currentTrackFirstVoice + voice->position();
525 724 : convertVoice(voice.get(), ctx);
526 : }
527 :
528 775 : bool hasFirstVoice = std::any_of(voices.begin(), voices.end(), [](const std::unique_ptr<GPVoice>& voice) {
529 674 : return voice->position() == 0;
530 : });
531 :
532 775 : if (!hasFirstVoice && _score->lastMeasure()) {
533 116 : _score->setRest(_score->lastMeasure()->tick(), currentTrackFirstVoice, _score->lastMeasure()->ticks(), true, nullptr);
534 : }
535 775 : }
536 :
537 724 : void GPConverter::convertVoice(const GPVoice* voice, Context ctx)
538 : {
539 724 : ctx.curTick = _score->lastMeasure()->tick();
540 724 : convertBeats(voice->beats(), ctx);
541 724 : }
542 :
543 724 : void GPConverter::convertBeats(const std::vector<std::shared_ptr<GPBeat> >& beats, Context ctx)
544 : {
545 724 : ChordRestContainer graceChords;
546 2857 : for (const auto& beat : beats) {
547 2133 : m_currentGPBeat = beat.get();
548 2133 : ctx.curTick = convertBeat(beat.get(), graceChords, ctx);
549 : }
550 :
551 724 : fillUncompletedMeasure(ctx);
552 724 : clearDefectedGraceChord(graceChords);
553 724 : }
554 :
555 2133 : Fraction GPConverter::convertBeat(const GPBeat* beat, ChordRestContainer& graceChords, Context ctx)
556 : {
557 2133 : Measure* lastMeasure = _score->lastMeasure();
558 :
559 2133 : if (ctx.curTick >= lastMeasure->ticks() + lastMeasure->tick()) {
560 2 : return ctx.curTick;
561 : }
562 :
563 2131 : auto curSegment = lastMeasure->getSegment(SegmentType::ChordRest, ctx.curTick);
564 :
565 2131 : ChordRest* cr = addChordRest(beat, ctx);
566 2131 : if (cr) {
567 2131 : curSegment->setTicks(cr->ticks());
568 : }
569 2131 : if (engravingConfiguration()->guitarProImportExperimental() && beat->deadSlapped() && cr->isRest()) {
570 0 : Rest* rest = toRest(cr);
571 0 : curSegment->add(rest);
572 0 : DeadSlapped* dc = Factory::createDeadSlapped(rest);
573 0 : dc->setTrack(rest->track());
574 0 : rest->add(dc);
575 : } else {
576 2131 : if (beat->graceNotes() != GPBeat::GraceNotes::None) {
577 36 : if (cr->isRest()) {
578 0 : delete cr;
579 0 : return ctx.curTick;
580 : }
581 :
582 36 : curSegment->add(cr);
583 :
584 36 : graceChords.push_back({ cr, beat });
585 :
586 36 : return ctx.curTick;
587 : }
588 :
589 2095 : curSegment->add(cr);
590 :
591 2095 : if (cr->isChord()) {
592 1595 : m_chordExistsForVoice[ctx.curTrack % VOICES] = true;
593 1595 : m_chordExistsInBar = true;
594 :
595 1595 : if (beat->stemOrientationUserDefined()) {
596 0 : toChord(cr)->setStemDirection(beat->stemOrientationUp() ? DirectionV::UP : DirectionV::DOWN);
597 : }
598 :
599 1595 : setBeamMode(beat, cr, lastMeasure, ctx.curTick);
600 : }
601 :
602 2095 : if (cr->isChord() && !graceChords.empty()) {
603 24 : int grIndex = 0;
604 :
605 60 : for (auto [pGrChord, pBeat] : graceChords) {
606 36 : configureGraceChord(pBeat, pGrChord, beat->ottavaType());
607 36 : if (pGrChord->isChord()) {
608 36 : toChord(pGrChord)->setGraceIndex(grIndex++);
609 : }
610 :
611 36 : Fraction fr(1, (graceChords.size() == 1 ? 1 : 2) * 8);
612 36 : pGrChord->setDurationType(TDuration(fr));
613 :
614 36 : cr->add(pGrChord);
615 36 : addLegato(pBeat, pGrChord);
616 : }
617 24 : graceChords.clear();
618 : }
619 :
620 2095 : convertNotes(beat->notes(), cr);
621 :
622 2095 : addTuplet(beat, cr);
623 2095 : addTimer(beat, cr);
624 2095 : addFreeText(beat, cr);
625 2095 : addFadding(beat, cr);
626 2095 : addHairPin(beat, cr);
627 2095 : addTremolo(beat, cr);
628 2095 : addPickStroke(beat, cr);
629 2095 : addDynamic(beat, cr);
630 2095 : addWah(beat, cr);
631 2095 : addGolpe(beat, cr);
632 2095 : addFretDiagram(beat, cr, ctx);
633 2095 : addBarre(beat, cr);
634 2095 : addTapping(beat, cr);
635 2095 : addSlapped(beat, cr);
636 2095 : addPopped(beat, cr);
637 2095 : addBrush(beat, cr);
638 2095 : addArpeggio(beat, cr);
639 2095 : addLyrics(beat, cr, ctx);
640 2095 : addLegato(beat, cr);
641 :
642 : // line segment elements
643 2095 : addOttava(beat, cr);
644 2095 : addLetRing(beat, cr);
645 2095 : addPalmMute(beat, cr);
646 2095 : addTrill(beat, cr);
647 2095 : addRasgueado(beat, cr);
648 2095 : addDive(beat, cr);
649 2095 : addPickScrape(beat, cr);
650 2095 : addHarmonicMark(beat, cr);
651 2095 : addVibratoLeftHand(beat, cr);
652 2095 : addVibratoWTremBar(beat, cr);
653 : }
654 :
655 2095 : ctx.curTick += cr->actualTicks();
656 :
657 2095 : return ctx.curTick;
658 : }
659 :
660 2131 : void GPConverter::convertNotes(const std::vector<std::shared_ptr<GPNote> >& notes, ChordRest* cr)
661 : {
662 4161 : for (const auto& note : notes) {
663 2030 : convertNote(note.get(), cr);
664 : }
665 :
666 : //! NOTE: later notes order is used in linked staff to create ties, glissando
667 2131 : if (cr->isChord()) {
668 1631 : Chord* ch = toChord(cr);
669 1631 : ch->sortNotes();
670 1631 : mu::iex::guitarpro::utils::createGhostNoteParenGroups(ch);
671 : }
672 2131 : }
673 :
674 2030 : void GPConverter::convertNote(const GPNote* gpnote, ChordRest* cr)
675 : {
676 2030 : if (!cr->isChord()) {
677 0 : return;
678 : }
679 :
680 2030 : Chord* ch = toChord(cr);
681 :
682 2030 : Note* note = mu::engraving::Factory::createNote(ch);
683 2030 : note->setTrack(cr->track());
684 2030 : cr->add(note);
685 2030 : setPitch(note, gpnote->midiPitch());
686 2030 : setTpc(note, gpnote->accidental());
687 :
688 2030 : Note* harmonicNote = addHarmonic(gpnote, note);
689 2030 : addBend(gpnote, note);
690 :
691 2030 : addLetRing(gpnote, note);
692 2030 : addPalmMute(gpnote, note);
693 2030 : note->setGhost(gpnote->ghostNote());
694 :
695 2030 : note->setDeadNote(gpnote->muted());
696 2030 : addAccent(gpnote, note);
697 2030 : addSlide(gpnote, note);
698 2030 : addPickScrape(gpnote, note);
699 2030 : collectHammerOn(gpnote, note);
700 2030 : addRightHandTapping(gpnote);
701 2030 : addLeftHandTapping(gpnote);
702 2030 : addStringNumber(gpnote, note);
703 2030 : addOrnament(gpnote, note);
704 2030 : addVibratoLeftHand(gpnote, note);
705 2030 : addTrill(gpnote, note);
706 2030 : addFingering(gpnote, note);
707 2030 : addTie(gpnote, note, _ties);
708 2030 : if (harmonicNote) {
709 49 : addTie(gpnote, harmonicNote, _harmonicTies);
710 : }
711 : }
712 :
713 36 : void GPConverter::configureGraceChord(const GPBeat* beat, ChordRest* cr, GPBeat::OttavaType type)
714 : {
715 36 : convertNotes(beat->notes(), cr);
716 :
717 36 : if (!cr->isChord()) {
718 0 : return;
719 : }
720 :
721 36 : Chord* grChord = toChord(cr);
722 :
723 36 : if (GPBeat::GraceNotes::OnBeat == beat->graceNotes()) {
724 16 : grChord->setNoteType(NoteType::APPOGGIATURA);
725 : } else {
726 20 : grChord->setNoteType(NoteType::ACCIACCATURA);
727 : }
728 :
729 36 : const auto& [foundOttava, muOttavaType] = ottavaType(type);
730 36 : if (foundOttava) {
731 0 : for (Note* note : grChord->notes()) {
732 0 : setPitchByOttavaType(note, muOttavaType);
733 : }
734 : }
735 : }
736 :
737 695 : void GPConverter::addTimeSig(const GPMasterBar* mB, Measure* measure)
738 : {
739 695 : GPMasterBar::TimeSig sig = mB->timeSig();
740 695 : Fraction tick = measure->tick();
741 695 : auto scoreTimeSig = Fraction(sig.numerator, sig.denominator);
742 695 : measure->setTicks(scoreTimeSig);
743 695 : size_t staves = _score->staves().size();
744 :
745 695 : if (_lastTimeSig.numerator == sig.numerator
746 471 : && _lastTimeSig.denominator == sig.denominator) {
747 471 : return;
748 : }
749 224 : _lastTimeSig.numerator = sig.numerator;
750 224 : _lastTimeSig.denominator = sig.denominator;
751 :
752 469 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
753 245 : Staff* staff = _score->staff(staffIdx);
754 245 : if (staff->staffType()->genTimesig()) {
755 245 : Segment* s = measure->getSegment(SegmentType::TimeSig, tick);
756 245 : TimeSig* t = Factory::createTimeSig(s);
757 245 : track_idx_t curTrack = staffIdx * VOICES;
758 245 : t->setTrack(curTrack);
759 245 : t->setSig(scoreTimeSig);
760 245 : if (mB->freeTime()) {
761 7 : t->setLargeParentheses(true);
762 : }
763 245 : s->add(t);
764 : }
765 : }
766 : }
767 :
768 695 : void GPConverter::addRepeat(const GPMasterBar* mB, Measure* measure)
769 : {
770 695 : addVolta(mB, measure);
771 :
772 695 : if (mB->repeat().type == GPMasterBar::Repeat::Type::None) {
773 687 : return;
774 : }
775 :
776 8 : if (mB->repeat().type == GPMasterBar::Repeat::Type::Start) {
777 2 : measure->setRepeatStart(true);
778 : }
779 8 : if (mB->repeat().type == GPMasterBar::Repeat::Type::End) {
780 6 : measure->setRepeatEnd(true);
781 : }
782 8 : if (mB->repeat().type == GPMasterBar::Repeat::Type::StartEnd) {
783 0 : measure->setRepeatStart(true);
784 0 : measure->setRepeatEnd(true);
785 : }
786 8 : measure->setRepeatCount(mB->repeat().count);
787 : }
788 :
789 695 : void GPConverter::addVolta(const GPMasterBar* mB, Measure* measure)
790 : {
791 695 : if (mB->alternateEnding().empty()) {
792 691 : _lastVolta = nullptr;
793 691 : return;
794 : }
795 :
796 4 : if (_lastVolta && _lastVolta->endings().size() != mB->alternateEnding().size()) {
797 2 : _lastVolta = nullptr;
798 : }
799 :
800 4 : if (_lastVolta) {
801 0 : for (const auto& end : mB->alternateEnding()) {
802 0 : if (!_lastVolta->hasEnding(end)) {
803 0 : _lastVolta = nullptr;
804 0 : break;
805 : }
806 : }
807 : }
808 :
809 4 : doAddVolta(mB, measure);
810 : }
811 :
812 4 : void GPConverter::doAddVolta(const GPMasterBar* mB, Measure* measure)
813 : {
814 : mu::engraving::Volta* volta;
815 4 : if (_lastVolta) {
816 0 : volta = _lastVolta;
817 0 : _score->removeElement(volta);
818 : } else {
819 4 : volta = new mu::engraving::Volta(_score->dummy());
820 4 : volta->setTick(measure->tick());
821 4 : _lastVolta = volta;
822 : }
823 :
824 4 : volta->endings().clear();
825 4 : volta->setTick2(measure->endTick());
826 :
827 4 : String str;
828 10 : for (const auto& end : mB->alternateEnding()) {
829 6 : volta->endings().push_back(end);
830 6 : if (str.isEmpty()) {
831 4 : str += String(u"%1").arg(end);
832 : } else {
833 2 : str += String(u"-%1").arg(end);
834 : }
835 : }
836 4 : volta->setText(str);
837 :
838 4 : _score->addElement(volta);
839 4 : }
840 :
841 695 : void GPConverter::addDirection(const GPMasterBar* mB, Measure* measure)
842 : {
843 731 : for (const auto& dir : mB->directions()) {
844 36 : if (dir.type == GPMasterBar::Direction::Type::Jump) {
845 22 : Jump* jump = Factory::createJump(measure);
846 22 : jump->setJumpType(jumpType(dir.name));
847 22 : jump->setTrack(0);
848 22 : measure->add(jump);
849 : } else {
850 14 : Marker* marker = Factory::createMarker(measure);
851 14 : marker->setMarkerType(markerType(dir.name));
852 14 : marker->resetProperty(Pid::LABEL);
853 14 : marker->setTrack(0);
854 14 : measure->add(marker);
855 : }
856 : }
857 695 : }
858 :
859 695 : void GPConverter::addSection(const GPMasterBar* mB, Measure* measure)
860 : {
861 695 : if (mB->section().first.isEmpty() && mB->section().second.isEmpty()) {
862 692 : return;
863 : }
864 :
865 3 : if (!mB->section().first.isEmpty()) {
866 0 : Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
867 0 : RehearsalMark* t = Factory::createRehearsalMark(s);
868 0 : t->setPlainText(mB->section().first);
869 0 : t->setType(RehearsalMark::Type::Main);
870 0 : t->setTrack(0);
871 0 : s->add(t);
872 : }
873 3 : if (!mB->section().second.isEmpty()) {
874 3 : Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
875 3 : RehearsalMark* t = Factory::createRehearsalMark(s);
876 3 : t->setPlainText(mB->section().second);
877 3 : t->setType(RehearsalMark::Type::Additional);
878 3 : t->setTrack(0);
879 3 : s->add(t);
880 : }
881 : }
882 :
883 695 : void GPConverter::addTripletFeel(const GPMasterBar* mB, Measure* measure)
884 : {
885 695 : if (mB->tripletFeel() == _lastTripletFeel) {
886 695 : return; // if last triplet of last measure is equal current dont create new system text
887 : }
888 :
889 0 : Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
890 0 : TripletFeelType type = tripletFeelType(mB->tripletFeel());
891 0 : TripletFeel* tripletFeel = Factory::createTripletFeel(segment, type);
892 0 : _lastTripletFeel = mB->tripletFeel();
893 0 : tripletFeel->setTrack(0);
894 0 : segment->add(tripletFeel);
895 : }
896 :
897 695 : void GPConverter::addKeySig(const GPMasterBar* mB, Measure* measure)
898 : {
899 302 : auto convertKeySig = [](GPMasterBar::KeySig::Accidentals kS) {
900 302 : if (kS == GPMasterBar::KeySig::Accidentals::C_B) {
901 4 : return Key::C_B;
902 298 : } else if (kS == GPMasterBar::KeySig::Accidentals::G_B) {
903 5 : return Key::G_B;
904 293 : } else if (kS == GPMasterBar::KeySig::Accidentals::D_B) {
905 5 : return Key::D_B;
906 288 : } else if (kS == GPMasterBar::KeySig::Accidentals::A_B) {
907 4 : return Key::A_B;
908 284 : } else if (kS == GPMasterBar::KeySig::Accidentals::E_B) {
909 4 : return Key::E_B;
910 280 : } else if (kS == GPMasterBar::KeySig::Accidentals::B_B) {
911 14 : return Key::B_B;
912 266 : } else if (kS == GPMasterBar::KeySig::Accidentals::F) {
913 4 : return Key::F;
914 262 : } else if (kS == GPMasterBar::KeySig::Accidentals::C) {
915 222 : return Key::C;
916 40 : } else if (kS == GPMasterBar::KeySig::Accidentals::G) {
917 16 : return Key::G;
918 24 : } else if (kS == GPMasterBar::KeySig::Accidentals::D) {
919 4 : return Key::D;
920 20 : } else if (kS == GPMasterBar::KeySig::Accidentals::A) {
921 4 : return Key::A;
922 16 : } else if (kS == GPMasterBar::KeySig::Accidentals::E) {
923 4 : return Key::E;
924 12 : } else if (kS == GPMasterBar::KeySig::Accidentals::B) {
925 4 : return Key::B;
926 8 : } else if (kS == GPMasterBar::KeySig::Accidentals::F_S) {
927 4 : return Key::F_S;
928 : } else {
929 4 : return Key::C_S;
930 : }
931 : };
932 :
933 302 : auto convertMode = [](GPMasterBar::KeySig::Mode m) {
934 302 : if (m == GPMasterBar::KeySig::Mode::Major) {
935 252 : return KeyMode::MAJOR;
936 : } else {
937 50 : return KeyMode::MINOR;
938 : }
939 : };
940 :
941 : using KS = GPMasterBar::KeySig::Accidentals;
942 :
943 695 : Fraction tick = measure->tick();
944 695 : size_t staves = _score->staves().size();
945 :
946 302 : auto getNumSharps = [this](int key) {
947 302 : if (key > 0) {
948 164 : for (size_t i = 0; i < m_sharpsToKeyConverter.size(); ++i) {
949 164 : if (m_sharpsToKeyConverter.at(i) == key) {
950 40 : return i;
951 : }
952 : }
953 : } else {
954 599 : for (size_t i = 0; i < m_sharpsToFlatKeysConverter.size(); ++i) {
955 599 : if (m_sharpsToFlatKeysConverter.at(i) == key) {
956 262 : return i;
957 : }
958 : }
959 : }
960 : // Never should get here
961 0 : return muse::nidx;
962 695 : };
963 :
964 1473 : for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
965 778 : if (!tick.isZero() && _lastKeySigs[staffIdx].accidentalCount == mB->keySig().accidentalCount) {
966 476 : continue;
967 : }
968 302 : int key = static_cast<int>(mB->keySig().accidentalCount);
969 :
970 302 : bool useFlats = mB->useFlats() || key < 0;
971 302 : size_t numSharps = getNumSharps(key);
972 302 : IF_ASSERT_FAILED(numSharps != muse::nidx) {
973 0 : LOGE() << "Unprocessable key for key signature";
974 0 : numSharps = 0;
975 : }
976 302 : numSharps = static_cast<int>(numSharps) % 12;
977 302 : if (useFlats) {
978 60 : key = m_sharpsToFlatKeysConverter.at(numSharps);
979 : } else {
980 242 : key = m_sharpsToKeyConverter.at(numSharps);
981 : }
982 :
983 302 : auto scoreKeySig = convertKeySig(static_cast<KS>(key));
984 302 : auto scoreMode = convertMode(mB->keySig().mode);
985 :
986 302 : Staff* staff = _score->staff(staffIdx);
987 302 : if (staff->staffType()->genTimesig()) {
988 302 : Segment* s = measure->getSegment(SegmentType::KeySig, tick);
989 302 : KeySig* t = mu::engraving::Factory::createKeySig(s);
990 302 : t->setTrack(staffIdx * VOICES);
991 302 : t->setKey(scoreKeySig);
992 302 : t->setMode(scoreMode);
993 302 : s->add(t);
994 302 : _lastKeySigs[staffIdx] = mB->keySig();
995 : }
996 : }
997 695 : }
998 :
999 221 : void GPConverter::setUpGPScore(const GPScore* gpscore)
1000 : {
1001 221 : engravingConfiguration()->setGuitarProMultivoiceEnabled(gpscore->multiVoice());
1002 :
1003 : std::vector<String> fieldNames = { gpscore->title(), gpscore->subTitle(), gpscore->artist(),
1004 1768 : gpscore->album(), gpscore->composer(), gpscore->poet() };
1005 :
1006 : bool createTitleField
1007 1497 : = std::any_of(fieldNames.begin(), fieldNames.end(), [](const String& fieldName) { return !fieldName.isEmpty(); });
1008 :
1009 221 : if (!createTitleField && !engravingConfiguration()->guitarProImportExperimental()) {
1010 211 : return;
1011 : }
1012 :
1013 10 : MeasureBase* m = nullptr;
1014 10 : if (!_score->measures()->first()) {
1015 10 : m = Factory::createTitleVBox(_score);
1016 10 : _score->measures()->append(m);
1017 : } else {
1018 0 : m = _score->measures()->first();
1019 0 : if (!m->isVBox()) {
1020 0 : MeasureBase* mb = Factory::createTitleVBox(_score);
1021 0 : _score->addMeasure(mb, m);
1022 0 : m = mb;
1023 : }
1024 : }
1025 :
1026 10 : if (!gpscore->title().isEmpty() || engravingConfiguration()->guitarProImportExperimental()) {
1027 10 : Text* s = Factory::createText(m, TextStyleType::TITLE);
1028 10 : s->setPlainText(gpscore->title());
1029 10 : m->add(s);
1030 : }
1031 15 : if (!gpscore->subTitle().isEmpty() || !gpscore->artist().isEmpty() || !gpscore->album().isEmpty()
1032 15 : || engravingConfiguration()->guitarProImportExperimental()) {
1033 9 : Text* s = Factory::createText(m, TextStyleType::SUBTITLE);
1034 9 : String str;
1035 9 : if (!gpscore->subTitle().isEmpty()) {
1036 5 : str.append(gpscore->subTitle());
1037 : }
1038 9 : if (!gpscore->artist().isEmpty()) {
1039 9 : if (!str.isEmpty()) {
1040 5 : str.append(u'\n');
1041 : }
1042 9 : str.append(gpscore->artist());
1043 : }
1044 9 : if (!gpscore->album().isEmpty()) {
1045 9 : if (!str.isEmpty()) {
1046 9 : str.append(u'\n');
1047 : }
1048 9 : str.append(gpscore->album());
1049 : }
1050 9 : s->setPlainText(str);
1051 9 : m->add(s);
1052 9 : }
1053 10 : if (!gpscore->composer().isEmpty()) {
1054 8 : Text* s = Factory::createText(m, TextStyleType::COMPOSER);
1055 8 : s->setPlainText(muse::mtrc("iex_guitarpro", "Music by %1").arg(gpscore->composer()));
1056 8 : m->add(s);
1057 : }
1058 :
1059 10 : if (!gpscore->poet().isEmpty() || engravingConfiguration()->guitarProImportExperimental()) {
1060 7 : Text* s = Factory::createText(m, TextStyleType::LYRICIST);
1061 7 : if (!gpscore->poet().isEmpty()) {
1062 7 : s->setPlainText(muse::mtrc("iex_guitarpro", "Words by %1").arg(gpscore->poet()));
1063 : }
1064 :
1065 7 : m->add(s);
1066 : }
1067 442 : }
1068 :
1069 221 : void GPConverter::setUpTracks(const std::map<int, std::unique_ptr<GPTrack> >& tracks)
1070 : {
1071 221 : stringDatas.clear();
1072 460 : for (const auto& track : tracks) {
1073 239 : setUpTrack(track.second);
1074 : }
1075 221 : }
1076 :
1077 239 : void GPConverter::setUpTrack(const std::unique_ptr<GPTrack>& tR)
1078 : {
1079 239 : int programm = tR->programm();
1080 239 : int midiChannel = tR->midiChannel();
1081 :
1082 239 : int idx = tR->idx() + 1; // 0 is invalid index
1083 :
1084 239 : Part* part = new Part(_score);
1085 239 : part->setPlainLongName(tR->name());
1086 239 : part->setPlainShortName(tR->shortName());
1087 239 : part->setId(idx);
1088 :
1089 239 : _score->appendPart(part);
1090 481 : for (size_t staffIdx = 0; staffIdx < tR->staffCount(); staffIdx++) {
1091 242 : Staff* s = Factory::createStaff(part);
1092 242 : _score->appendStaff(s);
1093 : }
1094 :
1095 239 : if (tR->staffCount() > 1) {
1096 3 : EditStaffBrackets::addBracket(_score, part->staff(0)->idx(),
1097 3 : mu::engraving::Factory::createBracketItem(_score->dummy(), BracketType::BRACE, 2));
1098 3 : part->staff(0)->setBarLineSpan(true);
1099 : }
1100 :
1101 239 : part->setMidiProgram(programm);
1102 239 : part->setMidiChannel(midiChannel);
1103 :
1104 239 : int vol_val = static_cast<int>(std::lround(tR->rse().volume * 127));
1105 239 : part->instrument()->channel(0)->setVolume(std::clamp(vol_val, 0, 127));
1106 :
1107 239 : int pan_val = static_cast<int>(std::lround(tR->rse().pan * 127));
1108 239 : part->instrument()->channel(0)->setPan(std::clamp(pan_val, 0, 127));
1109 :
1110 239 : Instrument* instr = part->instrument();
1111 239 : instr->setSingleNoteDynamics(false);
1112 239 : instr->setTranspose(tR->transpose());
1113 :
1114 239 : if (midiChannel == PERC_CHANNEL) {
1115 3 : String drumInstrName = tR->instrument();
1116 3 : if (!drumInstrName.empty()) {
1117 0 : part->setShortName(drumInstrName);
1118 : }
1119 :
1120 3 : Staff* staff = part->staff(0);
1121 3 : StaffTypes type = StaffTypes::PERC_DEFAULT;
1122 3 : if (auto it = drumset::PERC_STAFF_LINES_FROM_INSTRUMENT.find(tR->name().toStdString());
1123 3 : it != drumset::PERC_STAFF_LINES_FROM_INSTRUMENT.end()) {
1124 0 : drumset::initGuitarProPercussionSet(it->second);
1125 0 : drumset::setInstrumentDrumset(part->instrument(), it->second);
1126 0 : switch (it->second.numLines) {
1127 0 : case 1:
1128 0 : type = StaffTypes::PERC_1LINE;
1129 0 : break;
1130 0 : case 2:
1131 0 : type = StaffTypes::PERC_2LINE;
1132 0 : break;
1133 0 : case 3:
1134 0 : type = StaffTypes::PERC_3LINE;
1135 0 : break;
1136 0 : default:
1137 0 : type = StaffTypes::PERC_DEFAULT;
1138 0 : break;
1139 : }
1140 : } else {
1141 3 : drumset::initGuitarProDrumset();
1142 3 : part->instrument()->setDrumset(drumset::gpDrumset);
1143 : }
1144 3 : staff->setStaffType(Fraction(0, 1), *StaffType::preset(type));
1145 3 : } else {
1146 236 : auto staffProperties = tR->staffProperties();
1147 :
1148 236 : int capoFret = staffProperties.capoFret;
1149 :
1150 236 : m_capoParams.insert_or_assign(part->id().toUint64(), capoFret);
1151 236 : auto tuning = staffProperties.tuning;
1152 236 : bool usePresetTable = staffProperties.ignoreFlats;
1153 :
1154 236 : std::array<uint64_t, 3> flatPresets{ 0x3f3a36312c27, 0x3c37332e2924, 0x3f3a36312c25 };
1155 :
1156 236 : uint64_t k = 0;
1157 1635 : for (size_t i = 0; i < tuning.size(); ++i) {
1158 1399 : k |= (uint64_t)tuning[i] << 8 * i;
1159 : }
1160 : bool useFlats
1161 236 : = usePresetTable ? std::find(flatPresets.begin(), flatPresets.end(), k) != flatPresets.end() : staffProperties.useFlats;
1162 236 : auto fretCount = staffProperties.fretCount;
1163 :
1164 236 : if (tuning.empty()) {
1165 2 : tuning = utils::standardTuningFor(25, 6); // 6 string guitar
1166 : }
1167 :
1168 236 : int transpose = tR->transpose();
1169 1647 : for (auto& t : tuning) {
1170 1411 : t -= transpose;
1171 : }
1172 :
1173 236 : StringData stringData = StringData(fretCount, static_cast<int>(tuning.size()), tuning.data(), useFlats);
1174 236 : stringDatas.insert_or_assign(part->id().toUint64(), stringData);
1175 : // We're using Tuning String for non-standard string data
1176 : // Instrument string data should be set to the standard
1177 236 : tuning = utils::standardTuningFor(programm, (int)tuning.size());
1178 1650 : for (auto& t : tuning) {
1179 1414 : t -= tR->transpose();
1180 : }
1181 236 : stringData = StringData(fretCount, static_cast<int>(tuning.size()), tuning.data());
1182 236 : instr->setStringData(stringData);
1183 236 : }
1184 239 : }
1185 :
1186 221 : void GPConverter::collectTempoMap(const GPMasterTracks* mTr)
1187 : {
1188 444 : for (const auto& temp : mTr->tempoMap()) {
1189 223 : _tempoMap.insert(std::make_pair(temp.bar, temp));
1190 : }
1191 221 : }
1192 :
1193 695 : void GPConverter::collectFermatas(const GPMasterBar* mB, Measure* measure)
1194 : {
1195 705 : for (const auto& ferm : mB->fermatas()) {
1196 10 : _fermatas.push_back(std::make_pair(measure, ferm));
1197 : }
1198 695 : }
1199 :
1200 825 : void GPConverter::fillUncompletedMeasure(const Context& ctx)
1201 : {
1202 825 : if (m_nextTupletInfo.tuplet) {
1203 3 : fillTuplet();
1204 : }
1205 :
1206 825 : Measure* lastMeasure = _score->lastMeasure();
1207 825 : int tickOffset = lastMeasure->ticks().ticks() + lastMeasure->tick().ticks() - ctx.curTick.ticks();
1208 825 : if (tickOffset > 0) {
1209 173 : _score->setRest(ctx.curTick, ctx.curTrack, Fraction::fromTicks(tickOffset), true, nullptr);
1210 173 : m_continiousElementsBuilder->notifyUncompletedMeasure();
1211 : }
1212 825 : }
1213 :
1214 775 : void GPConverter::hideRestsInEmptyMeasures(track_idx_t startTrack, track_idx_t endTrack)
1215 : {
1216 2948 : for (Segment* segment = _score->lastMeasure()->first(SegmentType::ChordRest); segment;
1217 2173 : segment = segment->next(SegmentType::ChordRest)) {
1218 10865 : for (track_idx_t trackIdx = startTrack; trackIdx < endTrack; trackIdx++) {
1219 8692 : EngravingItem* element = segment->element(trackIdx);
1220 8692 : if (!element || !element->isRest()) {
1221 7975 : continue;
1222 : }
1223 :
1224 717 : Rest* rest = toRest(element);
1225 717 : size_t voice = trackIdx % VOICES;
1226 717 : bool mainVoice = (voice == 0);
1227 :
1228 : // hiding rests in secondary voices for measures without any chords
1229 717 : if (!m_chordExistsInBar) {
1230 210 : rest->setVisible(mainVoice);
1231 210 : continue;
1232 : }
1233 :
1234 : // hiding rests in voices without chords
1235 507 : if (!m_chordExistsForVoice[voice]) {
1236 152 : rest->setVisible(false);
1237 : }
1238 : }
1239 : }
1240 775 : }
1241 :
1242 221 : void GPConverter::addContinuousSlideHammerOn()
1243 : {
1244 53 : auto searchEndNote = [] (Note* start) -> Note* {
1245 : ChordRest* nextCr;
1246 53 : if (toChord(start->parent())->ChordRest::isGrace()) {
1247 : //! this case when start note is a grace note so end note can be next note in grace notes
1248 : //! or parent note of grace notes
1249 5 : Chord* startChord = toChord(start->parent());
1250 :
1251 5 : Chord* parentGrace = toChord(start->parent()->parent());
1252 :
1253 5 : auto it = parentGrace->graceNotes().begin();
1254 7 : for (; it != parentGrace->graceNotes().end(); ++it) {
1255 7 : if (*it == startChord) {
1256 5 : break;
1257 : }
1258 : }
1259 :
1260 5 : if (it == parentGrace->graceNotes().end()) {
1261 0 : nextCr = nullptr;
1262 10 : } else if (std::next(it) == parentGrace->graceNotes().end()) {
1263 5 : nextCr = parentGrace;
1264 : } else {
1265 0 : nextCr = *(++it);
1266 : }
1267 : } else {
1268 48 : nextCr = start->chord()->segment()->next1()->nextChordRest(start->track());
1269 48 : if (!nextCr) {
1270 0 : return nullptr;
1271 : }
1272 :
1273 48 : if (nextCr->isChord() && !toChord(nextCr)->graceNotes().empty()) {
1274 1 : Chord* firstGrace = toChord(nextCr)->graceNotes().front();
1275 1 : bool isDiveGrace = false;
1276 1 : if (!firstGrace->notes().empty()) {
1277 1 : Note* gn = firstGrace->notes().front();
1278 1 : isDiveGrace = gn->ghost() || gn->diveFor() || gn->diveBack();
1279 : }
1280 1 : if (!isDiveGrace) {
1281 0 : nextCr = firstGrace;
1282 : }
1283 : }
1284 : }
1285 :
1286 53 : if (!nextCr) {
1287 0 : return nullptr;
1288 : }
1289 :
1290 53 : if (!nextCr->isChord()) {
1291 0 : return nullptr;
1292 : }
1293 53 : auto nextChord = toChord(nextCr);
1294 53 : for (auto note : nextChord->notes()) {
1295 53 : if (note->string() == start->string() && (note->harmonic() == start->harmonic())) {
1296 53 : return note;
1297 : }
1298 : }
1299 :
1300 0 : return nextChord->upNote();
1301 : };
1302 :
1303 221 : std::unordered_map<Note*, Slur*> legatoSlides;
1304 221 : std::unordered_map<Note*, HammerOnPullOff*> hammerOnPullOffs;
1305 221 : std::unordered_set<Chord*> hammerOnInChord;
1306 274 : for (const auto& slide : _slideHammerOnMap) {
1307 53 : Note* startNote = slide.first;
1308 53 : Note* endNote = searchEndNote(startNote);
1309 53 : if (!endNote || startNote->string() == -1 || startNote->fret() == endNote->fret()) {
1310 : //mistake in GP: such kind od slides shouldn't exist
1311 0 : continue;
1312 : }
1313 :
1314 53 : Note* currentStart = nullptr;
1315 53 : if (startNote->bendFor()) {
1316 3 : Note* bendNote = startNote;
1317 3 : GuitarBend* bend = bendNote->bendFor();
1318 :
1319 4 : while (bend) {
1320 3 : if (bend->isDive()) {
1321 2 : break;
1322 : }
1323 :
1324 1 : bendNote = bend->endNote();
1325 1 : IF_ASSERT_FAILED(bendNote) {
1326 0 : LOGE() << "glissando start note may be incorrect";
1327 0 : break;
1328 : }
1329 :
1330 1 : if (!bendNote->chord()->isGraceAfter()) {
1331 0 : break;
1332 : }
1333 :
1334 1 : currentStart = bendNote;
1335 1 : bend = bendNote->bendFor();
1336 : }
1337 :
1338 3 : if (currentStart) {
1339 1 : startNote = currentStart;
1340 : }
1341 : }
1342 :
1343 53 : Fraction startTick = startNote->chord()->tick();
1344 53 : Fraction endTick = endNote->chord()->tick();
1345 53 : track_idx_t track = startNote->track();
1346 :
1347 : /// Layout info
1348 53 : if (slide.second == SlideHammerOn::LegatoSlide || slide.second == SlideHammerOn::Slide) {
1349 12 : Glissando* gl = mu::engraving::Factory::createGlissando(startNote);
1350 12 : gl->setStartElement(startNote);
1351 12 : gl->setTrack(track);
1352 12 : gl->setTick(startTick);
1353 12 : gl->setTick2(endNote->chord()->tick());
1354 12 : gl->setEndElement(endNote);
1355 12 : gl->setText(u"");
1356 12 : gl->setGlissandoType(GlissandoType::STRAIGHT);
1357 12 : gl->setGlissandoShift(slide.second == SlideHammerOn::Slide);
1358 12 : gl->setGlissandoStyle(startNote->part()->instrument(startTick)->glissandoStyle());
1359 12 : _score->addElement(gl);
1360 : }
1361 :
1362 53 : if (slide.second == SlideHammerOn::LegatoSlide) {
1363 8 : if (legatoSlides.count(startNote) == 0) {
1364 8 : Slur* slur = mu::engraving::Factory::createSlur(_score->dummy());
1365 8 : if (slide.second == SlideHammerOn::LegatoSlide) {
1366 8 : slur->setConnectedElement(mu::engraving::Slur::ConnectedElement::GLISSANDO);
1367 : }
1368 :
1369 8 : slur->setStartElement(startNote->chord());
1370 8 : slur->setTrack(track);
1371 8 : slur->setTick(startTick);
1372 8 : slur->setTick2(endTick);
1373 8 : legatoSlides[endNote] = slur;
1374 8 : slur->setEndElement(endNote->chord());
1375 8 : _score->addSpanner(slur);
1376 : } else {
1377 0 : Slur* slur = legatoSlides[startNote];
1378 0 : slur->setTick2(endTick);
1379 0 : slur->setEndElement(endNote->chord());
1380 0 : legatoSlides.erase(startNote);
1381 0 : legatoSlides[endNote] = slur;
1382 : }
1383 45 : } else if (slide.second == SlideHammerOn::HammerOn) {
1384 41 : Chord* startChord = startNote->chord();
1385 41 : if (hammerOnInChord.find(startChord) != hammerOnInChord.end()) {
1386 0 : continue;
1387 : }
1388 :
1389 41 : if (hammerOnPullOffs.count(startNote) == 0) {
1390 35 : HammerOnPullOff* hammerOnPullOff = Factory::createHammerOnPullOff(_score->dummy());
1391 35 : hammerOnPullOff->setTrack(startNote->track());
1392 35 : hammerOnPullOff->setTick(startNote->tick());
1393 35 : hammerOnPullOff->setTick2(endNote->tick());
1394 35 : hammerOnPullOff->setStartElement(startChord);
1395 35 : hammerOnPullOff->setEndElement(endNote->chord());
1396 35 : _score->addElement(hammerOnPullOff);
1397 35 : hammerOnPullOffs[endNote] = hammerOnPullOff;
1398 35 : hammerOnInChord.insert(startChord);
1399 : } else {
1400 6 : HammerOnPullOff* hammerOnPullOff = hammerOnPullOffs[startNote];
1401 6 : hammerOnPullOff->setTick2(endTick);
1402 6 : hammerOnPullOff->setEndElement(endNote->chord());
1403 6 : hammerOnPullOffs.erase(startNote);
1404 6 : hammerOnPullOffs[endNote] = hammerOnPullOff;
1405 : }
1406 : }
1407 : }
1408 221 : }
1409 :
1410 221 : void GPConverter::addFermatas()
1411 : {
1412 10 : auto fermataType = [](const GPMasterBar::Fermata& fer) {
1413 10 : if (fer.type == GPMasterBar::Fermata::Type::Long) {
1414 0 : return SymId::fermataLongAbove;
1415 10 : } else if (fer.type == GPMasterBar::Fermata::Type::Short) {
1416 0 : return SymId::fermataShortAbove;
1417 : }
1418 10 : return SymId::fermataAbove;
1419 : };
1420 :
1421 231 : for (const auto& fr : _fermatas) {
1422 10 : const auto& measure = fr.first;
1423 10 : const auto& gpFermata = fr.second;
1424 10 : Fraction tick = Fraction::fromTicks(mu::engraving::Constants::DIVISION * gpFermata.offsetNum / gpFermata.offsetDenom);
1425 : // bellow how gtp fermata timeStretch converting to MU timeStretch
1426 10 : float convertingLength = 1.5f - gpFermata.length * 0.5f + gpFermata.length * gpFermata.length * 3;
1427 10 : Segment* seg = measure->getSegmentR(SegmentType::ChordRest, tick);
1428 :
1429 20 : for (size_t staffIdx = 0; staffIdx < _score->staves().size(); staffIdx++) {
1430 10 : Fermata* fermata = mu::engraving::Factory::createFermata(seg);
1431 10 : SymId type = fermataType(fr.second);
1432 10 : fermata->setSymIdAndTimeStretch(type);
1433 10 : fermata->setTimeStretch(convertingLength);
1434 10 : fermata->setTrack(staffIdx * VOICES);
1435 10 : seg->add(fermata);
1436 : }
1437 : }
1438 221 : }
1439 :
1440 223 : static Segment* findClosestSegment(Measure* m, Fraction tick)
1441 : {
1442 223 : Segment* segment = m->findSegment(SegmentType::ChordRest, tick);
1443 223 : if (!segment) {
1444 0 : segment = m->getSegment(SegmentType::ChordRest, tick);
1445 0 : Segment* prev = segment->prev1(SegmentType::ChordRest);
1446 0 : Segment* next = segment->next1(SegmentType::ChordRest);
1447 0 : if (prev && next) {
1448 0 : segment = (next->tick() - segment->tick() < segment->tick() - prev->tick() ? next : prev);
1449 0 : } else if (prev) {
1450 0 : segment = prev;
1451 0 : } else if (next) {
1452 0 : segment = next;
1453 : }
1454 : }
1455 :
1456 223 : return segment;
1457 : }
1458 :
1459 221 : void GPConverter::addTempoMap()
1460 : {
1461 223 : auto realTempo = [](const GPMasterTracks::Automation& temp) {
1462 : //real tempo - beats per second
1463 : //formula ro convert tempo from GTP tempo values
1464 223 : double tempo = temp.value;
1465 :
1466 223 : if (temp.tempoUnit == 0) {
1467 0 : return tempo;
1468 : }
1469 :
1470 223 : if (temp.tempoUnit != 5) {
1471 223 : return tempo * temp.tempoUnit / 2;
1472 : }
1473 :
1474 0 : if (temp.tempoUnit == 5) {
1475 0 : return tempo + tempo * 2;
1476 : }
1477 :
1478 0 : return tempo;
1479 : };
1480 :
1481 221 : int measureIdx = 0;
1482 221 : GradualTempoChange* _lastGradualTempoChange = nullptr;
1483 221 : int previousTempo = -1;
1484 :
1485 916 : for (auto m = _score->firstMeasure(); m; m = m->nextMeasure()) {
1486 695 : auto range = _tempoMap.equal_range(measureIdx); //one measure can have several tempo changing
1487 695 : measureIdx++;
1488 918 : for (auto tempIt = range.first; tempIt != range.second; tempIt++) {
1489 223 : Fraction tick = m->tick() + Fraction::fromTicks(
1490 446 : tempIt->second.position * Constants::DIVISION * 4 * m->ticks().numerator() / m->ticks().denominator());
1491 223 : Segment* segment = findClosestSegment(m, tick);
1492 223 : double realTemp = realTempo(tempIt->second);
1493 223 : TempoText* tt = Factory::createTempoText(segment);
1494 223 : tt->setTempo((double)realTemp / 60);
1495 223 : String& labelText = tempIt->second.text;
1496 223 : String tempoText = String(u"<sym>metNoteQuarterUp</sym> = %1").arg(round(realTemp));
1497 :
1498 223 : if (!labelText.isEmpty()) {
1499 28 : tempoText.prepend(labelText.append(Char(' ')));
1500 : }
1501 :
1502 223 : tt->setXmlText(tempoText);
1503 223 : tt->setTrack(0);
1504 223 : segment->add(tt);
1505 :
1506 223 : if (_lastGradualTempoChange) {
1507 0 : _lastGradualTempoChange->setTick2(tick);
1508 0 : GradualTempoChangeType tempoChangeType = GradualTempoChangeType::Undefined;
1509 0 : String tempoChangeText;
1510 :
1511 0 : if (realTemp > previousTempo) {
1512 0 : tempoChangeType = GradualTempoChangeType::Accelerando;
1513 0 : tempoChangeText = u"accel.";
1514 : } else {
1515 0 : tempoChangeType = GradualTempoChangeType::Rallentando;
1516 0 : tempoChangeText = u"rall.";
1517 : }
1518 :
1519 0 : _lastGradualTempoChange->setTempoChangeType(tempoChangeType);
1520 0 : _lastGradualTempoChange->setBeginText(tempoChangeText);
1521 0 : _lastGradualTempoChange->setContinueText(String(u"(%1)").arg(tempoChangeText));
1522 0 : _score->addElement(_lastGradualTempoChange);
1523 0 : _lastGradualTempoChange = nullptr;
1524 0 : }
1525 :
1526 223 : if (tempIt->second.linear) {
1527 0 : GradualTempoChange* tempoChange = Factory::createGradualTempoChange(_score->dummy());
1528 0 : tempoChange->setTick(tick);
1529 0 : tempoChange->setTrack(0);
1530 0 : _lastGradualTempoChange = tempoChange;
1531 : }
1532 :
1533 223 : previousTempo = realTemp;
1534 223 : }
1535 : }
1536 221 : }
1537 :
1538 221 : void GPConverter::addInstrumentChanges()
1539 : {
1540 460 : for (const auto& track : _gpDom->tracks()) {
1541 410 : for (const auto& soundAutomation : track.second->soundAutomations()) {
1542 171 : int bar = soundAutomation.second.bar;
1543 171 : float pos = soundAutomation.second.position;
1544 :
1545 171 : int trackIdx = track.second->idx();
1546 :
1547 171 : int midiProgramm = 0;
1548 171 : String instrName;
1549 :
1550 171 : auto it = track.second->sounds().find(soundAutomation.second.value);
1551 171 : if (it == track.second->sounds().end()) {
1552 48 : midiProgramm = track.second->programm();
1553 48 : instrName = soundAutomation.second.value;
1554 : } else {
1555 123 : midiProgramm = it->second.programm;
1556 123 : instrName = it->second.label;
1557 : }
1558 :
1559 171 : if (bar == 0 && pos == 0 && midiProgramm == track.second->programm()) {
1560 : // skipping the default instrument in the beginning of the track
1561 154 : continue;
1562 : }
1563 :
1564 17 : Measure* m = _score->crMeasure(bar);
1565 17 : Segment* seg = m->first(SegmentType::ChordRest);
1566 17 : if (pos != 0) {
1567 : // searching for correct segment to put instrument change
1568 14 : Fraction tick = m->tick() + Fraction::fromTicks(pos * Constants::DIVISION);
1569 14 : Segment* positionedSegment = m->findSegment(SegmentType::ChordRest, tick);
1570 14 : if (positionedSegment) {
1571 14 : seg = positionedSegment;
1572 : }
1573 : }
1574 :
1575 17 : Instrument instr;
1576 17 : instr.setTranspose(track.second->transpose());
1577 17 : instr.setStringData(*_score->parts()[trackIdx]->instrument()->stringData());
1578 17 : instr.channel(0)->setProgram(midiProgramm);
1579 17 : if (track.second->midiChannel() == PERC_CHANNEL) {
1580 0 : instr.setDrumset(drumset::gpDrumset);
1581 : }
1582 :
1583 17 : InstrumentChange* instrCh = Factory::createInstrumentChange(seg, instr);
1584 17 : instrCh->setTrack(trackIdx * VOICES);
1585 17 : instrCh->setXmlText(instrName);
1586 :
1587 17 : seg->add(instrCh);
1588 171 : }
1589 : }
1590 221 : }
1591 :
1592 778 : bool GPConverter::addSimileMark(const GPBar* bar, int curTrack)
1593 : {
1594 778 : if (bar->simileMark() == GPBar::SimileMark::None) {
1595 775 : return false;
1596 : }
1597 :
1598 3 : Measure* measure = _score->lastMeasure();
1599 3 : Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
1600 3 : staff_idx_t staffIdx = track2staff(curTrack);
1601 :
1602 3 : switch (bar->simileMark()) {
1603 3 : case GPBar::SimileMark::Simple: {
1604 3 : MeasureRepeat* mr = Factory::createMeasureRepeat(segment);
1605 3 : mr->setTrack(curTrack);
1606 3 : mr->setTicks(measure->ticks());
1607 3 : mr->setNumMeasures(1);
1608 3 : segment->add(mr);
1609 3 : measure->setMeasureRepeatCount(1, staffIdx);
1610 3 : break;
1611 : }
1612 0 : case GPBar::SimileMark::FirstOfDouble: {
1613 0 : MeasureRepeat* mr = Factory::createMeasureRepeat(segment);
1614 0 : mr->setTrack(staff2track(staffIdx));
1615 0 : mr->setTicks(measure->ticks());
1616 0 : mr->setNumMeasures(2);
1617 0 : segment->add(mr);
1618 0 : measure->setMeasureRepeatCount(1, staffIdx);
1619 0 : break;
1620 : }
1621 0 : case GPBar::SimileMark::SecondOfDouble: {
1622 0 : Rest* r = Factory::createRest(segment);
1623 0 : r->setTrack(staff2track(staffIdx));
1624 0 : r->setTicks(measure->ticks());
1625 0 : r->setDurationType(DurationType::V_MEASURE);
1626 0 : segment->add(r);
1627 0 : measure->setMeasureRepeatCount(2, staffIdx);
1628 0 : break;
1629 : }
1630 0 : case GPBar::SimileMark::None:
1631 0 : break;
1632 : }
1633 3 : return true;
1634 : }
1635 :
1636 778 : void GPConverter::addClef(const GPBar* bar, int curTrack)
1637 : {
1638 778 : auto convertClef = [](GPBar::Clef cl) {
1639 778 : if (cl.type == GPBar::ClefType::Neutral) {
1640 32 : return ClefType::PERC;
1641 746 : } else if (cl.type == GPBar::ClefType::G2) {
1642 727 : switch (cl.ottavia) {
1643 2 : case GPBar::OttaviaType::va8:
1644 2 : return ClefType::G8_VA;
1645 2 : case GPBar::OttaviaType::vb8:
1646 2 : return ClefType::G8_VB;
1647 2 : case GPBar::OttaviaType::ma15:
1648 2 : return ClefType::G15_MA;
1649 0 : case GPBar::OttaviaType::mb15:
1650 0 : return ClefType::G15_MB;
1651 721 : default:
1652 721 : return ClefType::G;
1653 : }
1654 19 : } else if (cl.type == GPBar::ClefType::F4) {
1655 15 : switch (cl.ottavia) {
1656 0 : case GPBar::OttaviaType::va8:
1657 0 : return ClefType::F_8VA;
1658 0 : case GPBar::OttaviaType::vb8:
1659 0 : return ClefType::F8_VB;
1660 0 : case GPBar::OttaviaType::ma15:
1661 0 : return ClefType::F_15MA;
1662 0 : case GPBar::OttaviaType::mb15:
1663 0 : return ClefType::F15_MB;
1664 15 : default:
1665 15 : return ClefType::F;
1666 : }
1667 4 : } else if (cl.type == GPBar::ClefType::C3) {
1668 2 : return ClefType::C3;
1669 2 : } else if (cl.type == GPBar::ClefType::C4 && cl.ottavia == GPBar::OttaviaType::vb8) {
1670 0 : return ClefType::C4_8VB;
1671 2 : } else if (cl.type == GPBar::ClefType::C4) {
1672 2 : return ClefType::C4;
1673 : }
1674 0 : return ClefType::G;
1675 : };
1676 :
1677 778 : Measure* lastMeasure = _score->lastMeasure();
1678 778 : Fraction tick = lastMeasure->tick();
1679 778 : ClefType clef = convertClef(bar->clef());
1680 :
1681 778 : if (tick == Fraction{ 0, 1 }) {
1682 242 : _clefs[curTrack] = bar->clef();
1683 242 : Staff* s = _score->staff(track2staff(curTrack));
1684 242 : s->setDefaultClefType(ClefTypeList(clef, clef));
1685 766 : return;
1686 : }
1687 :
1688 536 : if (_clefs.find(curTrack) != _clefs.end() && _clefs.at(curTrack) == bar->clef()) {
1689 524 : return;
1690 : }
1691 :
1692 12 : Segment* s = lastMeasure->getSegment(SegmentType::HeaderClef, tick);
1693 12 : Clef* cl = mu::engraving::Factory::createClef(s);
1694 12 : cl->setTrack(curTrack);
1695 12 : cl->setClefType(clef);
1696 :
1697 12 : s->add(cl);
1698 12 : _clefs[curTrack] = bar->clef();
1699 : }
1700 :
1701 695 : Measure* GPConverter::addMeasure(const GPMasterBar* mB)
1702 : {
1703 695 : Fraction tick = _score->measures()->last() ? _score->measures()->last()->endTick() : Fraction(0, 1);
1704 695 : Measure* measure = Factory::createMeasure(_score);
1705 695 : measure->setTick(tick);
1706 695 : GPMasterBar::TimeSig sig = mB->timeSig();
1707 695 : auto scoreTimeSig = Fraction(sig.numerator, sig.denominator);
1708 695 : measure->setTimesig(scoreTimeSig);
1709 695 : measure->setTicks(scoreTimeSig);
1710 695 : _score->measures()->append(measure);
1711 :
1712 695 : return measure;
1713 : }
1714 :
1715 2131 : ChordRest* GPConverter::addChordRest(const GPBeat* beat, const Context& ctx)
1716 : {
1717 2148 : auto rhythm = [](GPRhythm::RhytmType rhythm) {
1718 2148 : if (rhythm == GPRhythm::RhytmType::Whole) {
1719 240 : return 1;
1720 1908 : } else if (rhythm == GPRhythm::RhytmType::Half) {
1721 169 : return 2;
1722 1739 : } else if (rhythm == GPRhythm::RhytmType::Quarter) {
1723 1378 : return 4;
1724 361 : } else if (rhythm == GPRhythm::RhytmType::Eighth) {
1725 176 : return 8;
1726 185 : } else if (rhythm == GPRhythm::RhytmType::Sixteenth) {
1727 149 : return 16;
1728 36 : } else if (rhythm == GPRhythm::RhytmType::ThirtySecond) {
1729 36 : return 32;
1730 : } else {
1731 0 : return 64;
1732 : }
1733 : };
1734 :
1735 2131 : ChordRest* cr{ nullptr };
1736 2131 : if (beat->isRest()) {
1737 500 : cr = Factory::createRest(_score->dummy());
1738 : } else {
1739 1631 : cr = Factory::createChord(_score->dummy());
1740 : }
1741 :
1742 2131 : cr->setTrack(ctx.curTrack);
1743 :
1744 2131 : Fraction fr(1, rhythm(beat->lenth().second));
1745 2148 : for (int dot = 1; dot <= beat->lenth().first; dot++) {
1746 17 : fr += Fraction(1, rhythm(beat->lenth().second) * 2 * dot);
1747 : }
1748 :
1749 2131 : cr->setTicks(fr);
1750 2131 : cr->setDurationType(TDuration(fr));
1751 :
1752 2131 : return cr;
1753 : }
1754 :
1755 2030 : void GPConverter::addFingering(const GPNote* gpnote, Note* note)
1756 : {
1757 2030 : if (gpnote->leftFingering().isEmpty() && gpnote->rightFingering().isEmpty()) {
1758 2008 : return;
1759 : }
1760 :
1761 12 : auto scoreFinger = [](const String& str) {
1762 12 : if (str == u"Open") {
1763 0 : return String(u'O');
1764 : }
1765 12 : return str;
1766 : };
1767 :
1768 22 : if (!gpnote->leftFingering().isEmpty()) {
1769 12 : Fingering* f = Factory::createFingering(note);
1770 12 : f->setPlainText(scoreFinger(gpnote->leftFingering()));
1771 12 : note->add(f);
1772 : }
1773 22 : if (!gpnote->rightFingering().isEmpty()) {
1774 10 : Fingering* f = Factory::createFingering(note);
1775 10 : f->setPlainText(gpnote->rightFingering());
1776 10 : note->add(f);
1777 : }
1778 : }
1779 :
1780 2030 : void GPConverter::addStringNumber(const GPNote* gpnote, Note* note)
1781 : {
1782 2030 : if (!gpnote->showStringNumber()) {
1783 2030 : return;
1784 : }
1785 :
1786 0 : Fingering* f = Factory::createFingering(note);
1787 :
1788 0 : f->setPlainText(String::number(note->string() + 1));
1789 0 : f->setTextStyleType(TextStyleType::STRING_NUMBER);
1790 0 : f->setFrameType(FrameType::CIRCLE);
1791 0 : note->add(f);
1792 : }
1793 :
1794 2030 : void GPConverter::addTrill(const GPNote* gpnote, Note* note)
1795 : {
1796 : UNUSED(note);
1797 2030 : if (gpnote->trill().auxillaryFret == -1) {
1798 2028 : return;
1799 : }
1800 :
1801 2 : m_currentGPBeat->setTrill(true);
1802 : }
1803 :
1804 2095 : void GPConverter::addTrill(const GPBeat* gpbeat, ChordRest* cr)
1805 : {
1806 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::TRILL, ContiniousElementsBuilder::ImportType::TRILL,
1807 2095 : gpbeat->trill());
1808 2095 : }
1809 :
1810 2030 : void GPConverter::addOrnament(const GPNote* gpnote, Note* note)
1811 : {
1812 2030 : if (gpnote->ornament() == GPNote::Ornament::None) {
1813 2022 : return;
1814 : }
1815 :
1816 8 : auto scoreOrnament = [](const auto& orn) {
1817 8 : if (orn == GPNote::Ornament::Turn) {
1818 2 : return SymId::ornamentTurn;
1819 6 : } else if (orn == GPNote::Ornament::InvertedTurn) {
1820 2 : return SymId::ornamentTurnInverted;
1821 4 : } else if (orn == GPNote::Ornament::LowerMordent) {
1822 2 : return SymId::ornamentMordent;
1823 2 : } else if (orn == GPNote::Ornament::UpperMordent) {
1824 2 : return SymId::ornamentShortTrill;
1825 : }
1826 0 : return SymId::ornamentUpPrall;
1827 : };
1828 :
1829 8 : Articulation* art = mu::engraving::Factory::createArticulation(_score->dummy());
1830 8 : art->setSymId(scoreOrnament(gpnote->ornament()));
1831 8 : if (!EditChord::toggleArticulation(_score, note, art)) {
1832 0 : delete art;
1833 : }
1834 : }
1835 :
1836 2030 : void GPConverter::addVibratoLeftHand(const GPNote* gpnote, Note*)
1837 : {
1838 2030 : if (gpnote->vibratoType() == GPNote::VibratoType::None) {
1839 2021 : return;
1840 : }
1841 :
1842 9 : m_currentGPBeat->setVibratoLeftHand(
1843 9 : gpnote->vibratoType() == GPNote::VibratoType::Slight ? GPBeat::VibratoLeftHand::Slight : GPBeat::VibratoLeftHand::Wide);
1844 : }
1845 :
1846 2030 : Note* GPConverter::addHarmonic(const GPNote* gpnote, Note* note)
1847 : {
1848 2030 : if (gpnote->harmonic().type == GPNote::Harmonic::Type::None) {
1849 1981 : return nullptr;
1850 : }
1851 :
1852 49 : Note* hnote = nullptr;
1853 49 : if (gpnote->harmonic().type != GPNote::Harmonic::Type::Natural) {
1854 46 : hnote = mu::engraving::Factory::createNote(note->chord());
1855 46 : hnote->setTrack(note->track());
1856 46 : hnote->setString(note->string());
1857 46 : hnote->setPitch(note->pitch());
1858 46 : hnote->setFret(note->fret());
1859 :
1860 : /// @note option to show or not additional harmonic fret in "<>" to be implemented
1861 : ///note->setDisplayFret(Note::DisplayFretOption::ArtificialHarmonic);
1862 : ///hnote->setDisplayFret(Note::DisplayFretOption::Hide);
1863 46 : hnote->setDisplayFret(Note::DisplayFretOption::Hide);
1864 :
1865 46 : hnote->setTpcFromPitch();
1866 46 : note->chord()->add(hnote);
1867 46 : hnote->setPlay(false);
1868 46 : note->setPlay(true);
1869 :
1870 46 : note->setHarmonicFret(note->fret() + gpnote->harmonic().fret);
1871 : } else {
1872 3 : note->setHarmonicFret(gpnote->harmonic().fret);
1873 3 : note->setDisplayFret(Note::DisplayFretOption::NaturalHarmonic);
1874 : }
1875 :
1876 49 : Note* harmonicNote = hnote ? hnote : note;
1877 :
1878 49 : int gproHarmonicType = static_cast<int>(gpnote->harmonic().type);
1879 49 : int harmonicFret = utils::harmonicOvertone(note, gpnote->harmonic().fret, gproHarmonicType);
1880 49 : int string = harmonicNote->string();
1881 49 : int harmonicPitch = harmonicNote->part()->instrument()->stringData()->getPitch(string,
1882 49 : harmonicFret + capoFret(harmonicNote->part()),
1883 49 : harmonicNote->staff());
1884 :
1885 49 : harmonicNote->setPitch(harmonicPitch);
1886 49 : harmonicNote->setTpcFromPitch();
1887 49 : harmonicNote->setHarmonic(true);
1888 49 : note->setHarmonicPitchOffset(harmonicNote->pitch() - note->pitch());
1889 :
1890 49 : if (GPNote::Harmonic::isArtificial(gpnote->harmonic().type) && m_currentGPBeat) {
1891 46 : m_currentGPBeat->addHarmonicMarkType(harmonicTypeNoteToBeat(gpnote->harmonic().type));
1892 : }
1893 :
1894 49 : return harmonicNote;
1895 : }
1896 :
1897 2030 : void GPConverter::addAccent(const GPNote* gpnote, Note* note)
1898 : {
1899 2030 : if (gpnote->accents().none()) {
1900 2024 : return;
1901 : }
1902 :
1903 6 : auto symbolsIds = note->chord()->articulationSymbolIds();
1904 :
1905 12 : auto accentType = [](size_t flagIdx) {
1906 12 : if (flagIdx == 0) {
1907 0 : return SymId::articStaccatoAbove;
1908 12 : } else if (flagIdx == 2) {
1909 4 : return SymId::articMarcatoAbove;
1910 8 : } else if (flagIdx == 3) {
1911 8 : return SymId::articAccentAbove;
1912 : } else {
1913 0 : return SymId::dynamicSforzando;
1914 : }
1915 : };
1916 :
1917 30 : for (size_t flagIdx = 0; flagIdx < gpnote->accents().size(); flagIdx++) {
1918 24 : if (gpnote->accents()[flagIdx] && symbolsIds.find(accentType(flagIdx)) == symbolsIds.end()) {
1919 6 : Articulation* art = mu::engraving::Factory::createArticulation(note->chord());
1920 6 : art->setSymId(accentType(flagIdx));
1921 6 : note->chord()->add(art);
1922 : }
1923 : }
1924 6 : }
1925 :
1926 2030 : void GPConverter::addLeftHandTapping(const GPNote* gpnote)
1927 : {
1928 2030 : if (gpnote->leftHandTapped() && m_currentGPBeat) {
1929 0 : m_currentGPBeat->setTappingHand(GPBeat::TappingHand::Left);
1930 : }
1931 2030 : }
1932 :
1933 2030 : void GPConverter::addRightHandTapping(const GPNote* gpnote)
1934 : {
1935 2030 : if (gpnote->rightHandTapping() && m_currentGPBeat) {
1936 2 : m_currentGPBeat->setTappingHand(GPBeat::TappingHand::Right);
1937 : }
1938 2030 : }
1939 :
1940 2030 : void GPConverter::addSlide(const GPNote* gpnote, Note* note)
1941 : {
1942 2030 : if (gpnote->slides().none()) {
1943 1986 : return;
1944 : }
1945 :
1946 44 : addSingleSlide(gpnote, note);
1947 44 : collectContinuousSlide(gpnote, note);
1948 : }
1949 :
1950 44 : void GPConverter::addSingleSlide(const GPNote* gpnote, Note* note)
1951 : {
1952 32 : auto slideType = [](size_t flagIdx) -> ChordLineType {
1953 32 : switch (flagIdx) {
1954 8 : case 2:
1955 8 : return ChordLineType::FALL;
1956 8 : case 3:
1957 8 : return ChordLineType::DOIT;
1958 8 : case 4:
1959 8 : return ChordLineType::SCOOP;
1960 8 : case 5:
1961 8 : return ChordLineType::PLOP;
1962 0 : default:
1963 0 : LOGE() << "wrong slide type";
1964 0 : return ChordLineType::NOTYPE;
1965 : }
1966 : };
1967 :
1968 220 : for (size_t flagIdx = 2; flagIdx < gpnote->slides().size(); flagIdx++) {
1969 176 : if (gpnote->slides()[flagIdx]) {
1970 32 : ChordLine* cl = Factory::createChordLine(note->chord());
1971 32 : cl->setChordLineType(slideType(flagIdx));
1972 32 : cl->setStraight(true);
1973 32 : note->chord()->add(cl);
1974 32 : cl->setNote(note);
1975 : }
1976 : }
1977 44 : }
1978 :
1979 44 : void GPConverter::collectContinuousSlide(const GPNote* gpnote, Note* note)
1980 : {
1981 44 : if (gpnote->slides()[0]) {
1982 4 : _slideHammerOnMap.push_back(std::pair(note, SlideHammerOn::Slide));
1983 : }
1984 44 : if (gpnote->slides()[1]) {
1985 8 : _slideHammerOnMap.push_back(std::pair(note, SlideHammerOn::LegatoSlide));
1986 : }
1987 44 : }
1988 :
1989 2030 : void GPConverter::addPickScrape(const GPNote* gpnote, Note* note)
1990 : {
1991 2030 : if (gpnote->pickScrape() != GPNote::PickScrape::None && m_currentGPBeat) {
1992 3 : if (engravingConfiguration()->guitarProImportExperimental()) {
1993 0 : ChordLine* cl = mu::engraving::Factory::createChordLine(note->chord());
1994 0 : cl->setChordLineType(gpnote->pickScrape() == GPNote::PickScrape::Down ? ChordLineType::FALL : ChordLineType::DOIT);
1995 0 : cl->setWavy(true);
1996 0 : note->chord()->add(cl);
1997 0 : cl->setNote(note);
1998 : }
1999 :
2000 3 : m_currentGPBeat->setPickScrape(true);
2001 : }
2002 2030 : }
2003 :
2004 2030 : void GPConverter::collectHammerOn(const GPNote* gpnote, Note* note)
2005 : {
2006 2030 : if (gpnote->hammerOn() == GPNote::HammerOn::Start) {
2007 41 : _slideHammerOnMap.push_back(std::pair(note, SlideHammerOn::HammerOn));
2008 : }
2009 2030 : }
2010 :
2011 185 : static mu::engraving::PitchValues gpBendCurveToPitchValues(const GPNote::Bend& b)
2012 : {
2013 : using namespace mu::engraving;
2014 :
2015 655 : auto gpTimeToMuTime = [] (float time) {
2016 655 : return time * PitchValue::MAX_TIME / 100;
2017 : };
2018 :
2019 : //! NOTE: In GPX format, -1 means "not set". Normalize to sensible defaults.
2020 185 : const float originOffset = std::max(0.f, b.originOffset);
2021 185 : const float destOffset = std::max(0.f, b.destinationOffset);
2022 185 : const bool hasMiddleValue = (b.middleValue != -1) && !(b.middleOffset1 == 12 && b.middleOffset2 == 12);
2023 :
2024 185 : PitchValues pitchValues;
2025 :
2026 185 : pitchValues.push_back(PitchValue(gpTimeToMuTime(originOffset), b.originValue));
2027 185 : PitchValue lastPoint = pitchValues.back();
2028 :
2029 185 : if (hasMiddleValue) {
2030 142 : if (PitchValue value(gpTimeToMuTime(b.middleOffset1), b.middleValue);
2031 142 : b.middleOffset1 >= 0 && b.middleOffset1 < destOffset && value != lastPoint) {
2032 132 : pitchValues.push_back(std::move(value));
2033 : }
2034 :
2035 142 : if (PitchValue value(gpTimeToMuTime(b.middleOffset2), b.middleValue);
2036 141 : b.middleOffset2 >= 0 && b.middleOffset2 != b.middleOffset1
2037 27 : && b.middleOffset2 < destOffset
2038 283 : && value != lastPoint) {
2039 25 : pitchValues.push_back(std::move(value));
2040 : }
2041 :
2042 142 : if (b.middleOffset1 == -1 && b.middleOffset2 == -1 && b.middleValue != -1) {
2043 : //!@NOTE It seems when middle point is places exactly in the middle
2044 : //!of bend GP6 stores this value equal -1
2045 1 : if (destOffset > 50) {
2046 0 : pitchValues.push_back(PitchValue(gpTimeToMuTime(50), b.middleValue));
2047 : }
2048 : }
2049 : }
2050 :
2051 185 : if (b.destinationOffset <= 0) {
2052 9 : if (hasMiddleValue) {
2053 1 : PitchValue fixGpxValue = PitchValue(gpTimeToMuTime(50), b.middleValue);
2054 1 : if (b.middleValue > b.destinationValue && pitchValues.back() != fixGpxValue) {
2055 1 : pitchValues.push_back(fixGpxValue);
2056 : }
2057 : }
2058 9 : pitchValues.push_back(PitchValue(gpTimeToMuTime(100), b.destinationValue)); //! In .gpx this value might be exist
2059 : } else {
2060 176 : if (PitchValue value(gpTimeToMuTime(destOffset), b.destinationValue); value != pitchValues.back()) {
2061 176 : pitchValues.push_back(std::move(value));
2062 : }
2063 : }
2064 :
2065 185 : return pitchValues;
2066 0 : }
2067 :
2068 2030 : void GPConverter::addBend(const GPNote* gpnote, Note* note)
2069 : {
2070 2030 : if (!gpnote->bend() || gpnote->bend()->isEmpty()) {
2071 1931 : return;
2072 : }
2073 :
2074 : using namespace mu::engraving;
2075 :
2076 101 : PitchValues pitchValues = gpBendCurveToPitchValues(*gpnote->bend());
2077 :
2078 101 : if (pitchValues.size() < 2) {
2079 0 : return;
2080 : }
2081 :
2082 : /// not adding "hold" on 0 pitch
2083 101 : int maxPitch = (std::max_element(pitchValues.begin(), pitchValues.end(), [](const PitchValue& l, const PitchValue& r) {
2084 174 : return l.pitch < r.pitch;
2085 101 : }))->pitch;
2086 :
2087 101 : if (maxPitch == 0) {
2088 2 : return;
2089 : }
2090 :
2091 99 : m_guitarBendImporter->collectBend(note, pitchValues);
2092 101 : }
2093 :
2094 2030 : void GPConverter::setPitch(Note* note, const GPNote::MidiPitch& midiPitch)
2095 : {
2096 2030 : int32_t fret = midiPitch.fret;
2097 2030 : int32_t musescoreString{ -1 };
2098 2030 : if (midiPitch.string != -1) {
2099 2019 : musescoreString = static_cast<int32_t>(note->part()->instrument()->stringData()->strings()) - 1 - midiPitch.string;
2100 : }
2101 :
2102 2030 : int pitch = 0;
2103 2030 : if (midiPitch.midi != -1) {
2104 1177 : pitch = midiPitch.midi;
2105 853 : } else if (midiPitch.octave != -1 || midiPitch.tone != 0) {
2106 11 : pitch = midiPitch.octave * 12 + midiPitch.tone;
2107 842 : } else if (midiPitch.variation != -1) {
2108 0 : pitch = calculateDrumPitch(midiPitch.element, midiPitch.variation, note->part()->shortName());
2109 842 : } else if (note->part()->midiChannel() == PERC_CHANNEL) {
2110 : //!@NOTE This is a case, when part of the note is a
2111 : // single drum instrument. It seems, that GP
2112 : // sets to -1 all midi parameters for the note,
2113 : // but sets the midi pitch value to the program
2114 : // instead.
2115 0 : pitch = note->part()->instrument()->channel(0)->program();
2116 : } else {
2117 842 : if (stringDatas.empty()) {
2118 0 : pitch = note->part()->instrument()->stringData()->getPitch(musescoreString, midiPitch.fret + capoFret(note->part()),
2119 0 : nullptr) + note->part()->instrument()->transpose().chromatic;
2120 : } else {
2121 842 : if (const auto sd = stringDatas.find(note->part()->id().toUint64()); sd != stringDatas.end()) {
2122 842 : pitch = sd->second.getPitch(musescoreString, midiPitch.fret + capoFret(note->part()),
2123 842 : nullptr) + note->part()->instrument()->transpose().chromatic;
2124 : } else {
2125 0 : pitch = note->part()->instrument()->stringData()->getPitch(musescoreString, midiPitch.fret + capoFret(note->part()),
2126 0 : nullptr) + note->part()->instrument()->transpose().chromatic;
2127 : }
2128 : }
2129 : }
2130 :
2131 2030 : pitch = std::clamp(pitch, 0, 127);
2132 :
2133 2030 : if (musescoreString == -1) {
2134 11 : musescoreString = getStringNumberFor(note, pitch);
2135 :
2136 11 : fret = note->part()->instrument()->stringData()->fret(pitch, musescoreString, nullptr);
2137 : }
2138 :
2139 2030 : bool hasDrumStaff = note->part()->hasDrumStaff();
2140 2030 : if (!engravingConfiguration()->guitarProImportExperimental() && hasDrumStaff) {
2141 64 : auto it = _drumExtension.find(pitch);
2142 64 : if (it != _drumExtension.end()) {
2143 0 : pitch = it->second;
2144 : }
2145 : }
2146 :
2147 2030 : note->setFret(fret);
2148 :
2149 2030 : if (note->part()->instrument(note->tick())->useDrumset()) {
2150 64 : note->setString(0);
2151 : } else {
2152 1966 : note->setString(musescoreString);
2153 : }
2154 :
2155 2030 : note->setPitch(pitch);
2156 2030 : }
2157 :
2158 2030 : void GPConverter::setTpc(Note* note, int accidental)
2159 : {
2160 2030 : if (0 == accidental || accidental == GPNote::invalidAccidental) {
2161 1919 : note->setTpcFromPitch();
2162 : } else {
2163 111 : note->setTpcFromPitch(accidental < 0 ? Prefer::FLATS : Prefer::SHARPS);
2164 : }
2165 2030 : }
2166 :
2167 0 : int GPConverter::calculateDrumPitch(int element, int variation, const String& instrumentName)
2168 : {
2169 0 : return _drumResolver->pitch(element, variation, instrumentName);
2170 : }
2171 :
2172 2095 : void GPConverter::addDynamic(const GPBeat* gpb, ChordRest* cr)
2173 : {
2174 2095 : if (!cr->isChord()) {
2175 1805 : return;
2176 : }
2177 :
2178 1595 : if (_dynamics.count(cr->track())) {
2179 1343 : if (_dynamics.at(cr->track()) == gpb->dynamic()) {
2180 1305 : return;
2181 : }
2182 : }
2183 :
2184 290 : auto convertDynamic = [](GPBeat::DynamicType t) {
2185 290 : if (t == GPBeat::DynamicType::FFF) {
2186 4 : return u"fff";
2187 286 : } else if (t == GPBeat::DynamicType::FF) {
2188 5 : return u"ff";
2189 281 : } else if (t == GPBeat::DynamicType::FF) {
2190 0 : return u"ff";
2191 281 : } else if (t == GPBeat::DynamicType::F) {
2192 66 : return u"f";
2193 215 : } else if (t == GPBeat::DynamicType::MF) {
2194 193 : return u"mf";
2195 22 : } else if (t == GPBeat::DynamicType::MP) {
2196 14 : return u"mp";
2197 8 : } else if (t == GPBeat::DynamicType::P) {
2198 2 : return u"p";
2199 6 : } else if (t == GPBeat::DynamicType::PP) {
2200 4 : return u"pp";
2201 : }
2202 2 : return u"ppp";
2203 : };
2204 :
2205 290 : Dynamic* dynamic = Factory::createDynamic(cr->segment());
2206 290 : dynamic->setTrack(cr->track());
2207 290 : dynamic->setDynamicType(convertDynamic(gpb->dynamic()));
2208 290 : cr->segment()->add(dynamic);
2209 290 : _dynamics[cr->track()] = gpb->dynamic();
2210 : }
2211 :
2212 2079 : void GPConverter::addTie(const GPNote* gpnote, Note* note, TieMap& ties)
2213 : {
2214 2079 : if (gpnote->tieType() == GPNote::TieType::None) {
2215 1913 : return;
2216 : }
2217 :
2218 102 : auto startTie = [](Note* startNote, TieMap& ties, track_idx_t curTrack) {
2219 102 : Tie* tie = Factory::createTie(startNote);
2220 102 : startNote->add(tie);
2221 102 : ties[curTrack].push_back(tie);
2222 102 : };
2223 :
2224 102 : auto endTie = [this](Note* endNote, TieMap& ties, track_idx_t curTrack) {
2225 102 : auto& tiesOnTrack = ties[curTrack];
2226 150 : for (auto it = tiesOnTrack.rbegin(); it != tiesOnTrack.rend(); it++) {
2227 150 : Tie* tie = *it;
2228 150 : Note* startNote = tie->startNote();
2229 150 : int startPitch = startNote->pitch();
2230 150 : if (m_originalPitches.find(startNote) != m_originalPitches.end()) {
2231 4 : startPitch = m_originalPitches[startNote];
2232 4 : m_originalPitches.erase(startNote);
2233 : }
2234 :
2235 150 : if (startPitch == endNote->pitch() && startNote->string() == endNote->string()) {
2236 102 : tie->setEndNote(endNote);
2237 102 : endNote->setTieBack(tie);
2238 102 : muse::remove(tiesOnTrack, tie);
2239 :
2240 : /// adding tremolos to tied note
2241 102 : Chord* startChord = toChord(startNote->parent());
2242 102 : Chord* endChord = toChord(endNote->parent());
2243 102 : if (m_tremolosInChords.find(startChord) != m_tremolosInChords.end()) {
2244 0 : TremoloType type = m_tremolosInChords.at(startChord);
2245 0 : DO_ASSERT(!isTremoloTwoChord(type));
2246 0 : TremoloSingleChord* t = Factory::createTremoloSingleChord(endChord);
2247 0 : t->setTremoloType(type);
2248 0 : endChord->add(t);
2249 0 : muse::remove(m_tremolosInChords, startChord);
2250 0 : m_tremolosInChords[endChord] = type;
2251 : }
2252 :
2253 102 : break;
2254 : }
2255 : }
2256 102 : };
2257 :
2258 166 : if (gpnote->tieType() == GPNote::TieType::Start) {
2259 64 : startTie(note, ties, note->track());
2260 102 : } else if (gpnote->tieType() == GPNote::TieType::Mediate) {
2261 38 : endTie(note, ties, note->track());
2262 38 : startTie(note, ties, note->track());
2263 64 : } else if (gpnote->tieType() == GPNote::TieType::End) {
2264 64 : endTie(note, ties, note->track());
2265 : }
2266 : }
2267 :
2268 2131 : void GPConverter::addLegato(const GPBeat* beat, ChordRest* cr)
2269 : {
2270 2131 : if (beat->legatoType() == GPBeat::LegatoType::None) {
2271 2063 : return;
2272 : }
2273 :
2274 : using slurMap = std::unordered_map<track_idx_t, Slur*>;
2275 :
2276 22 : auto startSlur = [](ChordRest* cr, Score* sc, slurMap& slurs, track_idx_t curTrack, Fraction curTick) {
2277 44 : if (auto it = slurs.find(curTrack); it != std::end(slurs)) {
2278 8 : slurs.erase(it);
2279 : }
2280 :
2281 22 : Slur* slur = Factory::createSlur(sc->dummy());
2282 22 : slur->setStartElement(cr);
2283 22 : slur->setTrack(curTrack);
2284 22 : slur->setTick(curTick);
2285 22 : slurs[curTrack] = slur;
2286 22 : sc->addSpanner(slur);
2287 22 : };
2288 :
2289 30 : auto mediateSlur = [](ChordRest* cr, slurMap& slurs, track_idx_t curTrack, Fraction curTick) {
2290 30 : Slur* slur = slurs[curTrack];
2291 30 : if (!slur) {
2292 0 : return;
2293 : }
2294 30 : slur->setTrack(curTrack);
2295 30 : slur->setTrack2(curTrack);
2296 30 : slur->setTick2(curTick);
2297 30 : slur->setEndElement(cr);
2298 : };
2299 :
2300 16 : auto endSlur = [](ChordRest* cr, slurMap& slurs, track_idx_t curTrack, Fraction curTick) {
2301 16 : Slur* slur = slurs[curTrack];
2302 16 : if (!slur) {
2303 0 : return;
2304 : }
2305 16 : slur->setTrack2(curTrack);
2306 16 : slur->setTick2(curTick);
2307 16 : slur->setEndElement(cr);
2308 : };
2309 :
2310 68 : if (beat->legatoType() == GPBeat::LegatoType::Start) {
2311 22 : startSlur(cr, _score, _slurs, cr->track(), cr->tick());
2312 46 : } else if (beat->legatoType() == GPBeat::LegatoType::Mediate) {
2313 30 : mediateSlur(cr, _slurs, cr->track(), cr->tick());
2314 16 : } else if (beat->legatoType() == GPBeat::LegatoType::End) {
2315 16 : endSlur(cr, _slurs, cr->track(), cr->tick());
2316 : }
2317 : }
2318 :
2319 2095 : void GPConverter::addOttava(const GPBeat* gpb, ChordRest* cr)
2320 : {
2321 2095 : GPBeat::OttavaType gpType = gpb->ottavaType();
2322 2095 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::OTTAVA, ottavaToImportType(
2323 : gpType), gpType != GPBeat::OttavaType::None);
2324 :
2325 2095 : if (!cr->isChord()) {
2326 2035 : return;
2327 : }
2328 :
2329 1595 : Chord* chord = toChord(cr);
2330 1595 : const auto& [foundOttava, muOttavaType] = ottavaType(gpType);
2331 1595 : if (!foundOttava) {
2332 1535 : return;
2333 : }
2334 :
2335 120 : for (mu::engraving::Note* note : chord->notes()) {
2336 60 : int pitch = note->pitch();
2337 60 : setPitchByOttavaType(note, muOttavaType);
2338 :
2339 : // pitch changed because of octave
2340 60 : if (pitch != note->pitch()) {
2341 60 : m_originalPitches[note] = pitch;
2342 : }
2343 : }
2344 : }
2345 :
2346 2030 : void GPConverter::addLetRing(const GPNote* gpnote, Note* note)
2347 : {
2348 : UNUSED(note);
2349 2030 : if (gpnote->letRing() && m_currentGPBeat) {
2350 11 : m_currentGPBeat->setLetRing(true);
2351 : }
2352 2030 : }
2353 :
2354 2030 : void GPConverter::addPalmMute(const GPNote* gpnote, Note* /*note*/)
2355 : {
2356 2030 : if (gpnote->palmMute() && m_currentGPBeat) {
2357 4 : m_currentGPBeat->setPalmMute(true);
2358 : }
2359 2030 : }
2360 :
2361 2095 : void GPConverter::addLetRing(const GPBeat* gpbeat, ChordRest* cr)
2362 : {
2363 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::LET_RING, ContiniousElementsBuilder::ImportType::LET_RING,
2364 2095 : gpbeat->letRing());
2365 2095 : }
2366 :
2367 2095 : void GPConverter::addPalmMute(const GPBeat* gpbeat, ChordRest* cr)
2368 : {
2369 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::PALM_MUTE, ContiniousElementsBuilder::ImportType::PALM_MUTE,
2370 2095 : gpbeat->palmMute());
2371 2095 : }
2372 :
2373 2095 : void GPConverter::addDive(const GPBeat* beat, ChordRest* cr)
2374 : {
2375 2095 : if (!beat->hasWhammy() || !cr->isChord()) {
2376 2010 : return;
2377 : }
2378 :
2379 : //! GPX reuses the same GPBeat pointer for multiple beat positions — treat as hold.
2380 85 : const track_idx_t track = cr->track();
2381 85 : PitchValues pitchValues;
2382 85 : if (m_lastWhammyBeat[track] == beat && beat->whammy().destinationValue != 0) {
2383 1 : const float holdValue = beat->whammy().destinationValue;
2384 1 : pitchValues.push_back(PitchValue(0, holdValue));
2385 1 : pitchValues.push_back(PitchValue(PitchValue::MAX_TIME, holdValue));
2386 : } else {
2387 84 : pitchValues = gpBendCurveToPitchValues(beat->whammy());
2388 : }
2389 85 : m_lastWhammyBeat[track] = beat;
2390 :
2391 85 : if (pitchValues.size() < 2) {
2392 0 : return;
2393 : }
2394 :
2395 85 : m_guitarBendImporter->collectDive(toChord(cr), pitchValues);
2396 85 : }
2397 :
2398 2095 : void GPConverter::addPickScrape(const GPBeat* beat, ChordRest* cr)
2399 : {
2400 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::PICK_SCRAPE, ContiniousElementsBuilder::ImportType::PICK_SCRAPE,
2401 2095 : beat->pickScrape());
2402 2095 : }
2403 :
2404 2095 : void GPConverter::addRasgueado(const GPBeat* beat, ChordRest* cr)
2405 : {
2406 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::RASGUEADO, ContiniousElementsBuilder::ImportType::RASGUEADO,
2407 2095 : beat->rasgueado() != GPBeat::Rasgueado::None);
2408 2095 : }
2409 :
2410 2095 : void GPConverter::addHarmonicMark(const GPBeat* gpbeat, ChordRest* cr)
2411 : {
2412 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HARMONIC_MARK,
2413 : ContiniousElementsBuilder::ImportType::HARMONIC_ARTIFICIAL,
2414 2095 : gpbeat->harmonicMarkArtificial(),
2415 : ContiniousElementsBuilder::HarmonicMarkSubType::ARTIFICIAL);
2416 :
2417 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HARMONIC_MARK,
2418 : ContiniousElementsBuilder::ImportType::HARMONIC_PINCH,
2419 2095 : gpbeat->harmonicMarkPinch(), ContiniousElementsBuilder::HarmonicMarkSubType::PINCH);
2420 :
2421 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HARMONIC_MARK, ContiniousElementsBuilder::ImportType::HARMONIC_TAP,
2422 2095 : gpbeat->harmonicMarkTap(), ContiniousElementsBuilder::HarmonicMarkSubType::TAP);
2423 :
2424 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HARMONIC_MARK,
2425 : ContiniousElementsBuilder::ImportType::HARMONIC_SEMI,
2426 2095 : gpbeat->harmonicMarkSemi(), ContiniousElementsBuilder::HarmonicMarkSubType::SEMI);
2427 :
2428 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HARMONIC_MARK,
2429 : ContiniousElementsBuilder::ImportType::HARMONIC_FEEDBACK,
2430 2095 : gpbeat->harmonicMarkFeedback(),
2431 : ContiniousElementsBuilder::HarmonicMarkSubType::FEEDBACK);
2432 2095 : }
2433 :
2434 2095 : void GPConverter::addFretDiagram(const GPBeat* gpnote, ChordRest* cr, const Context& ctx, bool asHarmony)
2435 : {
2436 2095 : int GPTrackIdx = static_cast<int>(muse::indexOf(_score->parts(), cr->part()));
2437 2095 : int diaId = gpnote->diagramIdx(GPTrackIdx, ctx.masterBarIndex);
2438 :
2439 2095 : if (_lastDiagramIdx == diaId) {
2440 2095 : return;
2441 : }
2442 :
2443 23 : _lastDiagramIdx = diaId;
2444 :
2445 23 : if (diaId == -1) {
2446 8 : return;
2447 : }
2448 :
2449 15 : auto trackIt = _gpDom->tracks().find(GPTrackIdx);
2450 15 : if (trackIt == _gpDom->tracks().cend()) {
2451 0 : return;
2452 : }
2453 :
2454 15 : if (trackIt->second->diagram().count(diaId) == 0) {
2455 1 : return;
2456 : }
2457 :
2458 14 : GPTrack::Diagram diagram = trackIt->second->diagram().at(diaId);
2459 :
2460 : /// currently importing fret diagrams as chord names
2461 14 : if (asHarmony) {
2462 14 : Harmony* h = Factory::createHarmony(cr->segment());
2463 14 : h->setTrack(cr->track());
2464 14 : h->setHarmonyType(HarmonyType::STANDARD);
2465 14 : h->setHarmony(diagram.name); // F#dim7
2466 14 : h->setPlainText(h->harmonyName());
2467 14 : cr->segment()->add(h);
2468 14 : return;
2469 : }
2470 :
2471 0 : FretDiagram* fretDiagram = mu::engraving::Factory::createFretDiagram(cr->segment());
2472 0 : fretDiagram->setTrack(cr->track());
2473 0 : fretDiagram->setStrings(diagram.stringCount);
2474 0 : fretDiagram->setFretOffset(diagram.baseFret);
2475 :
2476 0 : for (int st = 0; st < diagram.stringCount; st++) {
2477 0 : fretDiagram->setMarker(st, FretMarkerType::CROSS);
2478 : }
2479 :
2480 0 : for (const auto& fret: diagram.frets) {
2481 0 : if (fret.second == 0) {
2482 0 : fretDiagram->setMarker(fret.first, FretMarkerType::CIRCLE);
2483 : } else {
2484 0 : fretDiagram->setDot(fret.first, fret.second);
2485 : }
2486 : }
2487 :
2488 0 : cr->segment()->add(fretDiagram);
2489 14 : }
2490 :
2491 2095 : void GPConverter::addTapping(const GPBeat* beat, ChordRest* cr)
2492 : {
2493 2095 : if (beat->tappingHand() == GPBeat::TappingHand::None || !cr->isChord()) {
2494 2093 : return;
2495 : }
2496 :
2497 2 : Chord* chord = toChord(cr);
2498 2 : Tapping* tapping = Factory::createTapping(chord);
2499 2 : tapping->setTrack(chord->track());
2500 2 : tapping->setHand(beat->tappingHand() == GPBeat::TappingHand::Left ? engraving::TappingHand::LEFT : engraving::TappingHand::RIGHT);
2501 2 : chord->add(tapping);
2502 : }
2503 :
2504 2095 : void GPConverter::addSlapped(const GPBeat* beat, ChordRest* cr)
2505 : {
2506 2095 : if (!beat->slapped() || !cr->isChord()) {
2507 2087 : return;
2508 : }
2509 :
2510 8 : Articulation* art = mu::engraving::Factory::createArticulation(cr);
2511 8 : art->setTextType(ArticulationTextType::SLAP);
2512 8 : cr->add(art);
2513 : }
2514 :
2515 2095 : void GPConverter::addPopped(const GPBeat* beat, ChordRest* cr)
2516 : {
2517 2095 : if (!beat->popped() || !cr->isChord()) {
2518 2089 : return;
2519 : }
2520 :
2521 6 : Articulation* art = mu::engraving::Factory::createArticulation(cr);
2522 6 : art->setTextType(ArticulationTextType::POP);
2523 6 : cr->add(art);
2524 : }
2525 :
2526 2095 : void GPConverter::addBrush(const GPBeat* beat, ChordRest* cr)
2527 : {
2528 2095 : if (beat->brush() == GPBeat::Brush::None) {
2529 2091 : return;
2530 : }
2531 :
2532 4 : auto brushType = [](auto&& gpArp) {
2533 4 : if (gpArp == GPBeat::Brush::Up) {
2534 2 : return ArpeggioType::DOWN_STRAIGHT;
2535 : } else {
2536 2 : return ArpeggioType::UP_STRAIGHT;
2537 : }
2538 : };
2539 :
2540 4 : Arpeggio* arp = mu::engraving::Factory::createArpeggio(_score->dummy());
2541 4 : arp->setArpeggioType(brushType(beat->brush()));
2542 4 : arp->setStretch(beat->arpeggioStretch());
2543 :
2544 4 : cr->add(arp);
2545 : }
2546 :
2547 2095 : void GPConverter::addArpeggio(const GPBeat* beat, ChordRest* cr)
2548 : {
2549 2095 : if (beat->arpeggio() == GPBeat::Arpeggio::None) {
2550 2091 : return;
2551 : }
2552 :
2553 4 : auto arpeggioType = [](auto&& gpArp) {
2554 4 : if (gpArp == GPBeat::Arpeggio::Up) {
2555 2 : return ArpeggioType::DOWN;
2556 : } else {
2557 2 : return ArpeggioType::UP;
2558 : }
2559 : };
2560 :
2561 4 : Arpeggio* arp = mu::engraving::Factory::createArpeggio(_score->dummy());
2562 4 : arp->setArpeggioType(arpeggioType(beat->arpeggio()));
2563 4 : arp->setStretch(beat->arpeggioStretch());
2564 4 : cr->add(arp);
2565 : }
2566 :
2567 2095 : void GPConverter::addTimer(const GPBeat* beat, ChordRest* cr)
2568 : {
2569 2095 : if (beat->time() == -1) {
2570 2089 : return;
2571 : }
2572 :
2573 6 : int time = beat->time() / 1000;
2574 6 : int minutes = time / 60;
2575 6 : int seconds = time % 60;
2576 6 : StaffText* st = Factory::createStaffText(cr->segment());
2577 18 : st->setPlainText(String::number(minutes)
2578 12 : + u':'
2579 24 : + (seconds < 10 ? u'0' + String::number(seconds) : String::number(seconds)));
2580 6 : st->setTrack(cr->track());
2581 6 : cr->segment()->add(st);
2582 : }
2583 :
2584 2095 : void GPConverter::addFreeText(const GPBeat* beat, ChordRest* cr)
2585 : {
2586 2095 : if (beat->freeText().isEmpty()) {
2587 2088 : return;
2588 : }
2589 :
2590 7 : auto text = Factory::createStaffText(cr->segment());
2591 7 : text->setTrack(cr->track());
2592 7 : text->setPlainText(beat->freeText());
2593 7 : cr->segment()->add(text);
2594 : }
2595 :
2596 12 : void GPConverter::setupTupletStyle(Tuplet* tuplet)
2597 : {
2598 12 : bool real = false;
2599 :
2600 12 : switch (tuplet->ratio().numerator()) {
2601 0 : case 2: {
2602 0 : real = (tuplet->ratio().denominator() == 3);
2603 0 : break;
2604 : }
2605 10 : case 3:
2606 : case 4: {
2607 10 : real = ((tuplet->ratio().denominator() == 2) || (tuplet->ratio().denominator() == 6));
2608 10 : break;
2609 : }
2610 1 : case 5:
2611 : case 6:
2612 : case 7: {
2613 1 : real = (tuplet->ratio().denominator() == 4);
2614 1 : break;
2615 : }
2616 0 : case 9:
2617 : case 10:
2618 : case 11:
2619 : case 12:
2620 : case 13: {
2621 0 : real = (tuplet->ratio().denominator() == 8);
2622 0 : break;
2623 : }
2624 : }
2625 12 : if (!real) {
2626 1 : tuplet->setNumberType(TupletNumberType::SHOW_RELATION);
2627 1 : tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::UNSTYLED);
2628 : }
2629 12 : }
2630 :
2631 47 : bool GPConverter::tupletParamsChanged(const GPBeat* beat, const ChordRest* cr)
2632 : {
2633 47 : return beat->tuplet().num == -1
2634 41 : || Fraction(beat->tuplet().num, beat->tuplet().denom) != m_nextTupletInfo.ratio
2635 40 : || cr->track() != m_nextTupletInfo.track
2636 40 : || cr->measure() != m_nextTupletInfo.measure
2637 116 : || (m_nextTupletInfo.elements.size() > 1
2638 75 : && m_nextTupletInfo.duration == Fraction(m_nextTupletInfo.ratio.denominator(), m_nextTupletInfo.lowestBase));
2639 : }
2640 :
2641 2095 : void GPConverter::addTuplet(const GPBeat* beat, ChordRest* cr)
2642 : {
2643 2095 : Fraction currentTupletType = Fraction(beat->tuplet().num, beat->tuplet().denom);
2644 :
2645 2095 : if (m_nextTupletInfo.tuplet && tupletParamsChanged(beat, cr)) {
2646 9 : fillTuplet();
2647 : }
2648 :
2649 2095 : if (beat->tuplet().num == -1) {
2650 2045 : return;
2651 : }
2652 :
2653 50 : m_nextTupletInfo.lowestBase = std::min(m_nextTupletInfo.lowestBase, cr->ticks().denominator());
2654 :
2655 50 : if (!m_nextTupletInfo.tuplet) {
2656 12 : m_nextTupletInfo.tuplet = Factory::createTuplet(_score->dummy());
2657 12 : m_nextTupletInfo.tuplet->setRatio(currentTupletType);
2658 12 : m_nextTupletInfo.ratio = currentTupletType;
2659 12 : m_nextTupletInfo.measure = cr->measure();
2660 12 : m_nextTupletInfo.track = cr->track();
2661 : }
2662 :
2663 50 : m_nextTupletInfo.elements.push_back(cr);
2664 50 : cr->setTuplet(m_nextTupletInfo.tuplet);
2665 50 : m_nextTupletInfo.duration += cr->actualTicks();
2666 : }
2667 :
2668 2095 : void GPConverter::addVibratoWTremBar(const GPBeat* beat, ChordRest* cr)
2669 : {
2670 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::VIBRATO,
2671 : ContiniousElementsBuilder::ImportType::VIBRATO_W_TREM_BAR_SLIGHT,
2672 2095 : beat->vibratoWTremBar() == GPBeat::VibratoWTremBar::Slight,
2673 : ContiniousElementsBuilder::VibratoSubType::W_TREM_BAR_SLIGHT);
2674 :
2675 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::VIBRATO,
2676 : ContiniousElementsBuilder::ImportType::VIBRATO_W_TREM_BAR_WIDE,
2677 2095 : beat->vibratoWTremBar() == GPBeat::VibratoWTremBar::Wide,
2678 : ContiniousElementsBuilder::VibratoSubType::W_TREM_BAR_WIDE);
2679 2095 : }
2680 :
2681 2095 : void GPConverter::addVibratoLeftHand(const GPBeat* beat, ChordRest* cr)
2682 : {
2683 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::VIBRATO,
2684 : ContiniousElementsBuilder::ImportType::VIBRATO_LEFT_HAND_SLIGHT,
2685 2095 : beat->vibratoLeftHand() == GPBeat::VibratoLeftHand::Slight,
2686 : ContiniousElementsBuilder::VibratoSubType::LEFT_HAND_SLIGHT);
2687 :
2688 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::VIBRATO,
2689 : ContiniousElementsBuilder::ImportType::VIBRATO_LEFT_HAND_WIDE,
2690 2095 : beat->vibratoLeftHand() == GPBeat::VibratoLeftHand::Wide,
2691 : ContiniousElementsBuilder::VibratoSubType::LEFT_HAND_WIDE);
2692 2095 : }
2693 :
2694 2095 : void GPConverter::addFadding(const GPBeat* beat, ChordRest* cr)
2695 : {
2696 2095 : if (beat->fadding() == GPBeat::Fadding::None) {
2697 2091 : return;
2698 : }
2699 7 : if (!cr->isChord()) {
2700 3 : return;
2701 : }
2702 :
2703 4 : auto scoreFadding = [](const auto& fad) {
2704 4 : if (fad == GPBeat::Fadding::FadeIn) {
2705 2 : return SymId::guitarFadeIn;
2706 2 : } else if (fad == GPBeat::Fadding::FadeOut) {
2707 0 : return SymId::guitarFadeOut;
2708 : } else {
2709 2 : return SymId::guitarVolumeSwell;
2710 : }
2711 : };
2712 :
2713 4 : Articulation* art = mu::engraving::Factory::createArticulation(_score->dummy());
2714 4 : art->setSymId(scoreFadding(beat->fadding()));
2715 4 : if (!EditChord::toggleArticulation(_score, toChord(cr)->upNote(), art)) {
2716 0 : delete art;
2717 : }
2718 : }
2719 :
2720 2095 : void GPConverter::addHairPin(const GPBeat* beat, ChordRest* cr)
2721 : {
2722 4190 : m_continiousElementsBuilder->buildContiniousElement(cr, ElementType::HAIRPIN, hairpinToImportType(beat->hairpin()),
2723 2095 : beat->hairpin() != GPBeat::Hairpin::None);
2724 2095 : }
2725 :
2726 2095 : void GPConverter::addPickStroke(const GPBeat* beat, ChordRest* cr)
2727 : {
2728 2095 : if (beat->pickStroke() == GPBeat::PickStroke::None) {
2729 2091 : return;
2730 : }
2731 4 : if (!cr->isChord()) {
2732 0 : return;
2733 : }
2734 :
2735 4 : auto scorePickStroke = [](const auto& p) {
2736 4 : if (p == GPBeat::PickStroke::Up) {
2737 2 : return SymId::stringsUpBow;
2738 : } else {
2739 2 : return SymId::stringsDownBow;
2740 : }
2741 : };
2742 :
2743 4 : Articulation* art = mu::engraving::Factory::createArticulation(_score->dummy());
2744 4 : art->setSymId(scorePickStroke(beat->pickStroke()));
2745 4 : if (!EditChord::toggleArticulation(_score, toChord(cr)->upNote(), art)) {
2746 0 : delete art;
2747 : }
2748 : }
2749 :
2750 2095 : void GPConverter::addTremolo(const GPBeat* beat, ChordRest* cr)
2751 : {
2752 2095 : if (!cr->isChord() || beat->tremolo().numerator == -1) {
2753 2089 : return;
2754 : }
2755 :
2756 6 : auto scoreTremolo = [](const GPBeat::Tremolo tr) {
2757 6 : if (tr.denominator == 2) {
2758 2 : return TremoloType::R8;
2759 : }
2760 4 : if (tr.denominator == 4) {
2761 2 : return TremoloType::R16;
2762 : } else {
2763 2 : return TremoloType::R32;
2764 : }
2765 : };
2766 :
2767 6 : TremoloSingleChord* t = Factory::createTremoloSingleChord(_score->dummy());
2768 6 : t->setTremoloType(scoreTremolo(beat->tremolo()));
2769 6 : Chord* ch = toChord(cr);
2770 6 : ch->add(t);
2771 6 : m_tremolosInChords[ch] = t->tremoloType();
2772 : }
2773 :
2774 2095 : void GPConverter::addWah(const GPBeat* beat, ChordRest* cr)
2775 : {
2776 2095 : if (beat->wah() == GPBeat::Wah::None) {
2777 2091 : return;
2778 : }
2779 4 : if (!cr->isChord()) {
2780 0 : return;
2781 : }
2782 :
2783 4 : auto scoreWah = [] (const auto& wah) {
2784 4 : if (wah == GPBeat::Wah::Open) {
2785 2 : return SymId::brassMuteOpen;
2786 : }
2787 2 : return SymId::brassMuteClosed;
2788 : };
2789 :
2790 4 : Articulation* art = Factory::createArticulation(_score->dummy());
2791 4 : art->setSymId(scoreWah(beat->wah()));
2792 4 : if (!EditChord::toggleArticulation(_score, toChord(cr)->upNote(), art)) {
2793 0 : delete art;
2794 : }
2795 : }
2796 :
2797 2095 : void GPConverter::addGolpe(const GPBeat* beat, ChordRest* cr)
2798 : {
2799 2095 : if (beat->golpe() == GPBeat::Golpe::None) {
2800 2095 : return;
2801 : }
2802 0 : if (!cr->isChord()) {
2803 0 : return;
2804 : }
2805 :
2806 0 : Articulation* art = Factory::createArticulation(_score->dummy());
2807 0 : art->setSymId(SymId::guitarGolpe);
2808 :
2809 0 : if (beat->golpe() == GPBeat::Golpe::Thumb) {
2810 0 : art->setAnchor(ArticulationAnchor::BOTTOM);
2811 : }
2812 :
2813 0 : if (!EditChord::toggleArticulation(_score, toChord(cr)->upNote(), art)) {
2814 0 : delete art;
2815 : }
2816 : }
2817 :
2818 2095 : void GPConverter::addBarre(const GPBeat* beat, ChordRest* cr)
2819 : {
2820 2095 : if (beat->barre().fret == -1) {
2821 2091 : return;
2822 : }
2823 4 : if (!cr->isChord()) {
2824 0 : return;
2825 : }
2826 :
2827 4 : auto barreType = [](const GPBeat::Barre& barre) {
2828 4 : if (barre.string == 1) {
2829 4 : return std::string("1/2B ");
2830 : } else {
2831 4 : return std::string("B ");
2832 : }
2833 : };
2834 :
2835 4 : std::string barreFret;
2836 4 : int fret = beat->barre().fret;
2837 :
2838 6 : for (int i = 0; i < (fret / 10); i++) {
2839 2 : barreFret += "X";
2840 : }
2841 4 : int targetMod10 = fret % 10;
2842 4 : if (targetMod10 == 9) {
2843 0 : barreFret += "IX";
2844 4 : } else if (targetMod10 == 4) {
2845 0 : barreFret += "IV";
2846 : } else {
2847 4 : if (targetMod10 >= 5) {
2848 4 : barreFret += "V";
2849 4 : targetMod10-=5;
2850 : }
2851 10 : for (int j = 0; j < targetMod10; j++) {
2852 6 : barreFret += "I";
2853 : }
2854 : }
2855 :
2856 4 : addTextToNote(String::fromStdString(barreType(beat->barre()) + barreFret), toChord(cr)->upNote());
2857 4 : }
2858 :
2859 2095 : void GPConverter::addLyrics(const GPBeat* beat, ChordRest* cr, const Context& ctx)
2860 : {
2861 2095 : ID GPTrackIdx = cr->part()->id();
2862 :
2863 2095 : const std::string& lyrStr = beat->lyrics(GPTrackIdx.toUint64(), ctx.masterBarIndex);
2864 2095 : if (lyrStr.empty()) {
2865 2095 : return;
2866 : }
2867 :
2868 0 : Lyrics* lyr = Factory::createLyrics(_score->dummy());
2869 :
2870 0 : if (lyrStr.back() == '-') {
2871 0 : lyr->setSyllabic(LyricsSyllabic::MIDDLE);
2872 0 : lyr->setPlainText(String::fromUtf8(lyrStr.substr(0, lyrStr.size() - 1).c_str()));
2873 : } else {
2874 0 : lyr->setPlainText(String::fromUtf8(lyrStr.c_str()));
2875 : }
2876 0 : cr->add(lyr);
2877 : }
2878 :
2879 724 : void GPConverter::clearDefectedGraceChord(ChordRestContainer& graceGhords)
2880 : {
2881 724 : for (auto [pCr, gpBeat] : graceGhords) {
2882 : UNUSED(gpBeat);
2883 :
2884 0 : if (!pCr) {
2885 0 : continue;
2886 : }
2887 :
2888 0 : if (pCr->isChord()) {
2889 0 : Chord* pChord = toChord(pCr);
2890 0 : for (Note* pNote : pChord->notes()) {
2891 0 : auto it = _slideHammerOnMap.begin(), e = _slideHammerOnMap.end();
2892 0 : for (; it != e; ++it) {
2893 0 : if (it->first == pNote) {
2894 0 : _slideHammerOnMap.erase(it);
2895 0 : break;
2896 : }
2897 : }
2898 : }
2899 :
2900 0 : for (Chord* pGraceChord : pChord->graceNotes()) {
2901 0 : for (Note* pNote : pGraceChord->notes()) {
2902 0 : auto it = _slideHammerOnMap.begin(), e = _slideHammerOnMap.end();
2903 0 : for (; it != e; ++it) {
2904 0 : if (it->first == pNote) {
2905 0 : _slideHammerOnMap.erase(it);
2906 0 : break;
2907 : }
2908 : }
2909 : }
2910 : }
2911 : }
2912 0 : delete pCr;
2913 : }
2914 724 : graceGhords.clear();
2915 724 : }
2916 :
2917 4 : void GPConverter::addTextToNote(muse::String string, Note* note)
2918 : {
2919 4 : Segment* segment = note->chord()->segment();
2920 4 : StaffText* text = Factory::createStaffText(segment);
2921 :
2922 4 : if (!string.isEmpty()) {
2923 4 : bool useHarmony = string[string.size() - 1] == '\\';
2924 4 : if (useHarmony) {
2925 0 : string.chop(1);
2926 : }
2927 : }
2928 4 : text->setPlainText(string);
2929 4 : text->setTrack(note->chord()->track());
2930 4 : segment->add(text);
2931 4 : }
2932 :
2933 221 : void GPConverter::clearDefectedSpanner()
2934 : {
2935 262 : for (const auto& [track, elems] : _ties) {
2936 41 : for (Tie* tie : elems) {
2937 0 : if (tie->startNote()) {
2938 0 : tie->startNote()->setTieFor(nullptr);
2939 : }
2940 :
2941 0 : if (tie->endNote()) {
2942 0 : tie->endNote()->setTieBack(nullptr);
2943 : }
2944 :
2945 0 : delete tie;
2946 : }
2947 : }
2948 221 : }
2949 :
2950 11 : int GPConverter::getStringNumberFor(mu::engraving::Note* pNote, int pitch) const
2951 : {
2952 11 : const auto& stringTable = pNote->part()->instrument()->stringData()->stringList();
2953 :
2954 11 : const size_t stringTableSize = stringTable.size();
2955 :
2956 11 : if (!stringTableSize) {
2957 0 : return -1;
2958 : }
2959 :
2960 11 : const int32_t lastIdx = static_cast<int32_t>(stringTableSize) - 1;
2961 :
2962 22 : for (int32_t idx = lastIdx; idx >= 0; --idx) {
2963 22 : auto string = stringTable[idx];
2964 22 : const int32_t currenStringNumber = lastIdx - idx;
2965 :
2966 22 : if (pitch >= string.pitch) {
2967 11 : bool emptyString = true;
2968 :
2969 22 : for (const auto& noteTmp : pNote->chord()->notes()) {
2970 11 : if (noteTmp->string() == currenStringNumber) {
2971 0 : emptyString = false;
2972 0 : break;
2973 : }
2974 : }
2975 :
2976 11 : if (emptyString) {
2977 11 : return currenStringNumber;
2978 : }
2979 : }
2980 : }
2981 :
2982 0 : return -1;
2983 : }
2984 :
2985 1595 : void GPConverter::setBeamMode(const GPBeat* beat, ChordRest* cr, Measure* measure, Fraction tick)
2986 : {
2987 1595 : BeamMode beamMode = BeamMode::AUTO;
2988 :
2989 1595 : if (beat->beamMode() == GPBeat::BeamMode::BROKEN) {
2990 11 : beamMode = BeamMode::NONE;
2991 1584 : } else if (beat->beamMode() == GPBeat::BeamMode::JOINED) {
2992 11 : beamMode = BeamMode::MID;
2993 1573 : } else if (beat->beamMode() == GPBeat::BeamMode::BROKEN2 || beat->beamMode() == GPBeat::BeamMode::BROKEN2_JOINED) {
2994 7 : int measureDenom = measure->ticks().denominator();
2995 7 : double fract = (double)tick.numerator() / tick.denominator() * measureDenom;
2996 :
2997 7 : if ((int)fract == fract && beat->beamMode() != GPBeat::BeamMode::BROKEN2_JOINED) {
2998 : /// keep auto direction for some beams, so BEGIN16/BEGIN32 modes work properly
2999 : /// (forcing divide of beam groups, TODO-gp: make possible to show broken2 type from guitar pro
3000 1 : beamMode = BeamMode::AUTO;
3001 6 : } else if (cr->ticks() > Fraction(1, 32)) {
3002 6 : beamMode = BeamMode::BEGIN16;
3003 : } else {
3004 0 : beamMode = BeamMode::BEGIN32;
3005 : }
3006 : }
3007 :
3008 1595 : cr->setBeamMode(m_previousBeamMode);
3009 1595 : m_previousBeamMode = beamMode;
3010 1595 : }
3011 :
3012 221 : void GPConverter::addTuning()
3013 : {
3014 221 : const Measure* m = _score->firstMeasure();
3015 :
3016 : // NOTE: GP doesn't support multiple tunings on one part
3017 : // We're safe to just take the very first chord rest segment
3018 : // and check if it has any non-standard tuning
3019 221 : const Fraction& f{ 0, 1 };
3020 :
3021 460 : for (auto p : _score->parts()) {
3022 481 : for (auto s : p->staves()) {
3023 242 : if (!s->isPrimaryStaff()) {
3024 229 : continue;
3025 : }
3026 :
3027 242 : Segment* seg = m->findSegment(SegmentType::ChordRest, f);
3028 :
3029 242 : IF_ASSERT_FAILED(seg) {
3030 0 : LOGE() << "First measure MUST has a chord rest segment after import";
3031 0 : return;
3032 : }
3033 :
3034 242 : if (stringDatas.find(p->id().toUint64()) == stringDatas.end()) {
3035 3 : continue;
3036 : }
3037 :
3038 239 : const StringData sd = stringDatas.at(p->id().toUint64());
3039 239 : std::vector<int> tuning(sd.strings());
3040 1665 : for (size_t i = 0; i < tuning.size(); ++i) {
3041 1426 : tuning[i] = sd.stringList().at(i).pitch + p->instrument()->transpose().chromatic;
3042 : }
3043 :
3044 239 : if (utils::isStandardTuning(p->instrument()->channel(0)->program(), tuning)) {
3045 226 : continue;
3046 : }
3047 :
3048 13 : StringTunings* tun = Factory::createStringTunings(seg);
3049 13 : tun->setStringData(sd);
3050 13 : tun->setTrack(staff2track(s->idx()));
3051 13 : seg->add(tun);
3052 465 : }
3053 : }
3054 : }
3055 :
3056 891 : int GPConverter::capoFret(const Part* part) const
3057 : {
3058 891 : const auto it = m_capoParams.find(part->id().toUint64());
3059 891 : return it != m_capoParams.end() ? it->second : 0;
3060 : }
3061 :
3062 221 : void GPConverter::addCapos()
3063 : {
3064 221 : const Measure* firstMeasure = _score->firstMeasure();
3065 221 : const size_t stavesCount = _score->staves().size();
3066 :
3067 221 : Segment* segment = firstMeasure->first(SegmentType::ChordRest);
3068 463 : for (size_t i = 0; i < stavesCount; ++i) {
3069 242 : Staff* staff = _score->staff(i);
3070 242 : const auto& it = m_capoParams.find(staff->part()->id().toUint64());
3071 :
3072 242 : if (it != m_capoParams.end() && it->second > 0) {
3073 0 : size_t track = i * VOICES;
3074 :
3075 0 : CapoParams params;
3076 0 : params.active = true;
3077 0 : params.transposeMode = CapoParams::TransposeMode::TAB_ONLY;
3078 0 : params.fretPosition = it->second;
3079 :
3080 0 : Capo* capo = Factory::createCapo(_score->dummy());
3081 0 : capo->setTrack(track);
3082 0 : capo->setParams(params);
3083 0 : segment->add(capo);
3084 :
3085 0 : staff->insertCapoParams({ 0, 1 }, params, true);
3086 0 : }
3087 : }
3088 221 : }
3089 : } // namespace mu::iex::guitarpro
|