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) 2025 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 "importtef.h"
24 : #include "measurehandler.h"
25 : #include "readinglist.h"
26 : #include "tuplethandler.h"
27 :
28 : #include "engraving/dom/arpeggio.h"
29 : #include "engraving/dom/box.h"
30 : #include "engraving/dom/capo.h"
31 : #include "engraving/dom/chord.h"
32 : #include "engraving/dom/excerpt.h"
33 : #include "engraving/dom/factory.h"
34 : #include "engraving/dom/fingering.h"
35 : #include "engraving/dom/glissando.h"
36 : #include "engraving/dom/hammeronpulloff.h"
37 : #include "engraving/dom/keysig.h"
38 : #include "engraving/dom/letring.h"
39 : #include "engraving/dom/measure.h"
40 : #include "engraving/dom/measurebase.h"
41 : #include "engraving/dom/note.h"
42 : #include "engraving/dom/part.h"
43 : #include "engraving/dom/playcounttext.h"
44 : #include "engraving/dom/rest.h"
45 : #include "engraving/dom/staff.h"
46 : #include "engraving/dom/stafftext.h"
47 : #include "engraving/dom/stafftype.h"
48 : #include "engraving/dom/tempotext.h"
49 : #include "engraving/dom/text.h"
50 : #include "engraving/dom/timesig.h"
51 : #include "engraving/dom/volta.h"
52 : #include "log.h"
53 :
54 : using namespace mu::engraving;
55 :
56 : namespace mu::iex::tabledit {
57 142 : int8_t TablEdit::readInt8()
58 : {
59 : int8_t result;
60 142 : _file->read((uint8_t*)&result, 1);
61 142 : return result;
62 : }
63 :
64 11238 : uint8_t TablEdit::readUInt8()
65 : {
66 : uint8_t result;
67 11238 : _file->read((uint8_t*)&result, 1);
68 11238 : return result;
69 : }
70 :
71 1888 : uint16_t TablEdit::readUInt16()
72 : {
73 : uint16_t result;
74 1888 : _file->read((uint8_t*)&result, 2);
75 1888 : return result;
76 : }
77 :
78 1570 : uint32_t TablEdit::readUInt32()
79 : {
80 : uint32_t result;
81 1570 : _file->read((uint8_t*)&result, 4);
82 1570 : return result;
83 : }
84 :
85 : // read sized and null-terminated utf8 text from the current position
86 :
87 282 : std::string TablEdit::readUtf8Text()
88 : {
89 282 : std::string result;
90 282 : uint16_t size = readUInt16();
91 282 : if (size < 1) {
92 1 : LOGE() << "Invalid Utf8Text";
93 1 : return result;
94 : }
95 1942 : for (uint16_t i = 0; i < size - 1; ++i) {
96 1661 : result += readUInt8();
97 : }
98 281 : readUInt8(); // skip null
99 : LOGN("size %d result '%s'", size, result.c_str());
100 281 : return result;
101 0 : }
102 :
103 : // read sized and null-terminated utf8 text
104 : // input is the position where the text's position in the file is stored
105 :
106 366 : std::string TablEdit::readUtf8TextIndirect(uint32_t positionOfPosition)
107 : {
108 366 : _file->seek(positionOfPosition);
109 366 : uint32_t position = readUInt32();
110 366 : if (position) {
111 246 : _file->seek(position);
112 : LOGN("position %d", position);
113 246 : return readUtf8Text();
114 : } else {
115 240 : return "";
116 : }
117 : }
118 :
119 : // return the part index for the instrument containing stringIdx
120 :
121 471 : engraving::part_idx_t TablEdit::partIdx(size_t stringIdx, bool& ok) const
122 : {
123 471 : ok = true;
124 471 : engraving::part_idx_t result { 0 };
125 471 : engraving::track_idx_t lowerBound { 1 };
126 471 : engraving::track_idx_t upperBound { 0 };
127 :
128 517 : for (const auto& instrument : tefInstruments) {
129 517 : upperBound += instrument.stringNumber;
130 517 : if (lowerBound <= stringIdx && stringIdx <= upperBound) {
131 : LOGN("string %zu lower %zu upper %zu found result %zu", stringIdx, lowerBound, upperBound, result);
132 471 : return result;
133 : }
134 46 : ++result;
135 46 : lowerBound += instrument.stringNumber;
136 : }
137 0 : ok = false;
138 0 : result = 0;
139 : LOGN("string %zu not found result %zu", stringIdx, result);
140 0 : return result;
141 : }
142 :
143 : // return total number of strings in previous parts
144 :
145 401 : int TablEdit::stringNumberPreviousParts(part_idx_t partIdx) const
146 : {
147 401 : int result { 0 };
148 439 : for (part_idx_t i = 0; i < partIdx; ++i) {
149 38 : result += tefInstruments.at(i).stringNumber;
150 : }
151 : LOGN("partIdx %zu result %d", partIdx, result);
152 401 : return result;
153 : }
154 :
155 : // debug: use color cr to show voice
156 :
157 : #if 0
158 : static muse::draw::Color toColor(const voice_idx_t voice)
159 : {
160 : // debug: color notes based on voice number
161 : switch (voice) {
162 : case 0: return muse::draw::Color::BLUE;
163 : case 1: return muse::draw::Color::GREEN;
164 : case 2: return muse::draw::Color::RED;
165 : case 3: return { 150, 150, 0, 255 };
166 : default: return muse::draw::Color::BLACK;
167 : }
168 : }
169 :
170 : #else
171 434 : static muse::draw::Color toColor(const voice_idx_t)
172 : {
173 : // no debug: color notes black
174 434 : return muse::draw::Color::BLACK;
175 : }
176 :
177 : #endif
178 :
179 : // create a VoiceAllocator for every instrument
180 :
181 61 : void TablEdit::initializeVoiceAllocators(std::vector<VoiceAllocator>& allocators)
182 : {
183 134 : for (size_t i = 0; i < tefInstruments.size(); ++i) {
184 73 : VoiceAllocator allocator;
185 73 : allocators.push_back(allocator);
186 73 : }
187 61 : }
188 :
189 61 : void TablEdit::allocateVoices(std::vector<VoiceAllocator>& allocators)
190 : {
191 61 : std::vector<const TefNote*> column;
192 61 : int currentPosition { -1 };
193 61 : engraving::part_idx_t currentPart { 0 };
194 482 : for (const TefNote& tefNote : tefContents) {
195 : // a new column is started when either the postion or the part changes
196 421 : bool ok { true };
197 421 : const auto part = partIdx(tefNote.string, ok);
198 421 : if (!ok) {
199 0 : LOGD("error: invalid string %d", tefNote.string);
200 0 : continue;
201 : }
202 421 : if (tefNote.position != currentPosition || part != currentPart) {
203 : // handle the previous column
204 336 : allocators[currentPart].addColumn(column);
205 : // start a new column
206 336 : currentPosition = tefNote.position;
207 336 : currentPart = part;
208 336 : column.clear();
209 : }
210 421 : column.push_back(&tefNote);
211 : }
212 : // handle the last column
213 61 : allocators[currentPart].addColumn(column);
214 61 : }
215 :
216 5 : static void connectTie(mu::engraving::Chord* chord, Note* note)
217 : {
218 5 : Segment* segment { chord->segment() };
219 5 : auto startTrack { VOICES* (chord->track() / VOICES) };
220 5 : auto endTrack { startTrack + VOICES - 1 };
221 : LOGN("segment %p tick %d string %d startTrack %zu track %zu endTrack %zu",
222 : segment, segment->tick().ticks(), note->string(), startTrack, chord->track(), endTrack);
223 :
224 6 : for (Segment* seg = segment->prev1(); seg; seg = seg->prev1()) {
225 11 : for (auto track = startTrack; track <= endTrack; ++track) {
226 10 : EngravingItem* el = seg->element(track);
227 10 : if (el && el->isChord()) {
228 6 : Chord* firstChord = toChord(el);
229 : LOGN("firstChord %p tick %d track %zu", firstChord, firstChord->tick().ticks(), track);
230 9 : for (Note* firstNote : firstChord->notes()) {
231 : LOGN("- string %d", firstNote->string());
232 8 : if (firstNote->string() == note->string()) {
233 5 : Tie* tie = Factory::createTie(firstNote);
234 5 : tie->setEndNote(note);
235 5 : firstNote->add(tie);
236 : LOGN(" -> tie %p", tie);
237 5 : return;
238 : }
239 : }
240 : }
241 : }
242 : }
243 : }
244 :
245 5 : static String fingeringTextLH(int leftFinger)
246 : {
247 5 : switch (leftFinger) {
248 1 : case 0: return u"0";
249 1 : case 1: return u"1";
250 1 : case 2: return u"2";
251 1 : case 3: return u"3";
252 1 : case 4: return u"4";
253 0 : default: return u"invalid"; // shouldn't happen
254 : }
255 : }
256 :
257 5 : static String fingeringTextRH(int rightFinger)
258 : {
259 5 : switch (rightFinger) {
260 1 : case 0: return u"p";
261 1 : case 1: return u"i";
262 1 : case 2: return u"m";
263 1 : case 3: return u"a";
264 1 : case 4: return u"c";
265 0 : default: return u"invalid"; // shouldn't happen
266 : }
267 : }
268 :
269 401 : static mu::engraving::Note* addNoteToChord(mu::engraving::Chord* chord, const TefNote* tefNote, int stringOffset, int pitch,
270 : muse::draw::Color color,
271 : std::vector<mu::engraving::Note*>& tiedNotes)
272 : {
273 : LOGN("pitch %d", pitch);
274 401 : mu::engraving::Note* note = Factory::createNote(chord);
275 401 : if (note) {
276 401 : note->setTrack(chord->track());
277 401 : note->setPitch(pitch);
278 401 : note->setTpcFromPitch(Prefer::NEAREST);
279 401 : note->setFret(tefNote->fret);
280 401 : note->setString(tefNote->string - stringOffset - 1);
281 401 : note->setColor(color);
282 401 : if (tefNote->tie) {
283 5 : tiedNotes.push_back(note);
284 : }
285 401 : if (tefNote->fingeringLH) {
286 5 : Fingering* fi = Factory::createFingering(note, TextStyleType::LH_GUITAR_FINGERING);
287 5 : String finger { fingeringTextLH(tefNote->fingeringLH - 1) };
288 5 : fi->setPlainText(finger);
289 5 : note->add(fi);
290 5 : }
291 401 : if (tefNote->fingeringRH) {
292 5 : Fingering* fi = Factory::createFingering(note, TextStyleType::RH_GUITAR_FINGERING);
293 5 : String finger { fingeringTextRH(tefNote->fingeringRH - 1) };
294 5 : fi->setPlainText(finger);
295 5 : note->add(fi);
296 5 : }
297 401 : chord->add(note);
298 : }
299 401 : return note;
300 : }
301 :
302 13 : static void addGraceNotesToChord(mu::engraving::Chord* chord, int pitch, int fret, int string, muse::draw::Color color)
303 : {
304 13 : mu::engraving::TDuration durationType(mu::engraving::DurationType::V_INVALID);
305 13 : const int ticks { 240 };
306 13 : durationType.setVal(ticks);
307 13 : mu::engraving::Chord* cr = Factory::createChord(chord->score()->dummy());
308 13 : cr->setTrack(chord->track());
309 13 : cr->setNoteType(mu::engraving::NoteType::APPOGGIATURA);
310 13 : cr->setDurationType(mu::engraving::DurationType::V_EIGHTH);
311 13 : cr->setTicks(durationType.fraction());
312 13 : mu::engraving::Note* note = Factory::createNote(cr);
313 13 : note->setTrack(chord->track());
314 : //xmlSetPitch(note, sao.s.toLatin1(), sao.a, sao.o);
315 13 : note->setPitch(pitch);
316 13 : note->setTpcFromPitch(Prefer::NEAREST);
317 13 : note->setFret(fret);
318 13 : note->setString(string);
319 13 : note->setColor(color);
320 13 : cr->add(note);
321 13 : chord->add(cr);
322 13 : }
323 :
324 29 : static void addRest(Segment* segment, track_idx_t track, TDuration tDuration, Fraction length, muse::draw::Color color, bool visible = true)
325 : {
326 29 : mu::engraving::Rest* rest = Factory::createRest(segment);
327 29 : if (rest) {
328 29 : rest->setTrack(track);
329 29 : rest->setDurationType(tDuration);
330 29 : rest->setTicks(length);
331 29 : rest->setColor(color);
332 29 : rest->setVisible(visible);
333 29 : segment->add(rest);
334 : }
335 29 : }
336 :
337 4 : static void addVolta(Score* score, Measure* measure, const Ending& ending)
338 : {
339 4 : constexpr track_idx_t voltaTrackIdx = 0;
340 :
341 4 : Measure* endMeasure = measure;
342 5 : for (int countdown = ending.duration - 1; countdown > 0; countdown--) {
343 1 : Measure* next = endMeasure->nextMeasure();
344 1 : if (!next) {
345 0 : LOGD() << "Ending at " << "TBD" << " specifies non-existent end measure.";
346 0 : break;
347 : }
348 1 : endMeasure = next;
349 : }
350 :
351 4 : Volta* volta = Factory::createVolta(score->dummy());
352 4 : volta->setTrack(voltaTrackIdx);
353 4 : volta->setTick(measure->tick());
354 4 : volta->setTick2(endMeasure->endTick());
355 4 : volta->setVisible(true);
356 8 : volta->setEndings({ ending.number });
357 4 : String text;
358 4 : text += String("%1").arg(ending.number);
359 4 : volta->setText(text);
360 4 : score->addElement(volta);
361 4 : }
362 :
363 61 : void TablEdit::createCapos()
364 : {
365 134 : for (size_t part = 0; part < tefInstruments.size(); ++part) {
366 73 : int capo { tefInstruments.at(part).nCapo };
367 73 : if (capo > 0) {
368 2 : Measure* measure = score->firstMeasure();
369 2 : Segment* s = measure->getSegment(SegmentType::ChordRest, measure->tick());
370 2 : const size_t track = part * VOICES;
371 :
372 2 : CapoParams params;
373 2 : params.active = true;
374 2 : params.transposeMode = CapoParams::TransposeMode::TAB_ONLY;
375 2 : params.fretPosition = capo;
376 :
377 2 : Capo* capoEl = Factory::createCapo(score->dummy());
378 2 : capoEl->setTrack(track);
379 2 : capoEl->setParams(params);
380 2 : s->add(capoEl);
381 :
382 2 : Staff* staff = score->staff(part);
383 2 : staff->insertCapoParams({ 0, 1 }, params, true);
384 2 : }
385 : }
386 61 : }
387 :
388 : // string tuning contains higher values for lower sounding strings
389 : // for guitar:
390 : // E4 = 32 (MIDI 64)
391 : // B3 = 37
392 : // G3 = 41
393 : // D3 = 46
394 : // A2 = 51
395 : // E2 = 56 (MIDI 40)
396 : // thus to go from tuning value to MIDI pitch: pitch = 96 - tuning
397 : // todo: check interaction with fMiddleC
398 :
399 820 : static int stringTuningToMidi(int tuning)
400 : {
401 820 : return 96 - tuning;
402 : }
403 :
404 61 : void TablEdit::createContents(const MeasureHandler& measureHandler)
405 : {
406 61 : if (tefInstruments.size() == 0) {
407 0 : LOGD("error: no instruments");
408 0 : return;
409 : }
410 :
411 61 : std::vector<VoiceAllocator> voiceAllocators;
412 61 : initializeVoiceAllocators(voiceAllocators);
413 61 : allocateVoices(voiceAllocators);
414 :
415 134 : for (size_t part = 0; part < tefInstruments.size(); ++part) {
416 : LOGN("part %zu", part);
417 73 : std::vector<mu::engraving::Note*> tiedNotes;
418 365 : for (voice_idx_t voice = 0; voice < mu::engraving::VOICES; ++voice) {
419 : LOGN("- voice %zu", voice);
420 292 : auto& voiceContent { voiceAllocators.at(part).voiceContent(voice) };
421 292 : TupletHandler tupletHandler;
422 674 : for (size_t k = 0; k < voiceContent.size(); ++k) {
423 : LOGN(" - chord %zu", k);
424 : // tefNotes is either a rest or a chord of one or more notes
425 382 : const std::vector<const TefNote*>& tefNotes { voiceContent.at(k) };
426 :
427 382 : if (tefNotes.size() == 0) {
428 0 : continue; // shouldn't happen
429 : }
430 :
431 382 : const TefNote* const firstNote { tefNotes.at(0) };
432 382 : Fraction length { firstNote->length, 64 }; // length is in 64th
433 382 : if (firstNote->dots == 1) {
434 19 : length *= Fraction{ 3, 2 };
435 363 : } else if (firstNote->dots == 2) {
436 0 : length *= Fraction{ 7, 4 };
437 : }
438 :
439 382 : TDuration tDuration(length);
440 382 : if (firstNote->dots) {
441 19 : tDuration.setDots(firstNote->dots);
442 : }
443 :
444 382 : const auto idx { measureHandler.measureIndex(firstNote->position, tefMeasures) };
445 382 : const Fraction gapCorrection { measureHandler.sumPreviousGaps(idx), 64 };
446 382 : const auto positionCorrection = tupletHandler.doTuplet(firstNote);
447 :
448 382 : Fraction tick { firstNote->position, 64 }; // position is in 64th
449 382 : tick += positionCorrection;
450 382 : tick -= gapCorrection;
451 : LOGN(" positionCorrection %d/%d tick %d/%d length %d/%d",
452 : positionCorrection.numerator(), positionCorrection.denominator(),
453 : tick.numerator(), tick.denominator(),
454 : length.numerator(), length.denominator()
455 : );
456 :
457 382 : Measure* measure { score->tick2measure(tick) };
458 382 : if (!measure) {
459 0 : LOGD("error: no measure");
460 0 : continue;
461 : } else {
462 : LOGN("measure %p", measure);
463 : }
464 382 : Segment* segment { measure->getSegment(mu::engraving::SegmentType::ChordRest, tick) };
465 382 : if (!segment) {
466 0 : LOGD("error: no segment");
467 0 : continue;
468 : }
469 :
470 382 : const auto track = part * VOICES + voice;
471 382 : if (segment->element(track)) {
472 0 : LOGD("segment not empty");
473 0 : continue;
474 : }
475 :
476 382 : if (firstNote->rest) {
477 : LOGN(" - rest position %d string %d fret %d", firstNote->position, firstNote->string, firstNote->fret);
478 20 : addRest(segment, track, tDuration, length, toColor(voice));
479 : } else {
480 : LOGN(" - note(s) position %d string %d fret %d", firstNote->position, firstNote->string, firstNote->fret);
481 362 : mu::engraving::Chord* chord { Factory::createChord(segment) };
482 362 : if (chord) {
483 362 : chord->setTrack(track);
484 362 : chord->setDurationType(tDuration);
485 362 : chord->setTicks(length);
486 362 : segment->add(chord);
487 :
488 362 : const TefInstrument& instrument { tefInstruments.at(part) };
489 362 : if (instrument.stringNumber < 1 || 12 < instrument.stringNumber) {
490 0 : LOGD("error: invalid instrument.stringNumber %d", instrument.stringNumber);
491 0 : continue;
492 : }
493 :
494 763 : for (const auto note : tefNotes) {
495 401 : const auto stringOffset = stringNumberPreviousParts(part);
496 401 : const auto string { note->string - stringOffset - 1 }; // instrument-relative zero-based string number
497 401 : int unFrettedPitch = stringTuningToMidi(instrument.tuning.at(string)); // + note->fret;
498 401 : const int capo { instrument.nCapo };
499 401 : if (capo > 0) {
500 23 : unFrettedPitch += capo;
501 : }
502 401 : const int frettedPitch { unFrettedPitch + note->fret };
503 : LOGN(" -> string %d fret %d pitch %d", note->string, note->fret, frettedPitch);
504 : // note TableEdit's strings start at 1, MuseScore's at 0
505 401 : mu::engraving::Note* mn { addNoteToChord(chord, note, stringOffset, frettedPitch, toColor(voice), tiedNotes) };
506 401 : if (note->hasGrace) {
507 13 : int gracePitch = unFrettedPitch + note->graceFret;
508 13 : addGraceNotesToChord(chord, gracePitch, note->graceFret, string, toColor(voice));
509 : }
510 401 : if (mn && (note->effect() != EffectType::NONE || note->combinationEffect() != EffectType::NONE)) {
511 50 : effectMap.insert({ note, mn });
512 : }
513 : }
514 362 : tupletHandler.addCr(measure, chord);
515 : }
516 : }
517 : }
518 : }
519 78 : for (const auto note : tiedNotes) {
520 5 : connectTie(note->chord(), note);
521 : }
522 73 : }
523 61 : }
524 :
525 : // adapted copy of GPConverter::addContinuousSlideHammerOn()
526 :
527 61 : static void addContinuousSlideHammerOn(Score* _score, const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
528 : {
529 6 : auto searchEndNote = [] (Note* start) -> Note* {
530 : ChordRest* nextCr;
531 6 : if (toChord(start->parent())->ChordRest::isGrace()) {
532 : //! this case when start note is a grace note so end note can be next note in grace notes
533 : //! or parent note of grace notes
534 0 : Chord* startChord = toChord(start->parent());
535 :
536 0 : Chord* parentGrace = toChord(start->parent()->parent());
537 :
538 0 : auto it = parentGrace->graceNotes().begin();
539 0 : for (; it != parentGrace->graceNotes().end(); ++it) {
540 0 : if (*it == startChord) {
541 0 : break;
542 : }
543 : }
544 :
545 0 : if (it == parentGrace->graceNotes().end()) {
546 0 : nextCr = nullptr;
547 0 : } else if (std::next(it) == parentGrace->graceNotes().end()) {
548 0 : nextCr = parentGrace;
549 : } else {
550 0 : nextCr = *(++it);
551 : }
552 : } else {
553 6 : Segment* nextSegment = start->chord()->segment()->next1();
554 6 : if (!nextSegment) {
555 0 : return nullptr;
556 : }
557 6 : nextCr = nextSegment->nextChordRest(start->track());
558 6 : if (!nextCr) {
559 0 : return nullptr;
560 : }
561 :
562 6 : if (nextCr->isChord() && !toChord(nextCr)->graceNotes().empty()) {
563 0 : nextCr = toChord(nextCr)->graceNotes().front();
564 : }
565 : }
566 :
567 6 : if (!nextCr) {
568 0 : return nullptr;
569 : }
570 :
571 6 : if (!nextCr->isChord()) {
572 0 : return nullptr;
573 : }
574 6 : auto nextChord = toChord(nextCr);
575 6 : for (auto note : nextChord->notes()) {
576 6 : if (note->string() == start->string() && (note->harmonic() == start->harmonic())) {
577 6 : return note;
578 : }
579 : }
580 :
581 0 : return nextChord->upNote();
582 : };
583 :
584 61 : std::unordered_map<Note*, HammerOnPullOff*> hammerOnPullOffs;
585 61 : std::unordered_set<Chord*> hammerOnInChord;
586 111 : for (const auto& slide : effectMap) {
587 50 : const TefNote* const tefNote { slide.first };
588 50 : Note* startNote = slide.second;
589 : LOGN("has effect: tefNote %p simple %d complex %d startNote %p tick %s track %zu",
590 : slide.first, tefNote->simpleEffect, tefNote->complexEffect,
591 : startNote, qPrintable(startNote->tick().toString().toQString()), startNote->track());
592 98 : if (!(tefNote->effect() == EffectType::HAMMER_ON
593 48 : || tefNote->effect() == EffectType::PULL_OFF
594 47 : || tefNote->combinationEffect() == EffectType::HAMMER_ON
595 46 : || tefNote->combinationEffect() == EffectType::PULL_OFF
596 45 : || tefNote->effect() == EffectType::SLIDE)) {
597 44 : continue;
598 : }
599 :
600 6 : Note* endNote = searchEndNote(startNote);
601 6 : if (!endNote || startNote->string() == -1 || startNote->fret() == endNote->fret()) {
602 : //mistake in GP: such kind od slides shouldn't exist
603 0 : continue;
604 : }
605 :
606 6 : Fraction startTick = startNote->chord()->tick();
607 6 : Fraction endTick = endNote->chord()->tick();
608 6 : track_idx_t track = startNote->track();
609 :
610 : /// Layout info
611 6 : if (tefNote->effect() == EffectType::SLIDE) {
612 1 : Glissando* gl = mu::engraving::Factory::createGlissando(startNote);
613 1 : gl->setStartElement(startNote);
614 1 : gl->setTrack(track);
615 1 : gl->setTick(startTick);
616 1 : gl->setTick2(endNote->chord()->tick());
617 1 : gl->setEndElement(endNote);
618 1 : gl->setText(u"Sl");
619 1 : gl->setGlissandoType(GlissandoType::STRAIGHT);
620 1 : gl->setGlissandoStyle(startNote->part()->instrument(startTick)->glissandoStyle());
621 1 : _score->addElement(gl);
622 : }
623 :
624 10 : if (tefNote->effect() == EffectType::HAMMER_ON || tefNote->effect() == EffectType::PULL_OFF
625 10 : || tefNote->combinationEffect() == EffectType::HAMMER_ON || tefNote->combinationEffect() == EffectType::PULL_OFF) {
626 5 : Chord* startChord = startNote->chord();
627 5 : if (hammerOnInChord.find(startChord) != hammerOnInChord.end()) {
628 0 : continue;
629 : }
630 :
631 5 : if (hammerOnPullOffs.count(startNote) == 0) {
632 5 : HammerOnPullOff* hammerOnPullOff = Factory::createHammerOnPullOff(_score->dummy());
633 5 : hammerOnPullOff->setTrack(startNote->track());
634 5 : hammerOnPullOff->setTick(startNote->tick());
635 5 : hammerOnPullOff->setTick2(endNote->tick());
636 5 : hammerOnPullOff->setStartElement(startChord);
637 5 : hammerOnPullOff->setEndElement(endNote->chord());
638 5 : _score->addElement(hammerOnPullOff);
639 5 : hammerOnPullOffs[endNote] = hammerOnPullOff;
640 5 : hammerOnInChord.insert(startChord);
641 : } else {
642 0 : HammerOnPullOff* hammerOnPullOff = hammerOnPullOffs[startNote];
643 0 : hammerOnPullOff->setTick2(endTick);
644 0 : hammerOnPullOff->setEndElement(endNote->chord());
645 0 : hammerOnPullOffs.erase(startNote);
646 0 : hammerOnPullOffs[endNote] = hammerOnPullOff;
647 : }
648 : }
649 : }
650 61 : }
651 :
652 61 : static void addHarmonics(const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
653 : {
654 11 : const auto addHarmonicText = [](Note* msNote, const char* text) {
655 11 : msNote->setHeadGroup(NoteHeadGroup::HEAD_DIAMOND);
656 11 : Segment* segment = msNote->chord()->segment();
657 11 : StaffText* staffText = Factory::createStaffText(segment);
658 11 : staffText->setPlainText(String { text });
659 11 : staffText->setTrack(msNote->chord()->track());
660 11 : segment->add(staffText);
661 11 : };
662 :
663 111 : for (const auto& slide : effectMap) {
664 50 : const TefNote* const tefNote { slide.first };
665 :
666 50 : Note* msNote = slide.second;
667 50 : if (tefNote->effect() == EffectType::NATURAL_HARMONIC
668 50 : || tefNote->combinationEffect() == EffectType::NATURAL_HARMONIC) {
669 9 : addHarmonicText(msNote, "N.H.");
670 : }
671 50 : if (tefNote->effect() == EffectType::ARTIFICIAL_HARMONIC
672 50 : || tefNote->combinationEffect() == EffectType::ARTIFICIAL_HARMONIC) {
673 2 : addHarmonicText(msNote, "A.H.");
674 : }
675 : }
676 61 : }
677 :
678 : // return all chords in the same staff as a given note
679 : // ordered from lowest to highest track
680 :
681 40 : static std::vector<Chord*> allChordsInStaff(const Note* const msNote)
682 : {
683 40 : std::vector<Chord*> allChords;
684 40 : track_idx_t staffIdx { msNote->track() / VOICES };
685 40 : track_idx_t firstTrack { staffIdx* VOICES };
686 40 : Segment* segment { msNote->chord()->segment() };
687 200 : for (auto track = firstTrack; track < firstTrack + VOICES; ++track) {
688 160 : EngravingItem* element { segment->element(track) };
689 160 : if (element && element->isChord()) {
690 74 : Chord* chord { toChord(element) };
691 74 : allChords.push_back(chord);
692 : }
693 : }
694 40 : return allChords;
695 0 : }
696 :
697 : // arpeggios are down when they start at the note at the highest string
698 : // (which has the lowest string number)
699 :
700 20 : static bool isDown(const Note* const msNote)
701 : {
702 20 : Note* noteOnHighestString { nullptr };
703 57 : for (const Chord* const chord : allChordsInStaff(msNote)) {
704 96 : for (Note* note : chord->notes()) {
705 59 : if (noteOnHighestString) {
706 39 : if (note->string() < noteOnHighestString->string()) {
707 22 : noteOnHighestString = note;
708 : }
709 : } else {
710 20 : noteOnHighestString = note;
711 : }
712 : }
713 20 : }
714 20 : return msNote == noteOnHighestString;
715 : }
716 :
717 20 : static void addArpeggio(const Note* const msNote)
718 : {
719 20 : std::vector<Chord*> allChords { allChordsInStaff(msNote) };
720 20 : if (allChords.empty()) {
721 0 : return;
722 : }
723 20 : Chord* firstChord { allChords.at(0) };
724 20 : if (firstChord->arpeggio()) {
725 0 : return;
726 : }
727 :
728 20 : Arpeggio* a = Factory::createArpeggio(firstChord);
729 20 : track_idx_t firstChordIdx { firstChord->track() };
730 20 : track_idx_t lastChordIdx { allChords.at(allChords.size() - 1)->track() };
731 20 : int span { static_cast<int>(lastChordIdx - firstChordIdx + 1) };
732 20 : a->setSpan(span);
733 20 : a->setArpeggioType(isDown(msNote) ? ArpeggioType::DOWN : ArpeggioType::NORMAL);
734 20 : firstChord->add(a);
735 20 : }
736 :
737 61 : static void addSingleNoteEffects(Score* score, const std::map<const TefNote* const, mu::engraving::Note*>& effectMap)
738 : {
739 111 : for (const auto& effect : effectMap) {
740 50 : const TefNote* const tefNote { effect.first };
741 50 : Note* msNote { effect.second };
742 50 : const EffectType primary { tefNote->effect() };
743 50 : const EffectType combination { tefNote->combinationEffect() };
744 : LOGN("note %p tick %d track %zu effect %d combination %d",
745 : msNote, msNote->tick().ticks(), msNote->track(), primary, combination);
746 50 : if (primary == EffectType::BRUSH
747 48 : || combination == EffectType::BRUSH
748 47 : || primary == EffectType::RASGUEADO
749 45 : || combination == EffectType::ROLL
750 42 : || primary == EffectType::ROLL_ARPEGGIO) {
751 20 : addArpeggio(msNote);
752 : }
753 50 : if (primary == EffectType::MUTED
754 49 : || primary == EffectType::DEAD_NOTE
755 48 : || combination == EffectType::DEAD_NOTE) {
756 : // do not distinguish between muted and dead note
757 : // current implementation results in "X" in both staves
758 2 : msNote->setHeadGroup(NoteHeadGroup::HEAD_CROSS);
759 2 : msNote->setDeadNote(true);
760 : }
761 50 : if (primary == EffectType::RINGING_NOTE
762 46 : || combination == EffectType::RINGING_NOTE) {
763 5 : LetRing* lr = Factory::createLetRing(score->dummy());
764 5 : lr->setTrack(msNote->track());
765 5 : lr->setTick(msNote->tick());
766 5 : lr->setTick2(msNote->tick() + 2 * msNote->chord()->ticks());
767 5 : score->addSpanner(lr);
768 50 : } else if (primary == EffectType::STACCATO) {
769 8 : Chord* chord { toChord(msNote->parent()) };
770 8 : Articulation* na = Factory::createArticulation(chord);
771 8 : const SymId articSym { SymId::articStaccatoAbove };
772 8 : na->setSymId(articSym);
773 8 : if (!chord->hasArticulation(na)) {
774 7 : chord->add(na);
775 : } else {
776 1 : delete na;
777 : }
778 : }
779 : }
780 61 : }
781 :
782 61 : void TablEdit::createEffects()
783 : {
784 61 : addSingleNoteEffects(score, effectMap);
785 61 : addContinuousSlideHammerOn(score, effectMap);
786 61 : addHarmonics(effectMap);
787 61 : }
788 :
789 61 : void TablEdit::createLinkedTabs()
790 : {
791 61 : constexpr size_t stavesInPart = 2;
792 :
793 134 : for (Part* part : score->parts()) {
794 73 : part->setStaves(static_cast<int>(stavesInPart));
795 :
796 73 : Staff* srcStaff = part->staff(0);
797 73 : Staff* dstStaff = part->staff(1);
798 73 : Excerpt::cloneStaff(srcStaff, dstStaff, true);
799 :
800 : static const std::vector<StaffTypes> types {
801 : StaffTypes::TAB_4SIMPLE,
802 : StaffTypes::TAB_5SIMPLE,
803 : StaffTypes::TAB_6SIMPLE,
804 : StaffTypes::TAB_7SIMPLE,
805 : StaffTypes::TAB_8SIMPLE,
806 : StaffTypes::TAB_9SIMPLE,
807 : StaffTypes::TAB_10SIMPLE,
808 75 : };
809 :
810 73 : Fraction fr = Fraction(0, 1);
811 73 : size_t lines = part->instrument()->stringData()->strings();
812 73 : size_t index = (lines >= 4 && lines <= 10) ? lines - 4 : 2;
813 :
814 73 : dstStaff->setStaffType(fr, *StaffType::preset(types.at(index)));
815 73 : dstStaff->setLines(fr, static_cast<int>(lines));
816 : }
817 61 : }
818 :
819 141 : static Fraction reducedActualLength(const int actual, const int nominalDenominator)
820 : {
821 141 : Fraction res { actual, 64 };
822 705 : while (res.denominator() >= 2 * nominalDenominator && res.numerator() % 2 == 0) {
823 564 : res.setNumerator(res.numerator() / 2);
824 564 : res.setDenominator(res.denominator() / 2);
825 : }
826 : LOGN("actual %d nominalDenominator %d res %d/%d", actual, nominalDenominator, res.numerator(), res.denominator());
827 141 : return res;
828 : }
829 :
830 61 : void TablEdit::createMeasures(const MeasureHandler& measureHandler)
831 : {
832 61 : int lastKey { 0 }; // safe default
833 61 : Fraction lastTimeSig { -1, -1 }; // impossible value
834 61 : Fraction tick { 0, 1 };
835 202 : for (size_t idx = 0; idx < tefMeasures.size(); ++idx) {
836 141 : TefMeasure& tefMeasure { tefMeasures.at(idx) };
837 : // create measure
838 141 : auto measure = Factory::createMeasure(score);
839 141 : measure->setTick(tick);
840 141 : Fraction nominalLength{ tefMeasure.numerator, tefMeasure.denominator };
841 141 : Fraction actualLength{ reducedActualLength(measureHandler.actualSize(tefMeasures, idx), tefMeasure.denominator) };
842 141 : measure->setTimesig(nominalLength);
843 141 : measure->setTicks(actualLength);
844 141 : measure->setEndBarLineType(BarLineType::NORMAL, 0);
845 : LOGN("measure %p tick %d/%d nominalLength %d/%d actualLength %d/%d",
846 : measure,
847 : tick.numerator(), tick.denominator(),
848 : nominalLength.numerator(), nominalLength.denominator(),
849 : actualLength.numerator(), actualLength.denominator()
850 : );
851 141 : score->measures()->add(measure);
852 :
853 141 : if (tick == Fraction { 0, 1 }) {
854 61 : auto s1 = measure->getSegment(mu::engraving::SegmentType::KeySig, tick);
855 134 : for (size_t i = 0; i < tefInstruments.size(); ++i) {
856 73 : mu::engraving::KeySig* keysig = Factory::createKeySig(s1);
857 73 : keysig->setKey(Key(tefMeasure.key));
858 73 : keysig->setTrack(i * VOICES);
859 73 : s1->add(keysig);
860 : }
861 61 : lastKey = tefMeasure.key;
862 :
863 61 : auto s2 = measure->getSegment(mu::engraving::SegmentType::TimeSig, tick);
864 134 : for (size_t i = 0; i < tefInstruments.size(); ++i) {
865 73 : mu::engraving::TimeSig* timesig = Factory::createTimeSig(s2);
866 73 : timesig->setSig(nominalLength);
867 73 : timesig->setTrack(i * VOICES);
868 73 : s2->add(timesig);
869 : }
870 61 : lastTimeSig = nominalLength;
871 61 : createTempo();
872 : } else {
873 80 : if (tefMeasure.key != lastKey) {
874 6 : auto s1 = measure->getSegment(mu::engraving::SegmentType::KeySig, tick);
875 15 : for (size_t i = 0; i < tefInstruments.size(); ++i) {
876 9 : mu::engraving::KeySig* keysig = Factory::createKeySig(s1);
877 9 : keysig->setKey(Key(tefMeasure.key));
878 9 : keysig->setTrack(i * VOICES);
879 9 : s1->add(keysig);
880 : }
881 6 : lastKey = tefMeasure.key;
882 : }
883 80 : if (nominalLength != lastTimeSig) {
884 3 : auto s2 = measure->getSegment(mu::engraving::SegmentType::TimeSig, tick);
885 7 : for (size_t i = 0; i < tefInstruments.size(); ++i) {
886 4 : mu::engraving::TimeSig* timesig = Factory::createTimeSig(s2);
887 4 : timesig->setSig(nominalLength);
888 4 : timesig->setTrack(i * VOICES);
889 4 : s2->add(timesig);
890 : }
891 3 : lastTimeSig = nominalLength;
892 : }
893 : }
894 :
895 141 : tick += actualLength;
896 : }
897 61 : score->updateTicksAndTimeSigMap();
898 61 : }
899 :
900 61 : void TablEdit::createNotesFrame()
901 : {
902 61 : if (!tefHeader.notes.empty()) {
903 1 : TBox* tbox = Factory::createTBox(score);
904 1 : tbox->setTick(score->endTick());
905 1 : score->measures()->add(tbox);
906 1 : tbox->text()->setPlainText(muse::String::fromStdString(tefHeader.notes));
907 : }
908 61 : }
909 :
910 61 : void TablEdit::createParts()
911 : {
912 134 : for (const auto& instrument : tefInstruments) {
913 73 : Part* part = new Part(score);
914 73 : score->appendPart(part);
915 73 : muse::String staffName { muse::String::fromStdString(instrument.name) };
916 73 : part->setPlainLongName(staffName);
917 :
918 73 : StringData stringData;
919 73 : stringData.setFrets(25); // reasonable default (?)
920 492 : for (int i = 0; i < instrument.stringNumber; ++i) {
921 419 : int pitch = stringTuningToMidi(instrument.tuning.at(instrument.stringNumber - i - 1));
922 : LOGN("pitch %d", pitch);
923 419 : instrString str { pitch };
924 419 : stringData.stringList().push_back(str);
925 : }
926 73 : part->instrument()->setStringData(stringData);
927 :
928 73 : part->setMidiProgram(instrument.midiVoice);
929 73 : part->setMidiChannel(instrument.midiBank);
930 :
931 73 : Staff* staff = Factory::createStaff(part);
932 73 : ClefTypeList clefTypeList { ClefType::G8_VB, ClefType::G8_VB };
933 73 : staff->setDefaultClefType(clefTypeList);
934 :
935 73 : score->appendStaff(staff);
936 73 : }
937 61 : }
938 :
939 61 : void TablEdit::createProperties()
940 : {
941 61 : if (!tefHeader.title.empty()) {
942 24 : score->setMetaTag(u"workTitle", muse::String::fromStdString(tefHeader.title));
943 : }
944 61 : if (!tefHeader.subTitle.empty()) {
945 2 : score->setMetaTag(u"subtitle", muse::String::fromStdString(tefHeader.subTitle));
946 : }
947 61 : if (!tefHeader.comment.empty()) {
948 54 : score->setMetaTag(u"comment", muse::String::fromStdString(tefHeader.comment));
949 : }
950 61 : if (!tefHeader.internetLink.empty()) {
951 1 : score->setMetaTag(u"source", muse::String::fromStdString(tefHeader.internetLink));
952 : }
953 61 : if (!tefHeader.copyright.empty()) {
954 1 : score->setMetaTag(u"copyright", muse::String::fromStdString(tefHeader.copyright));
955 : }
956 61 : }
957 :
958 : // convert the reading list into repeat and (todo) codas, segnos and voltas
959 :
960 61 : void TablEdit::createRepeats()
961 : {
962 : LOGN("reading list size %zu number of measures %zu", tefReadingList.size(), tefMeasures.size());
963 61 : ReadingList readingList;
964 61 : readingList.calculate(tefMeasures.size(), tefReadingList);
965 202 : for (size_t i = 0; i < tefMeasures.size(); ++i) {
966 141 : Measure* m { score->crMeasure(static_cast<int>(i)) };
967 141 : m->setRepeatStart(readingList.status().at(i).repeatStart);
968 141 : m->setRepeatEnd(readingList.status().at(i).repeatEnd);
969 141 : const int repeatCount { readingList.status().at(i).repeatCount };
970 141 : if (repeatCount > 2) {
971 2 : m->setRepeatCount(repeatCount);
972 : // generate default play count text
973 2 : Segment* segment = m->getSegment(SegmentType::EndBarLine, m->tick() + m->ticks());
974 2 : PlayCountText* playCountText = Factory::createPlayCountText(segment);
975 2 : playCountText->setTrack(0); // todo check multi staff / part handling
976 2 : segment->add(playCountText);
977 : }
978 141 : if (const std::optional<Ending>& ending = readingList.status().at(i).ending) {
979 4 : addVolta(score, m, ending.value());
980 : }
981 : }
982 61 : if (readingList.status().back().barlineEnd) {
983 7 : Measure* m { score->crMeasure(static_cast<int>(tefMeasures.size()) - 1) };
984 7 : m->setEndBarLineType(BarLineType::END, 0);
985 : }
986 61 : }
987 :
988 61 : static void setInstrumentIDs(const std::vector<Part*>& parts)
989 : {
990 134 : for (Part* part : parts) {
991 146 : for (const auto& pair : part->instruments()) {
992 73 : pair.second->updateInstrumentId();
993 : }
994 : }
995 61 : }
996 :
997 : //---------------------------------------------------------
998 : // fillGap
999 : //---------------------------------------------------------
1000 :
1001 : // Fill one gap (tstart - tend) in this track in this measure with rest(s).
1002 :
1003 9 : static void fillGap(Measure* measure, track_idx_t track, const Fraction& tstart, const Fraction& tend)
1004 : {
1005 9 : Fraction ctick = tstart;
1006 9 : Fraction restLen = tend - tstart;
1007 : LOGN("measure %p track %zu tstart %d tend %d restLen %d len",
1008 : measure, track, tstart.ticks(), tend.ticks(), restLen.ticks());
1009 9 : auto durList = toDurationList(restLen, true);
1010 : LOGN("durList.size %zu", durList.size());
1011 18 : for (const auto& dur : durList) {
1012 : LOGN("type %d dots %d fraction %d/%d", static_cast<int>(dur.type()), dur.dots(), dur.fraction().numerator(),
1013 : dur.fraction().denominator());
1014 9 : Segment* s = measure->getSegment(SegmentType::ChordRest, ctick);
1015 9 : addRest(s, track, dur, dur.fraction(), muse::draw::Color::BLACK, false);
1016 9 : ctick += dur.fraction();
1017 : }
1018 9 : }
1019 :
1020 : //---------------------------------------------------------
1021 : // fillGapsInFirstVoices
1022 : //---------------------------------------------------------
1023 :
1024 : // Fill gaps in first voice of every staff in this measure for this part with rest(s).
1025 :
1026 61 : static void fillGapsInFirstVoices(MasterScore* score)
1027 : {
1028 61 : IF_ASSERT_FAILED(score) {
1029 0 : return;
1030 : }
1031 :
1032 134 : for (staff_idx_t idx = 0; idx < score->nstaves(); ++idx) {
1033 237 : for (Measure* measure = score->firstMeasure(); measure; measure = measure->nextMeasure()) {
1034 164 : Fraction measTick = measure->tick();
1035 164 : Fraction measLen = measure->ticks();
1036 164 : Fraction nextMeasTick = measTick + measLen;
1037 : LOGN("measure %p idx %zu tick %d - %d (len %d)",
1038 : measure, idx, measTick.ticks(), nextMeasTick.ticks(), measLen.ticks());
1039 164 : track_idx_t track = idx * VOICES;
1040 164 : Fraction endOfLastCR = measTick;
1041 834 : for (Segment* s = measure->first(); s; s = s->next()) {
1042 670 : EngravingItem* el = s->element(track);
1043 670 : if (el) {
1044 620 : if (s->isChordRestType()) {
1045 320 : ChordRest* cr = toChordRest(el);
1046 320 : Fraction crTick = cr->tick();
1047 320 : Fraction crLen = cr->globalTicks();
1048 320 : if (crTick > endOfLastCR) {
1049 6 : fillGap(measure, track, endOfLastCR, crTick);
1050 : }
1051 320 : endOfLastCR = crTick + crLen;
1052 : }
1053 : }
1054 : }
1055 164 : if (nextMeasTick > endOfLastCR) {
1056 3 : fillGap(measure, track, endOfLastCR, nextMeasTick);
1057 : }
1058 : }
1059 : }
1060 : }
1061 :
1062 61 : void TablEdit::createScore()
1063 : {
1064 61 : MeasureHandler measureHandler;
1065 61 : measureHandler.calculate(tefContents, tefMeasures);
1066 61 : createProperties();
1067 61 : createParts();
1068 61 : createTitleFrame();
1069 61 : createMeasures(measureHandler);
1070 61 : createCapos();
1071 61 : createNotesFrame();
1072 61 : createContents(measureHandler);
1073 61 : createEffects();
1074 61 : fillGapsInFirstVoices(score);
1075 61 : createRepeats();
1076 61 : createTexts();
1077 61 : createLinkedTabs();
1078 61 : setInstrumentIDs(score->parts());
1079 61 : }
1080 :
1081 61 : void TablEdit::createTempo()
1082 : {
1083 61 : mu::engraving::Measure* measure = score->firstMeasure();
1084 61 : mu::engraving::Segment* segment = measure->getSegment(mu::engraving::SegmentType::ChordRest, mu::engraving::Fraction(0, 1));
1085 61 : mu::engraving::TempoText* tt = new mu::engraving::TempoText(segment);
1086 61 : tt->setTempo(double(tefHeader.tempo) / 60.0);
1087 61 : tt->setTrack(0);
1088 61 : tt->setFollowText(true);
1089 61 : muse::String tempoText = mu::engraving::TempoText::duration2tempoTextString(mu::engraving::DurationType::V_QUARTER);
1090 61 : tempoText += u" = ";
1091 61 : tempoText += muse::String::number(tefHeader.tempo);
1092 61 : tt->setXmlText(tempoText);
1093 61 : segment->add(tt);
1094 61 : }
1095 :
1096 61 : void TablEdit::createTexts()
1097 : {
1098 111 : for (const auto& textMarker : tefTextMarkers) {
1099 : LOGN("position %d string %d text marker %d", textMarker.position, textMarker.string, textMarker.index);
1100 50 : bool ok { true };
1101 50 : const auto part = partIdx(textMarker.string, ok);
1102 50 : if (!ok) {
1103 0 : LOGD("error: invalid string %d", textMarker.string);
1104 1 : continue;
1105 : }
1106 50 : const auto track = part * VOICES;
1107 : LOGN("part %zu track %zu", part, track);
1108 :
1109 50 : Fraction tick { textMarker.position, 64 }; // position is in 64th
1110 50 : Measure* measure { score->tick2measure(tick) };
1111 50 : if (!measure) {
1112 0 : LOGD("error: no measure");
1113 0 : continue;
1114 : } else {
1115 : LOGN("measure %p", measure);
1116 : }
1117 50 : Segment* segment { measure->getSegment(mu::engraving::SegmentType::ChordRest, tick) };
1118 50 : if (!segment) {
1119 0 : LOGD("error: no segment");
1120 0 : continue;
1121 : }
1122 50 : if (textMarker.index < 0 || textMarker.index >= static_cast<int>(tefTexts.size())) {
1123 1 : LOGE() << "text marker index invalid";
1124 1 : continue;
1125 : }
1126 49 : StaffText* staffText = Factory::createStaffText(segment);
1127 49 : muse::String text { tefTexts.at(textMarker.index).c_str() };
1128 49 : staffText->setPlainText(text);
1129 49 : staffText->setTrack(track);
1130 49 : segment->add(staffText);
1131 49 : }
1132 61 : }
1133 :
1134 61 : void TablEdit::createTitleFrame()
1135 : {
1136 61 : VBox* vbox = Factory::createTitleVBox(score);
1137 61 : vbox->setTick(mu::engraving::Fraction(0, 1));
1138 61 : score->measures()->add(vbox);
1139 61 : if (!tefHeader.title.empty()) {
1140 24 : Text* s = Factory::createText(vbox, TextStyleType::TITLE);
1141 24 : s->setPlainText(muse::String::fromStdString(tefHeader.title));
1142 24 : vbox->add(s);
1143 : }
1144 61 : if (!tefHeader.subTitle.empty()) {
1145 2 : Text* s = Factory::createText(vbox, TextStyleType::SUBTITLE);
1146 2 : s->setPlainText(muse::String::fromStdString(tefHeader.subTitle));
1147 2 : vbox->add(s);
1148 : }
1149 61 : }
1150 :
1151 : /*
1152 : * encoding of note durations
1153 : * 0 whole
1154 : * 1 half dotted
1155 : * 2 whole triplet
1156 : * 3 half
1157 : * 4 quarter dotted
1158 : * 5 half triplet
1159 : * 6 quarter
1160 : * 7 eight dotted
1161 : * 8 quarter triplet
1162 : * ...
1163 : * 18 1/64th note
1164 : * 19 half double dotted
1165 : * 22 quarter double dotted
1166 : * 25 eighth double dotted
1167 : * 28 16th double dotted
1168 : */
1169 :
1170 : // return TablEdit note length in 64th
1171 : // (including triplets rounded down to nearest note length)
1172 : // TODO: remove code duplication with voiceallocator.cpp durationToInt()
1173 :
1174 421 : static int duration2length(const int duration)
1175 : {
1176 421 : if (0 <= duration && duration <= 18) {
1177 : // remove dot and triplet
1178 421 : int noteType { 0 };
1179 421 : int dotOrTriplet { duration % 3 };
1180 421 : switch (dotOrTriplet) {
1181 392 : case 0: noteType = duration / 3;
1182 392 : break;
1183 19 : case 1: noteType = (duration + 2) / 3;
1184 19 : break;
1185 10 : case 2: noteType = (duration - 2) / 3;
1186 10 : break;
1187 0 : default: LOGD("impossible value %d", dotOrTriplet);
1188 : }
1189 421 : switch (noteType) {
1190 93 : case 0: return 64; // 1/1
1191 104 : case 1: return 32; // 1/2
1192 190 : case 2: return 16; // 1/4
1193 20 : case 3: return 8; // 1/8
1194 4 : case 4: return 4; // 1/16
1195 6 : case 5: return 2; // 1/32
1196 4 : case 6: return 1; // 1/64
1197 : // as long as the if statement above is correct, this cannot actually happen
1198 0 : default: LOGD("impossible value %d", dotOrTriplet);
1199 : }
1200 0 : } else {
1201 0 : switch (duration) {
1202 0 : case 19: return 32; // 1/2
1203 0 : case 22: return 16; // 1/4
1204 0 : case 25: return 8; // 1/8
1205 0 : case 28: return 4; // 1/16
1206 0 : default: LOGD("impossible value %d", duration);
1207 : }
1208 : }
1209 0 : LOGD("invalid note duration %d", duration);
1210 0 : return 0; // invalid (result is weird layout)
1211 : }
1212 :
1213 : // return the number of dots
1214 : // TODO: remove code duplication with voiceallocator.cpp durationToInt()
1215 :
1216 421 : static int duration2dots(const int duration)
1217 : {
1218 421 : if (0 <= duration && duration <= 18 && (duration % 3) == 0) {
1219 392 : return 0;
1220 29 : } else if (0 <= duration && duration <= 18 && (duration % 3) == 1) {
1221 19 : return 1;
1222 10 : } else if (duration == 19 || duration == 22 || duration == 25 || duration == 28) {
1223 0 : return 2;
1224 : }
1225 10 : LOGD("invalid note duration %d", duration);
1226 10 : return 0; // invalid
1227 : }
1228 :
1229 : // check if 3:2 tuplet
1230 :
1231 421 : static bool duration2triplet(const int duration)
1232 : {
1233 421 : if (0 <= duration && duration <= 18) {
1234 421 : return duration % 3 == 2;
1235 : } else {
1236 0 : LOGD("invalid note duration %d", duration);
1237 0 : return false; // invalid
1238 : }
1239 : }
1240 :
1241 421 : Voice extractVoice(const uint8_t byte3)
1242 : {
1243 421 : switch ((byte3 & 0x30) / 0x10) {
1244 388 : case 0: return Voice::DEFAULT;
1245 18 : case 2: return Voice::UPPER;
1246 15 : case 3: return Voice::LOWER;
1247 0 : default: LOGE("unknown voice");
1248 0 : return Voice::DEFAULT;
1249 : }
1250 : }
1251 :
1252 61 : void TablEdit::readTefContents()
1253 : {
1254 : // calculate the total number of strings
1255 : // instruments must have been read before reading contents
1256 61 : if (tefInstruments.empty()) {
1257 0 : LOGD("no instruments");
1258 0 : return;
1259 : }
1260 :
1261 61 : int totalNumberOfStrings { 0 };
1262 134 : for (const auto& instrument : tefInstruments) {
1263 73 : totalNumberOfStrings += instrument.stringNumber;
1264 : }
1265 : LOGN("totalNumberOfStrings %d", totalNumberOfStrings);
1266 61 : if (totalNumberOfStrings < 1) {
1267 0 : LOGE() << "totalNumberOfStrings invalid";
1268 0 : return;
1269 : }
1270 :
1271 61 : _file->seek(OFFSET_CONTENTS);
1272 61 : uint32_t position = readUInt32();
1273 61 : _file->seek(position);
1274 61 : uint32_t offset = readUInt32();
1275 : LOGN("position %d offset %d", position, offset);
1276 533 : while (offset != 0xFFFFFFFF) {
1277 472 : uint8_t byte1 = readUInt8();
1278 472 : uint8_t byte2 = readUInt8();
1279 472 : uint8_t byte3 = readUInt8();
1280 472 : uint8_t byte4 = readUInt8();
1281 472 : uint8_t byte5 = readUInt8();
1282 472 : /* uint8_t byte6 = */ readUInt8();
1283 472 : uint8_t byte7 = readUInt8();
1284 472 : /* uint8_t byte8 = */ readUInt8();
1285 472 : TefNote note;
1286 472 : note.position = (offset >> 3) / totalNumberOfStrings;
1287 472 : const auto noteRestMarker = byte1 & 0x3F;
1288 472 : if (noteRestMarker < 0x33) {
1289 401 : note.string = ((offset >> 3) % totalNumberOfStrings) + 1;
1290 401 : note.fret = noteRestMarker - 1;
1291 71 : } else if (noteRestMarker == 0x33) {
1292 20 : note.string = ((offset >> 3) % totalNumberOfStrings) + 1;
1293 20 : note.rest = true;
1294 : } else {
1295 : // not a note or rest
1296 : //LOGD("marker %d duration %d length %d dots %d", noteRestMarker, note.duration, note.length, note.dots);
1297 : }
1298 472 : if (noteRestMarker <= 0x33) {
1299 421 : note.duration = byte2 & 0x1F;
1300 421 : note.length = duration2length(note.duration);
1301 421 : note.dots = duration2dots(note.duration);
1302 421 : note.triplet = duration2triplet(note.duration);
1303 421 : note.voice = extractVoice(byte3);
1304 421 : note.tie = (byte2 / 0x20) == 7;
1305 421 : if (byte1 & 0x40) {
1306 13 : note.graceEffect = byte4 / 0x20;
1307 13 : note.graceFret = byte4 & 0x1F;
1308 13 : note.hasGrace = true;
1309 : //LOGD("graceEffect %d graceFret %d", note.graceEffect, note.graceFret);
1310 : }
1311 421 : note.fingeringLH = (byte7 & 0x1F) % 6;
1312 421 : note.fingeringRH = (byte7 & 0x1F) / 6;
1313 : LOGN("fingeringLH %d fingeringRH %d", note.fingeringLH, note.fingeringRH);
1314 421 : note.simpleEffect = byte3 & 0x0F;
1315 421 : note.complexEffect = byte5;
1316 : LOGN("simpleEffect %d complexEffect %d", note.simpleEffect, note.complexEffect);
1317 421 : tefContents.push_back(note);
1318 51 : } else if (noteRestMarker == 0x39) {
1319 50 : TefTextMarker tefTextMarker;
1320 50 : tefTextMarker.position = (offset >> 3) / totalNumberOfStrings;
1321 50 : tefTextMarker.string = ((offset >> 3) % totalNumberOfStrings) + 1;
1322 50 : tefTextMarker.index = static_cast<int>((byte3 << 8) + byte2);
1323 : LOGN("text marker %d", tefTextMarker.index);
1324 50 : tefTextMarkers.push_back(tefTextMarker);
1325 : } else if (noteRestMarker == 0x3D) {
1326 : LOGN("connection");
1327 : } else {
1328 : LOGN("not supported %d", noteRestMarker);
1329 : }
1330 472 : offset = readUInt32();
1331 : }
1332 : }
1333 :
1334 61 : void TablEdit::readTefInstruments()
1335 : {
1336 61 : _file->seek(OFFSET_INSTRUMENTS);
1337 61 : uint32_t position = readUInt32();
1338 61 : _file->seek(position);
1339 61 : uint16_t structSize = readUInt16();
1340 61 : uint16_t numberOfInstruments = readUInt16();
1341 : //uint32_t zero = readUInt16();
1342 : LOGN("structSize %d numberOfInstruments %d", structSize, numberOfInstruments);
1343 134 : for (uint16_t i = 0; i < numberOfInstruments; ++i) {
1344 73 : TefInstrument instrument;
1345 73 : instrument.stringNumber = readUInt16();
1346 73 : instrument.firstString = readUInt16();
1347 73 : instrument.available16U = readUInt16();
1348 73 : instrument.verticalSpacing = readUInt16();
1349 73 : instrument.midiVoice = readUInt8();
1350 73 : instrument.midiBank = readUInt8();
1351 73 : instrument.nBanjo5 = readUInt8();
1352 73 : instrument.uSpec = readUInt8();
1353 73 : instrument.nCapo = readUInt16();
1354 73 : instrument.fMiddleC = readUInt8();
1355 73 : instrument.fClef = readUInt8();
1356 73 : instrument.output = readUInt16();
1357 73 : instrument.options = readUInt16();
1358 949 : for (uint16_t j = 0; j < 12; ++j) {
1359 876 : int n = readUInt8();
1360 876 : n += instrument.nCapo;
1361 876 : instrument.tuning[j] = n;
1362 : }
1363 : // name is a zero-terminated utf8 string
1364 73 : bool atEnd = false;
1365 2701 : for (uint16_t j = 0; j < 36; ++j) {
1366 2628 : auto c = readUInt8();
1367 2628 : if (c == 0) {
1368 : // stop appending, but continue reading
1369 1040 : atEnd = true;
1370 : }
1371 2628 : if (0x20 <= c && c <= 0x7E && !atEnd) {
1372 988 : instrument.name += static_cast<char>(c);
1373 : }
1374 : }
1375 73 : tefInstruments.push_back(instrument);
1376 73 : }
1377 61 : }
1378 :
1379 61 : void TablEdit::readTefMeasures()
1380 : {
1381 61 : _file->seek(OFFSET_MEASURES);
1382 61 : uint32_t position = readUInt32();
1383 61 : _file->seek(position);
1384 61 : /* uint16_t structSize = */ readUInt16();
1385 61 : uint16_t numberOfMeasures = readUInt16();
1386 61 : /* uint32_t zero = */ readUInt32();
1387 203 : for (uint16_t i = 0; i < numberOfMeasures; ++i) {
1388 142 : TefMeasure measure;
1389 142 : measure.flag = readUInt8();
1390 142 : measure.isPickup = measure.flag & 0x08;
1391 142 : /* uint8_t uTmp = */ readUInt8();
1392 142 : measure.key = readInt8();
1393 142 : measure.size = readUInt8();
1394 142 : const int denom = readUInt8();
1395 142 : if (denom < 1) {
1396 1 : LOGE() << "Error reading measure - corrupt denominator";
1397 1 : readUInt8(); // numerator
1398 1 : readUInt16(); // margins
1399 1 : continue;
1400 : }
1401 141 : measure.denominator = denom;
1402 141 : measure.numerator = readUInt8();
1403 141 : /* uint16_t margins = */ readUInt16();
1404 141 : tefMeasures.push_back(measure);
1405 : }
1406 61 : }
1407 :
1408 61 : void TablEdit::readTefReadingList()
1409 : {
1410 61 : _file->seek(OFFSET_READINGLIST);
1411 61 : uint32_t position = readUInt32();
1412 61 : if (!position) {
1413 48 : return;
1414 : }
1415 :
1416 13 : _file->seek(position);
1417 13 : uint16_t structSize = readUInt16();
1418 13 : uint16_t numberOfItems = readUInt16();
1419 44 : for (uint16_t i = 0; i < numberOfItems; ++i) {
1420 31 : TefReadingListItem item;
1421 31 : item.firstMeasure = readUInt16();
1422 31 : item.lastMeasure = readUInt16();
1423 : // skip the label
1424 899 : for (uint16_t j = 4; j < structSize; ++j) {
1425 868 : readUInt8();
1426 : }
1427 31 : tefReadingList.push_back(item);
1428 31 : }
1429 : }
1430 :
1431 61 : void TablEdit::readTefTexts()
1432 : {
1433 61 : _file->seek(OFFSET_TEXTS);
1434 61 : uint32_t position = readUInt32();
1435 61 : if (!position) {
1436 50 : return;
1437 : }
1438 :
1439 11 : _file->seek(position);
1440 11 : uint16_t numberOfTexts = readUInt16();
1441 47 : for (uint16_t i = 0; i < numberOfTexts; ++i) {
1442 36 : std::string text { readUtf8Text() };
1443 : LOGN("i %d text '%s'", i, text.c_str());
1444 36 : tefTexts.push_back(text);
1445 36 : }
1446 : }
1447 :
1448 61 : void TablEdit::readTefHeader()
1449 : {
1450 61 : readUInt16(); // skip private01
1451 61 : tefHeader.version = readUInt16();
1452 61 : tefHeader.subVersion = readUInt16();
1453 61 : tefHeader.tempo = readUInt16();
1454 61 : tefHeader.chorus = readUInt16();
1455 61 : tefHeader.reverb = readUInt16();
1456 61 : readUInt16(); // todo syncope (is signed)
1457 61 : readUInt16(); // skip private02
1458 61 : tefHeader.securityCode = readUInt32();
1459 61 : tefHeader.securityFlags = readUInt32();
1460 61 : _file->seek(OFFSET_TBED);
1461 61 : tefHeader.tbed = readUInt32();
1462 61 : readUInt32(); // skip contents
1463 61 : auto titlePtr = readUInt32();
1464 : LOGN("titlePtr %d", titlePtr);
1465 61 : _file->seek(titlePtr);
1466 61 : tefHeader.title = readUtf8TextIndirect(OFFSET_TITLE);
1467 61 : tefHeader.subTitle = readUtf8TextIndirect(OFFSET_SUBTITLE);
1468 61 : tefHeader.comment = readUtf8TextIndirect(OFFSET_COMMENT);
1469 61 : tefHeader.notes = readUtf8TextIndirect(OFFSET_NOTES);
1470 61 : tefHeader.internetLink = readUtf8TextIndirect(OFFSET_INTERNETLINK);
1471 61 : tefHeader.copyright = readUtf8TextIndirect(OFFSET_COPYRIGHT);
1472 61 : _file->seek(OFFSET_OLDNUM);
1473 61 : tefHeader.wOldNum = readUInt16();
1474 61 : tefHeader.wFormat = readUInt16();
1475 61 : }
1476 :
1477 : //---------------------------------------------------------
1478 : // import
1479 : //
1480 : // see src/importexport/guitarpro/internal/importgtp.cpp createLinkedTabs()
1481 : // for linked staves
1482 : //---------------------------------------------------------
1483 :
1484 61 : Err TablEdit::import()
1485 : {
1486 61 : readTefHeader();
1487 : LOGN("version %d subversion %d", tefHeader.version, tefHeader.subVersion);
1488 : LOGN("tempo %d chorus %d reverb %d", tefHeader.tempo, tefHeader.chorus, tefHeader.reverb);
1489 : LOGN("securityCode %d securityFlags %d", tefHeader.securityCode, tefHeader.securityFlags);
1490 : LOGN("title '%s'", tefHeader.title.c_str());
1491 : LOGN("subTitle '%s'", tefHeader.subTitle.c_str());
1492 : LOGN("comment '%s'", tefHeader.comment.c_str());
1493 : LOGN("notes '%s'", tefHeader.notes.c_str());
1494 : LOGN("internetLink '%s'", tefHeader.internetLink.c_str());
1495 : LOGN("copyright '%s'", tefHeader.copyright.c_str());
1496 : LOGN("tbed %d wOldNum %d wFormat %d", tefHeader.tbed, tefHeader.wOldNum, tefHeader.wFormat);
1497 61 : if ((tefHeader.wFormat >> 8) < 10) {
1498 0 : return Err::FileBadFormat;
1499 : //return Err::FileTooOld; // TODO: message is too specific for MuseScore format
1500 : }
1501 61 : if ((tefHeader.wFormat >> 8) > 10) {
1502 0 : return Err::FileBadFormat;
1503 : //return Err::FileTooNew;
1504 : }
1505 61 : if (tefHeader.securityCode != 0) {
1506 0 : return Err::FileBadFormat; // todo "file is protected" message ?
1507 : }
1508 61 : readTefTexts();
1509 97 : for (const auto& text : tefTexts) {
1510 : LOGN("text: '%s'", text.c_str());
1511 : }
1512 61 : readTefMeasures();
1513 202 : for (const auto& measure : tefMeasures) {
1514 : LOGN("flag %d key %d size %d numerator %d denominator %d",
1515 : measure.flag, measure.key, measure.size, measure.numerator, measure.denominator);
1516 : }
1517 61 : readTefInstruments();
1518 134 : for (const auto& instrument : tefInstruments) {
1519 : LOGN("stringNumber %d firstString %d midiVoice %d midiBank %d",
1520 : instrument.stringNumber, instrument.firstString, instrument.midiVoice, instrument.midiBank);
1521 : }
1522 61 : readTefReadingList();
1523 92 : for (const auto& item : tefReadingList) {
1524 : LOGN("firstMeasure %d lastMeasure %d", item.firstMeasure, item.lastMeasure);
1525 : }
1526 61 : readTefContents();
1527 482 : for (const auto& note : tefContents) {
1528 : LOGN("position %d rest %d string %d fret %d duration %d length %d dots %d tie %d triplet %d voice %d",
1529 : note.position, note.rest, note.string, note.fret,
1530 : note.duration, note.length, note.dots,
1531 : note.tie, note.triplet, static_cast<int>(note.voice));
1532 : }
1533 111 : for (const auto& textMarker : tefTextMarkers) {
1534 : LOGN("position %d string %d text marker %d", textMarker.position, textMarker.string, textMarker.index);
1535 : }
1536 61 : createScore();
1537 61 : return Err::NoError;
1538 : }
1539 : } // namespace mu::iex::tabledit
|