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 "musedata.h"
24 :
25 : #include <QFile>
26 :
27 : #include "engraving/dom/articulation.h"
28 : #include "engraving/dom/barline.h"
29 : #include "engraving/dom/bracket.h"
30 : #include "engraving/dom/chord.h"
31 : #include "engraving/dom/clef.h"
32 : #include "engraving/dom/dynamic.h"
33 : #include "engraving/dom/factory.h"
34 : #include "engraving/dom/key.h"
35 : #include "engraving/dom/lyrics.h"
36 : #include "engraving/dom/masterscore.h"
37 : #include "engraving/dom/measure.h"
38 : #include "engraving/dom/note.h"
39 : #include "engraving/dom/part.h"
40 : #include "engraving/dom/rest.h"
41 : #include "engraving/dom/segment.h"
42 : #include "engraving/dom/slur.h"
43 : #include "engraving/dom/staff.h"
44 : #include "engraving/dom/timesig.h"
45 : #include "engraving/dom/tuplet.h"
46 : #include "engraving/editing/editstaffbrackets.h"
47 : #include "engraving/editing/transpose.h"
48 :
49 : #include "log.h"
50 :
51 : using namespace mu::iex::musedata;
52 : using namespace mu::engraving;
53 :
54 : //---------------------------------------------------------
55 : // musicalAttribute
56 : //---------------------------------------------------------
57 :
58 0 : void MuseData::musicalAttribute(QStringView s, Part* part)
59 : {
60 0 : QList<QStringView> al = s.mid(2).split(u' ', Qt::SkipEmptyParts);
61 0 : foreach (QStringView item, al) {
62 0 : if (item.startsWith(u"K:")) {
63 0 : Key key = Key(item.mid(2).toInt());
64 0 : KeySigEvent ke;
65 0 : Interval v = part->instrument(curTick)->transpose();
66 0 : ke.setConcertKey(key);
67 0 : if (!v.isZero() && !score->style().styleB(Sid::concertPitch)) {
68 0 : v.flip();
69 0 : ke.setKey(Transpose::transposeKey(key, v));
70 : }
71 0 : for (Staff* staff : part->staves()) {
72 0 : staff->setKey(curTick, ke);
73 : }
74 0 : } else if (item.startsWith(u"Q:")) {
75 0 : _division = item.mid(2).toInt();
76 0 : } else if (item.startsWith(u"T:")) {
77 0 : QList<QStringView> tl = item.mid(2).split(u'/');
78 0 : if (tl.size() != 2) {
79 0 : LOGD() << "bad time sig: " << item;
80 0 : continue;
81 : }
82 0 : int z = tl[0].toInt();
83 0 : int n = tl[1].toInt();
84 0 : if ((z > 0) && (n > 0)) {
85 0 : Measure* mes = score->tick2measure(curTick);
86 0 : Segment* seg = mes->getSegment(SegmentType::TimeSig, curTick);
87 0 : TimeSig* ts = Factory::createTimeSig(seg);
88 0 : Staff* staff = part->staff(0);
89 0 : ts->setTrack(staff->idx() * VOICES);
90 0 : ts->setSig(Fraction(z, n));
91 0 : seg->add(ts);
92 : }
93 0 : } else if (item.startsWith(u"X:")) {
94 0 : } else if (item[0] == 'C') {
95 0 : int staffIdx = 1;
96 0 : int col = 2;
97 0 : if (item[1].isDigit()) {
98 0 : staffIdx = item.mid(1, 1).toInt();
99 0 : col = 3;
100 : }
101 0 : staffIdx -= 1;
102 0 : int clefCode = item.mid(col).toInt();
103 0 : ClefType clefType = ClefType::G;
104 0 : switch (clefCode) {
105 : // G clef
106 0 : case 04: clefType = ClefType::G;
107 0 : break;
108 0 : case 05: clefType = ClefType::G_1;
109 0 : break;
110 :
111 : // C clef
112 0 : case 11: clefType = ClefType::C5;
113 0 : break;
114 0 : case 12: clefType = ClefType::C4;
115 0 : break;
116 0 : case 13: clefType = ClefType::C3;
117 0 : break;
118 0 : case 14: clefType = ClefType::C2;
119 0 : break;
120 0 : case 15: clefType = ClefType::C1;
121 0 : break;
122 :
123 : // F clef
124 0 : case 21: clefType = ClefType::F_C;
125 0 : break;
126 0 : case 22: clefType = ClefType::F;
127 0 : break;
128 0 : case 23: clefType = ClefType::F_B;
129 0 : break;
130 :
131 : // G clef 8vb
132 0 : case 34: clefType = ClefType::G8_VB;
133 0 : break;
134 :
135 : // F clef 8vb
136 0 : case 52: clefType = ClefType::F8_VB;
137 0 : break;
138 :
139 : // G clef 8va
140 0 : case 64: clefType = ClefType::G8_VA;
141 0 : break;
142 :
143 : // F clef 8va
144 0 : case 82: clefType = ClefType::F_8VA;
145 0 : break;
146 :
147 0 : default:
148 0 : LOGD() << "unknown clef code: " << clefCode;
149 0 : break;
150 : }
151 :
152 0 : Measure* mes = score->tick2measure(curTick);
153 0 : Segment* seg = mes->getSegment(SegmentType::Clef, curTick);
154 0 : Staff* staff = part->staff(staffIdx);
155 0 : Clef* clef = Factory::createClef(seg);
156 0 : clef->setTrack(staff->idx() * VOICES);
157 0 : clef->setClefType(clefType);
158 0 : seg->add(clef);
159 : } else {
160 0 : LOGD() << "unknown $key: " << item;
161 : }
162 0 : }
163 0 : }
164 :
165 : //---------------------------------------------------------
166 : // readChord
167 : //---------------------------------------------------------
168 :
169 0 : void MuseData::readChord(Part*, QStringView s)
170 : {
171 : // a b c d e f g
172 : static int table[7] = { 9, 11, 0, 2, 4, 5, 7 };
173 :
174 0 : int step = s[1].toLatin1() - 'A';
175 0 : int alter = 0;
176 0 : int octave = 0;
177 0 : for (int i = 2; i < 4; ++i) {
178 0 : if (s[i] == '#') {
179 0 : alter += 1;
180 0 : } else if (s[i] == 'f') {
181 0 : alter -= 1;
182 0 : } else if (s[i].isDigit()) {
183 0 : octave = s.mid(i, 1).toInt();
184 0 : break;
185 : }
186 : }
187 0 : int staffIdx = 0;
188 0 : if (s.size() >= 24) {
189 0 : if (s[23].isDigit()) {
190 0 : staffIdx = s.mid(23, 1).toInt() - 1;
191 : }
192 : }
193 0 : int pitch = clampPitch(table[step] + alter + (octave + 1) * 12);
194 :
195 0 : Chord* chord = (Chord*)chordRest;
196 0 : Note* note = Factory::createNote(chord);
197 0 : note->setPitch(pitch);
198 0 : note->setTpcFromPitch();
199 0 : note->setTrack(staffIdx * VOICES + voice);
200 0 : chord->add(note);
201 0 : }
202 :
203 : //---------------------------------------------------------
204 : // openSlur
205 : //---------------------------------------------------------
206 :
207 0 : void MuseData::openSlur(int idx, const Fraction& tick, Staff* staff, int voc, EngravingItem* startChord)
208 : {
209 0 : staff_idx_t staffIdx = staff->idx();
210 0 : if (slur[idx]) {
211 0 : LOGD("%06d: slur %d already open", tick.ticks(), idx + 1);
212 0 : return;
213 : }
214 0 : slur[idx] = Factory::createSlur(score->dummy());
215 0 : slur[idx]->setTick(tick);
216 0 : slur[idx]->setTrack(staffIdx * VOICES + voc);
217 0 : slur[idx]->setStartElement(startChord);
218 0 : score->addElement(slur[idx]);
219 : }
220 :
221 : //---------------------------------------------------------
222 : // closeSlur
223 : //---------------------------------------------------------
224 :
225 0 : void MuseData::closeSlur(int idx, const Fraction& tick, Staff* staff, int voc, engraving::EngravingItem* endChord)
226 : {
227 0 : staff_idx_t staffIdx = staff->idx();
228 0 : if (slur[idx]) {
229 0 : slur[idx]->setTick2(tick);
230 0 : slur[idx]->setTrack2(staffIdx * VOICES + voc);
231 0 : slur[idx]->setEndElement(endChord);
232 0 : slur[idx] = 0;
233 : } else {
234 0 : LOGD("%06d: slur %d not open", tick.ticks(), idx + 1);
235 : }
236 0 : }
237 :
238 : //---------------------------------------------------------
239 : // readNote
240 : //---------------------------------------------------------
241 :
242 0 : void MuseData::readNote(Part* part, QStringView s)
243 : {
244 : // a b c d e f g
245 : static int table[7] = { 9, 11, 0, 2, 4, 5, 7 };
246 :
247 0 : int step = s[0].toLatin1() - 'A';
248 0 : int alter = 0;
249 0 : int octave = 0;
250 0 : for (int i = 1; i < 3; ++i) {
251 0 : if (s[i] == '#') {
252 0 : alter += 1;
253 0 : } else if (s[i] == 'f') {
254 0 : alter -= 1;
255 0 : } else if (s[i].isDigit()) {
256 0 : octave = s.mid(i, 1).toInt();
257 0 : break;
258 : }
259 : }
260 0 : DirectionV dir = DirectionV::AUTO;
261 0 : if (s.size() >= 23) {
262 0 : if (s[22] == 'u') {
263 0 : dir = DirectionV::UP;
264 0 : } else if (s[22] == 'd') {
265 0 : dir = DirectionV::DOWN;
266 : }
267 : }
268 :
269 0 : int staffIdx = 0;
270 0 : if (s.size() >= 24) {
271 0 : if (s[23].isDigit()) {
272 0 : staffIdx = s.mid(23, 1).toInt() - 1;
273 : }
274 : }
275 0 : Staff* staff = part->staff(staffIdx);
276 0 : staff_idx_t gstaff = staff->idx();
277 :
278 0 : int pitch = table[step] + alter + (octave + 1) * 12;
279 0 : if (pitch < 0) {
280 0 : pitch = 0;
281 : }
282 0 : if (pitch > 127) {
283 0 : pitch = 127;
284 : }
285 0 : Fraction ticks = Fraction::fromTicks((s.mid(5, 3).toInt() * Constants::DIVISION + _division / 2) / _division);
286 0 : Fraction tick = curTick;
287 0 : curTick += ticks;
288 :
289 0 : Tuplet* tuplet = 0;
290 0 : if (s.size() >= 22) {
291 0 : int a = 1;
292 0 : int b = 1;
293 0 : if (s[19] != ' ') {
294 0 : a = s[19].toLatin1() - '0';
295 0 : if (a == 3 && s[20] != ':') {
296 0 : b = 2;
297 : } else {
298 0 : b = s[21].toLatin1() - '0';
299 : }
300 : }
301 0 : if (a == 3 && b == 2) { // triplet
302 0 : if (chordRest && chordRest->tuplet() && ntuplet) {
303 0 : tuplet = chordRest->tuplet();
304 : } else {
305 0 : tuplet = new Tuplet(measure);
306 0 : tuplet->setTrack(gstaff * VOICES);
307 0 : tuplet->setTick(tick);
308 0 : ntuplet = a;
309 0 : tuplet->setRatio(Fraction(a, b));
310 0 : measure->add(tuplet);
311 : }
312 0 : } else if (a == 1 && b == 1) {
313 : } else {
314 0 : LOGD("unsupported tuple %d/%d", a, b);
315 : }
316 : }
317 :
318 0 : Chord* chord = Factory::createChord(score->dummy());
319 0 : chordRest = chord;
320 0 : chord->setTrack(gstaff * VOICES);
321 0 : chord->setStemDirection(dir);
322 0 : if (tuplet) {
323 0 : chord->setTuplet(tuplet);
324 0 : tuplet->add(chord);
325 0 : --ntuplet;
326 : }
327 0 : TDuration d;
328 0 : d.setVal(ticks.ticks());
329 0 : chord->setDurationType(d);
330 :
331 0 : Segment* segment = measure->getSegment(SegmentType::ChordRest, tick);
332 :
333 0 : voice = 0;
334 0 : for (; voice < static_cast<int>(VOICES); ++voice) {
335 0 : EngravingItem* e = segment->element(gstaff * VOICES + voice);
336 0 : if (e == 0) {
337 0 : chord->setTrack(gstaff * VOICES + voice);
338 0 : segment->add(chord);
339 0 : break;
340 : }
341 : }
342 0 : if (voice == VOICES) {
343 0 : LOGD("cannot allocate voice");
344 0 : delete chord;
345 0 : return;
346 : }
347 0 : Note* note = Factory::createNote(chord);
348 0 : note->setPitch(pitch);
349 0 : note->setTpcFromPitch();
350 0 : note->setTrack(gstaff * VOICES + voice);
351 0 : chord->add(note);
352 :
353 0 : QString dynamics;
354 0 : QStringView an = s.mid(31, 11);
355 0 : for (int i = 0; i < an.size(); ++i) {
356 0 : if (an[i] == '(') {
357 0 : openSlur(0, tick, staff, voice, chord);
358 0 : } else if (an[i] == ')') {
359 0 : closeSlur(0, tick, staff, voice, chord);
360 0 : } else if (an[i] == '[') {
361 0 : openSlur(1, tick, staff, voice, chord);
362 0 : } else if (an[i] == ']') {
363 0 : closeSlur(1, tick, staff, voice, chord);
364 0 : } else if (an[i] == '{') {
365 0 : openSlur(2, tick, staff, voice, chord);
366 0 : } else if (an[i] == '}') {
367 0 : closeSlur(2, tick, staff, voice, chord);
368 0 : } else if (an[i] == 'z') {
369 0 : openSlur(3, tick, staff, voice, chord);
370 0 : } else if (an[i] == 'x') {
371 0 : closeSlur(3, tick, staff, voice, chord);
372 0 : } else if (an[i] == '.') {
373 0 : Articulation* atr = Factory::createArticulation(chord);
374 0 : atr->setSymId(SymId::articStaccatoAbove);
375 0 : chord->add(atr);
376 0 : } else if (an[i] == '_') {
377 0 : Articulation* atr = Factory::createArticulation(chord);
378 0 : atr->setSymId(SymId::articTenutoAbove);
379 0 : chord->add(atr);
380 0 : } else if (an[i] == 'v') {
381 0 : Articulation* atr = Factory::createArticulation(chord);
382 0 : atr->setSymId(SymId::stringsUpBow);
383 0 : chord->add(atr);
384 0 : } else if (an[i] == 'n') {
385 0 : Articulation* atr = Factory::createArticulation(chord);
386 0 : atr->setSymId(SymId::stringsDownBow);
387 0 : chord->add(atr);
388 0 : } else if (an[i] == 't') {
389 0 : Articulation* atr = Factory::createArticulation(chord);
390 0 : atr->setSymId(SymId::ornamentTrill);
391 0 : chord->add(atr);
392 0 : } else if (an[i] == 'F') {
393 0 : Articulation* atr = Factory::createArticulation(chord);
394 0 : atr->setUp(true);
395 0 : atr->setSymId(SymId::fermataAbove);
396 0 : chord->add(atr);
397 0 : } else if (an[i] == 'E') {
398 0 : Articulation* atr = Factory::createArticulation(chord);
399 0 : atr->setUp(false);
400 0 : atr->setSymId(SymId::fermataBelow);
401 0 : chord->add(atr);
402 0 : } else if (an[i] == 'O') {
403 : // Articulation* atr = new Articulation(score);
404 : // atr->setArticulationType(ArticulationType::Downbow);
405 : // chord->add(atr);
406 0 : LOGD("%06d: open string '%c' not implemented", tick.ticks(), an[i].toLatin1());
407 0 : } else if (an[i] == '&') {
408 : // skip editorial level
409 0 : if (i <= an.size() && an[i + 1].isDigit()) {
410 0 : ++i;
411 : }
412 0 : } else if (an[i] == 'p') {
413 0 : dynamics += "p";
414 0 : } else if (an[i] == 'm') {
415 0 : dynamics += "m";
416 0 : } else if (an[i] == 'f') {
417 0 : dynamics += "f";
418 0 : } else if (an[i] == '-') { // tie
419 0 : } else if (an[i] == '*') { // start tuplet
420 0 : } else if (an[i] == '!') { // stop tuplet
421 0 : } else if (an[i] == '+') { // cautionary accidental
422 0 : } else if (an[i] == 'X') { // ???
423 0 : } else if (an[i] == ' ') {
424 : } else {
425 0 : LOGD("%06d: notation '%c' not implemented", tick.ticks(), an[i].toLatin1());
426 : }
427 : }
428 0 : if (!dynamics.isEmpty()) {
429 0 : Segment* seg = measure->getSegment(SegmentType::ChordRest, tick);
430 0 : Dynamic* dyn = Factory::createDynamic(seg);
431 0 : dyn->setDynamicType(dynamics);
432 0 : dyn->setTrack(gstaff * VOICES);
433 0 : seg->add(dyn);
434 : }
435 :
436 0 : QStringView txt = s.mid(43, 36);
437 0 : if (!txt.isEmpty()) {
438 0 : QList<QStringView> sl = txt.split(u'|');
439 0 : int no = 0;
440 0 : for (const QStringView w : sl) {
441 0 : Lyrics* l = Factory::createLyrics(chord);
442 0 : l->setPlainText(diacritical(w));
443 0 : l->setVerse(no++);
444 0 : l->setTrack(gstaff * VOICES);
445 0 : chord->add(l);
446 : }
447 0 : }
448 0 : }
449 :
450 : //---------------------------------------------------------
451 : // diacritical
452 : // TODO: not complete
453 : //---------------------------------------------------------
454 :
455 0 : QString MuseData::diacritical(QStringView s)
456 : {
457 : struct TAB {
458 : const char* a;
459 : const char* b;
460 0 : } tab[] = {
461 : { "\\\\", "\\" },
462 : { "\\2s", "ß" },
463 : { "\\3a", "ä" },
464 : { "\\3o", "ö" },
465 : { "\\3u", "ü" },
466 :
467 : { "\\s2", "ß" },
468 : { "\\a3", "ä" },
469 : { "\\o3", "ö" },
470 : { "\\u3", "ü" },
471 : };
472 :
473 0 : QString result = s.toString();
474 0 : for (unsigned int i = 0; i < sizeof(tab) / sizeof(*tab); ++i) {
475 0 : result = result.replace(tab[i].a, QString::fromUtf8(tab[i].b));
476 : }
477 0 : return result;
478 0 : }
479 :
480 : //---------------------------------------------------------
481 : // readRest
482 : //---------------------------------------------------------
483 :
484 0 : void MuseData::readRest(Part* part, QStringView s)
485 : {
486 0 : Fraction ticks = Fraction::fromTicks((s.mid(5, 3).toInt() * Constants::DIVISION + _division / 2) / _division);
487 :
488 0 : Fraction tick = curTick;
489 0 : curTick += ticks;
490 :
491 0 : int staffIdx = 0;
492 0 : if (s.size() >= 24) {
493 0 : if (s[23].isDigit()) {
494 0 : staffIdx = s.mid(23, 1).toInt() - 1;
495 : }
496 : }
497 0 : Staff* staff = part->staff(staffIdx);
498 0 : staff_idx_t gstaff = staff->idx();
499 :
500 0 : TDuration d;
501 0 : d.setVal(ticks.ticks());
502 0 : Rest* rest = Factory::createRest(score->dummy(), d);
503 0 : rest->setTicks(d.fraction());
504 0 : chordRest = rest;
505 0 : rest->setTrack(gstaff * VOICES);
506 0 : Segment* segment = measure->getSegment(SegmentType::ChordRest, tick);
507 :
508 0 : voice = 0;
509 0 : for (; voice < static_cast<int>(VOICES); ++voice) {
510 0 : EngravingItem* e = segment->element(gstaff * VOICES + voice);
511 0 : if (e == 0) {
512 0 : rest->setTrack(gstaff * VOICES + voice);
513 0 : segment->add(rest);
514 0 : break;
515 : }
516 : }
517 0 : if (voice == VOICES) {
518 0 : LOGD("cannot allocate voice");
519 0 : delete rest;
520 0 : return;
521 : }
522 : }
523 :
524 : //---------------------------------------------------------
525 : // readBackup
526 : //---------------------------------------------------------
527 :
528 0 : void MuseData::readBackup(QStringView s)
529 : {
530 0 : Fraction ticks = Fraction::fromTicks((s.mid(5, 3).toInt() * Constants::DIVISION + _division / 2) / _division);
531 0 : if (s[0] == 'b') {
532 0 : curTick -= ticks;
533 : } else {
534 0 : curTick += ticks;
535 : }
536 0 : }
537 :
538 : //---------------------------------------------------------
539 : // createMeasure
540 : //---------------------------------------------------------
541 :
542 0 : Measure* MuseData::createMeasure()
543 : {
544 0 : for (MeasureBase* mb = score->first(); mb; mb = mb->next()) {
545 0 : if (!mb->isMeasure()) {
546 0 : continue;
547 : }
548 0 : Measure* m = (Measure*)mb;
549 0 : Fraction st = m->tick();
550 0 : Fraction l = m->ticks();
551 0 : if (curTick == st) {
552 0 : return m;
553 : }
554 0 : if (curTick > st && curTick < (st + l)) {
555 : // irregular measure
556 0 : break;
557 : }
558 0 : if (curTick < st + l) {
559 0 : LOGD("cannot create measure at %d", curTick.ticks());
560 0 : return 0;
561 : }
562 : }
563 0 : Measure* mes = Factory::createMeasure(score);
564 0 : mes->setTick(curTick);
565 :
566 0 : score->measures()->append(mes);
567 0 : return mes;
568 : }
569 :
570 : //---------------------------------------------------------
571 : // readPart
572 : //---------------------------------------------------------
573 :
574 0 : void MuseData::readPart(const QStringList& sl, Part* part)
575 : {
576 0 : int line = 10;
577 0 : QString s;
578 0 : for (; line < sl.size(); ++line) {
579 0 : s = sl[line];
580 0 : if (!s.isEmpty() && s[0] == '$') {
581 0 : break;
582 : }
583 : }
584 0 : if (line >= sl.size()) {
585 0 : LOGD(" $ not found in part");
586 0 : return;
587 : }
588 0 : curTick = Fraction(0, 1);
589 0 : slur[0] = 0;
590 0 : slur[1] = 0;
591 0 : slur[2] = 0;
592 0 : slur[3] = 0;
593 0 : measure = 0;
594 0 : measure = createMeasure();
595 0 : for (; line < sl.size(); ++line) {
596 0 : s = sl[line];
597 : // LOGD("%6d: <%s>", curTick.ticks(), qPrintable(s));
598 0 : char c = s[0].toLatin1();
599 0 : switch (c) {
600 0 : case 'A':
601 : case 'B':
602 : case 'C':
603 : case 'D':
604 : case 'E':
605 : case 'F':
606 : case 'G':
607 0 : readNote(part, s);
608 0 : break;
609 0 : case ' ': // chord
610 0 : readChord(part, s);
611 0 : break;
612 0 : case 'r':
613 0 : readRest(part, s);
614 0 : break;
615 0 : case 'g': // grace note
616 : case 'c': // cue note
617 : case 'f': // basso continuo
618 0 : break;
619 0 : case 'b': // backspace
620 : case 'i': // forward space
621 0 : readBackup(s);
622 0 : break;
623 0 : case 'm': // measure line / bar line
624 0 : measure = createMeasure();
625 0 : break;
626 0 : case '*': // musical direction
627 0 : break;
628 0 : case 'P': // print suggestion
629 0 : break;
630 0 : case 'S': // sound record
631 0 : break;
632 0 : case '$':
633 0 : musicalAttribute(s, part);
634 0 : break;
635 0 : default:
636 0 : LOGD("unknown record <%s>", qPrintable(s));
637 0 : break;
638 : }
639 : }
640 0 : }
641 :
642 : //---------------------------------------------------------
643 : // countStaves
644 : //---------------------------------------------------------
645 :
646 0 : int MuseData::countStaves(const QStringList& sl)
647 : {
648 0 : int staves = 1;
649 0 : for (int i = 10; i < sl.size(); ++i) {
650 0 : QStringView s { sl[i] };
651 0 : char c = s[0].toLatin1();
652 0 : switch (c) {
653 0 : case 'A':
654 : case 'B':
655 : case 'C':
656 : case 'D':
657 : case 'E':
658 : case 'F':
659 : case 'G':
660 : case 'r':
661 : {
662 0 : int staffIdx = 1;
663 0 : if (s.size() >= 24) {
664 0 : if (s[23].isDigit()) {
665 0 : staffIdx = s.mid(23, 1).toInt();
666 : }
667 : }
668 0 : if (staffIdx > staves) {
669 0 : staves = staffIdx;
670 : }
671 : }
672 0 : break;
673 : }
674 : }
675 0 : return staves;
676 : }
677 :
678 : //---------------------------------------------------------
679 : // read
680 : // return false on error
681 : //---------------------------------------------------------
682 :
683 0 : bool MuseData::read(const QString& name)
684 : {
685 0 : QFile fp(name);
686 0 : if (!fp.open(QIODevice::ReadOnly)) {
687 0 : LOGD("Cannot open file <%s>", qPrintable(name));
688 0 : return false;
689 : }
690 0 : QTextStream ts(&fp);
691 0 : QStringList part;
692 0 : bool commentMode = false;
693 : for (;;) {
694 0 : QString s(ts.readLine());
695 0 : if (s.isNull()) {
696 0 : break;
697 : }
698 0 : if (s.isEmpty()) {
699 0 : if (!commentMode) {
700 0 : part.append(QString(""));
701 : }
702 0 : continue;
703 : }
704 0 : if (s[0] == '&') {
705 0 : commentMode = !commentMode;
706 0 : continue;
707 : }
708 0 : if (commentMode) {
709 0 : continue;
710 : }
711 0 : if (s[0] == '@') {
712 0 : continue;
713 : }
714 0 : if (s[0] == '/') {
715 0 : parts.append(part);
716 :
717 0 : Part* mpart = new Part(score);
718 0 : int staves = countStaves(part);
719 0 : for (int i = 0; i < staves; ++i) {
720 0 : Staff* staff = Factory::createStaff(mpart);
721 0 : score->appendStaff(staff);
722 :
723 0 : if ((staves == 2) && (i == 0)) {
724 0 : EditStaffBrackets::setBracketType(score, staff->idx(), 0, BracketType::BRACE);
725 0 : EditStaffBrackets::setBracketSpan(score, staff->idx(), 0, 2);
726 : }
727 : }
728 0 : score->appendPart(mpart);
729 0 : if (part.size() > 8) {
730 0 : mpart->setPlainLongName(part[8]);
731 : }
732 0 : part.clear();
733 0 : continue;
734 0 : }
735 0 : if (s[0] == 'a') {
736 0 : part.back().append(s.mid(1));
737 0 : continue;
738 : }
739 0 : part.append(s);
740 0 : }
741 0 : fp.close();
742 0 : return true;
743 0 : }
744 :
745 : //---------------------------------------------------------
746 : // convert
747 : //---------------------------------------------------------
748 :
749 0 : void MuseData::convert()
750 : {
751 0 : for (int pn = 0; pn < parts.size(); ++pn) {
752 0 : Part* part = (score->parts())[pn];
753 0 : readPart(parts[pn], part);
754 : }
755 0 : }
|