LCOV - code coverage report
Current view: top level - src/importexport/guitarpro/internal - importgtp-gp5.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 71.7 % 1135 814
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "importgtp.h"
      24              : 
      25              : #include "containers.h"
      26              : 
      27              : #include "engraving/dom/arpeggio.h"
      28              : #include "engraving/dom/articulation.h"
      29              : #include "engraving/dom/barline.h"
      30              : #include "engraving/dom/bracket.h"
      31              : #include "engraving/dom/chord.h"
      32              : #include "engraving/dom/chordline.h"
      33              : #include "engraving/dom/clef.h"
      34              : #include "engraving/dom/dynamic.h"
      35              : #include "engraving/dom/excerpt.h"
      36              : #include "engraving/dom/factory.h"
      37              : #include "engraving/dom/fingering.h"
      38              : #include "engraving/dom/glissando.h"
      39              : #include "engraving/dom/guitarbend.h"
      40              : #include "engraving/dom/instrtemplate.h"
      41              : #include "engraving/dom/keysig.h"
      42              : #include "engraving/dom/lyrics.h"
      43              : #include "engraving/dom/masterscore.h"
      44              : #include "engraving/dom/measure.h"
      45              : #include "engraving/dom/measurebase.h"
      46              : #include "engraving/dom/note.h"
      47              : #include "engraving/dom/notedot.h"
      48              : #include "engraving/dom/part.h"
      49              : #include "engraving/dom/pitchspelling.h"
      50              : #include "engraving/dom/rehearsalmark.h"
      51              : #include "engraving/dom/rest.h"
      52              : #include "engraving/dom/segment.h"
      53              : #include "engraving/dom/slur.h"
      54              : #include "engraving/dom/staff.h"
      55              : #include "engraving/dom/stafftext.h"
      56              : #include "engraving/dom/stafftype.h"
      57              : #include "engraving/dom/stringdata.h"
      58              : #include "engraving/dom/tempotext.h"
      59              : #include "engraving/dom/tie.h"
      60              : #include "engraving/dom/timesig.h"
      61              : #include "engraving/dom/tremolosinglechord.h"
      62              : #include "engraving/dom/tuplet.h"
      63              : #include "engraving/dom/volta.h"
      64              : #include "engraving/types/symid.h"
      65              : #include "engraving/dom/stringtunings.h"
      66              : #include "engraving/dom/capo.h"
      67              : 
      68              : #include "engraving/editing/editchord.h"
      69              : 
      70              : #include "guitarprodrumset.h"
      71              : #include "utils.h"
      72              : 
      73              : #include "log.h"
      74              : 
      75              : using namespace muse::io;
      76              : using namespace mu::engraving;
      77              : 
      78              : namespace mu::iex::guitarpro {
      79            2 : static TremoloType tremoloType(int division)
      80              : {
      81              :     static const std::map<int, TremoloType> types {
      82              :         { 1, TremoloType::R8 },
      83              :         { 2, TremoloType::R16 },
      84              :         { 3, TremoloType::R32 }
      85            4 :     };
      86              : 
      87            2 :     auto it = types.find(division);
      88            2 :     if (it != types.end()) {
      89            2 :         return it->second;
      90              :     }
      91              : 
      92            0 :     LOGE() << "wrong tremolo type";
      93            0 :     return TremoloType::INVALID_TREMOLO;
      94              : }
      95              : 
      96              : static std::unordered_map<uint64_t, mu::engraving::StringData> stringDatas;
      97              : //---------------------------------------------------------
      98              : //   readInfo
      99              : //---------------------------------------------------------
     100              : 
     101           40 : void GuitarPro5::readInfo()
     102              : {
     103           40 :     title        = readDelphiString();
     104           40 :     subtitle     = readDelphiString();
     105           40 :     artist       = readDelphiString();
     106           40 :     album        = readDelphiString();
     107           40 :     composer     = readDelphiString();
     108           40 :     poet         = readDelphiString();
     109           40 :     String copyright = readDelphiString();
     110           40 :     if (!copyright.isEmpty()) {
     111            1 :         score->setMetaTag(u"copyright", copyright);
     112              :     }
     113              : 
     114           40 :     readDelphiString(); // transcriber
     115           40 :     readDelphiString(); // instructions
     116           40 :     int n = readInt();
     117           50 :     for (int i = 0; i < n; ++i) {
     118           10 :         comments.append(readDelphiString());
     119              :     }
     120           40 : }
     121              : 
     122              : //---------------------------------------------------------
     123              : //   readBeatEffects
     124              : //---------------------------------------------------------
     125              : 
     126           28 : int GuitarPro5::readBeatEffects(int track, Segment* segment)
     127              : {
     128           28 :     int effects = 0;
     129              : 
     130           28 :     uint8_t fxBits1 = readUInt8();
     131           28 :     uint8_t fxBits2 = readUInt8();
     132           28 :     if (fxBits1 & BEAT_FADE) {
     133            2 :         effects = 4;      // fade in
     134              :     }
     135           28 :     if (fxBits1 & BEAT_EFFECT) {
     136            9 :         int k = readUInt8();
     137            9 :         effects = k + effects * 100;    // &effects;
     138              :     }
     139           28 :     if (fxBits1 & BEAT_VIBRATO_TREMOLO) {
     140            6 :         effects = 7 + effects * 100;
     141              :     }
     142           28 :     if (fxBits2 & BEAT_TREMOLO) {
     143            3 :         readTremoloBar(track, segment);           // readBend();
     144              :     }
     145           28 :     if (fxBits2 & BEAT_RASGUEADO) {
     146            4 :         m_currentBeatHasRasgueado = true;
     147              :     }
     148           28 :     if (fxBits1 & BEAT_ARPEGGIO) {
     149            2 :         int strokeup = readUInt8();                // up stroke length
     150            2 :         int strokedown = readUInt8();                // down stroke length
     151              : 
     152            2 :         Arpeggio* a = Factory::createArpeggio(score->dummy());
     153              :         // representation is different in guitar pro 5 - the up/down order below is correct
     154            2 :         if (strokeup > 0) {
     155            1 :             a->setArpeggioType(ArpeggioType::DOWN_STRAIGHT);
     156            1 :             if (strokeup < 7) {
     157            1 :                 a->setStretch(1.0 / std::pow(2, 6 - strokeup));
     158              :             }
     159            1 :         } else if (strokedown > 0) {
     160            1 :             a->setArpeggioType(ArpeggioType::UP_STRAIGHT);
     161            1 :             if (strokedown < 7) {
     162            1 :                 a->setStretch(1.0 / std::pow(2, 6 - strokedown));
     163              :             }
     164              :         } else {
     165            0 :             delete a;
     166            0 :             a = 0;
     167              :         }
     168            2 :         if (a) {
     169            2 :             ChordRest* cr = Factory::createChord(segment);
     170            2 :             cr->setTrack(track);
     171            2 :             cr->add(a);
     172            2 :             segment->add(cr);
     173              :         }
     174              :     }
     175           28 :     if (fxBits2 & BEAT_STROKE_DIR) {
     176            2 :         effects = readChar();                // stroke pick direction
     177            2 :         effects += 4;     //1 or 2 for effects becomes 4 or 5
     178              :     }
     179           28 :     return effects;
     180              : }
     181              : 
     182              : //---------------------------------------------------------
     183              : //   readPageSetup
     184              : //---------------------------------------------------------
     185              : 
     186           40 : void GuitarPro5::readPageSetup()
     187              : {
     188           40 :     skip(version > 500 ? 49 : 30);
     189          480 :     for (int i = 0; i < 11; ++i) {
     190          440 :         skip(4);
     191          440 :         readBytePascalString();
     192              :     }
     193           40 : }
     194              : 
     195              : //---------------------------------------------------------
     196              : //   readBeat
     197              : //---------------------------------------------------------
     198              : 
     199          698 : Fraction GuitarPro5::readBeat(const Fraction& tick, int voice, Measure* measure, int staffIdx, Tuplet** tuplets,
     200              :                               bool /*mixChange*/)
     201              : {
     202          698 :     uint8_t beatBits = readUInt8();
     203          698 :     bool dotted    = beatBits & BEAT_DOTTED;
     204          698 :     bool hasSlur = false;
     205          698 :     bool hasHammerOnPullOff = false;
     206          698 :     bool hasLetRing = false;
     207          698 :     bool hasPalmMute = false;
     208          698 :     bool hasTrill = false;
     209          698 :     bool hasVibratoLeftHand = false;
     210          698 :     bool hasVibratoWTremBar = false;
     211          698 :     bool hasHarmonicArtificial = false;
     212          698 :     bool hasHarmonicPinch = false;
     213          698 :     bool hasHarmonicTap = false;
     214          698 :     bool hasHarmonicSemi = false;
     215              : 
     216          698 :     m_currentBeatHasRasgueado = false;
     217              : 
     218          698 :     slide = -1;
     219          698 :     int track = staffIdx * VOICES + voice;
     220          698 :     if (muse::contains(slides, track)) {
     221            0 :         slide = muse::take(slides, track);
     222              :     }
     223              : 
     224          698 :     int pause = -1;
     225          698 :     if (beatBits & BEAT_PAUSE) {
     226          243 :         pause = readUInt8();
     227              :     }
     228              : 
     229              :     // readDuration
     230          698 :     int len   = readChar();
     231          698 :     int tuple = 0;
     232          698 :     if (beatBits & BEAT_TUPLET) {
     233           57 :         tuple = readInt();
     234              :     }
     235              : 
     236          698 :     Segment* segment = measure->getSegment(SegmentType::ChordRest, tick);
     237          698 :     if (beatBits & BEAT_CHORD) {
     238            3 :         size_t numStrings = score->staff(staffIdx)->part()->instrument()->stringData()->strings();
     239            3 :         skip(17);
     240            3 :         String name;
     241              :         {
     242            3 :             uint8_t charsCount = readUInt8();
     243              :             char c[21];
     244            3 :             f->read((uint8_t*)(c), 21);
     245              :             // Just in case
     246            3 :             if (charsCount <= 20) {
     247            3 :                 c[charsCount] = '\0';
     248              :             } else {
     249            0 :                 c[20] = '\0';
     250              :             }
     251            3 :             name = String::fromUtf8(c);
     252              :         }
     253            3 :         skip(4);
     254              :         // no header to be read in the GP5 format - default to true.
     255            3 :         readChord(segment, staffIdx * VOICES, static_cast<int>(numStrings), name, true);
     256            3 :         skip(32);
     257            3 :     }
     258          698 :     Lyrics* lyrics = 0;
     259          698 :     String free_text;
     260          698 :     if (beatBits & BEAT_LYRICS) {
     261              :         /// it is not beat lirics, it is free text
     262            0 :         free_text = readDelphiString();
     263              :     }
     264              : 
     265          698 :     int beatEffects = 0;
     266          698 :     if (beatBits & BEAT_EFFECTS) {
     267           28 :         beatEffects = readBeatEffects(track, segment);
     268              :     }
     269              : 
     270          698 :     last_segment = segment;
     271          698 :     if (beatBits & BEAT_MIX_CHANGE) {
     272            1 :         readMixChange(measure);
     273              :     }
     274              : 
     275          698 :     int strings = readUInt8();     // used strings mask
     276              : 
     277          698 :     Fraction l  = len2fraction(len);
     278              : 
     279              :     // Some beat effects could add a Chord before this
     280          698 :     ChordRest* cr = segment->cr(track);
     281          698 :     if (voice != 0 && pause == 0 && strings == 0) {
     282          153 :         cr = 0;
     283              :     } else {
     284          545 :         if (strings == 0) {
     285           90 :             if (cr) {
     286            0 :                 segment->remove(cr);
     287            0 :                 delete cr;
     288            0 :                 cr = 0;
     289              :             }
     290           90 :             cr = Factory::createRest(segment);
     291              :         } else {
     292          455 :             if (!cr) {
     293          453 :                 cr = Factory::createChord(segment);
     294              :             }
     295              :         }
     296          545 :         cr->setTrack(track);
     297              : 
     298          545 :         TDuration d(l);
     299          545 :         d.setDots(dotted ? 1 : 0);
     300              : 
     301          545 :         if (dotted) {
     302           13 :             l = l + (l * Fraction(1, 2));
     303              :         }
     304              : 
     305          545 :         if (tuple) {
     306           57 :             int track2 = staffIdx * 2 + voice;
     307           57 :             Tuplet* tuplet = tuplets[track2];
     308           57 :             if ((tuplet == nullptr) || (tuplet->elementsDuration() == tuplet->baseLen().fraction() * tuplet->ratio().numerator())) {
     309           19 :                 tuplet = Factory::createTuplet(measure);
     310           19 :                 tuplets[track2] = tuplet;
     311           19 :                 setTuplet(tuplet, tuple);
     312              :             }
     313           57 :             tuplet->setTrack(cr->track());
     314           57 :             tuplet->setBaseLen(l);
     315           57 :             tuplet->setTick(tick);
     316           57 :             tuplet->setTicks(l * tuplet->ratio().denominator());
     317           57 :             cr->setTuplet(tuplet);
     318           57 :             tuplet->add(cr);
     319              :         }
     320              : 
     321          545 :         cr->setTicks(l);
     322          545 :         if (cr->isRest() && (pause == 0 || l >= measure->ticks())) {
     323            8 :             cr->setDurationType(DurationType::V_MEASURE);
     324            8 :             cr->setTicks(measure->ticks());
     325              :         } else {
     326          537 :             cr->setDurationType(d);
     327              :         }
     328              : 
     329          545 :         if (!segment->cr(track)) {
     330          543 :             segment->add(cr);
     331              :         }
     332              : 
     333          545 :         Staff* staff = cr->staff();
     334          545 :         size_t numStrings = staff->part()->instrument()->stringData()->strings();
     335              : 
     336          545 :         Note* _note = nullptr;
     337          545 :         std::vector<Note*> delnote;
     338         4360 :         for (int i = 6; i >= 0; --i) {
     339         3815 :             if (strings & (1 << i) && ((6 - i) < static_cast<int>(numStrings))) {
     340          639 :                 Note* note = Factory::createNote(toChord(cr));
     341          639 :                 _note = note;
     342          639 :                 if (dotted) {
     343              :                     // there is at most one dotted note in this guitar pro version
     344           15 :                     NoteDot* dot = Factory::createNoteDot(note);
     345           15 :                     dot->setTrack(track);            // needed to know the staff it belongs to (and detect tablature)
     346           15 :                     dot->setVisible(true);
     347           15 :                     note->add(dot);
     348              :                 }
     349          639 :                 toChord(cr)->add(note);
     350              : 
     351          639 :                 ReadNoteResult readResult = readNote(6 - i, note);
     352          639 :                 hasSlur = readResult.slur || hasSlur;
     353          639 :                 hasHammerOnPullOff = readResult.hammerOnPullOff || hasHammerOnPullOff;
     354          639 :                 hasLetRing = readResult.letRing || hasLetRing;
     355          639 :                 hasPalmMute = readResult.palmMute || hasPalmMute;
     356          639 :                 hasTrill = readResult.trill || hasTrill;
     357          639 :                 hasVibratoLeftHand = readResult.vibrato || hasVibratoLeftHand;
     358          639 :                 hasHarmonicArtificial = readResult.harmonicArtificial || hasHarmonicArtificial;
     359          639 :                 hasHarmonicPinch = readResult.harmonicPinch || hasHarmonicPinch;
     360          639 :                 hasHarmonicTap = readResult.harmonicTap || hasHarmonicTap;
     361          639 :                 hasHarmonicSemi = readResult.harmonicSemi || hasHarmonicSemi;
     362          639 :                 if (!slideList.empty() && slideList.back() == nullptr) {
     363            2 :                     slideList.back() = note;
     364            2 :                     hasSlur = true;
     365              :                 }
     366          639 :                 if (note->fret() == -20) {
     367            0 :                     delnote.push_back(note);
     368              :                 } else {
     369          639 :                     note->setTpcFromPitch(Prefer::SHARPS);
     370              :                 }
     371              :             }
     372              :         }
     373          545 :         if (!delnote.empty()) {
     374            0 :             Chord* chord = toChord(cr);
     375            0 :             for (auto n : delnote) {
     376            0 :                 chord->remove(n);
     377            0 :                 delete n;
     378              :             }
     379            0 :             if (chord->notes().empty()) {
     380            0 :                 if (chord->tuplet()) {
     381            0 :                     chord->tuplet()->remove(chord);
     382              :                 }
     383            0 :                 chord->segment()->remove(chord);
     384            0 :                 delete chord;
     385            0 :                 cr = nullptr;
     386              :             }
     387            0 :             delnote.clear();
     388              :         }
     389          545 :         if (cr && cr->isChord()) {
     390          455 :             Chord* chord = toChord(cr);
     391          455 :             chord->sortNotes();
     392          455 :             mu::iex::guitarpro::utils::createGhostNoteParenGroups(chord);
     393              :         }
     394          545 :         createSlur(hasSlur, staffIdx, cr);
     395          545 :         if (lyrics) {
     396            0 :             cr->add(lyrics);
     397              :         }
     398          545 :         if (free_text.size() && _note) {
     399            0 :             addTextToNote(free_text, _note);
     400              :         }
     401          545 :     }
     402              : 
     403          698 :     int rr = readChar();
     404          698 :     if (cr && cr->isChord()) {
     405          455 :         Chord* chord = toChord(cr);
     406              : 
     407          455 :         bool hasVibratoLeftHandOnBeat = false;
     408          455 :         bool hasVibratoWTremBarOnBeat = false;
     409              : 
     410              :         do {
     411          455 :             applyBeatEffects(chord, beatEffects % 100, hasVibratoLeftHandOnBeat, hasVibratoWTremBarOnBeat);
     412          455 :             hasVibratoLeftHand = hasVibratoLeftHand || hasVibratoLeftHandOnBeat;
     413          455 :             hasVibratoWTremBar = hasVibratoWTremBar || hasVibratoWTremBarOnBeat;
     414          455 :         } while (beatEffects /= 100);
     415          455 :         if (rr == ARPEGGIO_DOWN) {
     416            0 :             chord->setStemDirection(DirectionV::DOWN);
     417          455 :         } else if (rr == ARPEGGIO_UP) {
     418            0 :             chord->setStemDirection(DirectionV::UP);
     419              :         }
     420              :     }
     421              : 
     422          698 :     if (cr) {
     423              :         //  fixing gp5 bug with not storying let ring for tied notes
     424          545 :         if (hasLetRing) {
     425           14 :             m_letRingForChords.insert(cr);
     426              :         }
     427              : 
     428          545 :         addLetRing(cr, hasLetRing);
     429          545 :         addPalmMute(cr, hasPalmMute);
     430          545 :         addTrill(cr, hasTrill);
     431          545 :         addHammerOnPullOff(cr, hasHammerOnPullOff);
     432          545 :         addRasgueado(cr, m_currentBeatHasRasgueado);
     433          545 :         addVibratoLeftHand(cr, hasVibratoLeftHand);
     434          545 :         addVibratoWTremBar(cr, hasVibratoWTremBar);
     435          545 :         addHarmonicMarks(cr, hasHarmonicArtificial, hasHarmonicPinch, hasHarmonicTap, hasHarmonicSemi);
     436              :     }
     437              : 
     438          698 :     int r = readChar();
     439          698 :     if (r & 0x8) {
     440            0 :         int rrr = readChar();
     441            0 :         LOGD("  3beat read 0x%02x", rrr);
     442              :     }
     443          698 :     if (cr && cr->isChord()) {
     444          455 :         if (toChord(cr)->notes().size() == 0) {
     445            0 :             segment->remove(cr);
     446            0 :             delete cr;
     447            0 :             cr = 0;
     448          455 :         } else if (slide > 0) {
     449            0 :             createSlide(slide, cr, staffIdx);
     450              :         }
     451              :     }
     452          698 :     restsForEmptyBeats(segment, measure, cr, l, track, tick);
     453         1396 :     return cr ? cr->actualTicks() : measure->ticks();
     454          698 : }
     455              : 
     456              : //---------------------------------------------------------
     457              : //   readMeasure
     458              : //---------------------------------------------------------
     459              : 
     460          143 : void GuitarPro5::readMeasure(Measure* measure, int staffIdx, Tuplet** tuplets, bool mixChange)
     461              : {
     462          429 :     for (int voice = 0; voice < 2; ++voice) {
     463          286 :         Fraction measureLen = { 0, 1 };
     464          286 :         Fraction tick       = measure->tick();
     465          286 :         int beats           = readInt();
     466          286 :         if (beats > 100) {
     467            0 :             return;
     468              :         }
     469          984 :         for (int beat = 0; beat < beats; ++beat) {
     470          698 :             Fraction ticks = readBeat(tick, voice, measure, staffIdx, tuplets, mixChange);
     471          698 :             ++_beat_counter;
     472          698 :             tick += ticks;
     473          698 :             measureLen += ticks;
     474              :         }
     475          286 :         if (measureLen < measure->ticks()) {
     476            9 :             score->setRest(tick, staffIdx * VOICES + voice, measure->ticks() - measureLen, false, nullptr, false);
     477            9 :             m_continiousElementsBuilder->notifyUncompletedMeasure();
     478              :         }
     479              :     }
     480              : }
     481              : 
     482              : //---------------------------------------------------------
     483              : //   readMixChange
     484              : //---------------------------------------------------------
     485              : 
     486            1 : bool GuitarPro5::readMixChange(Measure* measure)
     487              : {
     488              :     /*char patch   =*/
     489            1 :     readChar();
     490            1 :     skip(16);
     491            1 :     signed char volume  = readChar();
     492            1 :     signed char pan     = readChar();
     493            1 :     signed char chorus  = readChar();
     494            1 :     signed char reverb  = readChar();
     495            1 :     signed char phase   = readChar();
     496            1 :     signed char tremolo = readChar();
     497            1 :     readDelphiString();                   // tempo name
     498              : 
     499            1 :     int temp = readInt();
     500            1 :     bool editedTempo = false;
     501            1 :     if (volume >= 0) {
     502            0 :         readChar();
     503              :     }
     504            1 :     if (pan >= 0) {
     505            0 :         readChar();
     506              :     }
     507            1 :     if (chorus >= 0) {
     508            0 :         readChar();
     509              :     }
     510            1 :     if (reverb >= 0) {
     511            0 :         readChar();
     512              :     }
     513              : 
     514            1 :     if (phase >= 0) {
     515            0 :         readChar();
     516              :     }
     517            1 :     if (tremolo >= 0) {
     518            0 :         readChar();
     519              :     }
     520            1 :     if (temp >= 0) {
     521            1 :         if (temp != previousTempo) {
     522            1 :             if (last_segment && last_segment->tick() != measure->tick()) {
     523            0 :                 bool hasTempoText = false;
     524            0 :                 for (EngravingItem* e : last_segment->annotations()) {
     525            0 :                     hasTempoText |= e->isTempoText();
     526              :                 }
     527            0 :                 if (!hasTempoText) {
     528            0 :                     TempoText* tt = Factory::createTempoText(last_segment);
     529            0 :                     tt->setTempo(BeatsPerSecond::fromBPM(temp));
     530            0 :                     tt->setVisible(false);
     531            0 :                     tt->setTrack(0);
     532            0 :                     last_segment->add(tt);
     533              :                 }
     534              :             }
     535            1 :             previousTempo = temp;
     536            1 :             setTempo(temp, measure);
     537            1 :             editedTempo = true;
     538              :         }
     539            1 :         last_segment = nullptr;
     540            1 :         readChar();
     541            1 :         if (version > 500) {
     542            1 :             readChar();
     543              :         }
     544              :     }
     545            1 :     readChar();
     546            1 :     readChar();
     547            1 :     if (version > 500) {
     548            1 :         readDelphiString();
     549            1 :         readDelphiString();
     550              :     }
     551            1 :     return editedTempo;
     552              : }
     553              : 
     554              : //---------------------------------------------------------
     555              : //   readTracks
     556              : //---------------------------------------------------------
     557              : 
     558           40 : bool GuitarPro5::readTracks()
     559              : {
     560           40 :     stringDatas.clear();
     561           85 :     for (size_t i = 0; i < staves; ++i) {
     562              :         int tuning[GP_MAX_STRING_NUMBER];
     563           45 :         Staff* staff = score->staff(i);
     564           45 :         Part* part = staff->part();
     565              : 
     566           45 :         uint8_t c = readUInt8();       // simulations bitmask
     567           45 :         if (c & 0x2) {               // 12 stringed guitar
     568              :         }
     569           45 :         if (c & 0x4) {               // banjo track
     570              :         }
     571           45 :         if (i == 0 || version == 500) {
     572           40 :             skip(1);
     573              :         }
     574           45 :         String name = readPascalString(40);
     575              : 
     576           45 :         int strings  = readInt();
     577           45 :         if (strings <= 0 || strings > GP_MAX_STRING_NUMBER) {
     578            0 :             return false;
     579              :         }
     580          314 :         for (int j = 0; j < strings; ++j) {
     581          269 :             tuning[j] = readInt();
     582              :         }
     583           91 :         for (int j = strings; j < GP_MAX_STRING_NUMBER; ++j) {
     584           46 :             readInt();
     585              :         }
     586           45 :         int midiPort     = readInt() - 1;
     587           45 :         int midiChannel  = readInt() - 1;
     588           90 :         if (midiChannel < 0 || midiChannel >= static_cast<int>(channelDefaults.size())) {
     589            0 :             LOGE() << "midiChannel " << midiChannel << " out of range 0-" << channelDefaults.size();
     590            0 :             return false;
     591              :         }
     592           45 :         /*int midiChannel2 =*/ readInt();       // -1
     593              : 
     594           45 :         int frets        = readInt();
     595           45 :         int capo         = readInt();
     596           45 :         /*int color        =*/ readInt();
     597              : 
     598           45 :         skip(version > 500 ? 49 : 44);
     599           45 :         if (version > 500) {
     600              :             //  british stack clean / amp tone
     601           38 :             readDelphiString();
     602           38 :             readDelphiString();
     603              :         }
     604           45 :         std::vector<int> tuning2(strings);
     605              :         //int tuning2[strings];
     606          314 :         for (int k = 0; k < strings; ++k) {
     607          269 :             tuning2[strings - k - 1] = tuning[k];
     608              :         }
     609           45 :         bool useFlats = createTuningString(strings, &tuning2[0]);
     610           45 :         StringData stringData(frets, strings, &tuning2[0], useFlats);
     611           45 :         Instrument* instr = part->instrument();
     612           45 :         instr->setStringData(stringData);
     613           45 :         instr->setSingleNoteDynamics(false);
     614           45 :         part->setPlainLongName(name);
     615           45 :         stringDatas.insert_or_assign(part->id().toUint64(), stringData);
     616              : 
     617              :         //
     618              :         // determine clef
     619              :         //
     620           45 :         int patch = channelDefaults[midiChannel].patch;
     621           45 :         ClefType clefId = ClefType::G;
     622           45 :         if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
     623            1 :             clefId = ClefType::PERC;
     624            1 :             StaffTypes type = StaffTypes::PERC_DEFAULT;
     625            1 :             if (auto it = drumset::PERC_STAFF_LINES_FROM_INSTRUMENT.find(name.toStdString());
     626            1 :                 it != drumset::PERC_STAFF_LINES_FROM_INSTRUMENT.end()) {
     627            0 :                 drumset::initGuitarProPercussionSet(it->second);
     628            0 :                 drumset::setInstrumentDrumset(instr, it->second);
     629            0 :                 switch (it->second.numLines) {
     630            0 :                 case 1:
     631            0 :                     type = StaffTypes::PERC_1LINE;
     632            0 :                     break;
     633            0 :                 case 2:
     634            0 :                     type = StaffTypes::PERC_2LINE;
     635            0 :                     break;
     636            0 :                 case 3:
     637            0 :                     type = StaffTypes::PERC_3LINE;
     638            0 :                     break;
     639            0 :                 default:
     640            0 :                     type = StaffTypes::PERC_DEFAULT;
     641            0 :                     break;
     642              :                 }
     643              :             } else {
     644            1 :                 drumset::initGuitarProDrumset();
     645            1 :                 instr->setDrumset(drumset::gpDrumset);
     646              :             }
     647            1 :             staff->setStaffType(Fraction(0, 1), *StaffType::preset(type));
     648              :         } else {
     649           44 :             clefId = defaultClef(patch);
     650              :         }
     651              : 
     652           45 :         Measure* measure = score->firstMeasure();
     653           45 :         if (measure->tick() != Fraction(0, 1)) {
     654            0 :             Segment* segment = measure->getSegment(SegmentType::HeaderClef, Fraction(0, 1));
     655              : 
     656            0 :             Clef* clef = Factory::createClef(segment);
     657            0 :             clef->setClefType(clefId);
     658            0 :             clef->setTrack(i * VOICES);
     659            0 :             segment->add(clef);
     660              :         } else {
     661           45 :             staff->setDefaultClefType(ClefTypeList(clefId, clefId));
     662              :         }
     663              : 
     664           45 :         if (capo > 0 && !engravingConfiguration()->guitarProImportExperimental()) {
     665            2 :             Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
     666            2 :             const size_t track = i * VOICES;
     667              : 
     668            2 :             CapoParams params;
     669            2 :             params.active = true;
     670            2 :             params.transposeMode = CapoParams::TransposeMode::TAB_ONLY;
     671            2 :             params.fretPosition = capo;
     672              : 
     673            2 :             Capo* capoEl = Factory::createCapo(s);
     674            2 :             capoEl->setTrack(track);
     675            2 :             capoEl->setParams(params);
     676            2 :             s->add(capoEl);
     677              : 
     678            2 :             staff->insertCapoParams({ 0, 1 }, params, true);
     679            2 :         }
     680              : 
     681           45 :         InstrChannel* ch = instr->channel(0);
     682           45 :         if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
     683            1 :             ch->setProgram(0);
     684            1 :             ch->setBank(128);
     685              :         } else {
     686           44 :             ch->setProgram(patch);
     687           44 :             ch->setBank(0);
     688              :         }
     689           45 :         ch->setVolume(channelDefaults[midiChannel].volume);
     690           45 :         ch->setPan(channelDefaults[midiChannel].pan);
     691           45 :         ch->setChorus(channelDefaults[midiChannel].chorus);
     692           45 :         ch->setReverb(channelDefaults[midiChannel].reverb);
     693           45 :         staff->part()->setMidiChannel(midiChannel, midiPort);
     694              : 
     695              :         // missing: phase, tremolo
     696           45 :     }
     697           40 :     skip(version == 500 ? 2 : 1);
     698              : 
     699           40 :     return true;
     700              : }
     701              : 
     702              : //---------------------------------------------------------
     703              : //   readMeasures
     704              : //---------------------------------------------------------
     705              : 
     706           40 : void GuitarPro5::readMeasures(int /*startingTempo*/)
     707              : {
     708           40 :     Measure* measure = score->firstMeasure();
     709           40 :     bool mixChange = false;
     710          169 :     for (size_t bar = 0; bar < measures; ++bar, measure = measure->nextMeasure()) {
     711          129 :         const GpBar& gpbar = bars[bar];
     712              : 
     713          129 :         if (!gpbar.marker.isEmpty()) {
     714            4 :             Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
     715            4 :             RehearsalMark* s = Factory::createRehearsalMark(segment);
     716            4 :             s->setType(RehearsalMark::Type::Additional);
     717            4 :             s->setPlainText(gpbar.marker.trimmed());
     718            4 :             s->setTrack(0);
     719            4 :             segment->add(s);
     720              :         }
     721              : 
     722          129 :         std::vector<Tuplet*> tuplets(staves * 2);
     723              :         //Tuplet* tuplets[staves * 2];     // two voices
     724          415 :         for (size_t track = 0; track < staves * 2; ++track) {
     725          286 :             tuplets[track] = 0;
     726              :         }
     727              : 
     728          272 :         for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
     729          143 :             _beat_counter = 0;
     730          143 :             readMeasure(measure, static_cast<int>(staffIdx), &tuplets[0], mixChange);
     731          143 :             if (!(((bar == (measures - 1)) && (staffIdx == (staves - 1))))) {
     732              :                 /*int a = */
     733          103 :                 readChar();
     734              :             }
     735              :         }
     736          129 :         if (bar == 1 && !mixChange) {
     737           17 :             setTempo(tempo, score->firstMeasure());
     738              :         }
     739          129 :     }
     740              : 
     741           40 :     if (!gpLyrics.segments.empty()) {
     742            0 :         size_t size = std::min(gpLyrics.segments.size(), gpLyrics.lyrics.size());
     743            0 :         for (size_t i = 0; i < size; ++i) {
     744            0 :             std::string str = gpLyrics.lyrics[i].toStdString();
     745            0 :             auto seg = gpLyrics.segments[i];
     746            0 :             auto mes = seg->measure();
     747            0 :             while (str.size() && seg && seg->segmentType() == SegmentType::ChordRest) {
     748            0 :                 auto cr = seg->cr(gpLyrics.lyricTrack);
     749            0 :                 if (cr) {
     750            0 :                     if (str[0] != '-') {
     751            0 :                         Lyrics* lyr = Factory::createLyrics(cr);
     752              : 
     753            0 :                         std::string text;
     754            0 :                         auto pos = str.find('-');
     755            0 :                         auto pos2 = str.find('\n');
     756            0 :                         if (pos2 < pos) {
     757            0 :                             pos = pos2;
     758              :                         }
     759            0 :                         if (pos != std::string::npos) {
     760            0 :                             const char* c = &str.c_str()[pos + 1];
     761            0 :                             if (*c == 0) {
     762            0 :                                 pos = std::string::npos;
     763            0 :                                 text = str;
     764              :                             } else {
     765            0 :                                 text = str.substr(0, pos);
     766            0 :                                 str = str.substr(pos + 1);
     767              :                             }
     768              :                         } else {
     769            0 :                             text = str;
     770              :                         }
     771            0 :                         if (pos == std::string::npos) {
     772            0 :                             str.resize(0);
     773              :                         }
     774            0 :                         lyr->setPlainText(String::fromStdString(text));
     775            0 :                         cr->add(lyr);
     776            0 :                     } else {
     777            0 :                         str = str.substr(1);
     778              :                     }
     779              :                 }
     780            0 :                 seg = seg->next();
     781            0 :                 if (!seg) {
     782            0 :                     mes = mes->nextMeasure();
     783            0 :                     if (!mes) {
     784            0 :                         break;
     785              :                     }
     786            0 :                     seg = mes->first();
     787              :                 }
     788              :             }
     789            0 :         }
     790           40 :     } else if (gpLyrics.lyricTrack != 0) {
     791            0 :         size_t counter = 0;
     792            0 :         size_t index = 0;
     793            0 :         gpLyrics.lyricTrack -= 1;
     794            0 :         Measure* mes = score->firstMeasure();
     795            0 :         if (!mes) {
     796            0 :             return;
     797              :         }
     798              : 
     799            0 :         Segment* beg = mes->first();
     800              : 
     801              :         do {
     802            0 :             if (beg->segmentType() == SegmentType::ChordRest && beg->cr(gpLyrics.lyricTrack)) {
     803            0 :                 ChordRest* cr = beg->cr(gpLyrics.lyricTrack);
     804            0 :                 assert(cr);
     805            0 :                 ++counter;
     806            0 :                 if (!cr->isChord()) {
     807            0 :                     continue;
     808              :                 }
     809              : 
     810            0 :                 bool isTied = false;
     811            0 :                 Chord* chord = toChord(cr);
     812            0 :                 for (Note* n : chord->notes()) {
     813            0 :                     const auto& tiedNotes = n->tiedNotes();
     814              : 
     815            0 :                     if (tiedNotes.size() > 1 && tiedNotes.front() != n) {
     816            0 :                         isTied = true;
     817            0 :                         break;
     818              :                     }
     819            0 :                 }
     820              : 
     821            0 :                 if (isTied) {
     822            0 :                     continue;
     823              :                 }
     824              : 
     825            0 :                 if (counter >= gpLyrics.fromBeat) {
     826            0 :                     if (gpLyrics.lyricPos.size() == index) {
     827            0 :                         gpLyrics.lyricPos.push_back(0);
     828              :                     }
     829              : 
     830            0 :                     String& lyricsInIndex = gpLyrics.lyrics[index];
     831            0 :                     if (lyricsInIndex[0] != u'-') {
     832            0 :                         Lyrics* lyr = mu::engraving::Factory::createLyrics(cr);
     833              : 
     834            0 :                         String text;
     835            0 :                         size_t pos = lyricsInIndex.indexOf(u'-', gpLyrics.lyricPos[index]);
     836            0 :                         size_t pos2 = lyricsInIndex.indexOf(u'\n', gpLyrics.lyricPos[index]);
     837            0 :                         if (pos2 < pos) {
     838            0 :                             pos = pos2;
     839              :                         }
     840              : 
     841            0 :                         if (pos != std::string::npos && (pos + 1 < lyricsInIndex.size())) {
     842            0 :                             const char16_t* c = &lyricsInIndex[pos + 1];
     843            0 :                             if (*c == 0) {
     844            0 :                                 pos = std::string::npos;
     845            0 :                                 text = lyricsInIndex;
     846              :                             } else {
     847            0 :                                 text = lyricsInIndex.mid(gpLyrics.lyricPos[index], pos + 1 - gpLyrics.lyricPos[index]);
     848            0 :                                 gpLyrics.lyricPos[index] = pos + 1;
     849              :                             }
     850              :                         } else {
     851            0 :                             text = lyricsInIndex.mid(gpLyrics.lyricPos[index]);
     852              :                         }
     853              : 
     854            0 :                         if (pos == std::string::npos) {
     855            0 :                             ++index;
     856              :                         }
     857              : 
     858            0 :                         lyr->setPlainText(text);
     859            0 :                         cr->add(lyr);
     860            0 :                         if (index >= gpLyrics.lyrics.size()) {
     861            0 :                             break;
     862              :                         }
     863            0 :                     } else if (lyricsInIndex.size() >= 2) {
     864            0 :                         lyricsInIndex = lyricsInIndex.mid(1);
     865              :                     }
     866              :                 }
     867              :             }
     868            0 :         } while ((beg = beg->next())
     869            0 :                  || (mes->next() && mes->next()->isMeasure() && (mes = toMeasure(mes->next())) && (beg = mes->first())));
     870              :     }
     871              : }
     872              : 
     873              : //---------------------------------------------------------
     874              : //   read
     875              : //---------------------------------------------------------
     876              : 
     877           40 : bool GuitarPro5::read(IODevice* io)
     878              : {
     879           40 :     m_continiousElementsBuilder = std::make_unique<ContiniousElementsBuilder>(score);
     880           40 :     m_guitarBendImporter = std::make_unique<GuitarBendImporter>(score);
     881              : 
     882           40 :     f = io;
     883              : 
     884           40 :     readInfo();
     885           40 :     readLyrics();
     886           40 :     readPageSetup();
     887              : 
     888           40 :     previousTempo = -1;
     889              : 
     890           40 :     tempo = readInt();
     891           40 :     if (version > 500) {
     892           33 :         skip(1);
     893              :     }
     894              : 
     895           40 :     key    = readInt();
     896           40 :     /* int octave =*/ readChar();      // octave
     897              : 
     898           40 :     readChannels();
     899              : 
     900           40 :     std::vector<unsigned int> articulations;
     901           40 :     articulations.resize(19);
     902              :     {
     903              :         unsigned int r;
     904              :         unsigned char x;
     905          800 :         for (int i = 0; i < 19; ++i) {
     906          760 :             x = readUInt8();
     907          760 :             r = x;
     908          760 :             x = readUInt8();
     909          760 :             r += x << 8;
     910              : 
     911          760 :             articulations[i] = r;
     912              :         }
     913              :     }
     914              : 
     915              :     //skip(42);
     916           40 :     skip(4);
     917              : 
     918           40 :     measures = readInt();
     919           40 :     staves  = readInt();
     920           40 :     initDynamics(staves);
     921              : 
     922          320 :     for (size_t str = 0; str < 7; ++str) {
     923          595 :         for (size_t staff = 0; staff < staves; ++staff) {
     924          315 :             dead_end[{ static_cast<int>(staff), static_cast<int>(str) }] = true;
     925              :         }
     926              :     }
     927              : 
     928           40 :     slurs.resize(staves, nullptr);
     929              : 
     930           40 :     int tnumerator   = 4;
     931           40 :     int tdenominator = 4;
     932          169 :     for (size_t i = 0; i < measures; ++i) {
     933          129 :         if (i > 0) {
     934           89 :             skip(1);
     935              :         }
     936          129 :         GpBar bar;
     937          129 :         uint8_t barBits = readUInt8();
     938          129 :         if (barBits & SCORE_TIMESIG_NUMERATOR) {
     939           40 :             tnumerator = readUInt8();
     940              :         }
     941          129 :         if (barBits & SCORE_TIMESIG_DENOMINATOR) {
     942           40 :             tdenominator = readUInt8();
     943              :         }
     944          129 :         if (barBits & SCORE_REPEAT_START) {
     945            0 :             bar.repeatFlags = bar.repeatFlags | mu::engraving::Repeat::START;
     946              :         }
     947          129 :         if (barBits & SCORE_REPEAT_END) {                    // number of repeats
     948            1 :             bar.repeatFlags = bar.repeatFlags | mu::engraving::Repeat::END;
     949            1 :             bar.repeats = readUInt8();
     950              :         }
     951          129 :         if (barBits & SCORE_MARKER) {
     952            4 :             bar.marker = readDelphiString();           // new section?
     953            4 :             /*int color =*/ readInt();          // color?
     954              :         }
     955          129 :         if (barBits & SCORE_VOLTA) {                          // a volta
     956            2 :             uint8_t voltaNumber = readUInt8();
     957           11 :             while (voltaNumber > 0) {
     958              :                 // voltas are represented as flags
     959            9 :                 bar.volta.voltaType = GP_VOLTA_FLAGS;
     960            9 :                 bar.volta.voltaInfo.push_back(voltaNumber & 1);
     961            9 :                 voltaNumber >>= 1;
     962              :             }
     963            2 :             bar.repeats = static_cast<int>(bar.volta.voltaInfo.size()) + 1;
     964              :         }
     965          129 :         if (barBits & SCORE_KEYSIG) {
     966           70 :             int currentKey = readUInt8();
     967              :             /* key signatures are specified as
     968              :              * 1# = 1, 2# = 2, ..., 7# = 7
     969              :              * 1b = 255, 2b = 254, ... 7b = 249 */
     970           70 :             bar.keysig = currentKey <= 7 ? currentKey : -256 + currentKey;
     971           70 :             readUInt8();              // specified major/minor mode
     972              :         }
     973          129 :         if (barBits & SCORE_DOUBLE_BAR) {
     974            0 :             bar.barLine = BarLineType::DOUBLE;
     975              :         }
     976          129 :         if (barBits & 0x3) {
     977           40 :             skip(4);
     978              :         }
     979          129 :         if ((barBits & 0x10) == 0) {
     980          127 :             skip(1);
     981              :         }
     982              : 
     983          129 :         readChar();                 // triple feel  (none, 8, 16)
     984          129 :         bar.timesig = Fraction(tnumerator, tdenominator);
     985          129 :         bars.push_back(bar);
     986          129 :     }
     987              : 
     988              :     //
     989              :     // create a part for every staff
     990              :     //
     991           85 :     for (size_t staffIdx = 0; staffIdx < staves; ++staffIdx) {
     992           45 :         Part* part = new Part(score);
     993           45 :         Staff* s = Factory::createStaff(part);
     994              : 
     995           45 :         score->appendStaff(s);
     996           45 :         score->appendPart(part);
     997              :     }
     998              : 
     999           40 :     createMeasures();
    1000           40 :     if (!readTracks()) {
    1001            0 :         return false;
    1002              :     }
    1003           40 :     readMeasures(tempo);
    1004           40 :     const Measure* m = score->firstMeasure();
    1005              : 
    1006              :     // NOTE: GP doesn't support multiple tunings on one part
    1007              :     // We're safe to just take the very first chord rest segment
    1008              :     // and check if it has any non-standard tuning
    1009           40 :     const Fraction& frac{ 0, 1 };
    1010              : 
    1011           85 :     for (auto p : score->parts()) {
    1012           90 :         for (auto s : p->staves()) {
    1013           45 :             if (!s->isPrimaryStaff() || p->instrument()->useDrumset()) {
    1014           41 :                 continue;
    1015              :             }
    1016              : 
    1017           44 :             Segment* seg = m->findSegment(SegmentType::ChordRest, frac);
    1018              : 
    1019           44 :             IF_ASSERT_FAILED(seg) {
    1020            0 :                 LOGE() << "First measure MUST has a chord rest segment after import";
    1021            0 :                 return false;
    1022              :             }
    1023              : 
    1024           44 :             if (stringDatas.find(p->id().toUint64()) == stringDatas.end()) {
    1025            0 :                 continue;
    1026              :             }
    1027           44 :             StringData sd = stringDatas.at(p->id().toUint64());
    1028              : 
    1029           44 :             std::vector<int> tuningPitches(sd.strings());
    1030          307 :             for (size_t i = 0; i < tuningPitches.size(); ++i) {
    1031          263 :                 tuningPitches[i] = sd.stringList().at(i).pitch + p->instrument()->transpose().chromatic;
    1032              :             }
    1033              : 
    1034           44 :             if (utils::isStandardTuning(p->instrument()->channel(0)->program(), tuningPitches)) {
    1035           40 :                 continue;
    1036              :             }
    1037              : 
    1038            4 :             StringTunings* tun = Factory::createStringTunings(seg);
    1039            4 :             tun->setStringData(sd);
    1040            4 :             tun->setTrack(staff2track(s->idx()));
    1041            4 :             seg->add(tun);
    1042              : 
    1043            4 :             auto tuning = utils::standardTuningFor(p->instrument()->channel(0)->program(), (int)sd.strings());
    1044            4 :             sd = StringData(sd.frets(), (int)tuning.size(), tuning.data());
    1045            4 :             p->instrument()->setStringData(sd);
    1046           84 :         }
    1047              :     }
    1048           43 :     for (auto n : slideList) {
    1049            3 :         auto segment = n->chord()->segment();
    1050            3 :         auto measure = segment->measure();
    1051            3 :         while ((segment = segment->next1(SegmentType::ChordRest)) || ((measure = measure->nextMeasure()) && (segment = measure->first()))) {
    1052            3 :             if (segment->segmentType() != SegmentType::ChordRest) {
    1053            0 :                 continue;
    1054              :             }
    1055            3 :             bool br = false;
    1056            3 :             ChordRest* cr = toChordRest(segment->cr(n->track()));
    1057            3 :             if (cr && cr->isChord()) {
    1058            3 :                 Chord* c = toChord(cr);
    1059            3 :                 for (auto nt : c->notes()) {
    1060            3 :                     if (nt->string() == n->string()) {
    1061            3 :                         for (auto e : nt->el()) {
    1062            0 :                             if (e->isChordLine()) {
    1063            0 :                                 ChordLine* cl = toChordLine(e);
    1064            0 :                                 if (cl->chordLineType() == ChordLineType::PLOP || cl->chordLineType() == ChordLineType::SCOOP) {
    1065            0 :                                     br = true;
    1066            0 :                                     break;
    1067              :                                 }
    1068              :                             }
    1069              :                         }
    1070            3 :                         if (br) {
    1071            0 :                             break;
    1072              :                         }
    1073              : 
    1074            3 :                         m_glissandoNotePairs.push_back({ n, nt });
    1075            3 :                         br = true;
    1076            3 :                         break;
    1077              :                     }
    1078              :                 }
    1079              :             }
    1080            3 :             if (br) {
    1081            3 :                 break;
    1082              :             }
    1083              :         }
    1084              :     }
    1085              : 
    1086           40 :     std::map<int, int> counter;
    1087          800 :     for (int i = 0; i < 19; ++i) {
    1088          760 :         if (articulations[i] != 0xFFFF) {
    1089            0 :             Measure* measure = toMeasure(score->measure(articulations[i] - 1));
    1090            0 :             if (i < 4) {
    1091            0 :                 Segment* segment = measure->getSegment(SegmentType::BarLine, measure->tick());
    1092            0 :                 Symbol* sym = new Symbol(measure);
    1093            0 :                 if (i == 0) {
    1094            0 :                     sym->setSym(SymId::coda);
    1095            0 :                 } else if (i == 1) {
    1096            0 :                     sym->setSym(SymId::codaSquare);
    1097            0 :                 } else if (i == 2) {
    1098            0 :                     sym->setSym(SymId::segno);
    1099              :                 } else {
    1100            0 :                     sym->setSym(SymId::segnoSerpent2);
    1101              :                 }
    1102              : 
    1103            0 :                 sym->setTrack(0);
    1104            0 :                 segment->add(sym);
    1105            0 :                 auto iter = counter.find(articulations[i]);
    1106            0 :                 if (iter == counter.end()) {
    1107            0 :                     counter[articulations[i]] = 1;
    1108              :                 } else {
    1109            0 :                     counter[articulations[i]] += 1;
    1110              :                 }
    1111              :             } else {
    1112            0 :                 Segment* s = measure->getSegment(SegmentType::KeySig, measure->tick());
    1113            0 :                 StaffText* st = new StaffText(s);
    1114              :                 static constexpr char text[][22] = {
    1115              :                     "fine", "Da Capo", "D.C. al Coda", "D.C. al Doppia Coda",
    1116              :                     "D.C. al Fine", "Da Segno", "D.S. al Coda", "D.S. al Doppia Coda",
    1117              :                     "D.S. al Fine", "Da Doppio Segno", "D.D.S. al Coda", "D.D.S. al Doppia Coda",
    1118              :                     "D.D.S. al Fine", "Da Coda", "Da Doppia Coda"
    1119              :                 };
    1120            0 :                 st->setPlainText(String::fromAscii(text[i - 4]));
    1121            0 :                 st->setTrack(0);
    1122            0 :                 auto iter = counter.find(articulations[i]);
    1123            0 :                 if (iter == counter.end()) {
    1124            0 :                     counter[articulations[i]] = 1;
    1125              :                 } else {
    1126            0 :                     counter[articulations[i]] += 1;
    1127              :                 }
    1128            0 :                 score->addElement(st);
    1129              :             }
    1130              :         }
    1131              :     }
    1132              : 
    1133           40 :     m_continiousElementsBuilder->addElementsToScore();
    1134           40 :     m_guitarBendImporter->addElementsToScore();
    1135           40 :     addGlissandos();
    1136           40 :     utils::addPlayCountTexts(score);
    1137              : 
    1138           40 :     return true;
    1139           40 : }
    1140              : 
    1141              : //---------------------------------------------------------
    1142              : //   readNoteEffects
    1143              : //---------------------------------------------------------
    1144              : 
    1145           89 : GuitarPro::ReadNoteResult GuitarPro5::readNoteEffects(Note* note)
    1146              : {
    1147           89 :     ReadNoteResult result;
    1148           89 :     uint8_t modMask1 = readUInt8();
    1149           89 :     uint8_t modMask2 = readUInt8();
    1150           89 :     std::vector<PitchValue> bendData;
    1151           89 :     Note* bendParent = nullptr;
    1152              : 
    1153           89 :     if (modMask1 & EFFECT_BEND) {
    1154           12 :         bendData = readBendDataFromFile();
    1155           12 :         bendParent = note;
    1156              :     }
    1157           89 :     if (modMask1 & EFFECT_HAMMER) {
    1158            3 :         result.hammerOnPullOff = true;
    1159              :     }
    1160           89 :     if (modMask1 & EFFECT_LET_RING) {
    1161           12 :         result.letRing = true;
    1162              :     }
    1163              : 
    1164           89 :     if (modMask1 & EFFECT_GRACE) {
    1165            5 :         int fret = readUInt8();                // grace fret
    1166            5 :         /*int dynamic =*/ readUInt8();                // grace dynamic
    1167            5 :         int transition = readUInt8();                // grace transition
    1168            5 :         int duration = readUInt8();                // grace duration
    1169            5 :         int gflags = readUInt8();
    1170              : 
    1171            5 :         int grace_len = Constants::DIVISION / 8;
    1172            5 :         if (duration == 2) {
    1173            1 :             grace_len = Constants::DIVISION / 6;       //24th
    1174            4 :         } else if (duration == 3) {
    1175            2 :             grace_len = Constants::DIVISION / 4;       //16th
    1176              :         }
    1177            5 :         NoteType note_type = NoteType::ACCIACCATURA;
    1178              : 
    1179            5 :         if (gflags & NOTE_APPOGIATURA) {   //on beat
    1180            2 :             note_type = NoteType::APPOGGIATURA;
    1181              :         }
    1182              : 
    1183            5 :         int grace_pitch = note->staff()->part()->instrument()->stringData()->getPitch(note->string(), fret, nullptr);
    1184              : 
    1185            5 :         auto gnote = score->setGraceNote(note->chord(), grace_pitch, note_type, grace_len);
    1186              : 
    1187              :         // gp5 not supports more than one grace note,
    1188              :         // so it's always should be shown as eight note
    1189            5 :         gnote->chord()->setDurationType(Fraction { 1, 8 });
    1190              : 
    1191            5 :         gnote->setString(note->string());
    1192            5 :         gnote->setFret(fret);
    1193            5 :         if (transition == 0) {
    1194              :             // no transition
    1195            3 :         } else if (transition == 1) {
    1196            2 :         } else if (transition == 3) {
    1197            1 :             Slur* slur1 = Factory::createSlur(score->dummy());
    1198            1 :             slur1->setStartElement(gnote->chord());
    1199            1 :             slur1->setEndElement(note->chord());
    1200            1 :             slur1->setOwnershipParent(0);
    1201            1 :             slur1->setTrack(note->staffIdx());
    1202            1 :             slur1->setTrack2(note->staffIdx());
    1203            1 :             slur1->setTick(gnote->chord()->tick());
    1204            1 :             slur1->setTick2(note->chord()->tick());
    1205            1 :             score->addElement(slur1);
    1206              :             //TODO: Add a 'slide' guitar effect when implemented
    1207              :             //note->setSlideNote(gn);
    1208              :         }
    1209              :     }
    1210           89 :     if (modMask2 & EFFECT_STACCATO) {
    1211            0 :         Chord* chord = note->chord();
    1212            0 :         Articulation* a = Factory::createArticulation(chord);
    1213            0 :         a->setSymId(SymId::articStaccatoAbove);
    1214            0 :         bool add = true;
    1215            0 :         for (auto a1 : chord->articulations()) {
    1216            0 :             if (a1->symId() == SymId::articStaccatoAbove) {
    1217            0 :                 add = false;
    1218            0 :                 break;
    1219              :             }
    1220              :         }
    1221            0 :         if (add) {
    1222            0 :             chord->add(a);
    1223              :         }
    1224              :     }
    1225           89 :     if (modMask2 & EFFECT_PALM_MUTE) {
    1226            3 :         result.palmMute = true;
    1227              :     }
    1228              : 
    1229           89 :     if (modMask2 & EFFECT_TREMOLO) {      // tremolo picking length
    1230            2 :         int tremoloDivision = readUInt8();
    1231            2 :         if (tremoloDivision >= 1 && tremoloDivision <= 3) {
    1232            2 :             TremoloType type = tremoloType(tremoloDivision);
    1233            2 :             DO_ASSERT(!isTremoloTwoChord(type));
    1234            2 :             Chord* chord = note->chord();
    1235            2 :             TremoloSingleChord* t = Factory::createTremoloSingleChord(chord);
    1236            2 :             t->setTremoloType(type);
    1237            2 :             chord->add(t);
    1238            2 :             m_tremolosInChords[chord] = type;
    1239            2 :         } else {
    1240            0 :             LOGD("Unknown tremolo value");
    1241              :         }
    1242              :     }
    1243           89 :     if (modMask2 & EFFECT_SLIDE) {
    1244           22 :         int slideKind = readUInt8();
    1245           22 :         ChordLine* sld = nullptr;
    1246           22 :         ChordLineType slideType = ChordLineType::NOTYPE;
    1247              : 
    1248           22 :         if (slideKind & SLIDE_OUT_DOWN) {
    1249            4 :             slideKind &= ~SLIDE_OUT_DOWN;
    1250            4 :             sld = Factory::createChordLine(score->dummy());
    1251            4 :             slideType = ChordLineType::FALL;
    1252              :         }
    1253              :         // slide out upwards (doit)
    1254           22 :         if (slideKind & SLIDE_OUT_UP) {
    1255            4 :             slideKind &= ~SLIDE_OUT_UP;
    1256            4 :             sld = Factory::createChordLine(score->dummy());
    1257            4 :             slideType = ChordLineType::DOIT;
    1258              :         }
    1259              :         // slide in from below (plop)
    1260           22 :         if (slideKind & SLIDE_IN_BELOW) {
    1261            4 :             slideKind &= ~SLIDE_IN_BELOW;
    1262            4 :             sld = Factory::createChordLine(score->dummy());
    1263            4 :             slideType = ChordLineType::PLOP;
    1264              :         }
    1265              :         // slide in from above (scoop)
    1266           22 :         if (slideKind & SLIDE_IN_ABOVE) {
    1267            4 :             slideKind &= ~SLIDE_IN_ABOVE;
    1268            4 :             sld = Factory::createChordLine(score->dummy());
    1269            4 :             slideType = ChordLineType::SCOOP;
    1270              :         }
    1271              : 
    1272           22 :         if (sld) {
    1273           16 :             sld->setChordLineType(slideType);
    1274           16 :             sld->setStraight(true);
    1275           16 :             note->chord()->add(sld);
    1276           16 :             sld->setNote(note);
    1277              :         }
    1278              : 
    1279           22 :         if (slideKind & LEGATO_SLIDE) {
    1280            2 :             slideKind &= ~LEGATO_SLIDE;
    1281            2 :             slideList.push_back(nullptr);
    1282            2 :             createSlur(true, note->staffIdx(), note->chord());
    1283              :         }
    1284              : 
    1285           22 :         if (slideKind & SHIFT_SLIDE) {
    1286            1 :             slideKind &= ~SHIFT_SLIDE;
    1287            1 :             slideList.push_back(note);
    1288              :         }
    1289              :     }
    1290              : 
    1291           89 :     if (modMask2 & EFFECT_ARTIFICIAL_HARMONIC) {
    1292           25 :         int type = readChar();
    1293           25 :         if (type == HARMONIC_MARK_NATURAL) {
    1294            0 :             int fret = note->fret();
    1295              : 
    1296            0 :             note->setHarmonic(true);
    1297            0 :             float harmonicFret = naturalHarmonicFromFret(fret);
    1298            0 :             int harmonicOvertone = utils::harmonicOvertone(note, harmonicFret, type);
    1299            0 :             note->setDisplayFret(Note::DisplayFretOption::NaturalHarmonic);
    1300            0 :             note->setHarmonicFret(harmonicFret);
    1301            0 :             auto staff = note->staff();
    1302            0 :             int pitch = staff->part()->instrument()->stringData()->getPitch(note->string(), harmonicOvertone, staff, note->tick());
    1303              : 
    1304            0 :             note->setPitch(clampPitch(pitch));
    1305            0 :             note->setTpcFromPitch(Prefer::SHARPS);
    1306           25 :         } else if (type >= HARMONIC_MARK_ARTIFICIAL && type <= HARMONIC_MARK_SEMI) {
    1307           25 :             int fret = (type == HARMONIC_MARK_TAP ? (readChar() - note->fret()) : note->fret());
    1308           25 :             float midi = note->pitch() + fret;
    1309           25 :             int currentOctave = floor(midi / 12);
    1310           25 :             auto harmNote = (type == HARMONIC_MARK_ARTIFICIAL ? readChar() : 0);
    1311           25 :             if (type == HARMONIC_MARK_ARTIFICIAL) {
    1312           19 :                 readChar();
    1313              :             }
    1314              : 
    1315           25 :             auto octave = currentOctave + (type == HARMONIC_MARK_ARTIFICIAL ? readChar() : 0);
    1316              : 
    1317           25 :             Note* harmonicNote = Factory::createNote(note->chord());
    1318              : 
    1319           25 :             harmonicNote->setHarmonic(true);
    1320           25 :             harmonicNote->setPlay(false);
    1321           25 :             note->setPlay(true);
    1322              :             /// @note option to show or not additional harmonic fret in "<>" to be implemented
    1323              :             ///harmonicNote->setDisplayFret(Note::DisplayFretOption::ArtificialHarmonic);
    1324              :             ///note->setDisplayFret(Note::DisplayFretOption::Hide);
    1325           25 :             harmonicNote->setDisplayFret(Note::DisplayFretOption::Hide);
    1326           25 :             m_harmonicNotes[note] = harmonicNote;
    1327              : 
    1328           25 :             Staff* staff = note->staff();
    1329           25 :             float harmonicFret = 0;
    1330           25 :             switch (static_cast<int>(((octave * 12) + harmNote) - midi)) {
    1331            0 :             case 4:
    1332            0 :                 harmonicFret = 12;
    1333            0 :                 break;
    1334            0 :             case 11:
    1335            0 :                 harmonicFret = 7;
    1336            0 :                 break;
    1337            0 :             case 16:
    1338            0 :                 harmonicFret = 5;
    1339            0 :                 break;
    1340            1 :             case 20:
    1341            1 :                 harmonicFret = 4;
    1342            1 :                 break;
    1343            0 :             case 23:
    1344            0 :                 harmonicFret = 3.2f;
    1345            0 :                 break;
    1346            0 :             case 25:
    1347            0 :                 harmonicFret = 2.7f;
    1348            0 :                 break;
    1349            0 :             case 28:
    1350            0 :                 harmonicFret = 2.4f;
    1351            0 :                 break;
    1352           24 :             default:
    1353           24 :                 harmonicFret = 12;
    1354           24 :                 break;
    1355              :             }
    1356              : 
    1357           25 :             int overtoneFret = utils::harmonicOvertone(note, harmonicFret, type);
    1358           25 :             harmonicNote->setString(note->string());
    1359           25 :             harmonicNote->setFret(note->fret());
    1360           25 :             harmonicNote->setHarmonicFret(harmonicFret + fret);
    1361              : 
    1362           25 :             int pitch = staff->part()->instrument()->stringData()->getPitch(note->string(), overtoneFret, staff, note->tick());
    1363              : 
    1364           25 :             harmonicNote->setPitch(clampPitch(pitch));
    1365           25 :             harmonicNote->setTpcFromPitch(Prefer::SHARPS);
    1366           25 :             note->setHarmonicPitchOffset(harmonicNote->pitch() - note->pitch());
    1367           25 :             note->chord()->add(harmonicNote);
    1368              : 
    1369           25 :             switch (type) {
    1370           19 :             case HARMONIC_MARK_ARTIFICIAL:
    1371           19 :                 result.harmonicArtificial = true;
    1372           19 :                 break;
    1373            0 :             case HARMONIC_MARK_PINCH:
    1374            0 :                 result.harmonicPinch = true;
    1375            0 :                 break;
    1376            1 :             case HARMONIC_MARK_TAP:
    1377            1 :                 result.harmonicTap = true;
    1378            1 :                 break;
    1379            5 :             case HARMONIC_MARK_SEMI:
    1380            5 :                 result.harmonicSemi = true;
    1381            5 :                 break;
    1382              :             }
    1383              :         }
    1384              :     }
    1385              : 
    1386           89 :     if (bendParent) {
    1387           12 :         createBend(bendParent, bendData);
    1388              :     }
    1389              : 
    1390           89 :     if (modMask2 & EFFECT_VIBRATO) {
    1391            7 :         result.vibrato = true;
    1392              :     }
    1393              : 
    1394           89 :     if (modMask2 & EFFECT_TRILL) {
    1395            0 :         readUInt8();          // trill fret
    1396              : 
    1397            0 :         int period = readUInt8();          // trill length
    1398            0 :         result.trill = true;
    1399              : 
    1400            0 :         switch (period) {
    1401            0 :         case 1:                     // 16
    1402            0 :             break;
    1403            0 :         case 2:                     // 32
    1404            0 :             break;
    1405            0 :         case 3:                     // 64
    1406            0 :             break;
    1407            0 :         default:
    1408            0 :             LOGD("unknown trill period %d", period);
    1409            0 :             break;
    1410              :         }
    1411              :     }
    1412           89 :     return result;
    1413           89 : }
    1414              : 
    1415              : //---------------------------------------------------------
    1416              : //   readNote
    1417              : //---------------------------------------------------------
    1418              : 
    1419          639 : GuitarPro::ReadNoteResult GuitarPro5::readNote(int string, Note* note)
    1420              : {
    1421          639 :     uint8_t noteBits = readUInt8();
    1422              :     //
    1423              :     // noteBits:
    1424              :     //    7 - Right hand or left hand fingering;
    1425              :     //    6 - Accentuated note
    1426              :     //    5 - Note type (rest, empty note, normal note);
    1427              :     //    4 - note dynamic;
    1428              :     //    3 - Presence of effects linked to the note;
    1429              :     //    2 - Ghost note;
    1430              :     //    1 - Dotted note;  ?
    1431              :     //    0 - Time-independent duration
    1432              : 
    1433          639 :     if (noteBits & NOTE_GHOST) {
    1434            0 :         note->setGhost(true);
    1435              :     }
    1436              : 
    1437          639 :     bool tieNote = false;
    1438              : 
    1439          639 :     if (noteBits & NOTE_DEAD) {
    1440          639 :         uint8_t noteType = readUInt8();
    1441          639 :         if (noteType == 1) {
    1442              :         }                         //standard note
    1443           28 :         else if (noteType == 2) {
    1444           21 :             tieNote = true;
    1445            7 :         } else if (noteType == 3) {                   // dead notes
    1446            7 :             note->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
    1447            7 :             note->setDeadNote(true);
    1448              :         } else {
    1449            0 :             LOGD("unknown note type: %d", noteType);
    1450              :         }
    1451              :     }
    1452              : 
    1453          639 :     int& previousDynamic = previousDynamicByTrack[note->track()];
    1454              : 
    1455          639 :     if (noteBits & NOTE_DYNAMIC) {            // velocity
    1456          478 :         int d = readChar();
    1457          478 :         if (previousDynamic != d) {
    1458           51 :             previousDynamic = d;
    1459           51 :             addDynamic(note, d);
    1460              :         }
    1461          161 :     } else if (previousDynamic != DEFAULT_DYNAMIC) {
    1462           19 :         previousDynamic = DEFAULT_DYNAMIC;
    1463           19 :         addDynamic(note, previousDynamic);
    1464              :     }
    1465              : 
    1466          639 :     int fretNumber = 0;
    1467          639 :     if (noteBits & NOTE_FRET) {
    1468          639 :         fretNumber = readChar();
    1469              :     }
    1470              : 
    1471          639 :     if (noteBits & NOTE_FINGERING) {
    1472           10 :         int leftFinger = readUInt8();
    1473           10 :         int rightFinger = readUInt8();
    1474           10 :         Fingering* fi = Factory::createFingering(note);
    1475           10 :         String finger;
    1476              :         // if there is a valid left hand fingering
    1477           10 :         if (leftFinger < 5) {
    1478            5 :             if (leftFinger == 0) {
    1479            1 :                 finger = u"T";
    1480            4 :             } else if (leftFinger == 1) {
    1481            1 :                 finger = u"1";
    1482            3 :             } else if (leftFinger == 2) {
    1483            1 :                 finger = u"2";
    1484            2 :             } else if (leftFinger == 3) {
    1485            1 :                 finger = u"3";
    1486            1 :             } else if (leftFinger == 4) {
    1487            1 :                 finger = u"4";
    1488              :             }
    1489              :         } else {
    1490            5 :             if (rightFinger == 0) {
    1491            1 :                 finger = u"T";
    1492            4 :             } else if (rightFinger == 1) {
    1493            1 :                 finger = u"I";
    1494            3 :             } else if (rightFinger == 2) {
    1495            1 :                 finger = u"M";
    1496            2 :             } else if (rightFinger == 3) {
    1497            1 :                 finger = u"A";
    1498            1 :             } else if (rightFinger == 4) {
    1499            1 :                 finger = u"O";
    1500              :             }
    1501              :         }
    1502           10 :         fi->setPlainText(finger);
    1503           10 :         note->add(fi);
    1504           10 :         fi->reset();
    1505           10 :     }
    1506              : 
    1507          639 :     if (noteBits & 0x1) {     // Time independent duration
    1508            0 :         skip(8);
    1509              :     }
    1510              : 
    1511              :     // check if a note is supposed to be accented, and give it the marcato type
    1512          639 :     if (noteBits & NOTE_MARCATO) {
    1513            1 :         Articulation* art = Factory::createArticulation(note->score()->dummy());
    1514            1 :         art->setSymId(SymId::articMarcatoAbove);
    1515            1 :         if (!EditChord::toggleArticulation(note->score(), note, art)) {
    1516            0 :             delete art;
    1517              :         }
    1518              :     }
    1519              :     // check if a note is supposed to be accented, and give it the sforzato type
    1520          638 :     else if (noteBits & NOTE_SFORZATO) {
    1521            0 :         Articulation* art = Factory::createArticulation(note->score()->dummy());
    1522            0 :         art->setSymId(SymId::articAccentAbove);
    1523            0 :         note->add(art);
    1524            0 :         if (!EditChord::toggleArticulation(note->score(), note, art)) {
    1525            0 :             delete art;
    1526              :         }
    1527              :     }
    1528              : 
    1529          639 :     readUInt8();   //skip
    1530              : 
    1531          639 :     Staff* staff = note->staff();
    1532          639 :     if (fretNumber == 255 || fretNumber < 0) {
    1533            0 :         fretNumber = 0;
    1534            0 :         note->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
    1535            0 :         note->setDeadNote(true);
    1536              :     }
    1537          639 :     int pitch = staff->part()->instrument()->stringData()->getPitch(string, fretNumber, staff, note->tick());
    1538          639 :     note->setFret(fretNumber);
    1539          639 :     note->setString(string);
    1540          639 :     note->setPitch(pitch);
    1541              : 
    1542              :     // This function uses string and fret number, so it should be set before this
    1543          639 :     ReadNoteResult result;
    1544          639 :     if (noteBits & NOTE_SLUR) {
    1545           89 :         result = readNoteEffects(note);
    1546              :     }
    1547              : 
    1548          639 :     if (tieNote) {
    1549           21 :         auto staffIdx = note->staffIdx();
    1550           21 :         if (dead_end[{ static_cast<int>(staffIdx), string }]) {
    1551            0 :             note->setFret(INVALID_FRET_INDEX);
    1552            0 :             result.slur = false;
    1553            0 :             return result;
    1554              :         }
    1555           21 :         if (slurs[staffIdx]) {
    1556            1 :             score->removeSpanner(slurs[staffIdx]);
    1557            1 :             delete slurs[staffIdx];
    1558            1 :             slurs[staffIdx] = 0;
    1559              :         }
    1560           21 :         bool found = false;
    1561           21 :         Chord* chord     = note->chord();
    1562           21 :         Segment* segment = chord->segment()->prev1(SegmentType::ChordRest);
    1563           21 :         track_idx_t track = note->track();
    1564           21 :         std::vector<ChordRest*> chords;
    1565           21 :         Note* true_note = nullptr;
    1566           25 :         while (segment) {
    1567           25 :             EngravingItem* e = segment->element(track);
    1568           25 :             if (e && e->isChord()) {
    1569           23 :                 Chord* chord2 = toChord(e);
    1570           38 :                 for (Note* note2 : chord2->notes()) {
    1571           36 :                     if (note2->string() == string) {
    1572           21 :                         if (chords.empty()) {
    1573           19 :                             Tie* tie = Factory::createTie(note2);
    1574           19 :                             tie->setEndNote(note);
    1575           19 :                             note2->add(tie);
    1576              :                         }
    1577              : 
    1578              :                         //  fixing gp5 bug with not storying let ring for tied notes
    1579           21 :                         if (m_letRingForChords.find(chord2) != m_letRingForChords.end()) {
    1580            2 :                             result.letRing = true;
    1581            2 :                             muse::remove(m_letRingForChords, chord2);
    1582              :                         }
    1583              : 
    1584           21 :                         if (m_harmonicNotes.find(note) != m_harmonicNotes.end() && m_harmonicNotes.find(note2) != m_harmonicNotes.end()) {
    1585           12 :                             Note* startHarmonicNote = m_harmonicNotes.at(note2);
    1586           12 :                             Note* endHarmonicNote = m_harmonicNotes.at(note);
    1587              : 
    1588           12 :                             Tie* tieHarmonic = Factory::createTie(startHarmonicNote);
    1589           12 :                             tieHarmonic->setEndNote(endHarmonicNote);
    1590           12 :                             startHarmonicNote->add(tieHarmonic);
    1591              : 
    1592           12 :                             muse::remove(m_harmonicNotes, startHarmonicNote);
    1593           12 :                             muse::remove(m_harmonicNotes, endHarmonicNote);
    1594              :                         }
    1595              : 
    1596           21 :                         note->setFret(note2->fret());
    1597           21 :                         note->setPitch(note2->pitch());
    1598           21 :                         true_note = note2;
    1599           21 :                         if (m_tremolosInChords.find(chord2) != m_tremolosInChords.end()) {
    1600            0 :                             TremoloType type = m_tremolosInChords.at(chord2);
    1601            0 :                             DO_ASSERT(!isTremoloTwoChord(type));
    1602            0 :                             TremoloSingleChord* t = Factory::createTremoloSingleChord(chord);
    1603            0 :                             t->setTremoloType(type);
    1604            0 :                             chord->add(t);
    1605            0 :                             muse::remove(m_tremolosInChords, chord2);
    1606            0 :                             m_tremolosInChords[chord] = type;
    1607              :                         }
    1608              : 
    1609           21 :                         found = true;
    1610           21 :                         break;
    1611              :                     }
    1612              :                 }
    1613           23 :                 if (found) {
    1614           21 :                     break;
    1615            2 :                 } else if (e) {
    1616            2 :                     chords.push_back(toChordRest(e));
    1617              :                 }
    1618              :             }
    1619            4 :             segment = segment->prev1(SegmentType::ChordRest);
    1620              :         }
    1621              : 
    1622           21 :         if (true_note && !chords.empty()) {
    1623            2 :             Note* end_note = note;
    1624            4 :             for (unsigned int i = 0; i < chords.size(); ++i) {
    1625            2 :                 Chord* chord1 = nullptr;
    1626            2 :                 ChordRest* cr = chords.at(i);
    1627            2 :                 if (cr->isChord()) {
    1628            2 :                     chord1 = toChord(cr);
    1629              :                 } else {
    1630            0 :                     Rest* rest = toRest(cr);
    1631            0 :                     Fraction dur = rest->ticks();
    1632            0 :                     TDuration dut = rest->durationType();
    1633            0 :                     Segment* seg = rest->segment();
    1634            0 :                     seg->remove(rest);
    1635            0 :                     Tuplet* tuplet = rest->tuplet();
    1636            0 :                     if (tuplet) {
    1637            0 :                         tuplet->remove(rest);
    1638              :                     }
    1639            0 :                     delete rest;
    1640            0 :                     chord1 = Factory::createChord(seg);
    1641            0 :                     chord1->setTrack(note->track());
    1642            0 :                     chord1->setTicks(dur);
    1643            0 :                     chord1->setDurationType(dut);
    1644            0 :                     seg->add(chord1);
    1645            0 :                     if (tuplet) {
    1646            0 :                         tuplet->add(chord1);
    1647              :                     }
    1648              :                 }
    1649              : 
    1650            2 :                 Note* note2 = Factory::createNote(chord1);
    1651            2 :                 note2->setString(true_note->string());
    1652            2 :                 note2->setFret(true_note->fret());
    1653            2 :                 note2->setPitch(true_note->pitch());
    1654            2 :                 note2->setTpcFromPitch(Prefer::SHARPS);
    1655            2 :                 chord1->setNoteType(true_note->noteType());
    1656            2 :                 chord1->add(note2);
    1657            2 :                 Tie* tie = Factory::createTie(note2);
    1658            2 :                 tie->setEndNote(end_note);
    1659            2 :                 end_note->setHarmonic(true_note->harmonic());
    1660            2 :                 end_note = note2;
    1661            2 :                 note2->add(tie);
    1662              :             }
    1663            2 :             Tie* tie = Factory::createTie(true_note);
    1664            2 :             tie->setEndNote(end_note);
    1665            2 :             end_note->setHarmonic(true_note->harmonic());
    1666            2 :             true_note->add(tie);
    1667              :         }
    1668           21 :         if (!found) {
    1669            0 :             note->setFret(INVALID_FRET_INDEX);
    1670            0 :             dead_end[{ static_cast<int>(staffIdx), string }] = true;
    1671            0 :             LOGD("tied note not found, pitch %d fret %d string %d", note->pitch(), note->fret(), note->string());
    1672            0 :             result.slur = false;
    1673            0 :             return result;
    1674              :         }
    1675           21 :     }
    1676          639 :     dead_end[{ static_cast<int>(note->staffIdx()), string }] = false;
    1677          639 :     return result;
    1678              : }
    1679              : 
    1680            0 : float GuitarPro5::naturalHarmonicFromFret(int fret)
    1681              : {
    1682            0 :     switch (fret) {
    1683            0 :     case 2:
    1684            0 :         return 2.4f;
    1685            0 :     case 3:
    1686            0 :         return 3.2f;
    1687            0 :     case 4:
    1688              :     case 5:
    1689              :     case 7:
    1690              :     case 9:
    1691              :     case 12:
    1692              :     case 16:
    1693              :     case 17:
    1694              :     case 19:
    1695              :     case 24:
    1696            0 :         return fret;
    1697            0 :     case 8:
    1698            0 :         return 8.2f;
    1699            0 :     case 10:
    1700            0 :         return 9.6f;
    1701            0 :     case 14:
    1702            0 :         return 14.7f;
    1703            0 :     case 15:
    1704            0 :         return 14.7f;
    1705            0 :     case 21:
    1706            0 :         return 21.7f;
    1707            0 :     case 22:
    1708            0 :         return 21.7f;
    1709            0 :     default:
    1710            0 :         return 12.0f;
    1711              :     }
    1712              : }
    1713              : 
    1714           40 : void GuitarPro5::addGlissandos()
    1715              : {
    1716           43 :     for (auto& [startNote, endNote] : m_glissandoNotePairs) {
    1717            3 :         Note* currentStart = startNote;
    1718            3 :         if (startNote->bendFor()) {
    1719            1 :             Note* bendNote = startNote;
    1720            1 :             GuitarBend* bend = bendNote->bendFor();
    1721              : 
    1722            2 :             while (bend) {
    1723            1 :                 bendNote = bend->endNote();
    1724            1 :                 IF_ASSERT_FAILED(bendNote) {
    1725            0 :                     LOGE() << "glissando start note may be incorrect";
    1726            0 :                     break;
    1727              :                 }
    1728              : 
    1729            1 :                 if (!bendNote->chord()->isGraceAfter()) {
    1730            0 :                     break;
    1731              :                 }
    1732              : 
    1733            1 :                 currentStart = bendNote;
    1734            1 :                 bend = bendNote->bendFor();
    1735              :             }
    1736              :         }
    1737              : 
    1738            3 :         Glissando* gliss = mu::engraving::Factory::createGlissando(currentStart);
    1739            3 :         gliss->setStartElement(currentStart);
    1740            3 :         gliss->setTick(currentStart->tick());
    1741            3 :         gliss->setTrack(currentStart->track());
    1742            3 :         gliss->setGlissandoType(GlissandoType::STRAIGHT);
    1743            3 :         gliss->setGlissandoShift(true);
    1744            3 :         gliss->setEndElement(endNote);
    1745            3 :         gliss->setTick2(endNote->tick());
    1746            3 :         gliss->setTrack2(endNote->track());
    1747            3 :         score->addElement(gliss);
    1748              : 
    1749            3 :         std::vector<std::pair<Slur*, Note*> > movedSlurs;
    1750            3 :         for (Spanner* spanner : startNote->chord()->startingSpanners()) {
    1751            2 :             if (spanner && spanner->isSlur()) {
    1752            2 :                 Slur* slur = toSlur(spanner);
    1753            2 :                 if (slur->endElement() == endNote->chord()) {
    1754            2 :                     gliss->setGlissandoShift(false);
    1755            2 :                     slur->setConnectedElement(mu::engraving::Slur::ConnectedElement::GLISSANDO);
    1756            2 :                     movedSlurs.push_back({ slur, currentStart });
    1757            2 :                     break;
    1758              :                 }
    1759              :             }
    1760              :         }
    1761              : 
    1762            5 :         for (auto& [slur, note] : movedSlurs) {
    1763            2 :             slur->setStartElement(note->chord());
    1764            2 :             slur->setTick(note->tick());
    1765            2 :             slur->setTrack(note->track());
    1766              :         }
    1767            3 :     }
    1768           40 : }
    1769              : } // namespace mu::iex::guitarpro
        

Generated by: LCOV version 2.0-1