diff --git a/mscore/importxml.cpp b/mscore/importxml.cpp index 221327b8f09ec..c9b554b7dfb6e 100644 --- a/mscore/importxml.cpp +++ b/mscore/importxml.cpp @@ -266,8 +266,12 @@ static Fraction noteTypeToFraction(QString type) // calculateFraction //--------------------------------------------------------- +/** + Convert note type, number of dots and actual and normal notes into a duration + */ + static Fraction calculateFraction(QString type, int dots, int normalNotes, int actualNotes) -{ + { // type Fraction f = noteTypeToFraction(type); if (f.isValid()) { @@ -283,7 +287,7 @@ static Fraction calculateFraction(QString type, int dots, int normalNotes, int a f.reduce(); } return f; -} + } //--------------------------------------------------------- @@ -292,87 +296,90 @@ static Fraction calculateFraction(QString type, int dots, int normalNotes, int a /** Determine note duration as fraction. Prefer note type over duration. + Input e is the note element. If chord or grace, duration is 0. */ static Fraction noteDurationAsFraction(const int divisions, const QDomElement e) { - int actualNotes = 0; - bool chord = false; - int dots = 0; - int duration = 0; - bool grace = false; - int normalNotes = 0; - bool rest = false; - QString type; - for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) { - if (ee.tagName() == "chord") - chord = true; - else if (ee.tagName() == "dot") - dots++; - else if (ee.tagName() == "duration") { - bool ok; - duration = stringToInt(ee.text(), &ok); - if (!ok) - qDebug("MusicXml-Import: bad duration value: <%s>", - qPrintable(ee.text())); - } - else if (ee.tagName() == "grace") - grace = true; - else if (ee.tagName() == "rest") - rest = true; - else if (ee.tagName() == "time-modification") { - for (QDomElement eee = ee.firstChildElement(); !eee.isNull(); eee = eee.nextSiblingElement()) { - if (eee.tagName() == "actual-notes") { - bool ok; - actualNotes = stringToInt(eee.text(), &ok); - if (!ok || divisions <= 0) - qDebug("MusicXml-Import: bad actual-notes value: <%s>", - qPrintable(eee.text())); - } - if (eee.tagName() == "normal-notes") { - bool ok; - normalNotes = stringToInt(eee.text(), &ok); - if (!ok || divisions <= 0) - qDebug("MusicXml-Import: bad normal-notes value: <%s>", - qPrintable(eee.text())); - } - } - } - else if (ee.tagName() == "type") - type = ee.text(); - } + int actualNotes = 0; + bool chord = false; + int dots = 0; + int duration = 0; + bool grace = false; + int normalNotes = 0; + bool rest = false; + QString type; + for (QDomElement ee = e.firstChildElement(); !ee.isNull(); ee = ee.nextSiblingElement()) { + if (ee.tagName() == "chord") + chord = true; + else if (ee.tagName() == "dot") + dots++; + else if (ee.tagName() == "duration") { + bool ok; + duration = stringToInt(ee.text(), &ok); + if (!ok) + qDebug("MusicXml-Import: bad duration value: <%s>", + qPrintable(ee.text())); + } + else if (ee.tagName() == "grace") + grace = true; + else if (ee.tagName() == "rest") + rest = true; + else if (ee.tagName() == "time-modification") { + for (QDomElement eee = ee.firstChildElement(); !eee.isNull(); eee = eee.nextSiblingElement()) { + if (eee.tagName() == "actual-notes") { + bool ok; + actualNotes = stringToInt(eee.text(), &ok); + if (!ok || divisions <= 0) + qDebug("MusicXml-Import: bad actual-notes value: <%s>", + qPrintable(eee.text())); + } + if (eee.tagName() == "normal-notes") { + bool ok; + normalNotes = stringToInt(eee.text(), &ok); + if (!ok || divisions <= 0) + qDebug("MusicXml-Import: bad normal-notes value: <%s>", + qPrintable(eee.text())); + } + } + } + else if (ee.tagName() == "type") + type = ee.text(); + } - // if chord or grace, duration is 0 - if (chord || grace) - return Fraction(0, 1); + // if chord or grace, duration is 0 + if (chord || grace) + return Fraction(0, 1); - // calculate note duration as fraction based on type, dots, normal and actual notes - Fraction f = calculateFraction(type, dots, normalNotes, actualNotes); - if (!f.isValid()) { - qDebug("time-in-fraction: f invalid, using duration"); - f = Fraction(duration, 4 * divisions); // note divisions = ticks / quarter note - } + // calculate note duration as fraction based on type, dots, normal and actual notes + // if that does not succeed, fallback to using the element + // note divisions = ticks / quarter note + Fraction f = calculateFraction(type, dots, normalNotes, actualNotes); + if (!f.isValid()) { + qDebug("time-in-fraction: f invalid, using duration"); + f = Fraction(duration, 4 * divisions); + } - // bug fix for rests in triplet - if (f.isValid() && rest && normalNotes == 0 && actualNotes == 0) { - qDebug("time-in-fraction: check for rest in triplet bug: %s", - qPrintable((Fraction(duration, 4 * divisions) / f).print())); - if ((Fraction(duration, 4 * divisions) / f) == Fraction(2, 3)) { - // duration is exactly 2/3 of what is expected based on type - qDebug("time-in-fraction: rest in triplet bug found, fixing ..."); - f = Fraction(duration, 4 * divisions); - } - } + // bug fix for rests in triplet + if (f.isValid() && rest && normalNotes == 0 && actualNotes == 0) { + qDebug("time-in-fraction: check for rest in triplet bug: %s", + qPrintable((Fraction(duration, 4 * divisions) / f).print())); + if ((Fraction(duration, 4 * divisions) / f) == Fraction(2, 3)) { + // duration is exactly 2/3 of what is expected based on type + qDebug("time-in-fraction: rest in triplet bug found, fixing ..."); + f = Fraction(duration, 4 * divisions); + } + } #ifdef DEBUG_TICK - qDebug("time-in-fraction: note type %s dots %d norm %d act %d" - " dur %d chord %d grace %d-> dt frac %s (ticks %d)", - qPrintable(type), dots, normalNotes, actualNotes, duration, - chord, grace, qPrintable(f.print()), f.ticks()); + qDebug("time-in-fraction: note type %s dots %d norm %d act %d" + " dur %d chord %d grace %d-> dt frac %s (ticks %d)", + qPrintable(type), dots, normalNotes, actualNotes, duration, + chord, grace, qPrintable(f.print()), f.ticks()); #endif - return f; + return f; } @@ -381,7 +388,7 @@ static Fraction noteDurationAsFraction(const int divisions, const QDomElement e) //--------------------------------------------------------- /** - Move tick/typFr by amount specified in the element e, which must be + Move tick and typFr by amount specified in the element e, which must be a forward, backup or note. */ @@ -397,8 +404,8 @@ static void moveTick(const int mtick, int& tick, int& maxtick, Fraction& typFr, bool ok; val = stringToInt(ee.text(), &ok); if (!ok || divisions <= 0) - qDebug("MusicXml-Import: bad divisions value: <%s>", - qPrintable(ee.text())); + qDebug("MusicXml-Import: bad divisions value: <%s>", + qPrintable(ee.text())); Fraction f(val, 4 * divisions); // note divisions = ticks / quarter note typFr += f; typFr.reduce(); @@ -424,8 +431,8 @@ static void moveTick(const int mtick, int& tick, int& maxtick, Fraction& typFr, bool ok; val = stringToInt(ee.text(), &ok); if (!ok || divisions <= 0) - qDebug("MusicXml-Import: bad divisions value: <%s>", - qPrintable(ee.text())); + qDebug("MusicXml-Import: bad divisions value: <%s>", + qPrintable(ee.text())); Fraction f(val, 4 * divisions); // note divisions = ticks / quarter note if (f < typFr) { typFr -= f; @@ -1017,12 +1024,6 @@ static bool determineTimeSig(const QString beats, const QString beatType, const */ -/* - * TODO for debugging issue 17654, dump both - * - the timing in fraction based on note type (and backup/restore) - * - the timing in duration (as fraction) only - */ - static bool determineMeasureLength(QDomElement e, QVector& ml) { #ifdef DEBUG_TICK @@ -1042,12 +1043,8 @@ static bool determineMeasureLength(QDomElement e, QVector& ml) // current "tick" within this measure as fraction // calculated using note type, backup and forward Fraction noteTypeTickFr; - // current "tick" within this measure as fraction - // calculated using note duration, backup and forward - Fraction noteDurationTickFr; // maximum "tick" within this measure as fraction Fraction maxNoteTypeTickFr; - Fraction maxNoteDurationTickFr; // dummy int dummy_tick = 0; int dummy_maxtick = 0; @@ -1100,39 +1097,25 @@ static bool determineMeasureLength(QDomElement e, QVector& ml) // (divisions must be valid) if (divisions > 0) { if (ee.tagName() == "note") { - moveTick(0, dummy_tick, dummy_maxtick, noteTypeTickFr, divisions, ee); - if (noteTypeTickFr > maxNoteTypeTickFr) - maxNoteTypeTickFr = noteTypeTickFr; - qDebug("time-in-fraction: after note type based fraction %s tick %d max fraction %s tick %d", - qPrintable(noteTypeTickFr.print()), noteTypeTickFr.ticks(), - qPrintable(maxNoteTypeTickFr.print()), maxNoteTypeTickFr.ticks()); - // noteDurationTickFr += Fraction(duration, 4 * divisions); // note divisions = ticks / quarter note - noteDurationTickFr.reduce(); - if (noteDurationTickFr > maxNoteDurationTickFr) - maxNoteDurationTickFr = noteDurationTickFr; - qDebug("time-in-fraction: after note duration based fraction %s tick %d max fraction %s tick %d", - qPrintable(noteDurationTickFr.print()), noteDurationTickFr.ticks(), - qPrintable(maxNoteDurationTickFr.print()), maxNoteDurationTickFr.ticks()); + moveTick(0, dummy_tick, dummy_maxtick, noteTypeTickFr, divisions, ee); + if (noteTypeTickFr > maxNoteTypeTickFr) + maxNoteTypeTickFr = noteTypeTickFr; + qDebug("time-in-fraction: after note type based fraction %s tick %d max fraction %s tick %d", + qPrintable(noteTypeTickFr.print()), noteTypeTickFr.ticks(), + qPrintable(maxNoteTypeTickFr.print()), maxNoteTypeTickFr.ticks()); } else if (ee.tagName() == "backup") { moveTick(0, dummy_tick, dummy_maxtick, noteTypeTickFr, divisions, ee); qDebug("time-in-fraction: after backup type based fraction %s tick %d", qPrintable(noteTypeTickFr.print()), noteTypeTickFr.ticks()); - qDebug("time-in-fraction: after backup duration based fraction %s tick %d", - qPrintable(noteDurationTickFr.print()), noteDurationTickFr.ticks()); } else if (ee.tagName() == "forward") { moveTick(0, dummy_tick, dummy_maxtick, noteTypeTickFr, divisions, ee); if (noteTypeTickFr > maxNoteTypeTickFr) maxNoteTypeTickFr = noteTypeTickFr; - if (noteDurationTickFr > maxNoteDurationTickFr) - maxNoteDurationTickFr = noteDurationTickFr; qDebug("time-in-fraction: after forward type based fraction %s tick %d max fraction %s tick %d", qPrintable(noteTypeTickFr.print()), noteTypeTickFr.ticks(), qPrintable(maxNoteTypeTickFr.print()), maxNoteTypeTickFr.ticks()); - qDebug("time-in-fraction: after forward duration based fraction %s tick %d max fraction %s tick %d", - qPrintable(noteDurationTickFr.print()), noteDurationTickFr.ticks(), - qPrintable(maxNoteDurationTickFr.print()), maxNoteDurationTickFr.ticks()); } } else @@ -1140,9 +1123,8 @@ static bool determineMeasureLength(QDomElement e, QVector& ml) } // for (QDomElement ee .... // measure has been read, determine length - qDebug("time-in-fraction: max type based fraction %s tick %d max duration based fraction %s tick %d", - qPrintable(maxNoteTypeTickFr.print()), maxNoteTypeTickFr.ticks(), - qPrintable(maxNoteDurationTickFr.print()), maxNoteDurationTickFr.ticks()); + qDebug("time-in-fraction: max type based fraction %s tick %d", + qPrintable(maxNoteTypeTickFr.print()), maxNoteTypeTickFr.ticks()); int length = maxNoteTypeTickFr.ticks(); int correctedLength = length; #if 1 // change in 0.9.6 trunk revision 5241 for issue 14451, TODO verify if useful in trunk @@ -4981,7 +4963,7 @@ void MusicXml::xmlNote(Measure* measure, int staff, const QString& partId, QDomE Segment* s = measure->getSegment(cr, loc_tick); //sibelius might import 2 rests at the same place, ignore the 2one // - if(!s->element(cr->track())) + if (!s->element(cr->track())) s->add(cr); cr->setVisible(printObject == "yes"); if (step != "" && 0 <= octave && octave <= 9) { @@ -5145,79 +5127,13 @@ void MusicXml::xmlNote(Measure* measure, int staff, const QString& partId, QDomE addLyrics(cr, numberedLyrics, defaultyLyrics, unNumberedLyrics); if (!chord) - prevtick = tick; // remember tick where last chordrest was inserted + prevtick = tick; // remember tick where last chordrest was inserted #ifdef DEBUG_TICK qDebug(" after inserting note tick=%d prevtick=%d", tick, prevtick); #endif } -//--------------------------------------------------------- -// addWedge -//--------------------------------------------------------- - -/** - Add a MusicXML wedge to the wedge list. - - Called when the wedge start is read. Stores all wedge parameters known at this time. - */ - -/* -void MusicXml::addWedge(int no, int startTick, qreal rx, qreal ry, bool above, bool hasYoffset, qreal yoffset, int subType) - { - qDebug("addWedge(no %d, startTick %d, subType %d)", no, startTick, subType); - MusicXmlWedge wedge; - wedge.number = no; - wedge.startTick = startTick; - wedge.rx = rx; - wedge.ry = ry; - wedge.above = above; - wedge.hasYoffset = hasYoffset; - wedge.yoffset = yoffset; - wedge.subType = subType; - - if (int(wedgeList.size()) > no) - wedgeList[no] = wedge; - else - wedgeList.push_back(wedge); - } -*/ - -//--------------------------------------------------------- -// genWedge -//--------------------------------------------------------- - -/** - Add a MusicXML wedge to the score. - - Called when the wedge stop is read. Wedge stop tick was unknown until this time. - */ - -/* -void MusicXml::genWedge(int no, int endTick, Measure* measure, int staff) - { - qDebug("genWedge(no %d, endTick %d", no, endTick); - Hairpin* hp = new Hairpin(score); - hp->setSubtype(wedgeList[no].subType); - if (wedgeList[no].hasYoffset) - hp->setYoff(wedgeList[no].yoffset); - else - hp->setYoff(wedgeList[no].above ? -3 : 8); - hp->setUserOff(QPointF(wedgeList[no].rx, wedgeList[no].ry)); - hp->setTrack(staff * VOICES); - // TODO LVI following fails for wedges starting in a different measure ! - Segment* seg = measure->getSegment(Segment::SegChordRest, wedgeList[no].startTick); - qDebug("start seg %p", seg); - hp->setStartElement(seg); - seg->add(hp); - seg = measure->getSegment(Segment::SegChordRest, endTick); - qDebug(", stop seg %p", seg); - hp->setEndElement(seg); - seg->addSpannerBack(hp); - score->updateHairpin(hp); -// qDebug("gen wedge %p staff %d, tick %d-%d", hp, staff, hp->tick(), hp->tick2()); - } -*/ //--------------------------------------------------------- // xmlHarmony diff --git a/mscore/musicxml.h b/mscore/musicxml.h index 215792367c349..d645b671d6418 100644 --- a/mscore/musicxml.h +++ b/mscore/musicxml.h @@ -93,7 +93,8 @@ struct CreditWords { QString hAlign; QString vAlign; QString words; - CreditWords(double a, double b, QString c, QString d, QString e, QString f) { + CreditWords(double a, double b, QString c, QString d, QString e, QString f) + { defaultX = a; defaultY = b; justify = c; @@ -119,10 +120,10 @@ class MusicXmlCreator { QString _type; QString _text; - public: +public: MusicXmlCreator(QString& tp, QString& txt) { _type = tp; _text = txt; } - QString crType() const { return _type; } - QString crText() const { return _text; } + QString crType() const { return _type; } + QString crText() const { return _text; } }; //--------------------------------------------------------- @@ -134,24 +135,24 @@ class MusicXmlCreator { */ class VoiceDesc { - public: +public: VoiceDesc(); void incrChordRests(int s); int numberChordRests() const; int numberChordRests(int s) const { return (s >= 0 && s < MAX_STAVES) ? _chordRests[s] : 0; } int preferredStaff() const; ///< Determine preferred staff for this voice - void setStaff(int s) { if (s >= 0) _staff = s; } - int staff() const { return _staff; } - void setVoice(int v) { if (v >= 0) _voice = v; } - int voice() const { return _voice; } - void setVoice(int s, int v) { if (s >= 0 && s < MAX_STAVES) _voices[s] = v; } - int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? _voices[s] : -1; } - void setOverlap(bool b) { _overlaps = b; } - bool overlaps() const { return _overlaps; } - void setStaffAlloc(int s, int i) { if (s >= 0 && s < MAX_STAVES) _staffAlloc[s] = i; } - int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? _staffAlloc[s] : -1; } + void setStaff(int s) { if (s >= 0) _staff = s; } + int staff() const { return _staff; } + void setVoice(int v) { if (v >= 0) _voice = v; } + int voice() const { return _voice; } + void setVoice(int s, int v) { if (s >= 0 && s < MAX_STAVES) _voices[s] = v; } + int voice(int s) const { return (s >= 0 && s < MAX_STAVES) ? _voices[s] : -1; } + void setOverlap(bool b) { _overlaps = b; } + bool overlaps() const { return _overlaps; } + void setStaffAlloc(int s, int i) { if (s >= 0 && s < MAX_STAVES) _staffAlloc[s] = i; } + int staffAlloc(int s) const { return (s >= 0 && s < MAX_STAVES) ? _staffAlloc[s] : -1; } QString toString() const; - private: +private: int _chordRests[MAX_STAVES]; ///< The number of chordrests on each MusicXML staff int _staff; ///< The MuseScore staff allocated int _voice; ///< The MuseScore voice allocated @@ -172,9 +173,9 @@ class JumpMarkerDesc { Element* _el; const Measure* _meas; - public: +public: JumpMarkerDesc(Element* el, const Measure* meas) : _el(el), _meas(meas) {} - Element* el() const { return _el; } + Element* el() const { return _el; } const Measure* meas() const { return _meas; } }; @@ -223,7 +224,6 @@ class MusicXml { CreditWordsList credits; JumpMarkerDescList jumpsMarkers; -// std::vector wedgeList; std::vector partGroupList; QMap > spanners; @@ -242,8 +242,6 @@ class MusicXml { //----------------------------- -// void addWedge(int no, int startPos, qreal rx, qreal ry, bool above, bool hasYoffset, qreal yoffset, int subType); -// void genWedge(int no, int endPos, Measure*, int staff); void doCredits(); void direction(Measure* measure, int staff, QDomElement node); void scorePartwise(QDomElement); @@ -262,7 +260,7 @@ class MusicXml { int xmlClef(QDomElement, int staffIdx, Measure*); void initVoiceMapperAndMapVoices(QDomElement e); - public: +public: MusicXml(QDomDocument* d); void import(Score*); }; diff --git a/test/musicxml/testfiles_MusicXML_with_ref b/test/musicxml/testfiles_MusicXML_with_ref index c1331f8581c4c..b7811157ae197 100644 --- a/test/musicxml/testfiles_MusicXML_with_ref +++ b/test/musicxml/testfiles_MusicXML_with_ref @@ -1,3 +1,4 @@ +testDurationRoundingError.xml testDynamics3.xml testEmptyMeasure.xml testEmptyVoice1.xml