LCOV - code coverage report
Current view: top level - src/importexport/tabledit/internal - importtef.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 91.6 % 924 846
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) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "importtef.h"
      24              : #include "measurehandler.h"
      25              : #include "readinglist.h"
      26              : #include "tuplethandler.h"
      27              : 
      28              : #include "engraving/dom/arpeggio.h"
      29              : #include "engraving/dom/box.h"
      30              : #include "engraving/dom/capo.h"
      31              : #include "engraving/dom/chord.h"
      32              : #include "engraving/dom/excerpt.h"
      33              : #include "engraving/dom/factory.h"
      34              : #include "engraving/dom/fingering.h"
      35              : #include "engraving/dom/glissando.h"
      36              : #include "engraving/dom/hammeronpulloff.h"
      37              : #include "engraving/dom/keysig.h"
      38              : #include "engraving/dom/letring.h"
      39              : #include "engraving/dom/measure.h"
      40              : #include "engraving/dom/measurebase.h"
      41              : #include "engraving/dom/note.h"
      42              : #include "engraving/dom/part.h"
      43              : #include "engraving/dom/playcounttext.h"
      44              : #include "engraving/dom/rest.h"
      45              : #include "engraving/dom/staff.h"
      46              : #include "engraving/dom/stafftext.h"
      47              : #include "engraving/dom/stafftype.h"
      48              : #include "engraving/dom/tempotext.h"
      49              : #include "engraving/dom/text.h"
      50              : #include "engraving/dom/timesig.h"
      51              : #include "engraving/dom/volta.h"
      52              : #include "log.h"
      53              : 
      54              : using namespace mu::engraving;
      55              : 
      56              : namespace mu::iex::tabledit {
      57          142 : int8_t TablEdit::readInt8()
      58              : {
      59              :     int8_t result;
      60          142 :     _file->read((uint8_t*)&result, 1);
      61          142 :     return result;
      62              : }
      63              : 
      64        11238 : uint8_t TablEdit::readUInt8()
      65              : {
      66              :     uint8_t result;
      67        11238 :     _file->read((uint8_t*)&result, 1);
      68        11238 :     return result;
      69              : }
      70              : 
      71         1888 : uint16_t TablEdit::readUInt16()
      72              : {
      73              :     uint16_t result;
      74         1888 :     _file->read((uint8_t*)&result, 2);
      75         1888 :     return result;
      76              : }
      77              : 
      78         1570 : uint32_t TablEdit::readUInt32()
      79              : {
      80              :     uint32_t result;
      81         1570 :     _file->read((uint8_t*)&result, 4);
      82         1570 :     return result;
      83              : }
      84              : 
      85              : // read sized and null-terminated utf8 text from the current position
      86              : 
      87          282 : std::string TablEdit::readUtf8Text()
      88              : {
      89          282 :     std::string result;
      90          282 :     uint16_t size = readUInt16();
      91          282 :     if (size < 1) {
      92            1 :         LOGE() << "Invalid Utf8Text";
      93            1 :         return result;
      94              :     }
      95         1942 :     for (uint16_t i = 0; i < size - 1; ++i) {
      96         1661 :         result += readUInt8();
      97              :     }
      98          281 :     readUInt8(); // skip null
      99              :     LOGN("size %d result '%s'", size, result.c_str());
     100          281 :     return result;
     101            0 : }
     102              : 
     103              : // read sized and null-terminated utf8 text
     104              : // input is the position where the text's position in the file is stored
     105              : 
     106          366 : std::string TablEdit::readUtf8TextIndirect(uint32_t positionOfPosition)
     107              : {
     108          366 :     _file->seek(positionOfPosition);
     109          366 :     uint32_t position = readUInt32();
     110          366 :     if (position) {
     111          246 :         _file->seek(position);
     112              :         LOGN("position %d", position);
     113          246 :         return readUtf8Text();
     114              :     } else {
     115          240 :         return "";
     116              :     }
     117              : }
     118              : 
     119              : // return the part index for the instrument containing stringIdx
     120              : 
     121          471 : engraving::part_idx_t TablEdit::partIdx(size_t stringIdx, bool& ok) const
     122              : {
     123          471 :     ok = true;
     124          471 :     engraving::part_idx_t result { 0 };
     125          471 :     engraving::track_idx_t lowerBound { 1 };
     126          471 :     engraving::track_idx_t upperBound { 0 };
     127              : 
     128          517 :     for (const auto& instrument : tefInstruments) {
     129          517 :         upperBound += instrument.stringNumber;
     130          517 :         if (lowerBound <= stringIdx && stringIdx <= upperBound) {
     131              :             LOGN("string %zu lower %zu upper %zu found result %zu", stringIdx, lowerBound, upperBound, result);
     132          471 :             return result;
     133              :         }
     134           46 :         ++result;
     135           46 :         lowerBound += instrument.stringNumber;
     136              :     }
     137            0 :     ok = false;
     138            0 :     result = 0;
     139              :     LOGN("string %zu not found result %zu", stringIdx, result);
     140            0 :     return result;
     141              : }
     142              : 
     143              : // return total number of strings in previous parts
     144              : 
     145          401 : int TablEdit::stringNumberPreviousParts(part_idx_t partIdx) const
     146              : {
     147          401 :     int result { 0 };
     148          439 :     for (part_idx_t i = 0; i < partIdx; ++i) {
     149           38 :         result += tefInstruments.at(i).stringNumber;
     150              :     }
     151              :     LOGN("partIdx %zu result %d", partIdx, result);
     152          401 :     return result;
     153              : }
     154              : 
     155              : // debug: use color cr to show voice
     156              : 
     157              : #if 0
     158              : static muse::draw::Color toColor(const voice_idx_t voice)
     159              : {
     160              :     // debug: color notes based on voice number
     161              :     switch (voice) {
     162              :     case 0: return muse::draw::Color::BLUE;
     163              :     case 1: return muse::draw::Color::GREEN;
     164              :     case 2: return muse::draw::Color::RED;
     165              :     case 3: return { 150, 150, 0, 255 };
     166              :     default: return muse::draw::Color::BLACK;
     167              :     }
     168              : }
     169              : 
     170              : #else
     171          434 : static muse::draw::Color toColor(const voice_idx_t)
     172              : {
     173              :     // no debug: color notes black
     174          434 :     return muse::draw::Color::BLACK;
     175              : }
     176              : 
     177              : #endif
     178              : 
     179              : // create a VoiceAllocator for every instrument
     180              : 
     181           61 : void TablEdit::initializeVoiceAllocators(std::vector<VoiceAllocator>& allocators)
     182              : {
     183          134 :     for (size_t i = 0; i < tefInstruments.size(); ++i) {
     184           73 :         VoiceAllocator allocator;
     185           73 :         allocators.push_back(allocator);
     186           73 :     }
     187           61 : }
     188              : 
     189           61 : void TablEdit::allocateVoices(std::vector<VoiceAllocator>& allocators)
     190              : {
     191           61 :     std::vector<const TefNote*> column;
     192           61 :     int currentPosition { -1 };
     193           61 :     engraving::part_idx_t currentPart { 0 };
     194          482 :     for (const TefNote& tefNote : tefContents) {
     195              :         // a new column is started when either the postion or the part changes
     196          421 :         bool ok { true };
     197          421 :         const auto part = partIdx(tefNote.string, ok);
     198          421 :         if (!ok) {
     199            0 :             LOGD("error: invalid string %d", tefNote.string);
     200            0 :             continue;
     201              :         }
     202          421 :         if (tefNote.position != currentPosition || part != currentPart) {
     203              :             // handle the previous column
     204          336 :             allocators[currentPart].addColumn(column);
     205              :             // start a new column
     206          336 :             currentPosition = tefNote.position;
     207          336 :             currentPart = part;
     208          336 :             column.clear();
     209              :         }
     210          421 :         column.push_back(&tefNote);
     211              :     }
     212              :     // handle the last column
     213           61 :     allocators[currentPart].addColumn(column);
     214           61 : }
     215              : 
     216            5 : static void connectTie(mu::engraving::Chord* chord, Note* note)
     217              : {
     218            5 :     Segment* segment { chord->segment() };
     219            5 :     auto startTrack { VOICES* (chord->track() / VOICES) };
     220            5 :     auto endTrack { startTrack + VOICES - 1 };
     221              :     LOGN("segment %p tick %d string %d startTrack %zu track %zu endTrack %zu",
     222              :          segment, segment->tick().ticks(), note->string(), startTrack, chord->track(), endTrack);
     223              : 
     224            6 :     for (Segment* seg = segment->prev1(); seg; seg = seg->prev1()) {
     225           11 :         for (auto track = startTrack; track <= endTrack; ++track) {
     226           10 :             EngravingItem* el = seg->element(track);
     227           10 :             if (el && el->isChord()) {
     228            6 :                 Chord* firstChord = toChord(el);
     229              :                 LOGN("firstChord %p tick %d track %zu", firstChord, firstChord->tick().ticks(), track);
     230            9 :                 for (Note* firstNote : firstChord->notes()) {
     231              :                     LOGN("- string %d", firstNote->string());
     232            8 :                     if (firstNote->string() == note->string()) {
     233            5 :                         Tie* tie = Factory::createTie(firstNote);
     234            5 :                         tie->setEndNote(note);
     235            5 :                         firstNote->add(tie);
     236              :                         LOGN(" -> tie %p", tie);
     237            5 :                         return;
     238              :                     }
     239              :                 }
     240              :             }
     241              :         }
     242              :     }
     243              : }
     244              : 
     245            5 : static String fingeringTextLH(int leftFinger)
     246              : {
     247            5 :     switch (leftFinger) {
     248            1 :     case 0: return u"0";
     249            1 :     case 1: return u"1";
     250            1 :     case 2: return u"2";
     251            1 :     case 3: return u"3";
     252            1 :     case 4: return u"4";
     253            0 :     default: return u"invalid"; // shouldn't happen
     254              :     }
     255              : }
     256              : 
     257            5 : static String fingeringTextRH(int rightFinger)
     258              : {
     259            5 :     switch (rightFinger) {
     260            1 :     case 0: return u"p";
     261            1 :     case 1: return u"i";
     262            1 :     case 2: return u"m";
     263            1 :     case 3: return u"a";
     264            1 :     case 4: return u"c";
     265            0 :     default: return u"invalid"; // shouldn't happen
     266              :     }
     267              : }
     268              : 
     269          401 : static mu::engraving::Note* addNoteToChord(mu::engraving::Chord* chord, const TefNote* tefNote, int stringOffset, int pitch,
     270              :                                            muse::draw::Color color,
     271              :                                            std::vector<mu::engraving::Note*>& tiedNotes)
     272              : {
     273              :     LOGN("pitch %d", pitch);
     274          401 :     mu::engraving::Note* note = Factory::createNote(chord);
     275          401 :     if (note) {
     276          401 :         note->setTrack(chord->track());
     277          401 :         note->setPitch(pitch);
     278          401 :         note->setTpcFromPitch(Prefer::NEAREST);
     279          401 :         note->setFret(tefNote->fret);
     280          401 :         note->setString(tefNote->string - stringOffset - 1);
     281          401 :         note->setColor(color);
     282          401 :         if (tefNote->tie) {
     283            5 :             tiedNotes.push_back(note);
     284              :         }
     285          401 :         if (tefNote->fingeringLH) {
     286            5 :             Fingering* fi = Factory::createFingering(note, TextStyleType::LH_GUITAR_FINGERING);
     287            5 :             String finger { fingeringTextLH(tefNote->fingeringLH - 1) };
     288            5 :             fi->setPlainText(finger);
     289            5 :             note->add(fi);
     290            5 :         }
     291          401 :         if (tefNote->fingeringRH) {
     292            5 :             Fingering* fi = Factory::createFingering(note, TextStyleType::RH_GUITAR_FINGERING);
     293            5 :             String finger { fingeringTextRH(tefNote->fingeringRH - 1) };
     294            5 :             fi->setPlainText(finger);
     295            5 :             note->add(fi);
     296            5 :         }
     297          401 :         chord->add(note);
     298              :     }
     299          401 :     return note;
     300              : }
     301              : 
     302           13 : static void addGraceNotesToChord(mu::engraving::Chord* chord, int pitch, int fret, int string, muse::draw::Color color)
     303              : {
     304           13 :     mu::engraving::TDuration durationType(mu::engraving::DurationType::V_INVALID);
     305           13 :     const int ticks { 240 };
     306           13 :     durationType.setVal(ticks);
     307           13 :     mu::engraving::Chord* cr = Factory::createChord(chord->score()->dummy());
     308           13 :     cr->setTrack(chord->track());
     309           13 :     cr->setNoteType(mu::engraving::NoteType::APPOGGIATURA);
     310           13 :     cr->setDurationType(mu::engraving::DurationType::V_EIGHTH);
     311           13 :     cr->setTicks(durationType.fraction());
     312           13 :     mu::engraving::Note* note = Factory::createNote(cr);
     313           13 :     note->setTrack(chord->track());
     314              :     //xmlSetPitch(note, sao.s.toLatin1(), sao.a, sao.o);
     315           13 :     note->setPitch(pitch);
     316           13 :     note->setTpcFromPitch(Prefer::NEAREST);
     317           13 :     note->setFret(fret);
     318           13 :     note->setString(string);
     319           13 :     note->setColor(color);
     320           13 :     cr->add(note);
     321           13 :     chord->add(cr);
     322           13 : }
     323              : 
     324           29 : static void addRest(Segment* segment, track_idx_t track, TDuration tDuration, Fraction length, muse::draw::Color color, bool visible = true)
     325              : {
     326           29 :     mu::engraving::Rest* rest = Factory::createRest(segment);
     327           29 :     if (rest) {
     328           29 :         rest->setTrack(track);
     329           29 :         rest->setDurationType(tDuration);
     330           29 :         rest->setTicks(length);
     331           29 :         rest->setColor(color);
     332           29 :         rest->setVisible(visible);
     333           29 :         segment->add(rest);
     334              :     }
     335           29 : }
     336              : 
     337            4 : static void addVolta(Score* score, Measure* measure, const Ending& ending)
     338              : {
     339            4 :     constexpr track_idx_t voltaTrackIdx = 0;
     340              : 
     341            4 :     Measure* endMeasure = measure;
     342            5 :     for (int countdown = ending.duration - 1; countdown > 0; countdown--) {
     343            1 :         Measure* next = endMeasure->nextMeasure();
     344            1 :         if (!next) {
     345            0 :             LOGD() << "Ending at " << "TBD" << " specifies non-existent end measure.";
     346            0 :             break;
     347              :         }
     348            1 :         endMeasure = next;
     349              :     }
     350              : 
     351            4 :     Volta* volta = Factory::createVolta(score->dummy());
     352            4 :     volta->setTrack(voltaTrackIdx);
     353            4 :     volta->setTick(measure->tick());
     354            4 :     volta->setTick2(endMeasure->endTick());
     355            4 :     volta->setVisible(true);
     356            8 :     volta->setEndings({ ending.number });
     357            4 :     String text;
     358            4 :     text += String("%1").arg(ending.number);
     359            4 :     volta->setText(text);
     360            4 :     score->addElement(volta);
     361            4 : }
     362              : 
     363           61 : void TablEdit::createCapos()
     364              : {
     365          134 :     for (size_t part = 0; part < tefInstruments.size(); ++part) {
     366           73 :         int capo { tefInstruments.at(part).nCapo };
     367           73 :         if (capo > 0) {
     368            2 :             Measure* measure = score->firstMeasure();
     369            2 :             Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
     370            2 :             const size_t track = part * VOICES;
     371              : 
     372            2 :             CapoParams params;
     373            2 :             params.active = true;
     374            2 :             params.transposeMode = CapoParams::TransposeMode::TAB_ONLY;
     375            2 :             params.fretPosition = capo;
     376              : 
     377            2 :             Capo* capoEl = Factory::createCapo(score->dummy());
     378            2 :             capoEl->setTrack(track);
     379            2 :             capoEl->setParams(params);
     380            2 :             s->add(capoEl);
     381              : 
     382            2 :             Staff* staff = score->staff(part);
     383            2 :             staff->insertCapoParams({ 0, 1 }, params, true);
     384            2 :         }
     385              :     }
     386           61 : }
     387              : 
     388              : // string tuning contains higher values for lower sounding strings
     389              : // for guitar:
     390              : // E4 = 32 (MIDI 64)
     391              : // B3 = 37
     392              : // G3 = 41
     393              : // D3 = 46
     394              : // A2 = 51
     395              : // E2 = 56 (MIDI 40)
     396              : // thus to go from tuning value to MIDI pitch: pitch = 96 - tuning
     397              : // todo: check interaction with fMiddleC
     398              : 
     399          820 : static int stringTuningToMidi(int tuning)
     400              : {
     401          820 :     return 96 - tuning;
     402              : }
     403              : 
     404           61 : void TablEdit::createContents(const MeasureHandler& measureHandler)
     405              : {
     406           61 :     if (tefInstruments.size() == 0) {
     407            0 :         LOGD("error: no instruments");
     408            0 :         return;
     409              :     }
     410              : 
     411           61 :     std::vector<VoiceAllocator> voiceAllocators;
     412           61 :     initializeVoiceAllocators(voiceAllocators);
     413           61 :     allocateVoices(voiceAllocators);
     414              : 
     415          134 :     for (size_t part = 0; part < tefInstruments.size(); ++part) {
     416              :         LOGN("part %zu", part);
     417           73 :         std::vector<mu::engraving::Note*> tiedNotes;
     418          365 :         for (voice_idx_t voice = 0; voice < mu::engraving::VOICES; ++voice) {
     419              :             LOGN("- voice %zu", voice);
     420          292 :             auto& voiceContent { voiceAllocators.at(part).voiceContent(voice) };
     421          292 :             TupletHandler tupletHandler;
     422          674 :             for (size_t k = 0; k < voiceContent.size(); ++k) {
     423              :                 LOGN("  - chord %zu", k);
     424              :                 // tefNotes is either a rest or a chord of one or more notes
     425          382 :                 const std::vector<const TefNote*>& tefNotes { voiceContent.at(k) };
     426              : 
     427          382 :                 if (tefNotes.size() == 0) {
     428            0 :                     continue; // shouldn't happen
     429              :                 }
     430              : 
     431          382 :                 const TefNote* const firstNote { tefNotes.at(0) };
     432          382 :                 Fraction length { firstNote->length, 64 }; // length is in 64th
     433          382 :                 if (firstNote->dots == 1) {
     434           19 :                     length *= Fraction{ 3, 2 };
     435          363 :                 } else if (firstNote->dots == 2) {
     436            0 :                     length *= Fraction{ 7, 4 };
     437              :                 }
     438              : 
     439          382 :                 TDuration tDuration(length);
     440          382 :                 if (firstNote->dots) {
     441           19 :                     tDuration.setDots(firstNote->dots);
     442              :                 }
     443              : 
     444          382 :                 const auto idx { measureHandler.measureIndex(firstNote->position, tefMeasures) };
     445          382 :                 const Fraction gapCorrection { measureHandler.sumPreviousGaps(idx), 64 };
     446          382 :                 const auto positionCorrection = tupletHandler.doTuplet(firstNote);
     447              : 
     448          382 :                 Fraction tick { firstNote->position, 64 }; // position is in 64th
     449          382 :                 tick += positionCorrection;
     450          382 :                 tick -= gapCorrection;
     451              :                 LOGN("    positionCorrection %d/%d tick %d/%d length %d/%d",
     452              :                      positionCorrection.numerator(), positionCorrection.denominator(),
     453              :                      tick.numerator(), tick.denominator(),
     454              :                      length.numerator(), length.denominator()
     455              :                      );
     456              : 
     457          382 :                 Measure* measure { score->tick2measure(tick) };
     458          382 :                 if (!measure) {
     459            0 :                     LOGD("error: no measure");
     460            0 :                     continue;
     461              :                 } else {
     462              :                     LOGN("measure %p", measure);
     463              :                 }
     464          382 :                 Segment* segment { measure->getSegment(mu::engraving::SegmentType::ChordRest, tick) };
     465          382 :                 if (!segment) {
     466            0 :                     LOGD("error: no segment");
     467            0 :                     continue;
     468              :                 }
     469              : 
     470          382 :                 const auto track = part * VOICES + voice;
     471          382 :                 if (segment->element(track)) {
     472            0 :                     LOGD("segment not empty");
     473            0 :                     continue;
     474              :                 }
     475              : 
     476          382 :                 if (firstNote->rest) {
     477              :                     LOGN("    - rest position %d string %d fret %d", firstNote->position, firstNote->string, firstNote->fret);
     478           20 :                     addRest(segment, track, tDuration, length, toColor(voice));
     479              :                 } else {
     480              :                     LOGN("    - note(s) position %d string %d fret %d", firstNote->position, firstNote->string, firstNote->fret);
     481          362 :                     mu::engraving::Chord* chord { Factory::createChord(segment) };
     482          362 :                     if (chord) {
     483          362 :                         chord->setTrack(track);
     484          362 :                         chord->setDurationType(tDuration);
     485          362 :                         chord->setTicks(length);
     486          362 :                         segment->add(chord);
     487              : 
     488          362 :                         const TefInstrument& instrument { tefInstruments.at(part) };
     489          362 :                         if (instrument.stringNumber < 1 || 12 < instrument.stringNumber) {
     490            0 :                             LOGD("error: invalid instrument.stringNumber %d", instrument.stringNumber);
     491            0 :                             continue;
     492              :                         }
     493              : 
     494          763 :                         for (const auto note : tefNotes) {
     495          401 :                             const auto stringOffset = stringNumberPreviousParts(part);
     496          401 :                             const auto string { note->string - stringOffset - 1 }; // instrument-relative zero-based string number
     497          401 :                             int unFrettedPitch = stringTuningToMidi(instrument.tuning.at(string)); // + note->fret;
     498          401 :                             const int capo { instrument.nCapo };
     499          401 :                             if (capo > 0) {
     500           23 :                                 unFrettedPitch += capo;
     501              :                             }
     502          401 :                             const int frettedPitch { unFrettedPitch + note->fret };
     503              :                             LOGN("      -> string %d fret %d pitch %d", note->string, note->fret, frettedPitch);
     504              :                             // note TableEdit's strings start at 1, MuseScore's at 0
     505          401 :                             mu::engraving::Note* mn { addNoteToChord(chord, note, stringOffset, frettedPitch, toColor(voice), tiedNotes) };
     506          401 :                             if (note->hasGrace) {
     507           13 :                                 int gracePitch = unFrettedPitch + note->graceFret;
     508           13 :                                 addGraceNotesToChord(chord, gracePitch, note->graceFret, string, toColor(voice));
     509              :                             }
     510          401 :                             if (mn && (note->effect() != EffectType::NONE || note->combinationEffect() != EffectType::NONE)) {
     511           50 :                                 effectMap.insert({ note, mn });
     512              :                             }
     513              :                         }
     514          362 :                         tupletHandler.addCr(measure, chord);
     515              :                     }
     516              :                 }
     517              :             }
     518              :         }
     519           78 :         for (const auto note : tiedNotes) {
     520            5 :             connectTie(note->chord(), note);
     521              :         }
     522           73 :     }
     523           61 : }
     524              : 
     525              : // adapted copy of GPConverter::addContinuousSlideHammerOn()
     526              : 
     527           61 : static void addContinuousSlideHammerOn(Score* _score, const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
     528              : {
     529            6 :     auto searchEndNote = [] (Note* start) -> Note* {
     530              :         ChordRest* nextCr;
     531            6 :         if (toChord(start->parent())->ChordRest::isGrace()) {
     532              :             //! this case when start note is a grace note so end note can be next note in grace notes
     533              :             //! or parent note of grace notes
     534            0 :             Chord* startChord =  toChord(start->parent());
     535              : 
     536            0 :             Chord* parentGrace = toChord(start->parent()->parent());
     537              : 
     538            0 :             auto it = parentGrace->graceNotes().begin();
     539            0 :             for (; it != parentGrace->graceNotes().end(); ++it) {
     540            0 :                 if (*it == startChord) {
     541            0 :                     break;
     542              :                 }
     543              :             }
     544              : 
     545            0 :             if (it == parentGrace->graceNotes().end()) {
     546            0 :                 nextCr = nullptr;
     547            0 :             } else if (std::next(it) == parentGrace->graceNotes().end()) {
     548            0 :                 nextCr = parentGrace;
     549              :             } else {
     550            0 :                 nextCr = *(++it);
     551              :             }
     552              :         } else {
     553            6 :             Segment* nextSegment = start->chord()->segment()->next1();
     554            6 :             if (!nextSegment) {
     555            0 :                 return nullptr;
     556              :             }
     557            6 :             nextCr = nextSegment->nextChordRest(start->track());
     558            6 :             if (!nextCr) {
     559            0 :                 return nullptr;
     560              :             }
     561              : 
     562            6 :             if (nextCr->isChord() && !toChord(nextCr)->graceNotes().empty()) {
     563            0 :                 nextCr = toChord(nextCr)->graceNotes().front();
     564              :             }
     565              :         }
     566              : 
     567            6 :         if (!nextCr) {
     568            0 :             return nullptr;
     569              :         }
     570              : 
     571            6 :         if (!nextCr->isChord()) {
     572            0 :             return nullptr;
     573              :         }
     574            6 :         auto nextChord = toChord(nextCr);
     575            6 :         for (auto note : nextChord->notes()) {
     576            6 :             if (note->string() == start->string() && (note->harmonic() == start->harmonic())) {
     577            6 :                 return note;
     578              :             }
     579              :         }
     580              : 
     581            0 :         return nextChord->upNote();
     582              :     };
     583              : 
     584           61 :     std::unordered_map<Note*, HammerOnPullOff*> hammerOnPullOffs;
     585           61 :     std::unordered_set<Chord*> hammerOnInChord;
     586          111 :     for (const auto& slide : effectMap) {
     587           50 :         const TefNote* const tefNote { slide.first };
     588           50 :         Note* startNote = slide.second;
     589              :         LOGN("has effect: tefNote %p simple %d complex %d startNote %p tick %s track %zu",
     590              :              slide.first, tefNote->simpleEffect, tefNote->complexEffect,
     591              :              startNote, qPrintable(startNote->tick().toString().toQString()), startNote->track());
     592           98 :         if (!(tefNote->effect() == EffectType::HAMMER_ON
     593           48 :               || tefNote->effect() == EffectType::PULL_OFF
     594           47 :               || tefNote->combinationEffect() == EffectType::HAMMER_ON
     595           46 :               || tefNote->combinationEffect() == EffectType::PULL_OFF
     596           45 :               || tefNote->effect() == EffectType::SLIDE)) {
     597           44 :             continue;
     598              :         }
     599              : 
     600            6 :         Note* endNote = searchEndNote(startNote);
     601            6 :         if (!endNote || startNote->string() == -1 || startNote->fret() == endNote->fret()) {
     602              :             //mistake in GP: such kind od slides shouldn't exist
     603            0 :             continue;
     604              :         }
     605              : 
     606            6 :         Fraction startTick = startNote->chord()->tick();
     607            6 :         Fraction endTick = endNote->chord()->tick();
     608            6 :         track_idx_t track = startNote->track();
     609              : 
     610              :         /// Layout info
     611            6 :         if (tefNote->effect() == EffectType::SLIDE) {
     612            1 :             Glissando* gl = mu::engraving::Factory::createGlissando(startNote);
     613            1 :             gl->setStartElement(startNote);
     614            1 :             gl->setTrack(track);
     615            1 :             gl->setTick(startTick);
     616            1 :             gl->setTick2(endNote->chord()->tick());
     617            1 :             gl->setEndElement(endNote);
     618            1 :             gl->setText(u"Sl");
     619            1 :             gl->setGlissandoType(GlissandoType::STRAIGHT);
     620            1 :             gl->setGlissandoStyle(startNote->part()->instrument(startTick)->glissandoStyle());
     621            1 :             _score->addElement(gl);
     622              :         }
     623              : 
     624           10 :         if (tefNote->effect() == EffectType::HAMMER_ON || tefNote->effect() == EffectType::PULL_OFF
     625           10 :             || tefNote->combinationEffect() == EffectType::HAMMER_ON || tefNote->combinationEffect() == EffectType::PULL_OFF) {
     626            5 :             Chord* startChord = startNote->chord();
     627            5 :             if (hammerOnInChord.find(startChord) != hammerOnInChord.end()) {
     628            0 :                 continue;
     629              :             }
     630              : 
     631            5 :             if (hammerOnPullOffs.count(startNote) == 0) {
     632            5 :                 HammerOnPullOff* hammerOnPullOff = Factory::createHammerOnPullOff(_score->dummy());
     633            5 :                 hammerOnPullOff->setTrack(startNote->track());
     634            5 :                 hammerOnPullOff->setTick(startNote->tick());
     635            5 :                 hammerOnPullOff->setTick2(endNote->tick());
     636            5 :                 hammerOnPullOff->setStartElement(startChord);
     637            5 :                 hammerOnPullOff->setEndElement(endNote->chord());
     638            5 :                 _score->addElement(hammerOnPullOff);
     639            5 :                 hammerOnPullOffs[endNote] = hammerOnPullOff;
     640            5 :                 hammerOnInChord.insert(startChord);
     641              :             } else {
     642            0 :                 HammerOnPullOff* hammerOnPullOff = hammerOnPullOffs[startNote];
     643            0 :                 hammerOnPullOff->setTick2(endTick);
     644            0 :                 hammerOnPullOff->setEndElement(endNote->chord());
     645            0 :                 hammerOnPullOffs.erase(startNote);
     646            0 :                 hammerOnPullOffs[endNote] = hammerOnPullOff;
     647              :             }
     648              :         }
     649              :     }
     650           61 : }
     651              : 
     652           61 : static void addHarmonics(const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
     653              : {
     654           11 :     const auto addHarmonicText = [](Note* msNote, const char* text) {
     655           11 :         msNote->setHeadGroup(NoteHeadGroup::HEAD_DIAMOND);
     656           11 :         Segment* segment = msNote->chord()->segment();
     657           11 :         StaffText* staffText = Factory::createStaffText(segment);
     658           11 :         staffText->setPlainText(String { text });
     659           11 :         staffText->setTrack(msNote->chord()->track());
     660           11 :         segment->add(staffText);
     661           11 :     };
     662              : 
     663          111 :     for (const auto& slide : effectMap) {
     664           50 :         const TefNote* const tefNote { slide.first };
     665              : 
     666           50 :         Note* msNote = slide.second;
     667           50 :         if (tefNote->effect() == EffectType::NATURAL_HARMONIC
     668           50 :             || tefNote->combinationEffect() == EffectType::NATURAL_HARMONIC) {
     669            9 :             addHarmonicText(msNote, "N.H.");
     670              :         }
     671           50 :         if (tefNote->effect() == EffectType::ARTIFICIAL_HARMONIC
     672           50 :             || tefNote->combinationEffect() == EffectType::ARTIFICIAL_HARMONIC) {
     673            2 :             addHarmonicText(msNote, "A.H.");
     674              :         }
     675              :     }
     676           61 : }
     677              : 
     678              : // return all chords in the same staff as a given note
     679              : // ordered from lowest to highest track
     680              : 
     681           40 : static std::vector<Chord*> allChordsInStaff(const Note* const msNote)
     682              : {
     683           40 :     std::vector<Chord*> allChords;
     684           40 :     track_idx_t staffIdx { msNote->track() / VOICES };
     685           40 :     track_idx_t firstTrack { staffIdx* VOICES };
     686           40 :     Segment* segment { msNote->chord()->segment() };
     687          200 :     for (auto track = firstTrack; track < firstTrack + VOICES; ++track) {
     688          160 :         EngravingItem* element { segment->element(track) };
     689          160 :         if (element && element->isChord()) {
     690           74 :             Chord* chord { toChord(element) };
     691           74 :             allChords.push_back(chord);
     692              :         }
     693              :     }
     694           40 :     return allChords;
     695            0 : }
     696              : 
     697              : // arpeggios are down when they start at the note at the highest string
     698              : // (which has the lowest string number)
     699              : 
     700           20 : static bool isDown(const Note* const msNote)
     701              : {
     702           20 :     Note* noteOnHighestString { nullptr };
     703           57 :     for (const Chord* const chord : allChordsInStaff(msNote)) {
     704           96 :         for (Note* note : chord->notes()) {
     705           59 :             if (noteOnHighestString) {
     706           39 :                 if (note->string() < noteOnHighestString->string()) {
     707           22 :                     noteOnHighestString = note;
     708              :                 }
     709              :             } else {
     710           20 :                 noteOnHighestString = note;
     711              :             }
     712              :         }
     713           20 :     }
     714           20 :     return msNote == noteOnHighestString;
     715              : }
     716              : 
     717           20 : static void addArpeggio(const Note* const msNote)
     718              : {
     719           20 :     std::vector<Chord*> allChords { allChordsInStaff(msNote) };
     720           20 :     if (allChords.empty()) {
     721            0 :         return;
     722              :     }
     723           20 :     Chord* firstChord { allChords.at(0) };
     724           20 :     if (firstChord->arpeggio()) {
     725            0 :         return;
     726              :     }
     727              : 
     728           20 :     Arpeggio* a = Factory::createArpeggio(firstChord);
     729           20 :     track_idx_t firstChordIdx { firstChord->track() };
     730           20 :     track_idx_t lastChordIdx { allChords.at(allChords.size() - 1)->track() };
     731           20 :     int span { static_cast<int>(lastChordIdx - firstChordIdx + 1) };
     732           20 :     a->setSpan(span);
     733           20 :     a->setArpeggioType(isDown(msNote) ? ArpeggioType::DOWN : ArpeggioType::NORMAL);
     734           20 :     firstChord->add(a);
     735           20 : }
     736              : 
     737           61 : static void addSingleNoteEffects(Score* score, const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
     738              : {
     739          111 :     for (const auto& effect : effectMap) {
     740           50 :         const TefNote* const tefNote { effect.first };
     741           50 :         Note* msNote { effect.second };
     742           50 :         const EffectType primary { tefNote->effect() };
     743           50 :         const EffectType combination { tefNote->combinationEffect() };
     744              :         LOGN("note %p tick %d track %zu effect %d combination %d",
     745              :              msNote, msNote->tick().ticks(), msNote->track(), primary, combination);
     746           50 :         if (primary == EffectType::BRUSH
     747           48 :             || combination == EffectType::BRUSH
     748           47 :             || primary == EffectType::RASGUEADO
     749           45 :             || combination == EffectType::ROLL
     750           42 :             || primary == EffectType::ROLL_ARPEGGIO) {
     751           20 :             addArpeggio(msNote);
     752              :         }
     753           50 :         if (primary == EffectType::MUTED
     754           49 :             || primary == EffectType::DEAD_NOTE
     755           48 :             || combination == EffectType::DEAD_NOTE) {
     756              :             // do not distinguish between muted and dead note
     757              :             // current implementation results in "X" in both staves
     758            2 :             msNote->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
     759            2 :             msNote->setDeadNote(true);
     760              :         }
     761           50 :         if (primary == EffectType::RINGING_NOTE
     762           46 :             || combination == EffectType::RINGING_NOTE) {
     763            5 :             LetRing* lr = Factory::createLetRing(score->dummy());
     764            5 :             lr->setTrack(msNote->track());
     765            5 :             lr->setTick(msNote->tick());
     766            5 :             lr->setTick2(msNote->tick() + 2 * msNote->chord()->ticks());
     767            5 :             score->addSpanner(lr);
     768           50 :         } else if (primary == EffectType::STACCATO) {
     769            8 :             Chord* chord { toChord(msNote->parent()) };
     770            8 :             Articulation* na = Factory::createArticulation(chord);
     771            8 :             const SymId articSym { SymId::articStaccatoAbove };
     772            8 :             na->setSymId(articSym);
     773            8 :             if (!chord->hasArticulation(na)) {
     774            7 :                 chord->add(na);
     775              :             } else {
     776            1 :                 delete na;
     777              :             }
     778              :         }
     779              :     }
     780           61 : }
     781              : 
     782           61 : void TablEdit::createEffects()
     783              : {
     784           61 :     addSingleNoteEffects(score, effectMap);
     785           61 :     addContinuousSlideHammerOn(score, effectMap);
     786           61 :     addHarmonics(effectMap);
     787           61 : }
     788              : 
     789           61 : void TablEdit::createLinkedTabs()
     790              : {
     791           61 :     constexpr size_t stavesInPart = 2;
     792              : 
     793          134 :     for (Part* part : score->parts()) {
     794           73 :         part->setStaves(static_cast<int>(stavesInPart));
     795              : 
     796           73 :         Staff* srcStaff = part->staff(0);
     797           73 :         Staff* dstStaff = part->staff(1);
     798           73 :         Excerpt::cloneStaff(srcStaff, dstStaff, true);
     799              : 
     800              :         static const std::vector<StaffTypes> types {
     801              :             StaffTypes::TAB_4SIMPLE,
     802              :             StaffTypes::TAB_5SIMPLE,
     803              :             StaffTypes::TAB_6SIMPLE,
     804              :             StaffTypes::TAB_7SIMPLE,
     805              :             StaffTypes::TAB_8SIMPLE,
     806              :             StaffTypes::TAB_9SIMPLE,
     807              :             StaffTypes::TAB_10SIMPLE,
     808           75 :         };
     809              : 
     810           73 :         Fraction fr = Fraction(0, 1);
     811           73 :         size_t lines = part->instrument()->stringData()->strings();
     812           73 :         size_t index = (lines >= 4 && lines <= 10) ? lines - 4 : 2;
     813              : 
     814           73 :         dstStaff->setStaffType(fr, *StaffType::preset(types.at(index)));
     815           73 :         dstStaff->setLines(fr, static_cast<int>(lines));
     816              :     }
     817           61 : }
     818              : 
     819          141 : static Fraction reducedActualLength(const int actual, const int nominalDenominator)
     820              : {
     821          141 :     Fraction res { actual, 64 };
     822          705 :     while (res.denominator() >= 2 * nominalDenominator && res.numerator() % 2 == 0) {
     823          564 :         res.setNumerator(res.numerator() / 2);
     824          564 :         res.setDenominator(res.denominator() / 2);
     825              :     }
     826              :     LOGN("actual %d nominalDenominator %d res %d/%d", actual, nominalDenominator, res.numerator(), res.denominator());
     827          141 :     return res;
     828              : }
     829              : 
     830           61 : void TablEdit::createMeasures(const MeasureHandler& measureHandler)
     831              : {
     832           61 :     int lastKey { 0 };               // safe default
     833           61 :     Fraction lastTimeSig { -1, -1 }; // impossible value
     834           61 :     Fraction tick { 0, 1 };
     835          202 :     for (size_t idx = 0; idx < tefMeasures.size(); ++idx) {
     836          141 :         TefMeasure& tefMeasure { tefMeasures.at(idx) };
     837              :         // create measure
     838          141 :         auto measure = Factory::createMeasure(score);
     839          141 :         measure->setTick(tick);
     840          141 :         Fraction nominalLength{ tefMeasure.numerator, tefMeasure.denominator };
     841          141 :         Fraction actualLength{ reducedActualLength(measureHandler.actualSize(tefMeasures, idx), tefMeasure.denominator) };
     842          141 :         measure->setTimesig(nominalLength);
     843          141 :         measure->setTicks(actualLength);
     844          141 :         measure->setEndBarLineType(BarLineType::NORMAL, 0);
     845              :         LOGN("measure %p tick %d/%d nominalLength %d/%d actualLength %d/%d",
     846              :              measure,
     847              :              tick.numerator(), tick.denominator(),
     848              :              nominalLength.numerator(), nominalLength.denominator(),
     849              :              actualLength.numerator(), actualLength.denominator()
     850              :              );
     851          141 :         score->measures()->add(measure);
     852              : 
     853          141 :         if (tick == Fraction { 0, 1 }) {
     854           61 :             auto s1 = measure->getSegment(mu::engraving::SegmentType::KeySig, tick);
     855          134 :             for (size_t i = 0; i < tefInstruments.size(); ++i) {
     856           73 :                 mu::engraving::KeySig* keysig = Factory::createKeySig(s1);
     857           73 :                 keysig->setKey(Key(tefMeasure.key));
     858           73 :                 keysig->setTrack(i * VOICES);
     859           73 :                 s1->add(keysig);
     860              :             }
     861           61 :             lastKey = tefMeasure.key;
     862              : 
     863           61 :             auto s2 = measure->getSegment(mu::engraving::SegmentType::TimeSig, tick);
     864          134 :             for (size_t i = 0; i < tefInstruments.size(); ++i) {
     865           73 :                 mu::engraving::TimeSig* timesig = Factory::createTimeSig(s2);
     866           73 :                 timesig->setSig(nominalLength);
     867           73 :                 timesig->setTrack(i * VOICES);
     868           73 :                 s2->add(timesig);
     869              :             }
     870           61 :             lastTimeSig = nominalLength;
     871           61 :             createTempo();
     872              :         } else {
     873           80 :             if (tefMeasure.key != lastKey) {
     874            6 :                 auto s1 = measure->getSegment(mu::engraving::SegmentType::KeySig, tick);
     875           15 :                 for (size_t i = 0; i < tefInstruments.size(); ++i) {
     876            9 :                     mu::engraving::KeySig* keysig = Factory::createKeySig(s1);
     877            9 :                     keysig->setKey(Key(tefMeasure.key));
     878            9 :                     keysig->setTrack(i * VOICES);
     879            9 :                     s1->add(keysig);
     880              :                 }
     881            6 :                 lastKey = tefMeasure.key;
     882              :             }
     883           80 :             if (nominalLength != lastTimeSig) {
     884            3 :                 auto s2 = measure->getSegment(mu::engraving::SegmentType::TimeSig, tick);
     885            7 :                 for (size_t i = 0; i < tefInstruments.size(); ++i) {
     886            4 :                     mu::engraving::TimeSig* timesig = Factory::createTimeSig(s2);
     887            4 :                     timesig->setSig(nominalLength);
     888            4 :                     timesig->setTrack(i * VOICES);
     889            4 :                     s2->add(timesig);
     890              :                 }
     891            3 :                 lastTimeSig = nominalLength;
     892              :             }
     893              :         }
     894              : 
     895          141 :         tick += actualLength;
     896              :     }
     897           61 :     score->updateTicksAndTimeSigMap();
     898           61 : }
     899              : 
     900           61 : void TablEdit::createNotesFrame()
     901              : {
     902           61 :     if (!tefHeader.notes.empty()) {
     903            1 :         TBox* tbox = Factory::createTBox(score);
     904            1 :         tbox->setTick(score->endTick());
     905            1 :         score->measures()->add(tbox);
     906            1 :         tbox->text()->setPlainText(muse::String::fromStdString(tefHeader.notes));
     907              :     }
     908           61 : }
     909              : 
     910           61 : void TablEdit::createParts()
     911              : {
     912          134 :     for (const auto& instrument : tefInstruments) {
     913           73 :         Part* part = new Part(score);
     914           73 :         score->appendPart(part);
     915           73 :         muse::String staffName { muse::String::fromStdString(instrument.name) };
     916           73 :         part->setPlainLongName(staffName);
     917              : 
     918           73 :         StringData stringData;
     919           73 :         stringData.setFrets(25); // reasonable default (?)
     920          492 :         for (int i = 0; i < instrument.stringNumber; ++i) {
     921          419 :             int pitch = stringTuningToMidi(instrument.tuning.at(instrument.stringNumber - i - 1));
     922              :             LOGN("pitch %d", pitch);
     923          419 :             instrString str { pitch };
     924          419 :             stringData.stringList().push_back(str);
     925              :         }
     926           73 :         part->instrument()->setStringData(stringData);
     927              : 
     928           73 :         part->setMidiProgram(instrument.midiVoice);
     929           73 :         part->setMidiChannel(instrument.midiBank);
     930              : 
     931           73 :         Staff* staff = Factory::createStaff(part);
     932           73 :         ClefTypeList clefTypeList { ClefType::G8_VB, ClefType::G8_VB };
     933           73 :         staff->setDefaultClefType(clefTypeList);
     934              : 
     935           73 :         score->appendStaff(staff);
     936           73 :     }
     937           61 : }
     938              : 
     939           61 : void TablEdit::createProperties()
     940              : {
     941           61 :     if (!tefHeader.title.empty()) {
     942           24 :         score->setMetaTag(u"workTitle", muse::String::fromStdString(tefHeader.title));
     943              :     }
     944           61 :     if (!tefHeader.subTitle.empty()) {
     945            2 :         score->setMetaTag(u"subtitle", muse::String::fromStdString(tefHeader.subTitle));
     946              :     }
     947           61 :     if (!tefHeader.comment.empty()) {
     948           54 :         score->setMetaTag(u"comment", muse::String::fromStdString(tefHeader.comment));
     949              :     }
     950           61 :     if (!tefHeader.internetLink.empty()) {
     951            1 :         score->setMetaTag(u"source", muse::String::fromStdString(tefHeader.internetLink));
     952              :     }
     953           61 :     if (!tefHeader.copyright.empty()) {
     954            1 :         score->setMetaTag(u"copyright", muse::String::fromStdString(tefHeader.copyright));
     955              :     }
     956           61 : }
     957              : 
     958              : // convert the reading list into repeat and (todo) codas, segnos and voltas
     959              : 
     960           61 : void TablEdit::createRepeats()
     961              : {
     962              :     LOGN("reading list size %zu number of measures %zu", tefReadingList.size(), tefMeasures.size());
     963           61 :     ReadingList readingList;
     964           61 :     readingList.calculate(tefMeasures.size(), tefReadingList);
     965          202 :     for (size_t i = 0; i < tefMeasures.size(); ++i) {
     966          141 :         Measure* m { score->crMeasure(static_cast<int>(i)) };
     967          141 :         m->setRepeatStart(readingList.status().at(i).repeatStart);
     968          141 :         m->setRepeatEnd(readingList.status().at(i).repeatEnd);
     969          141 :         const int repeatCount { readingList.status().at(i).repeatCount };
     970          141 :         if (repeatCount > 2) {
     971            2 :             m->setRepeatCount(repeatCount);
     972              :             // generate default play count text
     973            2 :             Segment* segment = m->getSegment(SegmentType::EndBarLine, m->tick() + m->ticks());
     974            2 :             PlayCountText* playCountText = Factory::createPlayCountText(segment);
     975            2 :             playCountText->setTrack(0); // todo check multi staff / part handling
     976            2 :             segment->add(playCountText);
     977              :         }
     978          141 :         if (const std::optional<Ending>& ending = readingList.status().at(i).ending) {
     979            4 :             addVolta(score, m, ending.value());
     980              :         }
     981              :     }
     982           61 :     if (readingList.status().back().barlineEnd) {
     983            7 :         Measure* m { score->crMeasure(static_cast<int>(tefMeasures.size()) - 1) };
     984            7 :         m->setEndBarLineType(BarLineType::END, 0);
     985              :     }
     986           61 : }
     987              : 
     988           61 : static void setInstrumentIDs(const std::vector<Part*>& parts)
     989              : {
     990          134 :     for (Part* part : parts) {
     991          146 :         for (const auto& pair : part->instruments()) {
     992           73 :             pair.second->updateInstrumentId();
     993              :         }
     994              :     }
     995           61 : }
     996              : 
     997              : //---------------------------------------------------------
     998              : //   fillGap
     999              : //---------------------------------------------------------
    1000              : 
    1001              : // Fill one gap (tstart - tend) in this track in this measure with rest(s).
    1002              : 
    1003            9 : static void fillGap(Measure* measure, track_idx_t track, const Fraction& tstart, const Fraction& tend)
    1004              : {
    1005            9 :     Fraction ctick = tstart;
    1006            9 :     Fraction restLen = tend - tstart;
    1007              :     LOGN("measure %p track %zu tstart %d tend %d restLen %d len",
    1008              :          measure, track, tstart.ticks(), tend.ticks(), restLen.ticks());
    1009            9 :     auto durList = toDurationList(restLen, true);
    1010              :     LOGN("durList.size %zu", durList.size());
    1011           18 :     for (const auto& dur : durList) {
    1012              :         LOGN("type %d dots %d fraction %d/%d", static_cast<int>(dur.type()), dur.dots(), dur.fraction().numerator(),
    1013              :              dur.fraction().denominator());
    1014            9 :         Segment* s = measure->getSegment(SegmentType::ChordRest, ctick);
    1015            9 :         addRest(s, track, dur, dur.fraction(), muse::draw::Color::BLACK, false);
    1016            9 :         ctick += dur.fraction();
    1017              :     }
    1018            9 : }
    1019              : 
    1020              : //---------------------------------------------------------
    1021              : //   fillGapsInFirstVoices
    1022              : //---------------------------------------------------------
    1023              : 
    1024              : // Fill gaps in first voice of every staff in this measure for this part with rest(s).
    1025              : 
    1026           61 : static void fillGapsInFirstVoices(MasterScore* score)
    1027              : {
    1028           61 :     IF_ASSERT_FAILED(score) {
    1029            0 :         return;
    1030              :     }
    1031              : 
    1032          134 :     for (staff_idx_t idx = 0; idx < score->nstaves(); ++idx) {
    1033          237 :         for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
    1034          164 :             Fraction measTick     = measure->tick();
    1035          164 :             Fraction measLen      = measure->ticks();
    1036          164 :             Fraction nextMeasTick = measTick + measLen;
    1037              :             LOGN("measure %p idx %zu tick %d - %d (len %d)",
    1038              :                  measure, idx, measTick.ticks(), nextMeasTick.ticks(), measLen.ticks());
    1039          164 :             track_idx_t track = idx * VOICES;
    1040          164 :             Fraction endOfLastCR = measTick;
    1041          834 :             for (Segment* s = measure->first(); s; s = s->next()) {
    1042          670 :                 EngravingItem* el = s->element(track);
    1043          670 :                 if (el) {
    1044          620 :                     if (s->isChordRestType()) {
    1045          320 :                         ChordRest* cr  = toChordRest(el);
    1046          320 :                         Fraction crTick     = cr->tick();
    1047          320 :                         Fraction crLen      = cr->globalTicks();
    1048          320 :                         if (crTick > endOfLastCR) {
    1049            6 :                             fillGap(measure, track, endOfLastCR, crTick);
    1050              :                         }
    1051          320 :                         endOfLastCR = crTick + crLen;
    1052              :                     }
    1053              :                 }
    1054              :             }
    1055          164 :             if (nextMeasTick > endOfLastCR) {
    1056            3 :                 fillGap(measure, track, endOfLastCR, nextMeasTick);
    1057              :             }
    1058              :         }
    1059              :     }
    1060              : }
    1061              : 
    1062           61 : void TablEdit::createScore()
    1063              : {
    1064           61 :     MeasureHandler measureHandler;
    1065           61 :     measureHandler.calculate(tefContents, tefMeasures);
    1066           61 :     createProperties();
    1067           61 :     createParts();
    1068           61 :     createTitleFrame();
    1069           61 :     createMeasures(measureHandler);
    1070           61 :     createCapos();
    1071           61 :     createNotesFrame();
    1072           61 :     createContents(measureHandler);
    1073           61 :     createEffects();
    1074           61 :     fillGapsInFirstVoices(score);
    1075           61 :     createRepeats();
    1076           61 :     createTexts();
    1077           61 :     createLinkedTabs();
    1078           61 :     setInstrumentIDs(score->parts());
    1079           61 : }
    1080              : 
    1081           61 : void TablEdit::createTempo()
    1082              : {
    1083           61 :     mu::engraving::Measure* measure = score->firstMeasure();
    1084           61 :     mu::engraving::Segment* segment = measure->getSegment(mu::engraving::SegmentType::ChordRest, mu::engraving::Fraction(0, 1));
    1085           61 :     mu::engraving::TempoText* tt = new mu::engraving::TempoText(segment);
    1086           61 :     tt->setTempo(double(tefHeader.tempo) / 60.0);
    1087           61 :     tt->setTrack(0);
    1088           61 :     tt->setFollowText(true);
    1089           61 :     muse::String tempoText = mu::engraving::TempoText::duration2tempoTextString(mu::engraving::DurationType::V_QUARTER);
    1090           61 :     tempoText += u" = ";
    1091           61 :     tempoText += muse::String::number(tefHeader.tempo);
    1092           61 :     tt->setXmlText(tempoText);
    1093           61 :     segment->add(tt);
    1094           61 : }
    1095              : 
    1096           61 : void TablEdit::createTexts()
    1097              : {
    1098          111 :     for (const auto& textMarker : tefTextMarkers) {
    1099              :         LOGN("position %d string %d text marker %d", textMarker.position, textMarker.string, textMarker.index);
    1100           50 :         bool ok { true };
    1101           50 :         const auto part = partIdx(textMarker.string, ok);
    1102           50 :         if (!ok) {
    1103            0 :             LOGD("error: invalid string %d", textMarker.string);
    1104            1 :             continue;
    1105              :         }
    1106           50 :         const auto track = part * VOICES;
    1107              :         LOGN("part %zu track %zu", part, track);
    1108              : 
    1109           50 :         Fraction tick { textMarker.position, 64 }; // position is in 64th
    1110           50 :         Measure* measure { score->tick2measure(tick) };
    1111           50 :         if (!measure) {
    1112            0 :             LOGD("error: no measure");
    1113            0 :             continue;
    1114              :         } else {
    1115              :             LOGN("measure %p", measure);
    1116              :         }
    1117           50 :         Segment* segment { measure->getSegment(mu::engraving::SegmentType::ChordRest, tick) };
    1118           50 :         if (!segment) {
    1119            0 :             LOGD("error: no segment");
    1120            0 :             continue;
    1121              :         }
    1122           50 :         if (textMarker.index < 0 || textMarker.index >= static_cast<int>(tefTexts.size())) {
    1123            1 :             LOGE() << "text marker index invalid";
    1124            1 :             continue;
    1125              :         }
    1126           49 :         StaffText* staffText = Factory::createStaffText(segment);
    1127           49 :         muse::String text { tefTexts.at(textMarker.index).c_str() };
    1128           49 :         staffText->setPlainText(text);
    1129           49 :         staffText->setTrack(track);
    1130           49 :         segment->add(staffText);
    1131           49 :     }
    1132           61 : }
    1133              : 
    1134           61 : void TablEdit::createTitleFrame()
    1135              : {
    1136           61 :     VBox* vbox = Factory::createTitleVBox(score);
    1137           61 :     vbox->setTick(mu::engraving::Fraction(0, 1));
    1138           61 :     score->measures()->add(vbox);
    1139           61 :     if (!tefHeader.title.empty()) {
    1140           24 :         Text* s = Factory::createText(vbox, TextStyleType::TITLE);
    1141           24 :         s->setPlainText(muse::String::fromStdString(tefHeader.title));
    1142           24 :         vbox->add(s);
    1143              :     }
    1144           61 :     if (!tefHeader.subTitle.empty()) {
    1145            2 :         Text* s = Factory::createText(vbox, TextStyleType::SUBTITLE);
    1146            2 :         s->setPlainText(muse::String::fromStdString(tefHeader.subTitle));
    1147            2 :         vbox->add(s);
    1148              :     }
    1149           61 : }
    1150              : 
    1151              : /*
    1152              :  * encoding of note durations
    1153              :  * 0 whole
    1154              :  * 1 half dotted
    1155              :  * 2 whole triplet
    1156              :  * 3 half
    1157              :  * 4 quarter dotted
    1158              :  * 5 half triplet
    1159              :  * 6 quarter
    1160              :  * 7 eight dotted
    1161              :  * 8 quarter triplet
    1162              :  * ...
    1163              :  * 18 1/64th note
    1164              :  * 19 half double dotted
    1165              :  * 22 quarter double dotted
    1166              :  * 25 eighth double dotted
    1167              :  * 28 16th double dotted
    1168              :  */
    1169              : 
    1170              : // return TablEdit note length in 64th
    1171              : // (including triplets rounded down to nearest note length)
    1172              : // TODO: remove code duplication with voiceallocator.cpp durationToInt()
    1173              : 
    1174          421 : static int duration2length(const int duration)
    1175              : {
    1176          421 :     if (0 <= duration && duration <= 18) {
    1177              :         // remove dot and triplet
    1178          421 :         int noteType { 0 };
    1179          421 :         int dotOrTriplet { duration % 3 };
    1180          421 :         switch (dotOrTriplet) {
    1181          392 :         case 0: noteType = duration / 3;
    1182          392 :             break;
    1183           19 :         case 1: noteType = (duration + 2) / 3;
    1184           19 :             break;
    1185           10 :         case 2: noteType = (duration - 2) / 3;
    1186           10 :             break;
    1187            0 :         default: LOGD("impossible value %d", dotOrTriplet);
    1188              :         }
    1189          421 :         switch (noteType) {
    1190           93 :         case 0: return 64; // 1/1
    1191          104 :         case 1: return 32; // 1/2
    1192          190 :         case 2: return 16; // 1/4
    1193           20 :         case 3: return 8;  // 1/8
    1194            4 :         case 4: return 4;  // 1/16
    1195            6 :         case 5: return 2;  // 1/32
    1196            4 :         case 6: return 1;  // 1/64
    1197              :         // as long as the if statement above is correct, this cannot actually happen
    1198            0 :         default: LOGD("impossible value %d", dotOrTriplet);
    1199              :         }
    1200            0 :     } else {
    1201            0 :         switch (duration) {
    1202            0 :         case 19: return 32; // 1/2
    1203            0 :         case 22: return 16; // 1/4
    1204            0 :         case 25: return 8;  // 1/8
    1205            0 :         case 28: return 4;  // 1/16
    1206            0 :         default: LOGD("impossible value %d", duration);
    1207              :         }
    1208              :     }
    1209            0 :     LOGD("invalid note duration %d", duration);
    1210            0 :     return 0; // invalid (result is weird layout)
    1211              : }
    1212              : 
    1213              : // return the number of dots
    1214              : // TODO: remove code duplication with voiceallocator.cpp durationToInt()
    1215              : 
    1216          421 : static int duration2dots(const int duration)
    1217              : {
    1218          421 :     if (0 <= duration && duration <= 18 && (duration % 3) == 0) {
    1219          392 :         return 0;
    1220           29 :     } else if (0 <= duration && duration <= 18 && (duration % 3) == 1) {
    1221           19 :         return 1;
    1222           10 :     } else if (duration == 19 || duration == 22 || duration == 25 || duration == 28) {
    1223            0 :         return 2;
    1224              :     }
    1225           10 :     LOGD("invalid note duration %d", duration);
    1226           10 :     return 0; // invalid
    1227              : }
    1228              : 
    1229              : // check if 3:2 tuplet
    1230              : 
    1231          421 : static bool duration2triplet(const int duration)
    1232              : {
    1233          421 :     if (0 <= duration && duration <= 18) {
    1234          421 :         return duration % 3 == 2;
    1235              :     } else {
    1236            0 :         LOGD("invalid note duration %d", duration);
    1237            0 :         return false; // invalid
    1238              :     }
    1239              : }
    1240              : 
    1241          421 : Voice extractVoice(const uint8_t byte3)
    1242              : {
    1243          421 :     switch ((byte3 & 0x30) / 0x10) {
    1244          388 :     case 0: return Voice::DEFAULT;
    1245           18 :     case 2: return Voice::UPPER;
    1246           15 :     case 3: return Voice::LOWER;
    1247            0 :     default: LOGE("unknown voice");
    1248            0 :         return Voice::DEFAULT;
    1249              :     }
    1250              : }
    1251              : 
    1252           61 : void TablEdit::readTefContents()
    1253              : {
    1254              :     // calculate the total number of strings
    1255              :     // instruments must have been read before reading contents
    1256           61 :     if (tefInstruments.empty()) {
    1257            0 :         LOGD("no instruments");
    1258            0 :         return;
    1259              :     }
    1260              : 
    1261           61 :     int totalNumberOfStrings { 0 };
    1262          134 :     for (const auto& instrument : tefInstruments) {
    1263           73 :         totalNumberOfStrings += instrument.stringNumber;
    1264              :     }
    1265              :     LOGN("totalNumberOfStrings %d", totalNumberOfStrings);
    1266           61 :     if (totalNumberOfStrings < 1) {
    1267            0 :         LOGE() << "totalNumberOfStrings invalid";
    1268            0 :         return;
    1269              :     }
    1270              : 
    1271           61 :     _file->seek(OFFSET_CONTENTS);
    1272           61 :     uint32_t position = readUInt32();
    1273           61 :     _file->seek(position);
    1274           61 :     uint32_t offset = readUInt32();
    1275              :     LOGN("position %d offset %d", position, offset);
    1276          533 :     while (offset != 0xFFFFFFFF) {
    1277          472 :         uint8_t byte1 = readUInt8();
    1278          472 :         uint8_t byte2 = readUInt8();
    1279          472 :         uint8_t byte3 = readUInt8();
    1280          472 :         uint8_t byte4 = readUInt8();
    1281          472 :         uint8_t byte5 = readUInt8();
    1282          472 :         /* uint8_t byte6 = */ readUInt8();
    1283          472 :         uint8_t byte7 = readUInt8();
    1284          472 :         /* uint8_t byte8 = */ readUInt8();
    1285          472 :         TefNote note;
    1286          472 :         note.position = (offset >> 3) / totalNumberOfStrings;
    1287          472 :         const auto noteRestMarker = byte1 & 0x3F;
    1288          472 :         if (noteRestMarker < 0x33) {
    1289          401 :             note.string = ((offset >> 3) % totalNumberOfStrings) + 1;
    1290          401 :             note.fret = noteRestMarker - 1;
    1291           71 :         } else if (noteRestMarker == 0x33) {
    1292           20 :             note.string = ((offset >> 3) % totalNumberOfStrings) + 1;
    1293           20 :             note.rest = true;
    1294              :         } else {
    1295              :             // not a note or rest
    1296              :             //LOGD("marker %d duration %d length %d dots %d", noteRestMarker, note.duration, note.length, note.dots);
    1297              :         }
    1298          472 :         if (noteRestMarker <= 0x33) {
    1299          421 :             note.duration = byte2 & 0x1F;
    1300          421 :             note.length = duration2length(note.duration);
    1301          421 :             note.dots = duration2dots(note.duration);
    1302          421 :             note.triplet = duration2triplet(note.duration);
    1303          421 :             note.voice = extractVoice(byte3);
    1304          421 :             note.tie = (byte2 / 0x20) == 7;
    1305          421 :             if (byte1 & 0x40) {
    1306           13 :                 note.graceEffect = byte4 / 0x20;
    1307           13 :                 note.graceFret = byte4 & 0x1F;
    1308           13 :                 note.hasGrace = true;
    1309              :                 //LOGD("graceEffect %d graceFret %d", note.graceEffect, note.graceFret);
    1310              :             }
    1311          421 :             note.fingeringLH = (byte7 & 0x1F) % 6;
    1312          421 :             note.fingeringRH = (byte7 & 0x1F) / 6;
    1313              :             LOGN("fingeringLH %d fingeringRH %d", note.fingeringLH, note.fingeringRH);
    1314          421 :             note.simpleEffect = byte3 & 0x0F;
    1315          421 :             note.complexEffect = byte5;
    1316              :             LOGN("simpleEffect %d complexEffect %d", note.simpleEffect, note.complexEffect);
    1317          421 :             tefContents.push_back(note);
    1318           51 :         } else if (noteRestMarker == 0x39) {
    1319           50 :             TefTextMarker tefTextMarker;
    1320           50 :             tefTextMarker.position = (offset >> 3) / totalNumberOfStrings;
    1321           50 :             tefTextMarker.string = ((offset >> 3) % totalNumberOfStrings) + 1;
    1322           50 :             tefTextMarker.index = static_cast<int>((byte3 << 8) + byte2);
    1323              :             LOGN("text marker %d", tefTextMarker.index);
    1324           50 :             tefTextMarkers.push_back(tefTextMarker);
    1325              :         } else if (noteRestMarker == 0x3D) {
    1326              :             LOGN("connection");
    1327              :         } else {
    1328              :             LOGN("not supported %d", noteRestMarker);
    1329              :         }
    1330          472 :         offset = readUInt32();
    1331              :     }
    1332              : }
    1333              : 
    1334           61 : void TablEdit::readTefInstruments()
    1335              : {
    1336           61 :     _file->seek(OFFSET_INSTRUMENTS);
    1337           61 :     uint32_t position = readUInt32();
    1338           61 :     _file->seek(position);
    1339           61 :     uint16_t structSize = readUInt16();
    1340           61 :     uint16_t numberOfInstruments = readUInt16();
    1341              :     //uint32_t zero = readUInt16();
    1342              :     LOGN("structSize %d numberOfInstruments %d", structSize, numberOfInstruments);
    1343          134 :     for (uint16_t i = 0; i < numberOfInstruments; ++i) {
    1344           73 :         TefInstrument instrument;
    1345           73 :         instrument.stringNumber = readUInt16();
    1346           73 :         instrument.firstString = readUInt16();
    1347           73 :         instrument.available16U = readUInt16();
    1348           73 :         instrument.verticalSpacing = readUInt16();
    1349           73 :         instrument.midiVoice = readUInt8();
    1350           73 :         instrument.midiBank = readUInt8();
    1351           73 :         instrument.nBanjo5 = readUInt8();
    1352           73 :         instrument.uSpec = readUInt8();
    1353           73 :         instrument.nCapo = readUInt16();
    1354           73 :         instrument.fMiddleC = readUInt8();
    1355           73 :         instrument.fClef = readUInt8();
    1356           73 :         instrument.output = readUInt16();
    1357           73 :         instrument.options = readUInt16();
    1358          949 :         for (uint16_t j = 0; j < 12; ++j) {
    1359          876 :             int n = readUInt8();
    1360          876 :             n += instrument.nCapo;
    1361          876 :             instrument.tuning[j] = n;
    1362              :         }
    1363              :         // name is a zero-terminated utf8 string
    1364           73 :         bool atEnd = false;
    1365         2701 :         for (uint16_t j = 0; j < 36; ++j) {
    1366         2628 :             auto c = readUInt8();
    1367         2628 :             if (c == 0) {
    1368              :                 // stop appending, but continue reading
    1369         1040 :                 atEnd = true;
    1370              :             }
    1371         2628 :             if (0x20 <= c && c <= 0x7E && !atEnd) {
    1372          988 :                 instrument.name += static_cast<char>(c);
    1373              :             }
    1374              :         }
    1375           73 :         tefInstruments.push_back(instrument);
    1376           73 :     }
    1377           61 : }
    1378              : 
    1379           61 : void TablEdit::readTefMeasures()
    1380              : {
    1381           61 :     _file->seek(OFFSET_MEASURES);
    1382           61 :     uint32_t position = readUInt32();
    1383           61 :     _file->seek(position);
    1384           61 :     /* uint16_t structSize = */ readUInt16();
    1385           61 :     uint16_t numberOfMeasures = readUInt16();
    1386           61 :     /* uint32_t zero = */ readUInt32();
    1387          203 :     for (uint16_t i = 0; i < numberOfMeasures; ++i) {
    1388          142 :         TefMeasure measure;
    1389          142 :         measure.flag = readUInt8();
    1390          142 :         measure.isPickup = measure.flag & 0x08;
    1391          142 :         /* uint8_t uTmp = */ readUInt8();
    1392          142 :         measure.key = readInt8();
    1393          142 :         measure.size = readUInt8();
    1394          142 :         const int denom = readUInt8();
    1395          142 :         if (denom < 1) {
    1396            1 :             LOGE() << "Error reading measure - corrupt denominator";
    1397            1 :             readUInt8(); // numerator
    1398            1 :             readUInt16(); // margins
    1399            1 :             continue;
    1400              :         }
    1401          141 :         measure.denominator = denom;
    1402          141 :         measure.numerator = readUInt8();
    1403          141 :         /* uint16_t margins = */ readUInt16();
    1404          141 :         tefMeasures.push_back(measure);
    1405              :     }
    1406           61 : }
    1407              : 
    1408           61 : void TablEdit::readTefReadingList()
    1409              : {
    1410           61 :     _file->seek(OFFSET_READINGLIST);
    1411           61 :     uint32_t position = readUInt32();
    1412           61 :     if (!position) {
    1413           48 :         return;
    1414              :     }
    1415              : 
    1416           13 :     _file->seek(position);
    1417           13 :     uint16_t structSize = readUInt16();
    1418           13 :     uint16_t numberOfItems = readUInt16();
    1419           44 :     for (uint16_t i = 0; i < numberOfItems; ++i) {
    1420           31 :         TefReadingListItem item;
    1421           31 :         item.firstMeasure = readUInt16();
    1422           31 :         item.lastMeasure = readUInt16();
    1423              :         // skip the label
    1424          899 :         for (uint16_t j = 4; j < structSize; ++j) {
    1425          868 :             readUInt8();
    1426              :         }
    1427           31 :         tefReadingList.push_back(item);
    1428           31 :     }
    1429              : }
    1430              : 
    1431           61 : void TablEdit::readTefTexts()
    1432              : {
    1433           61 :     _file->seek(OFFSET_TEXTS);
    1434           61 :     uint32_t position = readUInt32();
    1435           61 :     if (!position) {
    1436           50 :         return;
    1437              :     }
    1438              : 
    1439           11 :     _file->seek(position);
    1440           11 :     uint16_t numberOfTexts = readUInt16();
    1441           47 :     for (uint16_t i = 0; i < numberOfTexts; ++i) {
    1442           36 :         std::string text { readUtf8Text() };
    1443              :         LOGN("i %d text '%s'", i, text.c_str());
    1444           36 :         tefTexts.push_back(text);
    1445           36 :     }
    1446              : }
    1447              : 
    1448           61 : void TablEdit::readTefHeader()
    1449              : {
    1450           61 :     readUInt16(); // skip private01
    1451           61 :     tefHeader.version = readUInt16();
    1452           61 :     tefHeader.subVersion = readUInt16();
    1453           61 :     tefHeader.tempo = readUInt16();
    1454           61 :     tefHeader.chorus = readUInt16();
    1455           61 :     tefHeader.reverb = readUInt16();
    1456           61 :     readUInt16(); // todo syncope (is signed)
    1457           61 :     readUInt16(); // skip private02
    1458           61 :     tefHeader.securityCode = readUInt32();
    1459           61 :     tefHeader.securityFlags = readUInt32();
    1460           61 :     _file->seek(OFFSET_TBED);
    1461           61 :     tefHeader.tbed = readUInt32();
    1462           61 :     readUInt32(); // skip contents
    1463           61 :     auto titlePtr = readUInt32();
    1464              :     LOGN("titlePtr %d", titlePtr);
    1465           61 :     _file->seek(titlePtr);
    1466           61 :     tefHeader.title = readUtf8TextIndirect(OFFSET_TITLE);
    1467           61 :     tefHeader.subTitle = readUtf8TextIndirect(OFFSET_SUBTITLE);
    1468           61 :     tefHeader.comment = readUtf8TextIndirect(OFFSET_COMMENT);
    1469           61 :     tefHeader.notes = readUtf8TextIndirect(OFFSET_NOTES);
    1470           61 :     tefHeader.internetLink = readUtf8TextIndirect(OFFSET_INTERNETLINK);
    1471           61 :     tefHeader.copyright = readUtf8TextIndirect(OFFSET_COPYRIGHT);
    1472           61 :     _file->seek(OFFSET_OLDNUM);
    1473           61 :     tefHeader.wOldNum = readUInt16();
    1474           61 :     tefHeader.wFormat = readUInt16();
    1475           61 : }
    1476              : 
    1477              : //---------------------------------------------------------
    1478              : //   import
    1479              : //
    1480              : //   see src/importexport/guitarpro/internal/importgtp.cpp createLinkedTabs()
    1481              : //   for linked staves
    1482              : //---------------------------------------------------------
    1483              : 
    1484           61 : Err TablEdit::import()
    1485              : {
    1486           61 :     readTefHeader();
    1487              :     LOGN("version %d subversion %d", tefHeader.version, tefHeader.subVersion);
    1488              :     LOGN("tempo %d chorus %d reverb %d", tefHeader.tempo, tefHeader.chorus, tefHeader.reverb);
    1489              :     LOGN("securityCode %d securityFlags %d", tefHeader.securityCode, tefHeader.securityFlags);
    1490              :     LOGN("title '%s'", tefHeader.title.c_str());
    1491              :     LOGN("subTitle '%s'", tefHeader.subTitle.c_str());
    1492              :     LOGN("comment '%s'", tefHeader.comment.c_str());
    1493              :     LOGN("notes '%s'", tefHeader.notes.c_str());
    1494              :     LOGN("internetLink '%s'", tefHeader.internetLink.c_str());
    1495              :     LOGN("copyright '%s'", tefHeader.copyright.c_str());
    1496              :     LOGN("tbed %d wOldNum %d wFormat %d", tefHeader.tbed, tefHeader.wOldNum, tefHeader.wFormat);
    1497           61 :     if ((tefHeader.wFormat >> 8) < 10) {
    1498            0 :         return Err::FileBadFormat;
    1499              :         //return Err::FileTooOld; // TODO: message is too specific for MuseScore format
    1500              :     }
    1501           61 :     if ((tefHeader.wFormat >> 8) > 10) {
    1502            0 :         return Err::FileBadFormat;
    1503              :         //return Err::FileTooNew;
    1504              :     }
    1505           61 :     if (tefHeader.securityCode != 0) {
    1506            0 :         return Err::FileBadFormat; // todo "file is protected" message ?
    1507              :     }
    1508           61 :     readTefTexts();
    1509           97 :     for (const auto& text : tefTexts) {
    1510              :         LOGN("text: '%s'", text.c_str());
    1511              :     }
    1512           61 :     readTefMeasures();
    1513          202 :     for (const auto& measure : tefMeasures) {
    1514              :         LOGN("flag %d key %d size %d numerator %d denominator %d",
    1515              :              measure.flag, measure.key, measure.size, measure.numerator, measure.denominator);
    1516              :     }
    1517           61 :     readTefInstruments();
    1518          134 :     for (const auto& instrument : tefInstruments) {
    1519              :         LOGN("stringNumber %d firstString %d midiVoice %d midiBank %d",
    1520              :              instrument.stringNumber, instrument.firstString, instrument.midiVoice, instrument.midiBank);
    1521              :     }
    1522           61 :     readTefReadingList();
    1523           92 :     for (const auto& item : tefReadingList) {
    1524              :         LOGN("firstMeasure %d lastMeasure %d", item.firstMeasure, item.lastMeasure);
    1525              :     }
    1526           61 :     readTefContents();
    1527          482 :     for (const auto& note : tefContents) {
    1528              :         LOGN("position %d rest %d string %d fret %d duration %d length %d dots %d tie %d triplet %d voice %d",
    1529              :              note.position, note.rest, note.string, note.fret,
    1530              :              note.duration, note.length, note.dots,
    1531              :              note.tie, note.triplet, static_cast<int>(note.voice));
    1532              :     }
    1533          111 :     for (const auto& textMarker : tefTextMarkers) {
    1534              :         LOGN("position %d string %d text marker %d", textMarker.position, textMarker.string, textMarker.index);
    1535              :     }
    1536           61 :     createScore();
    1537           61 :     return Err::NoError;
    1538              : }
    1539              : } // namespace mu::iex::tabledit
        

Generated by: LCOV version 2.0-1