LCOV - code coverage report
Current view: top level - src/engraving/rw/read400 - measurerw.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 74.0 % 434 321
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "measurerw.h"
      23              : 
      24              : #include "translation.h"
      25              : 
      26              : #include "../dom/ambitus.h"
      27              : #include "../dom/barline.h"
      28              : #include "../dom/beam.h"
      29              : #include "../dom/breath.h"
      30              : #include "../dom/chord.h"
      31              : #include "../dom/dynamic.h"
      32              : #include "../dom/expression.h"
      33              : #include "../dom/factory.h"
      34              : #include "../dom/instrchange.h"
      35              : #include "../dom/fermata.h"
      36              : #include "../dom/fret.h"
      37              : #include "../dom/keysig.h"
      38              : #include "../dom/location.h"
      39              : #include "../dom/measure.h"
      40              : #include "../dom/measurenumber.h"
      41              : #include "../dom/measurerepeat.h"
      42              : #include "../dom/mmrest.h"
      43              : #include "../dom/mmrestrange.h"
      44              : #include "../dom/part.h"
      45              : #include "../dom/score.h"
      46              : #include "../dom/segment.h"
      47              : #include "../dom/spacer.h"
      48              : #include "../dom/staff.h"
      49              : #include "../dom/stafflines.h"
      50              : #include "../dom/stafftext.h"
      51              : #include "../dom/system.h"
      52              : #include "../dom/systemdivider.h"
      53              : #include "../dom/timesig.h"
      54              : #include "../dom/tuplet.h"
      55              : #include "../dom/harmony.h"
      56              : #include "../dom/fret.h"
      57              : #include "../dom/tremolobar.h"
      58              : #include "../dom/tempotext.h"
      59              : #include "../dom/image.h"
      60              : #include "../editing/transpose.h"
      61              : 
      62              : #include "tread.h"
      63              : 
      64              : #include "log.h"
      65              : 
      66              : using namespace mu::engraving;
      67              : using namespace mu::engraving::read400;
      68              : using namespace mu::engraving::write;
      69              : 
      70         5112 : void MeasureRead::readMeasure(Measure* measure, XmlReader& e, ReadContext& ctx, int staffIdx)
      71              : {
      72         5112 :     IF_ASSERT_FAILED(ctx.isSameScore(measure)) {
      73            0 :         return;
      74              :     }
      75              : 
      76         5112 :     ctx.setCurrentMeasure(measure);
      77         5112 :     int nextTrack = staffIdx * VOICES;
      78         5112 :     ctx.setTrack(nextTrack);
      79              : 
      80         5112 :     for (int n = int(measure->mstaves().size()); n <= staffIdx; ++n) {
      81            0 :         Staff* staff = ctx.staff(n);
      82            0 :         MStaff* s = new MStaff;
      83            0 :         s->setLines(Factory::createStaffLines(measure));
      84            0 :         s->lines()->setTrack(n * VOICES);
      85            0 :         s->lines()->setVisible(!staff->isLinesInvisible(measure->tick()));
      86            0 :         measure->mstaves().push_back(s);
      87              :     }
      88              : 
      89         5112 :     bool irregular = false;
      90         5112 :     if (e.hasAttribute("len")) {
      91           43 :         bool ok = true;
      92           43 :         measure->setTicks(Fraction::fromString(e.attribute("len"), &ok));
      93           43 :         if (!ok || measure->ticks() < Fraction(1, 128)) {
      94            0 :             e.raiseError(muse::mtrc("engraving", "MSCX error at byte offset %1: invalid measure length: %2")
      95            0 :                          .arg(e.byteOffset()).arg(e.attribute("len")));
      96            0 :             return;
      97              :         }
      98           43 :         irregular = true;
      99           43 :         ctx.compatTimeSigMap()->add(measure->tick().ticks(), SigEvent(measure->ticks(), measure->timesig()));
     100           43 :         ctx.compatTimeSigMap()->add(measure->endTick().ticks(), SigEvent(measure->timesig()));
     101              :     }
     102              : 
     103        11321 :     while (e.readNextStartElement()) {
     104         6209 :         const AsciiStringView tag(e.name());
     105              : 
     106         6209 :         if (tag == "voice") {
     107         5310 :             ctx.setTrack(nextTrack++);
     108         5310 :             ctx.setTick(measure->tick());
     109         5310 :             readVoice(measure, e, ctx, staffIdx, irregular);
     110          899 :         } else if (tag == "Marker" || tag == "Jump") {
     111          158 :             EngravingItem* el = Factory::createItemByName(tag, measure);
     112          158 :             el->setTrack(ctx.track());
     113          158 :             TRead::readItem(el, e, ctx);
     114          158 :             if (el->systemFlag() && el->isTopSystemObject()) {
     115          158 :                 el->setTrack(0); // original system object always goes on top
     116              :             }
     117          158 :             measure->add(el);
     118          741 :         } else if (tag == "stretch") {
     119            0 :             if (measure->score()->mscVersion() < 400) {
     120              :                 // Ignore measure stretch pre 4.0
     121            0 :                 e.skipCurrentElement();
     122              :             } else {
     123            0 :                 double val = e.readDouble();
     124            0 :                 if (val < 0.0) {
     125            0 :                     val = 0;
     126              :                 }
     127            0 :                 measure->setUserStretch(val);
     128              :             }
     129          741 :         } else if (tag == "noOffset") {
     130            0 :             measure->setMeasureNumberOffset(e.readInt());
     131          741 :         } else if (tag == "measureNumberMode") {
     132            0 :             measure->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
     133          741 :         } else if (tag == "irregular") {
     134            1 :             measure->setExcludeFromNumbering(e.readBool());
     135          740 :         } else if (tag == "breakMultiMeasureRest") {
     136           25 :             measure->setBreakMultiMeasureRest(e.readBool());
     137          715 :         } else if (tag == "startRepeat") {
     138           68 :             measure->setRepeatStart(true);
     139           68 :             e.readNext();
     140          647 :         } else if (tag == "endRepeat") {
     141          106 :             measure->setRepeatCount(e.readInt());
     142          106 :             measure->setRepeatEnd(true);
     143          541 :         } else if (tag == "vspacer" || tag == "vspacerDown") {
     144            0 :             if (!measure->mstaves()[staffIdx]->vspacerDown()) {
     145            0 :                 Spacer* spacer = Factory::createSpacer(measure);
     146            0 :                 spacer->setSpacerType(SpacerType::DOWN);
     147            0 :                 spacer->setTrack(staffIdx * VOICES);
     148            0 :                 measure->add(spacer);
     149              :             }
     150            0 :             measure->mstaves()[staffIdx]->vspacerDown()->setGap(Spatium(e.readDouble()));
     151          541 :         } else if (tag == "vspacerFixed") {
     152            0 :             if (!measure->mstaves()[staffIdx]->vspacerDown()) {
     153            0 :                 Spacer* spacer = Factory::createSpacer(measure);
     154            0 :                 spacer->setSpacerType(SpacerType::FIXED);
     155            0 :                 spacer->setTrack(staffIdx * VOICES);
     156            0 :                 measure->add(spacer);
     157              :             }
     158            0 :             measure->mstaves()[staffIdx]->vspacerDown()->setGap(Spatium(e.readDouble()));
     159          541 :         } else if (tag == "vspacerUp") {
     160            0 :             if (!measure->mstaves()[staffIdx]->vspacerUp()) {
     161            0 :                 Spacer* spacer = Factory::createSpacer(measure);
     162            0 :                 spacer->setSpacerType(SpacerType::UP);
     163            0 :                 spacer->setTrack(staffIdx * VOICES);
     164            0 :                 measure->add(spacer);
     165              :             }
     166            0 :             measure->mstaves()[staffIdx]->vspacerUp()->setGap(Spatium(e.readDouble()));
     167          541 :         } else if (tag == "visible") {
     168            0 :             measure->mstaves()[staffIdx]->setVisible(e.readInt());
     169          541 :         } else if ((tag == "slashStyle") || (tag == "stemless")) {
     170            0 :             measure->mstaves()[staffIdx]->setStemless(e.readInt());
     171          541 :         } else if (tag == "measureRepeatCount") {
     172           23 :             measure->setMeasureRepeatCount(e.readInt(), staffIdx);
     173          518 :         } else if (tag == "SystemDivider") {
     174            0 :             SystemDivider* sd = new SystemDivider(ctx.dummy());
     175            0 :             TRead::read(sd, e, ctx);
     176              :             //! TODO Looks like a bug.
     177              :             //! The SystemDivider parent must be System
     178              :             //! there is a method: `System* system() const { return (System*)parent(); }`,
     179              :             //! but when we add it to Measure, the parent will be rewritten.
     180            0 :             measure->add(sd);
     181          518 :         } else if (tag == "multiMeasureRest") {
     182           30 :             measure->setMMRestCount(e.readInt());
     183              :             // set tick to previous measure
     184           30 :             measure->setTick(ctx.lastMeasure()->tick());
     185           30 :             ctx.setTick(ctx.lastMeasure()->tick());
     186          488 :         } else if (tag == "MeasureNumber") {
     187            1 :             MeasureNumber* noText = new MeasureNumber(measure);
     188            1 :             TRead::read(noText, e, ctx);
     189            1 :             noText->setTrack(ctx.track());
     190            1 :             measure->add(noText);
     191          487 :         } else if (tag == "MMRestRange") {
     192            0 :             MMRestRange* range = new MMRestRange(measure);
     193            0 :             TRead::read(range, e, ctx);
     194            0 :             range->setTrack(ctx.track());
     195            0 :             measure->add(range);
     196          487 :         } else if (TRead::readProperties(static_cast<MeasureBase*>(measure), e, ctx)) {
     197              :         } else {
     198            0 :             e.unknown();
     199              :         }
     200              :     }
     201         5112 :     ctx.checkConnectors();
     202         5112 :     if (measure->isMMRest()) {
     203           30 :         Measure* lm = ctx.lastMeasure();
     204           30 :         ctx.setTick(lm->endTick());
     205              :     }
     206         5112 :     ctx.setCurrentMeasure(nullptr);
     207              : 
     208         5112 :     measure->connectTremolo();
     209              : }
     210              : 
     211         5310 : void MeasureRead::readVoice(Measure* measure, XmlReader& e, ReadContext& ctx, int staffIdx, bool irregular)
     212              : {
     213         5310 :     Segment* segment = nullptr;
     214         5310 :     std::vector<Chord*> graceNotes;
     215         5310 :     Beam* startingBeam = nullptr;
     216         5310 :     Tuplet* tuplet = nullptr;
     217         5310 :     Fermata* fermata = nullptr;
     218              : 
     219         5310 :     Staff* staff = ctx.staff(staffIdx);
     220         5310 :     Fraction timeStretch(staff->timeStretch(measure->tick()));
     221              : 
     222        22383 :     while (e.readNextStartElement()) {
     223        17073 :         const AsciiStringView tag(e.name());
     224              : 
     225        17073 :         if (tag == "location") {
     226           81 :             Location loc = Location::relative();
     227           81 :             TRead::read(&loc, e, ctx);
     228           81 :             ctx.setLocation(loc);
     229        16992 :         } else if (tag == "tick") {             // obsolete?
     230            0 :             LOGD() << "read midi tick";
     231            0 :             ctx.setTick(Fraction::fromTicks(ctx.fileDivision(e.readInt())));
     232        16992 :         } else if (tag == "BarLine") {
     233          752 :             BarLine* barLine = Factory::createBarLine(ctx.dummy());
     234          752 :             barLine->setTrack(ctx.track());
     235          752 :             TRead::read(barLine, e, ctx);
     236              :             //
     237              :             //  StartRepeatBarLine: at rtick == 0, always BarLineType::START_REPEAT
     238              :             //  BarLine:            in the middle of a measure, has no semantic
     239              :             //  EndBarLine:         at the end of a measure
     240              :             //  BeginBarLine:       first segment of a measure, systemic barline
     241              : 
     242          752 :             SegmentType st = SegmentType::Invalid;
     243          752 :             Fraction t = ctx.tick() - measure->tick();
     244          752 :             if (t.isNotZero() && (t != measure->ticks())) {
     245            3 :                 st = SegmentType::BarLine;
     246          749 :             } else if (barLine->barLineType() == BarLineType::START_REPEAT && t.isZero()) {
     247           10 :                 st = SegmentType::StartRepeatBarLine;
     248          739 :             } else if (barLine->barLineType() == BarLineType::START_REPEAT && t == measure->ticks()) {
     249              :                 // old version, ignore
     250            0 :                 delete barLine;
     251            0 :                 barLine = 0;
     252          739 :             } else if (t.isZero() && segment == 0) {
     253            0 :                 st = SegmentType::BeginBarLine;
     254              :             } else {
     255          739 :                 st = SegmentType::EndBarLine;
     256              :             }
     257          752 :             if (barLine) {
     258          752 :                 segment = measure->getSegmentR(st, t);
     259          752 :                 segment->add(barLine);
     260              :             }
     261          752 :             if (fermata) {
     262            0 :                 segment->add(fermata);
     263            0 :                 fermata = nullptr;
     264              :             }
     265        16240 :         } else if (tag == "Chord") {
     266         7316 :             Chord* chord = Factory::createChord(ctx.dummy());
     267         7316 :             chord->setTrack(ctx.track());
     268         7316 :             TRead::read(chord, e, ctx);
     269         7316 :             if (startingBeam) {
     270          768 :                 startingBeam->add(chord);         // also calls chord->setBeam(startingBeam)
     271          768 :                 startingBeam = nullptr;
     272              :             }
     273              : //                  if (tuplet && !chord->isGrace())
     274              : //                        chord->readAddTuplet(tuplet);
     275         7316 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     276         7316 :             if (chord->noteType() != NoteType::NORMAL) {
     277          101 :                 graceNotes.push_back(chord);
     278              :             } else {
     279         7215 :                 segment->add(chord);
     280         7316 :                 for (size_t i = 0; i < graceNotes.size(); ++i) {
     281          101 :                     Chord* gc = graceNotes[i];
     282          101 :                     gc->setGraceIndex(static_cast<int>(i));
     283          101 :                     chord->add(gc);
     284              :                 }
     285         7215 :                 graceNotes.clear();
     286         7215 :                 if (tuplet) {
     287         1008 :                     tuplet->add(chord);
     288              :                 }
     289         7215 :                 ctx.incTick(chord->actualTicks());
     290              :             }
     291         7316 :             if (fermata) {
     292           38 :                 segment->add(fermata);
     293           38 :                 fermata = nullptr;
     294              :             }
     295         8924 :         } else if (tag == "Rest") {
     296         4307 :             if (measure->isMMRest()) {
     297           30 :                 segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     298           30 :                 MMRest* mmr = Factory::createMMRest(segment);
     299           30 :                 mmr->setTrack(ctx.track());
     300           30 :                 TRead::read(mmr, e, ctx);
     301           30 :                 segment->add(mmr);
     302           30 :                 ctx.incTick(mmr->actualTicks());
     303              :             } else {
     304         4277 :                 segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     305         4277 :                 Rest* rest = Factory::createRest(segment);
     306         4277 :                 rest->setDurationType(DurationType::V_MEASURE);
     307         4277 :                 rest->setTicks(measure->timesig() / timeStretch);
     308         4277 :                 rest->setTrack(ctx.track());
     309         4277 :                 TRead::read(rest, e, ctx);
     310         4277 :                 if (startingBeam) {
     311            0 :                     startingBeam->add(rest); // also calls rest->setBeam(startingBeam)
     312            0 :                     startingBeam = nullptr;
     313              :                 }
     314              : 
     315         4277 :                 segment->add(rest);
     316         4277 :                 if (fermata) {
     317            0 :                     segment->add(fermata);
     318            0 :                     fermata = nullptr;
     319              :                 }
     320              : 
     321         4277 :                 if (!rest->ticks().isValid()) {    // hack
     322            0 :                     rest->setTicks(measure->timesig() / timeStretch);
     323              :                 }
     324              : 
     325         4277 :                 if (tuplet) {
     326           23 :                     tuplet->add(rest);
     327              :                 }
     328         4277 :                 ctx.incTick(rest->actualTicks());
     329              :             }
     330         4617 :         } else if (tag == "Breath") {
     331           31 :             segment = measure->getSegment(SegmentType::Breath, ctx.tick());
     332           31 :             Breath* breath = Factory::createBreath(segment);
     333           31 :             breath->setTrack(ctx.track());
     334           31 :             breath->setPlacement(breath->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
     335           31 :             TRead::read(breath, e, ctx);
     336           31 :             segment->add(breath);
     337         4586 :         } else if (tag == "Spanner") {
     338          662 :             TRead::readSpanner(e, ctx, measure, ctx.track());
     339         3924 :         } else if (tag == "MeasureRepeat" || tag == "RepeatMeasure") {
     340              :             //             4.x                       3.x
     341           41 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     342           41 :             MeasureRepeat* mr = Factory::createMeasureRepeat(segment);
     343           41 :             mr->setTrack(ctx.track());
     344           41 :             TRead::read(mr, e, ctx);
     345           41 :             if (!mr->numMeasures()) {
     346           27 :                 mr->setNumMeasures(1); // 3.x doesn't have any other possibilities
     347              :             }
     348           41 :             if (!measure->measureRepeatCount(staffIdx)) {
     349           27 :                 measure->setMeasureRepeatCount(1, staffIdx);
     350              :             }
     351           41 :             segment->add(mr);
     352           41 :             ctx.incTick(measure->ticks());
     353         3883 :         } else if (tag == "Clef") {
     354          274 :             Clef* clef = Factory::createClef(ctx.dummy());
     355          274 :             clef->setTrack(ctx.track());
     356          274 :             bool isHeader = false;
     357          274 :             TRead::read(clef, e, ctx, &isHeader);
     358          274 :             clef->setGenerated(false);
     359              : 
     360          274 :             bool header = false;
     361          274 :             if (ctx.score()->mscVersion() < 410) {
     362              :                 /***********************************************************************
     363              :                  * LEGACY: we used to try to guess if the clef is a header based
     364              :                  * on context, which is very unreliable. After 4.1, we just TAG it.
     365              :                  * *********************************************************************/
     366              :                 // there may be more than one clef segment for same tick position
     367              :                 // the first clef may be missing and is added later in layout
     368          274 :                 if (ctx.tick() != measure->tick()) {
     369           58 :                     header = false;
     370          216 :                 } else if (!segment) {
     371          214 :                     header = true;
     372              :                 } else {
     373            2 :                     header = true;
     374            4 :                     for (Segment* s = measure->first(); s && s->rtick().isZero(); s = s->next()) {
     375            4 :                         if (s->isKeySigType() || s->isTimeSigType()) {
     376              :                             // hack: there may be other segment types which should
     377              :                             // generate a clef at current position
     378            2 :                             header = false;
     379            2 :                             break;
     380              :                         }
     381              :                     }
     382              :                 }
     383              :             } else {
     384            0 :                 header = isHeader;
     385              :             }
     386              : 
     387              :             // Clef segments are sorted on layout now.  Previously, clef barline position could be out of sync with segment placement.
     388          332 :             if (ctx.tick() != Fraction(0, 1) && ctx.tick() == measure->tick()
     389          332 :                 && !(measure->prevMeasure() && measure->prevMeasure()->repeatEnd()) && !header) {
     390            0 :                 clef->setClefToBarlinePosition(ClefToBarlinePosition::AFTER);
     391              :             }
     392              : 
     393          274 :             segment = measure->getSegment(header ? SegmentType::HeaderClef : SegmentType::Clef, ctx.tick());
     394          274 :             segment->add(clef);
     395         3609 :         } else if (tag == "TimeSig") {
     396          797 :             TimeSig* ts = Factory::createTimeSig(ctx.dummy());
     397          797 :             ts->setTrack(ctx.track());
     398          797 :             TRead::read(ts, e, ctx);
     399              :             // if time sig not at beginning of measure => courtesy time sig
     400          797 :             Fraction currTick = ctx.tick();
     401          797 :             bool courtesySig = (currTick > measure->tick());
     402          797 :             if (courtesySig) {
     403              :                 // if courtesy sig., just add it without map processing
     404            0 :                 segment = measure->getSegment(SegmentType::TimeSigAnnounce, currTick);
     405            0 :                 segment->add(ts);
     406              :             } else {
     407              :                 // if 'real' time sig., do full process
     408          797 :                 segment = measure->getSegment(SegmentType::TimeSig, currTick);
     409          797 :                 segment->add(ts);
     410              : 
     411          797 :                 timeStretch = ts->stretch().reduced();
     412          797 :                 measure->setTimesig(ts->sig() / timeStretch);
     413              : 
     414          797 :                 if (!irregular) {
     415          783 :                     measure->setTicks(measure->timesig());
     416              :                 }
     417              :             }
     418         2812 :         } else if (tag == "KeySig") {
     419          148 :             KeySig* ks = Factory::createKeySig(ctx.dummy());
     420          148 :             ks->setTrack(ctx.track());
     421          148 :             TRead::read(ks, e, ctx);
     422          148 :             Fraction curTick = ctx.tick();
     423              :             // if key sig not at beginning of measure => courtesy key sig
     424          148 :             bool courtesySig = (curTick == measure->endTick());
     425          148 :             segment = measure->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
     426          148 :             segment->add(ks);
     427          148 :             if (!courtesySig) {
     428          148 :                 staff->setKey(curTick, ks->keySigEvent());
     429              :             }
     430         2664 :         } else if (tag == "Text") {
     431            0 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     432            0 :             StaffText* t = Factory::createStaffText(segment);
     433            0 :             t->setTrack(ctx.track());
     434            0 :             TRead::read(t, e, ctx);
     435            0 :             if (t->empty()) {
     436            0 :                 LOGD("==reading empty text: deleted");
     437            0 :                 delete t;
     438              :             } else {
     439            0 :                 segment->add(t);
     440              :             }
     441              :         }
     442              :         //----------------------------------------------------
     443              :         // Annotation
     444         2664 :         else if (tag == "Dynamic") {
     445           65 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     446           65 :             Dynamic* dyn = Factory::createDynamic(segment);
     447           65 :             dyn->setTrack(ctx.track());
     448           65 :             TRead::read(dyn, e, ctx);
     449           65 :             segment->add(dyn);
     450         2599 :         } else if (tag == "Expression") {
     451            0 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     452            0 :             Expression* expr = Factory::createExpression(segment);
     453            0 :             expr->setTrack(ctx.track());
     454            0 :             TRead::read(expr, e, ctx);
     455            0 :             segment->add(expr);
     456         2599 :         } else if (tag == "Harmony") {
     457              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     458              :             // for symbols attached to anything but a measure
     459          578 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     460          578 :             Harmony* el = Factory::createHarmony(segment);
     461              : 
     462          578 :             el->setTrack(ctx.track());
     463          578 :             TRead::read(el, e, ctx);
     464          578 :             if (el->systemFlag() && el->isTopSystemObject()) {
     465            0 :                 el->setTrack(0); // original system object always goes on top
     466              :             }
     467          578 :             if (el->chords().empty()) {
     468              :                 // Invalid harmony
     469            0 :                 delete el;
     470              :             } else {
     471          578 :                 segment->add(el);
     472              :             }
     473         2021 :         } else if (tag == "FretDiagram") {
     474              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     475              :             // for symbols attached to anything but a measure
     476           41 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     477           41 :             FretDiagram* el = Factory::createFretDiagram(segment);
     478              : 
     479           41 :             el->setTrack(ctx.track());
     480           41 :             TRead::read(el, e, ctx);
     481           41 :             if (el->systemFlag() && el->isTopSystemObject()) {
     482            0 :                 el->setTrack(0); // original system object always goes on top
     483              :             }
     484           41 :             segment->add(el);
     485         1980 :         } else if (tag == "TremoloBar") {
     486              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     487              :             // for symbols attached to anything but a measure
     488           19 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     489           19 :             TremoloBar* el = Factory::createTremoloBar(segment);
     490           19 :             el->setTrack(ctx.track());
     491           19 :             TRead::read(el, e, ctx);
     492           19 :             if (el->systemFlag() && el->isTopSystemObject()) {
     493            0 :                 el->setTrack(0); // original system object always goes on top
     494              :             }
     495           19 :             segment->add(el);
     496         1961 :         } else if (tag == "Symbol") {
     497              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     498              :             // for symbols attached to anything but a measure
     499           18 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     500           18 :             Symbol* el = Factory::createSymbol(segment);
     501              : 
     502           18 :             el->setTrack(ctx.track());
     503           18 :             TRead::read(el, e, ctx);
     504           18 :             if (el->systemFlag() && el->isTopSystemObject()) {
     505            0 :                 el->setTrack(0); // original system object always goes on top
     506              :             }
     507           18 :             segment->add(el);
     508         1943 :         } else if (tag == "Tempo") {
     509              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     510              :             // for symbols attached to anything but a measure
     511          134 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     512          134 :             TempoText* el = Factory::createTempoText(segment);
     513              : 
     514          134 :             el->setTrack(ctx.track());
     515          134 :             TRead::read(el, e, ctx);
     516          134 :             if (el->systemFlag() && el->isTopSystemObject()) {
     517          134 :                 el->setTrack(0); // original system object always goes on top
     518              :             }
     519          134 :             segment->add(el);
     520         1809 :         } else if (tag == "StaffText") {
     521              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     522              :             // for symbols attached to anything but a measure
     523          162 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     524          162 :             StaffText* el = Factory::createStaffText(segment);
     525          162 :             el->setTrack(ctx.track());
     526          162 :             TRead::read(el, e, ctx);
     527          162 :             if (el->systemFlag() && el->isTopSystemObject()) {
     528            0 :                 el->setTrack(0);     // original system object always goes on top
     529              :             }
     530          162 :             segment->add(el);
     531         1647 :         } else if (tag == "Sticking"
     532         1647 :                    || tag == "SystemText"
     533         1613 :                    || tag == "PlayTechAnnotation"
     534         1611 :                    || tag == "RehearsalMark"
     535         1558 :                    || tag == "InstrumentChange"
     536         1533 :                    || tag == "StaffState"
     537         1533 :                    || tag == "FiguredBass"
     538         3294 :                    || tag == "HarpPedalDiagram"
     539              :                    ) {
     540              :             // hack - getSegment needed because tick tags are unreliable in 1.3 scores
     541              :             // for symbols attached to anything but a measure
     542          156 :             segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     543          156 :             EngravingItem* el = Factory::createItemByName(tag, segment);
     544              : 
     545          156 :             el->setTrack(ctx.track());
     546          156 :             TRead::readItem(el, e, ctx);
     547          156 :             if (el->systemFlag() && el->isTopSystemObject()) {
     548           87 :                 el->setTrack(0); // original system object always goes on top
     549              :             }
     550          156 :             segment->add(el);
     551          156 :             if (el->isInstrumentChange()) {
     552           25 :                 Fraction tick = ctx.tick();
     553           25 :                 Key key = staff->key(tick);
     554           25 :                 Key cKey = staff->concertKey(tick);
     555           25 :                 Interval vi = staff->part()->instrument(tick)->transpose();
     556           25 :                 Interval vk = calculateInterval(cKey, key);
     557           25 :                 bool concertPitch = ctx.style().styleB(Sid::concertPitch);
     558           25 :                 if (!concertPitch && vk.chromatic != std::abs(vi.chromatic)) {
     559            4 :                     InstrumentChange* ic = toInstrumentChange(el);
     560            5 :                     for (KeySig* ks : ic->keySigs(true)) {
     561            1 :                         KeySigEvent ke = ks->keySigEvent();
     562            1 :                         ke.setConcertKey(Transpose::transposeKey(key, vi));
     563            1 :                         ke.setKey(key);
     564            1 :                         ks->setKeySigEvent(ke);
     565              :                         //segment->add(ks);
     566            1 :                         staff->setKey(tick, ke);
     567            5 :                     }
     568              :                 }
     569              :             }
     570         1491 :         } else if (tag == "Fermata") {
     571           39 :             fermata = Factory::createFermata(ctx.dummy());
     572           39 :             fermata->setTrack(ctx.track());
     573           39 :             fermata->setPlacement(fermata->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
     574           39 :             TRead::read(fermata, e, ctx);
     575         1452 :         } else if (tag == "Image") {
     576            0 :             if (MScore::noImages) {
     577            0 :                 e.skipCurrentElement();
     578              :             } else {
     579            0 :                 segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
     580            0 :                 Image* el = Factory::createImage(segment);
     581            0 :                 el->setTrack(ctx.track());
     582            0 :                 TRead::read(el, e, ctx);
     583            0 :                 segment->add(el);
     584              :             }
     585              :         }
     586              :         //----------------------------------------------------
     587         1452 :         else if (tag == "Tuplet") {
     588          342 :             Tuplet* oldTuplet = tuplet;
     589          342 :             tuplet = Factory::createTuplet(measure);
     590          342 :             tuplet->setTrack(ctx.track());
     591          342 :             tuplet->setTick(ctx.tick());
     592          342 :             TRead::read(tuplet, e, ctx);
     593          342 :             if (oldTuplet) {
     594           14 :                 oldTuplet->add(tuplet);
     595              :             }
     596         1110 :         } else if (tag == "endTuplet") {
     597          342 :             if (!tuplet) {
     598            0 :                 LOGD("Measure::read: encountered <endTuplet/> when no tuplet was started");
     599            0 :                 e.skipCurrentElement();
     600            0 :                 continue;
     601              :             }
     602          342 :             Tuplet* oldTuplet = tuplet;
     603          342 :             tuplet = tuplet->tuplet();
     604          342 :             if (oldTuplet->elements().empty()) {
     605              :                 // this should not happen and is a sign of input file corruption
     606            0 :                 LOGD("Measure:read: empty tuplet in measure index=%d, input file corrupted?", ctx.currentMeasureIndex());
     607            0 :                 if (tuplet) {
     608            0 :                     tuplet->remove(oldTuplet);
     609              :                 }
     610            0 :                 delete oldTuplet;
     611              :             }
     612          342 :             e.readNext();
     613          768 :         } else if (tag == "Beam") {
     614          768 :             Beam* beam = Factory::createBeam(ctx.score());
     615          768 :             beam->setTrack(ctx.track());
     616          768 :             TRead::read(beam, e, ctx);
     617          768 :             if (startingBeam) {
     618            0 :                 LOGD("The read beam was not used");
     619            0 :                 delete startingBeam;
     620              :             }
     621          768 :             startingBeam = beam;
     622            0 :         } else if (tag == "Segment" && segment) {
     623            0 :             read400::TRead::read(segment, e, ctx);
     624            0 :         } else if (tag == "Ambitus") {
     625            0 :             segment = measure->getSegment(SegmentType::Ambitus, ctx.tick());
     626            0 :             Ambitus* range = Factory::createAmbitus(segment);
     627            0 :             TRead::read(range, e, ctx);
     628            0 :             range->setTrack(trackZeroVoice(ctx.track()));
     629            0 :             segment->add(range);
     630              :         } else {
     631            0 :             e.unknown();
     632              :         }
     633              :     }
     634         5310 :     if (startingBeam) {
     635            0 :         LOGD("The read beam was not used");
     636            0 :         delete startingBeam;
     637              :     }
     638         5310 :     if (tuplet) {
     639            0 :         LOGD("Measure:readVoice: measure index=%d, <endTuplet/> not found", ctx.currentMeasureIndex());
     640            0 :         if (tuplet->elements().empty()) {
     641            0 :             if (tuplet->tuplet()) {
     642            0 :                 tuplet->tuplet()->remove(tuplet);
     643              :             }
     644            0 :             delete tuplet;
     645              :         }
     646              :     }
     647         5310 :     if (fermata) {
     648            1 :         SegmentType st = (ctx.tick() == measure->endTick() ? SegmentType::EndBarLine : SegmentType::ChordRest);
     649            1 :         segment = measure->getSegment(st, ctx.tick());
     650            1 :         segment->add(fermata);
     651            1 :         fermata = nullptr;
     652              :     }
     653         5310 : }
        

Generated by: LCOV version 2.0-1