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

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "importgtp.h"
      24              : 
      25              : #include "serialization/xmldom.h"
      26              : 
      27              : #include "gtp/gp6dombuilder.h"
      28              : #include "gtp/gpconverter.h"
      29              : 
      30              : #include "engraving/dom/arpeggio.h"
      31              : #include "engraving/dom/articulation.h"
      32              : #include "engraving/dom/barline.h"
      33              : #include "engraving/dom/box.h"
      34              : #include "engraving/dom/bracket.h"
      35              : #include "engraving/dom/bracketitem.h"
      36              : #include "engraving/dom/chord.h"
      37              : #include "engraving/dom/clef.h"
      38              : #include "engraving/dom/dynamic.h"
      39              : #include "engraving/dom/excerpt.h"
      40              : #include "engraving/dom/factory.h"
      41              : #include "engraving/dom/fingering.h"
      42              : #include "engraving/dom/fret.h"
      43              : #include "engraving/dom/glissando.h"
      44              : #include "engraving/dom/hairpin.h"
      45              : #include "engraving/dom/harmony.h"
      46              : #include "engraving/dom/instrtemplate.h"
      47              : #include "engraving/dom/keysig.h"
      48              : #include "engraving/dom/lyrics.h"
      49              : #include "engraving/dom/marker.h"
      50              : #include "engraving/dom/masterscore.h"
      51              : #include "engraving/dom/measure.h"
      52              : #include "engraving/dom/measurebase.h"
      53              : #include "engraving/dom/measurerepeat.h"
      54              : #include "engraving/dom/note.h"
      55              : #include "engraving/dom/notedot.h"
      56              : #include "engraving/dom/ottava.h"
      57              : #include "engraving/dom/part.h"
      58              : #include "engraving/dom/rehearsalmark.h"
      59              : #include "engraving/dom/rest.h"
      60              : #include "engraving/dom/segment.h"
      61              : #include "engraving/dom/slur.h"
      62              : #include "engraving/dom/staff.h"
      63              : #include "engraving/dom/stafftext.h"
      64              : #include "engraving/dom/stafftype.h"
      65              : #include "engraving/dom/stringdata.h"
      66              : #include "engraving/dom/tempotext.h"
      67              : #include "engraving/dom/text.h"
      68              : #include "engraving/dom/textline.h"
      69              : #include "engraving/dom/tie.h"
      70              : #include "engraving/dom/timesig.h"
      71              : #include "engraving/dom/tremolobar.h"
      72              : #include "engraving/dom/tuplet.h"
      73              : #include "engraving/dom/volta.h"
      74              : #include "engraving/dom/stringtunings.h"
      75              : 
      76              : #include "engraving/types/symid.h"
      77              : 
      78              : #include "log.h"
      79              : 
      80              : using namespace mu;
      81              : using namespace muse;
      82              : using namespace muse::io;
      83              : using namespace mu::engraving;
      84              : 
      85              : namespace mu::iex::guitarpro {
      86              : const std::map<std::string, std::string> GuitarPro6::instrumentMapping = {
      87              :     { "2Mrcs",           "maracas" },
      88              :     { "a-bass4",         "acoustic-bass" },
      89              :     { "a-bass5",         "acoustic-bass" },
      90              :     { "a-bass6",         "acoustic-bass" },
      91              :     { "alt-c",           "alto" },
      92              :     { "alt-s",           "alto" },
      93              :     { "a-piano-gs",      "piano" },
      94              :     { "a-piano-ss",      "piano" },
      95              :     { "bass-c",          "bass" },
      96              :     { "bass-flt-c",      "bass-flute" },
      97              :     { "bassn",           "bassoon" },
      98              :     { "bass-s",          "bass" },
      99              :     { "basstuba-eb",     "bass-eb-tuba" },
     100              :     { "bnj4",            "banjo" },
     101              :     { "bnj5",            "banjo" },
     102              :     { "bnj6",            "banjo" },
     103              :     { "bongo",           "bongos" },
     104              :     { "brthns",          "baritone-horn" },
     105              :     { "brtn-c",          "baritone" },
     106              :     { "brtn-s",          "baritone" },
     107              :     { "cbs",             "cabasa" },
     108              :     { "cello",           "violoncello" },
     109              :     { "china",           "chinese-tom-toms" },
     110              :     { "clrnt-a",         "a-clarinet" },
     111              :     { "clrnt-bb-bass",   "bass-clarinet" },
     112              :     { "clrnt-bb",        "bb-clarinet" },
     113              :     { "clrnt-c",         "c-clarinet" },
     114              :     { "clrnt-d",         "d-clarinet" },
     115              :     { "clrnt-eb",        "eb-clarinet" },
     116              :     { "clrnt",           "clarinet" },
     117              :     { "clst-gs",         "celesta" },
     118              :     { "clst-ss",         "celesta" },
     119              :     { "clvs",            "claves" },
     120              :     { "cngKit",          "congas" },
     121              :     { "conga",           "congas" },
     122              :     { "cowbell",         "cowbell" },
     123              :     { "crash",           "crash-cymbal" },
     124              :     { "cstnt",           "castanets" },
     125              :     { "ctbassn",         "contrabassoon" },
     126              :     { "ctbass",          "contrabass" },
     127              :     { "cuicaKit",        "cuica" },
     128              :     { "cuica",           "cuica" },
     129              :     { "drmkt",           "drumset" },
     130              :     { "e-bass4",         "bass-guitar" },
     131              :     { "e-bass5",         "bass-guitar" },
     132              :     { "e-bass6",         "bass-guitar" },
     133              :     { "e-gtr12",         "electric-guitar-treble-clef" },
     134              :     { "e-gtr6",          "electric-guitar-treble-clef" },
     135              :     { "e-gtr7",          "electric-guitar-treble-clef" },
     136              :     { "e-gtr8",          "electric-guitar-treble-clef" },
     137              :     { "em-organ-gs",     "organ" },
     138              :     { "em-organ-ss",     "organ" },
     139              :     { "en-horn",         "english-horn" },
     140              :     { "e-piano-gs",      "electric-piano" },
     141              :     { "e-piano-ss",      "electric-piano" },
     142              :     { "flt-c",           "flute" },
     143              :     { "flt-g",           "alto-flute" },
     144              :     { "flt-whstl",       "tin-whistle" },
     145              :     { "fr-horn",         "horn" },
     146              :     { "grcss",           "bass-drum" },         //grancassa is an alternative name for bass drum
     147              :     { "guiro",           "guiro" },
     148              :     { "harp-gs",         "harp" },
     149              :     { "harp-ss",         "harp" },
     150              :     { "hclap",           "hand-clap" },
     151              :     { "hihat",           "hi-hat" },
     152              :     { "hrpch-gs",        "harpsichord" },
     153              :     { "hrpch-ss",        "harpsichord" },
     154              :     { "jngl-bell",       "sleigh-bells" },
     155              :     { "klmb",            "kalimba" },
     156              :     { "mrcs",            "maracas" },
     157              :     { "n-gtr6",          "guitar-nylon-treble-clef" },
     158              :     { "n-gtr7",          "guitar-nylon-treble-clef" },
     159              :     { "n-gtr8",          "guitar-nylon-treble-clef" },
     160              :     { "oboe",            "oboe" },
     161              :     { "ocrn",            "ocarina" },
     162              :     { "pccl",            "piccolo" },
     163              :     { "pedalhihat",      "hi-hat" },
     164              :     { "pnflt",           "pan-flute" },
     165              :     { "ptt",             "cymbal" },        //piatti is cymbal in italian
     166              :     { "rec",             "recorder" },
     167              :     { "ride",            "ride-cymbal" },
     168              :     { "rvs-cymb",        "cymbal" },
     169              :     { "sax-alt-eb",      "alto-saxophone" },
     170              :     { "sax-bar-eb",      "baritone-saxophone" },
     171              :     { "sax-bass-eb",     "bass-saxophone" },
     172              :     { "sax-ms-f",        "mezzo-soprano-saxophone" },
     173              :     { "sax-sop-bb",      "soprano-saxophone" },
     174              :     { "sax-ten-bb",      "tenor-saxophone" },
     175              :     { "sax-ten-c",       "melody-saxophone" },
     176              :     { "s-bass4",         "electric-bass" },
     177              :     { "s-bass5",         "5-string-electric-bass" },
     178              :     { "s-gtr12",         "guitar-steel-treble-clef" },
     179              :     { "s-gtr6",          "guitar-steel-treble-clef" },
     180              :     { "s-gtr7",          "guitar-steel-treble-clef" },
     181              :     { "s-gtr8",          "guitar-steel-treble-clef" },
     182              :     { "shkr",            "percussion" },
     183              :     { "shmsn",           "shamisen" },
     184              :     { "shn",             "sheng" },
     185              :     { "snare",           "snare-drum" },
     186              :     { "snr",             "snare-drum" },
     187              :     { "snt-brass-gs",    "brass-synthesizer" },
     188              :     { "snt-brass-ss",    "brass-synthesizer" },
     189              :     //{"snt-key-gs",    ""},
     190              :     //{"snt-key-ss",    ""},
     191              :     //{"snt-seq-gs",    ""},
     192              :     //{"snt-seq-ss",    ""},
     193              :     { "snt-lead-gs",     "poly-synth" },
     194              :     { "snt-lead-ss",     "poly-synth" },
     195              :     { "snt-pad",         "pad-synth" },
     196              :     { "snt-pad-gs",      "pad-synth" },
     197              :     { "snt-pad-ss",      "pad-synth" },
     198              :     { "splash",          "splash-cymbal" },
     199              :     { "sprn-c",          "soprano" },
     200              :     { "sprn-s",          "soprano" },
     201              :     { "tmbrn",           "tambourine" },       // to be mapped
     202              :     { "Tambourine-Perc", "tambourine" },
     203              :     { "Tambourine",      "tambourine" },
     204              :     { "tmblKit",         "timbales" },
     205              :     { "tmbl",            "timbales" },
     206              :     { "tmpn",            "timpani" },
     207              :     { "tnklbll",         "tubular-bells" },       //The short form does not match but this is very likely due to the description
     208              :     { "tnr-c",           "tenor" },
     209              :     { "tnr-s",           "tenor" },
     210              :     { "Triangle-Percu",  "triangle" },
     211              :     { "trmbn-bb-bass",   "bass-trombone" },
     212              :     { "trmbn-bb",        "tenor-trombone" },
     213              :     { "trmbn-bb-treble", "trombone-treble" },
     214              :     { "trmbn-eb",        "alto-trombone" },
     215              :     { "trmpt-a",         "a-trumpet" },
     216              :     { "trmpt-bb",        "bb-trumpet" },
     217              :     { "trmpt-c-bass",    "c-bass-trumpet" },
     218              :     { "trmpt-c",         "c-trumpet" },
     219              :     { "trmpt-d",         "d-trumpet" },
     220              :     { "trmpt-eb-bass",   "eb-bass-trumpet" },
     221              :     { "trmpt-eb",        "eb-trumpet" },
     222              :     { "trmpt-e",         "e-trumpet" },
     223              :     { "trmpt-f",         "f-trumpet" },
     224              :     { "trmpt-flgh",      "flugelhorn" },
     225              :     { "trngl",           "triangle" },
     226              :     { "ukll4",           "ukulele" },
     227              :     { "vbrphn",          "vibraphone" },
     228              :     { "vbrslp",          "vibraslap" },       // to be mapped
     229              :     { "vla",             "viola" },
     230              :     { "vln",             "violin" },
     231              :     { "wdblckKit",       "wood-blocks" },
     232              :     { "wdblck",          "wood-blocks" },
     233              :     { "whstlKit",        "slide-whistle" },
     234              :     { "whstl",           "tin-whistle" },
     235              :     { "xlphn",           "xylophone" }
     236              : };
     237              : 
     238           76 : std::unique_ptr<IGPDomBuilder> GuitarPro6::createGPDomBuilder() const
     239              : {
     240           76 :     return std::make_unique<GP6DomBuilder>();
     241              : }
     242              : 
     243              : //---------------------------------------------------------
     244              : //   readBit
     245              : //---------------------------------------------------------
     246              : 
     247     43939534 : int GuitarPro6::readBit(ByteArray* buffer)
     248              : {
     249              :     // calculate the byte index by dividing the position in bits by the bits per byte
     250     43939534 :     size_t byteIndex = position / BITS_IN_BYTE;
     251              : 
     252              :     // calculate our offset so we know how much to bit shift
     253     43939534 :     int byteOffset = ((BITS_IN_BYTE - 1) - (position % BITS_IN_BYTE));
     254              : 
     255              :     // calculate the bit which we want to read
     256     43939534 :     char byte = (byteIndex < buffer->size()) ? (*buffer)[byteIndex] : char(0);
     257     43939534 :     int bit = (((byte & 0xff) >> byteOffset) & 0x01);
     258              : 
     259              :     // increment our current position so we know this bit has been read
     260     43939534 :     position++;
     261     43939534 :     return bit;         // return the bit we calculated
     262              : }
     263              : 
     264              : //---------------------------------------------------------
     265              : //   readBits
     266              : //---------------------------------------------------------
     267              : 
     268     12791590 : int GuitarPro6::readBits(ByteArray* buffer, int bitsToRead)
     269              : {
     270     12791590 :     int bits = 0;
     271     31483764 :     for (int i = (bitsToRead - 1); i >= 0; i--) {
     272     18692174 :         bits |= (readBit(buffer) << i);
     273              :     }
     274     12791590 :     return bits;
     275              : }
     276              : 
     277              : //---------------------------------------------------------
     278              : //   readBitsReversed
     279              : //---------------------------------------------------------
     280              : 
     281     12000258 : int GuitarPro6::readBitsReversed(ByteArray* buffer, int bitsToRead)
     282              : {
     283     12000258 :     int bits = 0;
     284     37247618 :     for (int i = 0; i < bitsToRead; i++) {
     285     25247360 :         bits |= readBit(buffer) << i;
     286              :     }
     287     12000258 :     return bits;
     288              : }
     289              : 
     290              : //---------------------------------------------------------
     291              : //   getBytes
     292              : //---------------------------------------------------------
     293              : 
     294         1113 : ByteArray GuitarPro6::getBytes(ByteArray* buffer, int offset, int length)
     295              : {
     296         1113 :     ByteArray newBytes;
     297              :     // compute new bytes from our buffer and return byte array
     298      4836382 :     for (int i = 0; i < length; i++) {
     299      4835269 :         if (static_cast<int>(buffer->size()) > offset + i) {
     300      4835260 :             newBytes.insert(i, ((*buffer)[offset + i]));
     301              :         }
     302              :     }
     303         1113 :     return newBytes;
     304            0 : }
     305              : 
     306              : //---------------------------------------------------------
     307              : //   readInteger
     308              : //---------------------------------------------------------
     309              : 
     310         2175 : int GuitarPro6::readInteger(ByteArray* buffer, int offset)
     311              : {
     312              :     // assign four bytes and take them from the buffer
     313              :     char bytes[4];
     314         2175 :     bytes[0] = (*buffer)[offset + 0];
     315         2175 :     bytes[1] = (*buffer)[offset + 1];
     316         2175 :     bytes[2] = (*buffer)[offset + 2];
     317         2175 :     bytes[3] = (*buffer)[offset + 3];
     318              :     // increment positioning so we keep track of where we are
     319         2175 :     position += sizeof(int) * BITS_IN_BYTE;
     320              :     // bit shift in order to compute our integer value and return
     321         2175 :     return ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff);
     322              : }
     323              : 
     324              : //---------------------------------------------------------
     325              : //   readString
     326              : //---------------------------------------------------------
     327              : 
     328          379 : ByteArray GuitarPro6::readString(ByteArray* buffer, int offset, int length)
     329              : {
     330          379 :     ByteArray filename;
     331              :     // compute the string by iterating through the buffer
     332         5691 :     for (int i = 0; i < length; i++) {
     333         5691 :         int charValue = (((*buffer)[offset + i]) & 0xff);
     334         5691 :         if (charValue == 0) {
     335          379 :             break;
     336              :         }
     337         5312 :         filename.push_back((char)charValue);
     338              :     }
     339          379 :     return filename;
     340            0 : }
     341              : 
     342              : //---------------------------------------------------------
     343              : //   unhandledNode
     344              : //---------------------------------------------------------
     345              : 
     346            0 : void GuitarPro6::unhandledNode(String nodeName)
     347              : {
     348            0 :     LOGD() << "WARNING: Discovered unhandled node name" << nodeName;
     349            0 : }
     350              : 
     351              : //---------------------------------------------------------
     352              : //   getNode
     353              : //---------------------------------------------------------
     354              : 
     355            0 : XmlDomNode GuitarPro6::getNode(const String& id, XmlDomNode currentDomNode)
     356              : {
     357            0 :     while (!(currentDomNode).isNull()) {
     358            0 :         String currentId = currentDomNode.toElement().attribute("id").value();
     359            0 :         if (currentId == id) {
     360            0 :             return currentDomNode;
     361              :         }
     362            0 :         currentDomNode = currentDomNode.nextSibling();
     363            0 :     }
     364            0 :     LOGD() << "WARNING: A null node was returned when search for the identifier" << id << ". Your Guitar Pro file may be corrupted.";
     365            0 :     return currentDomNode;
     366              : }
     367              : 
     368              : //---------------------------------------------------------
     369              : //   readGpif
     370              : //---------------------------------------------------------
     371              : 
     372          221 : void GuitarPro6::readGpif(ByteArray* data)
     373              : {
     374          221 :     XmlDomDocument domDoc;
     375          221 :     domDoc.setContent(*data);
     376          221 :     XmlDomElement domElem = domDoc.rootElement();
     377              : 
     378          221 :     auto builder = createGPDomBuilder();
     379          221 :     builder->buildGPDomModel(&domElem);
     380              : 
     381          221 :     GPConverter scoreBuilder(score, builder->getGPDomModel(), iocContext());
     382          221 :     scoreBuilder.convertGP();
     383          221 : }
     384              : 
     385              : //---------------------------------------------------------
     386              : //   parseFile
     387              : //---------------------------------------------------------
     388              : 
     389          379 : void GuitarPro6::parseFile(const char* filename, ByteArray* data)
     390              : {
     391              :     // test to check if we are dealing with the score
     392          379 :     if (!strcmp(filename, "score.gpif")) {
     393           76 :         readGpif(data);
     394              :     }
     395          379 : }
     396              : 
     397              : //---------------------------------------------------------
     398              : //   readGPX
     399              : //---------------------------------------------------------
     400              : 
     401          152 : void GuitarPro6::readGPX(ByteArray* buffer)
     402              : {
     403              :     // start by reading the file header. It will tell us if the byte array is compressed.
     404          152 :     int fileHeader = readInteger(buffer, 0);
     405              : 
     406          152 :     if (fileHeader == GPX_HEADER_COMPRESSED) {
     407              :         // this is  a compressed file.
     408           76 :         int length             = readInteger(buffer, position / BITS_IN_BYTE);
     409           76 :         ByteArray bcfsBuffer;
     410           76 :         bcfsBuffer.reserve(length);
     411           76 :         int positionCounter    = 0;
     412     11879914 :         while ((position / BITS_IN_BYTE) < length) {
     413              :             // read the bit indicating compression information
     414     11879838 :             int flag = readBits(buffer, 1);
     415              : 
     416     11879838 :             if (flag) {
     417       120420 :                 int bits = readBits(buffer, 4);
     418       120420 :                 int offs = readBitsReversed(buffer, bits);
     419       120420 :                 int size = readBitsReversed(buffer, bits);
     420              : 
     421       120420 :                 int pos = (static_cast<int>(bcfsBuffer.size()) - offs);
     422      4822171 :                 for (int i = 0; i < (size > offs ? offs : size); i++) {
     423      4701751 :                     bcfsBuffer.insert(positionCounter, bcfsBuffer[pos + i]);
     424      4701751 :                     positionCounter++;
     425              :                 }
     426              :             } else {
     427     11759418 :                 int size = readBitsReversed(buffer, 2);
     428     12550750 :                 for (int i = 0; i < size; i++) {
     429       791332 :                     bcfsBuffer.insert(positionCounter, readBits(buffer, 8));
     430       791332 :                     positionCounter++;
     431              :                 }
     432              :             }
     433              :         }
     434              :         // recurse on the decompressed file stored as a byte array
     435           76 :         readGPX(&bcfsBuffer);
     436          152 :     } else if (fileHeader == GPX_HEADER_UNCOMPRESSED) {
     437              :         // this is an uncompressed file - strip the header off
     438           76 :         *buffer = buffer->right(buffer->size() - sizeof(int));
     439           76 :         size_t sectorSize = 0x1000;
     440           76 :         int offset        = 0;
     441          607 :         while ((offset = (offset + static_cast<int>(sectorSize))) + 3 < static_cast<int>(buffer->size())) {
     442          455 :             int newInt = readInteger(buffer, offset);
     443          455 :             if (newInt == 2) {
     444          379 :                 int indexFileName = (offset + 4);
     445          379 :                 int indexFileSize = (offset + 0x8C);
     446          379 :                 int indexOfBlock  = (offset + 0x94);
     447              : 
     448              :                 // create a byte array and put information about files found in it
     449          379 :                 int block             = 0;
     450          379 :                 int blockCount        = 0;
     451          379 :                 ByteArray* fileBytes = new ByteArray();
     452         1113 :                 while ((block = (readInteger(buffer, (indexOfBlock + (4 * (blockCount++)))))) != 0) {
     453          734 :                     fileBytes->push_back(getBytes(buffer, (offset = (block * static_cast<int>(sectorSize))), static_cast<int>(sectorSize)));
     454              :                 }
     455              :                 // get file information and read the file
     456          379 :                 size_t fileSize = readInteger(buffer, indexFileSize);
     457          379 :                 if (fileBytes->size() >= fileSize) {
     458          379 :                     ByteArray filenameBytes = readString(buffer, indexFileName, 127);
     459          379 :                     const char* filename = filenameBytes.constChar();
     460          379 :                     ByteArray data = getBytes(fileBytes, 0, static_cast<int>(fileSize));
     461          379 :                     parseFile(filename, &data);
     462          379 :                 }
     463          379 :                 delete fileBytes;
     464              :             }
     465              :         }
     466              :     }
     467          152 : }
     468              : 
     469              : //---------------------------------------------------------
     470              : //   read
     471              : //---------------------------------------------------------
     472              : 
     473           76 : bool GuitarPro6::read(IODevice* io)
     474              : {
     475           76 :     f = io;
     476           76 :     previousTempo = -1;
     477              :     // decompress and read files contained within GPX file
     478           76 :     ByteArray ba = io->readAll();
     479              : 
     480           76 :     readGPX(&ba);
     481              : 
     482           76 :     return true;
     483           76 : }
     484              : } // namespace mu::iex::guitarpro
        

Generated by: LCOV version 2.0-1