2016-09-13 09:39:50 +02:00
|
|
|
//=============================================================================
|
|
|
|
// 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 "segment.h"
|
|
|
|
#include "ottava.h"
|
|
|
|
#include "stafftype.h"
|
|
|
|
#include "text.h"
|
2018-10-24 10:40:03 +02:00
|
|
|
#include "measurenumber.h"
|
2016-09-13 09:39:50 +02:00
|
|
|
#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"
|
2016-09-22 12:02:27 +02:00
|
|
|
#include "utils.h"
|
|
|
|
#include "accidental.h"
|
|
|
|
#include "fingering.h"
|
2016-10-07 22:45:12 +02:00
|
|
|
#include "marker.h"
|
2016-10-06 12:21:28 +02:00
|
|
|
#include "read206.h"
|
2017-03-31 13:03:15 +02:00
|
|
|
#include "bracketItem.h"
|
2018-05-14 10:07:46 +02:00
|
|
|
#include "harmony.h"
|
|
|
|
#include "lyrics.h"
|
|
|
|
#include "image.h"
|
|
|
|
#include "textframe.h"
|
2018-07-26 13:14:06 +02:00
|
|
|
#include "jump.h"
|
2018-08-13 13:51:44 +02:00
|
|
|
#include "textline.h"
|
2018-08-15 08:56:32 +02:00
|
|
|
#include "pedal.h"
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
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 {
|
2018-10-26 10:41:07 +02:00
|
|
|
Sid sid;
|
2016-09-13 09:39:50 +02:00
|
|
|
QVariant val;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const StyleVal2 style114[] = {
|
2019-02-17 01:13:32 +01:00
|
|
|
// { Sid::lyricsMinBottomDistance, Spatium(2) },
|
|
|
|
{ Sid::lyricsDashForce, QVariant(false) },
|
2018-03-27 15:36:00 +02:00
|
|
|
{ Sid::frameSystemDistance, Spatium(1.0) },
|
|
|
|
{ Sid::minMeasureWidth, Spatium(4.0) },
|
2019-04-06 07:54:38 +02:00
|
|
|
// { Sid::endBarDistance, Spatium(0.30) },
|
2018-03-27 15:36:00 +02:00
|
|
|
|
|
|
|
{ Sid::repeatBarTips, QVariant(false) },
|
|
|
|
{ Sid::startBarlineSingle, QVariant(false) },
|
|
|
|
{ Sid::startBarlineMultiple, QVariant(true) },
|
|
|
|
{ Sid::bracketWidth, QVariant(0.35) },
|
|
|
|
{ Sid::bracketDistance, QVariant(0.25) },
|
|
|
|
{ Sid::clefLeftMargin, QVariant(0.5) },
|
|
|
|
{ Sid::keysigLeftMargin, QVariant(0.5) },
|
|
|
|
{ Sid::timesigLeftMargin, QVariant(0.5) },
|
|
|
|
{ Sid::clefKeyRightMargin, QVariant(1.75) },
|
|
|
|
{ Sid::clefBarlineDistance, QVariant(0.18) },
|
|
|
|
{ Sid::stemWidth, QVariant(0.13) },
|
|
|
|
{ Sid::shortenStem, QVariant(true) },
|
|
|
|
{ Sid::shortStemProgression, QVariant(0.25) },
|
|
|
|
{ Sid::shortestStem, QVariant(2.25) },
|
|
|
|
{ Sid::beginRepeatLeftMargin, QVariant(1.0) },
|
|
|
|
{ Sid::minNoteDistance, QVariant(0.4) },
|
|
|
|
{ Sid::barNoteDistance, QVariant(1.2) },
|
|
|
|
{ Sid::noteBarDistance, QVariant(1.0) },
|
|
|
|
{ Sid::measureSpacing, QVariant(1.2) },
|
|
|
|
{ Sid::staffLineWidth, QVariant(0.08) },
|
|
|
|
{ Sid::ledgerLineWidth, QVariant(0.12) },
|
|
|
|
{ Sid::akkoladeWidth, QVariant(1.6) },
|
|
|
|
{ Sid::accidentalDistance, QVariant(0.22) },
|
|
|
|
{ Sid::accidentalNoteDistance, QVariant(0.22) },
|
|
|
|
{ Sid::beamWidth, QVariant(0.48) },
|
|
|
|
{ Sid::beamDistance, QVariant(0.5) },
|
|
|
|
{ Sid::beamMinLen, QVariant(1.25) },
|
|
|
|
{ Sid::dotNoteDistance, QVariant(0.35) },
|
|
|
|
{ Sid::dotRestDistance, QVariant(0.25) },
|
|
|
|
{ Sid::dotDotDistance, QVariant(0.5) },
|
|
|
|
{ Sid::propertyDistanceHead, QVariant(1.0) },
|
|
|
|
{ Sid::propertyDistanceStem, QVariant(0.5) },
|
|
|
|
{ Sid::propertyDistance, QVariant(1.0) },
|
|
|
|
{ Sid::articulationMag, QVariant(qreal(1.0)) },
|
|
|
|
{ Sid::lastSystemFillLimit, QVariant(0.3) },
|
|
|
|
{ Sid::hairpinHeight, QVariant(1.2) },
|
|
|
|
{ Sid::hairpinContHeight, QVariant(0.5) },
|
|
|
|
{ Sid::hairpinLineWidth, QVariant(0.13) },
|
|
|
|
{ Sid::showPageNumber, QVariant(true) },
|
|
|
|
{ Sid::showPageNumberOne, QVariant(false) },
|
|
|
|
{ Sid::pageNumberOddEven, QVariant(true) },
|
|
|
|
{ Sid::showMeasureNumber, QVariant(true) },
|
|
|
|
{ Sid::showMeasureNumberOne, QVariant(false) },
|
|
|
|
{ Sid::measureNumberInterval, QVariant(5) },
|
|
|
|
{ Sid::measureNumberSystem, QVariant(true) },
|
|
|
|
{ Sid::measureNumberAllStaffs, QVariant(false) },
|
|
|
|
{ Sid::smallNoteMag, QVariant(qreal(0.7)) },
|
|
|
|
{ Sid::graceNoteMag, QVariant(qreal(0.7)) },
|
|
|
|
{ Sid::smallStaffMag, QVariant(qreal(0.7)) },
|
|
|
|
{ Sid::smallClefMag, QVariant(qreal(0.8)) },
|
|
|
|
{ Sid::genClef, QVariant(true) },
|
|
|
|
{ Sid::genKeysig, QVariant(true) },
|
|
|
|
{ Sid::genCourtesyTimesig, QVariant(true) },
|
|
|
|
{ Sid::genCourtesyKeysig, QVariant(true) },
|
|
|
|
{ Sid::useStandardNoteNames, QVariant(true) },
|
|
|
|
{ Sid::useGermanNoteNames, QVariant(false) },
|
|
|
|
{ Sid::useFullGermanNoteNames, QVariant(false) },
|
|
|
|
{ Sid::useSolfeggioNoteNames, QVariant(false) },
|
|
|
|
{ Sid::useFrenchNoteNames, QVariant(false) },
|
|
|
|
{ Sid::chordDescriptionFile, QVariant(QString("stdchords.xml")) },
|
|
|
|
{ Sid::chordStyle, QVariant(QString("custom")) },
|
|
|
|
{ Sid::chordsXmlFile, QVariant(true) },
|
2018-07-16 14:35:11 +02:00
|
|
|
// { Sid::harmonyY, QVariant(0.0) },
|
2018-03-27 15:36:00 +02:00
|
|
|
{ Sid::concertPitch, QVariant(false) },
|
|
|
|
{ Sid::createMultiMeasureRests, QVariant(false) },
|
|
|
|
{ Sid::minEmptyMeasures, QVariant(2) },
|
|
|
|
{ Sid::minMMRestWidth, QVariant(4.0) },
|
|
|
|
{ Sid::hideEmptyStaves, QVariant(false) },
|
|
|
|
{ Sid::gateTime, QVariant(100) },
|
|
|
|
{ Sid::tenutoGateTime, QVariant(100) },
|
|
|
|
{ Sid::staccatoGateTime, QVariant(50) },
|
|
|
|
{ Sid::slurGateTime, QVariant(100) },
|
|
|
|
{ Sid::ArpeggioNoteDistance, QVariant(.5) },
|
|
|
|
{ Sid::ArpeggioLineWidth, QVariant(.18) },
|
|
|
|
{ Sid::ArpeggioHookLen, QVariant(.8) },
|
|
|
|
{ Sid::keySigNaturals, QVariant(int(KeySigNatural::BEFORE)) },
|
|
|
|
{ Sid::tupletMaxSlope, QVariant(qreal(0.5)) },
|
|
|
|
{ Sid::tupletOufOfStaff, QVariant(false) },
|
|
|
|
{ Sid::tupletVHeadDistance, QVariant(.5) },
|
|
|
|
{ Sid::tupletVStemDistance, QVariant(.25) },
|
|
|
|
{ Sid::tupletStemLeftDistance, QVariant(.5) },
|
|
|
|
{ Sid::tupletStemRightDistance, QVariant(.5) },
|
|
|
|
{ Sid::tupletNoteLeftDistance, QVariant(0.0) },
|
|
|
|
{ Sid::tupletNoteRightDistance, QVariant(0.0) },
|
|
|
|
{ Sid::hideInstrumentNameIfOneInstrument, QVariant(false) },
|
2016-09-13 09:39:50 +02:00
|
|
|
};
|
|
|
|
|
2016-09-19 13:25:54 +02:00
|
|
|
#define MM(x) ((x)/INCH)
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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[] = {
|
2016-09-19 13:25:54 +02:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2017-08-16 10:53:03 +02:00
|
|
|
|
2016-09-19 13:25:54 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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");
|
2017-01-05 11:23:47 +01:00
|
|
|
return &paperSizes114[0];
|
2016-09-19 13:25:54 +02:00
|
|
|
}
|
2017-08-16 10:53:03 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// convertFromHtml
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2017-12-27 11:51:00 +01:00
|
|
|
QString convertFromHtml(TextBase* t, const QString& ss)
|
2017-08-16 10:53:03 +02:00
|
|
|
{
|
|
|
|
QTextDocument doc;
|
|
|
|
doc.setHtml(ss.trimmed());
|
|
|
|
|
|
|
|
QString s;
|
|
|
|
qreal size = t->size(); // textStyle().size();
|
|
|
|
QString family = t->family(); // textStyle().family();
|
|
|
|
|
|
|
|
for (auto b = doc.firstBlock(); b.isValid() ; b = b.next()) {
|
|
|
|
if (!s.isEmpty())
|
|
|
|
s += "\n";
|
|
|
|
for (auto it = b.begin(); !it.atEnd(); ++it) {
|
|
|
|
QTextFragment f = it.fragment();
|
|
|
|
if (f.isValid()) {
|
|
|
|
QTextCharFormat tf = f.charFormat();
|
|
|
|
QFont font = tf.font();
|
|
|
|
qreal htmlSize = font.pointSizeF();
|
|
|
|
// html font sizes may have spatium adjustments; need to undo this
|
|
|
|
if (t->sizeIsSpatiumDependent())
|
|
|
|
htmlSize *= SPATIUM20 / t->spatium();
|
|
|
|
if (fabs(size - htmlSize) > 0.1) {
|
|
|
|
size = htmlSize;
|
|
|
|
s += QString("<font size=\"%1\"/>").arg(size);
|
|
|
|
}
|
|
|
|
if (family != font.family()) {
|
|
|
|
family = font.family();
|
|
|
|
s += QString("<font face=\"%1\"/>").arg(family);
|
|
|
|
}
|
|
|
|
if (font.bold())
|
|
|
|
s += "<b>";
|
|
|
|
if (font.italic())
|
|
|
|
s += "<i>";
|
|
|
|
if (font.underline())
|
|
|
|
s += "<u>";
|
|
|
|
s += f.text().toHtmlEscaped();
|
|
|
|
if (font.underline())
|
|
|
|
s += "</u>";
|
|
|
|
if (font.italic())
|
|
|
|
s += "</i>";
|
|
|
|
if (font.bold())
|
|
|
|
s += "</b>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s.replace(QChar(0xe10e), QString("<sym>accidentalNatural</sym>")); //natural
|
|
|
|
s.replace(QChar(0xe10c), QString("<sym>accidentalSharp</sym>")); // sharp
|
|
|
|
s.replace(QChar(0xe10d), QString("<sym>accidentalFlat</sym>")); // flat
|
|
|
|
s.replace(QChar(0xe104), QString("<sym>metNoteHalfUp</sym>")), // note2_Sym
|
|
|
|
s.replace(QChar(0xe105), QString("<sym>metNoteQuarterUp</sym>")); // note4_Sym
|
|
|
|
s.replace(QChar(0xe106), QString("<sym>metNote8thUp</sym>")); // note8_Sym
|
|
|
|
s.replace(QChar(0xe107), QString("<sym>metNote16thUp</sym>")); // note16_Sym
|
|
|
|
s.replace(QChar(0xe108), QString("<sym>metNote32ndUp</sym>")); // note32_Sym
|
|
|
|
s.replace(QChar(0xe109), QString("<sym>metNote64thUp</sym>")); // note64_Sym
|
|
|
|
s.replace(QChar(0xe10a), QString("<sym>metAugmentationDot</sym>")); // dot
|
|
|
|
s.replace(QChar(0xe10b), QString("<sym>metAugmentationDot</sym><sym>space</sym><sym>metAugmentationDot</sym>")); // dotdot
|
|
|
|
s.replace(QChar(0xe167), QString("<sym>segno</sym>")); // segno
|
|
|
|
s.replace(QChar(0xe168), QString("<sym>coda</sym>")); // coda
|
|
|
|
s.replace(QChar(0xe169), QString("<sym>codaSquare</sym>")); // varcoda
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readTextProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2018-08-01 11:46:07 +02:00
|
|
|
static bool readTextProperties(XmlReader& e, TextBase* t, Element*)
|
2017-08-16 10:53:03 +02:00
|
|
|
{
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "style") {
|
|
|
|
int i = e.readInt();
|
2018-08-01 11:46:07 +02:00
|
|
|
Tid ss = Tid::DEFAULT;
|
2017-08-16 10:53:03 +02:00
|
|
|
switch (i) {
|
2018-08-01 11:46:07 +02:00
|
|
|
case 2: ss = Tid::TITLE; break;
|
|
|
|
case 3: ss = Tid::SUBTITLE; break;
|
|
|
|
case 4: ss = Tid::COMPOSER; break;
|
|
|
|
case 5: ss = Tid::POET; break;
|
|
|
|
|
|
|
|
case 6: ss = Tid::LYRICS_ODD; break;
|
|
|
|
case 7: ss = Tid::LYRICS_EVEN; break;
|
|
|
|
|
|
|
|
case 8: ss = Tid::FINGERING; break;
|
|
|
|
case 9: ss = Tid::INSTRUMENT_LONG; break;
|
|
|
|
case 10: ss = Tid::INSTRUMENT_SHORT; break;
|
|
|
|
case 11: ss = Tid::INSTRUMENT_EXCERPT; break;
|
|
|
|
|
|
|
|
case 12: ss = Tid::DYNAMICS; break;
|
|
|
|
case 13: ss = Tid::EXPRESSION; break;
|
|
|
|
case 14: ss = Tid::TEMPO; break;
|
|
|
|
case 15: ss = Tid::METRONOME; break;
|
|
|
|
case 16: ss = Tid::FOOTER; break; // TextStyleType::COPYRIGHT
|
|
|
|
case 17: ss = Tid::MEASURE_NUMBER; break;
|
|
|
|
case 18: ss = Tid::FOOTER; break; // TextStyleType::PAGE_NUMBER_ODD
|
|
|
|
case 19: ss = Tid::FOOTER; break; // TextStyleType::PAGE_NUMBER_EVEN
|
|
|
|
case 20: ss = Tid::TRANSLATOR; break;
|
|
|
|
case 21: ss = Tid::TUPLET; break;
|
|
|
|
|
|
|
|
case 22: ss = Tid::SYSTEM; break;
|
|
|
|
case 23: ss = Tid::STAFF; break;
|
|
|
|
case 24: ss = Tid::HARMONY_A; break;
|
|
|
|
case 25: ss = Tid::REHEARSAL_MARK; break;
|
|
|
|
case 26: ss = Tid::REPEAT_LEFT; break;
|
|
|
|
case 27: ss = Tid::VOLTA; break;
|
|
|
|
case 28: ss = Tid::FRAME; break;
|
|
|
|
case 29: ss = Tid::TEXTLINE; break;
|
|
|
|
case 30: ss = Tid::GLISSANDO; break;
|
|
|
|
case 31: ss = Tid::STRING_NUMBER; break;
|
|
|
|
|
|
|
|
case 32: ss = Tid::OTTAVA; break;
|
|
|
|
//?? case 33: ss = Tid::BENCH; break;
|
|
|
|
case 34: ss = Tid::HEADER; break;
|
|
|
|
case 35: ss = Tid::FOOTER; break;
|
2017-08-16 10:53:03 +02:00
|
|
|
case 0:
|
|
|
|
default:
|
|
|
|
qDebug("style %d invalid", i);
|
2018-08-01 11:46:07 +02:00
|
|
|
ss = Tid::DEFAULT;
|
2017-08-16 10:53:03 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-08-01 11:46:07 +02:00
|
|
|
t->initTid(ss);
|
2017-08-16 10:53:03 +02:00
|
|
|
}
|
|
|
|
else if (tag == "subtype")
|
|
|
|
e.skipCurrentElement();
|
2018-05-14 10:07:46 +02:00
|
|
|
else if (tag == "html-data") {
|
|
|
|
QString ss = e.readXml();
|
|
|
|
QString s = convertFromHtml(t, ss);
|
|
|
|
// qDebug("html-data <%s>", qPrintable(s));
|
|
|
|
t->setXmlText(s);
|
|
|
|
}
|
2017-08-16 10:53:03 +02:00
|
|
|
else if (tag == "foregroundColor") // same as "color" ?
|
|
|
|
e.skipCurrentElement();
|
|
|
|
else if (tag == "frame")
|
2018-07-26 13:14:06 +02:00
|
|
|
t->setFrameType(e.readBool() ? FrameType::SQUARE : FrameType::NO_FRAME);
|
2017-08-16 10:53:03 +02:00
|
|
|
else if (tag == "halign") {
|
2018-05-14 10:07:46 +02:00
|
|
|
Align align = Align(int(t->align()) & int(~Align::HMASK));
|
2017-08-16 10:53:03 +02:00
|
|
|
const QString& val(e.readElementText());
|
|
|
|
if (val == "center")
|
|
|
|
align = align | Align::HCENTER;
|
|
|
|
else if (val == "right")
|
|
|
|
align = align | Align::RIGHT;
|
|
|
|
else if (val == "left")
|
|
|
|
;
|
|
|
|
else
|
|
|
|
qDebug("readText: unknown alignment: <%s>", qPrintable(val));
|
|
|
|
t->setAlign(align);
|
|
|
|
}
|
2018-05-14 10:07:46 +02:00
|
|
|
else if (tag == "valign") {
|
|
|
|
Align align = Align(int(t->align()) & int(~Align::VMASK));
|
|
|
|
const QString& val(e.readElementText());
|
|
|
|
if (val == "bottom")
|
|
|
|
align = align | Align::BOTTOM;
|
|
|
|
else if (val == "top")
|
|
|
|
;
|
|
|
|
else if (val == "vcenter")
|
|
|
|
align = align | Align::VCENTER;
|
|
|
|
else if (val == "baseline")
|
|
|
|
align = align | Align::BASELINE;
|
|
|
|
else
|
|
|
|
qDebug("readText: unknown alignment: <%s>", qPrintable(val));
|
|
|
|
t->setAlign(align);
|
|
|
|
}
|
|
|
|
else if (tag == "rxoffset") { // TODO
|
|
|
|
e.readElementText();
|
|
|
|
}
|
|
|
|
else if (tag == "ryoffset") { // TODO
|
|
|
|
e.readElementText();
|
|
|
|
}
|
|
|
|
else if (tag == "yoffset") { // TODO
|
|
|
|
e.readElementText();
|
|
|
|
}
|
|
|
|
else if (tag == "systemFlag") { // TODO
|
|
|
|
e.readElementText();
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!t->readProperties(e))
|
2017-08-16 10:53:03 +02:00
|
|
|
return false;
|
2018-10-18 11:53:01 +02:00
|
|
|
t->setOffset(QPointF()); // ignore user offsets
|
2018-05-14 10:07:46 +02:00
|
|
|
t->setAutoplace(true);
|
2017-08-16 10:53:03 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2018-05-14 10:07:46 +02:00
|
|
|
// readText114
|
2017-08-16 10:53:03 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2018-10-24 10:40:03 +02:00
|
|
|
static void readText114(XmlReader& e, TextBase* t, Element* be)
|
2017-08-16 10:53:03 +02:00
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
if (!readTextProperties(e, t, be))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 13:25:54 +02:00
|
|
|
|
2016-09-22 12:02:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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)
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket(i));
|
2016-09-22 12:02:27 +02:00
|
|
|
}
|
|
|
|
else if (tag == "subtype") {
|
|
|
|
QString text(e.readElementText());
|
|
|
|
bool isInt;
|
|
|
|
int i = text.toInt(&isInt);
|
|
|
|
if (isInt) {
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(i & 0x8000 ? AccidentalBracket::PARENTHESIS : AccidentalBracket::BRACKET);
|
2016-09-22 12:02:27 +02:00
|
|
|
i &= ~0x8000;
|
|
|
|
AccidentalType at;
|
|
|
|
switch (i) {
|
|
|
|
case 0:
|
|
|
|
at = AccidentalType::NONE;
|
|
|
|
break;
|
|
|
|
case 6:
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket::PARENTHESIS);
|
2017-06-09 13:30:29 +02:00
|
|
|
// fall through
|
2016-09-22 12:02:27 +02:00
|
|
|
case 1:
|
|
|
|
case 11:
|
|
|
|
at = AccidentalType::SHARP;
|
|
|
|
break;
|
|
|
|
case 7:
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket::PARENTHESIS);
|
2017-06-09 13:30:29 +02:00
|
|
|
// fall through
|
2016-09-22 12:02:27 +02:00
|
|
|
case 2:
|
|
|
|
case 12:
|
|
|
|
at = AccidentalType::FLAT;
|
|
|
|
break;
|
|
|
|
case 8:
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket::PARENTHESIS);
|
2017-06-09 13:30:29 +02:00
|
|
|
// fall through
|
2016-09-22 12:02:27 +02:00
|
|
|
case 3:
|
|
|
|
case 13:
|
|
|
|
at = AccidentalType::SHARP2;
|
|
|
|
break;
|
|
|
|
case 9:
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket::PARENTHESIS);
|
2017-06-09 13:30:29 +02:00
|
|
|
// fall through
|
2016-09-22 12:02:27 +02:00
|
|
|
case 4:
|
|
|
|
case 14:
|
|
|
|
at = AccidentalType::FLAT2;
|
|
|
|
break;
|
|
|
|
case 10:
|
2017-01-31 17:28:56 +01:00
|
|
|
a->setBracket(AccidentalBracket::PARENTHESIS);
|
2017-06-09 13:30:29 +02:00
|
|
|
// fall through
|
2016-09-22 12:02:27 +02:00
|
|
|
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:
|
2016-09-29 01:18:02 +02:00
|
|
|
at = AccidentalType::NONE;//AccidentalType::MIRRORED_FLAT_SLASH;
|
2016-09-22 12:02:27 +02:00
|
|
|
break;
|
|
|
|
case 21:
|
2016-09-29 01:18:02 +02:00
|
|
|
at = AccidentalType::NONE;//AccidentalType::FLAT_FLAT_SLASH;
|
2016-09-22 12:02:27 +02:00
|
|
|
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:
|
2016-09-29 01:18:02 +02:00
|
|
|
at = AccidentalType::NONE;//AccidentalType::SHARP_ARROW_BOTH;
|
2016-09-22 12:02:27 +02:00
|
|
|
break;
|
|
|
|
case 29:
|
|
|
|
at = AccidentalType::FLAT_ARROW_UP;
|
|
|
|
break;
|
|
|
|
case 30:
|
|
|
|
at = AccidentalType::FLAT_ARROW_DOWN;
|
|
|
|
break;
|
|
|
|
case 31:
|
2016-09-29 01:18:02 +02:00
|
|
|
at = AccidentalType::NONE;//AccidentalType::FLAT_ARROW_BOTH;
|
2016-09-22 12:02:27 +02:00
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
at = AccidentalType::NATURAL_ARROW_UP;
|
|
|
|
break;
|
|
|
|
case 33:
|
|
|
|
at = AccidentalType::NATURAL_ARROW_DOWN;
|
|
|
|
break;
|
|
|
|
case 34:
|
2016-09-29 01:18:02 +02:00
|
|
|
at = AccidentalType::NONE;//AccidentalType::NATURAL_ARROW_BOTH;
|
2016-09-22 12:02:27 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
at = AccidentalType::NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
a->setAccidentalType(at);
|
|
|
|
}
|
2016-09-29 01:18:02 +02:00
|
|
|
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);
|
|
|
|
}
|
2016-09-22 12:02:27 +02:00
|
|
|
}
|
|
|
|
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
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (a->Element::readProperties(e))
|
2016-09-22 12:02:27 +02:00
|
|
|
;
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-20 10:13:12 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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:
|
2016-10-22 20:16:42 +02:00
|
|
|
val = NoteHead::Group::HEAD_TRIANGLE_DOWN;
|
2016-10-20 10:13:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-22 15:12:40 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readFingering114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readFingering114(XmlReader& e, Fingering* fing)
|
|
|
|
{
|
|
|
|
bool isStringNumber = false;
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
|
|
|
|
if (tag == "html-data") {
|
|
|
|
auto htmlDdata = QTextDocumentFragment::fromHtml(e.readXml()).toPlainText();
|
|
|
|
htmlDdata.replace(" ", "");
|
|
|
|
fing->setPlainText(htmlDdata);
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-22 15:12:40 +02:00
|
|
|
else if (tag == "subtype") {
|
|
|
|
auto subtype = e.readElementText();
|
|
|
|
if (subtype == "StringNumber") {
|
|
|
|
isStringNumber = true;
|
|
|
|
fing->setProperty(Pid::SUB_STYLE, QVariant(10));
|
|
|
|
fing->setPropertyFlags(Pid::SUB_STYLE, PropertyFlags::UNSTYLED);
|
|
|
|
}
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-22 15:12:40 +02:00
|
|
|
else if (tag == "frame") {
|
|
|
|
auto frame = e.readInt();
|
|
|
|
if (frame)
|
|
|
|
if (isStringNumber) //default value is circle for stringnumber, square is setted in tag circle
|
2018-10-18 11:53:01 +02:00
|
|
|
fing->setFrameType(FrameType::CIRCLE);
|
2018-08-22 15:12:40 +02:00
|
|
|
else //default value is square for stringnumber, circle is setted in tag circle
|
|
|
|
fing->setFrameType(FrameType::SQUARE);
|
|
|
|
else
|
|
|
|
fing->setFrameType(FrameType::NO_FRAME);
|
|
|
|
}
|
|
|
|
else if (tag == "circle") {
|
|
|
|
auto circle = e.readInt();
|
|
|
|
if (circle)
|
|
|
|
fing->setFrameType(FrameType::CIRCLE);
|
|
|
|
else
|
|
|
|
fing->setFrameType(FrameType::SQUARE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
e.skipCurrentElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-22 12:02:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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());
|
2018-08-22 15:12:40 +02:00
|
|
|
readFingering114(e, f);
|
2016-09-22 12:02:27 +02:00
|
|
|
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)
|
2019-01-30 15:13:54 +01:00
|
|
|
note->setOnTimeOffset(e.readInt() * 1000 / note->chord()->actualTicks().ticks());
|
2016-09-22 12:02:27 +02:00
|
|
|
else
|
|
|
|
note->setOnTimeOffset(e.readInt() * 10);
|
|
|
|
}
|
|
|
|
else if (tag == "offTimeOffset") {
|
|
|
|
if (note->offTimeType() == 1)
|
2019-01-30 15:13:54 +01:00
|
|
|
note->setOffTimeOffset(1000 + (e.readInt() * 1000 / note->chord()->actualTicks().ticks()));
|
2016-09-22 12:02:27 +02:00
|
|
|
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;
|
2016-09-29 01:18:02 +02:00
|
|
|
case 10: at = AccidentalType::NONE; break; // AccidentalType::MIRRORED_FLAT_SLASH
|
|
|
|
case 11: at = AccidentalType::NONE; break; // AccidentalType::FLAT_FLAT_SLASH
|
2016-09-22 12:02:27 +02:00
|
|
|
|
|
|
|
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;
|
2016-09-29 01:18:02 +02:00
|
|
|
case 18: at = AccidentalType::NONE; break; // AccidentalType::SHARP_ARROW_BOTH
|
2016-09-22 12:02:27 +02:00
|
|
|
case 19: at = AccidentalType::FLAT_ARROW_UP; break;
|
|
|
|
case 20: at = AccidentalType::FLAT_ARROW_DOWN; break;
|
2016-09-29 01:18:02 +02:00
|
|
|
case 21: at = AccidentalType::NONE; break; // AccidentalType::FLAT_ARROW_BOTH
|
2016-09-22 12:02:27 +02:00
|
|
|
case 22: at = AccidentalType::NATURAL_ARROW_UP; break;
|
|
|
|
case 23: at = AccidentalType::NATURAL_ARROW_DOWN; break;
|
2016-09-29 01:18:02 +02:00
|
|
|
case 24: at = AccidentalType::NONE; break; // AccidentalType::NATURAL_ARROW_BOTH
|
2016-09-22 12:02:27 +02:00
|
|
|
case 25: at = AccidentalType::SORI; break;
|
|
|
|
case 26: at = AccidentalType::KORON; break;
|
|
|
|
}
|
|
|
|
note->accidental()->setAccidentalType(at);
|
2017-01-31 17:28:56 +01:00
|
|
|
|
|
|
|
note->accidental()->setBracket(AccidentalBracket(bracket));
|
|
|
|
|
2016-09-22 12:02:27 +02:00
|
|
|
note->accidental()->setRole(AccidentalRole::USER);
|
|
|
|
e.hasAccidental = true; // we now have an accidental
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "offset")
|
|
|
|
e.skipCurrentElement(); // ignore manual layout in older scores
|
|
|
|
else if (tag == "move")
|
|
|
|
note->chord()->setStaffMove(e.readInt());
|
2016-10-18 09:21:54 +02:00
|
|
|
else if (tag == "head") {
|
|
|
|
int i = e.readInt();
|
2016-10-20 10:13:12 +02:00
|
|
|
NoteHead::Group val = convertHeadGroup(i);
|
2016-10-18 09:21:54 +02:00
|
|
|
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);
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (readNoteProperties206(note, e))
|
2016-09-22 12:02:27 +02:00
|
|
|
;
|
|
|
|
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()))) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = note->chord() ? note->chord()->tick() : Fraction(-1,1);
|
2016-09-22 12:02:27 +02:00
|
|
|
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
|
2018-08-27 22:57:15 +02:00
|
|
|
// see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
|
2016-09-22 12:02:27 +02:00
|
|
|
// 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-28 16:39:48 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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;
|
2016-09-28 20:34:21 +02:00
|
|
|
case 1: ct = ClefType::G8_VA; break;
|
|
|
|
case 2: ct = ClefType::G15_MA; break;
|
|
|
|
case 3: ct = ClefType::G8_VB; break;
|
2016-09-28 16:39:48 +02:00
|
|
|
case 4: ct = ClefType::F; break;
|
2016-09-28 20:34:21 +02:00
|
|
|
case 5: ct = ClefType::F8_VB; break;
|
|
|
|
case 6: ct = ClefType::F15_MB; break;
|
2016-09-28 16:39:48 +02:00
|
|
|
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;
|
2016-09-28 20:34:21 +02:00
|
|
|
case 16: ct = ClefType::G_1; break;
|
2016-09-28 16:39:48 +02:00
|
|
|
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()));
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!clef->readProperties(e))
|
2016-09-28 16:39:48 +02:00
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
if (clef->clefType() == ClefType::INVALID)
|
|
|
|
clef->setClefType(ClefType::G);
|
|
|
|
}
|
|
|
|
|
2016-11-09 17:12:52 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
2018-03-16 10:54:18 +01:00
|
|
|
tuplet->setNumberType(e.readInt() ? TupletNumberType::SHOW_NUMBER : TupletNumberType::NO_TEXT);
|
2016-11-09 17:12:52 +01:00
|
|
|
else if (tag == "hasLine") { // obsolete even in 1.3
|
|
|
|
tuplet->setHasBracket(e.readInt());
|
2018-03-16 10:54:18 +01:00
|
|
|
tuplet->setBracketType(TupletBracketType::AUTO_BRACKET);
|
2016-11-09 17:12:52 +01:00
|
|
|
}
|
|
|
|
else if (tag == "baseLen") // obsolete even in 1.3
|
|
|
|
bl = e.readInt();
|
2016-11-17 14:28:05 +01:00
|
|
|
else if (tag == "tick")
|
2019-01-30 15:13:54 +01:00
|
|
|
tuplet->setTick(Fraction::fromTicks(e.readInt()));
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!readTupletProperties206(e, tuplet))
|
2016-11-09 17:12:52 +01:00
|
|
|
e.unknown();
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction r = (tuplet->ratio() == Fraction(1,1)) ? tuplet->ratio() : tuplet->ratio().reduced();
|
2016-12-16 18:17:30 +01:00
|
|
|
// this may be wrong, but at this stage it is kept for compatibility. It will be corrected afterwards
|
|
|
|
// during "sanitize" step
|
2016-11-09 17:12:52 +01:00
|
|
|
Fraction f(r.denominator(), tuplet->baseLen().fraction().denominator());
|
2019-01-30 15:13:54 +01:00
|
|
|
tuplet->setTicks(f.reduced());
|
2016-11-09 17:12:52 +01:00
|
|
|
if (bl != -1) { // obsolete, even in 1.3
|
|
|
|
TDuration d;
|
|
|
|
d.setVal(bl);
|
|
|
|
tuplet->setBaseLen(d);
|
|
|
|
d.setVal(bl * tuplet->ratio().denominator());
|
2019-01-30 15:13:54 +01:00
|
|
|
tuplet->setTicks(d.fraction());
|
2016-11-09 17:12:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 23:06:56 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readTremolo
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readTremolo(Tremolo* tremolo, XmlReader& e)
|
|
|
|
{
|
|
|
|
enum class OldTremoloType : char {
|
|
|
|
OLD_R8 = 0,
|
|
|
|
OLD_R16,
|
|
|
|
OLD_R32,
|
|
|
|
OLD_C8,
|
|
|
|
OLD_C16,
|
|
|
|
OLD_C32
|
|
|
|
};
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
if (e.name() == "subtype") {
|
|
|
|
OldTremoloType sti = OldTremoloType(e.readElementText().toInt());
|
|
|
|
TremoloType st;
|
|
|
|
switch (sti) {
|
|
|
|
default:
|
|
|
|
case OldTremoloType::OLD_R8: st = TremoloType::R8; break;
|
|
|
|
case OldTremoloType::OLD_R16: st = TremoloType::R16; break;
|
|
|
|
case OldTremoloType::OLD_R32: st = TremoloType::R32; break;
|
|
|
|
case OldTremoloType::OLD_C8: st = TremoloType::C8; break;
|
|
|
|
case OldTremoloType::OLD_C16: st = TremoloType::C16; break;
|
|
|
|
case OldTremoloType::OLD_C32: st = TremoloType::C32; break;
|
|
|
|
}
|
|
|
|
tremolo->setTremoloType(st);
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!tremolo->Element::readProperties(e))
|
2018-05-14 23:06:56 +02:00
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-22 12:02:27 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readChord
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
static void readChord(Measure* m, Chord* chord, XmlReader& e)
|
2016-09-22 12:02:27 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2016-10-06 12:21:28 +02:00
|
|
|
else if (tag == "Attribute" || tag == "Articulation") {
|
2019-05-30 19:12:37 +02:00
|
|
|
Element* el = readArticulation(chord, e);
|
2018-05-14 10:07:46 +02:00
|
|
|
if (el->isFermata()) {
|
|
|
|
if (!chord->segment())
|
|
|
|
chord->setParent(m->getSegment(SegmentType::ChordRest, e.tick()));
|
2018-01-16 13:38:17 +01:00
|
|
|
chord->segment()->add(el);
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
2018-01-16 13:38:17 +01:00
|
|
|
else
|
|
|
|
chord->add(el);
|
2016-10-06 12:21:28 +02:00
|
|
|
}
|
2018-05-14 23:06:56 +02:00
|
|
|
else if (tag == "Tremolo") {
|
|
|
|
Tremolo* tremolo = new Tremolo(chord->score());
|
2018-12-19 14:36:02 +01:00
|
|
|
tremolo->setDurationType(chord->durationType());
|
2018-05-14 23:06:56 +02:00
|
|
|
chord->setTremolo(tremolo);
|
|
|
|
tremolo->setTrack(chord->track());
|
|
|
|
readTremolo(tremolo, e);
|
|
|
|
tremolo->setParent(chord);
|
2018-05-18 01:34:37 +02:00
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (readChordProperties206(e, chord))
|
2016-09-22 12:02:27 +02:00
|
|
|
;
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-06 12:21:28 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readRest
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
static void readRest(Measure* m, Rest* rest, XmlReader& e)
|
2016-10-06 12:21:28 +02:00
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "Attribute" || tag == "Articulation") {
|
2019-05-30 19:12:37 +02:00
|
|
|
Element* el = readArticulation(rest, e);
|
2018-05-14 10:07:46 +02:00
|
|
|
if (el->isFermata()) {
|
|
|
|
if (!rest->segment())
|
|
|
|
rest->setParent(m->getSegment(SegmentType::ChordRest, e.tick()));
|
2018-01-16 13:38:17 +01:00
|
|
|
rest->segment()->add(el);
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
2018-01-16 13:38:17 +01:00
|
|
|
else
|
|
|
|
rest->add(el);
|
2016-10-06 12:21:28 +02:00
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (readChordRestProperties206(e, rest))
|
2016-10-06 12:21:28 +02:00
|
|
|
;
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-16 10:53:03 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readTempoText
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void readTempoText(TempoText* t, XmlReader& e)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "tempo")
|
|
|
|
t->setTempo(e.readDouble());
|
|
|
|
else if (!readTextProperties(e, t, t))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readStaffText
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void readStaffText(StaffText* t, XmlReader& e)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
if (!readTextProperties(e, t, t))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readLineSegment114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readLineSegment114(XmlReader& e, LineSegment* ls)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "off1")
|
2018-10-18 11:53:01 +02:00
|
|
|
ls->setOffset(e.readPoint() * ls->spatium());
|
2018-05-14 10:07:46 +02:00
|
|
|
else
|
2018-08-27 22:57:15 +02:00
|
|
|
ls->readProperties(e);
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readTextLineProperties114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool readTextLineProperties114(XmlReader& e, TextLineBase* tl)
|
|
|
|
{
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
|
|
|
|
if (tag == "beginText") {
|
|
|
|
Text* text = new Text(tl->score());
|
|
|
|
readText114(e, text, tl);
|
|
|
|
tl->setBeginText(text->xmlText());
|
2018-10-25 15:43:59 +02:00
|
|
|
tl->setPropertyFlags(Pid::BEGIN_TEXT, PropertyFlags::UNSTYLED);
|
2018-05-14 10:07:46 +02:00
|
|
|
delete text;
|
|
|
|
}
|
|
|
|
else if (tag == "continueText") {
|
|
|
|
Text* text = new Text(tl->score());
|
|
|
|
readText114(e, text, tl);
|
|
|
|
tl->setContinueText(text->xmlText());
|
2018-10-25 15:43:59 +02:00
|
|
|
tl->setPropertyFlags(Pid::CONTINUE_TEXT, PropertyFlags::UNSTYLED);
|
2018-05-14 10:07:46 +02:00
|
|
|
delete text;
|
|
|
|
}
|
|
|
|
else if (tag == "endText") {
|
|
|
|
Text* text = new Text(tl->score());
|
|
|
|
readText114(e, text, tl);
|
|
|
|
tl->setEndText(text->xmlText());
|
2018-10-25 15:43:59 +02:00
|
|
|
tl->setPropertyFlags(Pid::END_TEXT, PropertyFlags::UNSTYLED);
|
2018-05-14 10:07:46 +02:00
|
|
|
delete text;
|
|
|
|
}
|
|
|
|
else if (tag == "beginHook")
|
|
|
|
tl->setBeginHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
|
|
|
|
else if (tag == "endHook")
|
|
|
|
tl->setEndHookType(e.readBool() ? HookType::HOOK_90 : HookType::NONE);
|
|
|
|
else if (tag == "beginHookType")
|
|
|
|
tl->setBeginHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
|
|
|
|
else if (tag == "endHookType")
|
|
|
|
tl->setEndHookType(e.readInt() == 0 ? HookType::HOOK_90 : HookType::HOOK_45);
|
|
|
|
else if (tag == "Segment") {
|
|
|
|
LineSegment* ls = tl->createLineSegment();
|
|
|
|
ls->setTrack(tl->track()); // needed in read to get the right staff mag
|
|
|
|
readLineSegment114(e, ls);
|
|
|
|
// in v1.x "visible" is a property of the segment only;
|
|
|
|
// we must ensure that it propagates also to the parent element.
|
|
|
|
// That's why the visibility is set after adding the segment
|
|
|
|
// to the corresponding spanner
|
|
|
|
ls->setVisible(ls->visible());
|
2018-10-18 11:53:01 +02:00
|
|
|
ls->setOffset(QPointF()); // ignore offsets
|
2018-05-14 10:07:46 +02:00
|
|
|
ls->setAutoplace(true);
|
|
|
|
tl->add(ls);
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (tl->readProperties(e))
|
2018-05-14 10:07:46 +02:00
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readVolta114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readVolta114(XmlReader& e, Volta* volta)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "endings") {
|
|
|
|
QString s = e.readElementText();
|
|
|
|
QStringList sl = s.split(",", QString::SkipEmptyParts);
|
|
|
|
volta->endings().clear();
|
|
|
|
for (const QString& l : sl) {
|
|
|
|
int i = l.simplified().toInt();
|
|
|
|
volta->endings().append(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "subtype") {
|
|
|
|
e.readInt(); // TODO
|
|
|
|
}
|
|
|
|
else if (tag == "lineWidth") {
|
|
|
|
volta->setLineWidth(e.readDouble() * volta->spatium());
|
2018-10-25 15:43:59 +02:00
|
|
|
volta->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
|
|
|
else if (!readTextLineProperties114(e, volta))
|
|
|
|
e.unknown();
|
|
|
|
}
|
2018-10-18 11:53:01 +02:00
|
|
|
volta->setOffset(QPointF()); // ignore offsets
|
2018-05-14 10:07:46 +02:00
|
|
|
volta->setAutoplace(true);
|
|
|
|
}
|
|
|
|
|
2018-08-03 14:23:16 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readOttava114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readOttava114(XmlReader& e, Ottava* ottava)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "subtype") {
|
|
|
|
QString s = e.readElementText();
|
|
|
|
bool ok;
|
|
|
|
int idx = s.toInt(&ok);
|
|
|
|
if (!ok) {
|
|
|
|
idx = int(OttavaType::OTTAVA_8VA);
|
|
|
|
for (unsigned i = 0; i < sizeof(ottavaDefault)/sizeof(*ottavaDefault); ++i) {
|
|
|
|
if (s == ottavaDefault[i].name) {
|
|
|
|
idx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-26 10:41:07 +02:00
|
|
|
//subtype are now in a different order...
|
|
|
|
if (idx == 1)
|
|
|
|
idx = 2;
|
|
|
|
else if (idx == 2)
|
|
|
|
idx = 1;
|
2018-08-03 14:23:16 +02:00
|
|
|
ottava->setOttavaType(OttavaType(idx));
|
|
|
|
}
|
2018-10-26 10:41:07 +02:00
|
|
|
else if (tag == "numbersOnly")
|
2018-08-03 14:23:16 +02:00
|
|
|
ottava->setNumbersOnly(e.readInt());
|
2018-10-26 10:41:07 +02:00
|
|
|
else if (tag == "lineWidth")
|
|
|
|
ottava->readProperty(e, Pid::LINE_WIDTH);
|
|
|
|
else if (tag == "lineStyle")
|
|
|
|
ottava->readProperty(e, Pid::LINE_STYLE);
|
2018-08-03 14:23:16 +02:00
|
|
|
else if (tag == "beginSymbol") { // obsolete
|
|
|
|
}
|
|
|
|
else if (tag == "continueSymbol") { // obsolete
|
|
|
|
}
|
|
|
|
else if (!readTextLineProperties114(e, ottava))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
//---------------------------------------------------------
|
2018-08-13 13:51:44 +02:00
|
|
|
// resolveSymCompatibility
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static QString resolveSymCompatibility(SymId i, QString programVersion)
|
|
|
|
{
|
|
|
|
if (!programVersion.isEmpty() && programVersion < "1.1")
|
|
|
|
i = SymId(int(i) + 5);
|
|
|
|
switch (int(i)) {
|
|
|
|
case 197:
|
|
|
|
return "keyboardPedalPed";
|
|
|
|
case 191:
|
|
|
|
return "keyboardPedalUp";
|
|
|
|
case 193:
|
|
|
|
return "noSym"; //SymId(pedaldotSym);
|
|
|
|
case 192:
|
|
|
|
return "noSym"; //SymId(pedaldashSym);
|
|
|
|
case 139:
|
|
|
|
return "ornamentTrill";
|
|
|
|
default:
|
|
|
|
return "noSym";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readTextLine114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readTextLine114(XmlReader& e, TextLine* textLine)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
|
|
|
|
if (tag == "lineVisible")
|
2018-10-18 11:53:01 +02:00
|
|
|
textLine->setLineVisible(e.readBool());
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "beginHookHeight") {
|
|
|
|
textLine->setBeginHookHeight(Spatium(e.readDouble()));
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "endHookHeight" || tag == "hookHeight") { // hookHeight is obsolete
|
|
|
|
textLine->setEndHookHeight(Spatium(e.readDouble()));
|
2018-10-25 15:43:59 +02:00
|
|
|
textLine->setPropertyFlags(Pid::END_HOOK_HEIGHT, PropertyFlags::UNSTYLED);
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "hookUp") // obsolete
|
|
|
|
textLine->setEndHookHeight(Spatium(qreal(-1.0)));
|
|
|
|
else if (tag == "beginSymbol" || tag == "symbol") { // "symbol" is obsolete
|
|
|
|
QString text(e.readElementText());
|
|
|
|
textLine->setBeginText(QString("<sym>%1</sym>").arg(
|
|
|
|
text[0].isNumber()
|
|
|
|
? resolveSymCompatibility(SymId(text.toInt()), textLine->score()->mscoreVersion())
|
|
|
|
: text));
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "continueSymbol") {
|
|
|
|
QString text(e.readElementText());
|
|
|
|
textLine->setContinueText(QString("<sym>%1</sym>").arg(
|
|
|
|
text[0].isNumber()
|
|
|
|
? resolveSymCompatibility(SymId(text.toInt()), textLine->score()->mscoreVersion())
|
|
|
|
: text));
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "endSymbol") {
|
|
|
|
QString text(e.readElementText());
|
|
|
|
textLine->setEndText(QString("<sym>%1</sym>").arg(
|
|
|
|
text[0].isNumber()
|
|
|
|
? resolveSymCompatibility(SymId(text.toInt()), textLine->score()->mscoreVersion())
|
|
|
|
: text));
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "beginSymbolOffset") // obsolete
|
|
|
|
e.readPoint();
|
|
|
|
else if (tag == "continueSymbolOffset") // obsolete
|
|
|
|
e.readPoint();
|
|
|
|
else if (tag == "endSymbolOffset") // obsolete
|
|
|
|
e.readPoint();
|
|
|
|
else if (tag == "beginTextPlace")
|
|
|
|
textLine->setBeginTextPlace(readPlacement(e));
|
|
|
|
else if (tag == "continueTextPlace")
|
|
|
|
textLine->setContinueTextPlace(readPlacement(e));
|
|
|
|
else if (tag == "endTextPlace")
|
|
|
|
textLine->setEndTextPlace(readPlacement(e));
|
|
|
|
else if (!readTextLineProperties114(e, textLine))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
2018-08-15 08:56:32 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readPedal114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readPedal114(XmlReader& e, Pedal* pedal)
|
|
|
|
{
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
2018-10-18 11:53:01 +02:00
|
|
|
if (tag == "beginSymbol"
|
|
|
|
|| tag == "beginSymbolOffset"
|
|
|
|
|| tag == "endSymbol"
|
2018-08-15 08:56:32 +02:00
|
|
|
|| tag == "endSymbolOffset"
|
|
|
|
|| tag == "subtype"
|
|
|
|
)
|
|
|
|
e.skipCurrentElement();
|
|
|
|
else if (tag == "endHookHeight" || tag == "hookHeight") { // hookHeight is obsolete
|
|
|
|
pedal->setEndHookHeight(Spatium(e.readDouble()));
|
2018-10-25 15:43:59 +02:00
|
|
|
pedal->setPropertyFlags(Pid::END_HOOK_HEIGHT, PropertyFlags::UNSTYLED);
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-15 08:56:32 +02:00
|
|
|
else if (tag == "lineWidth") {
|
|
|
|
pedal->setLineWidth(qreal(e.readDouble()));
|
2018-10-25 15:43:59 +02:00
|
|
|
pedal->setPropertyFlags(Pid::LINE_WIDTH, PropertyFlags::UNSTYLED);
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-15 08:56:32 +02:00
|
|
|
else if (tag == "lineStyle") {
|
|
|
|
pedal->setLineStyle(Qt::PenStyle(e.readInt()));
|
2018-10-25 15:43:59 +02:00
|
|
|
pedal->setPropertyFlags(Pid::LINE_STYLE, PropertyFlags::UNSTYLED);
|
2018-10-18 11:53:01 +02:00
|
|
|
}
|
2018-08-15 08:56:32 +02:00
|
|
|
else if (!readTextLineProperties114(e, pedal))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
pedal->setBeginText("<sym>keyboardPedalPed</sym>");
|
|
|
|
}
|
|
|
|
|
2018-08-13 13:51:44 +02:00
|
|
|
//---------------------------------------------------------
|
2018-05-14 10:07:46 +02:00
|
|
|
// readHarmony114
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readHarmony114(XmlReader& e, Harmony* h)
|
|
|
|
{
|
|
|
|
// convert table to tpc values
|
|
|
|
static const int table[] = {
|
|
|
|
14, 9, 16, 11, 18, 13, 8, 15, 10, 17, 12, 19
|
|
|
|
};
|
|
|
|
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "base") {
|
|
|
|
if (h->score()->mscVersion() >= 106)
|
|
|
|
h->setBaseTpc(e.readInt());
|
|
|
|
else
|
|
|
|
h->setBaseTpc(table[e.readInt()-1]);
|
|
|
|
}
|
|
|
|
else if (tag == "baseCase")
|
|
|
|
h->setBaseCase(static_cast<NoteCaseType>(e.readInt()));
|
|
|
|
else if (tag == "extension")
|
|
|
|
h->setId(e.readInt());
|
|
|
|
else if (tag == "name")
|
|
|
|
h->setTextName(e.readElementText());
|
|
|
|
else if (tag == "root") {
|
|
|
|
if (h->score()->mscVersion() >= 106)
|
|
|
|
h->setRootTpc(e.readInt());
|
|
|
|
else
|
|
|
|
h->setRootTpc(table[e.readInt()-1]);
|
|
|
|
}
|
|
|
|
else if (tag == "rootCase")
|
|
|
|
h->setRootCase(static_cast<NoteCaseType>(e.readInt()));
|
|
|
|
else if (tag == "degree") {
|
|
|
|
int degreeValue = 0;
|
|
|
|
int degreeAlter = 0;
|
|
|
|
QString degreeType = "";
|
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
|
|
|
if (t == "degree-value")
|
2018-05-14 10:07:46 +02:00
|
|
|
degreeValue = e.readInt();
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "degree-alter")
|
2018-05-14 10:07:46 +02:00
|
|
|
degreeAlter = e.readInt();
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "degree-type")
|
2018-05-14 10:07:46 +02:00
|
|
|
degreeType = e.readElementText();
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
if (degreeValue <= 0 || degreeValue > 13
|
|
|
|
|| degreeAlter < -2 || degreeAlter > 2
|
|
|
|
|| (degreeType != "add" && degreeType != "alter" && degreeType != "subtract")) {
|
|
|
|
qDebug("incorrect degree: degreeValue=%d degreeAlter=%d degreeType=%s",
|
|
|
|
degreeValue, degreeAlter, qPrintable(degreeType));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (degreeType == "add")
|
|
|
|
h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ADD));
|
|
|
|
else if (degreeType == "alter")
|
|
|
|
h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::ALTER));
|
|
|
|
else if (degreeType == "subtract")
|
|
|
|
h->addDegree(HDegree(degreeValue, degreeAlter, HDegreeType::SUBTRACT));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "leftParen") {
|
|
|
|
h->setLeftParen(true);
|
|
|
|
e.readNext();
|
|
|
|
}
|
|
|
|
else if (tag == "rightParen") {
|
|
|
|
h->setRightParen(true);
|
|
|
|
e.readNext();
|
|
|
|
}
|
|
|
|
else if (!readTextProperties(e, h, h))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: now that we can render arbitrary chords,
|
|
|
|
// we could try to construct a full representation from a degree list.
|
|
|
|
// These will typically only exist for chords imported from MusicXML prior to MuseScore 2.0
|
|
|
|
// or constructed in the Chord Symbol Properties dialog.
|
|
|
|
|
|
|
|
if (h->rootTpc() != Tpc::TPC_INVALID) {
|
|
|
|
if (h->id() > 0) {
|
|
|
|
// positive id will happen only for scores that were created with explicit chord lists
|
|
|
|
// lookup id in chord list and generate new description if necessary
|
|
|
|
h->getDescription();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// default case: look up by name
|
|
|
|
// description will be found for any chord already read in this score
|
|
|
|
// and we will generate a new one if necessary
|
|
|
|
h->getDescription(h->hTextName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (h->hTextName() == "") {
|
|
|
|
// unrecognized chords prior to 2.0 were stored as text with markup
|
|
|
|
// we need to strip away the markup
|
|
|
|
// this removes any user-applied formatting,
|
|
|
|
// but we no longer support user-applied formatting for chord symbols anyhow
|
|
|
|
// with any luck, the resulting text will be parseable now, so give it a shot
|
|
|
|
// h->createLayout();
|
|
|
|
QString s = h->plainText();
|
|
|
|
if (!s.isEmpty()) {
|
|
|
|
h->setHarmony(s);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// empty text could also indicate a root-less slash chord ("/E")
|
|
|
|
// we'll fall through and render it normally
|
|
|
|
}
|
|
|
|
|
|
|
|
// render chord from description (or _textName)
|
|
|
|
h->render();
|
|
|
|
}
|
|
|
|
|
2016-09-13 09:39:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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);
|
|
|
|
|
2016-12-12 14:55:35 +01:00
|
|
|
m->createStaves(staffIdx);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
// tick is obsolete
|
|
|
|
if (e.hasAttribute("tick"))
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(Fraction::fromTicks(m->score()->fileDivision(e.intAttribute("tick"))));
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
if (e.hasAttribute("len")) {
|
|
|
|
QStringList sl = e.attribute("len").split('/');
|
|
|
|
if (sl.size() == 2)
|
2019-01-30 15:13:54 +01:00
|
|
|
m->setTicks(Fraction(sl[0].toInt(), sl[1].toInt()));
|
2016-09-13 09:39:50 +02:00
|
|
|
else
|
|
|
|
qDebug("illegal measure size <%s>", qPrintable(e.attribute("len")));
|
2019-01-30 15:13:54 +01:00
|
|
|
m->score()->sigmap()->add(m->tick().ticks(), SigEvent(m->ticks(), m->timesig()));
|
|
|
|
m->score()->sigmap()->add(m->endTick().ticks(), SigEvent(m->timesig()));
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction lastTick = e.tick();
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
|
|
|
|
if (tag == "move")
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(e.readFraction() + m->tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "tick") {
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(Fraction::fromTicks(m->score()->fileDivision(e.readInt())));
|
2016-09-13 09:39:50 +02:00
|
|
|
lastTick = e.tick();
|
|
|
|
}
|
|
|
|
else if (tag == "BarLine") {
|
|
|
|
BarLine* barLine = new BarLine(m->score());
|
|
|
|
barLine->setTrack(e.track());
|
2018-03-27 15:36:00 +02:00
|
|
|
barLine->resetProperty(Pid::BARLINE_SPAN);
|
|
|
|
barLine->resetProperty(Pid::BARLINE_SPAN_FROM);
|
|
|
|
barLine->resetProperty(Pid::BARLINE_SPAN_TO);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& tg(e.name());
|
|
|
|
if (tg == "subtype") {
|
2018-02-21 13:30:07 +01:00
|
|
|
BarLineType t = BarLineType::NORMAL;
|
2016-09-13 09:39:50 +02:00
|
|
|
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:
|
2019-04-06 07:54:38 +02:00
|
|
|
t = BarLineType::END_START_REPEAT;
|
2016-09-13 09:39:50 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
barLine->setBarLineType(t);
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!barLine->Element::readProperties(e))
|
2016-09-13 09:39:50 +02:00
|
|
|
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
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
SegmentType st;
|
2016-09-13 09:39:50 +02:00
|
|
|
if ((e.tick() != m->tick()) && (e.tick() != m->endTick()))
|
2017-03-08 13:12:26 +01:00
|
|
|
st = SegmentType::BarLine;
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (barLine->barLineType() == BarLineType::START_REPEAT && e.tick() == m->tick())
|
2017-03-08 13:12:26 +01:00
|
|
|
st = SegmentType::StartRepeatBarLine;
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (e.tick() == m->tick() && segment == 0)
|
2017-03-08 13:12:26 +01:00
|
|
|
st = SegmentType::BeginBarLine;
|
2016-09-13 09:39:50 +02:00
|
|
|
else
|
2017-03-08 13:12:26 +01:00
|
|
|
st = SegmentType::EndBarLine;
|
2016-09-13 09:39:50 +02:00
|
|
|
segment = m->getSegment(st, e.tick());
|
|
|
|
segment->add(barLine);
|
|
|
|
}
|
|
|
|
else if (tag == "Chord") {
|
|
|
|
Chord* chord = new Chord(m->score());
|
|
|
|
chord->setTrack(e.track());
|
2018-05-14 10:07:46 +02:00
|
|
|
readChord(m, chord, e);
|
|
|
|
if (!chord->segment())
|
|
|
|
chord->setParent(m->getSegment(SegmentType::ChordRest, e.tick()));
|
|
|
|
segment = chord->segment();
|
2016-09-13 09:39:50 +02:00
|
|
|
if (chord->noteType() != NoteType::NORMAL) {
|
|
|
|
graceNotes.push_back(chord);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
segment->add(chord);
|
2017-03-08 13:12:26 +01:00
|
|
|
Q_ASSERT(segment->segmentType() == SegmentType::ChordRest);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < graceNotes.size(); ++i) {
|
|
|
|
Chord* gc = graceNotes[i];
|
|
|
|
gc->setGraceIndex(i);
|
|
|
|
chord->add(gc);
|
|
|
|
}
|
|
|
|
graceNotes.clear();
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction crticks = chord->actualTicks();
|
2016-09-13 09:39:50 +02:00
|
|
|
|
2018-05-14 23:06:56 +02:00
|
|
|
if (chord->tremolo()) {
|
2016-09-13 09:39:50 +02:00
|
|
|
Tremolo* tremolo = chord->tremolo();
|
|
|
|
if (tremolo->twoNotes()) {
|
|
|
|
int track = chord->track();
|
|
|
|
Segment* ss = 0;
|
2017-03-08 13:12:26 +01:00
|
|
|
for (Segment* ps = m->first(SegmentType::ChordRest); ps; ps = ps->next(SegmentType::ChordRest)) {
|
2016-09-13 09:39:50 +02:00
|
|
|
if (ps->tick() >= e.tick())
|
|
|
|
break;
|
|
|
|
if (ps->element(track))
|
|
|
|
ss = ps;
|
|
|
|
}
|
|
|
|
Chord* pch = 0; // previous chord
|
|
|
|
if (ss) {
|
2017-12-20 16:49:30 +01:00
|
|
|
ChordRest* cr = toChordRest(ss->element(track));
|
2017-01-18 14:16:33 +01:00
|
|
|
if (cr && cr->type() == ElementType::CHORD)
|
2017-12-20 16:49:30 +01:00
|
|
|
pch = toChord(cr);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
if (pch) {
|
|
|
|
tremolo->setParent(pch);
|
|
|
|
pch->setTremolo(tremolo);
|
|
|
|
chord->setTremolo(0);
|
|
|
|
// force duration to half
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction pts(timeStretch * pch->globalTicks());
|
|
|
|
pch->setTicks(pts * Fraction(1,2));
|
|
|
|
chord->setTicks(crticks * Fraction(1,2));
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
qDebug("tremolo: first note not found");
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
crticks = crticks * Fraction(1,2);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
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);
|
2019-01-30 15:13:54 +01:00
|
|
|
rest->setTicks(m->timesig()/timeStretch);
|
2016-09-13 09:39:50 +02:00
|
|
|
rest->setTrack(e.track());
|
2018-05-14 10:07:46 +02:00
|
|
|
readRest(m, rest, e);
|
|
|
|
if (!rest->segment())
|
|
|
|
rest->setParent(m->getSegment(SegmentType::ChordRest, e.tick()));
|
|
|
|
segment = rest->segment();
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(rest);
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
if (!rest->ticks().isValid()) // hack
|
|
|
|
rest->setTicks(m->timesig()/timeStretch);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
lastTick = e.tick();
|
|
|
|
e.incTick(rest->actualTicks());
|
|
|
|
}
|
|
|
|
else if (tag == "Breath") {
|
|
|
|
Breath* breath = new Breath(m->score());
|
|
|
|
breath->setTrack(e.track());
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = e.tick();
|
2018-08-27 22:57:15 +02:00
|
|
|
breath->read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
// 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
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction prevTick;
|
2016-09-13 09:39:50 +02:00
|
|
|
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
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* prev = m->findSegment(SegmentType::ChordRest, prevTick);
|
2016-09-13 09:39:50 +02:00
|
|
|
if (prev) {
|
|
|
|
// find chordrest
|
2017-12-20 16:49:30 +01:00
|
|
|
ChordRest* lastCR = toChordRest(prev->element(e.track()));
|
2016-09-13 09:39:50 +02:00
|
|
|
if (lastCR)
|
|
|
|
tick = prevTick + lastCR->actualTicks();
|
|
|
|
}
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::Breath, tick);
|
2016-09-13 09:39:50 +02:00
|
|
|
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());
|
2018-08-27 22:57:15 +02:00
|
|
|
readSlur206(e, sl);
|
2016-09-13 09:39:50 +02:00
|
|
|
//
|
|
|
|
// 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") {
|
2017-12-20 16:49:30 +01:00
|
|
|
Spanner* sp = toSpanner(Element::name2Element(tag, m->score()));
|
2016-09-13 09:39:50 +02:00
|
|
|
sp->setTrack(e.track());
|
|
|
|
sp->setTick(e.tick());
|
|
|
|
// ?? sp->setAnchor(Spanner::Anchor::SEGMENT);
|
2018-05-14 10:07:46 +02:00
|
|
|
if (tag == "Volta")
|
|
|
|
readVolta114(e, toVolta(sp));
|
|
|
|
else
|
|
|
|
readTextLine206(e, toTextLineBase(sp));
|
2016-09-13 09:39:50 +02:00
|
|
|
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());
|
2018-08-27 22:57:15 +02:00
|
|
|
readRest(m, rm, e);
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(rm);
|
2019-01-30 15:13:54 +01:00
|
|
|
if (rm->actualTicks().isZero()) // might happen with 1.3 scores
|
|
|
|
rm->setTicks(m->ticks());
|
2016-09-13 09:39:50 +02:00
|
|
|
lastTick = e.tick();
|
|
|
|
e.incTick(m->ticks());
|
|
|
|
}
|
|
|
|
else if (tag == "Clef") {
|
|
|
|
Clef* clef = new Clef(m->score());
|
|
|
|
clef->setTrack(e.track());
|
2016-09-28 16:39:48 +02:00
|
|
|
readClef(clef, e);
|
|
|
|
if (m->score()->mscVersion() < 113)
|
2018-10-18 11:53:01 +02:00
|
|
|
clef->setOffset(QPointF());
|
2016-09-13 09:39:50 +02:00
|
|
|
clef->setGenerated(false);
|
2016-09-28 16:39:48 +02:00
|
|
|
// MS3 doesn't support wrong clef for staff type: Default to G
|
2016-12-13 13:16:17 +01:00
|
|
|
bool isDrumStaff = staff->isDrumStaff(e.tick());
|
2016-09-28 16:39:48 +02:00
|
|
|
if (clef->clefType() == ClefType::TAB
|
2016-12-13 13:16:17 +01:00
|
|
|
|| (clef->clefType() == ClefType::PERC && !isDrumStaff)
|
|
|
|
|| (clef->clefType() != ClefType::PERC && isDrumStaff)) {
|
2016-09-28 16:39:48 +02:00
|
|
|
clef->setClefType(ClefType::G);
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->clefList().erase(e.tick().ticks());
|
|
|
|
staff->clefList().insert(std::pair<int,ClefType>(e.tick().ticks(), ClefType::G));
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
2016-10-25 17:30:55 +02:00
|
|
|
|
|
|
|
// 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;
|
2019-01-30 15:13:54 +01:00
|
|
|
for (Segment* s = m->segments().first(); s && s->rtick().isZero(); s = s->next()) {
|
2016-10-25 17:30:55 +02:00
|
|
|
if (s->isKeySigType() || s->isTimeSigType()) {
|
|
|
|
// hack: there may be other segment types which should
|
|
|
|
// generate a clef at current position
|
|
|
|
header = false;
|
|
|
|
break;
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(header ? SegmentType::HeaderClef : SegmentType::Clef, e.tick());
|
2016-10-25 17:30:55 +02:00
|
|
|
segment->add(clef);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "TimeSig") {
|
|
|
|
TimeSig* ts = new TimeSig(m->score());
|
|
|
|
ts->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
ts->read(e);
|
2018-02-14 20:20:21 +01:00
|
|
|
// if time sig not at beginning of measure => courtesy time sig
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction currTick = e.tick();
|
2016-09-13 09:39:50 +02:00
|
|
|
bool courtesySig = (currTick > m->tick());
|
|
|
|
if (courtesySig) {
|
|
|
|
// if courtesy sig., just add it without map processing
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::TimeSigAnnounce, currTick);
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(ts);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if 'real' time sig., do full process
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::TimeSig, currTick);
|
2016-09-13 09:39:50 +02:00
|
|
|
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());
|
2018-08-27 22:57:15 +02:00
|
|
|
ks->read(e);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction curTick = e.tick();
|
|
|
|
if (!ks->isCustom() && !ks->isAtonal() && ks->key() == Key::C && curTick.isZero()) {
|
2016-09-13 09:39:50 +02:00
|
|
|
// 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());
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(ks);
|
|
|
|
if (!courtesySig)
|
|
|
|
staff->setKey(curTick, ks->keySigEvent());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "Lyrics") {
|
2018-05-14 10:07:46 +02:00
|
|
|
Lyrics* l = new Lyrics(m->score());
|
|
|
|
l->setTrack(e.track());
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
int iEndTick = 0; // used for backward compatibility
|
2018-05-14 10:07:46 +02:00
|
|
|
Text* _verseNumber = 0;
|
|
|
|
|
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
|
|
|
if (t == "no")
|
2018-05-14 10:07:46 +02:00
|
|
|
l->setNo(e.readInt());
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "syllabic") {
|
2018-05-14 10:07:46 +02:00
|
|
|
QString val(e.readElementText());
|
|
|
|
if (val == "single")
|
|
|
|
l->setSyllabic(Lyrics::Syllabic::SINGLE);
|
|
|
|
else if (val == "begin")
|
|
|
|
l->setSyllabic(Lyrics::Syllabic::BEGIN);
|
|
|
|
else if (val == "end")
|
|
|
|
l->setSyllabic(Lyrics::Syllabic::END);
|
|
|
|
else if (val == "middle")
|
|
|
|
l->setSyllabic(Lyrics::Syllabic::MIDDLE);
|
|
|
|
else
|
|
|
|
qDebug("bad syllabic property");
|
|
|
|
}
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "endTick") { // obsolete
|
2018-05-14 10:07:46 +02:00
|
|
|
// store <endTick> tag value until a <ticks> tag has been read
|
|
|
|
// which positions this lyrics element in the score
|
|
|
|
iEndTick = e.readInt();
|
|
|
|
}
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "ticks")
|
2019-01-30 15:13:54 +01:00
|
|
|
l->setTicks(Fraction::fromTicks(e.readInt()));
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "Number") { // obsolete
|
2018-05-14 10:07:46 +02:00
|
|
|
_verseNumber = new Text(l->score());
|
2018-08-27 22:57:15 +02:00
|
|
|
readText114(e, _verseNumber, l);
|
2018-05-14 10:07:46 +02:00
|
|
|
_verseNumber->setParent(l);
|
|
|
|
}
|
|
|
|
else if (!readTextProperties(e, l, l))
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
// if any endTick, make it relative to current tick
|
|
|
|
if (iEndTick) {
|
2019-01-30 15:13:54 +01:00
|
|
|
l->setTicks(Fraction::fromTicks(iEndTick - e.tick().ticks()));
|
2018-05-14 10:07:46 +02:00
|
|
|
// qDebug("Lyrics::endTick: %d ticks %d", iEndTick, _ticks);
|
|
|
|
}
|
|
|
|
if (_verseNumber) {
|
|
|
|
// TODO: add text to main text
|
|
|
|
}
|
|
|
|
|
|
|
|
delete _verseNumber;
|
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2018-05-14 10:07:46 +02:00
|
|
|
ChordRest* cr = toChordRest(segment->element(l->track()));
|
2016-09-13 09:39:50 +02:00
|
|
|
if (!cr)
|
2017-12-20 16:49:30 +01:00
|
|
|
cr = toChordRest(segment->element(e.track())); // in case lyric itself has bad track info
|
2016-09-13 09:39:50 +02:00
|
|
|
if (!cr)
|
|
|
|
qDebug("Internal error: no chord/rest for lyrics");
|
|
|
|
else
|
2018-05-14 10:07:46 +02:00
|
|
|
cr->add(l);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "Text") {
|
2017-08-16 10:53:03 +02:00
|
|
|
StaffText* t = new StaffText(m->score());
|
2016-09-13 09:39:50 +02:00
|
|
|
t->setTrack(e.track());
|
2017-08-16 10:53:03 +02:00
|
|
|
readStaffText(t, e);
|
2016-09-13 09:39:50 +02:00
|
|
|
if (t->empty()) {
|
|
|
|
qDebug("reading empty text: deleted");
|
|
|
|
delete t;
|
|
|
|
}
|
|
|
|
else {
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "Dynamic") {
|
|
|
|
Dynamic* dyn = new Dynamic(m->score());
|
|
|
|
dyn->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
dyn->read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
dyn->setDynamicType(dyn->xmlText());
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(dyn);
|
|
|
|
}
|
2017-08-16 10:53:03 +02:00
|
|
|
else if (tag == "Tempo") {
|
|
|
|
TempoText* t = new TempoText(m->score());
|
|
|
|
t->setTrack(e.track());
|
|
|
|
readTempoText(t, e);
|
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
|
|
|
segment->add(t);
|
|
|
|
}
|
|
|
|
else if (tag == "StaffText") {
|
|
|
|
StaffText* t = new StaffText(m->score());
|
|
|
|
t->setTrack(e.track());
|
|
|
|
readStaffText(t, e);
|
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
|
|
|
segment->add(t);
|
|
|
|
}
|
2018-05-14 10:07:46 +02:00
|
|
|
else if (tag == "Harmony") {
|
|
|
|
Harmony* h = new Harmony(m->score());
|
|
|
|
h->setTrack(e.track());
|
|
|
|
readHarmony114(e, h);
|
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
|
|
|
segment->add(h);
|
|
|
|
}
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "Harmony"
|
|
|
|
|| tag == "FretDiagram"
|
|
|
|
|| tag == "TremoloBar"
|
|
|
|
|| tag == "Symbol"
|
|
|
|
|| tag == "StaffText"
|
|
|
|
|| tag == "RehearsalMark"
|
|
|
|
|| tag == "InstrumentChange"
|
|
|
|
|| tag == "StaffState"
|
|
|
|
) {
|
|
|
|
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
|
2017-01-18 14:16:33 +01:00
|
|
|
if (el->type() == ElementType::SYMBOL)
|
2016-09-13 09:39:50 +02:00
|
|
|
el->setParent(m); // this will get reset when adding to segment
|
|
|
|
el->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
el->read(e);
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(el);
|
|
|
|
}
|
2018-07-26 13:14:06 +02:00
|
|
|
else if (tag == "Jump") {
|
|
|
|
Jump* j = new Jump(m->score());
|
|
|
|
j->setTrack(e.track());
|
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
|
|
|
if (t == "jumpTo")
|
2018-07-26 13:14:06 +02:00
|
|
|
j->setJumpTo(e.readElementText());
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "playUntil")
|
2018-07-26 13:14:06 +02:00
|
|
|
j->setPlayUntil(e.readElementText());
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "continueAt")
|
2018-07-26 13:14:06 +02:00
|
|
|
j->setContinueAt(e.readElementText());
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "playRepeats")
|
2018-07-26 13:14:06 +02:00
|
|
|
j->setPlayRepeats(e.readBool());
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "subtype")
|
2018-07-26 13:14:06 +02:00
|
|
|
e.readInt();
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!j->TextBase::readProperties(e))
|
2018-07-26 13:14:06 +02:00
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
m->add(j);
|
|
|
|
}
|
|
|
|
else if (tag == "Marker") {
|
|
|
|
Marker* a = new Marker(m->score());
|
|
|
|
a->setTrack(e.track());
|
2016-10-10 18:37:28 +02:00
|
|
|
|
2018-07-26 13:14:06 +02:00
|
|
|
Marker::Type mt = Marker::Type::SEGNO;
|
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
|
|
|
if (t == "subtype") {
|
2018-07-26 13:14:06 +02:00
|
|
|
QString s(e.readElementText());
|
|
|
|
a->setLabel(s);
|
|
|
|
mt = a->markerType(s);
|
2016-10-07 22:45:12 +02:00
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (!a->TextBase::readProperties(e))
|
2018-07-26 13:14:06 +02:00
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
a->setMarkerType(mt);
|
|
|
|
|
|
|
|
if (a->markerType() == Marker::Type::SEGNO || a->markerType() == Marker::Type::CODA ||
|
|
|
|
a->markerType() == Marker::Type::VARCODA || a->markerType() == Marker::Type::CODETTA) {
|
|
|
|
// force the marker type for correct display
|
|
|
|
a->setXmlText("");
|
|
|
|
a->setMarkerType(a->markerType());
|
2018-08-01 11:46:07 +02:00
|
|
|
//TODO::ws a->initElementStyle(Tid::REPEAT_LEFT);
|
2016-10-07 22:45:12 +02:00
|
|
|
}
|
2018-07-26 13:14:06 +02:00
|
|
|
m->add(a);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "Image") {
|
|
|
|
if (MScore::noImages)
|
|
|
|
e.skipCurrentElement();
|
|
|
|
else {
|
|
|
|
Element* el = Element::name2Element(tag, m->score());
|
|
|
|
el->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
el->read(e);
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::ChordRest, e.tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
2017-03-08 13:12:26 +01:00
|
|
|
segment = m->getSegment(SegmentType::BeginBarLine, m->tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
segment->add(barLine);
|
|
|
|
}
|
|
|
|
else if (tag == "Tuplet") {
|
|
|
|
Tuplet* tuplet = new Tuplet(m->score());
|
|
|
|
tuplet->setTrack(e.track());
|
|
|
|
tuplet->setTick(e.tick());
|
|
|
|
tuplet->setParent(m);
|
2016-11-09 17:12:52 +01:00
|
|
|
readTuplet(tuplet, e);
|
2016-09-13 09:39:50 +02:00
|
|
|
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") {
|
2016-12-12 14:55:35 +01:00
|
|
|
if (!m->vspacerDown(staffIdx)) {
|
2016-09-13 09:39:50 +02:00
|
|
|
Spacer* spacer = new Spacer(m->score());
|
|
|
|
spacer->setSpacerType(SpacerType::DOWN);
|
|
|
|
spacer->setTrack(staffIdx * VOICES);
|
|
|
|
m->add(spacer);
|
|
|
|
}
|
2016-12-12 14:55:35 +01:00
|
|
|
m->vspacerDown(staffIdx)->setGap(e.readDouble() * _spatium);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "vspacer" || tag == "vspacerUp") {
|
2016-12-12 14:55:35 +01:00
|
|
|
if (!m->vspacerUp(staffIdx)) {
|
2016-09-13 09:39:50 +02:00
|
|
|
Spacer* spacer = new Spacer(m->score());
|
|
|
|
spacer->setSpacerType(SpacerType::UP);
|
|
|
|
spacer->setTrack(staffIdx * VOICES);
|
|
|
|
m->add(spacer);
|
|
|
|
}
|
2016-12-12 14:55:35 +01:00
|
|
|
m->vspacerUp(staffIdx)->setGap(e.readDouble() * _spatium);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "visible")
|
2016-12-12 14:55:35 +01:00
|
|
|
m->setStaffVisible(staffIdx, e.readInt());
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "slashStyle")
|
2019-08-19 15:07:30 +02:00
|
|
|
m->setStaffStemless(staffIdx, e.readInt());
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "Beam") {
|
|
|
|
Beam* beam = new Beam(m->score());
|
|
|
|
beam->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
beam->read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
beam->setParent(0);
|
|
|
|
e.addBeam(beam);
|
|
|
|
}
|
2017-08-16 10:53:03 +02:00
|
|
|
else if (tag == "Segment") {
|
2018-08-27 22:57:15 +02:00
|
|
|
segment->read(e);
|
2017-08-16 10:53:03 +02:00
|
|
|
while (e.readNextStartElement()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
|
|
|
if (t == "off1") {
|
2017-08-16 10:53:03 +02:00
|
|
|
qreal o = e.readDouble();
|
2018-05-14 10:07:46 +02:00
|
|
|
qDebug("TODO: off1 %f", o);
|
2017-08-16 10:53:03 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "MeasureNumber") {
|
2018-10-24 10:40:03 +02:00
|
|
|
MeasureNumber* noText = new MeasureNumber(m->score());
|
2018-08-27 22:57:15 +02:00
|
|
|
readText114(e, noText, m);
|
2016-09-13 09:39:50 +02:00
|
|
|
noText->setTrack(e.track());
|
|
|
|
noText->setParent(m);
|
2016-12-12 14:55:35 +01:00
|
|
|
m->setNoText(noText->staffIdx(), noText);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "multiMeasureRest") {
|
|
|
|
m->setMMRestCount(e.readInt());
|
|
|
|
// set tick to previous measure
|
|
|
|
m->setTick(e.lastMeasure()->tick());
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(e.lastMeasure()->tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (m->MeasureBase::readProperties(e))
|
2016-09-13 09:39:50 +02:00
|
|
|
;
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
2016-11-17 14:28:05 +01:00
|
|
|
// 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()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tupletTick = tuplet->tick();
|
|
|
|
Fraction tupletDuration = tuplet->actualTicks() - Fraction::fromTicks(1);
|
2016-11-17 14:28:05 +01:00
|
|
|
std::vector<DurationElement*> tElements = tuplet->elements();
|
|
|
|
for (Tuplet* tuplet2 : e.tuplets()) {
|
2016-11-21 13:46:47 +01:00
|
|
|
if ((tuplet2->tuplet()) || (tuplet2->voice() != tuplet->voice())) // already a nested tuplet or in a different voice
|
2016-11-17 14:28:05 +01:00
|
|
|
continue;
|
2019-01-30 15:13:54 +01:00
|
|
|
// int possibleDuration = tuplet2->duration().ticks() * tuplet->ratio().denominator() / tuplet->ratio().numerator() - 1;
|
|
|
|
Fraction possibleDuration = tuplet2->ticks() * Fraction(tuplet->ratio().denominator(), (tuplet->ratio().numerator()-1));
|
|
|
|
|
2016-11-17 14:28:05 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-26 10:15:20 +02:00
|
|
|
e.checkTuplets();
|
2019-07-06 14:35:18 +02:00
|
|
|
m->connectTremolo();
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
2018-05-14 10:07:46 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readBoxProperties
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readBox(XmlReader& e, Box* b);
|
|
|
|
|
|
|
|
static bool readBoxProperties(XmlReader& e, Box* b)
|
|
|
|
{
|
|
|
|
const QStringRef& tag(e.name());
|
|
|
|
if (tag == "height")
|
|
|
|
b->setBoxHeight(Spatium(e.readDouble()));
|
|
|
|
else if (tag == "width")
|
|
|
|
b->setBoxWidth(Spatium(e.readDouble()));
|
2018-10-26 10:41:07 +02:00
|
|
|
else if (tag == "topGap")
|
|
|
|
b->readProperty(e, Pid::TOP_GAP);
|
2018-05-14 10:07:46 +02:00
|
|
|
else if (tag == "bottomGap") {
|
2018-10-26 10:41:07 +02:00
|
|
|
b->readProperty(e, Pid::BOTTOM_GAP);
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
|
|
|
else if (tag == "leftMargin")
|
|
|
|
b->setLeftMargin(e.readDouble());
|
|
|
|
else if (tag == "rightMargin")
|
|
|
|
b->setRightMargin(e.readDouble());
|
|
|
|
else if (tag == "topMargin")
|
|
|
|
b->setTopMargin(e.readDouble());
|
|
|
|
else if (tag == "bottomMargin")
|
|
|
|
b->setBottomMargin(e.readDouble());
|
|
|
|
else if (tag == "offset")
|
2018-10-18 11:53:01 +02:00
|
|
|
b->setOffset(e.readPoint() * b->spatium());
|
2018-10-26 10:41:07 +02:00
|
|
|
else if (tag == "pos") // ignore
|
2018-05-14 10:07:46 +02:00
|
|
|
e.readPoint();
|
|
|
|
else if (tag == "Text") {
|
|
|
|
Text* t;
|
|
|
|
if (b->isTBox()) {
|
|
|
|
t = toTBox(b)->text();
|
|
|
|
readText114(e, t, t);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
t = new Text(b->score());
|
|
|
|
readText114(e, t, t);
|
|
|
|
if (t->empty())
|
|
|
|
qDebug("read empty text");
|
|
|
|
else
|
|
|
|
b->add(t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "Symbol") {
|
|
|
|
Symbol* s = new Symbol(b->score());
|
2018-08-27 22:57:15 +02:00
|
|
|
s->read(e);
|
2018-05-14 10:07:46 +02:00
|
|
|
b->add(s);
|
|
|
|
}
|
|
|
|
else if (tag == "Image") {
|
|
|
|
if (MScore::noImages)
|
|
|
|
e.skipCurrentElement();
|
|
|
|
else {
|
|
|
|
Image* image = new Image(b->score());
|
|
|
|
image->setTrack(e.track());
|
2018-08-27 22:57:15 +02:00
|
|
|
image->read(e);
|
2018-05-14 10:07:46 +02:00
|
|
|
b->add(image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "HBox") {
|
|
|
|
HBox* hb = new HBox(b->score());
|
|
|
|
readBox(e, hb);
|
|
|
|
b->add(hb);
|
|
|
|
}
|
|
|
|
else if (tag == "VBox") {
|
|
|
|
VBox* vb = new VBox(b->score());
|
|
|
|
readBox(e, vb);
|
|
|
|
b->add(vb);
|
|
|
|
}
|
2018-08-27 22:57:15 +02:00
|
|
|
// else if (MeasureBase::readProperties(e))
|
2018-05-14 10:07:46 +02:00
|
|
|
// ;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-08-16 10:53:03 +02:00
|
|
|
//---------------------------------------------------------
|
2018-05-14 10:07:46 +02:00
|
|
|
// readBox
|
2017-08-16 10:53:03 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
static void readBox(XmlReader& e, Box* b)
|
2017-08-16 10:53:03 +02:00
|
|
|
{
|
2018-05-14 10:07:46 +02:00
|
|
|
b->setLeftMargin(0.0);
|
|
|
|
b->setRightMargin(0.0);
|
|
|
|
b->setTopMargin(0.0);
|
|
|
|
b->setBottomMargin(0.0);
|
|
|
|
b->setBoxHeight(Spatium(0)); // override default set in constructor
|
|
|
|
b->setBoxWidth(Spatium(0));
|
|
|
|
|
2017-08-16 10:53:03 +02:00
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
const QStringRef& tag(e.name());
|
2018-05-14 10:07:46 +02:00
|
|
|
if (tag == "HBox") {
|
|
|
|
HBox* hb = new HBox(b->score());
|
|
|
|
readBox(e, hb);
|
|
|
|
b->add(hb);
|
2017-08-16 10:53:03 +02:00
|
|
|
}
|
2018-05-14 10:07:46 +02:00
|
|
|
else if (tag == "VBox") {
|
|
|
|
VBox* vb = new VBox(b->score());
|
|
|
|
readBox(e, vb);
|
|
|
|
b->add(vb);
|
|
|
|
}
|
|
|
|
else if (!readBoxProperties(e, b))
|
2017-08-16 10:53:03 +02:00
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readStaffContent
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static void readStaffContent(Score* score, XmlReader& e)
|
|
|
|
{
|
|
|
|
int staff = e.intAttribute("id", 1) - 1;
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(Fraction(0,1));
|
2016-09-13 09:39:50 +02:00
|
|
|
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());
|
2019-01-30 15:13:54 +01:00
|
|
|
measure->setTicks(ev.timesig());
|
2016-09-13 09:39:50 +02:00
|
|
|
measure->setTimesig(ev.nominal());
|
|
|
|
|
|
|
|
readMeasure(measure, staff, e);
|
2016-09-14 10:23:35 +02:00
|
|
|
measure->checkMeasure(staff);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
if (!measure->isMMRest()) {
|
|
|
|
score->measures()->add(measure);
|
|
|
|
e.setLastMeasure(measure);
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(measure->tick() + measure->ticks());
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(measure->tick());
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
readMeasure(measure, staff, e);
|
2016-09-14 10:23:35 +02:00
|
|
|
measure->checkMeasure(staff);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
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") {
|
2018-05-14 10:07:46 +02:00
|
|
|
Box* mb = toBox(Element::name2Element(tag, score));
|
|
|
|
readBox(e, mb);
|
2016-09-13 09:39:50 +02:00
|
|
|
mb->setTick(e.tick());
|
|
|
|
score->measures()->add(mb);
|
|
|
|
}
|
|
|
|
else if (tag == "tick")
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(Fraction::fromTicks(score->fileDivision(e.readInt())));
|
2016-09-13 09:39:50 +02:00
|
|
|
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();
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->setLines(Fraction(0,1), lines);
|
2016-09-13 09:39:50 +02:00
|
|
|
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")
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->setSmall(Fraction(0,1), e.readInt());
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
2016-09-28 16:39:48 +02:00
|
|
|
ClefType ct = readClefType(e.attribute("idx", "0"));
|
2016-09-13 09:39:50 +02:00
|
|
|
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") {
|
2018-10-31 18:33:29 +01:00
|
|
|
int col = staff->brackets().size();
|
|
|
|
staff->setBracketType(col, BracketType(e.intAttribute("type", -1)));
|
|
|
|
staff->setBracketSpan(col, e.intAttribute("span", 0));
|
2016-09-13 09:39:50 +02:00
|
|
|
e.readNext();
|
|
|
|
}
|
|
|
|
else if (tag == "barLineSpan")
|
|
|
|
staff->setBarLineSpan(e.readInt());
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-20 10:13:12 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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();
|
|
|
|
}
|
2018-12-22 11:43:23 +01:00
|
|
|
|
2016-10-20 10:13:12 +02:00
|
|
|
if (i->channel().empty()) { // for backward compatibility
|
|
|
|
Channel* a = new Channel;
|
fix #275313: rework mixer ui 2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
fix #275313: rework-mixer-ui-2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
Restoring the volume, pan, chorus, reverb to original char data type
& range. UI now shows different 'user friendly' ranges.
Overwriting tests with versions from master.
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Restoring test files to original state.
Restoring test files to original state.
Restoring old values for importing files.
Restoring part methods.
mtest/importmidi/simplify_8th_dotted_no_staccato.mscx
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Rearranging UI components for better feel.
Improving UI. Fixed crash when changing part name.
Adding support for two lighting modes. Showing part name over
channel expansion.
Adding master gain control to mixer.
Changing color of gain slider.
Adapting to latest source in main.
Changing master gain slider to use decibel calculation.
CSS now set on tracks whenever a Paint event received.
Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked.
Fixing crash when closing score.
Fixing alignment in mixer details.
Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
|
|
|
a->setName(Channel::DEFAULT_NAME);
|
|
|
|
a->setProgram(program);
|
|
|
|
a->setBank(bank);
|
|
|
|
a->setVolume(volume);
|
|
|
|
a->setPan(pan);
|
|
|
|
a->setReverb(reverb);
|
|
|
|
a->setChorus(chorus);
|
2016-10-20 10:13:12 +02:00
|
|
|
i->appendChannel(a);
|
|
|
|
}
|
|
|
|
if (i->useDrumset()) {
|
fix #275313: rework mixer ui 2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
fix #275313: rework-mixer-ui-2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
Restoring the volume, pan, chorus, reverb to original char data type
& range. UI now shows different 'user friendly' ranges.
Overwriting tests with versions from master.
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Restoring test files to original state.
Restoring test files to original state.
Restoring old values for importing files.
Restoring part methods.
mtest/importmidi/simplify_8th_dotted_no_staccato.mscx
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Rearranging UI components for better feel.
Improving UI. Fixed crash when changing part name.
Adding support for two lighting modes. Showing part name over
channel expansion.
Adding master gain control to mixer.
Changing color of gain slider.
Adapting to latest source in main.
Changing master gain slider to use decibel calculation.
CSS now set on tracks whenever a Paint event received.
Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked.
Fixing crash when closing score.
Fixing alignment in mixer details.
Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
|
|
|
if (i->channel()[0]->bank() == 0)
|
|
|
|
i->channel()[0]->setBank(128);
|
2016-10-20 10:13:12 +02:00
|
|
|
}
|
2019-06-02 17:16:02 +02:00
|
|
|
|
|
|
|
// Fix user bank controller read
|
|
|
|
for (Channel* c : i->channel()) {
|
|
|
|
if (c->bank() == 0)
|
|
|
|
c->setUserBankController(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read single-note dynamics from template
|
|
|
|
i->setSingleNoteDynamicsFromTemplate();
|
2016-10-20 10:13:12 +02:00
|
|
|
}
|
|
|
|
|
2016-09-13 09:39:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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);
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->setStaffType(Fraction(0,1), StaffType()); // will reset later if needed
|
2016-09-13 09:39:50 +02:00
|
|
|
_score->staves().push_back(staff);
|
|
|
|
part->staves()->push_back(staff);
|
|
|
|
readStaff(staff, e);
|
|
|
|
}
|
|
|
|
else if (tag == "Instrument") {
|
|
|
|
Instrument* i = part->instrument();
|
2016-10-20 10:13:12 +02:00
|
|
|
readInstrument(i, part, e);
|
2016-09-13 09:39:50 +02:00
|
|
|
// add string data from MIDI program number, if possible
|
|
|
|
if (i->stringData()->strings() == 0
|
|
|
|
&& i->channel().count() > 0
|
|
|
|
&& i->drumset() == nullptr) {
|
fix #275313: rework mixer ui 2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
fix #275313: rework-mixer-ui-2
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Moving PartEditBase into separate file. Creating new files for
building mixer.
Creating art assets/UI design for new components.
Styling the track control.
Adding track area.
Separating out score from update.
Creating instances of mixer UI.
Creating part per voice now.
Can click on tracks to select them now.
Can now switch bwtewwn tracks.
Setting patch channel now.
Setting enabled off when no track selected.
Improving slider ui.
Turning Channel into a class and adding listener to it.
Somewhat stabalized sharing track objects between interfaces.
Can now apply volume changes to both expanded and collapsed tracks.
Pan knob is now working.
Encapsulating the rest of the fields in Channel.
Mute and solo now working.
Reverb and chorus now working.
Drumkit checkbox now working. Port and channel somewhat working.
Adding support for colors per track.
Part name change now working.
Separating out MixerTrackItem
Finishing moving MixerTrackItem to new file.
Cleaning up code.
Setting color in collapsed mode now affects all channels.
Using shared_ptr to track MixerTrackItem. Part changes now affect
all instruments.
Creating new track UI object to handle parts.
Using shard_ptr to track MixerTrackItem objects.
setting port and channel data now.
Changing to horizontal layout.
Fixing knob display. Chaning track control appearance.
Setting init slider window size.
Switchong back to vertical orientation. Fixing a few UI bugs in
the slider.
Tracks now left aligned.
Moving details panel above mixer. Now changing track selection when
user clicks on sliders.
Pan and volume controls now reflect track color.
Showing volume and pan values in tooltips.
Creating a new slider control for mixer.
Switching Channel's volume, pan, reverb and chorus and chaning them
to doubles with a decimal range.
No longer writing out vol, pan, chor, reverb when at default values.
Nolonger writing vol, pan, chorus, reverb as controler values in
output file.
Now testing against default values on write.
More export fixes.
Manually editing test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
Manually editing more test files to reflect new channel parameters.
More test changes to make Travis happy.
More test changes to make Travis happy.
Importing MusicXML now matches new volume, pan ranges.
Changing range of pan. Fixing a few bugs with calculating MIDI.
Altering test files for Travis.
Restoring the volume, pan, chorus, reverb to original char data type
& range. UI now shows different 'user friendly' ranges.
Overwriting tests with versions from master.
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Restoring test files to original state.
Restoring test files to original state.
Restoring old values for importing files.
Restoring part methods.
mtest/importmidi/simplify_8th_dotted_no_staccato.mscx
mtest/libmscore/compat114/clef_missing_first-ref.mscx
mtest/libmscore/compat114/hor_frame_and_mmrest-ref.mscx
mtest/musicxml/io/testInstrumentChangeMIDIportExport_ref.xml
mtest/musicxml/io/testUninitializedDivisions_ref.xml
Rearranging UI components for better feel.
Improving UI. Fixed crash when changing part name.
Adding support for two lighting modes. Showing part name over
channel expansion.
Adding master gain control to mixer.
Changing color of gain slider.
Adapting to latest source in main.
Changing master gain slider to use decibel calculation.
CSS now set on tracks whenever a Paint event received.
Restoring mixer slider values to refect MIDI ranges. Fixing crash when drumkit checked.
Fixing crash when closing score.
Fixing alignment in mixer details.
Tweaking UI for better appearance.
2018-11-13 18:43:19 +01:00
|
|
|
int program = i->channel(0)->program();
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
2019-01-30 15:13:54 +01:00
|
|
|
if (d && st && st->lines(Fraction(0,1)) != 5) {
|
2016-09-13 09:39:50 +02:00
|
|
|
int n = 0;
|
2019-01-30 15:13:54 +01:00
|
|
|
if (st->lines(Fraction(0,1)) == 1)
|
2016-09-13 09:39:50 +02:00
|
|
|
n = 4;
|
2018-08-17 15:06:15 +02:00
|
|
|
for (int j = 0; j < DRUM_INSTRUMENTS; ++j)
|
|
|
|
d->drum(j).line -= n;
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "name") {
|
|
|
|
Text* t = new Text(_score);
|
2018-05-14 10:07:46 +02:00
|
|
|
readText114(e, t, t);
|
2016-09-13 09:39:50 +02:00
|
|
|
part->instrument()->setLongName(t->xmlText());
|
|
|
|
delete t;
|
|
|
|
}
|
|
|
|
else if (tag == "shortName") {
|
|
|
|
Text* t = new Text(_score);
|
2018-05-25 09:26:38 +02:00
|
|
|
readText114(e, t, t);
|
2016-09-13 09:39:50 +02:00
|
|
|
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()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
int lines = staff->lines(Fraction(0,1));
|
2016-09-13 09:39:50 +02:00
|
|
|
int bf = staff->barLineFrom();
|
|
|
|
int bt = staff->barLineTo();
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->setStaffType(Fraction(0,1), *StaffType::getDefaultPreset(StaffGroup::PERCUSSION));
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
// this allows 2/3-line percussion staves to keep the double spacing they had in 1.3
|
|
|
|
|
|
|
|
if (lines == 2 || lines == 3)
|
2019-01-30 15:13:54 +01:00
|
|
|
((StaffType*)(staff->staffType(Fraction(0,1))))->setLineDistance(Spatium(2.0));
|
2016-09-13 09:39:50 +02:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
staff->setLines(Fraction(0,1), lines); // this also sets stepOffset
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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());
|
2016-09-22 10:03:59 +02:00
|
|
|
if (tag == "landscape")
|
2016-09-13 09:39:50 +02:00
|
|
|
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()) {
|
2018-08-17 15:06:15 +02:00
|
|
|
const QStringRef& t(e.name());
|
2016-09-13 09:39:50 +02:00
|
|
|
qreal val = e.readDouble() * 0.5 / PPI;
|
2018-08-17 15:06:15 +02:00
|
|
|
if (t == "left-margin")
|
2016-09-13 09:39:50 +02:00
|
|
|
lm = val;
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "right-margin")
|
2016-09-13 09:39:50 +02:00
|
|
|
rm = val;
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "top-margin")
|
2016-09-13 09:39:50 +02:00
|
|
|
tm = val;
|
2018-08-17 15:06:15 +02:00
|
|
|
else if (t == "bottom-margin")
|
2016-09-13 09:39:50 +02:00
|
|
|
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")
|
2017-01-05 11:23:47 +01:00
|
|
|
pf->setSize(QSizeF(pf->size().width(), e.readDouble() * 0.5 / PPI));
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "page-width")
|
2017-01-05 11:23:47 +01:00
|
|
|
pf->setSize(QSizeF(e.readDouble() * 0.5 / PPI, pf->size().height()));
|
2016-09-13 09:39:50 +02:00
|
|
|
else if (tag == "pageFormat") {
|
2017-01-05 11:23:47 +01:00
|
|
|
const PaperSize* s = getPaperSize114(e.readElementText());
|
|
|
|
pf->setSize(QSizeF(s->w, s->h));
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "page-offset") {
|
|
|
|
e.readInt();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
if (landscape)
|
2017-01-05 11:23:47 +01:00
|
|
|
pf->setSize(pf->size().transposed());
|
2016-09-13 09:39:50 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-10-06 12:21:28 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// readStyle
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2016-09-19 22:57:36 +02:00
|
|
|
static void readStyle(MStyle* style, XmlReader& e)
|
|
|
|
{
|
2018-03-27 15:36:00 +02:00
|
|
|
QString oldChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
|
2016-09-19 22:57:36 +02:00
|
|
|
bool chordListTag = false;
|
|
|
|
while (e.readNextStartElement()) {
|
|
|
|
QString tag = e.name().toString();
|
|
|
|
|
|
|
|
if (tag == "lyricsDistance") // was renamed
|
|
|
|
tag = "lyricsPosBelow";
|
|
|
|
|
|
|
|
if (tag == "TextStyle") {
|
2017-01-16 20:51:12 +01:00
|
|
|
// TextStyle s;
|
2018-08-27 22:57:15 +02:00
|
|
|
//TODO s.read(e);
|
2017-01-16 20:51:12 +01:00
|
|
|
// style->setTextStyle(s);
|
|
|
|
e.skipCurrentElement();
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
2019-02-17 01:13:32 +01:00
|
|
|
else if (tag == "lyricsMinBottomDistance") {
|
|
|
|
// no longer meaningful since it is now measured from skyline rather than staff
|
|
|
|
//style->set(Sid::lyricsMinBottomDistance, QPointF(0.0, y));
|
|
|
|
e.skipCurrentElement();
|
|
|
|
}
|
2016-09-19 22:57:36 +02:00
|
|
|
else if (tag == "Spatium")
|
2018-03-27 15:36:00 +02:00
|
|
|
style->set(Sid::spatium, e.readDouble() * DPMM);
|
2016-09-19 22:57:36 +02:00
|
|
|
else if (tag == "page-layout") {
|
2017-01-25 15:54:46 +01:00
|
|
|
PageFormat pf;
|
|
|
|
initPageFormat(style, &pf);
|
|
|
|
pf.read(e);
|
|
|
|
setPageFormat(style, pf);
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
|
|
|
else if (tag == "displayInConcertPitch")
|
2018-03-27 15:36:00 +02:00
|
|
|
style->set(Sid::concertPitch, QVariant(bool(e.readInt())));
|
2016-09-19 22:57:36 +02:00
|
|
|
else if (tag == "ChordList") {
|
|
|
|
style->chordList()->clear();
|
|
|
|
style->chordList()->read(e);
|
2016-10-14 21:39:31 +02:00
|
|
|
for (ChordFont f : style->chordList()->fonts) {
|
|
|
|
if (f.family == "MuseJazz") {
|
|
|
|
f.family = "MuseJazz Text";
|
|
|
|
}
|
|
|
|
}
|
2016-09-19 22:57:36 +02:00
|
|
|
style->setCustomChordList(true);
|
|
|
|
chordListTag = true;
|
|
|
|
}
|
|
|
|
else if (tag == "pageFillLimit" || tag == "genTimesig" || tag == "FixMeasureNumbers" || tag == "FixMeasureWidth") // obsolete
|
|
|
|
e.skipCurrentElement();
|
|
|
|
else if (tag == "systemDistance") // obsolete
|
2018-03-27 15:36:00 +02:00
|
|
|
style->set(Sid::minSystemDistance, QVariant(e.readDouble()));
|
2018-07-11 11:07:22 +02:00
|
|
|
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;
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
2018-07-11 11:07:22 +02:00
|
|
|
}
|
|
|
|
// for compatibility:
|
|
|
|
else if (tag == "oddHeader" || tag == "evenHeader" || tag == "oddFooter" || tag == "evenFooter")
|
|
|
|
tag += "C";
|
2016-10-06 12:21:28 +02:00
|
|
|
#if 0 // TODO-ws
|
2016-09-19 22:57:36 +02:00
|
|
|
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
|
|
|
|
) {
|
2018-03-27 15:36:00 +02:00
|
|
|
Sid si = MStyle::styleIdx(QString(ai.name).append("Anchor"));
|
|
|
|
if (si != Sid::NOSTYLE) {
|
2016-09-19 22:57:36 +02:00
|
|
|
QString val(e.readElementText());
|
|
|
|
style->set(si, val.toInt());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (idx2 < int(ArticulationType::ARTICULATIONS))
|
|
|
|
continue;
|
2016-10-06 12:21:28 +02:00
|
|
|
#endif
|
2018-07-11 11:07:22 +02:00
|
|
|
else {
|
|
|
|
if (!style->readProperties(e)) {
|
|
|
|
e.skipCurrentElement();
|
|
|
|
}
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
2018-07-11 11:07:22 +02:00
|
|
|
//TODO style->convertToUnit(tag, val);
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// if we just specified a new chord description file
|
|
|
|
// and didn't encounter a ChordList tag
|
|
|
|
// then load the chord description file
|
|
|
|
|
2018-03-27 15:36:00 +02:00
|
|
|
QString newChordDescriptionFile = style->value(Sid::chordDescriptionFile).toString();
|
2016-09-19 22:57:36 +02:00
|
|
|
if (newChordDescriptionFile != oldChordDescriptionFile && !chordListTag) {
|
2018-03-27 15:36:00 +02:00
|
|
|
if (!newChordDescriptionFile.startsWith("chords_") && style->value(Sid::chordStyle).toString() == "std") {
|
2016-09-19 22:57:36 +02:00
|
|
|
// should not normally happen,
|
|
|
|
// but treat as "old" (114) score just in case
|
2018-03-27 15:36:00 +02:00
|
|
|
style->set(Sid::chordStyle, QVariant(QString("custom")));
|
|
|
|
style->set(Sid::chordsXmlFile, QVariant(true));
|
2016-09-19 22:57:36 +02:00
|
|
|
qDebug("StyleData::load: custom chord description file %s with chordStyle == std", qPrintable(newChordDescriptionFile));
|
|
|
|
}
|
2018-03-27 15:36:00 +02:00
|
|
|
if (style->value(Sid::chordStyle).toString() == "custom")
|
2016-09-19 22:57:36 +02:00
|
|
|
style->setCustomChordList(true);
|
|
|
|
else
|
|
|
|
style->setCustomChordList(false);
|
|
|
|
style->chordList()->unload();
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure we have a chordlist
|
2019-04-16 01:33:24 +02:00
|
|
|
if (!chordListTag)
|
|
|
|
style->checkChordList();
|
2017-01-25 15:54:46 +01:00
|
|
|
#if 0 // TODO
|
2016-09-19 22:57:36 +02:00
|
|
|
//
|
|
|
|
// Compatibility with old scores/styles:
|
|
|
|
// translate old frameWidthMM and paddingWidthMM
|
|
|
|
// into spatium units
|
|
|
|
//
|
|
|
|
int n = style->textStyles().size();
|
2018-03-27 15:36:00 +02:00
|
|
|
qreal _spatium = style->value(Sid::spatium).toDouble();
|
2016-09-19 22:57:36 +02:00
|
|
|
qreal spMM = _spatium / DPMM;
|
|
|
|
for (int i = 0; i < n; ++i) {
|
2017-01-16 20:51:12 +01:00
|
|
|
TextStyle* s = &style->textStyle(StyledPropertyListIdx(i));
|
2016-09-19 22:57:36 +02:00
|
|
|
if (s->frameWidthMM() != 0.0)
|
|
|
|
s->setFrameWidth(Spatium(s->frameWidthMM() / spMM));
|
|
|
|
if (s->paddingWidthMM() != 0.0)
|
|
|
|
s->setPaddingWidth(Spatium(s->paddingWidthMM() / spMM));
|
|
|
|
}
|
2017-01-25 15:54:46 +01:00
|
|
|
#endif
|
2016-09-19 22:57:36 +02:00
|
|
|
}
|
|
|
|
|
2016-09-13 09:39:50 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// read114
|
|
|
|
// import old version <= 1.3 files
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Score::FileError MasterScore::read114(XmlReader& e)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i < sizeof(style114)/sizeof(*style114); ++i)
|
2018-10-26 10:41:07 +02:00
|
|
|
style().set(style114[i].sid, style114[i].val);
|
2018-07-11 11:07:22 +02:00
|
|
|
#if 0
|
2016-09-13 09:39:50 +02:00
|
|
|
// old text style defaults
|
2017-01-05 11:23:47 +01:00
|
|
|
TextStyle ts = style().textStyle("Chord Symbol");
|
2016-09-13 09:39:50 +02:00
|
|
|
ts.setYoff(-4.0);
|
2017-01-05 11:23:47 +01:00
|
|
|
style().setTextStyle(ts);
|
|
|
|
ts = style().textStyle("Rehearsal Mark");
|
2016-10-07 16:32:53 +02:00
|
|
|
ts.setSquare(false);
|
2016-10-07 17:12:09 +02:00
|
|
|
ts.setFrameRound(20);
|
2017-01-05 11:23:47 +01:00
|
|
|
style().setTextStyle(ts);
|
|
|
|
ts = style().textStyle("Dynamics");
|
2016-10-07 22:45:12 +02:00
|
|
|
ts.setItalic(false);
|
2017-01-05 11:23:47 +01:00
|
|
|
style().setTextStyle(ts);
|
2017-01-16 20:51:12 +01:00
|
|
|
#endif
|
2016-10-07 16:32:53 +02:00
|
|
|
TempoMap tm;
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
2018-08-27 22:57:15 +02:00
|
|
|
ks->read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
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();
|
2017-01-05 11:23:47 +01:00
|
|
|
readStyle(&style(), e);
|
2016-09-19 22:57:36 +02:00
|
|
|
//style()->load(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
// adjust this now so chords render properly on read
|
|
|
|
// other style adjustments can wait until reading is finished
|
2018-03-27 15:36:00 +02:00
|
|
|
if (styleB(Sid::useGermanNoteNames))
|
|
|
|
style().set(Sid::useStandardNoteNames, false);
|
2016-09-13 09:39:50 +02:00
|
|
|
if (_layoutMode == LayoutMode::FLOAT) {
|
|
|
|
// style should not change spatium in
|
|
|
|
// float mode
|
|
|
|
setSpatium(sp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tag == "TextStyle") {
|
2017-01-16 20:51:12 +01:00
|
|
|
e.skipCurrentElement();
|
|
|
|
#if 0 // TODO
|
2016-09-13 09:39:50 +02:00
|
|
|
TextStyle s;
|
2018-08-27 22:57:15 +02:00
|
|
|
s.read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
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()));
|
2016-10-12 19:25:28 +02:00
|
|
|
if (s.family() == "MuseJazz")
|
|
|
|
s.setFamily("MuseJazz Text");
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
if (s.name() == "Lyrics Odd Lines" || s.name() == "Lyrics Even Lines")
|
2017-01-05 11:23:47 +01:00
|
|
|
s.setAlign(Align(int(s.align()) & int(~Align::VMASK)) | Align::BASELINE);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
style().setTextStyle(s);
|
2017-01-16 20:51:12 +01:00
|
|
|
#endif
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "page-layout") {
|
2017-08-16 10:53:03 +02:00
|
|
|
PageFormat pf;
|
|
|
|
readPageFormat(&pf, e);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (tag == "copyright" || tag == "rights") {
|
|
|
|
Text* text = new Text(this);
|
2018-05-14 10:07:46 +02:00
|
|
|
readText114(e, text, text);
|
2016-09-13 09:39:50 +02:00
|
|
|
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);
|
2018-08-27 22:57:15 +02:00
|
|
|
readSlur206(e, slur);
|
2016-09-13 09:39:50 +02:00
|
|
|
addSpanner(slur);
|
|
|
|
}
|
|
|
|
else if ((tag == "HairPin")
|
|
|
|
|| (tag == "Ottava")
|
|
|
|
|| (tag == "TextLine")
|
|
|
|
|| (tag == "Volta")
|
|
|
|
|| (tag == "Trill")
|
|
|
|
|| (tag == "Pedal")) {
|
2017-12-20 16:49:30 +01:00
|
|
|
Spanner* s = toSpanner(Element::name2Element(tag, this));
|
2018-05-14 10:07:46 +02:00
|
|
|
if (tag == "Volta")
|
|
|
|
readVolta114(e, toVolta(s));
|
2018-08-03 14:23:16 +02:00
|
|
|
else if (tag == "Ottava")
|
|
|
|
readOttava114(e, toOttava(s));
|
2018-08-13 13:51:44 +02:00
|
|
|
else if (tag == "TextLine")
|
|
|
|
readTextLine114(e, toTextLine(s));
|
2018-08-15 08:56:32 +02:00
|
|
|
else if (tag == "Pedal")
|
|
|
|
readPedal114(e, toPedal(s));
|
2018-08-27 22:57:15 +02:00
|
|
|
else if (tag == "Trill")
|
|
|
|
readTrill206(e, toTrill(s));
|
|
|
|
else {
|
|
|
|
Q_ASSERT(tag == "HairPin");
|
|
|
|
readHairpin206(e, toHairpin(s));
|
|
|
|
}
|
2016-09-13 09:39:50 +02:00
|
|
|
if (s->track() == -1)
|
|
|
|
s->setTrack(e.track());
|
|
|
|
else
|
|
|
|
e.setTrack(s->track()); // update current track
|
2019-01-30 15:13:54 +01:00
|
|
|
if (s->tick() == Fraction(-1,1))
|
2016-09-13 09:39:50 +02:00
|
|
|
s->setTick(e.tick());
|
|
|
|
else
|
2019-01-30 15:13:54 +01:00
|
|
|
e.setTick(s->tick()); // update current tick
|
2016-09-13 09:39:50 +02:00
|
|
|
if (s->track2() == -1)
|
|
|
|
s->setTrack2(s->track());
|
2019-01-30 15:13:54 +01:00
|
|
|
if (s->ticks().isZero()) {
|
|
|
|
qDebug("zero spanner %s ticks: %d", s->name(), s->ticks().ticks());
|
2018-08-15 08:56:32 +02:00
|
|
|
delete s;
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
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);
|
2018-08-27 22:57:15 +02:00
|
|
|
beam->read(e);
|
2016-09-13 09:39:50 +02:00
|
|
|
beam->setParent(0);
|
|
|
|
// _beams.append(beam);
|
|
|
|
}
|
|
|
|
else if (tag == "name")
|
|
|
|
setName(e.readElementText());
|
|
|
|
else
|
|
|
|
e.unknown();
|
|
|
|
}
|
|
|
|
|
2018-05-14 10:07:46 +02:00
|
|
|
if (e.error() != QXmlStreamReader::NoError) {
|
|
|
|
qDebug("%lld %lld: %s ", e.lineNumber(), e.columnNumber(), qPrintable(e.errorString()));
|
2016-09-13 09:39:50 +02:00
|
|
|
return FileError::FILE_BAD_FORMAT;
|
2018-05-14 10:07:46 +02:00
|
|
|
}
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
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()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = Fraction::fromTicks(i.first);
|
2016-09-13 09:39:50 +02:00
|
|
|
ClefType clefId = i.second._concertClef;
|
2019-01-30 15:13:54 +01:00
|
|
|
Measure* m = tick2measure(tick);
|
2016-09-13 09:39:50 +02:00
|
|
|
if (!m)
|
|
|
|
continue;
|
2017-03-08 13:12:26 +01:00
|
|
|
SegmentType st = SegmentType::Clef;
|
2016-10-26 18:15:02 +02:00
|
|
|
if (tick == m->tick()) {
|
2016-10-25 17:30:55 +02:00
|
|
|
if (m->prevMeasure())
|
|
|
|
m = m->prevMeasure();
|
|
|
|
else
|
2017-03-08 13:12:26 +01:00
|
|
|
st = SegmentType::HeaderClef;
|
2016-10-25 17:30:55 +02:00
|
|
|
}
|
|
|
|
Segment* seg = m->getSegment(st, tick);
|
2016-09-13 09:39:50 +02:00
|
|
|
if (seg->element(track))
|
2016-10-25 17:30:55 +02:00
|
|
|
seg->element(track)->setGenerated(false);
|
2016-09-13 09:39:50 +02:00
|
|
|
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) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = Fraction::fromTicks(i->first);
|
|
|
|
if (tick < Fraction(0,1)) {
|
|
|
|
qDebug("read114: Key tick %d", tick.ticks());
|
2016-09-13 09:39:50 +02:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick.isZero() && i->second.key() == Key::C)
|
2016-09-13 09:39:50 +02:00
|
|
|
continue;
|
|
|
|
Measure* m = tick2measure(tick);
|
|
|
|
if (!m) //empty score
|
|
|
|
break;
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* seg = m->getSegment(SegmentType::KeySig, tick);
|
2016-09-13 09:39:50 +02:00
|
|
|
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
|
2018-10-18 11:53:01 +02:00
|
|
|
yo = styleValue(Pid::OFFSET, Sid::ottavaPosAbove).toPointF().y();
|
2016-09-13 09:39:50 +02:00
|
|
|
if (s->placeBelow())
|
|
|
|
yo = -yo + s->staff()->height();
|
|
|
|
}
|
|
|
|
else if (s->isPedal()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
yo = styleValue(Pid::OFFSET, Sid::pedalPosBelow).toPointF().y();
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (s->isTrill()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
yo = styleValue(Pid::OFFSET, Sid::trillPosAbove).toPointF().y();
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
else if (s->isTextLine()) {
|
|
|
|
yo = -5.0 * spatium();
|
|
|
|
}
|
2018-11-25 23:35:00 +01:00
|
|
|
if (!s->spannerSegments().empty()) {
|
2016-09-13 09:39:50 +02:00
|
|
|
for (SpannerSegment* seg : s->spannerSegments()) {
|
2018-10-18 11:53:01 +02:00
|
|
|
if (!seg->offset().isNull())
|
|
|
|
seg->ryoffset() = seg->offset().y() - yo;
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2018-10-18 11:53:01 +02:00
|
|
|
s->ryoffset() = -yo;
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
2017-03-08 13:12:26 +01:00
|
|
|
if (s->segmentType() != SegmentType::ChordRest)
|
2016-09-13 09:39:50 +02:00
|
|
|
continue;
|
|
|
|
ChordRest* cr = toChordRest(s->element(track));
|
|
|
|
if (cr) {
|
2016-12-18 14:31:13 +01:00
|
|
|
#if 0 // TODO
|
2016-09-13 09:39:50 +02:00
|
|
|
if (cr->isRest()) {
|
|
|
|
Rest* r = toRest(cr);
|
2018-10-18 11:53:01 +02:00
|
|
|
if (!r->offset().isNull()) {
|
2016-09-13 09:39:50 +02:00
|
|
|
int lineOffset = r->computeLineOffset();
|
2016-12-13 13:16:17 +01:00
|
|
|
qreal lineDist = r->staff() ? r->staff()->staffType(cr->tick())->lineDistance().val() : 1.0;
|
2016-09-13 09:39:50 +02:00
|
|
|
r->rUserYoffset() -= (lineOffset * .5 * lineDist * r->spatium());
|
|
|
|
}
|
|
|
|
}
|
2016-12-18 14:31:13 +01:00
|
|
|
#endif
|
2016-09-13 09:39:50 +02:00
|
|
|
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()) {
|
2017-12-20 16:49:30 +01:00
|
|
|
if (mb->isVBox()) {
|
|
|
|
VBox* b = toVBox(mb);
|
2018-03-27 15:36:00 +02:00
|
|
|
qreal y = styleP(Sid::staffUpperBorder);
|
2016-09-13 09:39:50 +02:00
|
|
|
b->setBottomGap(y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_fileDivision = MScore::division;
|
|
|
|
|
|
|
|
//
|
|
|
|
// sanity check for barLineSpan and update ottavas
|
|
|
|
//
|
|
|
|
for (Staff* staff : staves()) {
|
|
|
|
int barLineSpan = staff->barLineSpan();
|
2016-09-28 21:13:05 +02:00
|
|
|
int idx = staff->idx();
|
2016-09-13 09:39:50 +02:00
|
|
|
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
|
2018-03-27 15:36:00 +02:00
|
|
|
if (styleB(Sid::hideEmptyStaves)) // http://musescore.org/en/node/16228
|
|
|
|
style().set(Sid::dontHideStavesInFirstSystem, false);
|
|
|
|
if (styleB(Sid::showPageNumberOne)) { // http://musescore.org/en/node/21207
|
|
|
|
style().set(Sid::evenFooterL, QString("$P"));
|
|
|
|
style().set(Sid::oddFooterR, QString("$P"));
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
2018-03-27 15:36:00 +02:00
|
|
|
if (styleI(Sid::minEmptyMeasures) == 0)
|
|
|
|
style().set(Sid::minEmptyMeasures, 1);
|
|
|
|
style().set(Sid::frameSystemDistance, styleS(Sid::frameSystemDistance) + Spatium(6.0));
|
2016-09-13 09:39:50 +02:00
|
|
|
// hack: net overall effect of layout changes has been for things to take slightly more room
|
2018-03-27 15:36:00 +02:00
|
|
|
qreal adjustedSpacing = qMax(styleD(Sid::measureSpacing) * 0.95, 1.0);
|
|
|
|
style().set(Sid::measureSpacing, adjustedSpacing);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
_showOmr = false;
|
|
|
|
|
|
|
|
// add invisible tempo text if necessary
|
|
|
|
// some 1.3 scores have tempolist but no tempo text
|
|
|
|
fixTicks();
|
|
|
|
for (auto i : tm) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick = Fraction::fromTicks(i.first);
|
|
|
|
qreal tempo = i.second.tempo;
|
|
|
|
if (tempomap()->tempo(tick.ticks()) != tempo) {
|
2016-09-13 09:39:50 +02:00
|
|
|
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) {
|
2017-03-08 13:12:26 +01:00
|
|
|
Segment* seg = m->getSegment(SegmentType::ChordRest, tick);
|
2016-09-13 09:39:50 +02:00
|
|
|
seg->add(tt);
|
|
|
|
setTempo(tick, tempo);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
delete tt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// create excerpts
|
|
|
|
|
2019-02-27 11:31:07 +01:00
|
|
|
QList<Excerpt*> readExcerpts;
|
|
|
|
readExcerpts.swap(_excerpts);
|
|
|
|
for (Excerpt* excerpt : readExcerpts) {
|
|
|
|
if (excerpt->parts().isEmpty()) // ignore empty parts
|
2016-09-13 09:39:50 +02:00
|
|
|
continue;
|
|
|
|
if (!excerpt->parts().isEmpty()) {
|
2019-02-27 11:31:07 +01:00
|
|
|
_excerpts.push_back(excerpt);
|
2016-09-13 09:39:50 +02:00
|
|
|
Score* nscore = new Score(this);
|
|
|
|
excerpt->setPartScore(nscore);
|
2018-03-27 15:36:00 +02:00
|
|
|
nscore->style().set(Sid::createMultiMeasureRests, true);
|
2016-10-12 11:09:53 +02:00
|
|
|
Excerpt::createExcerpt(excerpt);
|
2016-09-13 09:39:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2018-10-18 11:53:01 +02:00
|
|
|
|
|
|
|
if (styleV(Sid::voltaPosAbove) == MScore::baseStyle().value(Sid::voltaPosAbove))
|
|
|
|
style().set(Sid::voltaPosAbove, QPointF(0.0, -2.0f));
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
fixTicks();
|
|
|
|
rebuildMidiMapping();
|
|
|
|
updateChannel();
|
|
|
|
|
|
|
|
// treat reading a 1.14 file as import
|
|
|
|
// on save warn if old file will be overwritten
|
|
|
|
setCreated(true);
|
2017-02-03 09:38:35 +01:00
|
|
|
// don't autosave (as long as there's no change to the score)
|
|
|
|
setAutosaveDirty(false);
|
2016-09-13 09:39:50 +02:00
|
|
|
|
|
|
|
return FileError::FILE_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|