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 "notationbraille.h"
24 :
25 : #include "translation.h"
26 :
27 : #include "engraving/dom/factory.h"
28 : #include "engraving/dom/measure.h"
29 : #include "engraving/dom/score.h"
30 : #include "engraving/dom/segment.h"
31 : #include "engraving/dom/select.h"
32 : #include "engraving/dom/slur.h"
33 : #include "engraving/dom/staff.h"
34 : #include "engraving/dom/tie.h"
35 :
36 : #include "engraving/editing/noteinput.h"
37 :
38 : #include "notation/inotation.h"
39 : #include "notation/inotationelements.h" // IWYU pragma: keep
40 : #include "notation/inotationinteraction.h"
41 : #include "notation/inotationnoteinput.h" // IWYU pragma: keep
42 : #include "notationscene/notationcommands.h"
43 :
44 : #include "braille.h"
45 : #include "braillecode.h"
46 : #include "brailleinput.h"
47 : #include "brailleinputparser.h"
48 : #include "louis.h"
49 :
50 : using namespace muse;
51 : using namespace muse::io;
52 : using namespace mu::braille;
53 : using namespace mu::notation;
54 :
55 : namespace mu::engraving {
56 0 : void NotationBraille::init()
57 : {
58 0 : setEnabled(brailleConfiguration()->braillePanelEnabled());
59 0 : setCurrentItemPosition(-1, -1);
60 :
61 0 : setMode(BrailleMode::Navigation);
62 :
63 0 : path_t tablesdir = tablesDefaultDirPath();
64 0 : setTablesDir(tablesdir.toStdString().c_str());
65 0 : initTables(tablesdir.toStdString());
66 :
67 0 : std::string welcome = braille_translate(table_for_literature.c_str(), "Welcome to MuseScore Studio!");
68 0 : setBrailleInfo(QString(welcome.c_str()));
69 :
70 0 : brailleConfiguration()->braillePanelEnabledChanged().onNotify(this, [this]() {
71 0 : bool enabled = brailleConfiguration()->braillePanelEnabled();
72 0 : setEnabled(enabled);
73 0 : });
74 :
75 0 : updateTableForLyricsFromPreferences();
76 0 : brailleConfiguration()->brailleTableChanged().onNotify(this, [this]() {
77 0 : updateTableForLyricsFromPreferences();
78 0 : });
79 :
80 0 : setIntervalDirection(brailleConfiguration()->intervalDirection());
81 0 : brailleConfiguration()->intervalDirectionChanged().onNotify(this, [this]() {
82 0 : BrailleIntervalDirection direction = brailleConfiguration()->intervalDirection();
83 0 : setIntervalDirection(direction);
84 0 : });
85 :
86 0 : projectSession()->currentNotationChanged().onNotify(this, [this]() {
87 0 : if (notation()) {
88 0 : notation()->interaction()->selectionChanged().onNotify(this, [this]() {
89 0 : doBraille();
90 0 : }, Mode::SetReplace);
91 :
92 0 : notation()->notationChanged().onReceive(this, [this](const muse::RectF&) {
93 0 : setCurrentItemPosition(0, 0);
94 0 : doBraille(true);
95 0 : }, Mode::SetReplace);
96 :
97 0 : notation()->interaction()->noteInput()->stateChanged().onNotify(this, [this]() {
98 0 : if (interaction()->noteInput()->isNoteInputMode()) {
99 0 : if (isNavigationMode()) {
100 0 : setMode(BrailleMode::BrailleInput);
101 : }
102 : } else {
103 0 : if (isBrailleInputMode()) {
104 0 : setMode(BrailleMode::Navigation);
105 : }
106 : }
107 0 : }, Mode::SetReplace);
108 :
109 0 : notation()->interaction()->noteInput()->noteAdded().onNotify(this, [this]() {
110 0 : if (currentEngravingItem() && currentEngravingItem()->isNote()) {
111 0 : LOGD() << "Note added: " << currentEngravingItem()->accessibleInfo();
112 0 : Note* note = toNote(currentEngravingItem());
113 0 : brailleInput()->setOctave(note->octave());
114 : }
115 0 : }, Mode::SetReplace);
116 : }
117 0 : }, Mode::SetReplace);
118 0 : }
119 :
120 0 : void NotationBraille::updateTableForLyricsFromPreferences()
121 : {
122 0 : QString table = brailleConfiguration()->brailleTable();
123 0 : int startPos = table.indexOf('[');
124 0 : int endPos = table.indexOf(']');
125 0 : if (startPos != -1 && endPos != -1) {
126 0 : table = table.mid(startPos + 1, endPos - startPos - 1);
127 0 : QString table_full_path = QString::fromStdString(tables_dir) + "/" + table;
128 0 : if (check_tables(table_full_path.toStdString().c_str()) == 0) {
129 0 : updateTableForLyrics(table.toStdString());
130 : } else {
131 0 : LOGD() << "Table check error!";
132 : }
133 0 : }
134 0 : }
135 :
136 0 : void NotationBraille::doBraille(bool force)
137 : {
138 0 : if (brailleConfiguration()->braillePanelEnabled()) {
139 0 : EngravingItem* e = nullptr;
140 0 : Measure* m = nullptr;
141 :
142 0 : if (selection()->isSingle()) {
143 0 : e = selection()->element();
144 0 : LOGD() << " selected " << e->accessibleInfo();
145 0 : m = e->findMeasure();
146 0 : } else if (selection()->isRange()) {
147 0 : for (auto el: selection()->elements()) {
148 0 : if (el->isMeasure()) {
149 0 : m = toMeasure(el);
150 0 : break;
151 : } else {
152 0 : m = el->findMeasure();
153 0 : if (m) {
154 0 : break;
155 : }
156 : }
157 : }
158 0 : e = m ? m : selection()->elements().front();
159 0 : } else if (selection()->isList()) {
160 0 : e = selection()->elements().front();
161 0 : m = e->findMeasure();
162 : }
163 0 : if (e) {
164 0 : setCurrentEngravingItem(e, false);
165 :
166 0 : if (!m) {
167 0 : brailleEngravingItemList()->clear();
168 0 : Braille lb(score());
169 0 : bool res = lb.convertItem(e, brailleEngravingItemList());
170 0 : if (!res) {
171 0 : QString txt = e->accessibleInfo();
172 0 : std::string braille = braille_long_translate(table_for_literature.c_str(), txt.toStdString());
173 0 : brailleEngravingItemList()->setBrailleStr(QString::fromStdString(braille));
174 0 : setBrailleInfo(QString::fromStdString(braille));
175 0 : } else {
176 0 : setBrailleInfo(brailleEngravingItemList()->brailleStr());
177 : }
178 0 : current_measure = nullptr;
179 0 : } else {
180 0 : if (m != current_measure || force) {
181 0 : brailleEngravingItemList()->clear();
182 0 : Braille lb(score());
183 0 : lb.convertMeasure(m, brailleEngravingItemList());
184 0 : setBrailleInfo(brailleEngravingItemList()->brailleStr());
185 0 : current_measure = m;
186 0 : }
187 0 : current_bei = brailleEngravingItemList()->getItem(e);
188 0 : if (current_bei != nullptr) {
189 0 : setCurrentItemPosition(current_bei->start(), current_bei->end());
190 : }
191 : }
192 : }
193 : }
194 0 : }
195 :
196 0 : mu::engraving::Score* NotationBraille::score()
197 : {
198 0 : return notation()->elements()->msScore()->score();
199 : }
200 :
201 0 : mu::engraving::Selection* NotationBraille::selection()
202 : {
203 0 : return &score()->selection();
204 : }
205 :
206 0 : ValCh<std::string> NotationBraille::brailleInfo() const
207 : {
208 0 : return m_brailleInfo;
209 : }
210 :
211 0 : ValCh<int> NotationBraille::cursorPosition() const
212 : {
213 0 : return m_cursorPosition;
214 : }
215 :
216 0 : ValCh<int> NotationBraille::currentItemPositionStart() const
217 : {
218 0 : return m_currentItemPositionStart;
219 : }
220 :
221 0 : ValCh<int> NotationBraille::currentItemPositionEnd() const
222 : {
223 0 : return m_currentItemPositionEnd;
224 : }
225 :
226 0 : ValCh<std::string> NotationBraille::keys() const
227 : {
228 0 : return m_keys;
229 : }
230 :
231 0 : ValCh<bool> NotationBraille::enabled() const
232 : {
233 0 : return m_enabled;
234 : }
235 :
236 0 : ValCh<BrailleIntervalDirection> NotationBraille::intervalDirection() const
237 : {
238 0 : return m_intervalDirection;
239 : }
240 :
241 0 : ValCh<int> NotationBraille::mode() const
242 : {
243 0 : return m_mode;
244 : }
245 :
246 0 : ValCh<std::string> NotationBraille::cursorColor() const
247 : {
248 0 : return m_cursorColor;
249 : }
250 :
251 0 : void NotationBraille::setEnabled(bool enabled)
252 : {
253 0 : if (enabled == m_enabled.val) {
254 0 : return;
255 : }
256 0 : m_enabled.set(enabled);
257 : }
258 :
259 0 : void NotationBraille::setIntervalDirection(const BrailleIntervalDirection direction)
260 : {
261 0 : if (direction == m_intervalDirection.val) {
262 0 : return;
263 : }
264 0 : m_intervalDirection.set(direction);
265 : }
266 :
267 0 : BrailleEngravingItemList* NotationBraille::brailleEngravingItemList()
268 : {
269 0 : return &m_beil;
270 : }
271 :
272 0 : QString NotationBraille::getBrailleStr()
273 : {
274 0 : return m_beil.brailleStr();
275 : }
276 :
277 0 : BrailleInputState* NotationBraille::brailleInput()
278 : {
279 0 : return &m_braille_input;
280 : }
281 :
282 0 : void NotationBraille::setBrailleInfo(const QString& info)
283 : {
284 0 : std::string infoStd = info.toStdString();
285 :
286 0 : if (m_brailleInfo.val == infoStd) {
287 0 : return;
288 : }
289 :
290 0 : m_brailleInfo.set(infoStd);
291 0 : }
292 :
293 0 : void NotationBraille::setCursorPosition(const int pos)
294 : {
295 0 : if (!isNavigationMode()) {
296 0 : return;
297 : }
298 :
299 0 : if (m_cursorPosition.val == pos || pos == 0) {
300 0 : return;
301 : }
302 :
303 0 : m_cursorPosition.set(pos);
304 :
305 0 : current_bei = brailleEngravingItemList()->getItem(pos);
306 0 : if (current_bei != NULL && current_bei->el() != NULL
307 0 : && (current_bei->type() == BEIType::EngravingItem
308 0 : || current_bei->type() == BEIType::LyricItem)) {
309 0 : setCurrentEngravingItem(current_bei->el(), true);
310 : }
311 : }
312 :
313 0 : void NotationBraille::setCurrentItemPosition(const int start, const int end)
314 : {
315 0 : if (m_currentItemPositionStart.val == start
316 0 : && m_currentItemPositionEnd.val == end) {
317 0 : return;
318 : }
319 :
320 0 : m_currentItemPositionStart.set(start);
321 0 : m_currentItemPositionEnd.set(end);
322 : }
323 :
324 0 : INotationPtr NotationBraille::notation()
325 : {
326 0 : return projectSession()->currentNotation();
327 : }
328 :
329 0 : INotationInteractionPtr NotationBraille::interaction()
330 : {
331 0 : return notation() ? notation()->interaction() : nullptr;
332 : }
333 :
334 0 : DurationType getDuration(const QString key)
335 : {
336 : static const DurationType types[] = {
337 : DurationType::V_64TH, DurationType::V_32ND, DurationType::V_16TH,
338 : DurationType::V_EIGHTH, DurationType::V_QUARTER, DurationType::V_HALF,
339 : DurationType::V_WHOLE, DurationType::V_BREVE, DurationType::V_LONG
340 : };
341 0 : bool is_ok = false;
342 0 : int val = key.toInt(&is_ok);
343 :
344 0 : if (!is_ok || val < 1 || val > 9) {
345 0 : return DurationType::V_INVALID;
346 : } else {
347 0 : return types[val - 1];
348 : }
349 : }
350 :
351 0 : void NotationBraille::setInputNoteDuration(Duration d)
352 : {
353 0 : InputState& inputState = score()->inputState();
354 0 : inputState.setDuration(d);
355 0 : score()->setInputState(inputState);
356 0 : brailleInput()->setCurrentDuration(d.type());
357 0 : }
358 :
359 0 : void NotationBraille::setTupletDuration(int tuplet, Duration d)
360 : {
361 0 : LOGD() << tuplet << " " << (int)d.type();
362 0 : brailleInput()->setCurrentDuration(d.type());
363 :
364 0 : switch (tuplet) {
365 0 : case 2:
366 0 : d = d.shiftRetainDots(-1, false);
367 0 : d = d.shiftRetainDots(-1, true);
368 0 : break;
369 0 : case 3:
370 0 : d = d.shiftRetainDots(-1, false);
371 0 : break;
372 0 : case 5: case 6: case 7:
373 0 : d = d.shiftRetainDots(-2, false);
374 0 : break;
375 0 : case 8:
376 0 : d = d.shiftRetainDots(-2, false);
377 0 : d = d.shiftRetainDots(-1, true);
378 0 : break;
379 0 : case 9:
380 0 : d = d.shiftRetainDots(-3, false);
381 0 : break;
382 : }
383 0 : InputState& inputState = score()->inputState();
384 0 : inputState.setDuration(d);
385 0 : score()->setInputState(inputState);
386 0 : }
387 :
388 0 : static TupletOptions makeTupletOption(int num)
389 : {
390 0 : TupletOptions option;
391 0 : switch (num) {
392 0 : case 2:
393 0 : option.ratio = { 2, 3 };
394 0 : break;
395 0 : case 3:
396 0 : option.ratio = { 3, 2 };
397 0 : break;
398 0 : case 5:
399 0 : option.ratio = { 5, 4 };
400 0 : break;
401 0 : case 6:
402 0 : option.ratio = { 6, 4 };
403 0 : break;
404 0 : case 7:
405 0 : option.ratio = { 7, 4 };
406 0 : break;
407 0 : case 8:
408 0 : option.ratio = { 8, 6 };
409 0 : break;
410 0 : case 9:
411 0 : option.ratio = { 9, 8 };
412 0 : break;
413 0 : default:
414 0 : break;
415 : }
416 0 : return option;
417 : }
418 :
419 0 : bool matchPattern(const std::string sequence, const std::string pattern)
420 : {
421 0 : QStringList seqs = QString::fromStdString(sequence).split("+");
422 0 : QStringList pats = QString::fromStdString(pattern).split("+");
423 :
424 0 : if (seqs.length() != pats.length()) {
425 0 : return false;
426 : }
427 0 : seqs.sort();
428 0 : pats.sort();
429 :
430 0 : for (int i = 0; i < seqs.length(); i++) {
431 0 : if (seqs.at(i) != pats.at(i)) {
432 0 : return false;
433 : }
434 : }
435 0 : return true;
436 0 : }
437 :
438 0 : void NotationBraille::setKeys(const QString& sequence)
439 : {
440 0 : LOGD() << sequence;
441 0 : std::string seq = sequence.toStdString();
442 0 : m_keys.set(seq);
443 :
444 0 : if (seq == "Left") {
445 0 : interaction()->select(SelectionTarget::PrevChord);
446 0 : } else if (seq == "Right") {
447 0 : interaction()->select(SelectionTarget::NextChord);
448 0 : } else if (matchPattern(seq, "Ctrl+Left")) {
449 0 : interaction()->select(SelectionTarget::PrevMeasure);
450 0 : } else if (matchPattern(seq, "Ctrl+Right")) {
451 0 : interaction()->select(SelectionTarget::NextMeasure);
452 0 : } else if (matchPattern(seq, "Alt+Left")) {
453 0 : interaction()->select(SelectionTarget::PrevItem);
454 0 : } else if (matchPattern(seq, "Alt+Right")) {
455 0 : interaction()->select(SelectionTarget::NextItem);
456 0 : } else if (matchPattern(seq, "Ctrl+End")) {
457 0 : interaction()->select(SelectionTarget::LastItem);
458 0 : } else if (matchPattern(seq, "Ctrl+Home")) {
459 0 : interaction()->select(SelectionTarget::FirstItem);
460 0 : } else if (seq == "Delete") {
461 0 : if (currentEngravingItem()) {
462 0 : interaction()->deleteSelection();
463 0 : if (!brailleInput()->intervals().empty()) {
464 0 : brailleInput()->removeLastInterval();
465 : }
466 : }
467 0 : } else if (seq == "N") {
468 0 : toggleMode();
469 0 : brailleInput()->initialize();
470 0 : } else if (seq == "Plus") {
471 0 : if (isBrailleInputMode()) {
472 0 : interaction()->noteInput()->doubleNoteInputDuration();
473 : }
474 0 : } else if (seq == "Minus") {
475 0 : if (isBrailleInputMode()) {
476 0 : interaction()->noteInput()->halveNoteInputDuration();
477 : }
478 0 : } else if (matchPattern(seq, "Space+F") && isBrailleInputMode()) {
479 0 : brailleInput()->setNoteGroup(NoteGroup::Group1);
480 0 : brailleInput()->resetBuffer();
481 0 : } else if (matchPattern(seq, "Space+D") && isBrailleInputMode()) {
482 0 : brailleInput()->setNoteGroup(NoteGroup::Group2);
483 0 : brailleInput()->resetBuffer();
484 0 : } else if (matchPattern(seq, "Space+S") && isBrailleInputMode()) {
485 0 : brailleInput()->setNoteGroup(NoteGroup::Group3);
486 0 : brailleInput()->resetBuffer();
487 0 : } else if (matchPattern(seq, "Space+S+D+J+K") && isBrailleInputMode()) {
488 0 : LOGD() << "tuplet input";
489 0 : brailleInput()->setTupletIndicator(true);
490 0 : brailleInput()->resetBuffer();
491 0 : } else if (seq == "Space") {
492 0 : brailleInput()->reset();
493 0 : } else if (isBrailleInputMode() && !sequence.isEmpty()) {
494 0 : QString pattern = parseBrailleKeyInput(sequence);
495 0 : if (!pattern.isEmpty()) {
496 0 : brailleInput()->insertToBuffer(pattern);
497 : } else {
498 0 : brailleInput()->insertToBuffer(sequence);
499 : }
500 0 : LOGD() << brailleInput()->buffer();
501 0 : std::string braille = translate2Braille(brailleInput()->buffer().toStdString());
502 0 : BieRecognize(braille, brailleInput()->tupletIndicator());
503 :
504 0 : NoteName prevNoteName = NoteName::C;
505 0 : int prevNoteOctave = -1; // unknown octave
506 :
507 0 : if (const Segment* seg = interaction()->noteInput()->state().segment()) {
508 0 : const track_idx_t track = interaction()->noteInput()->state().track();
509 0 : Chord* prevChord = nullptr;
510 :
511 0 : for (Segment* s = seg->prev1(SegmentType::ChordRest); s; s = s->prev1(SegmentType::ChordRest)) {
512 0 : EngravingItem* e = s->element(track);
513 0 : if (e && e->isChord()) {
514 0 : prevChord = toChord(e);
515 0 : break;
516 : }
517 : }
518 :
519 0 : if (prevChord) {
520 0 : Note* rootNote = (currentIntervalDirection() == IntervalDirection::Up) ? prevChord->downNote() : prevChord->upNote();
521 0 : String pitchName;
522 0 : String accidental; // needed for tpc2name
523 0 : tpc2name(rootNote->tpc(), NoteSpellingType::STANDARD, NoteCaseType::UPPER, pitchName, accidental);
524 0 : size_t inote = String("CDEFGAB").indexOf(pitchName.at(0));
525 0 : IF_ASSERT_FAILED(inote <= 6) {
526 : } else {
527 0 : prevNoteName = static_cast<NoteName>(inote);
528 0 : prevNoteOctave = rootNote->octave();
529 : }
530 0 : }
531 : }
532 :
533 0 : brailleInput()->setNoteName(prevNoteName, true);
534 0 : brailleInput()->setOctave(prevNoteOctave, true);
535 :
536 0 : BieSequencePatternType type = brailleInput()->parseBraille(currentIntervalDirection());
537 0 : switch (type) {
538 0 : case BieSequencePatternType::Note: {
539 0 : LOGD() << "note";
540 0 : if (brailleInput()->accidental() != mu::notation::AccidentalType::NONE) {
541 0 : interaction()->noteInput()->setAccidental(brailleInput()->accidental());
542 : }
543 :
544 0 : setVoice(brailleInput()->accord());
545 :
546 0 : if (brailleInput()->tupletNumber() != -1
547 0 : && brailleInput()->tupletDuration().type() != DurationType::V_INVALID) {
548 0 : Duration duration = brailleInput()->tupletDuration();
549 0 : int tuplet = brailleInput()->tupletNumber();
550 0 : setTupletDuration(tuplet, duration);
551 0 : TupletOptions option = makeTupletOption(brailleInput()->tupletNumber());
552 0 : interaction()->noteInput()->addTuplet(option);
553 0 : brailleInput()->clearTuplet();
554 : }
555 :
556 0 : DurationType d = brailleInput()->getCloseDuration();
557 0 : Duration duration = Duration(d);
558 0 : setInputNoteDuration(duration);
559 :
560 0 : NoteInputParams params;
561 0 : const int note = static_cast<int>(brailleInput()->noteName());
562 0 : bool ok = mu::engraving::NoteInput::resolveNoteInputParams(score(), note, /*addFlag*/ false, params);
563 0 : if (!ok) {
564 0 : return;
565 : }
566 :
567 0 : interaction()->noteInput()->addNote(params, NoteAddingMode::NextChord);
568 :
569 0 : if (brailleInput()->addedOctave() != -1) {
570 0 : if (brailleInput()->addedOctave() < brailleInput()->octave()) {
571 0 : for (int i = brailleInput()->addedOctave(); i < brailleInput()->octave(); i++) {
572 0 : interaction()->movePitch(MoveDirection::Down, PitchMode::OCTAVE);
573 : }
574 0 : } else if (brailleInput()->addedOctave() > brailleInput()->octave()) {
575 0 : for (int i = brailleInput()->octave(); i < brailleInput()->addedOctave(); i++) {
576 0 : interaction()->movePitch(MoveDirection::Up, PitchMode::OCTAVE);
577 : }
578 : }
579 0 : brailleInput()->setOctave(brailleInput()->addedOctave(), true);
580 : }
581 :
582 0 : if (brailleInput()->longSlurStart()) {
583 0 : if (brailleInput()->longSlurStartNote() == NULL) {
584 0 : if (currentEngravingItem() != NULL && currentEngravingItem()->isNote()) {
585 0 : Note* currentNote = toNote(currentEngravingItem());
586 0 : brailleInput()->setLongSlurStartNote(currentNote);
587 : }
588 : }
589 : }
590 :
591 0 : if (brailleInput()->tieStartNote() != NULL) {
592 0 : addTie();
593 0 : brailleInput()->clearTie();
594 : }
595 :
596 0 : if (brailleInput()->slurStartNote() != NULL) {
597 0 : addSlur();
598 0 : brailleInput()->clearSlur();
599 : }
600 :
601 0 : playbackController()->playElements({ currentEngravingItem() });
602 0 : brailleInput()->reset();
603 0 : break;
604 : }
605 0 : case BieSequencePatternType::Rest: {
606 0 : LOGD() << "rest";
607 0 : DurationType d = brailleInput()->getCloseDuration();
608 0 : Duration duration = Duration(d);
609 0 : if (brailleInput()->dots() > 0) {
610 0 : duration.setDots(brailleInput()->dots());
611 : }
612 0 : setInputNoteDuration(Duration(duration));
613 0 : interaction()->putRest(duration);
614 0 : brailleInput()->reset();
615 0 : break;
616 : }
617 0 : case BieSequencePatternType::Interval: {
618 0 : LOGD() << "interval";
619 0 : if (brailleInput()->accidental() != mu::notation::AccidentalType::NONE) {
620 0 : interaction()->noteInput()->setAccidental(brailleInput()->accidental());
621 : }
622 :
623 0 : NoteInputParams params;
624 0 : const int note = static_cast<int>(brailleInput()->noteName());
625 0 : bool ok = mu::engraving::NoteInput::resolveNoteInputParams(score(), note, /*addFlag*/ true, params);
626 0 : if (!ok) {
627 0 : return;
628 : }
629 :
630 0 : interaction()->noteInput()->addNote(params, NoteAddingMode::CurrentChord);
631 :
632 0 : if (brailleInput()->addedOctave() != -1) {
633 0 : if (brailleInput()->addedOctave() < brailleInput()->octave()) {
634 0 : for (int i = brailleInput()->addedOctave(); i < brailleInput()->octave(); i++) {
635 0 : interaction()->movePitch(MoveDirection::Down, PitchMode::OCTAVE);
636 : }
637 0 : } else if (brailleInput()->addedOctave() > brailleInput()->octave()) {
638 0 : for (int i = brailleInput()->octave(); i < brailleInput()->addedOctave(); i++) {
639 0 : interaction()->movePitch(MoveDirection::Up, PitchMode::OCTAVE);
640 : }
641 : }
642 0 : brailleInput()->setOctave(brailleInput()->addedOctave());
643 : }
644 0 : playbackController()->playElements({ currentEngravingItem() });
645 0 : brailleInput()->reset();
646 0 : break;
647 : }
648 0 : case BieSequencePatternType::Tuplet: case BieSequencePatternType::Tuplet3: {
649 0 : LOGD() << "tuplet";
650 0 : const QString stateTuplet = muse::qtrc("braille/notation", "Tuplet %1").arg(brailleInput()->tupletNumber());
651 0 : accessibilityController()->announce(stateTuplet);
652 0 : break;
653 0 : }
654 0 : case BieSequencePatternType::Tie: {
655 0 : LOGD() << "tie";
656 0 : if (brailleInput()->tie()) {
657 0 : if (currentEngravingItem() != NULL && currentEngravingItem()->isNote()) {
658 0 : Note* note = toNote(currentEngravingItem());
659 0 : brailleInput()->setTieStartNote(note);
660 : }
661 : }
662 0 : brailleInput()->reset();
663 0 : break;
664 : }
665 0 : case BieSequencePatternType::NoteSlur: {
666 0 : LOGD() << "note slur";
667 0 : if (brailleInput()->noteSlur()) {
668 0 : if (brailleInput()->slurStartNote() == NULL) {
669 0 : if (currentEngravingItem() != NULL && currentEngravingItem()->isNote()) {
670 0 : Note* note = toNote(currentEngravingItem());
671 0 : brailleInput()->setSlurStartNote(note);
672 : }
673 : }
674 : }
675 0 : brailleInput()->reset();
676 0 : break;
677 : }
678 0 : case BieSequencePatternType::LongSlurStop: {
679 0 : LOGD() << "long slur stop";
680 0 : if (brailleInput()->longSlurStop()) {
681 0 : if (brailleInput()->longSlurStartNote() != NULL) {
682 0 : addLongSlur();
683 0 : brailleInput()->clearLongSlur();
684 : }
685 : }
686 0 : brailleInput()->reset();
687 0 : break;
688 : }
689 0 : case BieSequencePatternType::Dot: {
690 0 : LOGD() << "dot " << brailleInput()->dots();
691 0 : if (brailleInput()->dots() > 0) {
692 0 : interaction()->increaseDecreaseDuration(-brailleInput()->dots(), true);
693 : }
694 0 : brailleInput()->reset();
695 0 : break;
696 : }
697 0 : default: {
698 : // TODO
699 : }
700 : }
701 0 : } else if (isBrailleInputMode() && !sequence.isEmpty()) {
702 0 : QString pattern = parseBrailleKeyInput(sequence);
703 0 : if (!pattern.isEmpty()) {
704 0 : brailleInput()->insertToBuffer(pattern);
705 : } else {
706 0 : brailleInput()->insertToBuffer(sequence);
707 : }
708 0 : }
709 0 : }
710 :
711 0 : bool NotationBraille::addTie()
712 : {
713 0 : if (!brailleInput()->tieStartNote()) {
714 0 : return false;
715 : }
716 :
717 0 : if (!currentEngravingItem() || !currentEngravingItem()->isNote()) {
718 0 : return false;
719 : }
720 :
721 0 : score()->startCmd(TranslatableString("undoableAction", "Add tie"));
722 :
723 0 : Note* startNote = brailleInput()->tieStartNote();
724 0 : Note* endNote = toNote(currentEngravingItem());
725 :
726 0 : Tie* tie = Factory::createTie(startNote);
727 0 : tie->setStartNote(startNote);
728 0 : tie->setEndNote(endNote);
729 0 : tie->setTrack(startNote->track());
730 0 : tie->setTick(startNote->chord()->segment()->tick());
731 0 : tie->setTicks(endNote->chord()->segment()->tick() - startNote->chord()->segment()->tick());
732 0 : score()->undoAddElement(tie);
733 0 : score()->endCmd();
734 0 : return true;
735 : }
736 :
737 0 : bool NotationBraille::addSlur()
738 : {
739 0 : if (brailleInput()->slurStartNote() != NULL
740 0 : && currentEngravingItem() != NULL && currentEngravingItem()->isNote()) {
741 0 : Note* note1 = brailleInput()->slurStartNote();
742 0 : Note* note2 = toNote(currentEngravingItem());
743 :
744 0 : if (note1->parent()->isChordRest() && note2->parent()->isChordRest()) {
745 0 : LOGD() << "add slur";
746 0 : ChordRest* firstChordRest = toChordRest(note1->parent());
747 0 : ChordRest* secondChordRest = toChordRest(note2->parent());
748 :
749 0 : score()->startCmd(TranslatableString("undoableAction", "Add slur"));
750 :
751 0 : Slur* slur = Factory::createSlur(firstChordRest->score()->dummy());
752 0 : slur->setScore(firstChordRest->score());
753 0 : slur->setTick(firstChordRest->tick());
754 0 : slur->setTick2(secondChordRest->tick());
755 0 : slur->setTrack(firstChordRest->track());
756 0 : if (secondChordRest->staff()->part() == firstChordRest->staff()->part()
757 0 : && !secondChordRest->staff()->isLinked(firstChordRest->staff())) {
758 0 : slur->setTrack2(secondChordRest->track());
759 : } else {
760 0 : slur->setTrack2(firstChordRest->track());
761 : }
762 0 : slur->setStartElement(firstChordRest);
763 0 : slur->setEndElement(secondChordRest);
764 :
765 0 : firstChordRest->score()->undoAddElement(slur);
766 0 : SlurSegment* ss = new SlurSegment(slur);
767 0 : ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
768 0 : if (firstChordRest == secondChordRest) {
769 0 : ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->spatium(), 0.0));
770 : }
771 0 : slur->add(ss);
772 :
773 0 : score()->endCmd();
774 0 : doBraille(true);
775 0 : return true;
776 : }
777 0 : return false;
778 : } else {
779 0 : return false;
780 : }
781 : }
782 :
783 0 : bool NotationBraille::addLongSlur()
784 : {
785 0 : if (brailleInput()->longSlurStartNote() != NULL
786 0 : && currentEngravingItem() != NULL && currentEngravingItem()->isNote()) {
787 0 : Note* note1 = brailleInput()->longSlurStartNote();
788 0 : Note* note2 = toNote(currentEngravingItem());
789 :
790 0 : if (note1->parent()->isChordRest() && note2->parent()->isChordRest()) {
791 0 : ChordRest* firstChordRest = toChordRest(note1->parent());
792 0 : ChordRest* secondChordRest = toChordRest(note2->parent());
793 :
794 0 : score()->startCmd(TranslatableString("undoableAction", "Add long slur"));
795 :
796 0 : Slur* slur = Factory::createSlur(firstChordRest->score()->dummy());
797 0 : slur->setScore(firstChordRest->score());
798 0 : slur->setTick(firstChordRest->tick());
799 0 : slur->setTick2(secondChordRest->tick());
800 0 : slur->setTrack(firstChordRest->track());
801 0 : if (secondChordRest->staff()->part() == firstChordRest->staff()->part()
802 0 : && !secondChordRest->staff()->isLinked(firstChordRest->staff())) {
803 0 : slur->setTrack2(secondChordRest->track());
804 : } else {
805 0 : slur->setTrack2(firstChordRest->track());
806 : }
807 0 : slur->setStartElement(firstChordRest);
808 0 : slur->setEndElement(secondChordRest);
809 :
810 0 : firstChordRest->score()->undoAddElement(slur);
811 0 : SlurSegment* ss = new SlurSegment(slur);
812 0 : ss->setSpannerSegmentType(SpannerSegmentType::SINGLE);
813 0 : if (firstChordRest == secondChordRest) {
814 0 : ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->spatium(), 0.0));
815 : }
816 0 : slur->add(ss);
817 :
818 0 : score()->endCmd();
819 0 : doBraille(true);
820 0 : return true;
821 : }
822 0 : return false;
823 : } else {
824 0 : return false;
825 : }
826 : }
827 :
828 0 : bool NotationBraille::setVoice(bool new_voice)
829 : {
830 0 : if (currentEngravingItem() == NULL || currentMeasure() == NULL) {
831 0 : return false;
832 : }
833 :
834 0 : staff_idx_t staff = currentEngravingItem()->staffIdx();
835 :
836 0 : if (new_voice) {
837 0 : int voices = 0;
838 0 : for (size_t i = 1; i < VOICES; ++i) {
839 0 : if (current_measure->hasVoice(staff * VOICES + i)) {
840 0 : voices++;
841 : }
842 : }
843 0 : if (voices >= 3) {
844 0 : return false;
845 : }
846 :
847 0 : score()->inputState().moveInputPos(currentMeasure()->segments().firstCRSegment());
848 0 : interaction()->noteInput()->setCurrentVoice(voices + 1);
849 0 : return true;
850 : } else {
851 0 : Segment* segment = score()->inputState().segment();
852 0 : Measure* measure = segment->measure();
853 0 : if (!measure->hasVoice(staff + 1) && segment->tick() == measure->tick()) {
854 0 : interaction()->noteInput()->setCurrentVoice(0);
855 : }
856 0 : return false;
857 : }
858 : }
859 :
860 0 : void NotationBraille::setMode(const BrailleMode mode)
861 : {
862 0 : switch (mode) {
863 0 : case BrailleMode::Undefined:
864 : case BrailleMode::Navigation:
865 0 : setCursorColor("black");
866 0 : break;
867 0 : case BrailleMode::BrailleInput:
868 0 : setCursorColor("green");
869 0 : break;
870 : }
871 :
872 0 : m_mode.set((int)mode);
873 0 : }
874 :
875 0 : void NotationBraille::toggleMode()
876 : {
877 0 : switch ((BrailleMode)mode().val) {
878 0 : case BrailleMode::Undefined:
879 : case BrailleMode::Navigation:
880 0 : setMode(BrailleMode::BrailleInput);
881 0 : break;
882 0 : case BrailleMode::BrailleInput:
883 0 : setMode(BrailleMode::Navigation);
884 0 : break;
885 : }
886 :
887 0 : commandDispatcher()->dispatch(notation::TOGGLE_NOTE_INPUT_COMMAND);
888 :
889 0 : const QString stateTitle = interaction()->noteInput()->isNoteInputMode()
890 : //: Braille input with 6 keyboard keys (F,D,S & J,K,L) to represent the 6 dots in a braille cell.
891 0 : ? muse::qtrc("braille/notation", "Six-key input mode")
892 : //: Braille navigation.
893 0 : : muse::qtrc("braille/notation", "Navigation mode");
894 :
895 0 : accessibilityController()->announce(stateTitle);
896 0 : }
897 :
898 0 : bool NotationBraille::isNavigationMode()
899 : {
900 0 : return mode().val == (int)BrailleMode::Navigation;
901 : }
902 :
903 0 : bool NotationBraille::isBrailleInputMode()
904 : {
905 0 : return mode().val == (int)BrailleMode::BrailleInput;
906 : }
907 :
908 0 : void NotationBraille::setCursorColor(const QString color)
909 : {
910 0 : m_cursorColor.set(color.toStdString());
911 0 : }
912 :
913 0 : path_t NotationBraille::tablesDefaultDirPath() const
914 : {
915 0 : return globalConfiguration()->appDataPath() + "tables";
916 : }
917 :
918 0 : void NotationBraille::setCurrentEngravingItem(EngravingItem* e, bool select)
919 : {
920 0 : if (!e) {
921 0 : return;
922 : }
923 :
924 0 : bool play = current_engraving_item != e;
925 0 : current_engraving_item = e;
926 0 : if (isNavigationMode()) {
927 0 : if (select) {
928 0 : if (play) {
929 0 : playbackController()->playElements({ e });
930 : }
931 0 : if (e->isChord()) {
932 0 : e = toChord(e)->upNote(); // select a note rather than the chord
933 : }
934 0 : interaction()->select({ e });
935 : }
936 : }
937 : }
938 :
939 0 : IntervalDirection NotationBraille::currentIntervalDirection()
940 : {
941 0 : switch (m_intervalDirection.val) {
942 0 : case BrailleIntervalDirection::Up:
943 0 : return IntervalDirection::Up;
944 0 : case BrailleIntervalDirection::Down:
945 0 : return IntervalDirection::Down;
946 0 : case BrailleIntervalDirection::Auto:
947 0 : break;
948 : }
949 0 : if (EngravingItem* element = currentEngravingItem()) {
950 0 : if (Staff* staff = element->staff()) {
951 0 : ClefType clef = staff->clef(element->tick());
952 0 : if (clef >= ClefType::G && clef <= ClefType::C3) {
953 0 : return IntervalDirection::Down;
954 : }
955 0 : return IntervalDirection::Up;
956 : }
957 : }
958 0 : return IntervalDirection::Down;
959 : }
960 :
961 0 : EngravingItem* NotationBraille::currentEngravingItem()
962 : {
963 0 : return current_engraving_item;
964 : }
965 :
966 0 : Measure* NotationBraille::currentMeasure()
967 : {
968 0 : return current_measure;
969 : }
970 : }
|