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 "measurewrite.h"
23 :
24 : #include "dom/measure.h"
25 : #include "dom/measurenumber.h"
26 : #include "dom/mmrestrange.h"
27 : #include "dom/spacer.h"
28 : #include "dom/textlinebase.h"
29 :
30 : #include "types/typesconv.h"
31 : #include "twrite.h"
32 :
33 : using namespace mu::engraving;
34 : using namespace mu::engraving::write;
35 :
36 16371 : void MeasureWrite::writeMeasure(const Measure* measure, XmlWriter& xml, WriteContext& ctx,
37 : staff_idx_t staff,
38 : bool writeSystemElements,
39 : bool forceTimeSig)
40 : {
41 16371 : if (MScore::debugMode) {
42 0 : const int mno = measure->measureNumber() + 1;
43 0 : xml.comment(String(u"Measure %1").arg(mno));
44 : }
45 16371 : if (measure->ticks() != measure->timesig()) {
46 : // this is an irregular measure
47 616 : xml.startElement(measure, { { "len", measure->ticks().toString() } });
48 : } else {
49 16217 : xml.startElement(measure);
50 : }
51 :
52 16371 : ctx.setCurTick(measure->tick());
53 16371 : ctx.setCurTrack(staff * VOICES);
54 :
55 16371 : if (measure->mmRestCount() > 0) {
56 34 : xml.tag("multiMeasureRest", measure->mmRestCount());
57 : }
58 16371 : if (writeSystemElements) {
59 11952 : TWrite::writeItemEid(measure, xml);
60 :
61 11952 : if (measure->isMMRest()) {
62 29 : Measure* lastMeasure = measure->mmRestLast();
63 29 : EID eidOfLastMeasure = lastMeasure->eid();
64 29 : if (!eidOfLastMeasure.isValid()) {
65 19 : eidOfLastMeasure = lastMeasure->assignNewEID();
66 : }
67 29 : xml.tag("mmRestLast", eidOfLastMeasure.toStdString());
68 : }
69 :
70 11952 : if (measure->repeatStart()) {
71 35 : xml.tag("startRepeat");
72 : }
73 11952 : if (measure->repeatEnd()) {
74 58 : xml.tag("endRepeat", measure->repeatCount());
75 : }
76 11952 : TWrite::writeProperty(measure, xml, Pid::EXCLUDE_FROM_NUMBERING);
77 11952 : TWrite::writeProperty(measure, xml, Pid::BREAK_MMR);
78 11952 : TWrite::writeProperty(measure, xml, Pid::USER_STRETCH);
79 11952 : TWrite::writeProperty(measure, xml, Pid::MEASURE_NUMBER_OFFSET);
80 11952 : TWrite::writeProperty(measure, xml, Pid::MEASURE_NUMBER_MODE);
81 : }
82 16371 : MStaff* mstaff = measure->mstaves()[staff];
83 16371 : if (mstaff->measureNumber() && !mstaff->measureNumber()->generated()) {
84 0 : TWrite::write(mstaff->measureNumber(), xml, ctx);
85 : }
86 :
87 16371 : if (mstaff->mmRangeText() && !mstaff->mmRangeText()->generated()) {
88 0 : TWrite::write(mstaff->mmRangeText(), xml, ctx);
89 : }
90 :
91 16371 : if (mstaff->vspacerUp()) {
92 0 : xml.tag("vspacerUp", mstaff->vspacerUp()->gap().val());
93 : }
94 16371 : if (mstaff->vspacerDown()) {
95 0 : if (mstaff->vspacerDown()->spacerType() == SpacerType::FIXED) {
96 0 : xml.tag("vspacerFixed", mstaff->vspacerDown()->gap().val());
97 : } else {
98 0 : xml.tag("vspacerDown", mstaff->vspacerDown()->gap().val());
99 : }
100 : }
101 16371 : if (!mstaff->visible()) {
102 0 : xml.tag("visible", mstaff->visible());
103 : }
104 16371 : if (mstaff->stemless()) {
105 6 : xml.tag("stemless", mstaff->stemless());
106 : }
107 16371 : if (mstaff->hideIfEmpty() != AutoOnOff::AUTO) {
108 0 : xml.tag("hideIfEmpty", TConv::toXml(mstaff->hideIfEmpty()));
109 : }
110 16371 : if (mstaff->measureRepeatCount()) {
111 164 : xml.tag("measureRepeatCount", mstaff->measureRepeatCount());
112 : }
113 :
114 16371 : track_idx_t strack = staff * VOICES;
115 16371 : track_idx_t etrack = strack + VOICES;
116 17151 : for (const EngravingItem* e : measure->el()) {
117 780 : if (e->generated()) {
118 0 : continue;
119 : }
120 :
121 780 : bool writeSystem = writeSystemElements;
122 780 : if (e->systemFlag()) {
123 780 : ElementType et = e->type();
124 780 : if ((et == ElementType::REHEARSAL_MARK)
125 780 : || (et == ElementType::SYSTEM_TEXT)
126 780 : || (et == ElementType::TRIPLET_FEEL)
127 780 : || (et == ElementType::PLAYTECH_ANNOTATION)
128 780 : || (et == ElementType::JUMP)
129 705 : || (et == ElementType::MARKER)
130 626 : || (et == ElementType::TEMPO_TEXT)
131 1560 : || isSystemTextLine(e)) {
132 154 : writeSystem = (e->staffIdx() == staff); // always show these on appropriate staves
133 : }
134 : }
135 :
136 780 : if (e->staffIdx() != staff) {
137 115 : if (!e->systemFlag() || (e->systemFlag() && !writeSystem)) {
138 104 : continue;
139 : }
140 : }
141 :
142 676 : TWrite::writeItem(e, xml, ctx);
143 : }
144 16371 : assert(measure->first());
145 16371 : assert(measure->last());
146 16371 : if (measure->first() && measure->last()) {
147 16371 : TWrite::writeSegments(xml, ctx, strack, etrack, segmentsToWrite(measure), writeSystemElements, forceTimeSig);
148 : }
149 :
150 16371 : xml.endElement();
151 16679 : }
152 :
153 16371 : std::vector<Segment*> MeasureWrite::segmentsToWrite(const Measure* measure)
154 : {
155 : /* Layout moves the time signature at the start of an MM rest from its first underlying measure onto the MM rest.
156 : * During writing, the MM rest is written after that measure, and the reader needs the time signature before the measure's
157 : * contents to get its length right, so we write the signature in the underlying measure instead of the MM. */
158 :
159 71 : auto startTimeSigSegment = [](const Measure* mmRest) {
160 71 : return mmRest->findSegmentR(SegmentType::TimeSig, Fraction(0, 1));
161 : };
162 :
163 16371 : const Segment* segToSkip = nullptr;
164 16371 : Segment* segToInsert = nullptr;
165 16371 : if (measure->isMMRest()) {
166 34 : segToSkip = startTimeSigSegment(measure);
167 16337 : } else if (measure->mmRest()) {
168 37 : segToInsert = startTimeSigSegment(measure->mmRest());
169 : }
170 :
171 16371 : std::vector<Segment*> segments;
172 77066 : for (Segment* s = measure->first(); s; s = s->next()) {
173 : // insert it where the measure would place it if it had this segment itself
174 60695 : if (segToInsert && (s->rtick().isNotZero() || segToInsert->goesBefore(s))) {
175 17 : segments.push_back(segToInsert);
176 17 : segToInsert = nullptr;
177 : }
178 60695 : if (s != segToSkip) {
179 60679 : segments.push_back(s);
180 : }
181 : }
182 16371 : if (segToInsert) {
183 0 : segments.push_back(segToInsert);
184 : }
185 :
186 16371 : return segments;
187 0 : }
|