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 :
23 : #include "capella.h"
24 :
25 : // CapXML import filter
26 : // Supports the CapXML 1.0 file format version 1.0.8 (capella 2008)
27 : // The implementation shares as much code as possible with the capella
28 : // (.cap) importer (capella.cpp and capella.h)
29 : // If statements in the parser match the element order in the schema definition
30 :
31 : #include <assert.h>
32 : #include <cmath>
33 :
34 : #include <QRegularExpression>
35 :
36 : #include "engraving/rw/xmlreader.h"
37 : #include "engraving/engravingerrors.h"
38 : #include "engraving/dom/masterscore.h"
39 : #include "serialization/zipreader.h"
40 :
41 : #include "log.h"
42 :
43 : using namespace mu;
44 : using namespace mu::engraving;
45 :
46 : namespace mu::iex::capella {
47 : //---------------------------------------------------------
48 : // capxReadFont
49 : //---------------------------------------------------------
50 :
51 6 : static QFont capxReadFont(XmlReader& e)
52 : {
53 6 : QFont f;
54 6 : QString family = e.attribute("face");
55 6 : if (family != "") {
56 6 : f.setFamily(family);
57 : }
58 6 : qreal pointSize = e.doubleAttribute("height", 0);
59 6 : if (pointSize > 0.5) {
60 6 : f.setPointSizeF(pointSize);
61 : }
62 6 : int weight = e.intAttribute("weight");
63 6 : if (weight < 200) {
64 0 : f.setWeight(QFont::Light);
65 6 : } else if (weight < 550) {
66 0 : f.setWeight(QFont::Normal);
67 : } else {
68 6 : f.setWeight(QFont::Bold);
69 : }
70 6 : f.setItalic(e.asciiAttribute("italic", "false") == "true");
71 : // CAPELLA_TRACE("capxReadFont family '%s' ps %g w %d it '%s'", qPrintable(family), pointSize, weight, qPrintable(italic));
72 6 : e.readNext();
73 6 : return f;
74 6 : }
75 :
76 : //---------------------------------------------------------
77 : // qstring2timestep -- convert string to TIMESTEP
78 : //---------------------------------------------------------
79 :
80 366 : static bool qstring2timestep(QString& str, TIMESTEP& tstp)
81 : {
82 366 : if (str == "1/1") {
83 9 : tstp = TIMESTEP::D1;
84 9 : return true;
85 357 : } else if (str == "1/2") {
86 7 : tstp = TIMESTEP::D2;
87 7 : return true;
88 350 : } else if (str == "1/4") {
89 168 : tstp = TIMESTEP::D4;
90 168 : return true;
91 182 : } else if (str == "1/8") {
92 68 : tstp = TIMESTEP::D8;
93 68 : return true;
94 114 : } else if (str == "1/16") {
95 114 : tstp = TIMESTEP::D16;
96 114 : return true;
97 0 : } else if (str == "1/32") {
98 0 : tstp = TIMESTEP::D32;
99 0 : return true;
100 0 : } else if (str == "1/64") {
101 0 : tstp = TIMESTEP::D64;
102 0 : return true;
103 0 : } else if (str == "1/128") {
104 0 : tstp = TIMESTEP::D128;
105 0 : return true;
106 0 : } else if (str == "2/1") {
107 0 : tstp = TIMESTEP::D_BREVE;
108 0 : return true;
109 : }
110 0 : return false;
111 : }
112 :
113 : //---------------------------------------------------------
114 : // BasicDrawObj::readCapx -- capx equivalent of BasicDrawObj::read
115 : //---------------------------------------------------------
116 :
117 19 : void BasicDrawObj::readCapx(XmlReader& e)
118 : {
119 19 : nNotes = e.intAttribute("noteRange", 0);
120 : CAPELLA_TRACE("nNotes %d", nNotes);
121 19 : e.readNext();
122 19 : }
123 :
124 : //---------------------------------------------------------
125 : // BasicDurationalObj::readCapxDisplay -- capx equivalent of BasicDurationalObj::read
126 : // reads the <display> element only
127 : //---------------------------------------------------------
128 :
129 0 : void BasicDurationalObj::readCapxDisplay(XmlReader& e)
130 : {
131 0 : invisible = e.attribute("invisible") == "true";
132 0 : e.readNext();
133 0 : }
134 :
135 : //---------------------------------------------------------
136 : // BasicDurationalObj::readCapx -- capx equivalent of BasicDurationalObj::read
137 : //---------------------------------------------------------
138 :
139 366 : void BasicDurationalObj::readCapx(XmlReader& e, unsigned int& fullm)
140 : {
141 366 : nDots = 0;
142 366 : noDuration = false;
143 366 : postGrace = false;
144 366 : bSmall = false;
145 366 : invisible = false;
146 366 : notBlack = false;
147 366 : color = Qt::black;
148 366 : t = TIMESTEP::D1;
149 366 : horizontalShift = 0;
150 366 : tupletDenominator = 0;
151 366 : tripartite = 0;
152 366 : isProlonging = 0;
153 366 : QString base = e.attribute("base");
154 366 : bool res = false;
155 : // try to convert base to timestep ("first pattern")
156 366 : res = qstring2timestep(base, t);
157 366 : if (!res) {
158 : // try multi-measure rest ("second pattern")
159 0 : int i = base.toInt();
160 0 : if (i > 0) {
161 0 : fullm = i;
162 : } else {
163 0 : LOGD("BasicDurationalObj::readCapx: invalid base: '%s'", qPrintable(base));
164 : }
165 : }
166 366 : nDots = e.intAttribute("dots", 0);
167 366 : noDuration = e.asciiAttribute("noDuration", "false") == "true";
168 502 : while (e.readNextStartElement()) {
169 136 : const AsciiStringView tag(e.name());
170 136 : if (tag == "tuplet") {
171 136 : tupletDenominator = e.attribute("count").toInt();
172 136 : tripartite = e.asciiAttribute("tripartite", "false") == "true";
173 136 : isProlonging = e.asciiAttribute("prolong", "false") == "true";
174 136 : e.readNext();
175 : } else {
176 0 : e.unknown();
177 : }
178 : }
179 : CAPELLA_TRACE("DurationObj ndots %d nodur %d postgr %d bsm %d inv %d notbl %d t %d hsh %d den %d trp %d ispro %d fullm %d",
180 : nDots, noDuration, postGrace, bSmall, invisible, notBlack, int(t), horizontalShift, tupletDenominator, tripartite,
181 : isProlonging, fullm
182 : );
183 366 : }
184 :
185 : //---------------------------------------------------------
186 : // BasicDurationalObj::readCapxObjectArray
187 : //---------------------------------------------------------
188 :
189 25 : void BasicDurationalObj::readCapxObjectArray(XmlReader& e)
190 : {
191 25 : objects = cap->readCapxDrawObjectArray(e);
192 25 : }
193 :
194 : //---------------------------------------------------------
195 : // CapExplicitBarline::readCapx -- capx equivalent of CapExplicitBarline::read
196 : //---------------------------------------------------------
197 :
198 5 : void CapExplicitBarline::readCapx(XmlReader& e)
199 : {
200 5 : AsciiStringView type = e.asciiAttribute("type", "single");
201 5 : if (type == "single") {
202 3 : _type = BarLineType::NORMAL;
203 2 : } else if (type == "double") {
204 0 : _type = BarLineType::DOUBLE;
205 2 : } else if (type == "end") {
206 1 : _type = BarLineType::END;
207 1 : } else if (type == "repEnd") {
208 1 : _type = BarLineType::END_REPEAT;
209 0 : } else if (type == "repBegin") {
210 0 : _type = BarLineType::START_REPEAT;
211 0 : } else if (type == "repEndBegin") {
212 0 : _type = BarLineType::END_START_REPEAT;
213 0 : } else if (type == "dashed") {
214 0 : _type = BarLineType::BROKEN;
215 : } else {
216 0 : _type = BarLineType::NORMAL; // default
217 : }
218 5 : _barMode = 0;
219 6 : while (e.readNextStartElement()) {
220 1 : const AsciiStringView tag(e.name());
221 1 : if (tag == "drawObjects") {
222 1 : e.skipCurrentElement();
223 : } else {
224 0 : e.unknown();
225 : }
226 : }
227 5 : }
228 :
229 : //---------------------------------------------------------
230 : // CapClef::readCapx -- capx equivalent of CapClef::read
231 : //---------------------------------------------------------
232 :
233 43 : void CapClef::readCapx(XmlReader& e)
234 : {
235 43 : QString clef = e.attribute("clef");
236 43 : if (clef == "G2-") {
237 1 : form = Form::G;
238 1 : line = ClefLine::L2;
239 1 : oct = Oct::OCT_BASSA;
240 42 : } else if (clef == "treble") {
241 39 : form = Form::G;
242 39 : line = ClefLine::L2;
243 39 : oct = Oct::OCT_NULL;
244 3 : } else if (clef == "bass") {
245 3 : form = Form::F;
246 3 : line = ClefLine::L4;
247 3 : oct = Oct::OCT_NULL;
248 : } else {
249 : /* default */
250 0 : form = Form::G;
251 0 : line = ClefLine::L2;
252 0 : oct = Oct::OCT_NULL;
253 : }
254 : CAPELLA_TRACE("Clef::read '%s' -> form %d line %d oct %d", qPrintable(clef), int(form), int(line), int(oct));
255 43 : e.readNext();
256 43 : }
257 :
258 : //---------------------------------------------------------
259 : // CapKey::readCapx -- capx equivalent of CapKey::read
260 : //---------------------------------------------------------
261 :
262 8 : void CapKey::readCapx(XmlReader& e)
263 : {
264 8 : signature = e.intAttribute("fifths", 0);
265 : CAPELLA_TRACE("Key %d", signature);
266 8 : e.readNext();
267 8 : }
268 :
269 : //---------------------------------------------------------
270 : // qstring2timesig -- convert string to timesig
271 : // return true on success
272 : //---------------------------------------------------------
273 :
274 57 : static void qstring2timesig(QString& time, uchar& numerator, int& log2Denom, bool& allaBreve)
275 : {
276 57 : bool res = true;
277 57 : numerator = 4;
278 57 : log2Denom = 2;
279 57 : allaBreve = false; // set default
280 57 : if (time == "allaBreve") {
281 0 : numerator = 2;
282 0 : log2Denom = 1;
283 0 : allaBreve = true;
284 57 : } else if (time == "longAllaBreve") {
285 0 : numerator = 4;
286 0 : log2Denom = 1;
287 0 : allaBreve = true;
288 57 : } else if (time == "C") {
289 0 : numerator = 4;
290 0 : log2Denom = 2;
291 0 : allaBreve = false;
292 57 : } else if (time == "infinite") {
293 0 : LOGD("Meter infinite");
294 : } // not supported by MuseScore ?
295 : else {
296 57 : QStringList splitTime = time.split("/");
297 57 : if (splitTime.size() == 2) {
298 57 : numerator = splitTime.at(0).toInt();
299 57 : QString denom = splitTime.at(1);
300 57 : if (denom == "1") {
301 0 : log2Denom = 0;
302 57 : } else if (denom == "2") {
303 0 : log2Denom = 1;
304 57 : } else if (denom == "4") {
305 51 : log2Denom = 2;
306 6 : } else if (denom == "8") {
307 6 : log2Denom = 3;
308 0 : } else if (denom == "16") {
309 0 : log2Denom = 4;
310 0 : } else if (denom == "32") {
311 0 : log2Denom = 5;
312 0 : } else if (denom == "64") {
313 0 : log2Denom = 6;
314 0 : } else if (denom == "128") {
315 0 : log2Denom = 7;
316 : } else {
317 0 : res = false;
318 : }
319 57 : } else {
320 0 : res = false;
321 : }
322 57 : }
323 : // TODO: recovery required if decoding the timesig failed ?
324 : CAPELLA_TRACE("Meter '%s' res %d %d/%d allaBreve %d", qPrintable(time), res, numerator, log2Denom, allaBreve);
325 57 : }
326 :
327 : //---------------------------------------------------------
328 : // CapMeter::readCapx -- capx equivalent of CapMeter::read
329 : //---------------------------------------------------------
330 :
331 14 : void CapMeter::readCapx(XmlReader& e)
332 : {
333 : CAPELLA_TRACE("CapMeter::readCapx");
334 14 : QString time = e.attribute("time");
335 14 : qstring2timesig(time, numerator, log2Denom, allaBreve);
336 14 : e.readNext();
337 14 : }
338 :
339 : //---------------------------------------------------------
340 : // ChordObj::readCapxStem -- read <stem> element
341 : //---------------------------------------------------------
342 :
343 0 : void ChordObj::readCapxStem(XmlReader& e)
344 : {
345 0 : QString dir = e.attribute("dir");
346 0 : if (dir == "up") {
347 0 : stemDir = StemDir::UP;
348 0 : } else if (dir == "down") {
349 0 : stemDir = StemDir::DOWN;
350 0 : } else if (dir == "none") {
351 0 : stemDir = StemDir::NONE;
352 : }
353 0 : e.readNext();
354 0 : }
355 :
356 : //---------------------------------------------------------
357 : // ChordObj::readCapxArticulation -- read <articulation> element
358 : //---------------------------------------------------------
359 :
360 0 : void ChordObj::readCapxArticulation(XmlReader& e)
361 : {
362 0 : QString art = e.attribute("type");
363 0 : if (art == "staccato") {
364 0 : articulation = 1;
365 0 : } else if (art == "tenuto") {
366 0 : articulation = 2;
367 0 : } else if (art == "staccato tenuto") {
368 0 : articulation = 3;
369 0 : } else if (art == "staccatissimo") {
370 0 : articulation = 4;
371 0 : } else if (art == "normalAccent") {
372 0 : articulation = 5;
373 0 : } else if (art == "strongAccent") {
374 0 : articulation = 6;
375 0 : } else if (art == "weakBeat") {
376 0 : articulation = 7;
377 0 : } else if (art == "strongBeat") {
378 0 : articulation = 8;
379 : } else {
380 0 : articulation = 0;
381 : }
382 0 : e.readNext();
383 0 : }
384 :
385 : //---------------------------------------------------------
386 : // ChordObj::readCapx -- capx equivalent of ChordObj::read
387 : //---------------------------------------------------------
388 :
389 363 : void ChordObj::readCapx(XmlReader& e)
390 : {
391 363 : stemDir = StemDir::AUTO;
392 363 : dStemLength = 0;
393 363 : nTremoloBars = 0;
394 363 : articulation = 0;
395 363 : leftTie = false;
396 363 : rightTie = false;
397 363 : beamShift = 0;
398 363 : beamSlope = 0;
399 363 : beamMode = CapBeamMode::AUTO;
400 363 : notationStave = 0;
401 :
402 1122 : while (e.readNextStartElement()) {
403 759 : const AsciiStringView tag(e.name());
404 759 : if (tag == "duration") {
405 : unsigned int dummy;
406 363 : BasicDurationalObj::readCapx(e, dummy);
407 396 : } else if (tag == "display") {
408 0 : BasicDurationalObj::readCapxDisplay(e);
409 396 : } else if (tag == "stem") {
410 0 : readCapxStem(e);
411 396 : } else if (tag == "beam") {
412 7 : LOGD("ChordObj::readCapx: found beam (skipping)");
413 7 : e.skipCurrentElement();
414 389 : } else if (tag == "articulation") {
415 0 : readCapxArticulation(e);
416 389 : } else if (tag == "lyric") {
417 3 : readCapxLyrics(e);
418 386 : } else if (tag == "drawObjects") {
419 23 : readCapxObjectArray(e);
420 363 : } else if (tag == "heads") {
421 363 : readCapxNotes(e);
422 : } else {
423 0 : e.unknown();
424 : }
425 : }
426 363 : }
427 :
428 : //---------------------------------------------------------
429 : // pitchStr2Char -- convert pitch string ("[A-G][0-9]") to signed char
430 : // notes:
431 : // - in .capx middle C is called C5 (which is C4 in MusicXML)
432 : // - middle C is MIDI note number 60
433 : // - in .cap, pitch contains the diatonic note number relative to clef and key
434 : // - as .capx contains absolute notes, store the MIDI note number instead
435 : //---------------------------------------------------------
436 :
437 363 : static signed char pitchStr2Char(QString& strPitch)
438 : {
439 363 : QRegularExpression pitchRegex(QRegularExpression::anchoredPattern("[A-G][0-9]"));
440 363 : QRegularExpressionMatch match = pitchRegex.match(strPitch);
441 :
442 363 : if (!match.hasMatch()) {
443 0 : LOGD("pitchStr2Char: illegal pitch '%s'", qPrintable(strPitch));
444 0 : return 0;
445 : }
446 :
447 363 : QString steps("C.D.EF.G.A.B");
448 363 : int istep = steps.indexOf(strPitch.left(1));
449 363 : int octave = strPitch.right(1).toInt();
450 363 : int pitch = istep + 12 * octave;
451 :
452 363 : if (!pitchIsValid(pitch)) {
453 0 : pitch = -1;
454 : }
455 :
456 : CAPELLA_TRACE("pitchStr2Char: '%s' -> %d", qPrintable(strPitch), pitch);
457 :
458 363 : return static_cast<signed char>(pitch);
459 363 : }
460 :
461 : //---------------------------------------------------------
462 : // ChordObj::readCapxLyrics -- read the lyrics in a capx chord
463 : //---------------------------------------------------------
464 :
465 3 : void ChordObj::readCapxLyrics(XmlReader& e)
466 : {
467 9 : while (e.readNextStartElement()) {
468 6 : if (e.name() == "verse") {
469 6 : Verse v;
470 6 : v.leftAlign = true; // not used by capella.cpp
471 6 : v.extender = false; // not used by capella.cpp
472 6 : v.hyphen = e.attribute("hyphen") == "true";
473 6 : v.num = e.intAttribute("i", 0);
474 6 : v.verseNumber = e.attribute("verseNumber"); // not used by capella.cpp
475 6 : v.text = e.readText();
476 6 : verse.append(v);
477 6 : } else {
478 0 : e.unknown();
479 : }
480 : }
481 3 : }
482 :
483 : //---------------------------------------------------------
484 : // ChordObj::readCapxNotes -- read the notes in a capx chord
485 : //---------------------------------------------------------
486 :
487 363 : void ChordObj::readCapxNotes(XmlReader& e)
488 : {
489 726 : while (e.readNextStartElement()) {
490 363 : if (e.name() == "head") {
491 363 : QString pitch = e.attribute("pitch");
492 363 : QString sstep;
493 363 : QString shape = e.attribute("shape");
494 377 : while (e.readNextStartElement()) {
495 14 : const AsciiStringView tag(e.name());
496 14 : if (tag == "alter") {
497 12 : sstep = e.attribute("step");
498 12 : e.readNext();
499 2 : } else if (tag == "tie") {
500 2 : rightTie = e.attribute("begin") == "true";
501 2 : e.readNext();
502 : } else {
503 0 : e.unknown();
504 : }
505 : }
506 : CAPELLA_TRACE("ChordObj::readCapxNotes: pitch '%s' altstep '%s' shape '%s'",
507 : qPrintable(pitch), qPrintable(sstep), qPrintable(shape));
508 363 : int istep = sstep.toInt();
509 : CNote n;
510 363 : n.pitch = pitchStr2Char(pitch);
511 363 : n.explAlteration = 0;
512 363 : n.headType = 0;
513 363 : if (shape == "none") {
514 0 : n.headGroup = int(NoteHeadGroup::HEAD_CROSS);
515 : } else {
516 363 : n.headGroup = int(NoteHeadGroup::HEAD_NORMAL);
517 : }
518 363 : n.alteration = istep;
519 363 : n.silent = 0;
520 363 : notes.append(n);
521 363 : } else {
522 0 : e.unknown();
523 : }
524 : }
525 :
526 363 : if (tupletDenominator <= 0) {
527 230 : return; // no tuplet, so we are done
528 : }
529 :
530 : // find a BracketObj in the drawObjects list, get the data and delete it
531 : // this is used to read Tuplets with mixed durations
532 133 : for (BasicDrawObj* o : objects) {
533 16 : if (o->type == CapellaType::BRACKET) {
534 : // BracketObj* bo = static_cast<BracketObj*>(o);
535 16 : tupletStart = true;
536 16 : tupletCount = o->nNotes;
537 16 : objects.removeOne(o);
538 16 : delete o;
539 : CAPELLA_TRACE("==> Tuplet start, plus %d notes till end", tupletCount);
540 16 : return; // we are done, and we modified the iterator, so back out
541 : }
542 : }
543 117 : LOGE("no tuplet start found in drawObjects, but tupletDenominator %d", tupletDenominator);
544 : }
545 :
546 : //---------------------------------------------------------
547 : // RestObj::readCapx -- capx equivalent of RestObj::read
548 : //---------------------------------------------------------
549 :
550 3 : void RestObj::readCapx(XmlReader& e)
551 : {
552 3 : bVerticalCentered = false;
553 3 : fullMeasures = 0;
554 3 : vertShift = 0;
555 8 : while (e.readNextStartElement()) {
556 5 : const AsciiStringView tag(e.name());
557 5 : if (tag == "duration") {
558 3 : BasicDurationalObj::readCapx(e, fullMeasures);
559 2 : } else if (tag == "display") {
560 0 : BasicDurationalObj::readCapxDisplay(e);
561 2 : } else if (tag == "verticalPos") {
562 0 : LOGD("RestObj::readCapx: found verticalPos (skipping)");
563 0 : e.skipCurrentElement();
564 2 : } else if (tag == "drawObjects") {
565 2 : readCapxObjectArray(e);
566 : } else {
567 0 : e.unknown();
568 : }
569 : }
570 :
571 3 : if (tupletDenominator <= 0) {
572 0 : return; // no tuplet, so we are done
573 : }
574 :
575 : // find a BracketObj in the drawObjects list, get the data and delete it
576 : // this is used to read Tuplets with mixed durations
577 3 : for (BasicDrawObj* o : objects) {
578 2 : if (o->type == CapellaType::BRACKET) {
579 : // BracketObj* bo = static_cast<BracketObj*>(o);
580 2 : tupletStart = true;
581 2 : tupletCount = o->nNotes;
582 2 : objects.removeOne(o);
583 2 : delete o;
584 : CAPELLA_TRACE(" ==> Tuplet start, plus %d notes till end", tupletCount);
585 2 : return; // we are done, and we modified the iterator, so back out
586 : }
587 : }
588 1 : LOGE("no tuplet start found in drawObjects, but tupletDenominator %d", tupletDenominator);
589 : }
590 :
591 : //---------------------------------------------------------
592 : // SimpleTextObj::readCapx -- capx equivalent of SimpleTextObj::read
593 : //---------------------------------------------------------
594 :
595 6 : void SimpleTextObj::readCapx(XmlReader& e)
596 : {
597 6 : double x = e.doubleAttribute("x");
598 6 : double y = e.doubleAttribute("y");
599 6 : AsciiStringView stralign = e.asciiAttribute("align", "left");
600 6 : align = 0;
601 6 : if (stralign == "center") {
602 6 : align = 1;
603 0 : } else if (stralign == "right") {
604 0 : align = 2;
605 : }
606 6 : relPos = QPointF(x, y);
607 6 : relPos *= 32.0;
608 : // CAPELLA_TRACE("x %g y %g align %s", x, y, qPrintable(align));
609 18 : while (e.readNextStartElement()) {
610 12 : const AsciiStringView tag(e.name());
611 12 : if (tag == "font") {
612 6 : _font = capxReadFont(e);
613 6 : } else if (tag == "content") {
614 6 : _text = e.readText();
615 : // CAPELLA_TRACE("SimpleTextObj::readCapx: found content '%s'", qPrintable(_text));
616 : } else {
617 0 : e.unknown();
618 : }
619 : }
620 6 : }
621 :
622 : //---------------------------------------------------------
623 : // TransposableObj::readCapx -- capx equivalent of TransposableObj::read
624 : //---------------------------------------------------------
625 :
626 0 : void TransposableObj::readCapx(XmlReader& e)
627 : {
628 0 : String enharmonicNote = e.attribute("base");
629 0 : while (e.readNextStartElement()) {
630 0 : const AsciiStringView tag1(e.name());
631 0 : if (tag1 == "drawObj") {
632 0 : if (e.attribute("base") == enharmonicNote) {
633 0 : while (e.readNextStartElement()) {
634 0 : const AsciiStringView tag2(e.name());
635 0 : if (tag2 == "group") {
636 0 : variants.append(cap->readCapxDrawObjectArray(e));
637 : }
638 : }
639 : } else {
640 0 : e.skipCurrentElement();
641 : }
642 : }
643 : }
644 0 : }
645 :
646 : //---------------------------------------------------------
647 : // SlurObj::readCapx -- capx equivalent of SlurObj::read
648 : //---------------------------------------------------------
649 :
650 1 : void SlurObj::readCapx(XmlReader& e)
651 : {
652 : // nothing to be done yet
653 1 : e.skipCurrentElement();
654 1 : }
655 :
656 : //---------------------------------------------------------
657 : // VoltaObj::readCapx -- capx equivalent of VoltaObj::read
658 : //---------------------------------------------------------
659 :
660 2 : void VoltaObj::readCapx(XmlReader& e)
661 : {
662 2 : bLeft = e.asciiAttribute("leftBent", "true") == "true";
663 2 : bRight = e.asciiAttribute("rightBent", "true") == "true";
664 2 : bDotted = e.asciiAttribute("dotted", "false") == "true";
665 2 : allNumbers = e.asciiAttribute("allNumbers", "false") == "true";
666 2 : int firstNumber = e.intAttribute("firstNumber", 0);
667 2 : int lastNumber = e.intAttribute("lastNumber", 0);
668 2 : if (firstNumber == 0) {
669 : // don't know what this means (spec: no number)
670 : // cap status equivalent to no first or last number
671 0 : from = 1;
672 0 : to = 0;
673 : } else {
674 2 : from = firstNumber;
675 2 : to = (lastNumber == 0) ? firstNumber : lastNumber;
676 : }
677 : CAPELLA_TRACE("VoltaObj::read firstNumber %d lastNumber %d", firstNumber, lastNumber);
678 : CAPELLA_TRACE("VoltaObj::read x0 %d x1 %d y %d bLeft %d bRight %d bDotted %d allNumbers %d from %d to %d",
679 : x0, x1, y, bLeft, bRight, bDotted, allNumbers, from, to);
680 2 : e.readNext();
681 2 : }
682 :
683 : //---------------------------------------------------------
684 : // TrillObj::readCapx -- capx equivalent of TrillObj::read
685 : //---------------------------------------------------------
686 :
687 0 : void TrillObj::readCapx(XmlReader& e)
688 : {
689 0 : double x0d = e.doubleAttribute("x1", 0.0);
690 0 : double x1d = e.doubleAttribute("x2", 0.0);
691 0 : double yd = e.doubleAttribute("y", 0.0);
692 0 : trillSign = e.asciiAttribute("tr", "true") == "true";
693 0 : x0 = (int)round(x0d * 32.0);
694 0 : x1 = (int)round(x1d * 32.0);
695 0 : y = (int)round(yd * 32.0);
696 : // color -> skipped
697 : CAPELLA_TRACE("TrillObj::read x0 %d x1 %d y %d trillSign %d", x0, x1, y, trillSign);
698 0 : e.readNext();
699 0 : }
700 :
701 : //---------------------------------------------------------
702 : // WedgeObj::readCapx -- capx equivalent of WedgeObj::read
703 : //---------------------------------------------------------
704 :
705 0 : void WedgeObj::readCapx(XmlReader& e)
706 : {
707 : // TODO: read LineObj properties
708 0 : decresc = e.asciiAttribute("decrescendo", "false") == "true";
709 0 : double dheight = e.doubleAttribute("span", 1.0);
710 0 : height = (int)round(dheight * 32.0);
711 0 : e.readNext();
712 0 : }
713 :
714 : //------------------------------------------------------------
715 : // BracketObj::readCapx -- capx equivalent of WedgeObj::read
716 : //------------------------------------------------------------
717 :
718 18 : void BracketObj::readCapx(XmlReader& e)
719 : {
720 : // TODO: We're actually only interested in the <basic> element following and it's noteRange attribute ...
721 : // or to put in another way: Intentionally left empty ...
722 18 : e.skipCurrentElement();
723 18 : }
724 :
725 : //---------------------------------------------------------
726 : // readCapxDrawObjectArray -- capx equivalent of readDrawObjectArray()
727 : //---------------------------------------------------------
728 :
729 25 : QList<BasicDrawObj*> Capella::readCapxDrawObjectArray(XmlReader& e)
730 : {
731 25 : QList<BasicDrawObj*> ol;
732 52 : while (e.readNextStartElement()) {
733 27 : if (e.name() == "drawObj") {
734 27 : BasicDrawObj* bdo = 0;
735 73 : while (e.readNextStartElement()) {
736 46 : const AsciiStringView tag(e.name());
737 46 : if (tag == "basic") {
738 : // note: the <basic> element always follows the DrawObject it applies to
739 19 : if (bdo) {
740 19 : bdo->readCapx(e);
741 : } else {
742 0 : e.skipCurrentElement();
743 : }
744 27 : } else if (tag == "line") {
745 0 : LOGD("readCapxDrawObjectArray: found line (skipping)");
746 0 : e.skipCurrentElement();
747 27 : } else if (tag == "rectangle") {
748 0 : LOGD("readCapxDrawObjectArray: found rectangle (skipping)");
749 0 : e.skipCurrentElement();
750 27 : } else if (tag == "ellipse") {
751 0 : LOGD("readCapxDrawObjectArray: found ellipse (skipping)");
752 0 : e.skipCurrentElement();
753 27 : } else if (tag == "polygon") {
754 0 : LOGD("readCapxDrawObjectArray: found polygon (skipping)");
755 0 : e.skipCurrentElement();
756 27 : } else if (tag == "metafile") {
757 0 : LOGD("readCapxDrawObjectArray: found metafile (skipping)");
758 0 : e.skipCurrentElement();
759 27 : } else if (tag == "text") {
760 6 : SimpleTextObj* o = new SimpleTextObj(this);
761 6 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
762 6 : o->readCapx(e);
763 6 : ol.append(o);
764 21 : } else if (tag == "richText") {
765 0 : LOGD("readCapxDrawObjectArray: found richText (skipping)");
766 0 : e.skipCurrentElement();
767 21 : } else if (tag == "guitar") {
768 0 : LOGD("readCapxDrawObjectArray: found guitar (skipping)");
769 0 : e.skipCurrentElement();
770 21 : } else if (tag == "slur") {
771 1 : SlurObj* o = new SlurObj(this);
772 1 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
773 1 : o->readCapx(e);
774 1 : ol.append(o);
775 20 : } else if (tag == "wavyLine") {
776 0 : LOGD("readCapxDrawObjectArray: found wavyLine (skipping)");
777 0 : e.skipCurrentElement();
778 20 : } else if (tag == "bracket") {
779 18 : BracketObj* o = new BracketObj(this);
780 18 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
781 18 : o->readCapx(e);
782 18 : ol.append(o);
783 2 : } else if (tag == "wedge") {
784 0 : WedgeObj* o = new WedgeObj(this);
785 0 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
786 0 : o->readCapx(e);
787 0 : ol.append(o);
788 2 : } else if (tag == "notelines") {
789 0 : LOGD("readCapxDrawObjectArray: found notelines (skipping)");
790 0 : e.skipCurrentElement();
791 2 : } else if (tag == "volta") {
792 2 : VoltaObj* o = new VoltaObj(this);
793 2 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
794 2 : o->readCapx(e);
795 2 : ol.append(o);
796 0 : } else if (tag == "trill") {
797 0 : TrillObj* o = new TrillObj(this);
798 0 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
799 0 : o->readCapx(e);
800 0 : ol.append(o);
801 0 : } else if (tag == "transposable") {
802 0 : TransposableObj* o = new TransposableObj(this);
803 0 : bdo = o; // save o to handle the "basic" tag (which sometimes follows)
804 0 : o->readCapx(e);
805 0 : ol.append(o);
806 0 : } else if (tag == "group") {
807 0 : LOGD("readCapxDrawObjectArray: found group (skipping)");
808 0 : e.skipCurrentElement();
809 : } else {
810 0 : e.unknown();
811 : }
812 : }
813 : } else {
814 0 : e.unknown();
815 : }
816 : }
817 25 : return ol;
818 0 : }
819 :
820 : //---------------------------------------------------------
821 : // readCapxVoice -- capx equivalent of readVoice(CapStaff* cs, int idx)
822 : //---------------------------------------------------------
823 :
824 43 : void Capella::readCapxVoice(XmlReader& e, CapStaff* cs, int idx)
825 : {
826 43 : CapVoice* v = new CapVoice;
827 43 : v->voiceNo = idx;
828 43 : v->y0Lyrics = 0;
829 43 : v->dyLyrics = 0;
830 : // v->lyricsFont = 0;
831 43 : v->stemDir = 0;
832 :
833 43 : int tupletCounter = 0;
834 43 : Fraction tupletTicks = Fraction(0, 1);
835 43 : BasicDurationalObj* tupletObj = nullptr;
836 :
837 102 : while (e.readNextStartElement()) {
838 59 : if (e.name() == "lyricsSettings") {
839 16 : LOGD("readCapxVoice: found lyricsSettings (skipping)");
840 16 : e.skipCurrentElement();
841 43 : } else if (e.name() == "noteObjects") {
842 479 : while (e.readNextStartElement()) {
843 436 : const AsciiStringView tag(e.name());
844 436 : if (tag == "clefSign") {
845 43 : CapClef* clef = new CapClef(this);
846 43 : clef->readCapx(e);
847 43 : v->objects.append(clef);
848 393 : } else if (tag == "keySign") {
849 8 : CapKey* key = new CapKey(this);
850 8 : key->readCapx(e);
851 8 : v->objects.append(key);
852 385 : } else if (tag == "timeSign") {
853 14 : CapMeter* meter = new CapMeter(this);
854 14 : meter->readCapx(e);
855 14 : v->objects.append(meter);
856 371 : } else if (tag == "barline") {
857 5 : CapExplicitBarline* bl = new CapExplicitBarline(this);
858 5 : bl->readCapx(e);
859 5 : v->objects.append(bl);
860 366 : } else if (tag == "chord") {
861 363 : ChordObj* chord = new ChordObj(this);
862 363 : chord->readCapx(e);
863 363 : v->objects.append(chord);
864 363 : if (chord->tupletStart) {
865 16 : tupletCounter = chord->tupletCount;
866 16 : tupletTicks = chord->ticks();
867 16 : tupletObj = chord; // save the tuplet object for later
868 : CAPELLA_TRACE(" ==| (C) start of tuplet with %d notes, ticks %d",
869 : 1 + tupletCounter, tupletTicks.ticks());
870 347 : } else if (tupletCounter > 0) {
871 113 : if (!tupletObj) {
872 0 : LOGE(" !!! chord with tupletCounter %d without tupletObj", tupletCounter);
873 : } else {
874 113 : tupletTicks += chord->ticks();
875 113 : if (chord->tupletDenominator == 0) {
876 1 : LOGE(" !!! (C) chord with tupletCounter %d outside tuplet", tupletCounter);
877 1 : chord->tupletDenominator = tupletObj->tupletDenominator; // copy denominator from the tuplet object
878 : }
879 113 : --tupletCounter;
880 113 : if (tupletCounter == 0) {
881 18 : chord->tupletEnd = true;
882 18 : tupletObj->tupletTicks = tupletTicks; // set total ticks to the tuplet object
883 18 : tupletObj = nullptr; // reset tuplet object
884 : CAPELLA_TRACE(" ==| end of tuplet, total ticks %d", tupletTicks.ticks());
885 : } else {
886 : CAPELLA_TRACE(" ==> chord with tupletCounter %d inside tuplet, ticks now %d",
887 : tupletCounter, tupletTicks.ticks());
888 : }
889 : }
890 : }
891 3 : } else if (tag == "rest") {
892 3 : RestObj* rest = new RestObj(this);
893 3 : rest->readCapx(e);
894 3 : v->objects.append(rest);
895 3 : if (rest->tupletStart) {
896 2 : tupletCounter = rest->tupletCount;
897 2 : tupletTicks = rest->ticks();
898 2 : tupletObj = rest; // save the tuplet object for later
899 : CAPELLA_TRACE(" ==| (R) start of tuplet with %d notes, ticks %d",
900 : 1 + tupletCounter, tupletTicks.ticks());
901 1 : } else if (tupletCounter > 0) {
902 0 : if (!tupletObj) {
903 0 : LOGE(" !!! rest with tupletCounter %d without tupletObj", tupletCounter);
904 : } else {
905 : // add the rest ticks to the tuplet ticks
906 0 : tupletTicks += rest->ticks();
907 0 : if (rest->tupletDenominator == 0) {
908 0 : LOGE(" !!! rest with tupletCounter %d outside tuplet", tupletCounter);
909 0 : rest->tupletDenominator = tupletObj->tupletDenominator; // copy denominator from the tuplet object
910 : }
911 0 : --tupletCounter;
912 0 : if (tupletCounter == 0) {
913 0 : rest->tupletEnd = true;
914 0 : tupletObj->tupletTicks = tupletTicks; // set total ticks to the tuplet object
915 0 : tupletObj = nullptr; // reset tuplet object
916 : CAPELLA_TRACE(" ==| end of tuplet, total ticks %d", tupletTicks.ticks());
917 : } else {
918 : CAPELLA_TRACE(" ==> rest with tupletCounter %d inside tuplet, ticks now %d",
919 : tupletCounter, tupletTicks.ticks());
920 : }
921 : }
922 : }
923 : } else {
924 0 : e.unknown();
925 : }
926 : }
927 : } else {
928 0 : e.unknown();
929 : }
930 : }
931 :
932 43 : cs->voices.append(v);
933 43 : }
934 :
935 : //---------------------------------------------------------
936 : // findStaffIndex -- find index of layout in staffLayouts
937 : //---------------------------------------------------------
938 :
939 43 : static int findStaffIndex(const QString& layout, const QList<CapStaffLayout*>& staffLayouts)
940 : {
941 43 : int index = 0;
942 49 : for (const auto& capStaffLayout : staffLayouts) {
943 49 : if (capStaffLayout->descr == layout) {
944 43 : return index;
945 : }
946 6 : ++index;
947 : }
948 :
949 0 : ASSERT_X(QString::asprintf("staff layout '%s' not found", qPrintable(layout)));
950 : return 0;
951 : }
952 :
953 : //---------------------------------------------------------
954 : // readCapxStaff -- capx equivalent of readStaff(CapSystem* system)
955 : //---------------------------------------------------------
956 :
957 43 : void Capella::readCapxStaff(XmlReader& e, CapSystem* system)
958 : {
959 : CAPELLA_TRACE("Capella::readCapxStaff");
960 43 : CapStaff* staff = new CapStaff;
961 43 : QString layout = e.attribute("layout");
962 43 : QString time = e.attribute("defaultTime");
963 43 : qstring2timesig(time, staff->numerator, staff->log2Denom, staff->allaBreve);
964 :
965 43 : staff->iLayout = findStaffIndex(layout, staffLayouts());
966 43 : staff->topDistX = 0;
967 43 : staff->btmDistX = 0;
968 43 : staff->color = Qt::black;
969 :
970 88 : while (e.readNextStartElement()) {
971 45 : const AsciiStringView tag(e.name());
972 45 : if (tag == "extraDistance") {
973 2 : LOGD("readCapxStaff: found extraDistance (skipping)");
974 2 : e.skipCurrentElement();
975 43 : } else if (tag == "voices") {
976 43 : int i = 0;
977 86 : while (e.readNextStartElement()) {
978 43 : if (e.name() == "voice") {
979 43 : readCapxVoice(e, staff, i);
980 43 : ++i;
981 : } else {
982 0 : e.unknown();
983 : }
984 : }
985 : } else {
986 0 : e.unknown();
987 : }
988 : }
989 :
990 43 : system->staves[staff->iLayout] = staff;
991 43 : }
992 :
993 : //---------------------------------------------------------
994 : // initUnreadStaves
995 : //---------------------------------------------------------
996 :
997 39 : static void initUnreadStaves(QList<CapStaff*>& staves)
998 : {
999 : // find the first staff actually read
1000 39 : CapStaff* firstNonEmptyStaff = nullptr;
1001 84 : for (const auto staff : staves) {
1002 45 : if (staff && !firstNonEmptyStaff) {
1003 39 : firstNonEmptyStaff = staff;
1004 : }
1005 : }
1006 :
1007 : // copy its data to the staves present in the file
1008 84 : for (int i = 0; i < staves.size(); ++i) {
1009 45 : if (!(staves.at(i))) {
1010 2 : auto staff = new CapStaff;
1011 2 : staff->numerator = firstNonEmptyStaff->numerator;
1012 2 : staff->log2Denom = firstNonEmptyStaff->log2Denom;
1013 2 : staff->allaBreve = firstNonEmptyStaff->allaBreve;
1014 2 : staff->iLayout = i;
1015 2 : staves[i] = staff;
1016 : }
1017 : }
1018 39 : }
1019 :
1020 : //---------------------------------------------------------
1021 : // readCapxSystem -- capx equivalent of readSystem()
1022 : //---------------------------------------------------------
1023 :
1024 39 : void Capella::readCapxSystem(XmlReader& e)
1025 : {
1026 39 : CapSystem* s = new CapSystem;
1027 39 : s->nAddBarCount = 0;
1028 39 : s->bBarCountReset = 0;
1029 39 : s->explLeftIndent = 0; // ?? TODO ?? use in capella.cpp commented out
1030 :
1031 39 : s->beamMode = CapBeamMode::AUTO;
1032 39 : s->tempo = 0;
1033 39 : s->color = Qt::black;
1034 :
1035 39 : unsigned char b = 0;
1036 39 : s->bJustified = b & 2;
1037 39 : s->bPageBreak = (b & 4) != 0;
1038 39 : s->instrNotation = (b >> 3) & 7;
1039 :
1040 : // set all staves in this system to "not yet read"
1041 84 : for (int i = 0; i < _staffLayouts.size(); ++i) {
1042 45 : s->staves.append(nullptr);
1043 : }
1044 :
1045 : // read staves
1046 78 : while (e.readNextStartElement()) {
1047 39 : const AsciiStringView tag(e.name());
1048 39 : if (tag == "barCount") {
1049 0 : LOGD("readCapxSystem: found barCount (skipping)");
1050 0 : e.skipCurrentElement();
1051 39 : } else if (tag == "staves") {
1052 82 : while (e.readNextStartElement()) {
1053 43 : if (e.name() == "staff") {
1054 43 : readCapxStaff(e, s);
1055 : } else {
1056 0 : e.unknown();
1057 : }
1058 : }
1059 : } else {
1060 0 : e.unknown();
1061 : }
1062 : }
1063 :
1064 : // initializes staves not read
1065 39 : initUnreadStaves(s->staves);
1066 :
1067 39 : systems.append(s);
1068 39 : }
1069 :
1070 : //---------------------------------------------------------
1071 : // capxSystems -- read the capx <systems> element
1072 : //---------------------------------------------------------
1073 :
1074 18 : void Capella::capxSystems(XmlReader& e)
1075 : {
1076 57 : while (e.readNextStartElement()) {
1077 39 : const AsciiStringView tag(e.name());
1078 39 : if (tag == "system") {
1079 39 : readCapxSystem(e);
1080 : } else {
1081 0 : e.unknown();
1082 : }
1083 : }
1084 18 : }
1085 :
1086 : //---------------------------------------------------------
1087 : // capxNotation -- read the capx <notation> element
1088 : //---------------------------------------------------------
1089 :
1090 3 : static void capxNotation(XmlReader& e, uchar& barlineMode, uchar& barlineFrom, uchar& barlineTo)
1091 : {
1092 6 : while (e.readNextStartElement()) {
1093 3 : if (e.name() == "barlines") {
1094 3 : AsciiStringView mode = e.asciiAttribute("mode", "full");
1095 3 : if (mode == "full") {
1096 0 : barlineMode = 3;
1097 3 : } else if (mode == "none") {
1098 0 : barlineMode = 2; // correct ? not handled by capella.cpp anyway
1099 3 : } else if (mode == "internal") {
1100 3 : barlineMode = 1;
1101 0 : } else if (mode == "external") {
1102 0 : barlineMode = 0; // correct ? not handled by capella.cpp anyway
1103 : } else {
1104 0 : barlineMode = 1;
1105 : }
1106 3 : barlineFrom = e.intAttribute("from");
1107 3 : barlineTo = e.intAttribute("to");
1108 3 : e.readNext();
1109 : } else {
1110 0 : e.unknown();
1111 : }
1112 : }
1113 3 : }
1114 :
1115 : //---------------------------------------------------------
1116 : // readCapxStaveLayout -- capx equivalent of readStaveLayout(CapStaffLayout*, int)
1117 : // read the staffLayout element
1118 : //---------------------------------------------------------
1119 :
1120 21 : void Capella::readCapxStaveLayout(XmlReader& e, CapStaffLayout* sl, int /*idx*/)
1121 : {
1122 : // initialize same variables as readStaveLayout(CapStaffLayout*, int)
1123 :
1124 21 : sl->barlineMode = 0;
1125 21 : sl->noteLines = 0;
1126 :
1127 21 : sl->bSmall = 0; // TODO (unclear where this is in the capx file)
1128 :
1129 21 : sl->topDist = 0;
1130 21 : sl->btmDist = 0;
1131 21 : sl->groupDist = 0;
1132 21 : sl->barlineFrom = 0;
1133 21 : sl->barlineTo = 0;
1134 :
1135 21 : unsigned char clef = 0;
1136 21 : sl->form = Form(clef & 7);
1137 21 : sl->line = ClefLine((clef >> 3) & 7);
1138 21 : sl->oct = Oct((clef >> 6));
1139 : // CAPELLA_TRACE(" clef %x form %d, line %d, oct %d", clef, sl->form, sl->line, sl->oct);
1140 :
1141 : // Schlagzeuginformation
1142 21 : unsigned char b = 0; // ?? TODO ?? sl->soundMapIn and sl->soundMapOut are not used
1143 21 : sl->bPercussion = b & 1; // Schlagzeugkanal verwenden
1144 21 : sl->bSoundMapIn = b & 2;
1145 21 : sl->bSoundMapOut = b & 4;
1146 : /*
1147 : if (sl->bSoundMapIn) { // Umleitungstabelle fr Eingabe vom Keyboard
1148 : uchar iMin = readByte();
1149 : Q_UNUSED(iMin);
1150 : uchar n = readByte();
1151 : Q_ASSERT(n > 0 and iMin + n <= 128);
1152 : f->read(sl->soundMapIn, n);
1153 : curPos += n;
1154 : }
1155 : if (sl->bSoundMapOut) { // Umleitungstabelle fr das Vorspielen
1156 : unsigned char iMin = readByte();
1157 : unsigned char n = readByte();
1158 : Q_ASSERT(n > 0 and iMin + n <= 128);
1159 : f->read(sl->soundMapOut, n);
1160 : curPos += n;
1161 : }
1162 : */
1163 21 : sl->sound = 0;
1164 21 : sl->volume = 0;
1165 21 : sl->transp = 0;
1166 :
1167 : CAPELLA_TRACE("readCapxStaveLayout");
1168 21 : sl->descr = e.attribute("description");
1169 52 : while (e.readNextStartElement()) {
1170 31 : const AsciiStringView tag(e.name());
1171 31 : if (tag == "notation") {
1172 3 : capxNotation(e, sl->barlineMode, sl->barlineFrom, sl->barlineTo);
1173 28 : } else if (tag == "distances") {
1174 3 : LOGD("readCapxStaveLayout: found distances (skipping)");
1175 3 : e.skipCurrentElement();
1176 25 : } else if (tag == "instrument") {
1177 4 : sl->name = e.attribute("name");
1178 4 : sl->abbrev = e.attribute("abbrev");
1179 : // elements name and abbrev overrule attributes name and abbrev
1180 10 : while (e.readNextStartElement()) {
1181 6 : const AsciiStringView t(e.name());
1182 6 : if (t == "name") {
1183 3 : sl->name = e.readText();
1184 3 : } else if (t == "abbrev") {
1185 3 : sl->abbrev = e.readText();
1186 : } else {
1187 0 : e.unknown();
1188 : }
1189 : }
1190 21 : } else if (tag == "sound") {
1191 21 : sl->sound = e.intAttribute("instr", 0);
1192 21 : sl->volume = e.intAttribute("volume", 0);
1193 21 : sl->transp = e.intAttribute("transpose", 0);
1194 21 : e.readNext();
1195 : } else {
1196 0 : e.unknown();
1197 : }
1198 : }
1199 : CAPELLA_TRACE(" descr '%s' name '%s' abbrev '%s'",
1200 : qPrintable(sl->descr), qPrintable(sl->name), qPrintable(sl->abbrev));
1201 : CAPELLA_TRACE(" sound %d vol %d transp %d", sl->sound, sl->volume, sl->transp);
1202 : CAPELLA_TRACE("readCapxStaveLayout done");
1203 21 : }
1204 :
1205 : //---------------------------------------------------------
1206 : // capxLayoutBrackets -- read the capx <brackets> element (when child of <layout)
1207 : //---------------------------------------------------------
1208 :
1209 1 : static void capxLayoutBrackets(XmlReader& e, QList<CapBracket>& bracketList)
1210 : {
1211 2 : while (e.readNextStartElement()) {
1212 1 : if (e.name() == "bracket") {
1213 : CapBracket b;
1214 1 : b.from = e.intAttribute("from");
1215 1 : b.to = e.intAttribute("to");
1216 1 : b.curly = e.attribute("curly") == "true";
1217 : // LOGD("Bracket%d %d-%d curly %d", i, b.from, b.to, b.curly);
1218 1 : bracketList.append(b);
1219 1 : e.readNext();
1220 : } else {
1221 0 : e.unknown();
1222 : }
1223 : }
1224 1 : }
1225 :
1226 : //---------------------------------------------------------
1227 : // capxLayoutDistances -- read the capx <Distances> element (when child of <layout)
1228 : //---------------------------------------------------------
1229 :
1230 3 : static void capxLayoutDistances(XmlReader& e, double& smallLineDist, double& normalLineDist, int& topDist)
1231 : {
1232 9 : while (e.readNextStartElement()) {
1233 6 : const AsciiStringView tag(e.name());
1234 6 : if (tag == "staffLines") {
1235 3 : smallLineDist = e.doubleAttribute("small");
1236 3 : normalLineDist = e.doubleAttribute("normal");
1237 3 : e.readNext();
1238 3 : } else if (tag == "systems") {
1239 3 : topDist = e.intAttribute("top");
1240 3 : e.readNext();
1241 : } else {
1242 0 : e.unknown();
1243 : }
1244 : }
1245 3 : }
1246 :
1247 : //---------------------------------------------------------
1248 : // capxLayoutStaves -- read the capx <staves> element (when child of <layout)
1249 : //---------------------------------------------------------
1250 :
1251 18 : void Capella::capxLayoutStaves(XmlReader& e)
1252 : {
1253 18 : int iStave = 0;
1254 39 : while (e.readNextStartElement()) {
1255 21 : if (e.name() == "staffLayout") {
1256 21 : CapStaffLayout* sl = new CapStaffLayout;
1257 21 : readCapxStaveLayout(e, sl, iStave);
1258 21 : _staffLayouts.append(sl);
1259 21 : ++iStave;
1260 : } else {
1261 0 : e.unknown();
1262 : }
1263 : }
1264 18 : }
1265 :
1266 : //---------------------------------------------------------
1267 : // capxLayout -- read the capx <layout> element
1268 : // rough equivalent of readLayout() read part
1269 : //---------------------------------------------------------
1270 :
1271 18 : void Capella::capxLayout(XmlReader& e)
1272 : {
1273 53 : while (e.readNextStartElement()) {
1274 35 : const AsciiStringView tag(e.name());
1275 35 : if (tag == "pages") {
1276 3 : LOGD("capxLayout: found pages (skipping)");
1277 3 : e.skipCurrentElement();
1278 32 : } else if (tag == "distances") {
1279 3 : capxLayoutDistances(e, smallLineDist, normalLineDist, topDist);
1280 : // LOGD("Capella::capxLayout(): smallLineDist %g normalLineDist %g topDist %d",
1281 : // smallLineDist, normalLineDist, topDist);
1282 29 : } else if (tag == "instrumentNames") {
1283 3 : LOGD("capxLayout: found instrumentNames (skipping)");
1284 3 : e.skipCurrentElement();
1285 26 : } else if (tag == "style") {
1286 1 : LOGD("capxLayout: found style (skipping)");
1287 1 : e.skipCurrentElement();
1288 25 : } else if (tag == "staves") {
1289 18 : capxLayoutStaves(e);
1290 7 : } else if (tag == "brackets") {
1291 1 : capxLayoutBrackets(e, brackets);
1292 6 : } else if (tag == "spacing") {
1293 3 : LOGD("capxLayout: found spacing (skipping)");
1294 3 : e.skipCurrentElement();
1295 3 : } else if (tag == "beamFlattening") {
1296 3 : LOGD("capxLayout: found beamFlattening (skipping)");
1297 3 : e.skipCurrentElement();
1298 : } else {
1299 0 : e.unknown();
1300 : }
1301 : }
1302 18 : }
1303 :
1304 : //---------------------------------------------------------
1305 : // initCapxLayout -- capx equivalent of readLayout() initialize part
1306 : //---------------------------------------------------------
1307 :
1308 18 : void Capella::initCapxLayout()
1309 : {
1310 : // initialize same variables as readLayout()
1311 :
1312 18 : smallLineDist = 1.2; // TODO verify default
1313 18 : normalLineDist = 1.76; // TODO verify default
1314 :
1315 18 : topDist = 14; // TODO verify default
1316 18 : interDist = 0; // TODO verify default
1317 :
1318 18 : txtAlign = 0;
1319 18 : adjustVert = 0;
1320 :
1321 18 : unsigned char b = 0;
1322 18 : redundantKeys = b & 1;
1323 18 : modernDoubleNote = b & 2;
1324 18 : Q_ASSERT((b & 0xFC) == 0); // bits 2...7 reserviert
1325 :
1326 18 : bSystemSeparators = 0;
1327 18 : nUnnamed = 0;
1328 :
1329 : // namesFont = ... // ?? not used ??
1330 :
1331 : // Note: readLayout() also reads stave layout (using readStaveLayout(sl, iStave))
1332 : // and system brackets. Here this is handled by readCapx(XmlReader& e).
1333 18 : }
1334 :
1335 : //---------------------------------------------------------
1336 : // readCapx -- capx equivalent of read(QFile* fp)
1337 : //---------------------------------------------------------
1338 :
1339 18 : void Capella::readCapx(XmlReader& e)
1340 : {
1341 : // initialize same variables as read(QFile* fp)
1342 :
1343 18 : f = 0;
1344 18 : curPos = 0;
1345 :
1346 18 : author = 0;
1347 18 : keywords = 0;
1348 18 : comment = 0;
1349 :
1350 18 : nRel = 0;
1351 18 : nAbs = 0;
1352 18 : unsigned char b = 0;
1353 18 : bUseRealSize = b & 1;
1354 18 : bAllowCompression = b & 2;
1355 18 : bPrintLandscape = b & 16;
1356 :
1357 18 : beamRelMin0 = 0;
1358 18 : beamRelMin1 = 0;
1359 18 : beamRelMax0 = 0;
1360 18 : beamRelMax1 = 0;
1361 :
1362 18 : backgroundChord = new ChordObj(this); // contains graphic objects on the page background
1363 18 : bShowBarCount = 0;
1364 18 : barNumberFrame = 0;
1365 18 : nBarDistX = 0;
1366 18 : nBarDistY = 0;
1367 : // QFont barNumFont = ... // not used ?
1368 18 : nFirstPage = 0;
1369 18 : leftPageMargins = 0;
1370 18 : topPageMargins = 0;
1371 18 : rightPageMargins = 0;
1372 18 : btmPageMargins = 0;
1373 :
1374 : // Now do the equivalent of:
1375 : // readLayout(); (called only once)
1376 : // readExtra(); (this is a NOP)
1377 : // readDrawObjectArray();
1378 : // for (unsigned i = 0; i < nSystems; i++)
1379 : // readSystem();
1380 :
1381 18 : initCapxLayout();
1382 :
1383 : // read stave layout
1384 : // read systems
1385 58 : while (e.readNextStartElement()) {
1386 40 : const AsciiStringView tag(e.name());
1387 40 : if (tag == "info") {
1388 3 : LOGD("importCapXml: found info (skipping)");
1389 3 : e.skipCurrentElement();
1390 37 : } else if (tag == "layout") {
1391 18 : capxLayout(e);
1392 19 : } else if (tag == "gallery") {
1393 0 : LOGD("capxLayout: found gallery (skipping)");
1394 0 : e.skipCurrentElement();
1395 19 : } else if (tag == "pageObjects") {
1396 0 : backgroundChord->readCapxObjectArray(e);
1397 19 : } else if (tag == "barCount") {
1398 1 : LOGD("importCapXml: found barCount (skipping)");
1399 1 : e.skipCurrentElement();
1400 18 : } else if (tag == "systems") {
1401 18 : capxSystems(e);
1402 : } else {
1403 0 : e.unknown();
1404 : }
1405 : }
1406 18 : }
1407 :
1408 : //---------------------------------------------------------
1409 : // importCapXml
1410 : //---------------------------------------------------------
1411 :
1412 : void convertCapella(Score* score, Capella* cap, bool capxMode);
1413 :
1414 18 : Err importCapXml(MasterScore* score, const QString& name)
1415 : {
1416 18 : LOGD("importCapXml(score %p, name %s)", score, qPrintable(name));
1417 18 : muse::ZipReader uz(name);
1418 18 : if (!uz.exists()) {
1419 0 : LOGD("importCapXml: <%s> not found", qPrintable(name));
1420 0 : return Err::FileNotFound;
1421 : }
1422 :
1423 18 : muse::ByteArray dbuf = uz.fileData("score.xml");
1424 18 : XmlReader e(dbuf);
1425 18 : e.setDocName(name);
1426 18 : Capella cf;
1427 :
1428 36 : while (e.readNextStartElement()) {
1429 18 : if (e.name() == "score") {
1430 18 : String xmlns = e.attribute("xmlns", u"<none>"); // doesn't work ???
1431 : CAPELLA_TRACE("importCapXml: found score, namespace '%s'", xmlns.toUtf8().constChar());
1432 18 : cf.readCapx(e);
1433 18 : } else {
1434 0 : e.unknown();
1435 : }
1436 : }
1437 :
1438 : try {
1439 18 : convertCapella(score, &cf, true);
1440 : }
1441 0 : catch (Capella::Error) {
1442 : // avoid another error message box
1443 0 : return Err::NoError;
1444 0 : }
1445 18 : return Err::NoError;
1446 18 : }
1447 : }
|