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 : /**
24 : MusicXML support.
25 : */
26 :
27 : #include "musicxmlsupport.h"
28 : #include "global/serialization/xmlstreamreader.h"
29 :
30 : #include "translation.h"
31 : #include "engraving/dom/accidental.h"
32 : #include "engraving/dom/chordlist.h"
33 : #include "engraving/dom/harmony.h"
34 : #include "engraving/dom/score.h"
35 : #include "engraving/dom/staff.h"
36 : #include "engraving/types/symnames.h"
37 : #include "engraving/types/types.h"
38 :
39 : using namespace muse;
40 : using namespace mu::engraving;
41 :
42 : namespace mu::iex::musicxml {
43 : //---------------------------------------------------------
44 : // errorStringWithLocation
45 : //---------------------------------------------------------
46 :
47 0 : String errorStringWithLocation(int64_t byteOffset, const String& error)
48 : {
49 0 : return muse::mtrc("iex_musicxml", "byte offset %1:").arg(byteOffset) + u" " + error;
50 : }
51 :
52 : //---------------------------------------------------------
53 : // checkAtEndElement
54 : //---------------------------------------------------------
55 :
56 22366 : muse::String checkAtEndElement(const muse::XmlStreamReader& e, const muse::String& expName)
57 : {
58 22366 : if (e.isEndElement() && e.name() == expName.toAscii().constChar()) {
59 22366 : return String();
60 : }
61 :
62 0 : String res = muse::mtrc("iex_musicxml", "expected token type and name ‘EndElement %1’, actual ‘%2 %3’")
63 0 : .arg(expName)
64 0 : .arg(muse::String::fromAscii(e.tokenString().ascii()))
65 0 : .arg(muse::String::fromAscii(e.name().ascii()));
66 0 : return res;
67 0 : }
68 :
69 : //---------------------------------------------------------
70 : // stringToInt
71 : //---------------------------------------------------------
72 :
73 : /**
74 : Convert a string in \a s into an int. Set *ok to true if conversion was
75 : successful. \a s may end with ".0", as is generated by Audiveris 3.2 and up,
76 : in elements <divisions>, <duration>, <alter> and <sound> attributes
77 : dynamics and tempo.
78 : In case of error val return a default value of 0.
79 : Note that non-integer values cannot be handled by mscore.
80 : */
81 :
82 351 : int MusicXmlSupport::stringToInt(const String& s, bool* ok)
83 : {
84 351 : int res = 0;
85 351 : String str = s;
86 351 : if (s.endsWith(u".0")) {
87 0 : str = s.left(s.size() - 2);
88 : }
89 351 : res = str.toInt(ok);
90 351 : return res;
91 351 : }
92 :
93 : //---------------------------------------------------------
94 : // noteTypeToFraction
95 : //---------------------------------------------------------
96 :
97 : /**
98 : Convert MusicXML note type to fraction.
99 : */
100 :
101 0 : Fraction MusicXmlSupport::noteTypeToFraction(const String& type)
102 : {
103 0 : if (type == u"1024th") {
104 0 : return Fraction(1, 1024);
105 0 : } else if (type == u"512th") {
106 0 : return Fraction(1, 512);
107 0 : } else if (type == u"256th") {
108 0 : return Fraction(1, 256);
109 0 : } else if (type == u"128th") {
110 0 : return Fraction(1, 128);
111 0 : } else if (type == u"64th") {
112 0 : return Fraction(1, 64);
113 0 : } else if (type == u"32nd") {
114 0 : return Fraction(1, 32);
115 0 : } else if (type == u"16th") {
116 0 : return Fraction(1, 16);
117 0 : } else if (type == u"eighth") {
118 0 : return Fraction(1, 8);
119 0 : } else if (type == u"quarter") {
120 0 : return Fraction(1, 4);
121 0 : } else if (type == u"half") {
122 0 : return Fraction(1, 2);
123 0 : } else if (type == u"whole") {
124 0 : return Fraction(1, 1);
125 0 : } else if (type == u"breve") {
126 0 : return Fraction(2, 1);
127 0 : } else if (type == u"long") {
128 0 : return Fraction(4, 1);
129 0 : } else if (type == u"maxima") {
130 0 : return Fraction(8, 1);
131 : } else {
132 0 : return Fraction(0, 0);
133 : }
134 : }
135 :
136 : //---------------------------------------------------------
137 : // calculateFraction
138 : //---------------------------------------------------------
139 :
140 : /**
141 : Convert note type, number of dots and actual and normal notes into a duration
142 : */
143 :
144 0 : Fraction MusicXmlSupport::calculateFraction(const String& type, int dots, int normalNotes, int actualNotes)
145 : {
146 : // type
147 0 : Fraction f = MusicXmlSupport::noteTypeToFraction(type);
148 0 : if (f.isValid()) {
149 : // dot(s)
150 0 : Fraction f_no_dots = f;
151 0 : for (int i = 0; i < dots; ++i) {
152 0 : f += (f_no_dots / Fraction(2 << i, 1));
153 : }
154 : // tuplet
155 0 : if (actualNotes > 0 && normalNotes > 0) {
156 0 : f *= normalNotes;
157 0 : f /= Fraction(actualNotes, 1);
158 : }
159 : // clean up (just in case)
160 0 : f.reduce();
161 : }
162 0 : return f;
163 : }
164 :
165 : //---------------------------------------------------------
166 : // accSymId2MusicXmlString
167 : //---------------------------------------------------------
168 :
169 24 : String accSymId2MusicXmlString(const SymId id)
170 : {
171 24 : String s;
172 24 : switch (id) {
173 1 : case SymId::accidentalSharp: s = u"sharp";
174 1 : break;
175 0 : case SymId::accidentalNatural: s = u"natural";
176 0 : break;
177 1 : case SymId::accidentalFlat: s = u"flat";
178 1 : break;
179 8 : case SymId::accidentalDoubleSharp: s = u"double-sharp";
180 8 : break;
181 0 : case SymId::accidentalSharpSharp: s = u"sharp-sharp";
182 0 : break;
183 : //case SymId::accidentalDoubleFlat: s = u"double-flat"; break; // doesn't exist in MusicXML, but see below
184 8 : case SymId::accidentalDoubleFlat: s = u"flat-flat";
185 8 : break;
186 0 : case SymId::accidentalNaturalSharp: s = u"natural-sharp";
187 0 : break;
188 0 : case SymId::accidentalNaturalFlat: s = u"natural-flat";
189 0 : break;
190 1 : case SymId::accidentalQuarterToneFlatStein: s = u"quarter-flat";
191 1 : break;
192 1 : case SymId::accidentalQuarterToneSharpStein: s = u"quarter-sharp";
193 1 : break;
194 0 : case SymId::accidentalThreeQuarterTonesFlatZimmermann: s = u"three-quarters-flat";
195 0 : break;
196 : //case SymId::noSym: s = u"three-quarters-flat"; break; // AccidentalType::FLAT_FLAT_SLASH, MuseScore 1?
197 0 : case SymId::accidentalThreeQuarterTonesSharpStein: s = u"three-quarters-sharp";
198 0 : break;
199 0 : case SymId::accidentalQuarterToneSharpArrowDown: s = u"sharp-down";
200 0 : break;
201 0 : case SymId::accidentalThreeQuarterTonesSharpArrowUp: s = u"sharp-up";
202 0 : break;
203 0 : case SymId::accidentalQuarterToneFlatNaturalArrowDown: s = u"natural-down";
204 0 : break;
205 0 : case SymId::accidentalQuarterToneSharpNaturalArrowUp: s = u"natural-up";
206 0 : break;
207 0 : case SymId::accidentalThreeQuarterTonesFlatArrowDown: s = u"flat-down";
208 0 : break;
209 0 : case SymId::accidentalQuarterToneFlatArrowUp: s = u"flat-up";
210 0 : break;
211 0 : case SymId::accidentalThreeQuarterTonesSharpArrowDown: s = u"double-sharp-down";
212 0 : break;
213 0 : case SymId::accidentalFiveQuarterTonesSharpArrowUp: s = u"double-sharp-up";
214 0 : break;
215 0 : case SymId::accidentalFiveQuarterTonesFlatArrowDown: s = u"flat-flat-down";
216 0 : break;
217 0 : case SymId::accidentalThreeQuarterTonesFlatArrowUp: s = u"flat-flat-up";
218 0 : break;
219 :
220 0 : case SymId::accidentalArrowDown: s = u"arrow-down";
221 0 : break;
222 0 : case SymId::accidentalArrowUp: s = u"arrow-up";
223 0 : break;
224 :
225 0 : case SymId::accidentalTripleSharp: s = u"triple-sharp";
226 0 : break;
227 0 : case SymId::accidentalTripleFlat: s = u"triple-flat";
228 0 : break;
229 :
230 1 : case SymId::accidentalKucukMucennebSharp: s = u"slash-quarter-sharp";
231 1 : break;
232 1 : case SymId::accidentalBuyukMucennebSharp: s = u"slash-sharp";
233 1 : break;
234 1 : case SymId::accidentalBakiyeFlat: s = u"slash-flat";
235 1 : break;
236 1 : case SymId::accidentalBuyukMucennebFlat: s = u"double-slash-flat";
237 1 : break;
238 :
239 0 : case SymId::accidental1CommaSharp: s = u"sharp-1";
240 0 : break;
241 0 : case SymId::accidental2CommaSharp: s = u"sharp-2";
242 0 : break;
243 0 : case SymId::accidental3CommaSharp: s = u"sharp-3";
244 0 : break;
245 0 : case SymId::accidental5CommaSharp: s = u"sharp-5";
246 0 : break;
247 0 : case SymId::accidental1CommaFlat: s = u"flat-1";
248 0 : break;
249 0 : case SymId::accidental2CommaFlat: s = u"flat-2";
250 0 : break;
251 0 : case SymId::accidental3CommaFlat: s = u"flat-3";
252 0 : break;
253 0 : case SymId::accidental4CommaFlat: s = u"flat-4";
254 0 : break;
255 :
256 0 : case SymId::accidentalSori: s = u"sori";
257 0 : break;
258 0 : case SymId::accidentalKoron: s = u"koron";
259 0 : break;
260 0 : default:
261 0 : s = u"other";
262 0 : LOGD("accSymId2MusicXmlString: unknown accidental %d", static_cast<int>(id));
263 : }
264 24 : return s;
265 0 : }
266 :
267 : //---------------------------------------------------------
268 : // accSymId2SmuflMusicXmlString
269 : //---------------------------------------------------------
270 :
271 0 : String accSymId2SmuflMusicXmlString(const SymId id)
272 : {
273 0 : return String::fromAscii(SymNames::nameForSymId(id).ascii());
274 : }
275 :
276 : //---------------------------------------------------------
277 : // musicXmlString2accSymId
278 : // see https://github.com/w3c/musicxml/blob/6e3a667b85855b04d7e4548ea508b537bc29fc52/schema/musicxml.xsd#L1392-L1439
279 : //---------------------------------------------------------
280 :
281 24 : SymId musicXmlString2accSymId(const String mxmlName, const String smufl)
282 : {
283 : // map MusicXML accidental name to MuseScore enum SymId
284 : static const std::map<String, SymId> map {
285 0 : { u"sharp", SymId::accidentalSharp },
286 0 : { u"natural", SymId::accidentalNatural },
287 0 : { u"flat", SymId::accidentalFlat },
288 0 : { u"double-sharp", SymId::accidentalDoubleSharp },
289 0 : { u"sharp-sharp", SymId::accidentalSharpSharp },
290 : //{ u"double-flat", SymId::accidentalDoubleFlat }, // shouldn't harm, but doesn't exist in MusicXML
291 0 : { u"flat-flat", SymId::accidentalDoubleFlat },
292 0 : { u"natural-sharp", SymId::accidentalNaturalSharp },
293 0 : { u"natural-flat", SymId::accidentalNaturalFlat },
294 :
295 0 : { u"quarter-flat", SymId::accidentalQuarterToneFlatStein },
296 0 : { u"quarter-sharp", SymId::accidentalQuarterToneSharpStein },
297 0 : { u"three-quarters-flat", SymId::accidentalThreeQuarterTonesFlatZimmermann },
298 0 : { u"three-quarters-sharp", SymId::accidentalThreeQuarterTonesSharpStein },
299 :
300 0 : { u"sharp-down", SymId::accidentalQuarterToneSharpArrowDown },
301 0 : { u"sharp-up", SymId::accidentalThreeQuarterTonesSharpArrowUp },
302 0 : { u"natural-down", SymId::accidentalQuarterToneFlatNaturalArrowDown },
303 0 : { u"natural-up", SymId::accidentalQuarterToneSharpNaturalArrowUp },
304 0 : { u"flat-down", SymId::accidentalThreeQuarterTonesFlatArrowDown },
305 0 : { u"flat-up", SymId::accidentalQuarterToneFlatArrowUp },
306 0 : { u"double-sharp-down", SymId::accidentalThreeQuarterTonesSharpArrowDown },
307 0 : { u"double-sharp-up", SymId::accidentalFiveQuarterTonesSharpArrowUp },
308 0 : { u"flat-flat-down", SymId::accidentalFiveQuarterTonesFlatArrowDown },
309 0 : { u"flat-flat-up", SymId::accidentalThreeQuarterTonesFlatArrowUp },
310 :
311 0 : { u"arrow-down", SymId::accidentalArrowDown },
312 0 : { u"arrow-up", SymId::accidentalArrowUp },
313 :
314 0 : { u"triple-sharp", SymId::accidentalTripleSharp },
315 0 : { u"triple-flat", SymId::accidentalTripleFlat },
316 :
317 0 : { u"slash-quarter-sharp", SymId::accidentalKucukMucennebSharp },
318 0 : { u"slash-sharp", SymId::accidentalBuyukMucennebSharp },
319 0 : { u"slash-flat", SymId::accidentalBakiyeFlat },
320 0 : { u"double-slash-flat", SymId::accidentalBuyukMucennebFlat },
321 :
322 0 : { u"sharp-1", SymId::accidental1CommaSharp },
323 0 : { u"sharp-2", SymId::accidental2CommaSharp },
324 0 : { u"sharp-3", SymId::accidental3CommaSharp },
325 0 : { u"sharp-5", SymId::accidental5CommaSharp },
326 0 : { u"flat-1", SymId::accidental1CommaFlat },
327 0 : { u"flat-2", SymId::accidental2CommaFlat },
328 0 : { u"flat-3", SymId::accidental3CommaFlat },
329 0 : { u"flat-4", SymId::accidental4CommaFlat },
330 :
331 0 : { u"sori", SymId::accidentalSori },
332 0 : { u"koron", SymId::accidentalKoron },
333 66 : };
334 :
335 24 : auto it = map.find(mxmlName);
336 24 : if (it != map.end()) {
337 24 : return it->second;
338 0 : } else if (mxmlName == u"other") {
339 0 : return SymNames::symIdByName(smufl);
340 : } else {
341 0 : LOGD("musicXmlString2accSymId: unknown accidental '%s'", muPrintable(mxmlName));
342 : }
343 :
344 : // default
345 0 : return SymId::noSym;
346 1 : }
347 :
348 : //---------------------------------------------------------
349 : // accidentalType2MusicXmlString
350 : //---------------------------------------------------------
351 :
352 98 : String accidentalType2MusicXmlString(const AccidentalType type)
353 : {
354 98 : String s;
355 98 : switch (type) {
356 37 : case AccidentalType::SHARP: s = u"sharp";
357 37 : break;
358 18 : case AccidentalType::NATURAL: s = u"natural";
359 18 : break;
360 37 : case AccidentalType::FLAT: s = u"flat";
361 37 : break;
362 3 : case AccidentalType::SHARP2: s = u"double-sharp";
363 3 : break;
364 0 : case AccidentalType::SHARP_SHARP: s = u"sharp-sharp";
365 0 : break;
366 : //case AccidentalType::FLAT2: s = u"double-flat"; break; // doesn't exist in MusicXML, but see below
367 3 : case AccidentalType::FLAT2: s = u"flat-flat";
368 3 : break;
369 0 : case AccidentalType::NATURAL_SHARP: s = u"natural-sharp";
370 0 : break;
371 0 : case AccidentalType::NATURAL_FLAT: s = u"natural-flat";
372 0 : break;
373 0 : case AccidentalType::SHARP_ARROW_UP: s = u"sharp-up";
374 0 : break;
375 0 : case AccidentalType::MIRRORED_FLAT: s = u"quarter-flat";
376 0 : break;
377 0 : case AccidentalType::SHARP_SLASH: s = u"quarter-sharp";
378 0 : break;
379 0 : case AccidentalType::MIRRORED_FLAT2: s = u"three-quarters-flat";
380 0 : break;
381 : //case AccidentalType::FLAT_FLAT_SLASH: s = u"three-quarters-flat"; break; // MuseScore 1?
382 0 : case AccidentalType::SHARP_SLASH4: s = u"three-quarters-sharp";
383 0 : break;
384 0 : case AccidentalType::SHARP_ARROW_DOWN: s = u"sharp-down";
385 0 : break;
386 0 : case AccidentalType::NATURAL_ARROW_UP: s = u"natural-up";
387 0 : break;
388 0 : case AccidentalType::NATURAL_ARROW_DOWN: s = u"natural-down";
389 0 : break;
390 0 : case AccidentalType::FLAT_ARROW_DOWN: s = u"flat-down";
391 0 : break;
392 0 : case AccidentalType::FLAT_ARROW_UP: s = u"flat-up";
393 0 : break;
394 0 : case AccidentalType::SHARP2_ARROW_DOWN: s = u"double-sharp-down";
395 0 : break;
396 0 : case AccidentalType::SHARP2_ARROW_UP: s = u"double-sharp-up";
397 0 : break;
398 0 : case AccidentalType::FLAT2_ARROW_DOWN: s = u"flat-flat-down";
399 0 : break;
400 0 : case AccidentalType::FLAT2_ARROW_UP: s = u"flat-flat-up";
401 0 : break;
402 :
403 0 : case AccidentalType::ARROW_DOWN: s = u"arrow-down";
404 0 : break;
405 0 : case AccidentalType::ARROW_UP: s = u"arrow-up";
406 0 : break;
407 :
408 0 : case AccidentalType::SHARP3: s = u"triple-sharp";
409 0 : break;
410 0 : case AccidentalType::FLAT3: s = u"triple-flat";
411 0 : break;
412 :
413 0 : case AccidentalType::SHARP_SLASH3: s = u"slash-quarter-sharp";
414 0 : break;
415 0 : case AccidentalType::SHARP_SLASH2: s = u"slash-sharp";
416 0 : break;
417 0 : case AccidentalType::FLAT_SLASH: s = u"slash-flat";
418 0 : break;
419 0 : case AccidentalType::FLAT_SLASH2: s = u"double-slash-flat";
420 0 : break;
421 :
422 0 : case AccidentalType::ONE_COMMA_SHARP: s = u"sharp-1";
423 0 : break;
424 0 : case AccidentalType::TWO_COMMA_SHARP: s = u"sharp-2";
425 0 : break;
426 0 : case AccidentalType::THREE_COMMA_SHARP: s = u"sharp-3";
427 0 : break;
428 0 : case AccidentalType::FIVE_COMMA_SHARP: s = u"sharp-5";
429 0 : break;
430 0 : case AccidentalType::ONE_COMMA_FLAT: s = u"flat-1";
431 0 : break;
432 0 : case AccidentalType::TWO_COMMA_FLAT: s = u"flat-2";
433 0 : break;
434 0 : case AccidentalType::THREE_COMMA_FLAT: s = u"flat-3";
435 0 : break;
436 0 : case AccidentalType::FOUR_COMMA_FLAT: s = u"flat-4";
437 0 : break;
438 :
439 0 : case AccidentalType::SORI: s = u"sori";
440 0 : break;
441 0 : case AccidentalType::KORON: s = u"koron";
442 0 : break;
443 0 : default:
444 0 : s = u"other";
445 : }
446 98 : return s;
447 0 : }
448 :
449 : static const std::map<muse::String, AccidentalType> smuflAccidentalTypes {
450 : { u"accidentalDoubleFlatOneArrowDown", AccidentalType::DOUBLE_FLAT_ONE_ARROW_DOWN },
451 : { u"accidentalFlatOneArrowDown", AccidentalType::FLAT_ONE_ARROW_DOWN },
452 : { u"accidentalNaturalOneArrowDown", AccidentalType::NATURAL_ONE_ARROW_DOWN },
453 : { u"accidentalSharpOneArrowDown", AccidentalType::SHARP_ONE_ARROW_DOWN },
454 : { u"accidentalDoubleSharpOneArrowDown", AccidentalType::DOUBLE_SHARP_ONE_ARROW_DOWN },
455 : { u"accidentalDoubleFlatOneArrowUp", AccidentalType::DOUBLE_FLAT_ONE_ARROW_UP },
456 : { u"accidentalFlatOneArrowUp", AccidentalType::FLAT_ONE_ARROW_UP },
457 : { u"accidentalNaturalOneArrowUp", AccidentalType::NATURAL_ONE_ARROW_UP },
458 : { u"accidentalSharpOneArrowUp", AccidentalType::SHARP_ONE_ARROW_UP },
459 : { u"accidentalDoubleSharpOneArrowUp", AccidentalType::DOUBLE_SHARP_ONE_ARROW_UP },
460 : { u"accidentalDoubleFlatTwoArrowsDown", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_DOWN },
461 : { u"accidentalFlatTwoArrowsDown", AccidentalType::FLAT_TWO_ARROWS_DOWN },
462 : { u"accidentalNaturalTwoArrowsDown", AccidentalType::NATURAL_TWO_ARROWS_DOWN },
463 : { u"accidentalSharpTwoArrowsDown", AccidentalType::SHARP_TWO_ARROWS_DOWN },
464 : { u"accidentalDoubleSharpTwoArrowsDown", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_DOWN },
465 : { u"accidentalDoubleFlatTwoArrowsUp", AccidentalType::DOUBLE_FLAT_TWO_ARROWS_UP },
466 : { u"accidentalFlatTwoArrowsUp", AccidentalType::FLAT_TWO_ARROWS_UP },
467 : { u"accidentalNaturalTwoArrowsUp", AccidentalType::NATURAL_TWO_ARROWS_UP },
468 : { u"accidentalSharpTwoArrowsUp", AccidentalType::SHARP_TWO_ARROWS_UP },
469 : { u"accidentalDoubleSharpTwoArrowsUp", AccidentalType::DOUBLE_SHARP_TWO_ARROWS_UP },
470 : { u"accidentalDoubleFlatThreeArrowsDown", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_DOWN },
471 : { u"accidentalFlatThreeArrowsDown", AccidentalType::FLAT_THREE_ARROWS_DOWN },
472 : { u"accidentalNaturalThreeArrowsDown", AccidentalType::NATURAL_THREE_ARROWS_DOWN },
473 : { u"accidentalSharpThreeArrowsDown", AccidentalType::SHARP_THREE_ARROWS_DOWN },
474 : { u"accidentalDoubleSharpThreeArrowsDown", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_DOWN },
475 : { u"accidentalDoubleFlatThreeArrowsUp", AccidentalType::DOUBLE_FLAT_THREE_ARROWS_UP },
476 : { u"accidentalFlatThreeArrowsUp", AccidentalType::FLAT_THREE_ARROWS_UP },
477 : { u"accidentalNaturalThreeArrowsUp", AccidentalType::NATURAL_THREE_ARROWS_UP },
478 : { u"accidentalSharpThreeArrowsUp", AccidentalType::SHARP_THREE_ARROWS_UP },
479 : { u"accidentalDoubleSharpThreeArrowsUp", AccidentalType::DOUBLE_SHARP_THREE_ARROWS_UP },
480 : { u"accidentalLowerOneSeptimalComma", AccidentalType::LOWER_ONE_SEPTIMAL_COMMA },
481 : { u"accidentalRaiseOneSeptimalComma", AccidentalType::RAISE_ONE_SEPTIMAL_COMMA },
482 : { u"accidentalLowerTwoSeptimalCommas", AccidentalType::LOWER_TWO_SEPTIMAL_COMMAS },
483 : { u"accidentalRaiseTwoSeptimalCommas", AccidentalType::RAISE_TWO_SEPTIMAL_COMMAS },
484 : { u"accidentalLowerOneUndecimalQuartertone", AccidentalType::LOWER_ONE_UNDECIMAL_QUARTERTONE },
485 : { u"accidentalRaiseOneUndecimalQuartertone", AccidentalType::RAISE_ONE_UNDECIMAL_QUARTERTONE },
486 : { u"accidentalLowerOneTridecimalQuartertone", AccidentalType::LOWER_ONE_TRIDECIMAL_QUARTERTONE },
487 : { u"accidentalRaiseOneTridecimalQuartertone", AccidentalType::RAISE_ONE_TRIDECIMAL_QUARTERTONE },
488 : { u"accidentalDoubleFlatEqualTempered", AccidentalType::DOUBLE_FLAT_EQUAL_TEMPERED },
489 : { u"accidentalFlatEqualTempered", AccidentalType::FLAT_EQUAL_TEMPERED },
490 : { u"accidentalNaturalEqualTempered", AccidentalType::NATURAL_EQUAL_TEMPERED },
491 : { u"accidentalSharpEqualTempered", AccidentalType::SHARP_EQUAL_TEMPERED },
492 : { u"accidentalDoubleSharpEqualTempered", AccidentalType::DOUBLE_SHARP_EQUAL_TEMPERED },
493 : { u"accidentalQuarterFlatEqualTempered", AccidentalType::QUARTER_FLAT_EQUAL_TEMPERED },
494 : { u"accidentalQuarterSharpEqualTempered", AccidentalType::QUARTER_SHARP_EQUAL_TEMPERED }
495 : };
496 :
497 : //---------------------------------------------------------
498 : // accidentalType2SmuflMusicXmlString
499 : //---------------------------------------------------------
500 :
501 0 : String accidentalType2SmuflMusicXmlString(const AccidentalType type)
502 : {
503 0 : return muse::key(smuflAccidentalTypes, type);
504 : }
505 :
506 : //---------------------------------------------------------
507 : // musicXmlString2accidentalType
508 : //---------------------------------------------------------
509 :
510 : /**
511 : Convert a MusicXML accidental name to a MuseScore enum AccidentalType.
512 : see https://github.com/w3c/musicxml/blob/6e3a667b85855b04d7e4548ea508b537bc29fc52/schema/musicxml.xsd#L1392-L1439
513 : */
514 :
515 260 : AccidentalType musicXmlString2accidentalType(const String mxmlName, const String smufl)
516 : {
517 : // map MusicXML accidental name to MuseScore enum AccidentalType
518 : static const std::map<String, AccidentalType> map {
519 0 : { u"sharp", AccidentalType::SHARP },
520 0 : { u"natural", AccidentalType::NATURAL },
521 0 : { u"flat", AccidentalType::FLAT },
522 0 : { u"double-sharp", AccidentalType::SHARP2 },
523 0 : { u"sharp-sharp", AccidentalType::SHARP_SHARP },
524 : //{ u"double-flat", AccidentalType::FLAT2 }, // shouldn't harm, but doesn't exist in MusicXML
525 0 : { u"flat-flat", AccidentalType::FLAT2 },
526 0 : { u"natural-sharp", AccidentalType::NATURAL_SHARP },
527 0 : { u"natural-flat", AccidentalType::NATURAL_FLAT },
528 :
529 0 : { u"quarter-flat", AccidentalType::MIRRORED_FLAT },
530 0 : { u"quarter-sharp", AccidentalType::SHARP_SLASH },
531 0 : { u"three-quarters-flat", AccidentalType::MIRRORED_FLAT2 },
532 0 : { u"three-quarters-sharp", AccidentalType::SHARP_SLASH4 },
533 :
534 0 : { u"sharp-up", AccidentalType::SHARP_ARROW_UP },
535 0 : { u"natural-down", AccidentalType::NATURAL_ARROW_DOWN },
536 0 : { u"natural-up", AccidentalType::NATURAL_ARROW_UP },
537 0 : { u"sharp-down", AccidentalType::SHARP_ARROW_DOWN },
538 0 : { u"flat-down", AccidentalType::FLAT_ARROW_DOWN },
539 0 : { u"flat-up", AccidentalType::FLAT_ARROW_UP },
540 0 : { u"double-sharp-down", AccidentalType::SHARP2_ARROW_DOWN },
541 0 : { u"double-sharp-up", AccidentalType::SHARP2_ARROW_UP },
542 0 : { u"flat-flat-down", AccidentalType::FLAT2_ARROW_DOWN },
543 0 : { u"flat-flat-up", AccidentalType::FLAT2_ARROW_UP },
544 :
545 0 : { u"arrow-down", AccidentalType::ARROW_DOWN },
546 0 : { u"arrow-up", AccidentalType::ARROW_UP },
547 :
548 0 : { u"triple-sharp", AccidentalType::SHARP3 },
549 0 : { u"triple-flat", AccidentalType::FLAT3 },
550 :
551 0 : { u"slash-quarter-sharp", AccidentalType::SHARP_SLASH3 }, // MIRRORED_FLAT_SLASH }, ?
552 0 : { u"slash-sharp", AccidentalType::SHARP_SLASH2 }, // SHARP_SLASH }, ?
553 0 : { u"slash-flat", AccidentalType::FLAT_SLASH },
554 0 : { u"double-slash-flat", AccidentalType::FLAT_SLASH2 },
555 :
556 0 : { u"sharp-1", AccidentalType::ONE_COMMA_SHARP },
557 0 : { u"sharp-2", AccidentalType::TWO_COMMA_SHARP },
558 0 : { u"sharp-3", AccidentalType::THREE_COMMA_SHARP },
559 0 : { u"sharp-5", AccidentalType::FIVE_COMMA_SHARP },
560 0 : { u"flat-1", AccidentalType::ONE_COMMA_FLAT },
561 0 : { u"flat-2", AccidentalType::TWO_COMMA_FLAT },
562 0 : { u"flat-3", AccidentalType::THREE_COMMA_FLAT },
563 0 : { u"flat-4", AccidentalType::FOUR_COMMA_FLAT },
564 :
565 0 : { u"sori", AccidentalType::SORI },
566 0 : { u"koron", AccidentalType::KORON },
567 302 : };
568 :
569 260 : auto it = map.find(mxmlName);
570 260 : if (it != map.end()) {
571 260 : return it->second;
572 0 : } else if (mxmlName == "other" && muse::contains(smuflAccidentalTypes, smufl)) {
573 0 : return smuflAccidentalTypes.at(smufl);
574 : } else {
575 0 : LOGD("musicXmlString2accidentalType: unknown accidental '%s'", muPrintable(mxmlName));
576 : }
577 0 : return AccidentalType::NONE;
578 1 : }
579 :
580 : //---------------------------------------------------------
581 : // musicXmlAccidentalTextToChar
582 : //---------------------------------------------------------
583 :
584 : /**
585 : Convert a MusicXML accidental text to a accidental character.
586 : */
587 :
588 20 : String musicXmlAccidentalTextToChar(const String mxmlName)
589 : {
590 : // map MusicXML accidental name to MuseScore enum AccidentalType
591 : static const std::map<String, String> map {
592 : { u"sharp", u"♯" },
593 : { u"natural", u"♮" },
594 : { u"flat", u"♭" },
595 25 : };
596 :
597 20 : auto it = map.find(mxmlName);
598 20 : if (it != map.end()) {
599 20 : return it->second;
600 : } else {
601 0 : LOGD("musicXmlAccidentalTextToChar: unsupported accidental '%s'", muPrintable(mxmlName));
602 : }
603 0 : return u"";
604 1 : }
605 :
606 : //---------------------------------------------------------
607 : // microtonalGuess
608 : //---------------------------------------------------------
609 :
610 : /**
611 : Convert a MusicXML alter tag into a microtonal accidental in MuseScore enum AccidentalType.
612 : */
613 :
614 0 : AccidentalType microtonalGuess(double val)
615 : {
616 0 : if (muse::RealIsEqual(val, -1.5)) {
617 0 : return AccidentalType::MIRRORED_FLAT2;
618 0 : } else if (muse::RealIsEqual(val, -0.5)) {
619 0 : return AccidentalType::MIRRORED_FLAT;
620 0 : } else if (muse::RealIsEqual(val, 0.5)) {
621 0 : return AccidentalType::SHARP_SLASH;
622 0 : } else if (muse::RealIsEqual(val, 1.5)) {
623 0 : return AccidentalType::SHARP_SLASH4;
624 : }
625 :
626 0 : for (int i = 1; i < static_cast<int>(AccidentalType::END); ++i) {
627 0 : AccidentalType type = static_cast<AccidentalType>(i);
628 0 : double altVal = static_cast<int>(Accidental::subtype2value(type)) + Accidental::subtype2centOffset(type) / 100.0;
629 0 : if (muse::RealIsEqual(val, altVal)) {
630 0 : return type;
631 : }
632 : }
633 :
634 0 : LOGD("Guess for microtonal accidental corresponding to value %f failed.", val);
635 : // default
636 0 : return AccidentalType::NONE;
637 : }
638 :
639 : //---------------------------------------------------------
640 : // isLaissezVibrer
641 : //---------------------------------------------------------
642 :
643 6 : bool isLaissezVibrer(const SymId id)
644 : {
645 6 : return id == SymId::articLaissezVibrerAbove || id == SymId::articLaissezVibrerBelow;
646 : }
647 :
648 113 : String harmonyXmlKind(const engraving::HarmonyInfo* h)
649 : {
650 113 : if (!h) {
651 0 : return String();
652 : }
653 113 : const ChordDescription* cd = h->descr();
654 113 : return cd ? cd->xmlKind : String();
655 : }
656 :
657 198 : String harmonyXmlText(const engraving::HarmonyInfo* h)
658 : {
659 198 : if (!h) {
660 0 : return String();
661 : }
662 198 : const ChordDescription* cd = h->descr();
663 198 : return cd ? cd->xmlText : String();
664 : }
665 :
666 99 : String harmonyXmlSymbols(const engraving::HarmonyInfo* h)
667 : {
668 99 : if (!h) {
669 0 : return String();
670 : }
671 99 : const ChordDescription* cd = h->descr();
672 99 : return cd ? cd->xmlSymbols : String();
673 : }
674 :
675 99 : String harmonyXmlParens(const engraving::HarmonyInfo* h)
676 : {
677 99 : if (!h) {
678 0 : return String();
679 : }
680 99 : const ChordDescription* cd = h->descr();
681 99 : return cd ? cd->xmlParens : String();
682 : }
683 :
684 81 : StringList harmonyXmlDegrees(const engraving::HarmonyInfo* h)
685 : {
686 81 : if (!h) {
687 0 : return StringList();
688 : }
689 81 : const ChordDescription* cd = h->descr();
690 81 : return cd ? cd->xmlDegrees : StringList();
691 : }
692 :
693 79 : const ChordDescription* harmonyFromXml(engraving::HarmonyInfo* info, engraving::Score* score, const muse::String& kind,
694 : const muse::String& kindText, const muse::String& symbols, const muse::String& parens,
695 : const std::vector<engraving::HDegree>& dl)
696 : {
697 79 : if (!info) {
698 0 : return nullptr;
699 : }
700 79 : ParsedChord* pc = new ParsedChord;
701 79 : info->setTextName(pc->fromXml(kind, kindText, symbols, parens, dl, score->chordList()));
702 79 : info->setParsedChord(pc);
703 79 : const ChordDescription* cd = info->getDescription(info->textName(), pc);
704 79 : return cd;
705 : }
706 :
707 2 : String harmonyXmlFunction(const engraving::HarmonyInfo* info, const engraving::Harmony* h, engraving::Key key)
708 : {
709 2 : if (!info || !tpcIsValid(info->rootTpc())) {
710 0 : return String();
711 : }
712 2 : if (key == Key::INVALID) {
713 0 : const Staff* st = h->staff();
714 0 : key = st ? st->key(h->tick()) : Key::INVALID;
715 : }
716 2 : return tpc2Function(info->rootTpc(), key);
717 : }
718 :
719 20 : String harmonyXmlFunction(const engraving::HarmonyInfo* info, const engraving::Harmony* h)
720 : {
721 20 : if (!info || !tpcIsValid(info->rootTpc())) {
722 2 : return String();
723 : }
724 18 : const Staff* st = h->staff();
725 18 : Key key = st ? st->key(h->tick()) : Key::INVALID;
726 18 : return tpc2Function(info->rootTpc(), key);
727 : }
728 :
729 19 : void setHarmonyRootTpcFromFunction(HarmonyInfo* info, const Harmony* h, const muse::String& s, engraving::Key key)
730 : {
731 19 : if (!info) {
732 0 : return;
733 : }
734 19 : if (key == Key::INVALID) {
735 0 : const Staff* st = h->staff();
736 0 : key = st ? st->key(h->tick()) : Key::INVALID;
737 : }
738 19 : info->setRootTpc(function2Tpc(s, key));
739 : }
740 : }
|