//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2014 John Pirie
//  Parts of the GPX Import based on code contributed by J.Jørgen von Bargen
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================
 
#include "importgtp.h"
#include "globals.h"
#include "libmscore/score.h"
#include "libmscore/measurebase.h"
#include "libmscore/text.h"
#include "libmscore/stafftext.h"
#include "libmscore/box.h"
#include "libmscore/staff.h"
#include "libmscore/part.h"
#include "libmscore/measure.h"
#include "libmscore/timesig.h"
#include "libmscore/tremolo.h"
#include "libmscore/rest.h"
#include "libmscore/chord.h"
#include "libmscore/note.h"
#include "libmscore/stringdata.h"
#include "libmscore/clef.h"
#include "libmscore/lyrics.h"
#include "libmscore/tempotext.h"
#include "libmscore/slur.h"
#include "libmscore/tie.h"
#include "libmscore/tuplet.h"
#include "libmscore/barline.h"
#include "libmscore/excerpt.h"
#include "libmscore/stafftype.h"
#include "libmscore/bracket.h"
#include "libmscore/articulation.h"
#include "libmscore/keysig.h"
#include "libmscore/harmony.h"
#include "libmscore/bend.h"
#include "libmscore/tremolobar.h"
#include "libmscore/segment.h"
#include "libmscore/rehearsalmark.h"
#include "libmscore/repeat.h"
#include "libmscore/dynamic.h"
#include "libmscore/arpeggio.h"
#include "libmscore/volta.h"
#include "libmscore/instrtemplate.h"
#include "libmscore/hairpin.h"
#include "libmscore/fingering.h"
#include "libmscore/sym.h"
#include "libmscore/ottava.h"
#include "libmscore/marker.h"
#include "libmscore/notedot.h"
#include "libmscore/sym.h"
#include "libmscore/bracketItem.h"
#include "libmscore/textline.h"
#include "preferences.h"
 
namespace Ms {
 
const static std::map<QString, QString> instrumentMapping = {
            {"a-bass4", "acoustic-bass"},
            {"a-bass5", "acoustic-bass"},
            {"a-bass6", "acoustic-bass"},
            {"alt-c",   "alto"},
            {"alt-s",   "alto"},
            {"a-piano-gs", "piano"},
            {"a-piano-ss", "piano"},
            {"bass-c",     "bass"},
            {"bass-flt-c", "bass-flute"},
            {"bassn",      "bassoon"},
            {"bass-s",     "bass"},
            {"basstuba-bb",     "bass-eb-tuba"},
            {"basstuba-eb",     "bb-tuba"},  //???
            {"bnj4",     "banjo"},
            {"bnj5",     "banjo"},
            {"bnj6",     "banjo"},
            {"cello",    "violoncello"},
            {"drmkt", "drumset"},
            {"e-bass4", "bass-guitar"},
            {"e-bass5", "bass-guitar"},
            {"e-bass6", "bass-guitar"},
            {"e-gtr12", "electric-guitar-treble-clef"},
            {"e-gtr6", "electric-guitar-treble-clef"},
            {"e-gtr7", "electric-guitar-treble-clef"},
            {"e-gtr8", "electric-guitar-treble-clef"},
            {"em-organ-gs", "organ"},
            {"em-organ-ss", "organ"},
            {"en-horn", "english-horn"},
            {"e-piano-gs", "electric-piano"},
            {"e-piano-ss", "electric-piano"},
            //{"f-bass5", "bass-guitar"},  /// ??? not in the list
            {"hrpch-gs", "harpsichord"},
            {"hrpch-ss", "harpsichord"},
            {"mrcs", "maracas"},
            {"mrcs", "oboe"},
            {"mrcs", "oboe"},
            {"n-gtr6", "guitar-nylon-treble-clef"},
            {"n-gtr7", "guitar-nylon-treble-clef"},
            {"n-gtr8", "guitar-nylon-treble-clef"},
            {"s-gtr12", "guitar-steel-treble-clef"},
            {"s-gtr6", "guitar-steel-treble-clef"},
            {"s-gtr7", "guitar-steel-treble-clef"},
            {"s-gtr8", "guitar-steel-treble-clef"},
            {"snt-lead-ss", "poly-synth"},
            {"tmbrn",  "tambourine"}, // to be mapped
            {"tnr-s",  "voice"},
            {"snt-bass-ss", "metallic-synth"},
            {"vbrslp", "vibraslap"}, // to be mapped
            {"vla", "viola"},
            {"vln", "violin"},
            {"xlphn", "xylophone"}
            };
 
//---------------------------------------------------------
//   readBit
//---------------------------------------------------------
 
int GuitarPro6::readBit() {
      // calculate the byte index by dividing the position in bits by the bits per byte
      int byteIndex = this->position / BITS_IN_BYTE;
      // calculate our offset so we know how much to bit shift
      int byteOffset = ((BITS_IN_BYTE-1) - (this->position % BITS_IN_BYTE));
      // calculate the bit which we want to read
      int bit = ((((*buffer)[byteIndex] & 0xff) >> byteOffset) & 0x01);
      // increment our curent position so we know this bit has been read
      this->position++;
      // return the bit we calculated
      return bit;
      }
 
 
//---------------------------------------------------------
//   readBits
//---------------------------------------------------------
 
int GuitarPro6::readBits(int bitsToRead) {
      int bits = 0;
      for (int i = (bitsToRead-1); i>=0; i--) {
            bits |= (readBit() << i);
            }
      return bits;
      }
 
//---------------------------------------------------------
//   readBitsReversed
//---------------------------------------------------------
 
int GuitarPro6::readBitsReversed(int bitsToRead) {
      int bits = 0;
      for( int i = 0 ; i < bitsToRead ; i ++ ) {
            bits |= (readBit() << i );
            }
      return bits;
      }
 
//---------------------------------------------------------
//   getBytes
//---------------------------------------------------------
 
QByteArray GuitarPro6::getBytes(QByteArray* buffer, int offset, int length) {
      QByteArray newBytes;
      // compute new bytes from our buffer and return byte array
      for (int i = 0; i < length; i++) {
            if (buffer->length() > offset + i) {
                  newBytes.insert(i, ((*buffer)[offset+i]));
                  }
            }
      return newBytes;
      }
 
//---------------------------------------------------------
//   readInteger
//---------------------------------------------------------
 
int GuitarPro6::readInteger(QByteArray* buffer, int offset) {
      // assign four bytes and take them from the buffer
      char bytes[4];
      bytes[0] = (*buffer)[offset+0];
      bytes[1] = (*buffer)[offset+1];
      bytes[2] = (*buffer)[offset+2];
      bytes[3] = (*buffer)[offset+3];
      // increment positioning so we keep track of where we are
      this->position+=sizeof(int)*BITS_IN_BYTE;
      // bit shift in order to compute our integer value and return
      return ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff);
      }
 
//---------------------------------------------------------
//   readString
//---------------------------------------------------------
 
QByteArray GuitarPro6::readString(QByteArray* buffer, int offset, int length) {
      QByteArray filename;
      // compute the string by iterating through the buffer
      for (int i = 0; i < length; i++) {
            int charValue = (((*buffer)[offset + i]) & 0xff);
            if (charValue == 0)
                  break;
            filename.push_back((char)charValue);
      }
      return filename;
}
 
//---------------------------------------------------------
//   readGPX
//---------------------------------------------------------
 
void GuitarPro6::readGPX(QByteArray* buffer) {
      // start by reading the file header. It will tell us if the byte array is compressed.
      int fileHeader = readInteger(buffer, 0);
 
      if (fileHeader == GPX_HEADER_COMPRESSED) {
            // this is  a compressed file.
            int length = readInteger(buffer, this->position/BITS_IN_BYTE);
            QByteArray* bcfsBuffer = new QByteArray();
            int positionCounter = 0;
            while(!f->error() && (this->position/this->BITS_IN_BYTE) < length) {
                  // read the bit indicating compression information
                  int flag = this->readBits(1);
 
                  if (flag) {
                        int bits = this->readBits(4);
                        int offs = this->readBitsReversed(bits);
                        int size = this->readBitsReversed(bits);
 
                        QByteArray bcfsBufferCopy = *bcfsBuffer;
                        int pos = (bcfsBufferCopy.length() - offs );
                        for( int i = 0; i < (size > offs ? offs : size) ; i ++ ) {
                              bcfsBuffer->insert(positionCounter, bcfsBufferCopy[pos + i] ) ;
                              positionCounter++;
                              }
                        }
                  else  {
                        int size = this->readBitsReversed(2);
                        for(int i = 0; i < size; i++) {
                              bcfsBuffer->insert(positionCounter, this->readBits(8));
                              positionCounter++;
                              }
                        }
                  }
             // recurse on the decompressed file stored as a byte array
             readGPX(bcfsBuffer);
             delete bcfsBuffer;
            }
      else if (fileHeader == GPX_HEADER_UNCOMPRESSED) {
            // this is an uncompressed file - strip the header off
            *buffer = buffer->right(buffer->length()-sizeof(int));
            int sectorSize = 0x1000;
            int offset = 0;
            while (((offset = (offset + sectorSize)) + 3) < buffer->length()) {
                  int newInt = readInteger(buffer,offset);
                  if (newInt == 2) {
                        int indexFileName = (offset + 4);
                        int indexFileSize = (offset + 0x8C);
                        int indexOfBlock = (offset + 0x94);
 
                        // create a byte array and put information about files found in it
                        int block = 0;
                        int blockCount = 0;
                        QByteArray* fileBytes = new QByteArray();
                        while((block = (readInteger(buffer, (indexOfBlock + (4 * (blockCount ++)))))) != 0 ) {
                              fileBytes->push_back(getBytes(buffer, (offset = (block*sectorSize)), sectorSize));
                              }
                        // get file information and read the file
                        int fileSize = readInteger(buffer, indexFileSize);
                        if (fileBytes->length() >= fileSize) {
                              QByteArray filenameBytes = readString(buffer, indexFileName, 127);
                              const char* filename = filenameBytes.data();
                              qDebug() << filename;
                              QByteArray data = getBytes(fileBytes, 0, fileSize);
                              parseFile(filename, &data);
                              }
                        delete fileBytes;
                        }
                  }
            }
      }
 
//---------------------------------------------------------
//   parseFile
//---------------------------------------------------------
 
void GuitarPro6::parseFile(const char* filename, QByteArray* data)
      {
      // test to check if we are dealing with the score
      if (!strcmp(filename, "score.gpif"))
            readGpif(data);
      }
 
 
//---------------------------------------------------------
//   unhandledNode
//---------------------------------------------------------
 
void GuitarPro6::unhandledNode(QString nodeName)
      {
      qDebug() << "WARNING: Discovered unhandled node name" << nodeName;
      }
 
//---------------------------------------------------------
//   readScore
//---------------------------------------------------------
 
void GuitarPro6::readScore(QDomNode* scoreNode)
      {
      // loop through the score meta-info, grabbing title, artist, etc
      QDomNode currentNode = scoreNode->firstChild();
      while (!currentNode.isNull()) {
            QString nodeName = currentNode.nodeName();
            if (!nodeName.compare("Title"))
                  title = currentNode.toElement().text();
            if (!nodeName.compare("Copyright"))
                  score->setMetaTag("copyright", currentNode.toElement().text());
            else if (!nodeName.compare("SubTitle"))
                  subtitle = currentNode.toElement().text();
            else if (!nodeName.compare("Artist"))
                  artist = currentNode.toElement().text();
            else if (!nodeName.compare("Album"))
                  album = currentNode.toElement().text();
            else if (nodeName == "FirstPageHeader") {}
            else if (nodeName == "FirstPageFooter") {}
            else if (nodeName == "PageHeader") {}
            else if (nodeName == "PageFooter") {}
            else if (nodeName == "ScoreSystemsDefaultLayout") {}
            else if (nodeName == "ScoreSystemsLayout") {}
            currentNode = currentNode.nextSibling();
            }
      }
 
//---------------------------------------------------------
//   readMasterTracks
//---------------------------------------------------------
 
void GuitarPro6::readMasterTracks(QDomNode* masterTrack)
      {
      // inspects MasterTrack, gives information applying to start of score such as tempo
      QDomNode currentNode = masterTrack->firstChild();
      while (!currentNode.isNull()) {
            QString nodeName = currentNode.nodeName();
            if (!nodeName.compare("Automations")) {
                  QDomNode currentAutomation = currentNode.firstChild();
                  while (!currentAutomation.isNull()) {
                        if (!currentAutomation.nodeName().compare("Automation")) {
                              if (!currentAutomation.firstChild().toElement().text().compare("Tempo")) {
                                    QString t = currentAutomation.lastChild().toElement().text();
                                    QStringList sa = t.split(" ");
                                    if (sa.length() >= 1) {
                                          tempo = sa[0].toInt();
                                          }
                                    }
                              }
                        currentAutomation = currentAutomation.nextSibling();
                        }
                  }
            currentNode = currentNode.nextSibling();
            }
      }
 
//---------------------------------------------------------
//   readChord
//---------------------------------------------------------
 
void GuitarPro6::readFretboardDiagram(QDomNode* diagram, int track)
      {
      // initialise a new fret diagram for our current track
      FretDiagram* fretDiagram = new FretDiagram(score);
      fretDiagram->setTrack(track * VOICES);
 
      // get the identifier to set as the domain in the map
      int id = diagram->attributes().namedItem("id").toAttr().value().toInt();
      QString name = diagram->attributes().namedItem("name").toAttr().value();
      QDomNode diagramNode = diagram->firstChild();
 
      // set the number of strings on this part
      int stringCount = diagramNode.attributes().namedItem("stringCount").toAttr().value().toInt();
      fretDiagram->setStrings(stringCount);
 
      // set the fret offset
      int baseFret = diagramNode.attributes().namedItem("baseFret").toAttr().value().toInt();
      fretDiagram->setOffset(baseFret);
 
      QDomNode diagramEntity = diagramNode.firstChild();
      int counter = 0;
      while (!diagramEntity.isNull()) {
            QString nodeName = diagramEntity.nodeName();
            // new fret
            if (!nodeName.compare("Fret")) {
                  // get the string and fret numbers from the arguments to the node as integers
                  int string = diagramEntity.attributes().namedItem("string").toAttr().value().toInt();
                  int fret = diagramEntity.attributes().namedItem("fret").toAttr().value().toInt();
 
                  // if there are unspecified string values, add the X marker to that string
                  while (counter < string) {
                        fretDiagram->setMarker(counter, 'X');
                        counter++;
                        }
 
                  // look at the specified string/fret and add to diagram
                  if (fret == 0) {
                        fretDiagram->setMarker(string, '0');
                        counter++;
                        }
                  else  {
                        fretDiagram->setDot(string, fret);
                        counter++;
                        }
                  }
            // move to the next string/fret specification
            diagramEntity = diagramEntity.nextSibling();
            }
 
      // mark any missing strings as 'X'
      while (counter < stringCount) {
            fretDiagram->setMarker(counter, 'X');
            counter++;
            }
 
      // insert the fret diagram into the map of diagrams
      auto fds = fretDiagrams.value(track);
      fds.insert(id, fretDiagram);
      fretDiagrams.insert(track, fds);
      auto cns = chordnames.value(track);
      cns.insert(id, name);
      chordnames.insert(track, cns);
      }
 
//---------------------------------------------------------
//   readTracks
//---------------------------------------------------------
 
void GuitarPro6::readTracks(QDomNode* track)
      {
      QDomNode nextTrack = track->firstChild();
      int trackCounter = 0;
      while (!nextTrack.isNull()) {
            QDomNode currentNode = nextTrack.firstChild();
            Part* part = new Part(score);
            Staff* s = new Staff(score);
            s->setPart(part);
            part->insertStaff(s, -1);
            score->staves().push_back(s);
            while (!currentNode.isNull()) {
                  QString nodeName = currentNode.nodeName();
                  if (nodeName == "Name")
                        part->setPlainLongName(currentNode.toElement().text());
                  else if (nodeName == "Color") {}
                  // this is a typo is guitar pro - 'defaut' is correct here
                  else if (nodeName == "SystemsDefautLayout") {}
                  else if (nodeName == "SystemsLayout") {}
                  else if (nodeName == "RSE") {}
                  else if (nodeName == "GeneralMidi") {
                        if (currentNode.toElement().hasChildNodes()) {
                              int midiPort = currentNode.firstChildElement("Port").text().toInt();
                              int midiChannel = currentNode.firstChildElement("PrimaryChannel").text().toInt();
                              part->setMidiChannel(midiChannel, midiPort);
                              if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
                                    part->instrument()->setDrumset(gpDrumset);
                                    s->setStaffType(0, StaffType::preset(StaffTypes::PERC_DEFAULT));
                                    }
                              }
                        }
                  else if (nodeName == "PlaybackState") {}
                  else if (nodeName == "PlayingStyle") {}
                  else if (nodeName == "PageSetup") {}
                  else if (nodeName == "MultiVoice") {}
                  else if (nodeName == "ShortName")
                        part->setPartName(currentNode.toElement().text());
                  else if (nodeName == "Instrument") {
                        QString ref = currentNode.attributes().namedItem("ref").toAttr().value();
                        auto it = instrumentMapping.find(ref);
                        if (it != instrumentMapping.end()) {
                              part->setInstrument(Instrument::fromTemplate(Ms::searchTemplate(it->second)));
                              }
                        else
                              qDebug() << "Unknown instrument: " << ref;
                        if (ref.endsWith("-gs") || ref.startsWith("2")) { // grand staff
                              Staff* s2 = new Staff(score);
                              s2->setPart(part);
                              part->insertStaff(s2, -1);
                              score->staves().push_back(s2);
                              s->addBracket(new BracketItem(s->score(), BracketType::BRACE, 2));
                              s->setBarLineSpan(2);
                              }
                        }
                  else if (nodeName == "Properties") {
                        readTrackProperties(&currentNode, part, trackCounter);
                        }
                  else if (nodeName == "Staves") { // we consider only the first staff
                        QDomNode staff = currentNode.firstChild();
                        QDomNode properties = staff.firstChildElement("Properties");
                        readTrackProperties(&properties, part, trackCounter);
                        }
                  currentNode = currentNode.nextSibling();
                  }
 
            // add in a new part
            score->appendPart(part);
            trackCounter++;
            nextTrack = nextTrack.nextSibling();
            }
 
      previousDynamic = new int[score->staves().length() * VOICES];
      // initialise the dynamics to 0
      for (int i = 0; i < score->staves().length() * VOICES; i++)
           previousDynamic[i] = 0;
      // set the number of staves we need
      staves = score->staves().length();
      }
 
//---------------------------------------------------------
//   readTracks
//---------------------------------------------------------
 
void GuitarPro6::readTrackProperties(QDomNode* currentNode, Part* part, int trackCounter)
      {
      QDomNode currentProperty = currentNode->firstChild();
      while (!currentProperty.isNull()) {
            QString propertyName = currentProperty.attributes().namedItem("name").toAttr().value();
            if (!propertyName.compare("Tuning")) {
                  // set up the tuning for the part
                  QString tuningString = currentProperty.firstChild().toElement().text();
                  QStringList tuningStringList = tuningString.split(" ");
                  int strings = 0;
                  int tuning[tuningStringList.length()];
                  int frets   = 21;
                  for (auto iter = tuningStringList.begin(); iter != tuningStringList.end(); ++iter) {
                        int currentString = (*iter).toInt();
                        tuning[strings] = currentString;
                        strings++;
                        }
                        StringData* stringData = new StringData(frets, strings, tuning);
                        Instrument* instr = part->instrument();
                        instr->setStringData(*stringData);
                  }
            else if (!propertyName.compare("CapoFret")) {
                  //QString fret = currentProperty.firstChild().toElement().text();
                  //qDebug() << fret;
                  }
            else if (!propertyName.compare("DiagramCollection")) {
                  QDomNode items = currentProperty.firstChild();
                  QDomNode currentItem = items.firstChild();
                  while (!currentItem.isNull()) {
                        readFretboardDiagram(&currentItem, trackCounter);
                        currentItem = currentItem.nextSibling();
                        }
                  }
            else if (!propertyName.compare("ChordCollection")) {
                  QDomNode items = currentProperty.firstChild();
                  QDomNode currentItem = items.firstChild();
                  while (!currentItem.isNull()) {
                        int id = currentItem.attributes().namedItem("id").toAttr().value().toInt();
                        QString name = currentItem.attributes().namedItem("name").toAttr().value();
                        auto cns = chordnames.value(trackCounter);
                        cns.insert(id, name);
                        chordnames.insert(trackCounter, cns);
                        currentItem = currentItem.nextSibling();
                        }
                  }
            currentProperty = currentProperty.nextSibling();
            }
      }
 
//---------------------------------------------------------
//   getNode
//---------------------------------------------------------
 
QDomNode GuitarPro6::getNode(QString id, QDomNode currentNode)
      {
      while (!(currentNode).isNull()) {
            QString currentId = currentNode.attributes().namedItem("id").toAttr().value();
            if (id.compare(currentId) == 0) {
                  return currentNode;
                  }
            currentNode = (currentNode).nextSibling();
            }
      qDebug() << "WARNING: A null node was returned when search for the identifier" << id << ". Your Guitar Pro file may be corrupted.";
      return currentNode;
      }
 
//---------------------------------------------------------
//   findNumMeasures
//---------------------------------------------------------
 
int GuitarPro6::findNumMeasures(GPPartInfo* partInfo)
      {
      QDomNode masterBar = partInfo->masterBars.nextSibling();
      QDomNode masterBarElement;
      while (!masterBar.isNull()) {
            GpBar gpBar;
            gpBar.freeTime = false;
            gpBar.direction = "";
            gpBar.directionStyle = "";
            masterBarElement = masterBar.firstChild();
            while (!masterBarElement.isNull()) {
                  if (!masterBarElement.nodeName().compare("Key"))
                        gpBar.keysig = masterBarElement.firstChild().toElement().text().toInt();
                  else
                        if (!masterBarElement.nodeName().compare("Time")) {
                        QString timeSignature = masterBarElement.toElement().text();
                        QList<QString> timeSignatureList = timeSignature.split("/");
                        gpBar.timesig = Fraction(timeSignatureList.first().toInt(), timeSignatureList.last().toInt());
                        }
                  else if (!masterBarElement.nodeName().compare("Directions")) {
                        gpBar.direction = masterBarElement.firstChild().toElement().text();
                        gpBar.directionStyle = masterBarElement.firstChild().nodeName();
                        }
                  else if (!masterBarElement.nodeName().compare("FreeTime")) {
                        gpBar.freeTime = true;
                        gpBar.barLine = BarLineType::BROKEN;
                        }
                  else if (!masterBarElement.nodeName().compare("DoubleBar"))
                        gpBar.barLine = BarLineType::DOUBLE;
                  masterBarElement = masterBarElement.nextSibling();
                  }
            bars.append(gpBar);
            if (masterBar.nextSibling().isNull())
                  break;
            masterBar = masterBar.nextSibling();
            }
      QString finalBars = masterBar.lastChildElement("Bars").toElement().text();
      //work out the number of measures (add 1 as couning from 0, and divide by number of parts)
      int numMeasures = (finalBars.split(" ").last().toInt() + 1) / score->parts().length();
 
      if (numMeasures > bars.size()) {
            qDebug("GuitarPro6:findNumMeasures: bars %d < numMeasures %d\n", bars.size(), numMeasures);
            // HACK (ws)
            numMeasures = bars.size();
            }
      return numMeasures;
      }
 
//---------------------------------------------------------
//   fermataToFraction
//---------------------------------------------------------
 
Fraction GuitarPro6::fermataToFraction(int numerator, int denominator)
      {
      numerator++;
      // minim through to hemidemisemiquaver
      if (denominator == 0)       { denominator = 2; }
      else if (denominator == 1)  { denominator = 4; }
      else if (denominator == 2)  { denominator = 8;  }
      else if (denominator == 3)  { denominator = 12;  }
      else if (denominator == 4)  { denominator = 16;  }
      else if (denominator == 5)  { denominator = 32;  }
 
      return Fraction(numerator, denominator);
      }
 
//---------------------------------------------------------
//   rhythmToDuration
//---------------------------------------------------------
 
Fraction GuitarPro6::rhythmToDuration(QString value)
      {
      Fraction l;
      if (value.compare("Whole") == 0)
            l.set(1, 1);
      else if (value.compare("Half") == 0)
            l.set(1, 2);
      else if (value.compare("Quarter") == 0)
            l.set(1, 4);
      else if (value.compare("Eighth") == 0)
            l.set(1,8);
      else if (value.compare("16th") == 0)
            l.set(1,16);
      else if (value.compare("32nd") == 0)
            l.set(1,32);
      else if (value.compare("64th") == 0)
            l.set(1,64);
      else if (value.compare("128th") == 0)
            l.set(1,128);
      else
            qFatal( "Error - unknown note length: %s", qPrintable(value));
      return l;
      }
 
//---------------------------------------------------------
//   readDrumNote
//---------------------------------------------------------
 
void GuitarPro6::readDrumNote(Note* note, int element, int variation)
      {
      int pitch = 44;
      /* These numbers below were determined by creating all drum
       * notes in a GPX format file and then analyzing the score.gpif
       * file which specifies the score and then matching as much
       * as possible with the gpDrumset...   */
      if (element == 11 && variation == 0)  // pedal hihat
            pitch = 44;
      else if (element == 0 && variation == 0) // Kick (hit)
            pitch = 35; // or 36
      else if (element == 5 && variation == 0) // Tom very low (hit)
            pitch = 41;
      else if (element == 6 && variation == 0) // Tom low (hit)
            pitch = 43;
      else if (element == 7 && variation == 0) // Tom medium (hit)
            pitch = 45;
      else if (element == 1 && variation == 0) // Snare (hit)
            pitch = 38; //or 40
      else if (element == 1 && variation == 1) // Snare (rim shot)
            pitch = 37;
      else if (element == 1 && variation == 2) // Snare (side stick)
            pitch = 37;
      else if (element == 8 && variation == 0) // Tom high (hit)
            pitch = 48;
      else if (element == 9 && variation == 0) // Tom very high (hit)
            pitch = 50;
      else if (element == 15 && variation == 0) // Ride (middle)
            pitch = 51;
      else if (element == 15 && variation == 1) // Ride (edge)
            pitch = 59;
      else if (element == 15 && variation == 2) // Ride (bell)
            pitch = 59;
      else if (element == 10 && variation == 0) // Hihat (closed)
            pitch = 42;
      else if (element == 10 && variation == 1) // Hihat (half)
            pitch = 46;
      else if (element == 10 && variation == 2) // Hihat (open)
            pitch = 46;
      else if (element == 12 && variation == 0) // Crash medium (hit)
            pitch = 49;
      else if (element == 14 && variation == 0) // Splash (hit)
            pitch = 55;
      else if (element == 13 && variation == 0) // Crash high (hit)
            pitch = 57;
      else if (element == 16 && variation == 0) // China (hit)
            pitch = 52;
      else if (element == 4 && variation == 0) // Cowbell high (hit)
            pitch = 56;
      else if (element == 3 && variation == 0) // Cowbell medium (hit)
            pitch = 56;
      else if (element == 2 && variation == 0) // Cowbell low (hit)
            pitch = 56;
 
      note->setPitch(pitch);
      }
 
 
void GuitarPro6::makeTie(Note* note) {
      bool found = false;
      Chord* chord     = note->chord();
      Segment* segment = chord->segment();
      segment = segment->prev1(SegmentType::ChordRest);
      int track        = note->track();
      while (segment) {
            Element* e = segment->element(track);
            if (e) {
                  if (e->type() == ElementType::CHORD) {
                        Chord* chord2 = static_cast<Chord*>(e);
                        foreach(Note* note2, chord2->notes()) {
                              if (note2->string() == note->string()) {
                                    Tie* tie = new Tie(score);
                                    tie->setEndNote(note);
                                    note2->add(tie);
                                    note->setFret(note2->fret());
                                    note->setPitch(note2->pitch());
                                    found = true;
                                    break;
                              }
                        }
                  }
                  if (found)
                        break;
            }
            segment = segment->prev1(SegmentType::ChordRest);
      }
}
 
int GuitarPro6::readBeats(QString beats, GPPartInfo* partInfo, Measure* measure, int startTick, int staffIdx, int voiceNum, Tuplet* tuplets[], int measureCounter)
      {
            int beatsTick = 0;
            // we must count from the start of the bar, so declare a fraction to track this
            Fraction fermataIndex(0,1);
            int track = staffIdx * VOICES + voiceNum;
            auto currentBeatList = beats.split(" ");
            bool startSlur = false;
            bool endSlur = false;
            for (auto currentBeat = currentBeatList.begin(); currentBeat != currentBeatList.end(); currentBeat++) {
                  int slide = -1;
                  if (slides->contains(staffIdx * VOICES + voiceNum))
                        slide = slides->take(staffIdx * VOICES + voiceNum);
 
                  Fraction l;
                  int dotted = 0;
                  QDomNode beat = getNode(*currentBeat, partInfo->beats);
                  int currentTick = startTick + beatsTick;
                  Segment* segment = measure->getSegment(SegmentType::ChordRest, currentTick);
                  QDomNode currentNode = beat.firstChild();
                  bool noteSpecified = false;
                  ChordRest* cr = segment->cr(track);
                  bool tupletSet = false;
                  Tuplet* tuplet = tuplets[staffIdx * VOICES + voiceNum];
                  int whammyOrigin = -1;
                  int whammyMiddle = -1;
                  int whammyEnd = -1;
                  bool graceNote = false;
                  while (!currentNode.isNull()) {
                        if (currentNode.nodeName() == "GraceNotes") {
                              graceNote = true;
                              break;
                              }
                        else if (currentNode.nodeName() == "Notes") {
                              noteSpecified = true;
                              auto notesList = currentNode.toElement().text().split(" ");
 
                              // this could be set by rhythm if we dealt with a tuplet
                              if (!cr)
                                    cr = new Chord(score);
                              cr->setTrack(track);
                              cr->setDuration(l);
                              TDuration d(l);
                              d.setDots(dotted);
 
                              cr->setDurationType(d);
 
                              if(!segment->cr(track))
                                    segment->add(cr);
 
                              if (startSlur) {
                                    Slur* slur = new Slur(score);
                                    slur->setParent(0);
                                    slur->setTrack(track);
                                    slur->setTrack2(track);
                                    legatos[track] = slur;
                                    slur->setTick(cr->tick());
                                    slur->setTick2(cr->tick());
                                    startSlur = false;
                                    }
                              if (endSlur) {
                                    Slur* slur = legatos[track];
                                    if (slur) {
                                          slur->setTrack2(track);
                                          slur->setTick2(cr->tick());
                                          score->addElement(slur);
                                          legatos[track] = 0;
                                          }
                                    endSlur = false;
                                    }
 
                              for (auto iter = notesList.begin(); iter != notesList.end(); ++iter) {
                                    // we have found a note
                                    QDomNode note = getNode(*iter, partInfo->notes);
                                    QDomNode currentNote = (note).firstChild();
                                    bool tie = false;
                                    bool trill = false;
                                    // if a <Notes> tag is used but there is no <Note>, then we add a rest. This flag will allow us to check this.
                                    while (!currentNote.isNull()) {
                                          if (currentNote.nodeName() == "Properties") {
                                                QDomNode currentProperty = currentNote.firstChild();
                                                // these should not be in this scope - they may not even exist.
                                                QString stringNum;
                                                QString fretNum;
                                                QString tone;
                                                QString octave;
                                                QString midi;
                                                QString element;
                                                QString variation;
 
                                                Note* note = new Note(score);
                                                if (dotted) {
                                                      // there is at most one dotted note in this guitar pro version
                                                      NoteDot* dot = new NoteDot(score);
                                                      //dot->setIdx(dotted);
                                                      dot->setParent(note);
                                                      dot->setTrack(track);  // needed to know the staff it belongs to (and detect tablature)
                                                      dot->setVisible(true);
                                                      note->add(dot);
                                                      }
 
                                                Chord* chord = static_cast<Chord*>(cr);
                                                chord->add(note);
 
                                                QString harmonicText = "";
                                                bool hasSlur = false;
                                                bool hasPalmMute = false;
                                                while (!currentProperty.isNull()) {
                                                      QString argument = currentProperty.attributes().namedItem("name").toAttr().value();
                                                      if (argument == "String")
                                                            stringNum = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Element")
                                                            element = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Slide") {
                                                            int slideKind = currentProperty.firstChild().toElement().text().toInt();
                                                            if (slideKind >= 4)
                                                                  slide = slideKind;
                                                            else
                                                                  slides->insert(staffIdx * VOICES + voiceNum, slideKind);
                                                            }
                                                      else if (!argument.compare("HopoOrigin")) {
                                                            hasSlur = true;
                                                            createSlur(true, track, cr);
                                                            }
                                                      else if (!argument.compare("HopoDestination") && !hasSlur) {
                                                            createSlur(false, track, cr);
                                                            }
                                                      else if (argument == "Variation")
                                                            variation = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Fret")
                                                            fretNum = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Tone")
                                                            tone = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Octave")
                                                            octave = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Midi")
                                                            midi = currentProperty.firstChild().toElement().text();
                                                      else if (argument == "Muted") {
                                                            if (!currentProperty.firstChild().nodeName().compare("Enable")) {
                                                                  note->setHeadGroup(NoteHead::Group::HEAD_CROSS);
                                                                  note->setGhost(true);
                                                                  }
                                                            }
                                                      else if (argument == "PalmMuted") {
                                                            if (!currentProperty.firstChild().nodeName().compare("Enable"))
                                                                  hasPalmMute = true;
                                                            }
                                                      else if (argument == "Tapped") {
                                                            if (!currentProperty.firstChild().nodeName().compare("Enable"))
                                                                  addTap(note);
                                                            }
                                                      else if (!argument.compare("HarmonicType")) {
                                                            QString type = currentProperty.toElement().text();
                                                            // add the same text to the note that Guitar Pro does
                                                            if (!type.compare("Feedback"))
                                                                  harmonicText = "Fdbk.";
                                                            else if (!type.compare("Semi"))
                                                                  harmonicText = "S.H.";
                                                            else if (!type.compare("Pinch"))
                                                                  harmonicText = "P.H.";
                                                            else if (!type.compare("Tap"))
                                                                  harmonicText = "T.H.";
                                                            else if (!type.compare("Artificial"))
                                                                  harmonicText = "A.H.";
                                                            else
                                                                  harmonicText = "Natural";
                                                            }
                                                      else if (!argument.compare("HarmonicFret")) {
                                                            QString value = currentProperty.toElement().text();
                                                            Note* harmonicNote;
 
                                                            // natural harmonic = artificial harmonic?
                                                            if (!harmonicText.compare("Natural"))
                                                                  harmonicNote = note;
                                                            else {
                                                                  harmonicNote = new Note(score);
                                                                  chord->add(harmonicNote);
                                                                  }
 
                                                            Staff* staff = note->staff();
                                                            int harmonicFret = fretNum.toInt();
                                                            int musescoreString = staff->part()->instrument()->stringData()->strings() - 1 - stringNum.toInt();
                                                            harmonicNote->setString(musescoreString);
                                                            harmonicNote->setFret(harmonicFret); // add the octave for the harmonic
                                                            harmonicNote->setHeadGroup(NoteHead::Group::HEAD_DIAMOND);
                                                            if (!value.compare("12"))
                                                                  harmonicFret += 12;
                                                            else if (!value.compare("7") || !value.compare("19"))
                                                                  harmonicFret += 19;
                                                            else if (!value.compare("5") || !value.compare("24"))
                                                                  harmonicFret += 24;
                                                            else if (!value.compare("3.9") || !value.compare("4") || !value.compare("9") || !value.compare("16"))
                                                                  harmonicFret += 28;
                                                            else if (!value.compare("3.2"))
                                                                  harmonicFret += 31;
                                                            else if (!value.compare("2.7"))
                                                                  harmonicFret += 34;
                                                            else if (!value.compare("2.3") || !value.compare("2.4"))
                                                                  harmonicFret += 36;
                                                            else if (!value.compare("2"))
                                                                  harmonicFret += 38;
                                                            else if (!value.compare("1.8"))
                                                                  harmonicFret += 40;
                                                            harmonicNote->setFret(harmonicFret);
                                                            harmonicNote->setPitch(staff->part()->instrument()->stringData()->getPitch(musescoreString, harmonicFret, nullptr, 0));
                                                            harmonicNote->setTpcFromPitch();
                                                            if (harmonicText.compare("Natural")) {
                                                                  harmonicNote->setFret(fretNum.toInt());
                                                                  addTextToNote(harmonicText, Align::CENTER, harmonicNote);
                                                                  }
                                                            }
                                                      currentProperty = currentProperty.nextSibling();
                                                }
                                                addPalmMute(chord, staffIdx, hasPalmMute);
                                                if (midi != "")
                                                      note->setPitch(midi.toInt());
                                                else if (element != "")
                                                      readDrumNote(note, element.toInt(), variation.toInt());
                                                else if (stringNum != "" && note->headGroup() != NoteHead::Group::HEAD_DIAMOND) {
                                                      Staff* staff = note->staff();
                                                      int fretNumber = fretNum.toInt();
                                                      int musescoreString = staff->part()->instrument()->stringData()->strings() - 1 - stringNum.toInt();
                                                      auto pitch = staff->part()->instrument()->stringData()->getPitch(musescoreString, fretNumber, nullptr, 0);
                                                      note->setFret(fretNumber);
                                                      // we need to turn this string number for GP to the the correct string number for musescore
                                                      note->setString(musescoreString);
                                                      note->setPitch(pitch);
                                                      }
                                                else if (tone != "")
                                                      note->setPitch((octave.toInt() * 12) + tone.toInt()); // multiply octaves by 12 as 12 semitones in octave
                                                QDomNode graceNode = currentNode.parentNode().firstChildElement("GraceNotes");
                                                if (!graceNode.isNull()) {
                                                      note->setTpcFromPitch();
                                                      // before beat grace notes have to be handled after the Tpc is set from pitch
                                                      if (!graceNode.toElement().text().compare("OnBeat")) {
                                                            chord->setDurationType(MScore::division/2);
                                                            chord->setMag(note->chord()->staff()->mag(0) * score->styleD(StyleIdx::graceNoteMag));
                                                            chord->setNoteType(NoteType::GRACE4);
                                                           }
                                                      else if (!graceNode.toElement().text().compare("BeforeBeat")) {
                                                            score->setGraceNote(chord, note->pitch(), NoteType::ACCIACCATURA, MScore::division/2);
                                                            }
                                                      }
                                                 if (tie) {
                                                      makeTie(note);
                                                      tie = false;
                                                      }
                                                if (trill) {
                                                      Articulation* art = new Articulation(note->score());
                                                      art->setSymId(SymId::ornamentTrill);
                                                      if (!note->score()->addArticulation(note, art))
                                                            delete art;
                                                      }
                                                QDomNode tremoloNode = currentNode.parentNode().firstChildElement("Tremolo");
                                                if (!tremoloNode.isNull()) {
                                                      QString value = tremoloNode.toElement().text();
                                                      Tremolo* t = new Tremolo(chord->score());
                                                      if (!value.compare("1/2")) {
                                                            t->setTremoloType(TremoloType::R8);
                                                            chord->add(t);
                                                            }
                                                      else if (!value.compare("1/4")) {
                                                            t->setTremoloType(TremoloType::R16);
                                                            chord->add(t);
                                                            }
                                                      else if (!value.compare("1/8")) {
                                                            t->setTremoloType(TremoloType::R32);
                                                            chord->add(t);
                                                            }
                                                      }
                                                QDomNode wahNode = currentNode.parentNode().firstChildElement("Wah");
                                                if (!wahNode.isNull()) {
                                                      QString value = wahNode.toElement().text();
                                                      if (!value.compare("Open")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::brassMuteOpen);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      else if (!value.compare("Closed")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::brassMuteClosed);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      }
                                                QDomNode accentNode = currentNote.parentNode().firstChildElement("Accent");
                                                if (!accentNode.isNull()) {
                                                      int value = accentNode.toElement().text().toInt();
                                                      if (value == 4) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::articMarcatoAbove);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      else if (value == 8) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::articAccentAbove);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                }
                                                QDomNode ornamentNode = currentNote.parentNode().firstChildElement("Ornament");
                                                if (!ornamentNode.isNull()) {
                                                      QString value = ornamentNode.toElement().text();
                                                      // guitar pro represents the turns the other way to what we do
                                                      if (!value.compare("InvertedTurn")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::ornamentTurn);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      else if (!value.compare("Turn")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::ornamentTurnInverted);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      else if (!value.compare("LowerMordent")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::ornamentMordentInverted);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      else if (!value.compare("UpperMordent")) {
                                                            Articulation* art = new Articulation(note->score());
                                                            art->setSymId(SymId::ornamentMordent);
                                                            if (!note->score()->addArticulation(note, art))
                                                                  delete art;
                                                            }
                                                      }
                                                QDomNode propertiesNode = currentNode.parentNode().firstChildElement("Properties");
                                                if (!propertiesNode.isNull()) {
                                                      QDomNode currentProperty = propertiesNode.firstChild();
                                                      QString barreFret = "";
                                                      bool halfBarre = false;
                                                      while (!currentProperty.isNull()) {
                                                            QString argument = currentProperty.attributes().namedItem("name").toAttr().value();
                                                            if (!argument.compare("PickStroke")) {
                                                                  if (!currentProperty.firstChild().toElement().text().compare("Up")) {
                                                                        Articulation* art = new Articulation(note->score());
                                                                        art->setSymId(SymId::stringsUpBow);
                                                                        if (!note->score()->addArticulation(note, art))
                                                                              delete art;
                                                                        }
                                                                  else if (!currentProperty.firstChild().toElement().text().compare("Down")) {
                                                                        Articulation* art = new Articulation(note->score());
                                                                        art->setSymId(SymId::stringsDownBow);
                                                                        if (!note->score()->addArticulation(note, art))
                                                                              delete art;
                                                                        }
                                                                  }
                                                            else if (!argument.compare("Brush")) {
                                                                  Arpeggio* a = new Arpeggio(score);
                                                                  // directions in arpeggion type are reversed, they are correct below
                                                                  if (!currentProperty.firstChild().toElement().text().compare("Up"))
                                                                        a->setArpeggioType(ArpeggioType::DOWN_STRAIGHT);
                                                                  else if (!currentProperty.firstChild().toElement().text().compare("Down"))
                                                                        a->setArpeggioType(ArpeggioType::UP_STRAIGHT);
                                                                  chord->add(a);
                                                                  }
                                                            else if (!argument.compare("Slapped")) {
                                                                  if (!currentProperty.firstChild().nodeName().compare("Enable"))
                                                                        addSlap(note);
                                                                  }
                                                            else if (!argument.compare("Popped")) {
                                                                  if (!currentProperty.firstChild().nodeName().compare("Enable"))
                                                                        addPop(note);
                                                                  }
                                                            else if (!argument.compare("VibratoWTremBar")) {
                                                                  Articulation* art = new Articulation(note->score());
 
                                                                  if (!currentProperty.firstChild().toElement().text().compare("Slight"))
                                                                        art->setSymId(SymId::wiggleSawtooth);
                                                                  else
                                                                        art->setSymId(SymId::wiggleSawtoothWide);
                                                                  art->setAnchor(ArticulationAnchor::TOP_STAFF);
                                                                  if (!note->score()->addArticulation(note, art))
                                                                        delete art;
 
                                                                  }
                                                            else if (!argument.compare("BarreFret")) {
                                                                  // target can be anywhere from 1 to 36
                                                                  int target = currentProperty.firstChild().toElement().text().toInt();
                                                                  for (int i = 0; i < (target/10); i++)
                                                                        barreFret += "X";
                                                                  int targetMod10 = target % 10;
                                                                  if (targetMod10 == 9)
                                                                        barreFret += "IX";
                                                                  else if (targetMod10 == 4)
                                                                        barreFret += "IV";
                                                                  else  {
                                                                        if (targetMod10 >= 5) {
                                                                              barreFret += "V";
                                                                              targetMod10-=5;
                                                                              }
                                                                        for (int j = 0; j < targetMod10; j++)
                                                                              barreFret += "I";
                                                                        }
                                                                  }
                                                            else if (!argument.compare("BarreString"))
                                                                  halfBarre = true;
                                                            else if (!argument.compare("WhammyBarOriginValue"))
                                                                  whammyOrigin = currentProperty.firstChild().toElement().text().toInt();
                                                            else if (!argument.compare("WhammyBarMiddleValue"))
                                                                  whammyMiddle = currentProperty.firstChild().toElement().text().toInt();
                                                            else if (!argument.compare("WhammyBarDestinationValue"))
                                                                  whammyEnd = currentProperty.firstChild().toElement().text().toInt();
 
                                                            currentProperty = currentProperty.nextSibling();
                                                            }
 
                                                      if (whammyOrigin != -1) {
                                                            // a whammy bar has been detected
                                                            addTremoloBar(segment, track, whammyOrigin, whammyMiddle, whammyEnd);
                                                            }
 
                                                      // if a barre fret has been specified
                                                      if (barreFret.compare("")) {
                                                            QString barreText = QString("%1 %2").arg(halfBarre ? "1/2B " : "B ", barreFret);
                                                            addBarre(chord, staffIdx, true, barreText);
                                                            }
                                                      else {
                                                            addBarre(chord, staffIdx, false);
                                                            }
                                                      }
                                                QDomNode dynamicsNode = currentNode.parentNode().firstChildElement("Dynamic");
                                                if (!dynamicsNode.isNull()) {
                                                      QString dynamicStr = dynamicsNode.toElement().text();
                                                      int dynamic = 0;
                                                      if (!dynamicStr.compare("PPP"))
                                                            dynamic = 1;
                                                      else if (!dynamicStr.compare("PP"))
                                                            dynamic = 2;
                                                      else if (!dynamicStr.compare("P"))
                                                            dynamic = 3;
                                                      else if (!dynamicStr.compare("MP"))
                                                            dynamic = 4;
                                                      else if (!dynamicStr.compare("MF"))
                                                            dynamic = 5;
                                                      else if (!dynamicStr.compare("F"))
                                                            dynamic = 6;
                                                      else if (!dynamicStr.compare("FF"))
                                                            dynamic = 7;
                                                      else if (!dynamicStr.compare("FFF"))
                                                            dynamic = 8;
                                                      if (previousDynamic[track] != dynamic) {
                                                            previousDynamic[track] = dynamic;
                                                            addDynamic(note, dynamic);
                                                            }
                                                      }
                                                /* while left and right fingering nodes have distinct values, they are represented
                                                 * the same way w.r.t. identifying digit/char in the score file. */
                                                QDomNode leftFingeringNode = currentNote.parentNode().firstChildElement("LeftFingering");
                                                QDomNode rightFingeringNode = currentNote.parentNode().firstChildElement("RightFingering");
                                                if (!leftFingeringNode.isNull() || !rightFingeringNode.isNull()) {
                                                      QDomNode fingeringNode = leftFingeringNode.isNull() ? rightFingeringNode : leftFingeringNode;
                                                      QString finger = fingeringNode.toElement().text();
                                                      Fingering* f = new Fingering(score);
                                                      if (!leftFingeringNode.isNull()) {
                                                            if (!finger.compare("Open"))
                                                                  finger = "O";
                                                            else if (!finger.compare("P"))
                                                                  finger = "t";
                                                            else if (!finger.compare("I"))
                                                                  finger = "1";
                                                            else if (!finger.compare("M"))
                                                                  finger = "2";
                                                            else if (!finger.compare("A"))
                                                                  finger = "3";
                                                            else if (!finger.compare("C"))
                                                                  finger = "4";
                                                            }
                                                      f->setPlainText(finger);
                                                      note->add(f);
                                                      f->reset();
                                                      }
                                                QDomNode arpeggioNode = currentNode.parentNode().firstChildElement("Arpeggio");
                                                if (!arpeggioNode.isNull()) {
                                                      QString arpeggioStr = arpeggioNode.toElement().text();
                                                      Arpeggio* a = new Arpeggio(score);
                                                      if (!arpeggioStr.compare("Up"))
                                                            a->setArpeggioType(ArpeggioType::UP);
                                                      else
                                                            a->setArpeggioType(ArpeggioType::DOWN);
                                                      chord->add(a);
                                                      }
                                                QDomNode letRingNode = currentNote.parentNode().firstChildElement("LetRing");
                                                addLetRing(chord, staffIdx, !letRingNode.isNull());
                                                QDomNode timerNode = currentNode.parentNode().firstChildElement("Timer");
                                                if (!timerNode.isNull()) {
                                                      int time = timerNode.toElement().text().toInt();
                                                      int minutes = time/60;
                                                      int seconds = time % 60;
                                                      addTextToNote(QString::number(minutes) + ":" + (seconds < 10 ? "0" + QString::number(seconds) : QString::number(seconds)), Align::CENTER, note);
                                                      }
                                                QDomNode ghostNode = currentNote.parentNode().firstChildElement("AntiAccent");
                                                if (!ghostNode.isNull()) {
                                                      Symbol* leftSym = new Symbol(note->score());
                                                      Symbol* rightSym = new Symbol(note->score());
                                                      leftSym->setSym(SymId::noteheadParenthesisLeft);
                                                      rightSym->setSym(SymId::noteheadParenthesisRight);
                                                      leftSym->setParent(note);
                                                      rightSym->setParent(note);
                                                      note->add(leftSym);
                                                      note->add(rightSym);
                                                      }
                                                QDomNode swellNode = currentNode.parentNode().firstChildElement("Fadding");
                                                if (!swellNode.isNull()) {
                                                      Articulation* art = new Articulation(note->score());
                                                      art->setSymId(SymId::guitarVolumeSwell);
                                                      art->setAnchor(ArticulationAnchor::TOP_STAFF);
                                                      if (!note->score()->addArticulation(note, art))
                                                            delete art;
                                                      }
                                                QDomNode noteVibrato = currentNote.parentNode().firstChildElement("Vibrato");
                                                if (!noteVibrato.isNull()) {
                                                      Articulation* art = new Articulation(note->score());
                                                      if (!noteVibrato.toElement().text().compare("Slight"))
                                                            art->setSymId(SymId::wiggleVibratoLargeFaster);
                                                      else
                                                            art->setSymId(SymId::wiggleVibratoLargeSlowest);
                                                      art->setAnchor(ArticulationAnchor::TOP_STAFF);
                                                      if (!note->score()->addArticulation(note, art))
                                                            delete art;
                                                      }
 
                                                if (cr && (cr->type() == ElementType::CHORD) && slide > 0)
                                                createSlide(slide, cr, staffIdx);
                                                note->setTpcFromPitch();
 
                                                /* if the ottava is a continuation (need to end old one), or we don't
                                                 * see one in the current note when we are tracking one then end the ottava. */
                                                if (ottavaFound.at(track) == 2 || (ottavaFound.at(track) == 1 && currentNode.parentNode().firstChildElement("Ottavia").isNull())) {
                                                      createOttava(false, track, cr, ottavaValue.at(track));
                                                      if (ottavaFound.at(track) == 2)
                                                            ottavaFound.at(track) = 1;
                                                      else
                                                            ottavaFound.at(track) = 0;
                                                      }
                                                if (ottavaFound.at(track)) {
                                                      createOttava(true, track, cr, ottavaValue.at(track));
                                                      int pitch = note->pitch();
                                                      OttavaType type = ottava.at(track)->ottavaType();
                                                      if (type == OttavaType::OTTAVA_8VA)
                                                            note->setPitch(pitch-12);
                                                      else if (type == OttavaType::OTTAVA_8VB)
                                                            note->setPitch(pitch+12);
                                                      else if (type == OttavaType::OTTAVA_15MA)
                                                            note->setPitch(pitch-24);
                                                      else if (type == OttavaType::OTTAVA_15MB)
                                                            note->setPitch(pitch+24);
                                                      }
 
                                                currentNote = currentNote.nextSibling();
                                                }
                                          else if (!currentNote.nodeName().compare("Trill")) {
                                                trill = true;
                                                }
                                          else if (!currentNote.nodeName().compare("Tie")) {
                                                if (!currentNote.attributes().namedItem("destination").toAttr().value().compare("true"))
                                                      tie = true;
                                                }
                                          currentNote = currentNote.nextSibling();
                                    }
                              }
                        }
                        else if (!currentNode.nodeName().compare("FreeText")) {
                              QDomNode textNode = currentNode;
                              QString text = currentNode.toElement().text();
                              bool t = false;
                              // do not add twice the same text per staff
                              int strack = staffIdx * VOICES;
                              int etrack = staffIdx * VOICES + VOICES;
                              for (const Element* e : segment->annotations()) {
                                    if (e->type() == ElementType::STAFF_TEXT && e->track() >= strack && e->track() < etrack) {
                                          const StaffText* st = static_cast<const StaffText*>(e);
                                          if (!st->xmlText().compare(text)) {
                                                t = true;
                                                break;
                                                }
                                          }
                                    }
                              if (!t && !text.isEmpty()) {
                                    StaffText* s = new StaffText(score);
                                    s->setPlainText(text);
                                    s->setTrack(track);
                                    segment->add(s);
                                    }
                              }
                        else if (!currentNode.nodeName().compare("Dynamic")) {
                              }
                        else if (!currentNode.nodeName().compare("Chord")) {
                              int key = currentNode.toElement().text().toInt();
                              if (fretDiagrams.contains(staffIdx) && fretDiagrams[staffIdx].contains(key)) {
                                    auto fds = fretDiagrams[staffIdx];
                                    segment->add(fds[key]->clone());
                                    }
                              if (chordnames.contains(staffIdx) && chordnames[staffIdx].contains(key)){
                                    Harmony* h = new Harmony(score);
                                    h->setHarmony(chordnames[staffIdx][key]);
                                    h->setTrack(track);
                                    segment->add(h);
                                    }
                              }
                        else if (currentNode.nodeName() == "Rhythm") {
                              // we have found a rhythm
                              QString refString = currentNode.attributes().namedItem("ref").toAttr().value();
                              QDomNode rhythm = getNode(refString, partInfo->rhythms);
                              QDomNode currentNode = (rhythm).firstChild();
                              while (!currentNode.isNull()) {
                                    if (currentNode.nodeName() == "NoteValue") {
                                          l = rhythmToDuration(currentNode.toElement().text());
                                    }
                                    else if (currentNode.nodeName() == "AugmentationDot") {
                                          dotted = currentNode.attributes().namedItem("count").toAttr().value().toInt();
                                          Fraction tmp = l;
                                          for (int count = 1; count <= dotted; count++)
                                                l = l + (tmp / pow(2, count));
                                    }
                                    else if (currentNode.nodeName() == "PrimaryTuplet") {
                                          tupletSet = true;
                                          cr = new Chord(score);
                                          cr->setTrack(track);
                                          if ((tuplet == 0) || (tuplet->elementsDuration() == tuplet->baseLen().fraction() * tuplet->ratio().numerator())) {
                                                tuplet = new Tuplet(score);
                                                tuplets[staffIdx * VOICES + voiceNum] = tuplet;
                                                tuplet->setParent(measure);
                                          }
                                          tuplet->setTrack(cr->track());
                                          tuplet->setBaseLen(l);
                                          tuplet->setRatio(Fraction(currentNode.attributes().namedItem("num").toAttr().value().toInt(),currentNode.attributes().namedItem("den").toAttr().value().toInt()));
                                          tuplet->setDuration(l * tuplet->ratio().denominator());
                                          tuplet->add(cr);
                                    }
                                    else
                                          qDebug() << "WARNING: Not handling node: " << currentNode.nodeName();
                                    currentNode = currentNode.nextSibling();
                              }
                              fermataIndex += l;
                        }
                        else if (currentNode.nodeName() == "Legato") {
                              QString origin = currentNode.attributes().namedItem("origin").toAttr().value();
                              QString destination = currentNode.attributes().namedItem("destination").toAttr().value();
                              if (!destination.compare("false") && !origin.compare("true")) {
                                    qDebug() << "origin";
                                    startSlur = true;
                                    }
                              else if (!destination.compare("true") && !origin.compare("false")) {
                                    qDebug() << "destination";
                                    endSlur = true;
                                    }
                              }
                        else if (currentNode.nodeName() == "Hairpin") {
                              Segment* seg = segment->prev1(SegmentType::ChordRest);
                              bool isCrec = !currentNode.toElement().text().compare("Crescendo");
                              if (seg && hairpins[staffIdx]) {
                                    if (hairpins[staffIdx]->tick2() == seg->tick())
                                          hairpins[staffIdx]->setTick2(currentTick);
                                    else
                                          createCrecDim(staffIdx, track, currentTick, isCrec);
                                    }
                              else
                                    createCrecDim(staffIdx, track, currentTick, isCrec);
                              }
                        else if (!currentNode.nodeName().compare("Properties")) {
                              QDomNode currentProperty = currentNode.firstChild();
                              while (!currentProperty.isNull()) {
                                    QString argument = currentProperty.attributes().namedItem("name").toAttr().value();
                                    if (!argument.compare("Rasgueado")) {
                                          StaffText* st = new StaffText(score);
                                          st->setXmlText("rasg.");
                                          st->setParent(segment);
                                          st->setTrack(track);
                                          score->addElement(st);
                                          }
                                    currentProperty = currentProperty.nextSibling();
                                    }
                              }
                        else if (!currentNode.nodeName().compare("Ottavia")) {
                              /* if we saw an ottava and have an updated
                               * information string, set to 2 indicating that. */
                              if (ottavaFound.at(track) == 1 && ottavaValue.at(track).compare(currentNode.toElement().text()))
                                    ottavaFound.at(track) = 2;
                              else
                                    ottavaFound.at(track) = 1;
                              ottavaValue.at(track) = currentNode.toElement().text();
                              }
                        currentNode = currentNode.nextSibling();
                  }
                  dotted = 0;
                  if (graceNote)
                        continue;
                  // we have handled the beat - was there a note?
                  if (!noteSpecified) {
                        // add a rest with length of l
                        // we already have a chord, delete it first
                        ChordRest* prevCr = cr; // added in "Rhythm"
                        cr = new Rest(score);
                        cr->setTrack(track);
                        if (tupletSet) {
                              tuplet->remove(prevCr);
                              delete prevCr;
                              tuplet->add(cr);
                              }
                        cr->setDuration(l);
                        if (cr->type() == ElementType::REST && l >= measure->len()) {
                              cr->setDurationType(TDuration::DurationType::V_MEASURE);
                              cr->setDuration(measure->len());
                              }
                        else {
                              TDuration d(l);
                              cr->setDurationType(d);
                              }
                        if(!segment->cr(track))
                              segment->add(cr);
                  }
                  auto fermataList = fermatas.find(measureCounter);
                  if (fermataList != fermatas.end()) {
                        // iterator is a list of GPFermata values
                        for (auto fermataIter = (*fermataList)->begin(); fermataIter != (*fermataList)->end(); fermataIter++) {
                              Fraction targetIndex = fermataToFraction((*fermataIter).index, ((*fermataIter).timeDivision));
                              if (fermataIndex == targetIndex) {
                                    Articulation* art = new Articulation(score);
                                    art->setSymId(SymId::fermataAbove);
                                    art->setUp(true);
                                    art->setAnchor(ArticulationAnchor::TOP_STAFF);
                                    cr->add(art);
                                    }
                              }
                        }
                  beatsTick += cr->actualTicks();
            }
            return beatsTick;
      }
 
//---------------------------------------------------------
//   readBars
//---------------------------------------------------------
 
void GuitarPro6::readBars(QDomNode* barList, Measure* measure, ClefType oldClefId[], GPPartInfo* partInfo, int measureCounter)
      {
      // unique bar identifiers are represented as a space separated string of numbers
      QStringList barsString = barList->toElement().text().split(" ");
      int staffIdx = 0;
 
      // used to keep track of tuplets
      Tuplet* tuplets[staves * VOICES];
      for (int track = 0; track < staves * VOICES; ++track)
            tuplets[track] = 0;
 
      // iterate through all the bars that have been specified
      for (auto iter = barsString.begin(); iter != barsString.end(); ++iter) {
            int tick = measure->tick();
 
            QDomNode barNode = getNode(*iter, partInfo->bars);
            QDomNode currentNode = (barNode).firstChild();
            QDomNode voice;
            while (!currentNode.isNull()) {
                  voice.clear();
                  // get the clef of the bar and apply
                  if (!currentNode.nodeName().compare("Clef")) {
                        QString clefString = currentNode.toElement().text();
                        QDomNode nextNode = currentNode.nextSibling();
                        QString clefOctave;
                        if (!nextNode.nodeName().compare("Ottavia"))
                              clefOctave = nextNode.toElement().text();
                        ClefType clefId = ClefType::G;
                        if (!clefString.compare("F4")) {
                              clefId = ClefType::F;
                              if (clefOctave == "8va")
                                    clefId = ClefType::F_8VA;
                              else if (clefOctave == "8vb")
                                    clefId = ClefType::F8_VB;
                              else if (clefOctave == "15ma")
                                    clefId = ClefType::F_15MA;
                              else if (clefOctave == "15mb")
                                    clefId = ClefType::F15_MB;
                              }
                        else if (!clefString.compare("G2")) {
                              clefId = ClefType::G;
                              if (clefOctave == "8va")
                                    clefId = ClefType::G8_VA;
                              else if (clefOctave == "8vb")
                                    clefId = ClefType::G8_VB;
                              else if (clefOctave == "15ma")
                                    clefId = ClefType::G15_MA;
                              else if (clefOctave == "15mb")
                                    clefId = ClefType::G15_MB;
                              }
                        else if (!clefString.compare("C3"))
                              clefId = ClefType::C3;
                        else if (!clefString.compare("C4"))
                              clefId = ClefType::C4;
                        else if (!clefString.compare("Neutral"))
                               clefId = ClefType::PERC;
                        else
                              qDebug() << "WARNING: unhandled clef type: " << clefString;
                        Clef* newClef = new Clef(score);
                        newClef->setClefType(clefId);
                        newClef->setTrack(staffIdx * VOICES);
                        // only add the clef to the bar if it differs from previous measure
                        if (measure->prevMeasure()) {
                              if (clefId != oldClefId[staffIdx]) {
                                    Segment* segment = measure->getSegment(SegmentType::Clef, tick);
                                    segment->add(newClef);
                                    oldClefId[staffIdx] = clefId;
                                    }
                              else
                                    delete newClef;
                              }
                        else  {
                              Segment* segment = measure->getSegment(SegmentType::HeaderClef, 0);
                              segment->add(newClef);
                              oldClefId[staffIdx] = clefId;
                              }
                        }
                  // a repeated bar (simile marking)
                  else if (!currentNode.nodeName().compare("SimileMark")) {
                        if (!currentNode.toElement().text().compare("Simple") ||
                            !currentNode.toElement().text().compare("FirstOfDouble") ||
                            !currentNode.toElement().text().compare("SecondOfDouble")) {
                              RepeatMeasure* rm = new RepeatMeasure(score);
                              rm->setTrack(staffIdx * VOICES);
                              rm->setDuration(measure->len());
                              rm->setDurationType(TDuration::DurationType::V_MEASURE);
                              Segment* segment = measure->getSegment(SegmentType::ChordRest, tick);
                              segment->add(rm);
                              }
                        else
                              qDebug() << "WARNING: unhandle similie mark type: " << currentNode.toElement().text();
                        }
                  // new voice specification
                  else if (!currentNode.nodeName().compare("Voices")) {
                        QString voicesString = currentNode.toElement().text();
                        auto voices = voicesString.split(" ");
                        bool contentAdded = false;
                        int voiceNum = -1;
                        for (auto currentVoice : voices) {
                              // if the voice is not -1 then we set voice
                              if (currentVoice.compare("-1"))
                                    voice = getNode(currentVoice, partInfo->voices);
                              voiceNum +=1;
                              if (currentVoice.toInt() == - 1) {
                                    if (contentAdded) continue;
                                    Fraction l = measure->len();
                                    // add a rest with length of l
                                    ChordRest* cr = new Rest(score);
                                    cr->setTrack(staffIdx * VOICES + voiceNum);
                                    cr->setDuration(l);
                                    cr->setDurationType(TDuration::DurationType::V_MEASURE);
                                    Segment* segment = measure->getSegment(SegmentType::ChordRest, tick);
                                    if(!segment->cr(staffIdx * VOICES + voiceNum)) {
                                          segment->add(cr);
                                          }
                                    contentAdded = true;
                                    continue;
                                    }
 
                              // read the beats that occur in the bar
                              int ticks = readBeats(voice.firstChild().toElement().text(), partInfo, measure, tick, staffIdx, voiceNum, tuplets, measureCounter);
                              if (ticks > 0)
                                    contentAdded = true;
                              // deal with possible anacrusis
                              if (ticks < measure->ticks() && voiceNum == 0) {
                                    int mticks = measure->ticks();
                                    measure->setLen(Fraction::fromTicks(ticks));
                                    int offset = mticks - measure->ticks();
                                    for (Measure* m = measure->nextMeasure(); m; m = m->nextMeasure()) {
                                          m->setTick(m->tick() - offset);
                                          }
                                    }
                              }
                        }
                  else if (!currentNode.nodeName().compare("XProperties")) {}
                  else { qDebug() << currentNode.nodeName(); }
                  // go to the next node in the tree
                  currentNode = currentNode.nextSibling();
                  }
            // increment the counter for parts
            staffIdx++;
            }
      }
 
 
//---------------------------------------------------------
//   checkForHeld
//---------------------------------------------------------
 
bool checkForHold(Segment* segment, QList<PitchValue> points)
      {
      bool same = false;
      Segment* prevSeg = segment->prev1(SegmentType::ChordRest);
      if (!prevSeg)
            return false;
      foreach (Element* e, prevSeg->annotations()) {
            if (e->type() == ElementType::TREMOLOBAR) {
                  QList<PitchValue> prevPoints = ((TremoloBar*)e)->points();
                  if (prevPoints.length() != points.length())
                        break;
 
                  auto iter = points.begin();
                  for (auto prevIter = prevPoints.begin(); prevIter != prevPoints.end(); ++prevIter) {
                        if (*prevIter == *iter)
                              same = true;
                        else  {
                              same = false;
                              break;
                              }
                        ++iter;
                        }
                  }
            }
      return same;
      }
 
 
//---------------------------------------------------------
//   addTremoloBar
//---------------------------------------------------------
 
void GuitarPro6::addTremoloBar(Segment* segment, int track, int whammyOrigin, int whammyMiddle, int whammyEnd)
      {
      if ((whammyOrigin == whammyEnd) && (whammyOrigin != whammyMiddle) && whammyMiddle != -1) {
            /* we are dealing with a dip. We need the chek for whammy middle
             * to be set as a predive has the same characteristics. */
            QList<PitchValue> points;
            points.append(PitchValue(0, whammyOrigin, false));
            points.append(PitchValue(50, whammyMiddle, false));
            points.append(PitchValue(100, whammyEnd, false));
            TremoloBar* b = new TremoloBar(score);
            b->setPoints(points);
            b->setTrack(track);
            segment->add(b);
            }
      else if (whammyOrigin == 0) {
            // we're dealing with a dive that does not continue from a previous marking
            QList<PitchValue> points;
            points.append(PitchValue(0, whammyOrigin, false));
            points.append(PitchValue(50, whammyMiddle, false));
            points.append(PitchValue(100, whammyEnd, false));
            TremoloBar* b = new TremoloBar(score);
            b->setPoints(points);
            b->setTrack(track);
            segment->add(b);
            }
      else if (whammyOrigin == whammyEnd && whammyMiddle == -1) {
            // this deals with a pre-dive
            QList<PitchValue> points;
            points.append(PitchValue(0, 0, false));
            points.append(PitchValue(50, whammyOrigin, false));
            points.append(PitchValue(100, whammyEnd, false));
            TremoloBar* b = new TremoloBar(score);
            b->setPoints(points);
            b->setTrack(track);
            segment->add(b);
            }
      else if (whammyMiddle != -1) {
            // dive starting from pre-existing point
            Segment* prevSeg = segment->prev1(SegmentType::ChordRest);
            if (!prevSeg)
                  return;
            foreach (Element* e, prevSeg->annotations()) {
                  if (e->type() == ElementType::TREMOLOBAR) {
                        QList<PitchValue> prevPoints = ((TremoloBar*)e)->points();
                        QList<PitchValue> points;
                        points.append(PitchValue(0, prevPoints[prevPoints.length()-1].pitch, false));
                        points.append(PitchValue(50, whammyOrigin, false));
                        points.append(PitchValue(100, whammyEnd, false));
                        TremoloBar* b = new TremoloBar(score);
                        b->setPoints(points);
                        b->setTrack(track);
                        segment->add(b);
                        }
                  }
            }
      else  {
            // a predive/dive
            QList<PitchValue> points;
            points.append(PitchValue(0, 0, false));
            points.append(PitchValue(50, whammyOrigin, false));
            points.append(PitchValue(100, whammyEnd, false));
 
            if (checkForHold(segment, points)) {
                  points.clear();
                  points.append(PitchValue(0, whammyEnd, false));
                  points.append(PitchValue(100, whammyEnd, false));
                  }
 
            TremoloBar* b = new TremoloBar(score);
            b->setPoints(points);
            b->setTrack(track);
            segment->add(b);
            }
      }
 
//---------------------------------------------------------
//   readMasterBars
//---------------------------------------------------------
 
void GuitarPro6::readMasterBars(GPPartInfo* partInfo)
      {
      Measure* measure = score->firstMeasure();
      int bar = 0;
      QDomNode nextMasterBar = partInfo->masterBars;
      nextMasterBar = nextMasterBar.nextSibling();
      int measureCounter = 0;
      ClefType oldClefId[staves];
      hairpins = new Hairpin*[staves];
      for (int i = 0; i < staves; i++)
            hairpins[i] = 0;
      do    {
            const GpBar& gpbar = bars[bar];
 
            if (!gpbar.marker.isEmpty()) {
                  Text* s = new RehearsalMark(score);
                  s->setPlainText(gpbar.marker.trimmed());
                  s->setTrack(0);
                  Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
                  segment->add(s);
                  }
 
            QDomNode masterBarElementTemplate = nextMasterBar.firstChild();
            for (int stave = 0; stave < staves; stave++) {
            QDomNode masterBarElement = masterBarElementTemplate;
            while (!masterBarElement.isNull()) {
                  if (bars[measureCounter].freeTime) {
                        if (measureCounter > 0 && (!bars[measureCounter - 1].freeTime || bars[measureCounter - 1].timesig != bars[measureCounter].timesig)) {
                              TimeSig* ts = new TimeSig(score);
                              ts->setSig(bars[measureCounter].timesig);
                              ts->setTrack(stave);
                              Measure* m = score->getCreateMeasure(measure->tick());
                              Segment* s = m->getSegment(SegmentType::TimeSig, measure->tick());
                              ts->setLargeParentheses(true);
                              s->add(ts);
                              StaffText* st = new StaffText(score);
                              st->setXmlText("Free time");
                              st->setParent(s);
                              st->setTrack(stave);
                              score->addElement(st);
                              }
                        }
                  else if (measureCounter > 0 && bars[measureCounter - 1].freeTime) {
                        TimeSig* ts = new TimeSig(score);
                        ts->setSig(bars[measureCounter].timesig);
                        ts->setTrack(stave);
                        Measure* m = score->getCreateMeasure(measure->tick());
                        Segment* s = m->getSegment(SegmentType::TimeSig, measure->tick());
                        ts->setLargeParentheses(false);
                        s->add(ts);
                        }
                  else
                        measure->setTimesig(bars[measureCounter].timesig);
                  measure->setLen(bars[measureCounter].timesig);
 
                  if (!bars[measureCounter].direction.compare("Fine") || (bars[measureCounter].direction.compare("") && !bars[measureCounter].directionStyle.compare("Jump"))) {
                        Segment* s = measure->getSegment(SegmentType::KeySig, measure->tick());
                        StaffText* st = new StaffText(score);
                        if (!bars[measureCounter].direction.compare("Fine"))
                              st->setXmlText("fine");
                        else if (!bars[measureCounter].direction.compare("DaCapo"))
                              st->setXmlText("Da Capo");
                        else if (!bars[measureCounter].direction.compare("DaCapoAlCoda"))
                              st->setXmlText("D.C. al Coda");
                        else if (!bars[measureCounter].direction.compare("DaCapoAlDoubleCoda"))
                              st->setXmlText("D.C. al Double Coda");
                        else if (!bars[measureCounter].direction.compare("DaCapoAlFine"))
                              st->setXmlText("D.C. al Fine");
                        else if (!bars[measureCounter].direction.compare("DaSegnoAlCoda"))
                              st->setXmlText("D.S. al Coda");
                        else if (!bars[measureCounter].direction.compare("DaSegnoAlDoubleCoda"))
                              st->setXmlText("D.S. al Double Coda");
                        else if (!bars[measureCounter].direction.compare("DaSegnoAlFine"))
                              st->setXmlText("D.S. al Fine");
                        else if (!bars[measureCounter].direction.compare("DaSegnoSegno"))
                              st->setXmlText("Da Segno Segno");
                        else if (!bars[measureCounter].direction.compare("DaSegnoSegnoAlCoda"))
                              st->setXmlText("D.S.S. al Coda");
                        else if (!bars[measureCounter].direction.compare("DaSegnoSegnoAlDoubleCoda"))
                              st->setXmlText("D.S.S. al Double Coda");
                        else if (!bars[measureCounter].direction.compare("DaSegnoSegnoAlFine"))
                              st->setXmlText("D.S.S. al Fine");
                        else if (!bars[measureCounter].direction.compare("DaCoda"))
                              st->setXmlText("Da Coda");
                        else if (!bars[measureCounter].direction.compare("DaDoubleCoda"))
                              st->setXmlText("Da Double Coda");
                        st->setParent(s);
                        st->setTrack(stave);
                        score->addElement(st);
                        bars[measureCounter].direction = "";
                  }
                  else if (bars[measureCounter].direction.compare("") && !bars[measureCounter].directionStyle.compare("Target")) {
                        Segment* s = measure->getSegment(SegmentType::BarLine, measure->tick());
                        Symbol* sym = new Symbol(score);
                        if (!bars[measureCounter].direction.compare("Segno"))
                              sym->setSym(SymId::segno);
                        else if (!bars[measureCounter].direction.compare("SegnoSegno")) {
                              sym->setSym(SymId::segno);
                              Segment* s2 = measure->getSegment(SegmentType::ChordRest, measure->tick());
                              Symbol* sym2 = new Symbol(score);
                              sym2->setSym(SymId::segno);
                              sym2->setParent(measure);
                              sym2->setTrack(stave);
                              s2->add(sym2);
                              }
                        else if (!bars[measureCounter].direction.compare("Coda"))
                              sym->setSym(SymId::coda);
                        else if (!bars[measureCounter].direction.compare("DoubleCoda")) {
                              sym->setSym(SymId::coda);
                              Segment* s2 = measure->getSegment(SegmentType::ChordRest, measure->tick());
                              Symbol* sym2 = new Symbol(score);
                              sym2->setSym(SymId::coda);
                              sym2->setParent(measure);
                              sym2->setTrack(stave);
                              s2->add(sym2);
                              }
                        sym->setParent(measure);
                        sym->setTrack(stave);
                        s->add(sym);
                        bars[measureCounter].direction = "";
                        }
 
                  // we no longer set the key here, the gpbar has the information stored in it
                  if (!masterBarElement.nodeName().compare("Fermatas")) {
                        QDomNode currentFermata = masterBarElement.firstChild();
                        while (!currentFermata.isNull()) {
                              QString fermata = currentFermata.lastChildElement("Offset").toElement().text();
                              currentFermata = currentFermata.nextSibling();
 
                              // get the fermata information and construct a gpFermata from them
                              QStringList fermataComponents = fermata.split("/", QString::SkipEmptyParts);
                              GPFermata gpFermata;
                              gpFermata.index = fermataComponents.at(0).toInt();
                              gpFermata.timeDivision = fermataComponents.at(1).toInt();
 
                              if (fermatas.contains(measureCounter)) {
                                    QList<GPFermata>* fermataList = fermatas.value(measureCounter);
                                    fermataList->push_back(gpFermata);
                                    }
                              else  {
                                    QList<GPFermata>* fermataList = new QList<GPFermata>;
                                    fermataList->push_back(gpFermata);
                                    fermatas.insert(measureCounter, fermataList);
                                    }
                              }
                        }
                  else if (!masterBarElement.nodeName().compare("Repeat")) {
                        bool start = !masterBarElement.attributes().namedItem("start").toAttr().value().compare("true");
                        int count = masterBarElement.attributes().namedItem("count").toAttr().value().toInt();
                        if (start)
                              measure->setRepeatStart(true);
                        else
                              measure->setRepeatEnd(true);
                        measure->setRepeatCount(count);
                        }
                  else if (!masterBarElement.nodeName().compare("Section")) {
                        QString letter = masterBarElement.firstChild().toElement().text();
                        QString text = masterBarElement.lastChild().toElement().text();
                        qDebug() << letter << text;
                        Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
                        bool rm = segment->findAnnotation(ElementType::REHEARSAL_MARK, 0, 1);
                        if (!rm && (!letter.isEmpty() || !text.isEmpty())) {
                              Text* s = new RehearsalMark(score);
                              s->setPlainText(letter.isEmpty() ? text : letter);
                              s->setTrack(0);
                              segment->add(s);
                              }
                        }
                  else if (!masterBarElement.nodeName().compare("AlternateEndings")) {
                        QString endNumbers = masterBarElement.toElement().text();
                        Ms::Volta* volta = new Ms::Volta(score);
                        volta->endings().clear();
                        volta->setText(endNumbers.replace(" ",","));
                        volta->setTick(measure->tick());
                        volta->setTick2(measure->tick() + measure->ticks());
                        score->addElement(volta);
                        }
                  else if (!masterBarElement.nodeName().compare("Bars") && stave == staves - 1) {
                        readBars(&masterBarElement, measure, oldClefId, partInfo, measureCounter);
                        for (int i = 0; i < staves * VOICES; ++i) {
                              Slur* slur = legatos[i];
                              if (slur) {
                                     if (measure->prevMeasure() && !measure->hasVoice(i)) {
                                           //find last chord in track
                                           Chord* c = nullptr;
                                          for (const Segment* seg = measure->prevMeasure()->last(); seg; seg = seg->prev1()) {
                                                Element* el = seg->element(i);
                                                if (el && el->isChord()) {
                                                      c = static_cast<Chord*>(el);
                                                      break;
                                                      }
                                                }
                                          if (c) {
                                                 slur->setTick2(c->tick());
                                                 score->addElement(slur);
                                                 legatos[slur->track()] = 0;
                                                 }
                                          }
                                    }
                              }
                        }
                  masterBarElement = masterBarElement.nextSibling();
                  }
                  }
            measureCounter++;
            nextMasterBar = nextMasterBar.nextSibling();
            measure = measure->nextMeasure();
            bar++;
            } while (!nextMasterBar.isNull());
      }
 
//---------------------------------------------------------
//   readGpif
//---------------------------------------------------------
 
void GuitarPro6::readGpif(QByteArray* data)
      {
      //qDebug() << QString(*data);
      QDomDocument qdomDoc;
      qdomDoc.setContent(*data);
      QDomElement qdomElem = qdomDoc.documentElement();
      // GPRevision node
      QDomNode revision = qdomElem.firstChildElement("GPRevision");
      // Score node
      QDomNode scoreNode = qdomElem.firstChildElement("Score");
      readScore(&scoreNode);
      // MasterTrack node
      QDomNode masterTrack = qdomElem.firstChildElement("MasterTrack");
      readMasterTracks(&masterTrack);
      // Tracks node
      QDomNode eachTrack = qdomElem.firstChildElement("Tracks");
      readTracks(&eachTrack);
 
      slides = new QMap<int,int>();
      previousTempo = -1;
      // now we know how many staves there are from readTracks, we can initialise slurs (for hammer/pulloff)
      // and legatos
      slurs = new Slur*[staves * VOICES];
      legatos = new Slur*[staves * VOICES];
      letRings = new Pedal*[staves];
      barres = new TextLine*[staves];
      palmMutes = new TextLine*[staves];
      ottava.assign(staves * VOICES, 0);
      ottavaFound.assign(staves * VOICES, 0);
      ottavaValue.assign(staves * VOICES, "");
      for (int i = 0; i < staves * VOICES; ++i) {
            slurs[i] = 0;
            legatos[i] = 0;
            }
      for (int i = 0; i < staves; ++i) {
            letRings[i] = 0;
            barres[i] = 0;
            palmMutes[i] = 0;
            }
 
      // MasterBars node
      GPPartInfo partInfo;
      QDomNode masterBars = eachTrack.nextSibling();
      QDomNode bars = masterBars.nextSibling();
      QDomNode voices = bars.nextSibling();
      QDomNode beats = voices.nextSibling();
      QDomNode notes = beats.nextSibling();
      QDomNode rhythms = notes.nextSibling();
 
      // set up the partInfo struct to contain information from the file
      partInfo.masterBars = masterBars.firstChild();
      partInfo.bars = bars.firstChild();
      partInfo.voices = voices.firstChild();
      partInfo.beats = beats.firstChild();
      partInfo.notes = notes.firstChild();
      partInfo.rhythms = rhythms.firstChild();
 
      measures = findNumMeasures(&partInfo);
 
      createMeasures();
      fermatas.clear();
      readMasterBars(&partInfo);
      // complete slurs (GP6 sometimes output destination=true even for last beat)
      for (int i = 0; i < staves * VOICES; ++i) {
            Slur* slur = legatos[i];
            if (slur) {
                  //find last chord in track
                  Chord* c = nullptr;
                  for (const Segment* seg = score->lastSegment(); seg; seg = seg->prev1()) {
                        Element* el = seg->element(i);
                        if (el && el->isChord()) {
                              c = static_cast<Chord*>(el);
                              break;
                              }
                        }
                  if (c) {
                        slur->setTick2(c->tick());
                        score->addElement(slur);
                        legatos[slur->track()] = 0;
                        }
                  else {
                        delete slur;
                        legatos[slur->track()] = 0;
                        }
                  }
            }
 
      // change the tuning to deal with transposition
      // It's needed to create correct tabs
      for (Part * p : score->parts()) {
            Instrument* instr = p->instrument();
            if (instr->transpose().chromatic == 0)
                  continue;
            const StringData* sd = instr->stringData();
            if (sd) {
                  int tuning[sd->strings()];
                  int frets   = sd->frets();
                  int strings;
                  for (strings = 0; strings < sd->strings(); strings++) {
                        tuning[strings] = sd->stringList()[strings].pitch - instr->transpose().chromatic;
                        }
                  StringData* stringData = new StringData(frets, strings, tuning);
                  instr->setStringData(*stringData);
                  }
            }
      // set the starting tempo of the score
      if (tempo > 0)
            setTempo(tempo, score->firstMeasure());
 
      for (auto i : fretDiagrams.keys()) {
            qDeleteAll(fretDiagrams.value(i));
            fretDiagrams[i].clear();
            }
      }
 
//---------------------------------------------------------
//   read
//---------------------------------------------------------
 
void GuitarPro6::read(QFile* fp)
      {
      f = fp;
      this->buffer = new QByteArray();
      *(this->buffer) = fp->readAll();
      // decompress and read files contained within GPX file
      readGPX(this->buffer);
      delete this->buffer;
      }
 
//---------------------------------------------------------
//   readBeatEffects
//---------------------------------------------------------
 
int GuitarPro6::readBeatEffects(int, Segment*)
      {
      qDebug("reading beat effects (.gpx)...\n");
      return 0;
      }
 
//---------------------------------------------------------
//   addBarre
//---------------------------------------------------------
 
void GuitarPro6::addBarre(Chord* chord, int staffIdx, bool hasBarre, QString text)
      {
      TextLine* tl = barres[staffIdx];
      int tick = chord->tick();
      // Check we have a similar barre in previous voice
      // if we find one, we should try to expand it instead of creating a new one or overlapping
      int startTick = tick;
      int endTick = tick;
      if (tl) {
            startTick = tl->tick() < tick ? tl->tick() : tick;
            endTick = tl->tick2() > tick ? tl->tick2() : tick;
            }
      auto spanners = score->spannerMap().findOverlapping(startTick, endTick);
      TextLine* foundTextLine = nullptr;
      for (auto interval : spanners) {
           Spanner* sp = interval.value;
           if (sp->type() == ElementType::TEXTLINE && sp->staffIdx() == staffIdx && sp != barres[staffIdx]) {
                 TextLine* ftl = static_cast<TextLine*>(sp);
                 if (!ftl->beginText().compare(text)) {
                       foundTextLine = ftl;
                       break;
                       }
                 }
           }
 
      if (hasBarre) {
            if (!foundTextLine) {
                  if (tl && tl->beginText() == text) {
                        // we already have barre, let's expand it
                        tl->setTick2(tick + chord->actualTicks());
                        }
                  else {
                        // we don't have barre. Let's create one
                        tl = new TextLine(score);
                        tl->setParent(0);
                        tl->setBeginText(text);
                        tl->setContinueText(text);
                        tl->setEndHookType(HookType::HOOK_90);
                        tl->setTick(tick);
                        tl->setTrack(chord->track());
                        tl->setTrack2(chord->track());
                        tl->setEndHookHeight(Spatium(0.5));
                        tl->setYoff(-2.5);
                        barres[staffIdx] = tl;
                        score->addElement(tl);
                        }
                  }
            else {
                  // we have an overlapping spanner at tick : found, reuse it if we can
                  if (tl) {
                        if (!tl->beginText().compare(text)) {
                              if (tl->tick() < foundTextLine->tick())
                                    foundTextLine->setTick(tl->tick());
                              if (tl->tick2() > foundTextLine->tick2())
                                    foundTextLine->setTick2(tl->tick2());
                              score->removeElement(tl);
                              delete tl;
                              barres[staffIdx] = foundTextLine;
                              }
                        }
                  }
            }
      else {
            // no more barre
            if (!foundTextLine) {
                  if (tl)
                        barres[staffIdx] = 0;
                  }
            else {
                  if (tl) { // we could have another barre overlapping at end. Let's reuse it.
                        if (tl->tick() < foundTextLine->tick())
                              foundTextLine->setTick(tl->tick());
                        if (tl->tick2() > foundTextLine->tick2())
                              foundTextLine->setTick2(tl->tick2());
                        score->removeElement(tl);
                        delete tl;
                        barres[staffIdx] = foundTextLine;
                        }
                  }
            }
      }
 
}

V766 An item with the same key '"mrcs"' has already been added.

V766 An item with the same key '"mrcs"' has already been added.

V773 Visibility scope of the 'stringData' pointer was exited without releasing the memory. A memory leak is possible.

V688 The 'slide' local variable possesses the same name as one of the class members, which can result in a confusion.

V688 The 'f' local variable possesses the same name as one of the class members, which can result in a confusion.

V688 The 'key' local variable possesses the same name as one of the class members, which can result in a confusion.

V688 The 'bars' local variable possesses the same name as one of the class members, which can result in a confusion.

V774 The 'slur' pointer was used after the memory was released.

V773 Visibility scope of the 'stringData' pointer was exited without releasing the memory. A memory leak is possible.

V547 Expression 'sd' is always true.