MuseScore/libmscore/read114.cpp
Joachim Schmitz 9b9081bd36 fix some compiler warnings
not dealing with those where the code suggests that there are TODOs and
those are too far away to immediatelly spot them
2017-02-17 11:04:49 +01:00

2402 lines
107 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2011 Werner Schweer and others
//
// 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 LICENSE.GPL
//=============================================================================
#include "score.h"
#include "slur.h"
#include "staff.h"
#include "excerpt.h"
#include "chord.h"
#include "rest.h"
#include "keysig.h"
#include "volta.h"
#include "measure.h"
#include "beam.h"
#include "page.h"
#include "segment.h"
#include "ottava.h"
#include "stafftype.h"
#include "text.h"
#include "part.h"
#include "sig.h"
#include "box.h"
#include "dynamic.h"
#include "drumset.h"
#include "style.h"
#include "sym.h"
#include "xml.h"
#include "stringdata.h"
#include "tempo.h"
#include "tempotext.h"
#include "clef.h"
#include "barline.h"
#include "timesig.h"
#include "tuplet.h"
#include "spacer.h"
#include "stafftext.h"
#include "repeat.h"
#include "breath.h"
#include "tremolo.h"
#include "articulation.h"
#include "utils.h"
#include "accidental.h"
#include "fingering.h"
#include "marker.h"
#include "read206.h"
namespace Ms {
static int g_guitarStrings[] = {40,45,50,55,59,64};
static int g_bassStrings[] = {28,33,38,43};
static int g_violinStrings[] = {55,62,69,76};
static int g_violaStrings[] = {48,55,62,69};
static int g_celloStrings[] = {36,43,50,57};
//---------------------------------------------------------
// StyleVal114
//---------------------------------------------------------
struct StyleVal2 {
StyleIdx idx;
QVariant val;
};
static const StyleVal2 style114[] = {
{ StyleIdx::lyricsMinBottomDistance, Spatium(2) },
{ StyleIdx::frameSystemDistance, Spatium(1.0) },
{ StyleIdx::minMeasureWidth, Spatium(4.0) },
{ StyleIdx::endBarDistance, Spatium(0.30) },
{ StyleIdx::repeatBarTips, QVariant(false) },
{ StyleIdx::startBarlineSingle, QVariant(false) },
{ StyleIdx::startBarlineMultiple, QVariant(true) },
{ StyleIdx::bracketWidth, QVariant(0.35) },
{ StyleIdx::bracketDistance, QVariant(0.25) },
{ StyleIdx::clefLeftMargin, QVariant(0.5) },
{ StyleIdx::keysigLeftMargin, QVariant(0.5) },
{ StyleIdx::timesigLeftMargin, QVariant(0.5) },
{ StyleIdx::clefKeyRightMargin, QVariant(1.75) },
{ StyleIdx::clefBarlineDistance, QVariant(0.18) },
{ StyleIdx::stemWidth, QVariant(0.13) },
{ StyleIdx::shortenStem, QVariant(true) },
{ StyleIdx::shortStemProgression, QVariant(0.25) },
{ StyleIdx::shortestStem, QVariant(2.25) },
{ StyleIdx::beginRepeatLeftMargin, QVariant(1.0) },
{ StyleIdx::minNoteDistance, QVariant(0.4) },
{ StyleIdx::barNoteDistance, QVariant(1.2) },
{ StyleIdx::noteBarDistance, QVariant(1.0) },
{ StyleIdx::measureSpacing, QVariant(1.2) },
{ StyleIdx::staffLineWidth, QVariant(0.08) },
{ StyleIdx::ledgerLineWidth, QVariant(0.12) },
{ StyleIdx::akkoladeWidth, QVariant(1.6) },
{ StyleIdx::accidentalDistance, QVariant(0.22) },
{ StyleIdx::accidentalNoteDistance, QVariant(0.22) },
{ StyleIdx::beamWidth, QVariant(0.48) },
{ StyleIdx::beamDistance, QVariant(0.5) },
{ StyleIdx::beamMinLen, QVariant(1.25) },
{ StyleIdx::dotNoteDistance, QVariant(0.35) },
{ StyleIdx::dotRestDistance, QVariant(0.25) },
{ StyleIdx::dotDotDistance, QVariant(0.5) },
{ StyleIdx::propertyDistanceHead, QVariant(1.0) },
{ StyleIdx::propertyDistanceStem, QVariant(0.5) },
{ StyleIdx::propertyDistance, QVariant(1.0) },
{ StyleIdx::articulationMag, QVariant(qreal(1.0)) },
{ StyleIdx::lastSystemFillLimit, QVariant(0.3) },
{ StyleIdx::hairpinHeight, QVariant(1.2) },
{ StyleIdx::hairpinContHeight, QVariant(0.5) },
{ StyleIdx::hairpinLineWidth, QVariant(0.13) },
{ StyleIdx::showPageNumber, QVariant(true) },
{ StyleIdx::showPageNumberOne, QVariant(false) },
{ StyleIdx::pageNumberOddEven, QVariant(true) },
{ StyleIdx::showMeasureNumber, QVariant(true) },
{ StyleIdx::showMeasureNumberOne, QVariant(false) },
{ StyleIdx::measureNumberInterval, QVariant(5) },
{ StyleIdx::measureNumberSystem, QVariant(true) },
{ StyleIdx::measureNumberAllStaffs, QVariant(false) },
{ StyleIdx::smallNoteMag, QVariant(qreal(0.7)) },
{ StyleIdx::graceNoteMag, QVariant(qreal(0.7)) },
{ StyleIdx::smallStaffMag, QVariant(qreal(0.7)) },
{ StyleIdx::smallClefMag, QVariant(qreal(0.8)) },
{ StyleIdx::genClef, QVariant(true) },
{ StyleIdx::genKeysig, QVariant(true) },
{ StyleIdx::genCourtesyTimesig, QVariant(true) },
{ StyleIdx::genCourtesyKeysig, QVariant(true) },
{ StyleIdx::useStandardNoteNames, QVariant(true) },
{ StyleIdx::useGermanNoteNames, QVariant(false) },
{ StyleIdx::useFullGermanNoteNames, QVariant(false) },
{ StyleIdx::useSolfeggioNoteNames, QVariant(false) },
{ StyleIdx::useFrenchNoteNames, QVariant(false) },
{ StyleIdx::chordDescriptionFile, QVariant(QString("stdchords.xml")) },
{ StyleIdx::chordStyle, QVariant(QString("custom")) },
{ StyleIdx::chordsXmlFile, QVariant(true) },
{ StyleIdx::harmonyY, QVariant(0.0) },
{ StyleIdx::concertPitch, QVariant(false) },
{ StyleIdx::createMultiMeasureRests, QVariant(false) },
{ StyleIdx::minEmptyMeasures, QVariant(2) },
{ StyleIdx::minMMRestWidth, QVariant(4.0) },
{ StyleIdx::hideEmptyStaves, QVariant(false) },
{ StyleIdx::gateTime, QVariant(100) },
{ StyleIdx::tenutoGateTime, QVariant(100) },
{ StyleIdx::staccatoGateTime, QVariant(50) },
{ StyleIdx::slurGateTime, QVariant(100) },
{ StyleIdx::ArpeggioNoteDistance, QVariant(.5) },
{ StyleIdx::ArpeggioLineWidth, QVariant(.18) },
{ StyleIdx::ArpeggioHookLen, QVariant(.8) },
{ StyleIdx::keySigNaturals, QVariant(int(KeySigNatural::BEFORE)) },
{ StyleIdx::tupletMaxSlope, QVariant(qreal(0.5)) },
{ StyleIdx::tupletOufOfStaff, QVariant(false) },
{ StyleIdx::tupletVHeadDistance, QVariant(.5) },
{ StyleIdx::tupletVStemDistance, QVariant(.25) },
{ StyleIdx::tupletStemLeftDistance, QVariant(.5) },
{ StyleIdx::tupletStemRightDistance, QVariant(.5) },
{ StyleIdx::tupletNoteLeftDistance, QVariant(0.0) },
{ StyleIdx::tupletNoteRightDistance, QVariant(0.0) },
{ StyleIdx::hideInstrumentNameIfOneInstrument, QVariant(false) },
};
#define MM(x) ((x)/INCH)
//---------------------------------------------------------
// PaperSize
//---------------------------------------------------------
struct PaperSize {
const char* name;
qreal w, h; // size in inch
PaperSize(const char* n, qreal wi, qreal hi)
: name(n), w(wi), h(hi) {}
};
static const PaperSize paperSizes114[] = {
PaperSize("Custom", MM(1), MM(1)),
PaperSize("A4", MM(210), MM(297)),
PaperSize("B5", MM(176), MM(250)),
PaperSize("Letter", 8.5, 11),
PaperSize("Legal", 8.5, 14),
PaperSize("Executive", 7.5, 10),
PaperSize("A0", MM(841), MM(1189)),
PaperSize("A1", MM(594), MM(841)),
PaperSize("A2", MM(420), MM(594)),
PaperSize("A3", MM(297), MM(420)),
PaperSize("A5", MM(148), MM(210)),
PaperSize("A6", MM(105), MM(148)),
PaperSize("A7", MM(74), MM(105)),
PaperSize("A8", MM(52), MM(74)),
PaperSize("A9", MM(37), MM(52)),
PaperSize("A10", MM(26), MM(37)),
PaperSize("B0", MM(1000), MM(1414)),
PaperSize("B1", MM(707), MM(1000)),
PaperSize("B2", MM(500), MM(707)),
PaperSize("B3", MM(353), MM(500)),
PaperSize("B4", MM(250), MM(353)),
PaperSize("B6", MM(125), MM(176)),
PaperSize("B7", MM(88), MM(125)),
PaperSize("B8", MM(62), MM(88)),
PaperSize("B9", MM(44), MM(62)),
PaperSize("B10", MM(31), MM(44)),
PaperSize("Comm10E", MM(105), MM(241)),
PaperSize("DLE", MM(110), MM(220)),
PaperSize("Folio", MM(210), MM(330)),
PaperSize("Ledger", MM(432), MM(279)),
PaperSize("Tabloid", MM(279), MM(432)),
PaperSize(0, MM(1), MM(1)) // mark end of list
};
#if 0
//---------------------------------------------------------
// getPaperSize
//---------------------------------------------------------
static const PaperSize* getPaperSize114(const QString& name)
{
for (int i = 0; paperSizes114[i].name; ++i) {
if (name == paperSizes114[i].name)
return &paperSizes114[i];
}
qDebug("unknown paper size");
return &paperSizes114[0];
}
#endif
//---------------------------------------------------------
// readAccidental
//---------------------------------------------------------
static void readAccidental(Accidental* a, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "bracket") {
int i = e.readInt();
if (i == 0 || i == 1)
a->setBracket(AccidentalBracket(i));
}
else if (tag == "subtype") {
QString text(e.readElementText());
bool isInt;
int i = text.toInt(&isInt);
if (isInt) {
a->setBracket(i & 0x8000 ? AccidentalBracket::PARENTHESIS : AccidentalBracket::BRACKET);
i &= ~0x8000;
AccidentalType at;
switch (i) {
case 0:
at = AccidentalType::NONE;
break;
case 6:
a->setBracket(AccidentalBracket::PARENTHESIS);
case 1:
case 11:
at = AccidentalType::SHARP;
break;
case 7:
a->setBracket(AccidentalBracket::PARENTHESIS);
case 2:
case 12:
at = AccidentalType::FLAT;
break;
case 8:
a->setBracket(AccidentalBracket::PARENTHESIS);
case 3:
case 13:
at = AccidentalType::SHARP2;
break;
case 9:
a->setBracket(AccidentalBracket::PARENTHESIS);
case 4:
case 14:
at = AccidentalType::FLAT2;
break;
case 10:
a->setBracket(AccidentalBracket::PARENTHESIS);
case 5:
case 15:
at = AccidentalType::NATURAL;
break;
case 16:
at = AccidentalType::FLAT_SLASH;
break;
case 17:
at = AccidentalType::FLAT_SLASH2;
break;
case 18:
at = AccidentalType::MIRRORED_FLAT2;
break;
case 19:
at = AccidentalType::MIRRORED_FLAT;
break;
case 20:
at = AccidentalType::NONE;//AccidentalType::MIRRORED_FLAT_SLASH;
break;
case 21:
at = AccidentalType::NONE;//AccidentalType::FLAT_FLAT_SLASH;
break;
case 22:
at = AccidentalType::SHARP_SLASH;
break;
case 23:
at = AccidentalType::SHARP_SLASH2;
break;
case 24:
at = AccidentalType::SHARP_SLASH3;
break;
case 25:
at = AccidentalType::SHARP_SLASH4;
break;
case 26:
at = AccidentalType::SHARP_ARROW_UP;
break;
case 27:
at = AccidentalType::SHARP_ARROW_DOWN;
break;
case 28:
at = AccidentalType::NONE;//AccidentalType::SHARP_ARROW_BOTH;
break;
case 29:
at = AccidentalType::FLAT_ARROW_UP;
break;
case 30:
at = AccidentalType::FLAT_ARROW_DOWN;
break;
case 31:
at = AccidentalType::NONE;//AccidentalType::FLAT_ARROW_BOTH;
break;
case 32:
at = AccidentalType::NATURAL_ARROW_UP;
break;
case 33:
at = AccidentalType::NATURAL_ARROW_DOWN;
break;
case 34:
at = AccidentalType::NONE;//AccidentalType::NATURAL_ARROW_BOTH;
break;
default:
at = AccidentalType::NONE;
break;
}
a->setAccidentalType(at);
}
else {
const static std::map<QString, AccidentalType> accMap = {
{"none", AccidentalType::NONE}, {"sharp", AccidentalType::SHARP},
{"flat", AccidentalType::FLAT}, {"natural", AccidentalType::NATURAL},
{"double sharp", AccidentalType::SHARP2}, {"double flat", AccidentalType::FLAT2},
{"flat-slash", AccidentalType::FLAT_SLASH}, {"flat-slash2", AccidentalType::FLAT_SLASH2},
{"mirrored-flat2", AccidentalType::MIRRORED_FLAT2}, {"mirrored-flat", AccidentalType::MIRRORED_FLAT},
{"sharp-slash", AccidentalType::SHARP_SLASH}, {"sharp-slash2", AccidentalType::SHARP_SLASH2},
{"sharp-slash3", AccidentalType::SHARP_SLASH3}, {"sharp-slash4", AccidentalType::SHARP_SLASH4},
{"sharp arrow up", AccidentalType::SHARP_ARROW_UP}, {"sharp arrow down", AccidentalType::SHARP_ARROW_DOWN},
{"flat arrow up", AccidentalType::FLAT_ARROW_UP}, {"flat arrow down", AccidentalType::FLAT_ARROW_DOWN},
{"natural arrow up", AccidentalType::NATURAL_ARROW_UP}, {"natural arrow down", AccidentalType::NATURAL_ARROW_DOWN},
{"sori", AccidentalType::SORI}, {"koron", AccidentalType::KORON}
};
auto it = accMap.find(text);
if (it == accMap.end()) {
qDebug("invalid type %s", qPrintable(text));
a->setAccidentalType(AccidentalType::NONE);
}
else
a->setAccidentalType(it->second);
}
}
else if (tag == "role") {
AccidentalRole r = AccidentalRole(e.readInt());
if (r == AccidentalRole::AUTO || r == AccidentalRole::USER)
a->setRole(r);
}
else if (tag == "small")
a->setSmall(e.readInt());
else if (tag == "offset")
e.skipCurrentElement(); // ignore manual layout in older scores
else if (a->Element::readProperties(e))
;
else
e.unknown();
}
}
//---------------------------------------------------------
// convertHeadGroup
//---------------------------------------------------------
static NoteHead::Group convertHeadGroup(int i)
{
NoteHead::Group val;
switch (i) {
case 1:
val = NoteHead::Group::HEAD_CROSS;
break;
case 2:
val = NoteHead::Group::HEAD_DIAMOND;
break;
case 3:
val = NoteHead::Group::HEAD_TRIANGLE_DOWN;
break;
case 4:
val = NoteHead::Group::HEAD_MI;
break;
case 5:
val = NoteHead::Group::HEAD_SLASH;
break;
case 6:
val = NoteHead::Group::HEAD_XCIRCLE;
break;
case 7:
val = NoteHead::Group::HEAD_DO;
break;
case 8:
val = NoteHead::Group::HEAD_RE;
break;
case 9:
val = NoteHead::Group::HEAD_FA;
break;
case 10:
val = NoteHead::Group::HEAD_LA;
break;
case 11:
val = NoteHead::Group::HEAD_TI;
break;
case 12:
val = NoteHead::Group::HEAD_SOL;
break;
case 13:
val = NoteHead::Group::HEAD_BREVIS_ALT;
break;
default:
val = NoteHead::Group::HEAD_NORMAL;
}
return val;
}
//---------------------------------------------------------
// readNote
//---------------------------------------------------------
static void readNote(Note* note, XmlReader& e)
{
e.hasAccidental = false; // used for userAccidental backward compatibility
note->setTpc1(Tpc::TPC_INVALID);
note->setTpc2(Tpc::TPC_INVALID);
if (e.hasAttribute("pitch")) // obsolete
note->setPitch(e.intAttribute("pitch"));
if (e.hasAttribute("tpc")) // obsolete
note->setTpc1(e.intAttribute("tpc"));
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Accidental") {
// on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
// if a userAccidental has some other property set (like for instance offset)
Accidental* a;
if (e.hasAccidental) // if the other tag has already been read,
a = note->accidental(); // re-use the accidental it constructed
else
a = new Accidental(note->score());
// the accidental needs to know the properties of the
// track it belongs to (??)
a->setTrack(note->track());
readAccidental(a, e);
if (!e.hasAccidental) // only the new accidental, if it has been added previously
note->add(a);
e.hasAccidental = true; // we now have an accidental
}
else if (tag == "Text") {
Fingering* f = new Fingering(note->score());
f->read(e);
note->add(f);
}
else if (tag == "onTimeType") {
if (e.readElementText() == "offset")
note->setOnTimeType(2);
else
note->setOnTimeType(1);
}
else if (tag == "offTimeType") {
if (e.readElementText() == "offset")
note->setOffTimeType(2);
else
note->setOffTimeType(1);
}
else if (tag == "onTimeOffset") {
if (note->onTimeType() == 1)
note->setOnTimeOffset(e.readInt() * 1000 / note->chord()->actualTicks());
else
note->setOnTimeOffset(e.readInt() * 10);
}
else if (tag == "offTimeOffset") {
if (note->offTimeType() == 1)
note->setOffTimeOffset(1000 + (e.readInt() * 1000 / note->chord()->actualTicks()));
else
note->setOffTimeOffset(1000 + (e.readInt() * 10));
}
else if (tag == "userAccidental") {
QString val(e.readElementText());
bool ok;
int k = val.toInt(&ok);
if (ok) {
// on older scores, a note could have both a <userAccidental> tag and an <Accidental> tag
// if a userAccidental has some other property set (like for instance offset)
// only construct a new accidental, if the other tag has not been read yet
// (<userAccidental> tag is only used in older scores: no need to check the score mscVersion)
if (!e.hasAccidental) {
Accidental* a = new Accidental(note->score());
note->add(a);
}
// TODO: for backward compatibility
bool bracket = k & 0x8000;
k &= 0xfff;
AccidentalType at = AccidentalType::NONE;
switch(k) {
case 0: at = AccidentalType::NONE; break;
case 1: at = AccidentalType::SHARP; break;
case 2: at = AccidentalType::FLAT; break;
case 3: at = AccidentalType::SHARP2; break;
case 4: at = AccidentalType::FLAT2; break;
case 5: at = AccidentalType::NATURAL; break;
case 6: at = AccidentalType::FLAT_SLASH; break;
case 7: at = AccidentalType::FLAT_SLASH2; break;
case 8: at = AccidentalType::MIRRORED_FLAT2; break;
case 9: at = AccidentalType::MIRRORED_FLAT; break;
case 10: at = AccidentalType::NONE; break; // AccidentalType::MIRRORED_FLAT_SLASH
case 11: at = AccidentalType::NONE; break; // AccidentalType::FLAT_FLAT_SLASH
case 12: at = AccidentalType::SHARP_SLASH; break;
case 13: at = AccidentalType::SHARP_SLASH2; break;
case 14: at = AccidentalType::SHARP_SLASH3; break;
case 15: at = AccidentalType::SHARP_SLASH4; break;
case 16: at = AccidentalType::SHARP_ARROW_UP; break;
case 17: at = AccidentalType::SHARP_ARROW_DOWN; break;
case 18: at = AccidentalType::NONE; break; // AccidentalType::SHARP_ARROW_BOTH
case 19: at = AccidentalType::FLAT_ARROW_UP; break;
case 20: at = AccidentalType::FLAT_ARROW_DOWN; break;
case 21: at = AccidentalType::NONE; break; // AccidentalType::FLAT_ARROW_BOTH
case 22: at = AccidentalType::NATURAL_ARROW_UP; break;
case 23: at = AccidentalType::NATURAL_ARROW_DOWN; break;
case 24: at = AccidentalType::NONE; break; // AccidentalType::NATURAL_ARROW_BOTH
case 25: at = AccidentalType::SORI; break;
case 26: at = AccidentalType::KORON; break;
}
note->accidental()->setAccidentalType(at);
note->accidental()->setBracket(AccidentalBracket(bracket));
note->accidental()->setRole(AccidentalRole::USER);
e.hasAccidental = true; // we now have an accidental
}
}
else if (tag == "onTimeType")
e.skipCurrentElement(); // _onTimeType = readValueType(e);
else if (tag == "offTimeType")
e.skipCurrentElement(); // _offTimeType = readValueType(e);
else if (tag == "offset")
e.skipCurrentElement(); // ignore manual layout in older scores
else if (tag == "move")
note->chord()->setStaffMove(e.readInt());
else if (tag == "head") {
int i = e.readInt();
NoteHead::Group val = convertHeadGroup(i);
note->setHeadGroup(val);
}
else if (tag == "headType") {
int i = e.readInt();
NoteHead::Type val;
switch (i) {
case 1:
val = NoteHead::Type::HEAD_WHOLE;
break;
case 2:
val = NoteHead::Type::HEAD_HALF;
break;
case 3:
val = NoteHead::Type::HEAD_QUARTER;
break;
case 4:
val = NoteHead::Type::HEAD_BREVIS;
break;
default:
val = NoteHead::Type::HEAD_AUTO;
}
note->setHeadType(val);
}
else if (note->readProperties(e))
;
else
e.unknown();
}
// ensure sane values:
note->setPitch(limit(note->pitch(), 0, 127));
if (note->concertPitch())
note->setTpc2(Tpc::TPC_INVALID);
else {
note->setPitch(note->pitch() + note->transposition());
note->setTpc2(note->tpc1());
note->setTpc1(Tpc::TPC_INVALID);
}
if (!tpcIsValid(note->tpc1()) && !tpcIsValid(note->tpc2())) {
Key key = (note->staff() && note->chord()) ? note->staff()->key(note->chord()->tick()) : Key::C;
int tpc = pitch2tpc(note->pitch(), key, Prefer::NEAREST);
if (note->concertPitch())
note->setTpc1(tpc);
else
note->setTpc2(tpc);
}
if (!(tpcIsValid(note->tpc1()) && tpcIsValid(note->tpc2()))) {
int tick = note->chord() ? note->chord()->tick() : -1;
Interval v = note->staff() ? note->part()->instrument(tick)->transpose() : Interval();
if (tpcIsValid(note->tpc1())) {
v.flip();
if (v.isZero())
note->setTpc2(note->tpc1());
else
note->setTpc2(Ms::transposeTpc(note->tpc1(), v, true));
}
else {
if (v.isZero())
note->setTpc1(note->tpc2());
else
note->setTpc1(Ms::transposeTpc(note->tpc2(), v, true));
}
}
// check consistency of pitch, tpc1, tpc2, and transposition
// see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
// but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
// including perhaps some we don't know about yet,
// we will attempt to fix some problems here regardless of version
if (!e.pasteMode() && !MScore::testMode) {
int tpc1Pitch = (tpc2pitch(note->tpc1()) + 12) % 12;
int tpc2Pitch = (tpc2pitch(note->tpc2()) + 12) % 12;
int concertPitch = note->pitch() % 12;
if (tpc1Pitch != concertPitch) {
qDebug("bad tpc1 - concertPitch = %d, tpc1 = %d", concertPitch, tpc1Pitch);
note->setPitch(note->pitch() + tpc1Pitch - concertPitch);
}
Interval v = note->staff()->part()->instrument(e.tick())->transpose();
int transposedPitch = (note->pitch() - v.chromatic) % 12;
if (tpc2Pitch != transposedPitch) {
qDebug("bad tpc2 - transposedPitch = %d, tpc2 = %d", transposedPitch, tpc2Pitch);
// just in case the staff transposition info is not reliable here,
v.flip();
note->setTpc2(Ms::transposeTpc(note->tpc1(), v, true));
}
}
}
//---------------------------------------------------------
// readClefType
//---------------------------------------------------------
static ClefType readClefType(const QString& s)
{
ClefType ct = ClefType::G;
bool ok;
int i = s.toInt(&ok);
if (ok) {
// convert obsolete old coding
switch (i) {
default:
case 0: ct = ClefType::G; break;
case 1: ct = ClefType::G8_VA; break;
case 2: ct = ClefType::G15_MA; break;
case 3: ct = ClefType::G8_VB; break;
case 4: ct = ClefType::F; break;
case 5: ct = ClefType::F8_VB; break;
case 6: ct = ClefType::F15_MB; break;
case 7: ct = ClefType::F_B; break;
case 8: ct = ClefType::F_C; break;
case 9: ct = ClefType::C1; break;
case 10: ct = ClefType::C2; break;
case 11: ct = ClefType::C3; break;
case 12: ct = ClefType::C4; break;
case 13: ct = ClefType::TAB; break;
case 14: ct = ClefType::PERC; break;
case 15: ct = ClefType::C5; break;
case 16: ct = ClefType::G_1; break;
case 17: ct = ClefType::F_8VA; break;
case 18: ct = ClefType::F_15MA; break;
case 19: ct = ClefType::PERC; break; // PERC2 no longer supported
case 20: ct = ClefType::TAB_SERIF; break;
}
}
return ct;
}
//---------------------------------------------------------
// readClef
//---------------------------------------------------------
static void readClef(Clef* clef, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype")
clef->setClefType(readClefType(e.readElementText()));
else if (!clef->readProperties(e))
e.unknown();
}
if (clef->clefType() == ClefType::INVALID)
clef->setClefType(ClefType::G);
}
//---------------------------------------------------------
// readTuplet
//---------------------------------------------------------
static void readTuplet(Tuplet* tuplet, XmlReader& e)
{
int bl = -1;
tuplet->setId(e.intAttribute("id", 0));
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") // obsolete
e.skipCurrentElement();
else if (tag == "hasNumber") // obsolete even in 1.3
tuplet->setNumberType(e.readInt() ? Tuplet::NumberType::SHOW_NUMBER : Tuplet::NumberType::NO_TEXT);
else if (tag == "hasLine") { // obsolete even in 1.3
tuplet->setHasBracket(e.readInt());
tuplet->setBracketType(Tuplet::BracketType::AUTO_BRACKET);
}
else if (tag == "baseLen") // obsolete even in 1.3
bl = e.readInt();
else if (tag == "tick")
tuplet->setTick(e.readInt());
else if (!tuplet->readProperties(e))
e.unknown();
}
Fraction r = (tuplet->ratio() == 1) ? tuplet->ratio() : tuplet->ratio().reduced();
// this may be wrong, but at this stage it is kept for compatibility. It will be corrected afterwards
// during "sanitize" step
Fraction f(r.denominator(), tuplet->baseLen().fraction().denominator());
tuplet->setDuration(f.reduced());
if (bl != -1) { // obsolete, even in 1.3
TDuration d;
d.setVal(bl);
tuplet->setBaseLen(d);
d.setVal(bl * tuplet->ratio().denominator());
tuplet->setDuration(d.fraction());
}
}
//---------------------------------------------------------
// readChord
//---------------------------------------------------------
static void readChord(Chord* chord, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Note") {
Note* note = new Note(chord->score());
// the note needs to know the properties of the track it belongs to
note->setTrack(chord->track());
note->setChord(chord);
readNote(note, e);
chord->add(note);
}
else if (tag == "Attribute" || tag == "Articulation") {
Articulation* atr = new Articulation(chord->score());
atr->setTrack(chord->track());
readArticulation(atr, e);
chord->add(atr);
}
else if (chord->readProperties(e))
;
else
e.unknown();
}
}
//---------------------------------------------------------
// readRest
//---------------------------------------------------------
static void readRest(Rest* rest, XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Attribute" || tag == "Articulation") {
Articulation* atr = new Articulation(rest->score());
atr->setTrack(rest->track());
readArticulation(atr, e);
rest->add(atr);
}
else if (rest->readProperties(e))
;
else
e.unknown();
}
}
//---------------------------------------------------------
// readMeasure
//---------------------------------------------------------
static void readMeasure(Measure* m, int staffIdx, XmlReader& e)
{
Segment* segment = 0;
qreal _spatium = m->spatium();
QList<Chord*> graceNotes;
//sort tuplet elements. needed for nested tuplets #22537
for (Tuplet* t : e.tuplets())
t->sortElements();
e.tuplets().clear();
e.setTrack(staffIdx * VOICES);
m->createStaves(staffIdx);
// tick is obsolete
if (e.hasAttribute("tick"))
e.initTick(m->score()->fileDivision(e.intAttribute("tick")));
if (e.hasAttribute("len")) {
QStringList sl = e.attribute("len").split('/');
if (sl.size() == 2)
m->setLen(Fraction(sl[0].toInt(), sl[1].toInt()));
else
qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
m->score()->sigmap()->add(m->tick(), SigEvent(m->len(), m->timesig()));
m->score()->sigmap()->add(m->endTick(), SigEvent(m->timesig()));
}
Staff* staff = m->score()->staff(staffIdx);
Fraction timeStretch(staff->timeStretch(m->tick()));
// keep track of tick of previous element
// this allows markings that need to apply to previous element to do so
// even though we may have already advanced to next tick position
int lastTick = e.tick();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "move")
e.initTick(e.readFraction().ticks() + m->tick());
else if (tag == "tick") {
e.initTick(m->score()->fileDivision(e.readInt()));
lastTick = e.tick();
}
else if (tag == "BarLine") {
BarLine* barLine = new BarLine(m->score());
barLine->setTrack(e.track());
barLine->resetProperty(P_ID::BARLINE_SPAN);
barLine->resetProperty(P_ID::BARLINE_SPAN_FROM);
barLine->resetProperty(P_ID::BARLINE_SPAN_TO);
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "subtype") {
BarLineType t;
switch (e.readInt()) {
default:
case 0:
t = BarLineType::NORMAL;
break;
case 1:
t = BarLineType::DOUBLE;
break;
case 2:
t = BarLineType::START_REPEAT;
break;
case 3:
t = BarLineType::END_REPEAT;
break;
case 4:
t = BarLineType::BROKEN;
break;
case 5:
t = BarLineType::END;
break;
case 6:
t = BarLineType::END_START_REPEAT;
break;
}
barLine->setBarLineType(t);
}
else if (!barLine->Element::readProperties(e))
e.unknown();
}
//
// StartRepeatBarLine: always at the beginning tick of a measure, always BarLineType::START_REPEAT
// BarLine: in the middle of a measure, has no semantic
// EndBarLine: at the end tick of a measure
// BeginBarLine: first segment of a measure
Segment::Type st;
if ((e.tick() != m->tick()) && (e.tick() != m->endTick()))
st = Segment::Type::BarLine;
else if (barLine->barLineType() == BarLineType::START_REPEAT && e.tick() == m->tick())
st = Segment::Type::StartRepeatBarLine;
else if (e.tick() == m->tick() && segment == 0)
st = Segment::Type::BeginBarLine;
else
st = Segment::Type::EndBarLine;
segment = m->getSegment(st, e.tick());
segment->add(barLine);
}
else if (tag == "Chord") {
Chord* chord = new Chord(m->score());
chord->setTrack(e.track());
readChord(chord, e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
if (chord->noteType() != NoteType::NORMAL) {
graceNotes.push_back(chord);
if (chord->tremolo() && chord->tremolo()->tremoloType() < TremoloType::R8) {
// old style tremolo found
Tremolo* tremolo = chord->tremolo();
TremoloType st;
switch (tremolo->tremoloType()) {
default:
case TremoloType::OLD_R8: st = TremoloType::R8; break;
case TremoloType::OLD_R16: st = TremoloType::R16; break;
case TremoloType::OLD_R32: st = TremoloType::R32; break;
case TremoloType::OLD_C8: st = TremoloType::C8; break;
case TremoloType::OLD_C16: st = TremoloType::C16; break;
case TremoloType::OLD_C32: st = TremoloType::C32; break;
}
tremolo->setTremoloType(st);
}
}
else {
segment->add(chord);
Q_ASSERT(segment->segmentType() == Segment::Type::ChordRest);
for (int i = 0; i < graceNotes.size(); ++i) {
Chord* gc = graceNotes[i];
gc->setGraceIndex(i);
chord->add(gc);
}
graceNotes.clear();
int crticks = chord->actualTicks();
if (chord->tremolo() && chord->tremolo()->tremoloType() < TremoloType::R8) {
// old style tremolo found
Tremolo* tremolo = chord->tremolo();
TremoloType st;
switch (tremolo->tremoloType()) {
default:
case TremoloType::OLD_R8: st = TremoloType::R8; break;
case TremoloType::OLD_R16: st = TremoloType::R16; break;
case TremoloType::OLD_R32: st = TremoloType::R32; break;
case TremoloType::OLD_C8: st = TremoloType::C8; break;
case TremoloType::OLD_C16: st = TremoloType::C16; break;
case TremoloType::OLD_C32: st = TremoloType::C32; break;
}
tremolo->setTremoloType(st);
if (tremolo->twoNotes()) {
int track = chord->track();
Segment* ss = 0;
for (Segment* ps = m->first(Segment::Type::ChordRest); ps; ps = ps->next(Segment::Type::ChordRest)) {
if (ps->tick() >= e.tick())
break;
if (ps->element(track))
ss = ps;
}
Chord* pch = 0; // previous chord
if (ss) {
ChordRest* cr = static_cast<ChordRest*>(ss->element(track));
if (cr && cr->type() == ElementType::CHORD)
pch = static_cast<Chord*>(cr);
}
if (pch) {
tremolo->setParent(pch);
pch->setTremolo(tremolo);
chord->setTremolo(0);
// force duration to half
Fraction pts(timeStretch * pch->globalDuration());
int pcrticks = pts.ticks();
pch->setDuration(Fraction::fromTicks(pcrticks / 2));
chord->setDuration(Fraction::fromTicks(crticks / 2));
}
else {
qDebug("tremolo: first note not found");
}
crticks /= 2;
}
else {
tremolo->setParent(chord);
}
}
lastTick = e.tick();
e.incTick(crticks);
}
}
else if (tag == "Rest") {
Rest* rest = new Rest(m->score());
rest->setDurationType(TDuration::DurationType::V_MEASURE);
rest->setDuration(m->timesig()/timeStretch);
rest->setTrack(e.track());
readRest(rest, e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(rest);
if (!rest->duration().isValid()) // hack
rest->setDuration(m->timesig()/timeStretch);
lastTick = e.tick();
e.incTick(rest->actualTicks());
}
else if (tag == "Breath") {
Breath* breath = new Breath(m->score());
breath->setTrack(e.track());
int tick = e.tick();
breath->read(e);
// older scores placed the breath segment right after the chord to which it applies
// rather than before the next chordrest segment with an element for the staff
// result would be layout too far left if there are other segments due to notes in other staves
// we need to find tick of chord to which this applies, and add its duration
int prevTick;
if (e.tick() < tick)
prevTick = e.tick(); // use our own tick if we explicitly reset to earlier position
else
prevTick = lastTick; // otherwise use tick of previous tick/chord/rest tag
// find segment
Segment* prev = m->findSegment(Segment::Type::ChordRest, prevTick);
if (prev) {
// find chordrest
ChordRest* lastCR = static_cast<ChordRest*>(prev->element(e.track()));
if (lastCR)
tick = prevTick + lastCR->actualTicks();
}
segment = m->getSegment(Segment::Type::Breath, tick);
segment->add(breath);
}
else if (tag == "endSpanner") {
int id = e.attribute("id").toInt();
Spanner* spanner = e.findSpanner(id);
if (spanner) {
spanner->setTicks(e.tick() - spanner->tick());
// if (spanner->track2() == -1)
// the absence of a track tag [?] means the
// track is the same as the beginning of the slur
if (spanner->track2() == -1)
spanner->setTrack2(spanner->track() ? spanner->track() : e.track());
}
else {
// remember "endSpanner" values
SpannerValues sv;
sv.spannerId = id;
sv.track2 = e.track();
sv.tick2 = e.tick();
e.addSpannerValues(sv);
}
e.readNext();
}
else if (tag == "Slur") {
Slur *sl = new Slur(m->score());
sl->setTick(e.tick());
sl->read(e);
//
// check if we already saw "endSpanner"
//
int id = e.spannerId(sl);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sl->setTick2(sv->tick2);
sl->setTrack2(sv->track2);
}
m->score()->addSpanner(sl);
}
else if (tag == "HairPin"
|| tag == "Pedal"
|| tag == "Ottava"
|| tag == "Trill"
|| tag == "TextLine"
|| tag == "Volta") {
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, m->score()));
sp->setTrack(e.track());
sp->setTick(e.tick());
// ?? sp->setAnchor(Spanner::Anchor::SEGMENT);
sp->read(e);
m->score()->addSpanner(sp);
//
// check if we already saw "endSpanner"
//
int id = e.spannerId(sp);
const SpannerValues* sv = e.spannerValues(id);
if (sv) {
sp->setTicks(sv->tick2 - sp->tick());
sp->setTrack2(sv->track2);
}
}
else if (tag == "RepeatMeasure") {
RepeatMeasure* rm = new RepeatMeasure(m->score());
rm->setTrack(e.track());
rm->read(e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(rm);
if (rm->actualDuration().isZero()) { // might happen with 1.3 scores
rm->setDuration(m->len());
}
lastTick = e.tick();
e.incTick(m->ticks());
}
else if (tag == "Clef") {
Clef* clef = new Clef(m->score());
clef->setTrack(e.track());
readClef(clef, e);
if (m->score()->mscVersion() < 113)
clef->setUserOff(QPointF());
clef->setGenerated(false);
// MS3 doesn't support wrong clef for staff type: Default to G
bool isDrumStaff = staff->isDrumStaff(e.tick());
if (clef->clefType() == ClefType::TAB
|| (clef->clefType() == ClefType::PERC && !isDrumStaff)
|| (clef->clefType() != ClefType::PERC && isDrumStaff)) {
clef->setClefType(ClefType::G);
staff->clefList().erase(e.tick());
staff->clefList().insert(std::pair<int,ClefType>(e.tick(), ClefType::G));
}
// there may be more than one clef segment for same tick position
// the first clef may be missing and is added later in layout
bool header;
if (e.tick() != m->tick())
header = false;
else if (!segment)
header = true;
else {
header = true;
for (Segment* s = m->segments().first(); s && !s->rtick(); s = s->next()) {
if (s->isKeySigType() || s->isTimeSigType()) {
// hack: there may be other segment types which should
// generate a clef at current position
header = false;
break;
}
}
}
segment = m->getSegment(header ? Segment::Type::HeaderClef : Segment::Type::Clef, e.tick());
segment->add(clef);
}
else if (tag == "TimeSig") {
TimeSig* ts = new TimeSig(m->score());
ts->setTrack(e.track());
ts->read(e);
// if time sig not at begining of measure => courtesy time sig
int currTick = e.tick();
bool courtesySig = (currTick > m->tick());
if (courtesySig) {
// if courtesy sig., just add it without map processing
segment = m->getSegment(Segment::Type::TimeSigAnnounce, currTick);
segment->add(ts);
}
else {
// if 'real' time sig., do full process
segment = m->getSegment(Segment::Type::TimeSig, currTick);
segment->add(ts);
timeStretch = ts->stretch().reduced();
m->setTimesig(ts->sig() / timeStretch);
}
}
else if (tag == "KeySig") {
KeySig* ks = new KeySig(m->score());
ks->setTrack(e.track());
ks->read(e);
int curTick = e.tick();
if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick == 0) {
// ignore empty key signature
qDebug("remove keysig c at tick 0");
if (ks->links()) {
if (ks->links()->size() == 1)
e.linkIds().remove(ks->links()->lid());
}
}
else {
// if key sig not at beginning of measure => courtesy key sig
bool courtesySig = (curTick == m->endTick());
segment = m->getSegment(courtesySig ? Segment::Type::KeySigAnnounce : Segment::Type::KeySig, curTick);
segment->add(ks);
if (!courtesySig)
staff->setKey(curTick, ks->keySigEvent());
}
}
else if (tag == "Lyrics") {
Element* element = Element::name2Element(tag, m->score());
element->setTrack(e.track());
element->read(e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
ChordRest* cr = static_cast<ChordRest*>(segment->element(element->track()));
if (!cr)
cr = static_cast<ChordRest*>(segment->element(e.track())); // in case lyric itself has bad track info
if (!cr)
qDebug("Internal error: no chord/rest for lyrics");
else
cr->add(element);
}
else if (tag == "Text") {
Text* t = new StaffText(m->score());
t->setTrack(e.track());
t->read(e);
if (t->empty()) {
qDebug("reading empty text: deleted");
delete t;
}
else {
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(t);
}
}
else if (tag == "Dynamic") {
Dynamic* dyn = new Dynamic(m->score());
dyn->setTrack(e.track());
dyn->read(e);
dyn->setDynamicType(dyn->xmlText());
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(dyn);
}
else if (tag == "Harmony"
|| tag == "FretDiagram"
|| tag == "TremoloBar"
|| tag == "Symbol"
|| tag == "Tempo"
|| tag == "StaffText"
|| tag == "RehearsalMark"
|| tag == "InstrumentChange"
|| tag == "StaffState"
|| tag == "FiguredBass"
) {
Element* el = Element::name2Element(tag, m->score());
// hack - needed because tick tags are unreliable in 1.3 scores
// for symbols attached to anything but a measure
if (el->type() == ElementType::SYMBOL)
el->setParent(m); // this will get reset when adding to segment
el->setTrack(e.track());
el->read(e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(el);
}
else if (tag == "Marker" || tag == "Jump") {
Element* el = Element::name2Element(tag, m->score());
el->setTrack(e.track());
el->read(e);
if (el->isMarker()) {
Marker* m = toMarker(el);
if (m->markerType() == Marker::Type::SEGNO || m->markerType() == Marker::Type::CODA ||
m->markerType() == Marker::Type::VARCODA || m->markerType() == Marker::Type::CODETTA) {
// force the marker type for correct display
m->setXmlText("");
m->setMarkerType(m->markerType());
m->setSubStyle(SubStyle::REPEAT_LEFT);
}
}
m->add(el);
}
else if (tag == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Element* el = Element::name2Element(tag, m->score());
el->setTrack(e.track());
el->read(e);
segment = m->getSegment(Segment::Type::ChordRest, e.tick());
segment->add(el);
}
}
else if (tag == "stretch") {
double val = e.readDouble();
if (val < 0.0)
val = 0;
m->setUserStretch(val);
}
else if (tag == "noOffset")
m->setNoOffset(e.readInt());
else if (tag == "measureNumberMode")
m->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
else if (tag == "irregular")
m->setIrregular(e.readBool());
else if (tag == "breakMultiMeasureRest")
m->setBreakMultiMeasureRest(e.readBool());
else if (tag == "sysInitBarLineType") {
const QString& val(e.readElementText());
BarLine* barLine = new BarLine(m->score());
barLine->setTrack(e.track());
barLine->setBarLineType(val);
segment = m->getSegment(Segment::Type::BeginBarLine, m->tick());
segment->add(barLine);
}
else if (tag == "Tuplet") {
Tuplet* tuplet = new Tuplet(m->score());
tuplet->setTrack(e.track());
tuplet->setTick(e.tick());
tuplet->setParent(m);
readTuplet(tuplet, e);
e.addTuplet(tuplet);
}
else if (tag == "startRepeat") {
m->setRepeatStart(true);
e.readNext();
}
else if (tag == "endRepeat") {
m->setRepeatCount(e.readInt());
m->setRepeatEnd(true);
}
else if (tag == "vspacer" || tag == "vspacerDown") {
if (!m->vspacerDown(staffIdx)) {
Spacer* spacer = new Spacer(m->score());
spacer->setSpacerType(SpacerType::DOWN);
spacer->setTrack(staffIdx * VOICES);
m->add(spacer);
}
m->vspacerDown(staffIdx)->setGap(e.readDouble() * _spatium);
}
else if (tag == "vspacer" || tag == "vspacerUp") {
if (!m->vspacerUp(staffIdx)) {
Spacer* spacer = new Spacer(m->score());
spacer->setSpacerType(SpacerType::UP);
spacer->setTrack(staffIdx * VOICES);
m->add(spacer);
}
m->vspacerUp(staffIdx)->setGap(e.readDouble() * _spatium);
}
else if (tag == "visible")
m->setStaffVisible(staffIdx, e.readInt());
else if (tag == "slashStyle")
m->setStaffSlashStyle(staffIdx, e.readInt());
else if (tag == "Beam") {
Beam* beam = new Beam(m->score());
beam->setTrack(e.track());
beam->read(e);
beam->setParent(0);
e.addBeam(beam);
}
else if (tag == "Segment")
segment->read(e);
else if (tag == "MeasureNumber") {
Text* noText = new Text(SubStyle::MEASURE_NUMBER, m->score());
noText->read(e);
noText->setFlag(ElementFlag::ON_STAFF, true);
// noText->setFlag(ElementFlag::MOVABLE, false); ??
noText->setTrack(e.track());
noText->setParent(m);
m->setNoText(noText->staffIdx(), noText);
}
else if (tag == "multiMeasureRest") {
m->setMMRestCount(e.readInt());
// set tick to previous measure
m->setTick(e.lastMeasure()->tick());
e.initTick(e.lastMeasure()->tick());
}
else if (m->MeasureBase::readProperties(e))
;
else
e.unknown();
}
// For nested tuplets created with MuseScore 1.3 tuplet dialog (i.e. "Other..." dialog),
// the parent tuplet was not set. Try to infere if the tuplet was actually a nested tuplet
for (Tuplet* tuplet : e.tuplets()) {
int tupletTick = tuplet->tick();
int tupletDuration = tuplet->actualTicks() - 1;
std::vector<DurationElement*> tElements = tuplet->elements();
for (Tuplet* tuplet2 : e.tuplets()) {
if ((tuplet2->tuplet()) || (tuplet2->voice() != tuplet->voice())) // already a nested tuplet or in a different voice
continue;
int possibleDuration = tuplet2->duration().ticks() * tuplet->ratio().denominator() / tuplet->ratio().numerator() - 1;
if ((tuplet2 != tuplet) && (tuplet2->tick() >= tupletTick) && (tuplet2->tick() < tupletTick + tupletDuration) && (tuplet2->tick() + possibleDuration < tupletTick + tupletDuration)) {
bool found = false;
for (DurationElement* de : tElements) {
if (de == tuplet2)
found = true;
}
if (!found) {
qDebug("Adding tuplet %p as nested tuplet to tuplet %p",tuplet2,tuplet);
tuplet2->setTuplet(tuplet);
tuplet->add(tuplet2);
}
}
}
}
e.checkTuplets();
}
//---------------------------------------------------------
// readStaffContent
//---------------------------------------------------------
static void readStaffContent(Score* score, XmlReader& e)
{
int staff = e.intAttribute("id", 1) - 1;
e.initTick(0);
e.setTrack(staff * VOICES);
Measure* measure = score->firstMeasure();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Measure") {
if (staff == 0) {
measure = new Measure(score);
measure->setTick(e.tick());
const SigEvent& ev = score->sigmap()->timesig(measure->tick());
measure->setLen(ev.timesig());
measure->setTimesig(ev.nominal());
readMeasure(measure, staff, e);
measure->checkMeasure(staff);
if (!measure->isMMRest()) {
score->measures()->add(measure);
e.setLastMeasure(measure);
e.initTick(measure->tick() + measure->ticks());
}
else {
// this is a multi measure rest
// always preceded by the first measure it replaces
Measure* m = e.lastMeasure();
if (m) {
m->setMMRest(measure);
measure->setTick(m->tick());
}
}
}
else {
if (measure == 0) {
qDebug("Score::readStaff(): missing measure!");
measure = new Measure(score);
measure->setTick(e.tick());
score->measures()->add(measure);
}
e.initTick(measure->tick());
readMeasure(measure, staff, e);
measure->checkMeasure(staff);
if (measure->isMMRest())
measure = e.lastMeasure()->nextMeasure();
else {
e.setLastMeasure(measure);
if (measure->mmRest())
measure = measure->mmRest();
else
measure = measure->nextMeasure();
}
}
}
else if (tag == "HBox" || tag == "VBox" || tag == "TBox" || tag == "FBox") {
MeasureBase* mb = static_cast<MeasureBase*>(Element::name2Element(tag, score));
mb->read(e);
mb->setTick(e.tick());
score->measures()->add(mb);
}
else if (tag == "tick")
e.initTick(score->fileDivision(e.readInt()));
else
e.unknown();
}
}
//---------------------------------------------------------
// readStaff
//---------------------------------------------------------
static void readStaff(Staff* staff, XmlReader& e)
{
Score* _score = staff->score();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "lines") {
int lines = e.readInt();
staff->setLines(0, lines);
if (lines != 5) {
staff->setBarLineFrom(lines == 1 ? BARLINE_SPAN_1LINESTAFF_FROM : 0);
staff->setBarLineTo(lines == 1 ? BARLINE_SPAN_1LINESTAFF_TO : (lines - 1) * 2);
}
}
else if (tag == "small")
staff->setSmall(0, e.readInt());
else if (tag == "invisible")
staff->setInvisible(e.readInt());
else if (tag == "slashStyle")
e.skipCurrentElement();
else if (tag == "cleflist") {
// QList<std::pair<int, ClefType>>& cl = e.clefs(idx());
staff->clefList().clear();
while (e.readNextStartElement()) {
if (e.name() == "clef") {
int tick = e.intAttribute("tick", 0);
ClefType ct = readClefType(e.attribute("idx", "0"));
staff->clefList().insert(std::pair<int,ClefType>(_score->fileDivision(tick), ct));
e.readNext();
}
else
e.unknown();
}
if (staff->clefList().empty())
staff->clefList().insert(std::pair<int,ClefType>(0, ClefType::G));
}
else if (tag == "keylist")
staff->keyList()->read(e, _score);
else if (tag == "bracket") {
BracketItem b;
b._bracket = BracketType(e.intAttribute("type", -1));
b._bracketSpan = e.intAttribute("span", 0);
staff->brackets().push_back(b);
e.readNext();
}
else if (tag == "barLineSpan")
staff->setBarLineSpan(e.readInt());
else
e.unknown();
}
}
//---------------------------------------------------------
// readDrumset
//---------------------------------------------------------
static void readDrumset(Drumset* ds, XmlReader& e)
{
int pitch = e.intAttribute("pitch", -1);
if (pitch < 0 || pitch > 127) {
qDebug("load drumset: invalid pitch %d", pitch);
return;
}
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "head")
ds->drum(pitch).notehead = convertHeadGroup(e.readInt());
else if (ds->readProperties(e, pitch))
;
else
e.unknown();
}
}
//---------------------------------------------------------
// readInstrument
//---------------------------------------------------------
static void readInstrument(Instrument *i, Part* p, XmlReader& e)
{
int program = -1;
int bank = 0;
int chorus = 30;
int reverb = 30;
int volume = 100;
int pan = 60;
bool customDrumset = false;
i->clearChannels(); // remove default channel
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "chorus")
chorus = e.readInt();
else if (tag == "reverb")
reverb = e.readInt();
else if (tag == "midiProgram")
program = e.readInt();
else if (tag == "volume")
volume = e.readInt();
else if (tag == "pan")
pan = e.readInt();
else if (tag == "midiChannel")
e.skipCurrentElement();
else if (tag == "Drum") {
// if we see one of this tags, a custom drumset will
// be created
if (!i->drumset())
i->setDrumset(new Drumset(*smDrumset));
if (!customDrumset) {
i->drumset()->clear();
customDrumset = true;
}
readDrumset(i->drumset(), e);
}
else if (i->readProperties(e, p, &customDrumset))
;
else
e.unknown();
}
if (i->channel().empty()) { // for backward compatibility
Channel* a = new Channel;
a->chorus = chorus;
a->reverb = reverb;
a->name = "normal";
a->program = program;
a->bank = bank;
a->volume = volume;
a->pan = pan;
i->appendChannel(a);
}
if (i->useDrumset()) {
if (i->channel()[0]->bank == 0)
i->channel()[0]->bank = 128;
i->channel()[0]->updateInitList();
}
}
//---------------------------------------------------------
// readPart
//---------------------------------------------------------
static void readPart(Part* part, XmlReader& e)
{
Score* _score = part->score();
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Staff") {
Staff* staff = new Staff(_score);
staff->setPart(part);
_score->staves().push_back(staff);
part->staves()->push_back(staff);
readStaff(staff, e);
}
else if (tag == "Instrument") {
Instrument* i = part->instrument();
readInstrument(i, part, e);
// add string data from MIDI program number, if possible
if (i->stringData()->strings() == 0
&& i->channel().count() > 0
&& i->drumset() == nullptr) {
int program = i->channel(0)->program;
if (program >= 24 && program <= 30) // guitars
i->setStringData(StringData(19, 6, g_guitarStrings));
else if ( (program >= 32 && program <= 39) || program == 43) // bass / double-bass
i->setStringData(StringData(24, 4, g_bassStrings));
else if (program == 40) // violin and other treble string instr.
i->setStringData(StringData(24, 4, g_violinStrings));
else if (program == 41) // viola and other alto string instr.
i->setStringData(StringData(24, 4, g_violaStrings));
else if (program == 42) // cello and other bass string instr.
i->setStringData(StringData(24, 4, g_celloStrings));
}
Drumset* d = i->drumset();
Staff* st = part->staff(0);
if (d && st && st->lines(0) != 5) {
int n = 0;
if (st->lines(0) == 1)
n = 4;
for (int i = 0; i < DRUM_INSTRUMENTS; ++i)
d->drum(i).line -= n;
}
}
else if (tag == "name") {
Text* t = new Text(_score);
t->read(e);
part->instrument()->setLongName(t->xmlText());
delete t;
}
else if (tag == "shortName") {
Text* t = new Text(_score);
t->read(e);
part->instrument()->setShortName(t->xmlText());
delete t;
}
else if (tag == "trackName")
part->setPartName(e.readElementText());
else if (tag == "show")
part->setShow(e.readInt());
else
e.unknown();
}
if (part->partName().isEmpty())
part->setPartName(part->instrument()->trackName());
if (part->instrument()->useDrumset()) {
for (Staff* staff : *part->staves()) {
int lines = staff->lines(0);
int bf = staff->barLineFrom();
int bt = staff->barLineTo();
staff->setStaffType(0, StaffType::getDefaultPreset(StaffGroup::PERCUSSION));
// this allows 2/3-line percussion staves to keep the double spacing they had in 1.3
if (lines == 2 || lines == 3)
staff->staffType(0)->setLineDistance(Spatium(2.0));
staff->setLines(0, lines); // this also sets stepOffset
staff->setBarLineFrom(bf);
staff->setBarLineTo(bt);
}
}
//set default articulations
QList<MidiArticulation> articulations;
articulations.append(MidiArticulation("", "", 100, 100));
articulations.append(MidiArticulation("staccato", "", 100, 50));
articulations.append(MidiArticulation("tenuto", "", 100, 100));
articulations.append(MidiArticulation("sforzato", "", 120, 100));
part->instrument()->setArticulation(articulations);
}
//---------------------------------------------------------
// convertOldTextStyleNames
//---------------------------------------------------------
static QString convertOldTextStyleNames(const QString& s)
{
QString rs(s);
// convert 1.2 text styles
if (s == "Chordname")
rs = "Chord Symbol";
else if (s == "Lyrics odd lines")
rs = "Lyrics Odd Lines";
else if (s == "Lyrics even lines")
rs = "Lyrics Even Lines";
else if (s == "InstrumentsLong")
rs = "Instrument Name (Long)";
else if (s == "InstrumentsShort")
rs = "Instrument Name (Short)";
else if (s == "InstrumentsExcerpt")
rs = "Instrument Name (Part)";
else if (s == "Poet")
rs = "Lyricist";
else if (s == "Technik")
rs = "Technique";
else if (s == "TextLine")
rs = "Text Line";
else if (s == "Tuplets")
rs = "Tuplet";
else if (s == "Dynamics2")
rs = "Dynamics";
else if (s == "Repeat Text")
rs = "Repeat Text Right";
return rs;
}
#if 0
//---------------------------------------------------------
// readPageFormat
//---------------------------------------------------------
static void readPageFormat(PageFormat* pf, XmlReader& e)
{
qreal _oddRightMargin = 0.0;
qreal _evenRightMargin = 0.0;
bool landscape = false;
QString type;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "landscape")
landscape = e.readInt();
else if (tag == "page-margins") {
type = e.attribute("type","both");
qreal lm = 0.0, rm = 0.0, tm = 0.0, bm = 0.0;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
qreal val = e.readDouble() * 0.5 / PPI;
if (tag == "left-margin")
lm = val;
else if (tag == "right-margin")
rm = val;
else if (tag == "top-margin")
tm = val;
else if (tag == "bottom-margin")
bm = val;
else
e.unknown();
}
pf->setTwosided(type == "odd" || type == "even");
if (type == "odd" || type == "both") {
pf->setOddLeftMargin(lm);
_oddRightMargin = rm;
pf->setOddTopMargin(tm);
pf->setOddBottomMargin(bm);
}
if (type == "even" || type == "both") {
pf->setEvenLeftMargin(lm);
_evenRightMargin = rm;
pf->setEvenTopMargin(tm);
pf->setEvenBottomMargin(bm);
}
}
else if (tag == "page-height")
pf->setSize(QSizeF(pf->size().width(), e.readDouble() * 0.5 / PPI));
else if (tag == "page-width")
pf->setSize(QSizeF(e.readDouble() * 0.5 / PPI, pf->size().height()));
else if (tag == "pageFormat") {
const PaperSize* s = getPaperSize114(e.readElementText());
pf->setSize(QSizeF(s->w, s->h));
}
else if (tag == "page-offset") {
e.readInt();
}
else
e.unknown();
}
if (landscape)
pf->setSize(pf->size().transposed());
qreal w1 = pf->size().width() - pf->oddLeftMargin() - _oddRightMargin;
qreal w2 = pf->size().width() - pf->evenLeftMargin() - _evenRightMargin;
pf->setPrintableWidth(qMin(w1, w2)); // silently adjust right margins
}
#endif
//---------------------------------------------------------
// readStyle
//---------------------------------------------------------
static void readStyle(MStyle* style, XmlReader& e)
{
QString oldChordDescriptionFile = style->value(StyleIdx::chordDescriptionFile).toString();
bool chordListTag = false;
while (e.readNextStartElement()) {
QString tag = e.name().toString();
if (tag == "lyricsDistance") // was renamed
tag = "lyricsPosBelow";
if (tag == "TextStyle") {
// TextStyle s;
//TODO s.read(e);
// style->setTextStyle(s);
e.skipCurrentElement();
}
else if (tag == "Spatium")
style->set(StyleIdx::spatium, e.readDouble() * DPMM);
else if (tag == "page-layout") {
PageFormat pf;
initPageFormat(style, &pf);
pf.read(e);
setPageFormat(style, pf);
}
else if (tag == "displayInConcertPitch")
style->set(StyleIdx::concertPitch, QVariant(bool(e.readInt())));
else if (tag == "ChordList") {
style->chordList()->clear();
style->chordList()->read(e);
for (ChordFont f : style->chordList()->fonts) {
if (f.family == "MuseJazz") {
f.family = "MuseJazz Text";
}
}
style->setCustomChordList(true);
chordListTag = true;
}
else if (tag == "pageFillLimit" || tag == "genTimesig" || tag == "FixMeasureNumbers" || tag == "FixMeasureWidth") // obsolete
e.skipCurrentElement();
else if (tag == "systemDistance") // obsolete
style->set(StyleIdx::minSystemDistance, QVariant(e.readDouble()));
else {
if (tag == "stemDir") {
int voice = e.attribute("voice", "1").toInt() - 1;
switch(voice) {
case 0: tag = "StemDir1"; break;
case 1: tag = "StemDir2"; break;
case 2: tag = "StemDir3"; break;
case 3: tag = "StemDir4"; break;
}
}
// for compatibility:
if (tag == "oddHeader" || tag == "evenHeader" || tag == "oddFooter" || tag == "evenFooter")
tag += "C";
#if 0 // TODO-ws
int idx2;
for (idx2 = 0; idx2 < int(ArticulationType::ARTICULATIONS); ++idx2) {
ArticulationInfo& ai = Articulation::articulationList[idx2];
// deal with obsolete tags from 1.14 format
if (tag == "SforzatoaccentAnchor")
tag = "SforzatoAnchor";
if (tag == "SnappizzicatorAnchor")
tag = "SnappizzicatoAnchor";
else if (tag == "EspressivoAnchor")
continue;
if (QString::compare(tag, QString(ai.name).append("Anchor"), Qt::CaseInsensitive) == 0
|| QString::compare(tag, QString("U").append(ai.name).append("Anchor"), Qt::CaseInsensitive) == 0
|| QString::compare(tag, QString("D").append(ai.name).append("Anchor"), Qt::CaseInsensitive) == 0
) {
StyleIdx si = MStyle::styleIdx(QString(ai.name).append("Anchor"));
if (si != StyleIdx::NOSTYLE) {
QString val(e.readElementText());
style->set(si, val.toInt());
break;
}
}
}
if (idx2 < int(ArticulationType::ARTICULATIONS))
continue;
#endif
QString val(e.readElementText());
//TODO style->convertToUnit(tag, val);
}
}
// if we just specified a new chord description file
// and didn't encounter a ChordList tag
// then load the chord description file
QString newChordDescriptionFile = style->value(StyleIdx::chordDescriptionFile).toString();
if (newChordDescriptionFile != oldChordDescriptionFile && !chordListTag) {
if (!newChordDescriptionFile.startsWith("chords_") && style->value(StyleIdx::chordStyle).toString() == "std") {
// should not normally happen,
// but treat as "old" (114) score just in case
style->set(StyleIdx::chordStyle, QVariant(QString("custom")));
style->set(StyleIdx::chordsXmlFile, QVariant(true));
qDebug("StyleData::load: custom chord description file %s with chordStyle == std", qPrintable(newChordDescriptionFile));
}
if (style->value(StyleIdx::chordStyle).toString() == "custom")
style->setCustomChordList(true);
else
style->setCustomChordList(false);
style->chordList()->unload();
}
// make sure we have a chordlist
if (!style->chordList()->loaded() && !chordListTag) {
if (style->value(StyleIdx::chordsXmlFile).toBool())
style->chordList()->read("chords.xml");
style->chordList()->read(newChordDescriptionFile);
}
#if 0 // TODO
//
// Compatibility with old scores/styles:
// translate old frameWidthMM and paddingWidthMM
// into spatium units
//
int n = style->textStyles().size();
qreal _spatium = style->value(StyleIdx::spatium).toDouble();
qreal spMM = _spatium / DPMM;
for (int i = 0; i < n; ++i) {
TextStyle* s = &style->textStyle(StyledPropertyListIdx(i));
if (s->frameWidthMM() != 0.0)
s->setFrameWidth(Spatium(s->frameWidthMM() / spMM));
if (s->paddingWidthMM() != 0.0)
s->setPaddingWidth(Spatium(s->paddingWidthMM() / spMM));
}
#endif
}
//---------------------------------------------------------
// read114
// import old version <= 1.3 files
//---------------------------------------------------------
Score::FileError MasterScore::read114(XmlReader& e)
{
#if 0
for (unsigned int i = 0; i < sizeof(style114)/sizeof(*style114); ++i)
style().set(style114[i].idx, style114[i].val);
// old text style defaults
TextStyle ts = style().textStyle("Chord Symbol");
ts.setYoff(-4.0);
style().setTextStyle(ts);
ts = style().textStyle("Rehearsal Mark");
ts.setSquare(false);
ts.setFrameRound(20);
style().setTextStyle(ts);
ts = style().textStyle("Dynamics");
ts.setItalic(false);
style().setTextStyle(ts);
#endif
TempoMap tm;
while (e.readNextStartElement()) {
e.setTrack(-1);
const QStringRef& tag(e.name());
if (tag == "Staff")
readStaffContent(this, e);
else if (tag == "KeySig") { // not supported
KeySig* ks = new KeySig(this);
ks->read(e);
delete ks;
}
else if (tag == "siglist")
_sigmap->read(e, _fileDivision);
else if (tag == "programVersion") {
setMscoreVersion(e.readElementText());
parseVersion(mscoreVersion());
}
else if (tag == "programRevision")
setMscoreRevision(e.readInt());
else if (tag == "Mag"
|| tag == "MagIdx"
|| tag == "xoff"
|| tag == "Symbols"
|| tag == "cursorTrack"
|| tag == "yoff")
e.skipCurrentElement(); // obsolete
else if (tag == "tempolist") {
// store the tempo list to create invisible tempo text later
qreal tempo = e.attribute("fix","2.0").toDouble();
tm.setRelTempo(tempo);
while (e.readNextStartElement()) {
if (e.name() == "tempo") {
int tick = e.attribute("tick").toInt();
double tmp = e.readElementText().toDouble();
tick = (tick * MScore::division + _fileDivision/2) / _fileDivision;
auto pos = tm.find(tick);
if (pos != tm.end())
tm.erase(pos);
tm.setTempo(tick, tmp);
}
else if (e.name() == "relTempo")
e.readElementText();
else
e.unknown();
}
}
else if (tag == "playMode")
setPlayMode(PlayMode(e.readInt()));
else if (tag == "SyntiSettings")
_synthesizerState.read(e);
else if (tag == "Spatium")
setSpatium (e.readDouble() * DPMM);
else if (tag == "Division")
_fileDivision = e.readInt();
else if (tag == "showInvisible")
setShowInvisible(e.readInt());
else if (tag == "showFrames")
setShowFrames(e.readInt());
else if (tag == "showMargins")
setShowPageborders(e.readInt());
else if (tag == "Style") {
qreal sp = spatium();
readStyle(&style(), e);
//style()->load(e);
// adjust this now so chords render properly on read
// other style adjustments can wait until reading is finished
if (styleB(StyleIdx::useGermanNoteNames))
style().set(StyleIdx::useStandardNoteNames, false);
if (_layoutMode == LayoutMode::FLOAT) {
// style should not change spatium in
// float mode
setSpatium(sp);
}
}
else if (tag == "TextStyle") {
e.skipCurrentElement();
#if 0 // TODO
TextStyle s;
s.read(e);
qreal spMM = spatium() / DPMM;
if (s.frameWidthMM() != 0.0)
s.setFrameWidth(Spatium(s.frameWidthMM() / spMM));
if (s.paddingWidthMM() != 0.0)
s.setPaddingWidth(Spatium(s.paddingWidthMM() / spMM));
// convert 1.2 text styles
s.setName(convertOldTextStyleNames(s.name()));
if (s.family() == "MuseJazz")
s.setFamily("MuseJazz Text");
if (s.name() == "Lyrics Odd Lines" || s.name() == "Lyrics Even Lines")
s.setAlign(Align(int(s.align()) & int(~Align::VMASK)) | Align::BASELINE);
style().setTextStyle(s);
#endif
}
else if (tag == "page-layout") {
if (_layoutMode != LayoutMode::FLOAT && _layoutMode != LayoutMode::SYSTEM) {
PageFormat pf;
initPageFormat(&style(), &pf);
pf.read(e);
setPageFormat(&style(), pf);
}
else
e.skipCurrentElement();
}
else if (tag == "copyright" || tag == "rights") {
Text* text = new Text(this);
text->read(e);
text->layout();
setMetaTag("copyright", text->plainText());
delete text;
}
else if (tag == "movement-number")
setMetaTag("movementNumber", e.readElementText());
else if (tag == "movement-title")
setMetaTag("movementTitle", e.readElementText());
else if (tag == "work-number")
setMetaTag("workNumber", e.readElementText());
else if (tag == "work-title")
setMetaTag("workTitle", e.readElementText());
else if (tag == "source")
setMetaTag("source", e.readElementText());
else if (tag == "metaTag") {
QString name = e.attribute("name");
setMetaTag(name, e.readElementText());
}
else if (tag == "Part") {
Part* part = new Part(this);
readPart(part, e);
parts().push_back(part);
}
else if (tag == "Slur") {
Slur* slur = new Slur(this);
slur->read(e);
addSpanner(slur);
}
else if ((tag == "HairPin")
|| (tag == "Ottava")
|| (tag == "TextLine")
|| (tag == "Volta")
|| (tag == "Trill")
|| (tag == "Pedal")) {
Spanner* s = static_cast<Spanner*>(Element::name2Element(tag, this));
s->read(e);
if (s->track() == -1)
s->setTrack(e.track());
else
e.setTrack(s->track()); // update current track
if (s->tick() == -1)
s->setTick(e.tick());
else
e.initTick(s->tick()); // update current tick
if (s->track2() == -1)
s->setTrack2(s->track());
if (s->ticks() == 0) {
delete s;
qDebug("zero spanner %s ticks: %d", s->name(), s->ticks());
}
else {
addSpanner(s);
}
}
else if (tag == "Excerpt") {
if (MScore::noExcerpts)
e.skipCurrentElement();
else {
Excerpt* ex = new Excerpt(this);
ex->read(e);
_excerpts.append(ex);
}
}
else if (tag == "Beam") {
Beam* beam = new Beam(this);
beam->read(e);
beam->setParent(0);
// _beams.append(beam);
}
else if (tag == "name")
setName(e.readElementText());
else
e.unknown();
}
if (e.error() != QXmlStreamReader::NoError)
return FileError::FILE_BAD_FORMAT;
for (Staff* s : staves()) {
int idx = s->idx();
int track = idx * VOICES;
// check barLineSpan
if (s->barLineSpan() > (nstaves() - idx)) {
qDebug("read114: invalid barline span %d (max %d)",
s->barLineSpan(), nstaves() - idx);
s->setBarLineSpan(nstaves() - idx);
}
for (auto i : s->clefList()) {
int tick = i.first;
ClefType clefId = i.second._concertClef;
Measure* m = tick2measure(tick);
if (!m)
continue;
Segment::Type st = Segment::Type::Clef;
if (tick == m->tick()) {
if (m->prevMeasure())
m = m->prevMeasure();
else
st = Segment::Type::HeaderClef;
}
Segment* seg = m->getSegment(st, tick);
if (seg->element(track))
seg->element(track)->setGenerated(false);
else {
Clef* clef = new Clef(this);
clef->setClefType(clefId);
clef->setTrack(track);
clef->setParent(seg);
clef->setGenerated(false);
seg->add(clef);
}
}
// create missing KeySig
KeyList* km = s->keyList();
for (auto i = km->begin(); i != km->end(); ++i) {
int tick = i->first;
if (tick < 0) {
qDebug("read114: Key tick %d", tick);
continue;
}
if (tick == 0 && i->second.key() == Key::C)
continue;
Measure* m = tick2measure(tick);
if (!m) //empty score
break;
Segment* seg = m->getSegment(Segment::Type::KeySig, tick);
if (seg->element(track))
toKeySig(seg->element(track))->setGenerated(false);
else {
KeySigEvent ke = i->second;
KeySig* ks = new KeySig(this);
ks->setKeySigEvent(ke);
ks->setParent(seg);
ks->setTrack(track);
ks->setGenerated(false);
seg->add(ks);
}
}
}
for (std::pair<int,Spanner*> p : spanner()) {
Spanner* s = p.second;
if (!s->isSlur()) {
if (s->isVolta()) {
Volta* volta = toVolta(s);
volta->setAnchor(Spanner::Anchor::MEASURE);
}
}
if (s->isOttava() || s->isPedal() || s->isTrill() || s->isTextLine()) {
qreal yo = 0;
if (s->isOttava()) {
// fix ottava position
yo = styleP(StyleIdx::ottavaPosAbove);
if (s->placeBelow())
yo = -yo + s->staff()->height();
}
else if (s->isPedal()) {
yo = styleP(StyleIdx::pedalPosBelow);
}
else if (s->isTrill()) {
yo = styleP(StyleIdx::trillPosAbove);
}
else if (s->isTextLine()) {
yo = -5.0 * spatium();
}
if (!s->spannerSegments().isEmpty()) {
for (SpannerSegment* seg : s->spannerSegments()) {
if (!seg->userOff().isNull())
seg->setUserYoffset(seg->userOff().y() - yo);
}
}
else {
s->setUserYoffset(-yo);
}
}
}
connectTies();
//
// remove "middle beam" flags from first ChordRest in
// measure
//
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
int tracks = nstaves() * VOICES;
bool first = true;
for (int track = 0; track < tracks; ++track) {
for (Segment* s = m->first(); s; s = s->next()) {
if (s->segmentType() != Segment::Type::ChordRest)
continue;
ChordRest* cr = toChordRest(s->element(track));
if (cr) {
#if 0 // TODO
if (cr->isRest()) {
Rest* r = toRest(cr);
if (!r->userOff().isNull()) {
int lineOffset = r->computeLineOffset();
qreal lineDist = r->staff() ? r->staff()->staffType(cr->tick())->lineDistance().val() : 1.0;
r->rUserYoffset() -= (lineOffset * .5 * lineDist * r->spatium());
}
}
#endif
if (!first) {
switch (cr->beamMode()) {
case Beam::Mode::AUTO:
case Beam::Mode::BEGIN:
case Beam::Mode::END:
case Beam::Mode::NONE:
break;
case Beam::Mode::MID:
case Beam::Mode::BEGIN32:
case Beam::Mode::BEGIN64:
cr->setBeamMode(Beam::Mode::BEGIN);
break;
case Beam::Mode::INVALID:
if (cr->isChord())
cr->setBeamMode(Beam::Mode::AUTO);
else
cr->setBeamMode(Beam::Mode::NONE);
break;
}
first = false;
}
}
}
}
}
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
if (mb->type() == ElementType::VBOX) {
Box* b = static_cast<Box*>(mb);
qreal y = styleP(StyleIdx::staffUpperBorder);
b->setBottomGap(y);
}
}
_fileDivision = MScore::division;
//
// sanity check for barLineSpan and update ottavas
//
for (Staff* staff : staves()) {
int barLineSpan = staff->barLineSpan();
int idx = staff->idx();
int n = nstaves();
if (idx + barLineSpan > n) {
qDebug("bad span: idx %d span %d staves %d", idx, barLineSpan, n);
staff->setBarLineSpan(n - idx);
}
staff->updateOttava();
}
// adjust some styles
Spatium lmbd = styleS(StyleIdx::lyricsMinBottomDistance);
style().set(StyleIdx::lyricsMinBottomDistance, Spatium(lmbd.val() + 4.0));
if (styleB(StyleIdx::hideEmptyStaves)) // http://musescore.org/en/node/16228
style().set(StyleIdx::dontHideStavesInFirstSystem, false);
if (styleB(StyleIdx::showPageNumberOne)) { // http://musescore.org/en/node/21207
style().set(StyleIdx::evenFooterL, QString("$P"));
style().set(StyleIdx::oddFooterR, QString("$P"));
}
if (styleI(StyleIdx::minEmptyMeasures) == 0)
style().set(StyleIdx::minEmptyMeasures, 1);
style().set(StyleIdx::frameSystemDistance, styleS(StyleIdx::frameSystemDistance) + Spatium(6.0));
// hack: net overall effect of layout changes has been for things to take slightly more room
qreal adjustedSpacing = qMax(styleD(StyleIdx::measureSpacing) * 0.95, 1.0);
style().set(StyleIdx::measureSpacing, adjustedSpacing);
_showOmr = false;
// add invisible tempo text if necessary
// some 1.3 scores have tempolist but no tempo text
fixTicks();
for (auto i : tm) {
int tick = i.first;
qreal tempo = i.second.tempo;
if (tempomap()->tempo(tick) != tempo) {
TempoText* tt = new TempoText(this);
tt->setXmlText(QString("<sym>metNoteQuarterUp</sym> = %1").arg(qRound(tempo*60)));
tt->setTempo(tempo);
tt->setTrack(0);
tt->setVisible(false);
Measure* m = tick2measure(tick);
if (m) {
Segment* seg = m->getSegment(Segment::Type::ChordRest, tick);
seg->add(tt);
setTempo(tick, tempo);
}
else
delete tt;
}
}
// create excerpts
for (Excerpt* excerpt : _excerpts) {
if (excerpt->parts().isEmpty()) { // ignore empty parts
_excerpts.removeOne(excerpt);
continue;
}
if (!excerpt->parts().isEmpty()) {
Score* nscore = new Score(this);
excerpt->setPartScore(nscore);
nscore->style().set(StyleIdx::createMultiMeasureRests, true);
Excerpt::createExcerpt(excerpt);
}
}
// volta offsets in older scores are hardcoded to be relative to a voltaY of -2.0sp
// we'll force this and live with it for the score
// but we wait until now to do it so parts don't have this issue
if (styleV(StyleIdx::voltaY) == MScore::baseStyle().value(StyleIdx::voltaY))
style().set(StyleIdx::voltaY, -2.0f);
fixTicks();
rebuildMidiMapping();
updateChannel();
// treat reading a 1.14 file as import
// on save warn if old file will be overwritten
setCreated(true);
return FileError::FILE_NO_ERROR;
}
}