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) 2026 MuseScore Limited
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 "measureread.h"
23 :
24 : #include "translation.h"
25 :
26 : #include "../dom/ambitus.h"
27 : #include "../dom/anchors.h"
28 : #include "../dom/barline.h"
29 : #include "../dom/beam.h"
30 : #include "../dom/breath.h"
31 : #include "../dom/chord.h"
32 : #include "../dom/dynamic.h"
33 : #include "../dom/expression.h"
34 : #include "../dom/factory.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/score.h"
45 : #include "../dom/segment.h"
46 : #include "../dom/spacer.h"
47 : #include "../dom/staff.h"
48 : #include "../dom/stafflines.h"
49 : #include "../dom/stafftext.h"
50 : #include "../dom/system.h"
51 : #include "../dom/systemdivider.h"
52 : #include "../dom/timesig.h"
53 : #include "../dom/tuplet.h"
54 : #include "../dom/harmony.h"
55 : #include "../dom/fret.h"
56 : #include "../dom/tremolobar.h"
57 : #include "../dom/tempotext.h"
58 : #include "../dom/image.h"
59 :
60 : #include "../types/typesconv.h"
61 : #include "tread.h"
62 :
63 : #include "log.h"
64 :
65 : using namespace mu::engraving;
66 : using namespace mu::engraving::read500;
67 :
68 6506 : void MeasureRead::readMeasure(Measure* measure, XmlReader& e, ReadContext& ctx, int staffIdx)
69 : {
70 6506 : IF_ASSERT_FAILED(ctx.isSameScore(measure)) {
71 0 : return;
72 : }
73 :
74 6506 : ctx.setCurrentMeasure(measure);
75 6506 : int nextTrack = staffIdx * VOICES;
76 6506 : ctx.setTrack(nextTrack);
77 :
78 6506 : for (int n = int(measure->mstaves().size()); n <= staffIdx; ++n) {
79 0 : Staff* staff = ctx.staff(n);
80 0 : MStaff* s = new MStaff;
81 0 : s->setLines(Factory::createStaffLines(measure));
82 0 : s->lines()->setTrack(n * VOICES);
83 0 : s->lines()->setVisible(!staff->isLinesInvisible(measure->tick()));
84 0 : measure->mstaves().push_back(s);
85 : }
86 :
87 6506 : bool irregular = false;
88 6506 : if (e.hasAttribute("len")) {
89 14 : bool ok = true;
90 14 : measure->setTicks(Fraction::fromString(e.attribute("len"), &ok));
91 14 : if (!ok || measure->ticks() < Fraction(1, 128)) {
92 0 : e.raiseError(muse::mtrc("engraving", "MSCX error at byte offset %1: invalid measure length: %2")
93 0 : .arg(e.byteOffset()).arg(e.attribute("len")));
94 0 : return;
95 : }
96 14 : irregular = true;
97 : }
98 :
99 17611 : while (e.readNextStartElement()) {
100 11105 : const AsciiStringView tag(e.name());
101 :
102 11105 : if (tag == "voice") {
103 6519 : ctx.setTrack(nextTrack++);
104 6519 : ctx.setTick(measure->tick());
105 6519 : readVoice(measure, e, ctx, staffIdx, irregular);
106 4586 : } else if (tag == "Marker" || tag == "Jump") {
107 6 : EngravingItem* el = Factory::createItemByName(tag, measure);
108 6 : el->setTrack(ctx.track());
109 6 : TRead::readItem(el, e, ctx);
110 6 : if (el->systemFlag() && el->isTopSystemObject()) {
111 6 : el->setTrack(0); // original system object always goes on top
112 : }
113 6 : measure->add(el);
114 4580 : } else if (tag == "stretch") {
115 0 : double val = e.readDouble();
116 0 : if (val < 0.0) {
117 0 : val = 0;
118 : }
119 0 : measure->setUserStretch(val);
120 4580 : } else if (tag == "noOffset") {
121 0 : measure->setMeasureNumberOffset(e.readInt());
122 4580 : } else if (tag == "measureNumberMode") {
123 0 : measure->setMeasureNumberMode(MeasureNumberMode(e.readInt()));
124 4580 : } else if (tag == "irregular") {
125 1 : measure->setExcludeFromNumbering(e.readBool());
126 4579 : } else if (tag == "breakMultiMeasureRest") {
127 0 : measure->setBreakMultiMeasureRest(e.readBool());
128 4579 : } else if (tag == "startRepeat") {
129 3 : measure->setRepeatStart(true);
130 3 : e.readNext();
131 4576 : } else if (tag == "endRepeat") {
132 9 : measure->setRepeatCount(e.readInt());
133 9 : measure->setRepeatEnd(true);
134 4567 : } else if (tag == "vspacer" || tag == "vspacerDown") {
135 0 : if (!measure->mstaves()[staffIdx]->vspacerDown()) {
136 0 : Spacer* spacer = Factory::createSpacer(measure);
137 0 : spacer->setSpacerType(SpacerType::DOWN);
138 0 : spacer->setTrack(staffIdx * VOICES);
139 0 : measure->add(spacer);
140 : }
141 0 : measure->mstaves()[staffIdx]->vspacerDown()->setGap(Spatium(e.readDouble()));
142 4567 : } else if (tag == "vspacerFixed") {
143 0 : if (!measure->mstaves()[staffIdx]->vspacerDown()) {
144 0 : Spacer* spacer = Factory::createSpacer(measure);
145 0 : spacer->setSpacerType(SpacerType::FIXED);
146 0 : spacer->setTrack(staffIdx * VOICES);
147 0 : measure->add(spacer);
148 : }
149 0 : measure->mstaves()[staffIdx]->vspacerDown()->setGap(Spatium(e.readDouble()));
150 4567 : } else if (tag == "vspacerUp") {
151 0 : if (!measure->mstaves()[staffIdx]->vspacerUp()) {
152 0 : Spacer* spacer = Factory::createSpacer(measure);
153 0 : spacer->setSpacerType(SpacerType::UP);
154 0 : spacer->setTrack(staffIdx * VOICES);
155 0 : measure->add(spacer);
156 : }
157 0 : measure->mstaves()[staffIdx]->vspacerUp()->setGap(Spatium(e.readDouble()));
158 4567 : } else if (tag == "visible") {
159 0 : measure->mstaves()[staffIdx]->setVisible(e.readInt());
160 4567 : } else if (tag == "stemless") {
161 2 : measure->mstaves()[staffIdx]->setStemless(e.readInt());
162 4565 : } else if (tag == "hideIfEmpty") {
163 0 : measure->mstaves()[staffIdx]->setHideIfEmpty(TConv::fromXml(e.readAsciiText(), AutoOnOff::AUTO));
164 4565 : } else if (tag == "measureRepeatCount") {
165 106 : measure->setMeasureRepeatCount(e.readInt(), staffIdx);
166 4459 : } else if (tag == "SystemDivider") {
167 0 : SystemDivider* sd = new SystemDivider(ctx.dummy());
168 0 : TRead::read(sd, e, ctx);
169 : //! TODO Looks like a bug.
170 : //! The SystemDivider parent must be System
171 : //! there is a method: `System* system() const { return (System*)parent(); }`,
172 : //! but when we add it to Measure, the parent will be rewritten.
173 0 : measure->add(sd);
174 4459 : } else if (tag == "multiMeasureRest") {
175 14 : measure->setMMRestCount(e.readInt());
176 : // set tick to previous measure
177 14 : measure->setTick(ctx.lastMeasure()->tick());
178 14 : ctx.setTick(ctx.lastMeasure()->tick());
179 4445 : } else if (tag == "mmRestLast") {
180 10 : AsciiStringView s = e.readAsciiText();
181 10 : EID eid = EID::fromStdString(s);
182 10 : DO_ASSERT(eid.isValid());
183 10 : ctx.addMMRestEndMeasureEID(measure, eid);
184 4435 : } else if (tag == "MeasureNumber") {
185 0 : MeasureNumber* noText = new MeasureNumber(measure);
186 0 : TRead::read(noText, e, ctx);
187 0 : noText->setTrack(ctx.track());
188 0 : measure->add(noText);
189 4435 : } else if (tag == "MMRestRange") {
190 0 : MMRestRange* range = new MMRestRange(measure);
191 0 : TRead::read(range, e, ctx);
192 0 : range->setTrack(ctx.track());
193 0 : measure->add(range);
194 4435 : } else if (TRead::readProperties(static_cast<MeasureBase*>(measure), e, ctx)) {
195 : } else {
196 0 : e.unknown();
197 : }
198 : }
199 6506 : if (measure->isMMRest()) {
200 14 : Measure* lm = ctx.lastMeasure();
201 14 : ctx.setTick(lm->endTick());
202 : }
203 6506 : ctx.setCurrentMeasure(nullptr);
204 :
205 6506 : measure->connectTremolo();
206 : }
207 :
208 6519 : void MeasureRead::readVoice(Measure* measure, XmlReader& e, ReadContext& ctx, int staffIdx, bool irregular)
209 : {
210 6519 : Segment* segment = nullptr;
211 6519 : std::vector<Chord*> graceNotes;
212 6519 : Beam* startingBeam = nullptr;
213 6519 : Tuplet* tuplet = nullptr;
214 6519 : Fermata* fermata = nullptr;
215 :
216 6519 : Staff* staff = ctx.staff(staffIdx);
217 6519 : Fraction timeStretch(staff->timeStretch(measure->tick()));
218 :
219 17062 : while (e.readNextStartElement()) {
220 10543 : const AsciiStringView tag(e.name());
221 :
222 10543 : if (tag == "location") {
223 2 : Location loc = Location::relative();
224 2 : TRead::read(&loc, e, ctx);
225 2 : ctx.setLocation(loc);
226 2 : if (loc.isTimeTick()) {
227 0 : EditTimeTickAnchors::createTimeTickAnchor(measure, ctx.tick() - measure->tick(), track2staff(ctx.track()));
228 : }
229 10541 : } else if (tag == "tick") { // obsolete?
230 0 : LOGD() << "read midi tick";
231 0 : ctx.setTick(Fraction::fromTicks(ctx.fileDivision(e.readInt())));
232 10541 : } else if (tag == "BarLine") {
233 15 : BarLine* barLine = Factory::createBarLine(ctx.dummy());
234 15 : barLine->setTrack(ctx.track());
235 15 : 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 15 : SegmentType st = SegmentType::Invalid;
243 15 : Fraction t = ctx.tick() - measure->tick();
244 15 : if (t.isNotZero() && (t != measure->ticks())) {
245 0 : st = SegmentType::BarLine;
246 15 : } else if (barLine->barLineType() == BarLineType::START_REPEAT && t.isZero()) {
247 0 : st = SegmentType::StartRepeatBarLine;
248 15 : } else if (barLine->barLineType() == BarLineType::START_REPEAT && t == measure->ticks()) {
249 : // old version, ignore
250 0 : delete barLine;
251 0 : barLine = 0;
252 15 : } else if (t.isZero() && segment == 0) {
253 0 : st = SegmentType::BeginBarLine;
254 : } else {
255 15 : st = SegmentType::EndBarLine;
256 : }
257 15 : if (barLine) {
258 15 : segment = measure->getSegmentR(st, t);
259 15 : segment->add(barLine);
260 : }
261 15 : if (fermata) {
262 0 : segment->add(fermata);
263 0 : fermata = nullptr;
264 : }
265 10526 : } else if (tag == "Chord") {
266 2717 : Chord* chord = Factory::createChord(ctx.dummy());
267 2717 : chord->setTrack(ctx.track());
268 2717 : TRead::read(chord, e, ctx);
269 2717 : if (startingBeam) {
270 453 : startingBeam->add(chord); // also calls chord->setBeam(startingBeam)
271 453 : startingBeam = nullptr;
272 : }
273 : // if (tuplet && !chord->isGrace())
274 : // chord->readAddTuplet(tuplet);
275 2717 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
276 2717 : if (chord->noteType() != NoteType::NORMAL) {
277 4 : graceNotes.push_back(chord);
278 : } else {
279 2713 : segment->add(chord);
280 2717 : for (size_t i = 0; i < graceNotes.size(); ++i) {
281 4 : Chord* gc = graceNotes[i];
282 4 : gc->setGraceIndex(static_cast<int>(i));
283 4 : chord->add(gc);
284 : }
285 2713 : graceNotes.clear();
286 2713 : if (tuplet) {
287 299 : tuplet->add(chord);
288 : }
289 2713 : ctx.incTick(chord->actualTicks());
290 : }
291 2717 : if (fermata) {
292 1 : segment->add(fermata);
293 1 : fermata = nullptr;
294 : }
295 7809 : } else if (tag == "Rest") {
296 6294 : if (measure->isMMRest()) {
297 14 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
298 14 : MMRest* mmr = Factory::createMMRest(segment);
299 14 : mmr->setTrack(ctx.track());
300 14 : TRead::read(mmr, e, ctx);
301 14 : segment->add(mmr);
302 14 : ctx.incTick(mmr->actualTicks());
303 : } else {
304 6280 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
305 6280 : Rest* rest = Factory::createRest(segment);
306 6280 : rest->setDurationType(DurationType::V_MEASURE);
307 6280 : rest->setTicks(measure->timesig() / timeStretch);
308 6280 : rest->setTrack(ctx.track());
309 6280 : TRead::read(rest, e, ctx);
310 6280 : if (startingBeam) {
311 0 : startingBeam->add(rest); // also calls rest->setBeam(startingBeam)
312 0 : startingBeam = nullptr;
313 : }
314 :
315 6280 : segment->add(rest);
316 6280 : if (fermata) {
317 0 : segment->add(fermata);
318 0 : fermata = nullptr;
319 : }
320 :
321 6280 : if (!rest->ticks().isValid()) { // hack
322 0 : rest->setTicks(measure->timesig() / timeStretch);
323 : }
324 :
325 6280 : if (tuplet) {
326 131 : tuplet->add(rest);
327 : }
328 6280 : ctx.incTick(rest->actualTicks());
329 : }
330 1515 : } else if (tag == "Breath") {
331 10 : segment = measure->getSegment(SegmentType::Breath, ctx.tick());
332 10 : Breath* breath = Factory::createBreath(segment);
333 10 : breath->setTrack(ctx.track());
334 10 : breath->setPlacement(breath->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
335 10 : TRead::read(breath, e, ctx);
336 10 : segment->add(breath);
337 1505 : } else if (tag == "MeasureRepeat" || tag == "RepeatMeasure") {
338 : // 4.x 3.x
339 50 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
340 50 : MeasureRepeat* mr = Factory::createMeasureRepeat(segment);
341 50 : mr->setTrack(ctx.track());
342 50 : TRead::read(mr, e, ctx);
343 50 : if (!mr->numMeasures()) {
344 0 : mr->setNumMeasures(1); // 3.x doesn't have any other possibilities
345 : }
346 50 : if (!measure->measureRepeatCount(staffIdx)) {
347 0 : measure->setMeasureRepeatCount(1, staffIdx);
348 : }
349 50 : segment->add(mr);
350 50 : ctx.incTick(measure->ticks());
351 1455 : } else if (tag == "Clef") {
352 170 : Clef* clef = Factory::createClef(ctx.dummy());
353 170 : clef->setTrack(ctx.track());
354 170 : bool isHeader = false;
355 170 : TRead::read(clef, e, ctx, &isHeader);
356 170 : clef->setGenerated(false);
357 :
358 170 : segment = measure->getSegment(isHeader ? SegmentType::HeaderClef : SegmentType::Clef, ctx.tick());
359 170 : segment->add(clef);
360 1285 : } else if (tag == "TimeSig") {
361 381 : TimeSig* ts = Factory::createTimeSig(ctx.dummy());
362 381 : ts->setTrack(ctx.track());
363 381 : TRead::read(ts, e, ctx);
364 :
365 381 : Fraction currTick = ctx.tick();
366 381 : bool courtesySig = ts->isCourtesy();
367 381 : segment = measure->getSegment(courtesySig ? SegmentType::TimeSigAnnounce : SegmentType::TimeSig, currTick);
368 381 : segment->add(ts);
369 :
370 381 : if (!courtesySig) {
371 381 : if (currTick == measure->endTick()) {
372 0 : segment->setEndOfMeasureChange(true);
373 0 : measure->setEndOfMeasureChange(true);
374 : }
375 :
376 381 : if (currTick == measure->tick()) {
377 381 : timeStretch = ts->stretch().reduced();
378 381 : measure->setTimesig(ts->sig() / timeStretch);
379 :
380 381 : if (!irregular) {
381 379 : measure->setTicks(measure->timesig());
382 : }
383 : }
384 :
385 381 : if (currTick > measure->tick()) {
386 0 : ctx.setTimeSigForNextMeasure(ts->sig() / ts->stretch().reduced());
387 : }
388 : }
389 904 : } else if (tag == "KeySig") {
390 74 : KeySig* ks = Factory::createKeySig(ctx.dummy());
391 74 : ks->setTrack(ctx.track());
392 74 : TRead::read(ks, e, ctx);
393 :
394 74 : Fraction curTick = ctx.tick();
395 74 : bool courtesySig = ks->isCourtesy();
396 74 : segment = measure->getSegment(courtesySig ? SegmentType::KeySigAnnounce : SegmentType::KeySig, curTick);
397 74 : segment->add(ks);
398 :
399 74 : if (!courtesySig) {
400 74 : if (curTick == measure->endTick()) {
401 0 : segment->setEndOfMeasureChange(true);
402 0 : measure->setEndOfMeasureChange(true);
403 : }
404 :
405 74 : staff->setKey(curTick, ks->keySigEvent());
406 : }
407 830 : } else if (tag == "Text") {
408 0 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
409 0 : StaffText* t = Factory::createStaffText(segment);
410 0 : t->setTrack(ctx.track());
411 0 : TRead::read(t, e, ctx);
412 0 : if (t->empty()) {
413 0 : LOGD("==reading empty text: deleted");
414 0 : delete t;
415 : } else {
416 0 : segment->add(t);
417 : }
418 : }
419 : //----------------------------------------------------
420 : // Annotation
421 830 : else if (tag == "Dynamic") {
422 95 : segment = measure->getChordRestOrTimeTickSegment(ctx.tick());
423 95 : Dynamic* dyn = Factory::createDynamic(segment);
424 95 : dyn->setTrack(ctx.track());
425 95 : TRead::read(dyn, e, ctx);
426 95 : segment->add(dyn);
427 735 : } else if (tag == "Expression") {
428 0 : segment = measure->getChordRestOrTimeTickSegment(ctx.tick());
429 0 : Expression* expr = Factory::createExpression(segment);
430 0 : expr->setTrack(ctx.track());
431 0 : TRead::read(expr, e, ctx);
432 0 : segment->add(expr);
433 735 : } else if (tag == "Harmony") {
434 3 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
435 3 : Harmony* el = Factory::createHarmony(segment);
436 :
437 3 : el->setTrack(ctx.track());
438 3 : TRead::read(el, e, ctx);
439 3 : if (el->systemFlag() && el->isTopSystemObject()) {
440 0 : el->setTrack(0); // original system object always goes on top
441 : }
442 3 : if (el->chords().empty()) {
443 : // Invalid harmony
444 0 : delete el;
445 : } else {
446 3 : segment->add(el);
447 : }
448 732 : } else if (tag == "FretDiagram") {
449 0 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
450 0 : FretDiagram* el = Factory::createFretDiagram(segment);
451 :
452 0 : el->setTrack(ctx.track());
453 0 : TRead::read(el, e, ctx);
454 0 : if (el->systemFlag() && el->isTopSystemObject()) {
455 0 : el->setTrack(0); // original system object always goes on top
456 : }
457 0 : segment->add(el);
458 732 : } else if (tag == "TremoloBar") {
459 0 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
460 0 : TremoloBar* el = Factory::createTremoloBar(segment);
461 0 : el->setTrack(ctx.track());
462 0 : TRead::read(el, e, ctx);
463 0 : if (el->systemFlag() && el->isTopSystemObject()) {
464 0 : el->setTrack(0); // original system object always goes on top
465 : }
466 0 : segment->add(el);
467 732 : } else if (tag == "Symbol") {
468 0 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
469 0 : Symbol* el = Factory::createSymbol(segment);
470 :
471 0 : el->setTrack(ctx.track());
472 0 : TRead::read(el, e, ctx);
473 0 : if (el->systemFlag() && el->isTopSystemObject()) {
474 0 : el->setTrack(0); // original system object always goes on top
475 : }
476 0 : segment->add(el);
477 732 : } else if (tag == "Tempo") {
478 103 : segment = measure->getChordRestOrTimeTickSegment(ctx.tick());
479 103 : TempoText* el = Factory::createTempoText(segment);
480 :
481 103 : el->setTrack(ctx.track());
482 103 : TRead::read(el, e, ctx);
483 103 : if (el->systemFlag() && el->isTopSystemObject()) {
484 103 : el->setTrack(0); // original system object always goes on top
485 : }
486 103 : segment->add(el);
487 629 : } else if (tag == "StaffText") {
488 15 : segment = measure->getChordRestOrTimeTickSegment(ctx.tick());
489 15 : StaffText* el = Factory::createStaffText(segment);
490 15 : el->setTrack(ctx.track());
491 15 : TRead::read(el, e, ctx);
492 15 : if (el->systemFlag() && el->isTopSystemObject()) {
493 0 : el->setTrack(0); // original system object always goes on top
494 : }
495 15 : segment->add(el);
496 614 : } else if (tag == "Sticking"
497 614 : || tag == "SystemText"
498 613 : || tag == "PlayTechAnnotation"
499 613 : || tag == "Capo"
500 613 : || tag == "StringTunings"
501 613 : || tag == "RehearsalMark"
502 613 : || tag == "InstrumentChange"
503 608 : || tag == "StaffState"
504 608 : || tag == "FiguredBass"
505 1228 : || tag == "HarpPedalDiagram"
506 : ) {
507 10 : EngravingItem* el = Factory::createItemByName(tag, ctx.dummy());
508 10 : if (el->allowTimeAnchor()) {
509 0 : segment = measure->getChordRestOrTimeTickSegment(ctx.tick());
510 : } else {
511 10 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
512 : }
513 :
514 10 : el->setTrack(ctx.track());
515 10 : TRead::readItem(el, e, ctx);
516 10 : if (el->systemFlag() && el->isTopSystemObject()) {
517 1 : el->setTrack(0); // original system object always goes on top
518 : }
519 10 : segment->add(el);
520 604 : } else if (tag == "Fermata") {
521 1 : fermata = Factory::createFermata(ctx.dummy());
522 1 : fermata->setTrack(ctx.track());
523 1 : fermata->setPlacement(fermata->track() & 1 ? PlacementV::BELOW : PlacementV::ABOVE);
524 1 : TRead::read(fermata, e, ctx);
525 603 : } else if (tag == "Image") {
526 0 : if (MScore::noImages) {
527 0 : e.skipCurrentElement();
528 : } else {
529 0 : segment = measure->getSegment(SegmentType::ChordRest, ctx.tick());
530 0 : Image* el = Factory::createImage(segment);
531 0 : el->setTrack(ctx.track());
532 0 : TRead::read(el, e, ctx);
533 0 : segment->add(el);
534 : }
535 603 : } else if (tag == "PlayCountText") {
536 0 : segment = measure->getSegment(SegmentType::EndBarLine, ctx.tick());
537 0 : EngravingItem* el = Factory::createItemByName(tag, segment);
538 :
539 0 : el->setTrack(ctx.track());
540 0 : TRead::readItem(el, e, ctx);
541 0 : if (el->systemFlag() && el->isTopSystemObject()) {
542 0 : el->setTrack(0); // original system object always goes on top
543 : }
544 0 : segment->add(el);
545 : }
546 : //----------------------------------------------------
547 603 : else if (tag == "Tuplet") {
548 75 : Tuplet* oldTuplet = tuplet;
549 75 : tuplet = Factory::createTuplet(measure);
550 75 : tuplet->setTrack(ctx.track());
551 75 : tuplet->setTick(ctx.tick());
552 75 : TRead::read(tuplet, e, ctx);
553 75 : if (oldTuplet) {
554 13 : oldTuplet->add(tuplet);
555 : }
556 528 : } else if (tag == "endTuplet") {
557 75 : if (!tuplet) {
558 0 : LOGD("Measure::read: encountered <endTuplet/> when no tuplet was started");
559 0 : e.skipCurrentElement();
560 0 : continue;
561 : }
562 75 : Tuplet* oldTuplet = tuplet;
563 75 : tuplet = tuplet->tuplet();
564 75 : if (oldTuplet->elements().empty()) {
565 : // this should not happen and is a sign of input file corruption
566 0 : LOGD("Measure:read: empty tuplet in measure index=%d, input file corrupted?", ctx.currentMeasureIndex());
567 0 : if (tuplet) {
568 0 : tuplet->remove(oldTuplet);
569 : }
570 0 : delete oldTuplet;
571 : }
572 75 : e.readNext();
573 453 : } else if (tag == "Beam") {
574 453 : Beam* beam = Factory::createBeam(ctx.score());
575 453 : beam->setTrack(ctx.track());
576 453 : TRead::read(beam, e, ctx);
577 453 : if (startingBeam) {
578 0 : LOGD("The read beam was not used");
579 0 : delete startingBeam;
580 : }
581 453 : startingBeam = beam;
582 0 : } else if (tag == "Segment" && segment) {
583 0 : TRead::read(segment, e, ctx);
584 0 : } else if (tag == "Ambitus") {
585 0 : segment = measure->getSegment(SegmentType::Ambitus, ctx.tick());
586 0 : Ambitus* range = Factory::createAmbitus(segment);
587 0 : TRead::read(range, e, ctx);
588 0 : range->setTrack(trackZeroVoice(ctx.track()));
589 0 : segment->add(range);
590 : } else {
591 0 : e.unknown();
592 : }
593 : }
594 6519 : if (startingBeam) {
595 0 : LOGD("The read beam was not used");
596 0 : delete startingBeam;
597 : }
598 6519 : if (tuplet) {
599 0 : LOGD("Measure:readVoice: measure index=%d, <endTuplet/> not found", ctx.currentMeasureIndex());
600 0 : if (tuplet->elements().empty()) {
601 0 : if (tuplet->tuplet()) {
602 0 : tuplet->tuplet()->remove(tuplet);
603 : }
604 0 : delete tuplet;
605 : }
606 : }
607 6519 : if (fermata) {
608 0 : SegmentType st = (ctx.tick() == measure->endTick() ? SegmentType::EndBarLine : SegmentType::ChordRest);
609 0 : segment = measure->getSegment(st, ctx.tick());
610 0 : segment->add(fermata);
611 0 : fermata = nullptr;
612 : }
613 6519 : }
|