MuseScore/mscore/importgtp.cpp
2019-06-10 17:54:06 +02:00

2945 lines
119 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2011 Werner Schweer
//
// 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 "importptb.h"
#include "globals.h"
#include "preferences.h"
#include <libmscore/measurebase.h>
#include <libmscore/text.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/dynamic.h>
#include <libmscore/arpeggio.h>
#include <libmscore/volta.h>
#include <libmscore/fret.h>
#include <libmscore/instrtemplate.h>
#include <libmscore/glissando.h>
#include <libmscore/chordline.h>
#include <libmscore/instrtemplate.h>
#include <libmscore/hairpin.h>
#include <libmscore/ottava.h>
#include <libmscore/notedot.h>
#include <libmscore/stafftext.h>
#include <libmscore/sym.h>
#include <libmscore/textline.h>
#include <libmscore/letring.h>
#include <libmscore/palmmute.h>
#include <libmscore/vibrato.h>
namespace Ms {
//---------------------------------------------------------
// errmsg
//---------------------------------------------------------
const char* const GuitarPro::errmsg[] = {
"no error",
"unknown file format",
"unexpected end of file",
"bad number of strings",
};
#ifdef _MSC_VER
#pragma optimize("", off)
#endif
//---------------------------------------------------------
// GpBar
//---------------------------------------------------------
GpBar::GpBar()
{
barLine = BarLineType::NORMAL;
keysig = GP_INVALID_KEYSIG;
timesig = Fraction(4,4);
repeatFlags = Repeat::NONE;
repeats = 2;
}
//---------------------------------------------------------
// GuitarPro
//---------------------------------------------------------
GuitarPro::GuitarPro(MasterScore* s, int v)
{
score = s;
version = v;
_codec = QTextCodec::codecForName(preferences.getString(PREF_IMPORT_GUITARPRO_CHARSET).toLatin1());
voltaSequence = 1;
tempo = -1;
}
GuitarPro::~GuitarPro()
{
delete[] slurs;
}
//---------------------------------------------------------
// skip
//---------------------------------------------------------
void GuitarPro::skip(qint64 len)
{
f->seek(f->pos() + len);
/*char c;
while (len--)
read(&c, 1);*/
}
//---------------------------------------------------------
// createTuningString
//---------------------------------------------------------
void GuitarPro::createTuningString(int strings, int tuning[])
{
const char* tune[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
//TODO-ws score->tuning.clear();
std::vector<int> pitch;
for (int i = 0; i < strings; ++i) {
pitch.push_back(tuning[i]);
//score->tuning += tune[tuning[i] % 12];
}
std::string t;
for (auto i : pitch) {
t += tune[i % 12];
t += " ";
}
tunings.push_back(t);
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
void GuitarPro::read(void* p, qint64 len)
{
if (len == 0)
return;
qint64 rv = f->read((char*)p, len);
if (rv != len) {
Q_ASSERT(rv == len); //to have assert in debug and no warnings from AppVeyor in release
}
curPos += len;
}
//---------------------------------------------------------
// readChar
//---------------------------------------------------------
int GuitarPro::readChar()
{
signed char c;
read(&c, 1);
return c;
}
//---------------------------------------------------------
// readUChar
//---------------------------------------------------------
int GuitarPro::readUChar()
{
uchar c;
read(&c, 1);
return c;
}
//---------------------------------------------------------
// readPascalString
//---------------------------------------------------------
QString GuitarPro::readPascalString(int n)
{
uchar l = readUChar();
std::vector<char> s(l + 1);
//char s[l + 1];
read(&s[0], l);
s[l] = 0;
if (n - l > 0)
skip(n - l);
if (_codec)
return _codec->toUnicode(&s[0]);
else
return QString(&s[0]);
}
//---------------------------------------------------------
// readWordPascalString
//---------------------------------------------------------
QString GuitarPro::readWordPascalString()
{
int l = readInt();
std::vector<char> c(l + 1);
//char c[l+1];
read(&c[0], l);
c[l] = 0;
if (_codec)
return _codec->toUnicode(&c[0]);
else
return QString::fromLocal8Bit(&c[0]);
}
//---------------------------------------------------------
// readBytePascalString
//---------------------------------------------------------
QString GuitarPro::readBytePascalString()
{
int l = readUChar();
std::vector<char> c(l + 1);
//char c[l+1];
read(&c[0], l);
c[l] = 0;
if(_codec)
return _codec->toUnicode(&c[0]);
else
return QString::fromLocal8Bit(&c[0]);
}
//---------------------------------------------------------
// readDelphiString
//---------------------------------------------------------
QString GuitarPro::readDelphiString()
{
int maxl = readInt();
int l = readUChar();
if (maxl != l + 1 && maxl > 255) {
qFatal("readDelphiString: first word doesn't match second byte");
l = maxl - 1;
}
std::vector<char> c(l + 1);
//char c[l + 1];
read(&c[0], l);
c[l] = 0;
std::string g(&c[0]);
if (g.find("2nd ") == 0) {
; //?? int k = 1;
}
if (_codec)
return _codec->toUnicode(&c[0]);
else
return QString::fromLatin1(&c[0]);
}
//---------------------------------------------------------
// readInt
//---------------------------------------------------------
int GuitarPro::readInt()
{
uchar x;
read(&x, 1);
int r = x;
read(&x, 1);
r += x << 8;
read(&x, 1);
r += x << 16;
read(&x, 1);
r += x << 24;
return r;
}
//---------------------------------------------------------
// initGuitarProDrumset
//---------------------------------------------------------
void GuitarPro::initGuitarProDrumset()
{
gpDrumset = new Drumset;
for (int i = 0; i < 128; ++i) {
gpDrumset->drum(i).notehead = NoteHead::Group::HEAD_INVALID;
gpDrumset->drum(i).line = 0;
gpDrumset->drum(i).shortcut = 0;
gpDrumset->drum(i).voice = 0;
gpDrumset->drum(i).stemDirection = Direction::UP;
}
// new drumset determined via guitar pro (third argument specifies position on staff, 10 = C3, 9 = D3, 8 = E3,...)
gpDrumset->drum(27) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "High Q"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(28) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Slap"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(29) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Scratch Push"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(30) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Scratch Pull"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(31) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Sticks"), NoteHead::Group::HEAD_CROSS, 3, Direction::UP);
gpDrumset->drum(32) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Square Click"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(33) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Metronome Click"), NoteHead::Group::HEAD_CROSS, 3, Direction::UP);
gpDrumset->drum(34) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Metronome Bell"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(35) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Acoustic Bass Drum"), NoteHead::Group::HEAD_NORMAL, 7, Direction::UP);
gpDrumset->drum(36) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Bass Drum 1"), NoteHead::Group::HEAD_NORMAL, 7, Direction::UP);
gpDrumset->drum(37) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Side Stick"), NoteHead::Group::HEAD_CROSS, 3, Direction::UP);
gpDrumset->drum(38) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Acoustic Snare"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(39) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Hand Clap"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(40) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Electric Snare"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(41) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Floor Tom"), NoteHead::Group::HEAD_NORMAL, 6, Direction::UP);
gpDrumset->drum(42) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Closed Hi-Hat"), NoteHead::Group::HEAD_CROSS, -1, Direction::UP);
gpDrumset->drum(43) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "High Floor Tom"), NoteHead::Group::HEAD_NORMAL, 6, Direction::UP);
gpDrumset->drum(44) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Pedal Hi-Hat"), NoteHead::Group::HEAD_CROSS, 9, Direction::UP);
gpDrumset->drum(45) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Tom"), NoteHead::Group::HEAD_NORMAL, 5, Direction::UP);
gpDrumset->drum(46) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Open Hi-Hat"), NoteHead::Group::HEAD_XCIRCLE, -1, Direction::UP);
gpDrumset->drum(47) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low-Mid Tom"), NoteHead::Group::HEAD_NORMAL, 4, Direction::UP);
gpDrumset->drum(48) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Hi-Mid Tom"), NoteHead::Group::HEAD_NORMAL, 2, Direction::UP);
gpDrumset->drum(49) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Crash Cymbal 1"), NoteHead::Group::HEAD_CROSS, -1, Direction::UP);
gpDrumset->drum(50) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "High Tom"), NoteHead::Group::HEAD_NORMAL, 1, Direction::UP);
gpDrumset->drum(51) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Ride Cymbal 1"), NoteHead::Group::HEAD_CROSS, 0, Direction::UP);
gpDrumset->drum(52) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Chinese Cymbal"), NoteHead::Group::HEAD_CROSS, -3, Direction::UP);
gpDrumset->drum(53) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Ride Bell"), NoteHead::Group::HEAD_DIAMOND, 0, Direction::UP);
gpDrumset->drum(54) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Tambourine"), NoteHead::Group::HEAD_CROSS, 2, Direction::UP);
gpDrumset->drum(55) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Splash Cymbal"), NoteHead::Group::HEAD_CROSS, -2, Direction::UP);
gpDrumset->drum(56) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Cowbell Medium"), NoteHead::Group::HEAD_NORMAL, 0, Direction::UP);
gpDrumset->drum(57) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Crash Cymbal 2"), NoteHead::Group::HEAD_CROSS, -2, Direction::UP);
gpDrumset->drum(58) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Vibraslap"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(59) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Ride Cymbal 2"), NoteHead::Group::HEAD_DIAMOND, 0, Direction::UP);
gpDrumset->drum(60) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Hi Bongo"), NoteHead::Group::HEAD_NORMAL, 8, Direction::UP);
gpDrumset->drum(61) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Bongo"), NoteHead::Group::HEAD_NORMAL, 9, Direction::UP);
gpDrumset->drum(62) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Mute Hi Conga"), NoteHead::Group::HEAD_CROSS, 5, Direction::UP);
gpDrumset->drum(63) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Open Hi Conga"), NoteHead::Group::HEAD_CROSS, 4, Direction::UP);
gpDrumset->drum(64) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Conga"), NoteHead::Group::HEAD_CROSS, 6, Direction::UP);
gpDrumset->drum(65) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "High Timbale"), NoteHead::Group::HEAD_CROSS, 8, Direction::UP);
gpDrumset->drum(66) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Timbale"), NoteHead::Group::HEAD_CROSS, 9, Direction::UP);
gpDrumset->drum(67) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "High Agogo"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(68) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Agogo"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(69) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Cabasa"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(70) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Maracas"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(71) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Short Whistle"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(72) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Long Whistle"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(73) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Short Güiro"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(74) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Long Güiro"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(75) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Claves"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(76) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Hi Wood Block"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(77) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Low Wood Block"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(78) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Mute Cuica"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(79) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Open Cuica"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(80) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Mute Triangle"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(81) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Open Triangle"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(82) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Shaker"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(83) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Sleigh Bell"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(84) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Bell Tree"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(85) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Castanets"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(86) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Mute Surdo"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(87) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Open Surdo"), NoteHead::Group::HEAD_NORMAL, 3, Direction::UP);
gpDrumset->drum(91) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Snare (Rim shot)"), NoteHead::Group::HEAD_DIAMOND, 3, Direction::UP);
gpDrumset->drum(93) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Ride (Edge)"), NoteHead::Group::HEAD_CROSS, 0, Direction::UP);
//Additional clutch presets (midi by default can't play this)
gpDrumset->drum(99) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Cowbell Low"), NoteHead::Group::HEAD_TRIANGLE_UP, 1, Direction::UP);
gpDrumset->drum(102) = DrumInstrument(QT_TRANSLATE_NOOP("drumset", "Cowbell High"), NoteHead::Group::HEAD_TRIANGLE_UP, -1, Direction::UP);
}
//---------------------------------------------------------
// addPalmMate
//---------------------------------------------------------
void GuitarPro::addPalmMute(Note* note)
{
int track = note->track();
while (int(_palmMutes.size()) < track + 1)
_palmMutes.push_back(0);
Chord* chord = note->chord();
if (_palmMutes[track]) {
PalmMute* pm = _palmMutes[track];
Chord* lastChord = toChord(pm->endCR());
if (lastChord == note->chord())
return;
//
// extend the current palm mute or start a new one
//
Fraction tick = note->chord()->segment()->tick();
if (pm->tick2() < tick)
_palmMutes[track] = 0;
else {
pm->setTick2(chord->tick() + chord->actualTicks());
pm->setEndElement(chord);
}
}
if (!_palmMutes[track]) {
PalmMute* pm = new PalmMute(score);
_palmMutes[track] = pm;
Segment* segment = chord->segment();
Fraction tick = segment->tick();
pm->setTick(tick);
pm->setTick2(tick + chord->actualTicks());
pm->setTrack(track);
pm->setTrack2(track);
pm->setStartElement(chord);
pm->setEndElement(chord);
score->addElement(pm);
}
}
//---------------------------------------------------------
// addLetRing
//---------------------------------------------------------
void GuitarPro::addLetRing(Note* note)
{
int track = note->track();
while (int(_letRings.size()) < track + 1)
_letRings.push_back(0);
Chord* chord = note->chord();
if (_letRings[track]) {
LetRing* lr = _letRings[track];
Chord* lastChord = toChord(lr->endCR());
if (lastChord == note->chord())
return;
//
// extend the current "let ring" or start a new one
//
Fraction tick = note->chord()->segment()->tick();
if (lr->tick2() < tick)
_letRings[track] = 0;
else {
lr->setTick2(chord->tick() + chord->actualTicks());
lr->setEndElement(chord);
}
}
if (!_letRings[track]) {
LetRing* lr = new LetRing(score);
_letRings[track] = lr;
Segment* segment = chord->segment();
Fraction tick = segment->tick();
lr->setTick(tick);
lr->setTick2(tick + chord->actualTicks());
lr->setTrack(track);
lr->setTrack2(track);
lr->setStartElement(chord);
lr->setEndElement(chord);
score->addElement(lr);
}
}
//---------------------------------------------------------
// addVibrato
//---------------------------------------------------------
void GuitarPro::addVibrato(Note* note, Vibrato::Type type)
{
int track = note->track();
while (int(_vibratos.size()) < track + 1)
_vibratos.push_back(0);
Chord* chord = note->chord();
if (_vibratos[track]) {
Vibrato* v = _vibratos[track];
if (v->vibratoType() == type) {
Chord* lastChord = toChord(v->endCR());
if (lastChord == note->chord())
return;
//
// extend the current "vibrato" or start a new one
//
Fraction tick = note->chord()->segment()->tick();
if (v->tick2() < tick)
_vibratos[track] = 0;
else {
v->setTick2(chord->tick() + chord->actualTicks());
v->setEndElement(chord);
}
}
else
_vibratos[track] = 0;
}
if (!_vibratos[track]) {
Vibrato* v = new Vibrato(score);
v->setVibratoType(type);
_vibratos[track] = v;
Segment* segment = chord->segment();
Fraction tick = segment->tick();
v->setTick(tick);
v->setTick2(tick + chord->actualTicks());
v->setTrack(track);
v->setTrack2(track);
v->setStartElement(chord);
v->setEndElement(chord);
score->addElement(v);
}
}
//---------------------------------------------------------
// addTap
//---------------------------------------------------------
void GuitarPro::addTap(Note* note)
{
addTextToNote("T", Align::CENTER, note);
}
//---------------------------------------------------------
// addSlap
//---------------------------------------------------------
void GuitarPro::addSlap(Note* note)
{
addTextToNote("S", Align::CENTER, note);
}
//---------------------------------------------------------
// addPop
//---------------------------------------------------------
void GuitarPro::addPop(Note* note)
{
addTextToNote("P", Align::CENTER, note);
}
//---------------------------------------------------------
// addTextToNote
//---------------------------------------------------------
Text* GuitarPro::addTextToNote(QString string, Align a, Note* note)
{
Text* text = new Text(score);
//TODO-ws if (textStyle.underline())
// text->setFramed(true);
text->setAlign(a);
bool use_harmony = string[string.size() - 1] == '\\';
if (use_harmony)
string.resize(string.size() - 1);
text->setPlainText(string);
// text->setTextStyleType(use_harmony ? TextStyleType::HARMONY : TextStyleType::TECHNIQUE);
note->add(text);
return text;
}
void GuitarPro::setupTupletStyle(Tuplet* tuplet)
{
bool real;
switch (tuplet->ratio().numerator()) {
case 2: real = (tuplet->ratio().denominator() == 3); break;
case 3:
case 4: real = (tuplet->ratio().denominator() == 2); break;
case 5:
case 6:
case 7: real = (tuplet->ratio().denominator() == 4); break;
case 9:
case 10:
case 11:
case 12:
case 13: real = (tuplet->ratio().denominator() == 8); break;
default: real = false;
}
if (!real) {
tuplet->setNumberType(TupletNumberType::SHOW_RELATION);
tuplet->setPropertyFlags(Pid::NUMBER_TYPE, PropertyFlags::UNSTYLED);
}
}
//---------------------------------------------------------
// setTuplet
//---------------------------------------------------------
void GuitarPro::setTuplet(Tuplet* tuplet, int tuple)
{
switch (tuple) {
case 3:
tuplet->setRatio(Fraction(3,2));
break;
case 5:
tuplet->setRatio(Fraction(5,4));
break;
case 6:
tuplet->setRatio(Fraction(6,4));
break;
case 7:
tuplet->setRatio(Fraction(7,4));
break;
case 9:
tuplet->setRatio(Fraction(9,8));
break;
case 10:
tuplet->setRatio(Fraction(10,8));
break;
case 11:
tuplet->setRatio(Fraction(11,8));
break;
case 12:
tuplet->setRatio(Fraction(12,8));
break;
case 13:
tuplet->setRatio(Fraction(13,8));
break;
default:
qFatal("unsupported tuplet %d\n", tuple);
}
}
//---------------------------------------------------------
// addDynamic
//---------------------------------------------------------
void GuitarPro::addDynamic(Note* note, int d)
{
if (d < 0)
return;
if (!note->chord()){
qDebug() << "addDynamics: No chord associated with this note";
return;
}
Segment* s = nullptr;
if (note->chord()->isGrace()) {
Chord* parent = static_cast<Chord*>(note->chord()->parent());
s = parent->segment();
}
else
s = note->chord()->segment();
if (!s->findAnnotation(ElementType::DYNAMIC, note->staffIdx() * VOICES, note->staffIdx() * VOICES + VOICES - 1)) {
Dynamic* dyn = new Dynamic(score);
// guitar pro only allows their users to go from ppp to fff
QString map_dyn[] = {"f","ppp","pp","p","mp","mf","f","ff","fff"};
dyn->setDynamicType(map_dyn[d]);
dyn->setTrack(note->track());
s->add(dyn);
}
}
//---------------------------------------------------------
// readVolta
//---------------------------------------------------------
void GuitarPro::readVolta(GPVolta* gpVolta, Measure* m)
{
/* Volta information is at most eight bits
* signifying which numbers should appear in the
* volta. A single bit 1 represents we should show
* 1, 100 represents 3, 10000 represents 6, 10101
* represents 1,3,5 etc. */
if (gpVolta->voltaInfo.length() != 0) {
// we have volta information - set up a volta
Ms::Volta* volta = new Ms::Volta(score);
volta->endings().clear();
QString voltaTextString = "";
// initialise count to 1 as the first bit processed with represesnt first time volta
int count = 0;
int binaryNumber = 0;
// iterate through the volta information and determine the decimal numbers for voltas
auto iter = gpVolta->voltaInfo.begin();
while (iter != gpVolta->voltaInfo.end()) {
switch (gpVolta->voltaType) {
case GP_VOLTA_FLAGS:
count++;
if (*iter == 1) { // we want this number to be displayed in the volta
if (voltaTextString == "")
voltaTextString += QString::number(count);
else
voltaTextString += "," + QString::number(count);
// add the decimal number to the endings field of voltas as well as the text
volta->endings().append(count);
}
++iter;
break;
case GP_VOLTA_BINARY:
// find the binary number in decimal
if (*iter == 1) {
binaryNumber += pow(2,count);
}
++iter;
if (iter == gpVolta->voltaInfo.end()) {
// display all numbers in the volta from voltaSequence to the decimal
while (voltaSequence <= binaryNumber) {
if (voltaTextString == "")
voltaTextString = QString::number(voltaSequence);
else
voltaTextString += "," + QString::number(voltaSequence);
volta->endings().append(voltaSequence);
voltaSequence++;
}
}
count++;
break;
}
}
volta->setText(XmlWriter::xmlString(voltaTextString));
volta->setTick(m->tick());
volta->setTick2(m->tick() + m->ticks());
score->addElement(volta);
}
}
//---------------------------------------------------------
// readBend
// bend graph
//---------------------------------------------------------
void GuitarPro::readBend(Note* note)
{
readUChar(); // icon
/*int amplitude =*/ readInt(); // shown aplitude
int numPoints = readInt(); // the number of points in the bend
// there are no notes in the bend, exit the function
if (numPoints == 0)
return;
Bend* bend = new Bend(note->score());
//TODO-ws bend->setNote(note);
for (int i = 0; i < numPoints; ++i) {
int bendTime = readInt();
int bendPitch = readInt();
int bendVibrato = readUChar();
bend->points().append(PitchValue(bendTime, bendPitch, bendVibrato));
}
//TODO-ws bend->setAmplitude(amplitude);
bend->setTrack(note->track());
note->add(bend);
}
//---------------------------------------------------------
// readLyrics
//---------------------------------------------------------
void GuitarPro::readLyrics()
{
gpLyrics.lyricTrack = readInt(); // lyric track
gpLyrics.fromBeat = readInt();
gpLyrics.beatCounter = 0;
QString lyrics = readWordPascalString();
lyrics.replace(QRegExp("\n"), " ");
lyrics.replace(QRegExp("\r"), " ");
auto sl = lyrics.split(" ", QString::KeepEmptyParts);
//gpLyrics.lyrics = lyrics.split(" ", QString::KeepEmptyParts);
for (auto& str : sl) {
/*while (str[0] == '-')
{
gpLyrics.lyrics.push_back("aa");
str = str.substr(1);
}*/
gpLyrics.lyrics.push_back(str);
}
for (int i = 0; i < 4; ++i) {
readInt();
readWordPascalString();
}
}
//---------------------------------------------------------
// createSlide
//---------------------------------------------------------
void GuitarPro::createSlide(int sl, ChordRest* cr, int staffIdx, Note* /*note*/)
{
// shift / legato slide
if (sl == SHIFT_SLIDE || sl == LEGATO_SLIDE) {
Glissando* s = new Glissando(score);
//s->setXmlText("");
s->setGlissandoType(GlissandoType::STRAIGHT);
cr->add(s);
s->setAnchor(Spanner::Anchor::NOTE);
Segment* prevSeg = cr->segment()->prev1(SegmentType::ChordRest);
Element* prevElem = prevSeg->element(staffIdx);
if (prevElem) {
if (prevElem->type() == ElementType::CHORD) {
Chord* prevChord = static_cast<Chord*>(prevElem);
/** TODO we should not just take the top note here
* but the /correct/ note need to check whether GP
* supports multi-note gliss. I think it can in modern
* versions */
s->setStartElement(prevChord->upNote());
s->setTick(prevSeg->tick());
s->setTrack(staffIdx);
s->setParent(prevChord->upNote());
s->setText("");
s->setGlissandoType(GlissandoType::STRAIGHT);
if (sl == LEGATO_SLIDE)
createSlur(true, staffIdx, prevChord);
}
}
Chord* chord = (Chord*) cr;
/* TODO again here, we should not just set the up note but the
* /correct/ note need to check whether GP supports
* multi-note gliss. I think it can in modern versions */
s->setEndElement(chord->upNote());
s->setTick2(chord->segment()->tick());
s->setTrack2(staffIdx);
score->addElement(s);
if (sl == LEGATO_SLIDE)
createSlur(false, staffIdx, cr);
}
// slide out downwards (fall)
if (sl & SLIDE_OUT_DOWN) {
ChordLine* cl = new ChordLine(score);
cl->setChordLineType(ChordLineType::FALL);
cl->setStraight(true);
//TODO-ws cl->setNote(note);
cr->add(cl);
}
// slide out upwards (doit)
if (sl & SLIDE_OUT_UP) {
ChordLine* cl = new ChordLine(score);
cl->setChordLineType(ChordLineType::DOIT);
cl->setStraight(true);
//TODO-ws cl->setNote(note);
cr->add(cl);
}
// slide in from below (plop)
if (sl & SLIDE_IN_BELOW) {
ChordLine* cl = new ChordLine(score);
cl->setChordLineType(ChordLineType::PLOP);
cl->setStraight(true);
//TODO-ws cl->setNote(note);
cr->add(cl);
}
// slide in from above (scoop)
if (sl & SLIDE_IN_ABOVE) {
ChordLine* cl = new ChordLine(score);
cl->setChordLineType(ChordLineType::SCOOP);
cl->setStraight(true);
//TODO-ws cl->setNote(note);
cr->add(cl);
}
}
//---------------------------------------------------------
// readChannels
//---------------------------------------------------------
void GuitarPro::readChannels()
{
for (int i = 0; i < GP_MAX_TRACK_NUMBER * 2; ++i) {
channelDefaults[i].patch = readInt();
channelDefaults[i].volume = readUChar() * 8 - 1;
channelDefaults[i].pan = readUChar() * 8 - 1;
channelDefaults[i].chorus = readUChar() * 8 - 1;
channelDefaults[i].reverb = readUChar() * 8 - 1;
channelDefaults[i].phase = readUChar() * 8 - 1;
channelDefaults[i].tremolo = readUChar() * 8 - 1;
// defaults of 255, or any value above 127, are set to 0 (Musescore range is 0-127)
if (channelDefaults[i].patch > 127) { channelDefaults[i].patch = 0; }
if (channelDefaults[i].volume > 127) { channelDefaults[i].volume = 0; }
if (channelDefaults[i].pan > 127) { channelDefaults[i].pan = 0; }
if (channelDefaults[i].chorus > 127) { channelDefaults[i].chorus = 0; }
if (channelDefaults[i].reverb > 127) { channelDefaults[i].reverb = 0; }
if (channelDefaults[i].phase > 127) { channelDefaults[i].phase = 0; }
if (channelDefaults[i].tremolo > 127) { channelDefaults[i].tremolo = 0; }
// skip over blank information included for backwards compatibility with 3.0
skip(2);
}
}
//---------------------------------------------------------
// len2fraction
//---------------------------------------------------------
Fraction GuitarPro::len2fraction(int len)
{
Fraction l;
switch(len) {
case -2: l.set(1, 1); break;
case -1: l.set(1, 2); break;
case 0: l.set(1, 4); break;
case 1: l.set(1, 8); break;
case 2: l.set(1, 16); break;
case 3: l.set(1, 32); break;
case 4: l.set(1, 64); break;
case 5: l.set(1, 128); break;
// set to len - in some cases we get whacky numbers for this (40, 28...)
default:
l.set(1,len);
}
return l;
}
//---------------------------------------------------------
// readMixChange
//---------------------------------------------------------
bool GuitarPro::readMixChange(Measure* measure)
{
/*char patch =*/ readChar();
signed char volume = readChar();
signed char pan = readChar();
signed char chorus = readChar();
signed char reverb = readChar();
signed char phase = readChar();
signed char tremolo = readChar();
int temp = readInt();
if (volume >= 0)
readChar();
if (pan >= 0)
readChar();
if (chorus >= 0)
readChar();
if (reverb >= 0)
readChar();
if (phase >= 0)
readChar();
if (tremolo >= 0)
readChar();
if (temp >= 0) {
if (temp != previousTempo) {
previousTempo = temp;
setTempo(temp, measure);
}
readChar();
}
return true;
}
//---------------------------------------------------------
// createMeasures
//---------------------------------------------------------
void GuitarPro::createMeasures()
{
Fraction tick = Fraction(0,1);
Fraction ts;
qDebug("measures %d bars.size %d", measures, bars.size());
// for (int i = 0; i < measures; ++i) {
for (int i = 0; i < bars.size(); ++i) { // ?? (ws)
Fraction nts = bars[i].timesig;
Measure* m = new Measure(score);
m->setTick(tick);
m->setTimesig(nts);
m->setTicks(nts);
if (i == 0 || ts != nts) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
const Staff* staff = score->staff(staffIdx);
const StaffType* staffType = staff->staffType(Fraction(0,1)); // at tick 0
if (staffType->genTimesig()) {
TimeSig* t = new TimeSig(score);
t->setTrack(staffIdx * VOICES);
t->setSig(nts);
Segment* s = m->getSegment(SegmentType::TimeSig, tick);
s->add(t);
}
}
}
if (i == 0 || (bars[i].keysig != GP_INVALID_KEYSIG)) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
int keysig = bars[i].keysig != GP_INVALID_KEYSIG ? bars[i].keysig : key;
if (tick.isZero() || (int)score->staff(staffIdx)->key(tick) != (int)Key(keysig)) {
KeySig* t = new KeySig(score);
t->setKey(Key(keysig));
t->setTrack(staffIdx * VOICES);
Segment* s = m->getSegment(SegmentType::KeySig, tick);
s->add(t);
}
}
}
readVolta(&bars[i].volta, m);
m->setRepeatEnd(bars[i].repeatFlags == Repeat::END);
m->setRepeatStart(bars[i].repeatFlags == Repeat::START);
m->setRepeatJump(bars[i].repeatFlags == Repeat::JUMP);
// m->setRepeatFlags(bars[i].repeatFlags);
m->setRepeatCount(bars[i].repeats); // supported in gp5
// reset the volta sequence if we have an opening repeat
if (bars[i].repeatFlags == Repeat::START)
voltaSequence = 1;
// otherwise, if we see an end repeat symbol, only reset if the bar after it does not contain a volta
else if (bars[i].repeatFlags == Repeat::END && i < bars.length() - 1) {
if (bars[i+1].volta.voltaInfo.length() == 0) {
voltaSequence = 1; // reset the volta count
}
}
score->measures()->add(m);
tick += nts;
ts = nts;
}
}
//---------------------------------------------------------
// applyBeatEffects
//---------------------------------------------------------
void GuitarPro::applyBeatEffects(Chord* chord, int beatEffect)
{
/* tap/slap/pop implemented as text until SMuFL has
* specifications and we can add them to fonts. Note that
* tap/slap/pop are just added to the top note in the chord,
* technically these can be applied to individual notes on the
* UI, but Guitar Pro has no way to express that on the
* score. To get the same result, we should just add the marking
* to above the top note.
*/
if (beatEffect == 1) {
if (version > 300)
addTap(chord->upNote());
else
addVibrato(chord->upNote());
}
else if (beatEffect == 2)
addSlap(chord->upNote());
else if (beatEffect == 3)
addPop(chord->upNote());
else if (beatEffect == 4) {
if (version >= 400) {
Articulation* a = new Articulation(chord->score());
a->setSymId(SymId::guitarFadeIn);
a->setAnchor(ArticulationAnchor::TOP_STAFF);
a->setPropertyFlags(Pid::ARTICULATION_ANCHOR, PropertyFlags::UNSTYLED);
chord->add(a);
}
//TODO-ws else for (auto n : chord->notes())
// n->setHarmonic(true);
}
else if (beatEffect == 5) {
Articulation* a = new Articulation(chord->score());
a->setSymId(SymId::stringsUpBow);
chord->add(a);
}
else if (beatEffect == 6) {
Articulation* art = new Articulation(chord->score());
art->setSymId(SymId::stringsDownBow);
chord->add(art);
}
else if (beatEffect == 7) {
addVibrato(chord->upNote(), Vibrato::Type::VIBRATO_SAWTOOTH);
}
}
#ifdef _MSC_VER
#pragma optimize("", on)
#endif
//---------------------------------------------------------
// read
//---------------------------------------------------------
bool GuitarPro1::read(QFile* fp)
{
f = fp;
curPos = 30;
title = readDelphiString();
artist = readDelphiString();
readDelphiString();
int temp = readInt();
/*uchar num =*/ readUChar(); // Shuffle rhythm feel
// int octave = 0;
key = 0;
if (version > 102)
key = readInt(); // key
staves = version > 102 ? 8 : 1;
slurs = new Slur*[staves];
for (int i = 0; i < staves; ++i)
slurs[i] = nullptr;
//int tnumerator = 4;
//int tdenominator = 4;
//
// create a part for every staff
//
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Part* part = new Part(score);
Staff* s = new Staff(score);
s->setPart(part);
part->insertStaff(s, 0);
score->staves().push_back(s);
score->appendPart(part);
}
for (int i = 0; i < staves; ++i) {
int tuning[GP_MAX_STRING_NUMBER];
int strings = version > 101 ? readInt() : 6;
for (int j = 0; j < strings; ++j)
tuning[j] = readInt();
std::vector<int> tuning2(strings);
//int tuning2[strings];
for (int k = 0; k < strings; ++k)
tuning2[strings-k-1] = tuning[k];
int frets = 32; // TODO
StringData stringData(frets, strings, &tuning2[0]);
createTuningString(strings, &tuning2[0]);
Part* part = score->staff(i)->part();
Instrument* instr = part->instrument();
instr->setStringData(stringData);
instr->setSingleNoteDynamics(false);
}
measures = readInt();
Fraction ts;
Fraction tick = {0,1};
for (int i = 0; i < measures; ++i) {
Fraction nts = bars[i].timesig;
Measure* m = new Measure(score);
m->setTick(tick);
m->setTimesig(nts);
m->setTicks(nts);
if (i == 0 || ts != nts) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
TimeSig* t = new TimeSig(score);
t->setTrack(staffIdx * VOICES);
t->setSig(nts);
Segment* s = m->getSegment(SegmentType::TimeSig, tick);
s->add(t);
}
}
score->measures()->add(m);
tick += nts;
ts = nts;
}
previousTempo = temp;
Measure* measure = score->firstMeasure();
bool mixChange = false;
for (int bar = 0; bar < measures; ++bar, measure = measure->nextMeasure()) {
const GpBar& gpbar = bars[bar];
if (!gpbar.marker.isEmpty()) {
RehearsalMark* s = new RehearsalMark(score);
s->setPlainText(gpbar.marker.trimmed());
s->setTrack(0);
Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
segment->add(s);
}
std::vector<Tuplet*> tuplets(staves);
//Tuplet* tuplets[staves];
for (int staffIdx = 0; staffIdx < staves; ++staffIdx)
tuplets[staffIdx] = 0;
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Fraction measureLen = {0,1};
int track = staffIdx * VOICES;
Fraction fraction = measure->tick();
int beats = readInt();
for (int beat = 0; beat < beats; ++beat) {
// int pause = 0;
uchar beatBits = readUChar();
bool dotted = beatBits & BEAT_DOTTED;
if (beatBits & BEAT_PAUSE)
/*pause =*/ readUChar();
int len = readChar();
int tuple = 0;
if (beatBits & BEAT_TUPLET)
tuple = readInt();
Segment* segment = measure->getSegment(SegmentType::ChordRest, fraction);
if (beatBits & BEAT_CHORD) {
int numStrings = score->staff(staffIdx)->part()->instrument()->stringData()->strings();
int header = readUChar();
QString name;
if ((header & 1) == 0) {
name = readDelphiString();
readChord(segment, track, numStrings, name, false);
}
else {
skip(25);
name = readPascalString(34);
readChord(segment, track, numStrings, name, true);
skip(36);
}
}
Lyrics* lyrics = 0;
if (beatBits & BEAT_LYRICS) {
lyrics = new Lyrics(score);
lyrics->setPlainText(readDelphiString());
}
if (beatBits & BEAT_EFFECTS)
readBeatEffects(track, segment);
if (beatBits & BEAT_MIX_CHANGE) {
readMixChange(measure);
mixChange = true;
}
int strings = readUChar(); // used strings mask
Fraction l = len2fraction(len);
ChordRest* cr;
if (strings)
cr = new Chord(score);
else
cr = new Rest(score);
cr->setTrack(track);
if (lyrics)
cr->add(lyrics);
TDuration d(l);
d.setDots(dotted ? 1 : 0);
if (dotted)
l = l + (l * Fraction(1,2));
if (tuple) {
Tuplet* tuplet = tuplets[staffIdx];
if ((tuplet == 0) || (tuplet->elementsDuration() == tuplet->baseLen().fraction() * tuplet->ratio().numerator())) {
tuplet = new Tuplet(score);
tuplet->setTick(fraction);
tuplet->setTrack(cr->track());
tuplets[staffIdx] = tuplet;
setTuplet(tuplet, tuple);
tuplet->setParent(measure);
}
tuplet->setTrack(track);
tuplet->setBaseLen(l);
tuplet->setTicks(l * tuplet->ratio().denominator());
cr->setTuplet(tuplet);
tuplet->add(cr); //TODOxxx
}
cr->setTicks(l);
cr->setDurationType(d);
segment->add(cr);
Staff* staff = cr->staff();
int numStrings = staff->part()->instrument()->stringData()->strings();
for (int i = 6; i >= 0; --i) {
if (strings & (1 << i) && ((6-i) < numStrings)) {
Note* note = new Note(score);
static_cast<Chord*>(cr)->add(note);
readNote(6-i, note);
note->setTpcFromPitch();
}
}
restsForEmptyBeats(segment, measure, cr, l, track, fraction);
fraction += cr->actualTicks();
measureLen += cr->actualTicks();
}
if (measureLen < measure->ticks()) {
score->setRest(fraction, track, measure->ticks() - measureLen, false, nullptr, false);
}
}
if (bar == 1 && !mixChange)
setTempo(temp, score->firstMeasure());
}
return true;
}
//---------------------------------------------------------
// setTempo
//---------------------------------------------------------
void GuitarPro::setTempo(int temp, Measure* measure)
{
if (!last_measure) {
last_measure = measure;
last_tempo = temp;
}
else if (last_measure == measure) {
last_tempo = temp;
}
else {
std::swap(last_tempo, temp);
std::swap(last_measure, measure);
Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
for (Element* e : segment->annotations()) {
if (e->isTempoText()) {
qDebug("already there");
return;
}
}
TempoText* tt = new TempoText(score);
tt->setTempo(double(temp) / 60.0);
tt->setXmlText(QString("<sym>metNoteQuarterUp</sym> = %1").arg(temp));
tt->setTrack(0);
segment->add(tt);
score->setTempo(measure->tick(), tt->tempo());
previousTempo = temp;
}
}
//---------------------------------------------------------
// readChord
//---------------------------------------------------------
void GuitarPro::readChord(Segment* seg, int track, int numStrings, QString name, bool gpHeader)
{
int firstFret = readInt();
if (firstFret || gpHeader) {
FretDiagram* fret = new FretDiagram(score);
fret->setTrack(track);
fret->setStrings(numStrings);
fret->setFretOffset(firstFret-1);
for (int i = 0; i < (gpHeader ? 7 : 6); ++i) {
int currentFret = readInt();
// read the frets and add them to the fretboard
// substract 1 extra from numStrings as we count from 0
if (i > numStrings - 1) {
}
else if (currentFret > 0) {
fret->setDot(numStrings - 1 - i, currentFret - firstFret + 1, true);
}
else if (currentFret == 0) {
fret->setDot(numStrings - 1 - i, 0, true);
fret->setMarker(numStrings - 1 - i, FretMarkerType::CIRCLE);
}
else if (currentFret == -1) {
fret->setDot(numStrings - 1 - i, 0, true);
fret->setMarker(numStrings - 1 - i, FretMarkerType::CROSS);
}
}
seg->add(fret);
if (!name.isEmpty()) {
Harmony* harmony = new Harmony(seg->score());
harmony->setHarmony(name);
harmony->setTrack(track);
fret->add(harmony);
}
}
else if (!name.isEmpty()) {
Harmony* harmony = new Harmony(seg->score());
harmony->setHarmony(name);
harmony->setTrack(track);
seg->add(harmony);
}
}
//---------------------------------------------------------
// restsForEmptyBeats
//---------------------------------------------------------
void GuitarPro::restsForEmptyBeats(Segment* seg, Measure* measure, ChordRest* cr, Fraction& l, int track, const Fraction& tick)
{
/* this can happen as Guitar Pro versions 5 and below allows
* users to create empty segments. Here, we create rests and
* make them invisible so users get the same visual if they are
* at a valid tick of the score. */
if (seg->empty()) {
if (tick < measure->first()->tick() + measure->ticks()) {
cr = new Rest(score);
cr->setTrack(track);
TDuration d(l);
cr->setDurationType(d);
cr->setVisible(false);
seg->add(cr);
}
else
measure->remove(seg);
}
}
//---------------------------------------------------------
// createSlur
//---------------------------------------------------------
void GuitarPro::createSlur(bool hasSlur, int staffIdx, ChordRest* cr)
{
if (hasSlur && (slurs[staffIdx] == 0)) {
Slur* slur = new Slur(score);
slur->setParent(0);
slur->setTrack(cr->track());
slur->setTrack2(cr->track());
slur->setTick(cr->tick());
slur->setTick2(cr->tick());
slurs[staffIdx] = slur;
score->addElement(slur);
}
else if (slurs[staffIdx] && !hasSlur) {
Slur* s = slurs[staffIdx];
slurs[staffIdx] = 0;
s->setTick2(cr->tick());
s->setTrack2(cr->track());
}
}
//---------------------------------------------------------
// createOttava
//---------------------------------------------------------
void GuitarPro::createOttava(bool hasOttava, int track, ChordRest* cr, QString value)
{
if (hasOttava && (ottava.at(track) == 0)) {
Ottava* newOttava = new Ottava(score);
newOttava->setTrack(track);
if (!value.compare("8va"))
newOttava->setOttavaType(OttavaType::OTTAVA_8VA);
else if (!value.compare("8vb"))
newOttava->setOttavaType(OttavaType::OTTAVA_8VB);
else if (!value.compare("15ma"))
newOttava->setOttavaType(OttavaType::OTTAVA_15MA);
else if (!value.compare("15mb"))
newOttava->setOttavaType(OttavaType::OTTAVA_15MB);
newOttava->setTick(cr->tick());
/* we set the second tick when we encounter the next note
without an ottava. We also allow the ottava to continue
over rests, as that's what Guitar Pro does. */
newOttava->setTick2(cr->tick());
ottava.at(track) = newOttava;
score->addElement(newOttava);
}
else if (ottava.at(track) && !hasOttava) {
Ottava* currentOttava = ottava.at(track);
ottava.at(track) = 0;
currentOttava->setTick2(cr->tick());
//ottava.at(track)->staff()->updateOttava(ottava.at(track));
}
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
bool GuitarPro2::read(QFile* fp)
{
f = fp;
curPos = 30;
title = readDelphiString();
subtitle = readDelphiString();
artist = readDelphiString();
album = readDelphiString();
composer = readDelphiString();
QString copyright = readDelphiString();
if (!copyright.isEmpty())
score->setMetaTag("copyright", QString("%1").arg(copyright));
transcriber = readDelphiString();
instructions = readDelphiString();
int n = readInt();
for (int i = 0; i < n; ++i)
comments.append(readDelphiString());
/*uchar num =*/ readUChar(); // Shuffle rhythm feel
int temp = readInt();
// int octave = 0;
/*int key =*/ readInt(); // key
for (int i = 0; i < GP_MAX_TRACK_NUMBER * 2; ++i) {
channelDefaults[i].patch = readInt();
channelDefaults[i].volume = readUChar() * 8 - 1;
channelDefaults[i].pan = readUChar() * 8 - 1;
channelDefaults[i].chorus = readUChar() * 8 - 1;
channelDefaults[i].reverb = readUChar() * 8 - 1;
channelDefaults[i].phase = readUChar() * 8 - 1;
channelDefaults[i].tremolo = readUChar() * 8 - 1;
readUChar(); // padding
readUChar();
}
measures = readInt();
staves = readInt();
int tnumerator = 4;
int tdenominator = 4;
for (int i = 0; i < measures; ++i) {
GpBar bar;
uchar barBits = readUChar();
if (barBits & SCORE_TIMESIG_NUMERATOR)
tnumerator = readUChar();
if (barBits & SCORE_TIMESIG_DENOMINATOR)
tdenominator = readUChar();
if (barBits & SCORE_REPEAT_START)
bar.repeatFlags = bar.repeatFlags | Repeat::START;
if (barBits & SCORE_REPEAT_END) {
bar.repeatFlags = bar.repeatFlags | Repeat::END;
bar.repeats = readUChar() + 1;
}
if (barBits & SCORE_VOLTA) {
uchar voltaNumber = readUChar();
while (voltaNumber > 0) {
// volta information is represented as a binary number
bar.volta.voltaType = GP_VOLTA_BINARY;
bar.volta.voltaInfo.append(voltaNumber & 1);
voltaNumber >>= 1;
}
}
if (barBits & SCORE_MARKER) {
bar.marker = readDelphiString(); // new section?
/*int color =*/ readInt(); // color?
}
if (barBits & SCORE_KEYSIG) {
bar.keysig = readUChar();
/*uchar c =*/ readUChar(); // minor
}
if (barBits & SCORE_DOUBLE_BAR)
bar.barLine = BarLineType::DOUBLE;
bar.timesig = Fraction(tnumerator, tdenominator);
bars.append(bar);
}
//
// create a part for every staff
//
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Part* part = new Part(score);
Staff* s = new Staff(score);
s->setPart(part);
part->insertStaff(s, 0);
score->staves().push_back(s);
score->appendPart(part);
}
Fraction ts;
Fraction tick = {0, 1};
for (int i = 0; i < measures; ++i) {
Fraction nts = bars[i].timesig;
Measure* m = new Measure(score);
m->setTick(tick);
m->setTimesig(nts);
m->setTicks(nts);
if (i == 0 || ts != nts) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
TimeSig* t = new TimeSig(score);
t->setTrack(staffIdx * VOICES);
t->setSig(nts);
Segment* s = m->getSegment(SegmentType::TimeSig, tick);
s->add(t);
}
}
score->measures()->add(m);
tick += nts;
ts = nts;
}
for (int i = 0; i < staves; ++i) {
int tuning[GP_MAX_STRING_NUMBER];
uchar c = readUChar(); // simulations bitmask
if (c & 0x2) { // 12 stringed guitar
}
if (c & 0x4) { // banjo track
}
QString name = readPascalString(40);
int strings = readInt();
if (strings <= 0 || strings > GP_MAX_STRING_NUMBER)
return false;
for (int j = 0; j < strings; ++j)
tuning[j] = readInt();
for (int j = strings; j < GP_MAX_STRING_NUMBER; ++j)
readInt();
/*int midiPort =*/ readInt(); // - 1;
int midiChannel = readInt() - 1;
/*int midiChannel2 =*/ readInt(); // - 1;
int frets = readInt();
int capo = readInt();
/*int color =*/ readInt();
std::vector<int> tuning2(strings);
//int tuning2[strings];
for (int k = 0; k < strings; ++k)
tuning2[strings-k-1] = tuning[k];
StringData stringData(frets, strings, &tuning2[0]);
Part* part = score->staff(i)->part();
Instrument* instr = part->instrument();
instr->setStringData(stringData);
instr->setSingleNoteDynamics(false);
part->setPartName(name);
part->setPlainLongName(name);
createTuningString(strings, &tuning2[0]);
//
// determine clef
//
Staff* staff = score->staff(i);
int patch = channelDefaults[midiChannel].patch;
ClefType clefId = ClefType::G;
if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
clefId = ClefType::PERC;
// instr->setUseDrumset(DrumsetKind::GUITAR_PRO);
instr->setDrumset(gpDrumset);
staff->setStaffType(Fraction(0,1), *StaffType::preset(StaffTypes::PERC_DEFAULT));
}
else
clefId = defaultClef(patch);
Measure* measure = score->firstMeasure();
Clef* clef = new Clef(score);
clef->setClefType(clefId);
clef->setTrack(i * VOICES);
Segment* segment = measure->getSegment(SegmentType::HeaderClef, Fraction(0,1));
segment->add(clef);
if (capo > 0) {
Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
StaffText* st = new StaffText(score);
// st->setTextStyleType(TextStyleType::STAFF);
st->setPlainText(QString("Capo. fret ") + QString::number(capo));
st->setTrack(i * VOICES);
s->add(st);
}
Channel* ch = instr->channel(0);
if (midiChannel == int(StaffTypes::PERC_DEFAULT)) {
ch->setProgram(0);
ch->setBank(128);
}
else {
ch->setProgram(patch);
ch->setBank(0);
}
ch->setVolume(channelDefaults[midiChannel].volume);
ch->setPan(channelDefaults[midiChannel].pan);
ch->setChorus(channelDefaults[midiChannel].chorus);
ch->setReverb(channelDefaults[midiChannel].reverb);
// missing: phase, tremolo
}
previousTempo = temp;
Measure* measure = score->firstMeasure();
bool mixChange = false;
for (int bar = 0; bar < measures; ++bar, measure = measure->nextMeasure()) {
const GpBar& gpbar = bars[bar];
if (!gpbar.marker.isEmpty()) {
RehearsalMark* s = new RehearsalMark(score);
s->setPlainText(gpbar.marker.trimmed());
s->setTrack(0);
Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
segment->add(s);
}
std::vector<Tuplet*> tuplets(staves);
// Tuplet* tuplets[staves];
for (int staffIdx = 0; staffIdx < staves; ++staffIdx)
tuplets[staffIdx] = 0;
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Fraction measureLen = {0,1};
int track = staffIdx * VOICES;
Fraction fraction = measure->tick();
int beats = readInt();
for (int beat = 0; beat < beats; ++beat) {
// int pause = 0;
uchar beatBits = readUChar();
bool dotted = beatBits & BEAT_DOTTED;
if (beatBits & BEAT_PAUSE)
/*pause =*/ readUChar();
int len = readChar();
int tuple = 0;
if (beatBits & BEAT_TUPLET)
tuple = readInt();
Segment* segment = measure->getSegment(SegmentType::ChordRest, fraction);
if (beatBits & BEAT_CHORD) {
int numStrings = score->staff(staffIdx)->part()->instrument()->stringData()->strings();
int header = readUChar();
QString name;
if ((header & 1) == 0) {
name = readDelphiString();
readChord(segment, track, numStrings, name, false);
}
else {
skip(25);
name = readPascalString(34);
readChord(segment, track, numStrings, name, true);
skip(36);
}
}
Lyrics* lyrics = 0;
if (beatBits & BEAT_LYRICS) {
QString txt = readDelphiString();
lyrics = new Lyrics(score);
lyrics->setPlainText(txt);
}
if (beatBits & BEAT_EFFECTS)
readBeatEffects(track, segment);
if (beatBits & BEAT_MIX_CHANGE) {
readMixChange(measure);
mixChange = true;
}
int strings = readUChar(); // used strings mask
Fraction l = len2fraction(len);
ChordRest* cr;
if (strings)
cr = new Chord(score);
else
cr = new Rest(score);
cr->setTrack(track);
if (lyrics)
cr->add(lyrics);
TDuration d(l);
d.setDots(dotted ? 1 : 0);
if (dotted)
l = l + (l * Fraction(1,2));
if (tuple) {
Tuplet* tuplet = tuplets[staffIdx];
if ((tuplet == 0) || (tuplet->elementsDuration() == tuplet->baseLen().fraction() * tuplet->ratio().numerator())) {
tuplet = new Tuplet(score);
tuplet->setTick(fraction);
tuplet->setTrack(cr->track());
tuplets[staffIdx] = tuplet;
setTuplet(tuplet, tuple);
tuplet->setParent(measure);
}
tuplet->setTrack(track);
tuplet->setBaseLen(l);
tuplet->setTicks(l * tuplet->ratio().denominator());
cr->setTuplet(tuplet);
tuplet->add(cr);
}
cr->setTicks(l);
cr->setDurationType(d);
segment->add(cr);
Staff* staff = cr->staff();
int numStrings = staff->part()->instrument()->stringData()->strings();
for (int i = 6; i >= 0; --i) {
if (strings & (1 << i) && ((6-i) < numStrings)) {
Note* note = new Note(score);
static_cast<Chord*>(cr)->add(note);
readNote(6-i, note);
note->setTpcFromPitch();
}
}
restsForEmptyBeats(segment, measure, cr, l, track, fraction);
fraction += cr->actualTicks();
measureLen += cr->actualTicks();
}
if (measureLen < measure->ticks()) {
score->setRest(fraction, track, measure->ticks() - measureLen, false, nullptr, false);
}
}
if (bar == 1 && !mixChange)
setTempo(temp, score->firstMeasure());
}
return true;
}
//---------------------------------------------------------
// readNote
//---------------------------------------------------------
bool GuitarPro1::readNote(int string, Note* note)
{
bool slur = false;
uchar noteBits = readUChar();
if (noteBits & NOTE_GHOST) {
if (version == 300)
note->setGhost(true);
else {
note->setHeadGroup(NoteHead::Group::HEAD_CROSS);
note->setGhost(true);
}
}
bool tieNote = false;
uchar variant = 1;
if (noteBits & NOTE_DEAD) {
variant = readUChar();
if (variant == 1) { // normal note
}
else if (variant == 2) {
tieNote = true;
}
else if (variant == 3) { // dead notes
note->setHeadGroup(NoteHead::Group::HEAD_CROSS);
note->setGhost(true);
}
else
qDebug("unknown note variant: %d", variant);
}
//
// noteBits:
// 7 - Right hand or left hand fingering;
// 6 - Accentuated note
// 5 - Note type (rest, empty note, normal note);
// 4 - note dynamic;
// 3 - Presence of effects linked to the note;
// 2 - Ghost note;
// 1 - Dotted note; ?
// 0 - Time-independent duration
if (noteBits & 0x1) { // note != beat
int a = readUChar(); // length
int b = readUChar(); // t
qDebug("Time independent note len, len %d t %d", a, b);
}
if (noteBits & 0x2) { // note is dotted
//readUChar();
}
// set dynamic information on note if different from previous note
if (noteBits & NOTE_DYNAMIC) {
int d = readChar();
if (previousDynamic != d) {
previousDynamic = d;
addDynamic(note, d);
}
}
int fretNumber = -1;
if (noteBits & NOTE_FRET)
fretNumber = readUChar();
if (noteBits & NOTE_FINGERING) { // fingering
int a = readUChar();
int b = readUChar();
qDebug("Fingering=========%d %d", a, b);
}
if (noteBits & BEAT_EFFECTS) {
uchar modMask1 = readUChar();
uchar modMask2 = 0;
if (version >= 400)
modMask2 = readUChar();
if (modMask1 & EFFECT_BEND)
readBend(note);
if (modMask1 & EFFECT_GRACE) {
// GP3 grace note
int fret = readUChar(); // grace fret
int dynamic = readUChar(); // grace dynamic
int transition = readUChar(); // grace transition
int duration = readUChar(); // grace duration
int grace_len = MScore::division/8;
if (duration == 1)
grace_len = MScore::division/8; //32th
else if (duration == 2)
grace_len = MScore::division/6; //24th
else if (duration == 3)
grace_len = MScore::division/4; //16th
Note* gn = new Note(score);
if (fret == 255) {
gn->setHeadGroup(NoteHead::Group::HEAD_CROSS);
gn->setGhost(true);
}
if (fret == 255)
fret = 0;
gn->setFret(fret);
gn->setString(string);
int grace_pitch = note->staff()->part()->instrument()->stringData()->getPitch(string, fret, nullptr, Fraction(0,1));
gn->setPitch(grace_pitch);
gn->setTpcFromPitch();
Chord* gc = nullptr;
if (note->chord()->graceNotes().size())
{
gc = note->chord()->graceNotes().first();
}
if (!gc)
{
gc = new Chord(score);
TDuration d;
d.setVal(grace_len);
if (grace_len == MScore::division / 6)
d.setDots(1);
gc->setDurationType(d);
gc->setTicks(d.fraction());
gc->setNoteType(NoteType::ACCIACCATURA);
gc->setMag(note->chord()->staff()->mag(Fraction(0,1)) * score->styleD(Sid::graceNoteMag));
note->chord()->add(gc); // sets parent + track
addDynamic(gn, dynamic);
}
gc->add(gn);
if (transition == 0) {
// no transition
}
else if(transition == 1){
//note->setSlideNote(gn);
Glissando* glis = new Glissando(score);
glis->setGlissandoType(GlissandoType::STRAIGHT);
gn->chord()->add(glis);
glis->setAnchor(Spanner::Anchor::NOTE);
glis->setStartElement(gn);
glis->setTick(gn->chord()->tick());
glis->setTrack(gn->track());
glis->setParent(gn);
glis->setEndElement(note);
glis->setTick2(note->chord()->tick());
glis->setTrack2(note->track());
score->addElement(glis);
//HammerOn here??? Maybe version...
Slur* slur1 = new Slur(score);
slur1->setStartElement(gc);
slur1->setEndElement(note->chord());
slur1->setTick(gc->tick());
slur1->setTick2(note->chord()->tick());
slur1->setTrack(gc->track());
slur1->setTrack2(note->track());
score->addElement(slur1);
//TODO: Add a 'slide' guitar effect when implemented
}
else if (transition == 2 && fretNumber>=0 && fretNumber<=255 && fretNumber!=gn->fret()) {
/*QList<PitchValue> points;
points.append(PitchValue(0,0, false));
points.append(PitchValue(60,(fretNumber-gn->fret())*100, false));
Bend* b = new Bend(note->score());
b->setPoints(points);
b->setTrack(gn->track());
gn->add(b);*/
}
else if (transition == 3) {
// TODO:
// major: replace with a 'hammer-on' guitar effect when implemented
// minor: make slurs for parts
ChordRest* cr1 = static_cast<Chord*>(gc);
ChordRest* cr2 = static_cast<Chord*>(note->chord());
Slur* slur1 = new Slur(score);
slur1->setStartElement(cr1);
slur1->setEndElement(cr2);
slur1->setTick(cr1->tick());
slur1->setTick2(cr2->tick());
slur1->setTrack(cr1->track());
slur1->setTrack2(cr2->track());
score->addElement(slur1);
}
}
if (modMask1 & EFFECT_HAMMER) // hammer on / pull off
slur = true;
if (modMask1 & EFFECT_LET_RING) // let ring
addLetRing(note);
if (modMask1 & EFFECT_SLIDE_OLD)
slideList.push_back(note);
if (version >= 400) {
if (modMask2 & EFFECT_STACATTO) {
}
if (modMask2 & EFFECT_PALM_MUTE) {
//note->setPalmMute(true);
addPalmMute(note);
}
if (modMask2 & EFFECT_TREMOLO) {
readUChar();
}
if (modMask2 & EFFECT_ARTIFICIAL_HARMONIC) {
/*int type =*/ readUChar();
//TODO-ws if (type == 1 || type == 4 || type == 5)
// note->setHarmonic(true);
}
if (modMask2 & EFFECT_TRILL) {
//TODO-ws note->setTrillFret(readUChar()); // trill fret
readUChar(); // trill length
}
}
}
if (fretNumber == -1) {
qDebug("Note: no fret number, tie %d", tieNote);
}
Staff* staff = note->staff();
if (fretNumber == 255) {
fretNumber = 0;
note->setHeadGroup(NoteHead::Group::HEAD_CROSS);
note->setGhost(true);
}
// dead note represented as high numbers - fix to zero
if (fretNumber > 99 || fretNumber == -1)
fretNumber = 0;
int pitch = staff->part()->instrument()->stringData()->getPitch(string, fretNumber, nullptr, Fraction(0,1));
/* it's possible to specify extraordinarily high pitches by
specifying fret numbers that don't exist. This is an issue that
comes from tuxguitar. Just set to maximum pitch. GP6 actually
sets the fret number to 0 also, so that's what I've opted to do
here. */
if (pitch > MAX_PITCH) {
fretNumber = 0;
pitch = MAX_PITCH;
}
note->setFret(fretNumber);
note->setString(string);
note->setPitch(pitch);
if (tieNote) {
bool found = false;
Chord* chord = note->chord();
Segment* segment = chord->segment()->prev1(SegmentType::ChordRest);
int track = note->track();
std::vector<Chord*> chords;
Note* true_note = nullptr;
while (segment) {
Element* e = segment->element(track);
if (e) {
if (e->isChord()) {
Chord* chord2 = toChord(e);
foreach (Note* note2, chord2->notes()) {
if (note2->string() == string) {
if (chords.empty()) {
Tie* tie = new Tie(score);
tie->setEndNote(note);
note2->add(tie);
}
note->setFret(note2->fret());
note->setPitch(note2->pitch());
found = true;
true_note = note2;
break;
}
}
if (!found)
chords.push_back(chord2);
}
if (found)
break;
}
segment = segment->prev1(SegmentType::ChordRest);
}
if (chords.size() && true_note) {
Note* end_note = note;
for (unsigned int i = 0; i < chords.size(); ++i) {
Note* note2 = new Note(score);
note2->setString(true_note->string());
note2->setFret(true_note->fret());
note2->setPitch(true_note->pitch());
note2->setTpcFromPitch();
chords[i]->add(note2);
Tie* tie = new Tie(score);
tie->setEndNote(end_note);
end_note = note2;
note2->add(tie);
}
Tie* tie = new Tie(score);
tie->setEndNote(end_note);
true_note->add(tie);
}
}
return slur;
}
//---------------------------------------------------------
// readBeatEffects
//---------------------------------------------------------
int GuitarPro1::readBeatEffects(int, Segment*)
{
uchar fxBits1 = readUChar();
if (fxBits1 & BEAT_EFFECT) {
uchar num = readUChar();
switch(num) {
case 0: // tremolo bar
readInt();
break;
default:
readInt();
break;
}
}
if (fxBits1 & BEAT_ARPEGGIO) {
readUChar(); // down stroke length
readUChar(); // up stroke length
}
return 0;
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
bool GuitarPro3::read(QFile* fp)
{
f = fp;
curPos = 30;
title = readDelphiString();
subtitle = readDelphiString();
artist = readDelphiString();
album = readDelphiString();
composer = readDelphiString();
QString copyright = readDelphiString();
if (!copyright.isEmpty())
score->setMetaTag("copyright", QString("%1").arg(copyright));
transcriber = readDelphiString();
instructions = readDelphiString();
for (int i = 0, n = readInt(); i < n; ++i)
comments.append(readDelphiString());
/*uchar num =*/ readUChar(); // Shuffle rhythm feel
int temp = readInt();
// int octave = 0;
key = readInt(); // key
for (int i = 0; i < GP_MAX_TRACK_NUMBER * 2; ++i) {
channelDefaults[i].patch = readInt();
channelDefaults[i].volume = readUChar() * 8 - 1;
channelDefaults[i].pan = readUChar() * 8 - 1;
channelDefaults[i].chorus = readUChar() * 8 - 1;
channelDefaults[i].reverb = readUChar() * 8 - 1;
channelDefaults[i].phase = readUChar() * 8 - 1;
channelDefaults[i].tremolo = readUChar() * 8 - 1;
readUChar(); // padding
readUChar();
}
measures = readInt();
staves = readInt();
slurs = new Slur*[staves];
for (int i = 0; i < staves; ++i)
slurs[i] = nullptr;
//previousDynamic = new int [staves * VOICES];
// initialise the dynamics to 0
//for (int i = 0; i < staves * VOICES; i++)
// previousDynamic[i] = 0;
previousDynamic = -1;
int tnumerator = 4;
int tdenominator = 4;
for (int i = 0; i < measures; ++i) {
GpBar bar;
uchar barBits = readUChar();
if (barBits & SCORE_TIMESIG_NUMERATOR)
tnumerator = readUChar();
if (barBits & SCORE_TIMESIG_DENOMINATOR)
tdenominator = readUChar();
if (barBits & SCORE_REPEAT_START)
bar.repeatFlags = bar.repeatFlags | Repeat::START;
if (barBits & SCORE_REPEAT_END) { // number of repeats
bar.repeatFlags = bar.repeatFlags | Repeat::END;
bar.repeats = readUChar() + 1;
}
if (barBits & SCORE_VOLTA) { // a volta
uchar voltaNumber = readUChar();
// voltas are represented as a binary number
bar.volta.voltaType = GP_VOLTA_BINARY;
while (voltaNumber > 0) {
bar.volta.voltaInfo.append(voltaNumber & 1);
voltaNumber >>= 1;
}
}
if (barBits & SCORE_MARKER) {
bar.marker = readDelphiString(); // new section?
/*int color =*/ readInt(); // color?
}
if (barBits & SCORE_KEYSIG) {
int currentKey = readUChar();
/* key signatures are specified as
* 1# = 1, 2# = 2, ..., 7# = 7
* 1b = 255, 2b = 254, ... 7b = 249 */
bar.keysig = currentKey <= 7 ? currentKey : -256+currentKey;
readUChar(); // specifies major/minor mode
}
if (barBits & SCORE_DOUBLE_BAR)
bar.barLine = BarLineType::DOUBLE;
bar.timesig = Fraction(tnumerator, tdenominator);
bars.append(bar);
}
//
// create a part for every staff
//
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Part* part = new Part(score);
Staff* s = new Staff(score);
s->setPart(part);
part->insertStaff(s, 0);
score->staves().push_back(s);
score->appendPart(part);
}
Fraction ts;
Fraction tick = {0,1};
for (int i = 0; i < measures; ++i) {
Fraction nts = bars[i].timesig;
Measure* m = new Measure(score);
m->setTick(tick);
m->setTimesig(nts);
m->setTicks(nts);
if (i == 0 || ts != nts) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
TimeSig* t = new TimeSig(score);
t->setTrack(staffIdx * VOICES);
t->setSig(nts);
Segment* s = m->getSegment(SegmentType::TimeSig, tick);
s->add(t);
}
}
if (i == 0 && key) {
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
KeySig* t = new KeySig(score);
t->setKey(Key(key));
t->setTrack(staffIdx * VOICES);
Segment* s = m->getSegment(SegmentType::KeySig, tick);
s->add(t);
}
}
readVolta(&bars[i].volta, m);
m->setRepeatEnd(bars[i].repeatFlags == Repeat::END);
m->setRepeatStart(bars[i].repeatFlags == Repeat::START);
m->setRepeatJump(bars[i].repeatFlags == Repeat::JUMP);
// m->setRepeatFlags(bars[i].repeatFlags);
m->setRepeatCount(bars[i].repeats);
// reset the volta sequence if we have an opening repeat
if (bars[i].repeatFlags == Repeat::START)
voltaSequence = 1;
// otherwise, if we see an end repeat symbol, only reset if the bar after it does not contain a volta
else if (bars[i].repeatFlags == Repeat::END && i < bars.length() - 1) {
if (bars[i+1].volta.voltaInfo.length() == 0) {
voltaSequence = 1;
}
}
score->measures()->add(m);
tick += nts;
ts = nts;
}
for (int i = 0; i < staves; ++i) {
int tuning[GP_MAX_STRING_NUMBER];
uchar c = readUChar(); // simulations bitmask
if (c & 0x2) { // 12 stringed guitar
}
if (c & 0x4) { // banjo track
}
QString name = readPascalString(40);
int strings = readInt();
if (strings <= 0 || strings > GP_MAX_STRING_NUMBER)
return false;
for (int j = 0; j < strings; ++j)
tuning[j] = readInt();
for (int j = strings; j < GP_MAX_STRING_NUMBER; ++j)
readInt();
/*int midiPort =*/ readInt(); // - 1;
int midiChannel = readInt() - 1;
/*int midiChannel2 =*/ readInt(); // - 1;
int frets = readInt();
int capo = readInt();
/*int color =*/ readInt();
std::vector<int> tuning2(strings);
//int tuning2[strings];
for (int k = 0; k < strings; ++k)
tuning2[strings-k-1] = tuning[k];
StringData stringData(frets, strings, &tuning2[0]);
Part* part = score->staff(i)->part();
Instrument* instr = part->instrument();
instr->setStringData(stringData);
instr->setSingleNoteDynamics(false);
part->setPartName(name);
part->setPlainLongName(name);
createTuningString(strings, &tuning2[0]);
//
// determine clef
//
Staff* staff = score->staff(i);
int patch = channelDefaults[midiChannel].patch;
ClefType clefId = ClefType::G;
if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
clefId = ClefType::PERC;
// instr->setUseDrumset(DrumsetKind::GUITAR_PRO);
instr->setDrumset(gpDrumset);
staff->setStaffType(Fraction(0,1), *StaffType::preset(StaffTypes::PERC_DEFAULT));
}
else
clefId = defaultClef(patch);
Measure* measure = score->firstMeasure();
Clef* clef = new Clef(score);
clef->setClefType(clefId);
clef->setTrack(i * VOICES);
Segment* segment = measure->getSegment(SegmentType::HeaderClef, Fraction(0,1));
segment->add(clef);
if (capo > 0) {
Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
StaffText* st = new StaffText(score);
// st->setTextStyleType(TextStyleType::STAFF);
st->setPlainText(QString("Capo. fret ") + QString::number(capo));
st->setTrack(i * VOICES);
s->add(st);
}
Channel* ch = instr->channel(0);
if (midiChannel == GP_DEFAULT_PERCUSSION_CHANNEL) {
ch->setProgram(0);
ch->setBank(128);
}
else {
ch->setProgram(patch);
ch->setBank(0);
}
ch->setVolume(channelDefaults[midiChannel].volume);
ch->setPan(channelDefaults[midiChannel].pan);
ch->setChorus(channelDefaults[midiChannel].chorus);
ch->setReverb(channelDefaults[midiChannel].reverb);
// missing: phase, tremolo
}
previousTempo = temp;
Measure* measure = score->firstMeasure();
bool mixChange = false;
for (int bar = 0; bar < measures; ++bar, measure = measure->nextMeasure()) {
const GpBar& gpbar = bars[bar];
if (!gpbar.marker.isEmpty()) {
RehearsalMark* s = new RehearsalMark(score);
s->setPlainText(gpbar.marker.trimmed());
s->setTrack(0);
Segment* segment = measure->getSegment(SegmentType::ChordRest, measure->tick());
segment->add(s);
}
std::vector<Tuplet*> tuplets(staves);
//Tuplet* tuplets[staves];
for (int staffIdx = 0; staffIdx < staves; ++staffIdx)
tuplets[staffIdx] = 0;
for (int staffIdx = 0; staffIdx < staves; ++staffIdx) {
Fraction measureLen = {0,1};
int track = staffIdx * VOICES;
Fraction fraction = measure->tick();
int beats = readInt();
if (beats > 200)
return false;
for (int beat = 0; beat < beats; ++beat) {
// int pause = 0;
uchar beatBits = readUChar();
bool dotted = beatBits & BEAT_DOTTED;
if (beatBits & BEAT_PAUSE)
/*pause =*/ readUChar();
slide = -1;
if (slides.contains(track))
slide = slides.take(track);
int len = readChar();
int tuple = 0;
if (beatBits & BEAT_TUPLET)
tuple = readInt();
Segment* segment = measure->getSegment(SegmentType::ChordRest, fraction);
if (beatBits & BEAT_CHORD) {
int numStrings = score->staff(staffIdx)->part()->instrument()->stringData()->strings();
int header = readUChar();
QString name;
if ((header & 1) == 0) {
name = readDelphiString();
readChord(segment, track, numStrings, name, false);
}
else {
skip(25);
name = readPascalString(34);
readChord(segment, track, numStrings, name, false);
skip(36);
}
}
Lyrics* lyrics = 0;
if (beatBits & BEAT_LYRICS) {
QString txt = readDelphiString();
lyrics = new Lyrics(score);
lyrics->setPlainText(txt);
}
int beatEffects = 0;
if (beatBits & BEAT_EFFECTS) {
beatEffects = readBeatEffects(track, segment);
}
bool vibrato = beatEffects & 0x1 || beatEffects & 0x2;
if (beatBits & BEAT_MIX_CHANGE) {
readMixChange(measure);
mixChange = true;
}
int strings = readUChar(); // used strings mask
Fraction l = len2fraction(len);
// Some beat effects could add a Chord before this
ChordRest* cr = segment->cr(track);
// if (!pause || strings)
if (strings) {
if(!segment->cr(track))
cr = new Chord(score);
}
else
{
if(segment->cr(track)){
segment->remove(segment->cr(track));
delete cr;
cr = 0;
}
cr = new Rest(score);
}
cr->setTrack(track);
if (lyrics)
cr->add(lyrics);
TDuration d(l);
d.setDots(dotted ? 1 : 0);
if (dotted)
l = l + (l * Fraction(1,2));
if (tuple) {
Tuplet* tuplet = tuplets[staffIdx];
if ((tuplet == 0) || (tuplet->elementsDuration() == tuplet->baseLen().fraction() * tuplet->ratio().numerator())) {
tuplet = new Tuplet(score);
tuplet->setTick(fraction);
tuplet->setTrack(cr->track());
tuplets[staffIdx] = tuplet;
setTuplet(tuplet, tuple);
tuplet->setParent(measure);
}
tuplet->setTrack(track);
tuplet->setBaseLen(l);
tuplet->setTicks(l * tuplet->ratio().denominator());
cr->setTuplet(tuplet);
tuplet->add(cr);
}
cr->setTicks(l);
if (cr->type() == ElementType::REST && l >= measure->ticks()) {
cr->setDurationType(TDuration::DurationType::V_MEASURE);
cr->setTicks(measure->ticks());
}
else
cr->setDurationType(d);
if(!segment->cr(track))
segment->add(cr);
Staff* staff = cr->staff();
int numStrings = staff->part()->instrument()->stringData()->strings();
bool hasSlur = false;
for (int i = 6; i >= 0; --i) {
if (strings & (1 << i) && ((6-i) < numStrings)) {
Note* note = new Note(score);
toChord(cr)->add(note);
if (vibrato)
addVibrato(note);
if (dotted) {
NoteDot* dot = new NoteDot(score);
// there is at most one dotted note in this guitar pro version - set 0 index
dot->setParent(note);
dot->setTrack(track); // needed to know the staff it belongs to (and detect tablature)
dot->setVisible(true);
note->add(dot);
}
hasSlur = (readNote(6-i, note) || hasSlur);
note->setTpcFromPitch();
}
}
if (cr && cr->type() == ElementType::CHORD && static_cast<Chord*>(cr)->notes().empty()) {
if (segment->cr(track))
segment->remove(cr);
delete cr;
cr = new Rest(score);
cr->setTicks(l);
cr->setTrack(track);
cr->setDurationType(d);
segment->add(cr);
}
createSlur(hasSlur, staffIdx, cr);
if (cr && (cr->isChord())) {
if (beatEffects >= 200) {
beatEffects -= 200;
Articulation* art = new Articulation(score);
art->setSymId(SymId::guitarFadeOut);
art->setAnchor(ArticulationAnchor::TOP_STAFF);
art->setPropertyFlags(Pid::ARTICULATION_ANCHOR, PropertyFlags::UNSTYLED);
if (!score->addArticulation(cr, art)) {
delete art;
}
}
applyBeatEffects(static_cast<Chord*>(cr), beatEffects);
if (slide > 0)
createSlide(slide, cr, staffIdx);
}
restsForEmptyBeats(segment, measure, cr, l, track, fraction);
fraction += cr->actualTicks();
measureLen += cr->actualTicks();
}
if (measureLen < measure->ticks()) {
score->setRest(fraction, track, measure->ticks() - measureLen, false, nullptr, false);
}
bool removeRests = true;
int counter = 0;
Rest* lastRest = nullptr;
for (auto seg = measure->first(); seg; seg = seg->next())
{
if (seg->segmentType() == SegmentType::ChordRest)
{
auto cr = seg->cr(track);
if (cr && cr->type() == ElementType::CHORD)
{
removeRests = false;
break;
}
else if (cr) {
++counter;
lastRest = static_cast<Rest*>(cr);
}
}
}
if (removeRests && counter < 2) {
removeRests = false;
if (counter == 1)
{
lastRest->setTicks(measure->timesig());
lastRest->setDurationType(TDuration::DurationType::V_MEASURE);
}
}
if (removeRests)
{
auto seg = measure->first();
while (seg && seg != measure->last())
{
if (seg->segmentType() == SegmentType::ChordRest)
{
auto cr = seg->cr(track);
if (cr) {
seg->remove(cr);
delete cr;
}
}
seg = seg->next();
}
auto cr = new Rest(score);
cr->setTicks(measure->timesig());
cr->setDurationType(TDuration::DurationType::V_MEASURE);
cr->setTrack(track);
seg->add(cr);
}
}
if (bar == 1 && !mixChange)
setTempo(temp, score->firstMeasure());
}
for (auto n : slideList) {
auto segment = n->chord()->segment();
auto measure1 = segment->measure();
while ((segment = segment->next1(SegmentType::ChordRest)) || ((measure1 = measure1->nextMeasure()) && (segment = measure1->first()))) {
// bool br = false;
auto crest = segment->cr(n->track());
if (!crest)
continue;
if (crest->type() == Ms::ElementType::REST)
break;
auto cr = static_cast<Chord*>(crest);
if (!cr)
continue;
if (cr->graceNotes().size())
cr = cr->graceNotes().first();
if (cr) {
for (auto nt : cr->notes()) {
if (nt->string() == n->string()) {
// auto mg = nt->magS();
Glissando* s = new Glissando(score);
s->setAnchor(Spanner::Anchor::NOTE);
s->setStartElement(n);
s->setTick(n->chord()->segment()->tick());
s->setTrack(n->track());
s->setParent(n);
s->setGlissandoType(GlissandoType::STRAIGHT);
s->setEndElement(nt);
s->setTick2(cr->segment()->tick());
s->setTrack2(n->track());
score->addElement(s);
break;
}
}
}
break;
}
}
return true;
}
//---------------------------------------------------------
// readBeatEffects
//---------------------------------------------------------
int GuitarPro3::readBeatEffects(int track, Segment* segment)
{
int effects = 0;
uchar fxBits = readUChar();
if (fxBits & BEAT_EFFECT) {
effects = readUChar(); // effect 1-tapping, 2-slapping, 3-popping
readInt(); // we don't need this integer
}
if (fxBits & BEAT_ARPEGGIO) {
int strokeup = readUChar(); // up stroke length
int strokedown = readUChar(); // down stroke length
Arpeggio* a = new Arpeggio(score);
if ( strokeup > 0 ) {
a->setArpeggioType(ArpeggioType::UP_STRAIGHT);
}
else if( strokedown > 0 ) {
a->setArpeggioType(ArpeggioType::DOWN_STRAIGHT);
}
else {
delete a;
a = 0;
}
if(a) {
ChordRest* cr = new Chord(score);
cr->setTrack(track);
cr->add(a);
segment->add(cr);
}
}
if (fxBits & BEAT_TREMOLO) {
}
if (fxBits & BEAT_FADE) {
#if 0
Articulation* art = new Articulation(score);
// art->setArticulationType(ArticulationType::FadeOut);
art->setSym(SymId::guitarFadeOut);
art->setAnchor(ArticulationAnchor::TOP_STAFF);
art->setPropertyFlags(Pid::ARTICULATION_ANCHOR, PropertyFlags::UNSTYLED);
if (!score->addArticulation(segment->cr(track), art)) {
delete art;
}
#endif
effects += 200;
}
if (fxBits & BEAT_DOTTED) {
}
if (fxBits & BEAT_CHORD) {
}
if (effects == 0)
return fxBits;
return effects;
}
//---------------------------------------------------------
// readTremoloBar
//---------------------------------------------------------
void GuitarPro::readTremoloBar(int /*track*/, Segment* /*segment*/)
{
/*int a1 =*/ readChar();
/*int a2 =*/ readChar();
/*int a3 =*/ readChar();
/*int a4 =*/ readChar();
/*int a5 =*/ readChar();
int n = readInt();
QList<PitchValue> points;
for (int i = 0; i < n; ++i) {
int time = readInt();
int pitch = readInt();
int vibrato = readUChar();
points.append(PitchValue(time, pitch, vibrato));
}
#if 0
//TODO
TremoloBar* b = new TremoloBar(segment->score());
b->setPoints(points);
b->setTrack(track);
segment->add(b);
#endif
}
//---------------------------------------------------------
// createCrecDim
//---------------------------------------------------------
void GuitarPro::createCrecDim(int staffIdx, int track, const Fraction& tick, bool crec)
{
hairpins[staffIdx] = new Hairpin(score);
if (crec)
hairpins[staffIdx]->setHairpinType(HairpinType::CRESC_HAIRPIN);
else
hairpins[staffIdx]->setHairpinType(HairpinType::DECRESC_HAIRPIN);
hairpins[staffIdx]->setTick(tick);
hairpins[staffIdx]->setTick2(tick);
hairpins[staffIdx]->setTrack(track);
hairpins[staffIdx]->setTrack(track);
score->undoAddElement(hairpins[staffIdx]);
}
//---------------------------------------------------------
// importGTP
//---------------------------------------------------------
Score::FileError importGTP(MasterScore* score, const QString& name)
{
QFile fp(name);
if (!fp.exists())
return Score::FileError::FILE_NOT_FOUND;
if (!fp.open(QIODevice::ReadOnly))
return Score::FileError::FILE_OPEN_ERROR;
char header[5];
fp.read(header, 4);
header[4] = 0;
fp.seek(0);
if (name.endsWith(".ptb", Qt::CaseInsensitive) || strcmp(header, "ptab") == 0) {
PowerTab ptb(&fp, score);
return ptb.read();
}
GuitarPro* gp;
bool readResult = false;
// check to see if we are dealing with a GPX file via the extension
if (name.endsWith(".gpx", Qt::CaseInsensitive) || strcmp(header, "BCFZ") == 0) {
gp = new GuitarPro6(score);
gp->initGuitarProDrumset();
readResult = gp->read(&fp);
gp->setTempo(0, 0);
}
// otherwise it's an older version - check the header
else if (strcmp(&header[1], "FIC") == 0) {
uchar l;
fp.read((char*)&l, 1);
char ss[30];
fp.read(ss, 30);
ss[l] = 0;
QString s(ss);
if (s.startsWith("FICHIER GUITAR PRO "))
s = s.mid(20);
else if (s.startsWith("FICHIER GUITARE PRO "))
s = s.mid(21);
else {
qDebug("unknown gtp format <%s>", ss);
return Score::FileError::FILE_BAD_FORMAT;
}
int a = s.left(1).toInt();
int b = s.mid(2).toInt();
int version = a * 100 + b;
if (a == 1)
gp = new GuitarPro1(score, version);
else if (a == 2)
gp = new GuitarPro2(score, version);
else if (a == 3)
gp = new GuitarPro3(score, version);
else if (a == 4)
gp = new GuitarPro4(score, version);
else if (a == 5)
gp = new GuitarPro5(score, version);
else {
qDebug("unknown gtp format %d", version);
return Score::FileError::FILE_BAD_FORMAT;
}
gp->initGuitarProDrumset();
readResult = gp->read(&fp);
gp->setTempo(0, 0);
}
else {
return Score::FileError::FILE_BAD_FORMAT;
}
if (readResult == false) {
/*if (!MScore::noGui) {
QMessageBox::warning(0,
QWidget::tr("Import Guitar Pro"),
QWidget::tr("Load failed: %1").arg(gp->error(errNo)),
QString::null, QWidget::tr("Quit"), QString::null, 0, 1);
}*/
qDebug("guitar pro import error====");
// avoid another error message box
return Score::FileError::FILE_NO_ERROR;
}
score->style().set(Sid::ArpeggioHiddenInStdIfTab, true);
MeasureBase* m;
if (!score->measures()->first()) {
m = new VBox(score);
m->setTick(Fraction(0,1));
score->addMeasure(m, 0);
}
else {
m = score->measures()->first();
if (!m->isVBox()) {
MeasureBase* mb = new VBox(score);
mb->setTick(Fraction(0,1));
score->addMeasure(mb, m);
m = mb;
}
}
if (!gp->title.isEmpty()) {
Text* s = new Text(score, Tid::TITLE);
s->setPlainText(gp->title);
m->add(s);
}
if (!gp->subtitle.isEmpty()|| !gp->artist.isEmpty() || !gp->album.isEmpty()) {
Text* s = new Text(score, Tid::SUBTITLE);
QString str;
if (!gp->subtitle.isEmpty())
str.append(gp->subtitle);
if (!gp->artist.isEmpty()) {
if (!str.isEmpty())
str.append("\n");
str.append(gp->artist);
}
if (!gp->album.isEmpty()) {
if (!str.isEmpty())
str.append("\n");
str.append(gp->album);
}
s->setPlainText(str);
m->add(s);
}
if (!gp->composer.isEmpty()) {
Text* s = new Text(score, Tid::COMPOSER);
s->setPlainText(gp->composer);
m->add(s);
}
int idx = 0;
for (Measure* m1 = score->firstMeasure(); m1; m1 = m1->nextMeasure(), ++idx) {
const GpBar& bar = gp->bars[idx];
if (bar.barLine != BarLineType::NORMAL && bar.barLine != BarLineType::END_REPEAT && bar.barLine != BarLineType::START_REPEAT && bar.barLine != BarLineType::END_START_REPEAT)
m1->setEndBarLineType(bar.barLine, 0);
}
if (score->lastMeasure() && score->lastMeasure()->endBarLineType() != BarLineType::NORMAL)
score->lastMeasure()->setEndBarLineType(BarLineType::END, false);
//
// create parts (excerpts)
//
std::vector<Part*> infoParts;
for (Part* part : score->parts()) {
const QString& longName = part->longName();
if (!longName.isEmpty() && longName[0] == '@') {
infoParts.push_back(part);
continue;
}
QMultiMap<int, int> tracks;
Score* pscore = new Score(score);
//TODO-ws pscore->showLyrics = score->showLyrics;
pscore->style().set(Sid::createMultiMeasureRests, false);
pscore->style().set(Sid::ArpeggioHiddenInStdIfTab, true);
QList<int> stavesMap;
Part* p = new Part(pscore);
p->setInstrument(*part->instrument());
//TODO-ws pscore->tuning = gp->tunings[counter++];
Staff* staff = part->staves()->front();
Staff* s = new Staff(pscore);
s->setPart(p);
const StaffType* st = staff->constStaffType(Fraction(0,1));
s->setStaffType(Fraction(0,1), *st);
s->linkTo(staff);
p->staves()->append(s);
pscore->staves().append(s);
stavesMap.append(staff->idx());
for (int i = staff->idx() * VOICES, j = 0; i < staff->idx() * VOICES + VOICES; i++, j++)
tracks.insert(i, j);
Excerpt* excerpt = new Excerpt(score);
excerpt->setTracks(tracks);
excerpt->setPartScore(pscore);
pscore->setExcerpt(excerpt);
excerpt->setTitle(part->partName());
excerpt->parts().append(part);
score->excerpts().append(excerpt);
Excerpt::cloneStaves(score, pscore, stavesMap, tracks);
if (staff->part()->instrument()->stringData()->strings() > 0
&& part->staves()->front()->constStaffType(Fraction(0,1))->group() == StaffGroup::STANDARD) {
p->setStaves(2);
Staff* s1 = p->staff(1);
int lines = staff->part()->instrument()->stringData()->strings();
StaffTypes sts = StaffTypes::TAB_DEFAULT;
if (lines == 4)
sts = StaffTypes::TAB_4COMMON;
StaffType st1 = *StaffType::preset(sts);
s1->setStaffType(Fraction(0,1), st1);
s1->setLines(Fraction(0,1), lines);
Excerpt::cloneStaff(s,s1);
p->staves()->front()->addBracket(new BracketItem(pscore, BracketType::NORMAL, 2));
}
pscore->appendPart(p);
//
// create excerpt title
//
MeasureBase* measure = pscore->first();
if (!measure || (measure->type() != ElementType::VBOX)) {
MeasureBase* mb = new VBox(pscore);
mb->setTick(Fraction(0,1));
pscore->addMeasure(mb, measure);
measure = mb;
}
Text* txt = new Text(pscore, Tid::INSTRUMENT_EXCERPT);
txt->setPlainText(part->longName());
measure->add(txt);
//
// layout score
//
pscore->setPlaylistDirty();
pscore->setLayoutAll();
pscore->addLayoutFlags(LayoutFlag::FIX_PITCH_VELO);
// pscore->doLayout();
}
for (auto p : infoParts) {
auto staff = p->staves()->back();
score->removeStaff(staff);
score->removePart(p);
delete staff;
delete p;
}
// score->rebuildMidiMapping();
// score->updateChannel();
// album
// copyright
score->setCreated(true);
delete gp;
return Score::FileError::FILE_NO_ERROR;
}
}