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 "read302.h"
23 :
24 : #include "global/defer.h"
25 :
26 : #include "iengravingfont.h"
27 :
28 : #include "rw/compat/compatutils.h"
29 : #include "style/style.h"
30 :
31 : #include "dom/excerpt.h"
32 : #include "dom/factory.h"
33 : #include "dom/masterscore.h"
34 : #include "dom/part.h"
35 : #include "dom/score.h"
36 : #include "dom/scoreorder.h"
37 : #include "dom/spanner.h"
38 : #include "dom/staff.h"
39 : #include "dom/text.h"
40 :
41 : #include "../read400/staffrw.h"
42 : #include "../read400/tread.h"
43 : #include "../compat/readstyle.h"
44 :
45 : #include "log.h"
46 :
47 : using namespace mu;
48 : using namespace mu::engraving;
49 : using namespace mu::engraving::rw;
50 : using namespace mu::engraving::read400;
51 : using namespace mu::engraving::read302;
52 : using namespace mu::engraving::compat;
53 :
54 430 : bool Read302::readScoreTag(Score* score, XmlReader& e, ReadContext& ctx)
55 : {
56 8939 : while (e.readNextStartElement()) {
57 8509 : ctx.setTrack(muse::nidx);
58 8509 : const AsciiStringView tag(e.name());
59 8509 : if (tag == "Staff") {
60 751 : read400::StaffRead::readStaff(score, e, ctx);
61 7758 : } else if (tag == "Omr") {
62 0 : e.skipCurrentElement();
63 7758 : } else if (tag == "Audio") {
64 0 : e.skipCurrentElement();
65 7758 : } else if (tag == "showOmr") {
66 0 : e.skipCurrentElement();
67 7758 : } else if (tag == "playMode") {
68 0 : e.skipCurrentElement();
69 7758 : } else if (tag == "LayerTag") {
70 53 : e.skipCurrentElement();
71 7705 : } else if (tag == "Layer") {
72 0 : e.skipCurrentElement();
73 7705 : } else if (tag == "currentLayer") {
74 53 : e.skipCurrentElement();
75 7652 : } else if (tag == "Synthesizer") {
76 8 : score->m_synthesizerState.read(e);
77 7644 : } else if (tag == "page-offset") {
78 0 : score->m_pageNumberOffset = e.readInt();
79 7644 : } else if (tag == "Division") {
80 430 : score->m_fileDivision = e.readInt();
81 7214 : } else if (tag == "showInvisible") {
82 430 : score->m_showInvisible = e.readInt();
83 6784 : } else if (tag == "showUnprintable") {
84 430 : score->m_showUnprintable = e.readInt();
85 6354 : } else if (tag == "showFrames") {
86 430 : score->m_showFrames = e.readInt();
87 5924 : } else if (tag == "showMargins") {
88 430 : score->m_showPageborders = e.readInt();
89 5494 : } else if (tag == "markIrregularMeasures") {
90 0 : score->m_markIrregularMeasures = e.readInt();
91 5494 : } else if (tag == "Style") {
92 430 : double sp = score->style().value(Sid::spatium).toReal();
93 :
94 430 : ReadStyleHook::readStyleTag(score, e);
95 :
96 430 : if (ctx.overrideSpatium()) {
97 0 : ctx.setOriginalSpatium(score->style().spatium());
98 0 : score->style().set(Sid::spatium, sp);
99 : }
100 430 : score->m_engravingFont = score->engravingFonts()->fontByName(score->style().styleSt(Sid::musicalSymbolFont).toStdString());
101 5064 : } else if (tag == "copyright" || tag == "rights") {
102 0 : score->setMetaTag(u"copyright", Text::readXmlText(e, score));
103 5064 : } else if (tag == "movement-number") {
104 0 : score->setMetaTag(u"movementNumber", e.readText());
105 5064 : } else if (tag == "movement-title") {
106 0 : score->setMetaTag(u"movementTitle", e.readText());
107 5064 : } else if (tag == "work-number") {
108 0 : score->setMetaTag(u"workNumber", e.readText());
109 5064 : } else if (tag == "work-title") {
110 0 : score->setMetaTag(u"workTitle", e.readText());
111 5064 : } else if (tag == "source") {
112 0 : score->setMetaTag(u"source", e.readText());
113 5064 : } else if (tag == "metaTag") {
114 4364 : String name = e.attribute("name");
115 4364 : score->setMetaTag(name, e.readText());
116 5064 : } else if (tag == "Order") {
117 3 : ScoreOrder order;
118 3 : order.read(e);
119 3 : if (order.isValid()) {
120 3 : score->setScoreOrder(order);
121 : }
122 700 : } else if (tag == "Part") {
123 665 : Part* part = new Part(score);
124 665 : read400::TRead::read(part, e, ctx);
125 665 : score->appendPart(part);
126 32 : } else if ((tag == "HairPin")
127 32 : || (tag == "Ottava")
128 32 : || (tag == "TextLine")
129 32 : || (tag == "Volta")
130 32 : || (tag == "Trill")
131 32 : || (tag == "Slur")
132 64 : || (tag == "Pedal")) {
133 0 : Spanner* s = toSpanner(Factory::createItemByName(tag, score->dummy()));
134 0 : read400::TRead::readItem(s, e, ctx);
135 0 : score->addSpanner(s);
136 32 : } else if (e.name() == "Tracklist") {
137 0 : int strack = e.intAttribute("sTrack", -1);
138 0 : int dtrack = e.intAttribute("dstTrack", -1);
139 0 : if (strack != -1 && dtrack != -1) {
140 0 : ctx.tracks().insert({ strack, dtrack });
141 : }
142 0 : e.skipCurrentElement();
143 32 : } else if (tag == "Score") { // recursion
144 16 : ctx.tracks().clear(); // ???
145 16 : MasterScore* m = score->masterScore();
146 16 : Score* s = m->createScore();
147 :
148 16 : ReadStyleHook::setupDefaultStyle(s);
149 :
150 16 : Excerpt* ex = new Excerpt(m);
151 16 : ex->setExcerptScore(s);
152 16 : ctx.setLastMeasure(nullptr);
153 :
154 16 : Score* curScore = ctx.score();
155 16 : ctx.setScore(s);
156 :
157 16 : s->setIsOpen(true);
158 16 : readScoreTag(s, e, ctx);
159 :
160 16 : ctx.setScore(curScore);
161 :
162 16 : Excerpt::linkMeasures(s, m);
163 16 : ex->setTracksMapping(ctx.tracks());
164 16 : m->addExcerpt(ex);
165 16 : } else if (tag == "name") {
166 16 : String n = e.readText();
167 16 : if (!score->isMaster()) { //ignore the name if it's not a child score
168 16 : score->excerpt()->setName(n, /*saveAndNotify=*/ false);
169 : }
170 16 : } else if (tag == "layoutMode") {
171 0 : if (ctx.forcePageMode()) {
172 0 : e.skipCurrentElement();
173 0 : continue;
174 : }
175 0 : String s = e.readText();
176 0 : if (s == "line") {
177 0 : score->setLayoutMode(LayoutMode::LINE);
178 0 : } else if (s == "system") {
179 0 : score->setLayoutMode(LayoutMode::SYSTEM);
180 : } else {
181 0 : LOGD("layoutMode: %s", muPrintable(s));
182 : }
183 0 : } else {
184 0 : e.unknown();
185 : }
186 : }
187 430 : ctx.reconnectBrokenConnectors();
188 430 : if (e.error() != muse::XmlStreamReader::NoError) {
189 0 : if (e.error() == muse::XmlStreamReader::CustomError) {
190 0 : LOGE() << e.errorString();
191 : } else {
192 0 : LOGE() << String(u"XML read error at byte offset %1: %2").arg(e.byteOffset())
193 0 : .arg(String::fromAscii(e.name().ascii()));
194 : }
195 0 : return false;
196 : }
197 :
198 430 : score->m_fileDivision = Constants::DIVISION;
199 :
200 430 : if (score->mscVersion() == 302) {
201 : // MuseScore 3.6.x scores had some wrong instrument IDs
202 14 : for (Part* part : score->parts()) {
203 14 : for (const auto& pair : part->instruments()) {
204 7 : fixInstrumentId(pair.second);
205 : }
206 : }
207 : } else {
208 : // Older scores had no IDs at all
209 1081 : for (Part* part : score->parts()) {
210 1341 : for (const auto& pair : part->instruments()) {
211 683 : pair.second->updateInstrumentId();
212 : }
213 : }
214 : }
215 :
216 430 : score->updateTicksAndTimeSigMap();
217 430 : if (score->isMaster()) {
218 : // While reading the score, some elements might use `score->repeatList()` (which is incorrect
219 : // anyway, because the repeatList will be incomplete because the score is incomplete, but some
220 : // elements still do it).
221 : // `score->repeatList()` calls `_repeatList->update()`; the repeat list then thinks that it is
222 : // up-to-date from that point. But we weren't finished reading the score, so the score will still
223 : // change. We need to tell the repeat list about that, so that it will be updated next time
224 : // someone uses it.
225 414 : static_cast<MasterScore*>(score)->invalidateRepeatList();
226 : }
227 430 : score->connectTies();
228 430 : score->undoRemoveStaleTieJumpPoints(false);
229 :
230 1095 : for (Part* p : score->parts()) {
231 665 : p->updateHarmonyChannels(false);
232 : }
233 :
234 430 : score->masterScore()->rebuildMidiMapping();
235 430 : score->masterScore()->updateChannel();
236 :
237 1181 : for (Staff* staff : score->staves()) {
238 751 : staff->updateOttava();
239 : }
240 :
241 430 : if (score->isMaster()) {
242 414 : CompatUtils::assignInitialPartToExcerpts(score->masterScore()->excerpts());
243 : }
244 :
245 430 : return true;
246 : }
247 :
248 414 : muse::Ret Read302::readScoreFile(Score* score, XmlReader& e, ReadInOutData* out)
249 : {
250 414 : ReadContext ctx(score);
251 414 : if (out) {
252 414 : if (out->overriddenSpatium.has_value()) {
253 0 : ctx.setSpatium(out->overriddenSpatium.value());
254 0 : ctx.setOverrideSpatium(true);
255 : }
256 :
257 414 : ctx.setPropertiesToSkip(out->propertiesToSkip);
258 414 : ctx.setForcePageMode(out->forcePageMode);
259 : }
260 :
261 414 : DEFER {
262 414 : if (out) {
263 414 : out->settingsCompat = std::move(ctx.settingCompat());
264 : }
265 414 : };
266 :
267 1002 : while (e.readNextStartElement()) {
268 588 : const AsciiStringView tag(e.name());
269 588 : if (tag == "programVersion") {
270 87 : score->setMscoreVersion(e.readText());
271 501 : } else if (tag == "programRevision") {
272 87 : score->setMscoreRevision(e.readInt(nullptr, 16));
273 414 : } else if (tag == "Score") {
274 414 : if (!readScoreTag(score, e, ctx)) {
275 0 : if (e.error() == muse::XmlStreamReader::CustomError) {
276 0 : return make_ret(Err::FileCriticallyCorrupted, e.errorString());
277 : }
278 0 : return make_ret(Err::FileBadFormat, e.errorString());
279 : }
280 :
281 414 : if (ctx.overrideSpatium() && out) {
282 0 : out->originalSpatium = ctx.originalSpatium();
283 : }
284 0 : } else if (tag == "Revision") {
285 0 : e.skipCurrentElement();
286 : }
287 : }
288 :
289 414 : return muse::make_ok();
290 414 : }
291 :
292 7 : void Read302::fixInstrumentId(Instrument* instrument)
293 : {
294 7 : String id = instrument->id();
295 7 : String trackName = instrument->trackName().toLower();
296 :
297 : // incorrect instrument IDs in 3.6.x
298 7 : if (id == u"Winds") {
299 1 : id = u"winds";
300 6 : } else if (id == u"harmonica-d12high-g") {
301 0 : id = u"harmonica-d10high-g";
302 6 : } else if (id == u"harmonica-d12f") {
303 0 : id = u"harmonica-d10f";
304 6 : } else if (id == u"harmonica-d12d") {
305 0 : id = u"harmonica-d10d";
306 6 : } else if (id == u"harmonica-d12c") {
307 0 : id = u"harmonica-d10c";
308 6 : } else if (id == u"harmonica-d12a") {
309 0 : id = u"harmonica-d10a";
310 6 : } else if (id == u"harmonica-d12-g") {
311 0 : id = u"harmonica-d10g";
312 6 : } else if (id == u"drumset" && trackName == u"percussion") {
313 0 : id = u"percussion";
314 6 : } else if (id == u"cymbal" && trackName == u"cymbals") {
315 0 : id = u"marching-cymbals";
316 6 : } else if (id == u"bass-drum" && trackName == u"bass drums") {
317 0 : id = u"marching-bass-drums";
318 6 : } else if (id.startsWith(u"mdl-")) {
319 : // See https://github.com/musescore/mdl/blob/master/resources/instruments/mdl_1_3_0.xml
320 0 : if (id == u"mdl-snareline" || id == u"mdl-snareline-a" || id == u"mdl-snaresolo" || id == u"mdl-snaresolo-a") {
321 0 : id = u"marching-snare";
322 0 : } else if (id == u"mdl-tenorline" || id == u"mdl-tenorsolo" || id == u"mdl-flubs") {
323 0 : id = u"marching-tenor-drums";
324 0 : } else if (id == u"mdl-bassline-10" || id == u"mdl-bassline-5") {
325 0 : id = u"marching-bass-drums";
326 0 : } else if (id == u"mdl-cymballine") {
327 0 : id = u"marching-cymbals";
328 0 : } else if (id == u"mdl-showtenorline" || id == u"mdl-rail" || id == u"mdl-drumset" || id == u"mdl-sampler") {
329 0 : id = u"drumset";
330 : }
331 : }
332 :
333 7 : instrument->setId(id);
334 7 : }
335 :
336 0 : bool Read302::pasteStaff(XmlReader&, Segment*, staff_idx_t, Fraction)
337 : {
338 0 : UNREACHABLE;
339 : return false;
340 : }
341 :
342 0 : void Read302::pasteSymbols(XmlReader&, ChordRest*)
343 : {
344 0 : UNREACHABLE;
345 : }
346 :
347 0 : void Read302::readTremoloCompat(compat::TremoloCompat*, XmlReader&)
348 : {
349 0 : UNREACHABLE;
350 : }
351 :
352 0 : void Read302::doReadItem(EngravingItem*, XmlReader&)
353 : {
354 0 : UNREACHABLE;
355 : }
|