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 "note.h"
25 : #include "voiceallocator.h"
26 : #include "engraving/dom/mscore.h"
27 :
28 : namespace mu::iex::tabledit {
29 440 : bool VoiceAllocator::canAddTefNoteToVoice(const TefNote* const note, const int voice)
30 : {
31 : // is there room after the previous note ?
32 440 : if (stopPosition(voice) <= note->position) {
33 : LOGN("add string %d fret %d to voice %d", note->string, note->fret, voice);
34 382 : return true;
35 : }
36 : // can the note go into a chord ?
37 58 : const auto notePlaying = notesPlaying.at(voice);
38 58 : if (notePlaying
39 58 : && !notePlaying->rest
40 56 : && !note->rest
41 56 : && notePlaying->position == note->position
42 45 : && notePlaying->duration == note->duration) {
43 : LOGN("add string %d fret %d to voice %d as chord", note->string, note->fret, voice);
44 39 : return true;
45 : }
46 19 : return false;
47 : }
48 :
49 421 : int VoiceAllocator::findFirstPossibleVoice(const TefNote* const note, const std::array<int, 3> voices)
50 : {
51 440 : for (const auto v : voices) {
52 440 : if (canAddTefNoteToVoice(note, v)) {
53 421 : return v;
54 : }
55 : }
56 0 : return -1;
57 : }
58 :
59 440 : int VoiceAllocator::stopPosition(const size_t voice)
60 : {
61 440 : if (mu::engraving::VOICES <= voice) {
62 0 : LOGD("incorrect voice %zu", voice);
63 0 : return -1;
64 : }
65 :
66 440 : const auto note = notesPlaying.at(voice);
67 440 : if (note) {
68 353 : return note->position + durationToInt(note->duration);
69 : }
70 87 : return 0;
71 : }
72 :
73 421 : void VoiceAllocator::appendNoteToVoice(const TefNote* const note, int voice)
74 : {
75 : LOGN("position %d string %d fret %d voice %d", note->position, note->string, note->fret, voice);
76 421 : const auto nChords { voiceContents[voice].size() };
77 : LOGN("voice %d nChords %zu", voice, nChords);
78 421 : if (nChords == 0) {
79 : LOGN("create first chord");
80 87 : std::vector<const TefNote*> chord;
81 87 : chord.push_back(note);
82 87 : voiceContents[voice].push_back(chord);
83 87 : } else {
84 334 : const auto position { voiceContents[voice].at(nChords - 1).at(0)->position };
85 : LOGN("chord %zu position %d", nChords - 1, position);
86 334 : if (position == note->position) {
87 : LOGN("add to last chord");
88 39 : voiceContents[voice].at(nChords - 1).push_back(note);
89 : } else {
90 : LOGN("create next chord at position %d", note->position);
91 295 : std::vector<const TefNote*> chord;
92 295 : chord.push_back(note);
93 295 : voiceContents[voice].push_back(chord);
94 295 : }
95 : }
96 : LOGN("done");
97 421 : }
98 :
99 : // debug: dump voices
100 :
101 0 : void VoiceAllocator::dump()
102 : {
103 0 : for (size_t i = 0; i < mu::engraving::VOICES; ++i) {
104 : LOGN("- voice %zu", i);
105 0 : for (size_t j = 0; j < voiceContents.at(i).size(); ++j) {
106 : LOGN(" - chord %zu", j);
107 0 : for (const auto note : voiceContents.at(i).at(j)) {
108 : LOGN(" - position %d string %d fret %d", note->position, note->string, note->fret);
109 : }
110 : }
111 : }
112 0 : }
113 :
114 421 : void VoiceAllocator::allocateVoice(const TefNote* const note, int voice)
115 : {
116 421 : if (voice >= 0) {
117 : // do actual allocation
118 : // note chord info is lost
119 421 : if (allocations.count(note) == 0) {
120 421 : allocations[note] = voice;
121 421 : notesPlaying[voice] = note;
122 421 : appendNoteToVoice(note, voice);
123 : } else {
124 0 : LOGD("duplicate note allocation");
125 : }
126 : } else {
127 0 : LOGD("cannot add string %d fret %d to voice %d", note->string, note->fret, voice);
128 : }
129 421 : }
130 :
131 397 : void VoiceAllocator::addColumn(const std::vector<const TefNote*>& column)
132 : {
133 397 : if (column.empty()) {
134 61 : return;
135 : }
136 :
137 : // first add the highest note to voice 0
138 336 : addNote(column.at(0), true);
139 336 : if (column.size() >= 2) {
140 : // then add the lowest note to voice 1
141 49 : addNote(column.at(column.size() - 1), false);
142 : // finally add the remaining notes where possible
143 85 : for (unsigned int i = 1; i < column.size() - 1; ++i) {
144 36 : addNote(column.at(i), true);
145 : }
146 : }
147 : }
148 :
149 421 : void VoiceAllocator::addNote(const TefNote* const note, const bool preferVoice0)
150 : {
151 421 : int voice { -1 };
152 : LOGN("note position %d voice %d", note->position, static_cast<int>(note->voice));
153 421 : if (note->voice == Voice::UPPER) {
154 18 : voice = findFirstPossibleVoice(note, { 0, 2, 3 });
155 403 : } else if (note->voice == Voice::LOWER) {
156 15 : voice = findFirstPossibleVoice(note, { 1, 2, 3 });
157 : } else {
158 388 : if (preferVoice0) {
159 346 : voice = findFirstPossibleVoice(note, { 0, 1, 2 /* TODO , 3 */ });
160 : } else {
161 42 : voice = findFirstPossibleVoice(note, { 1, 0, 2 /* TODO , 3 */ });
162 : }
163 : }
164 421 : allocateVoice(note, voice);
165 421 : }
166 :
167 0 : int VoiceAllocator::voice(const TefNote* const note)
168 : {
169 0 : int res { -1 }; // TODO -1 ?
170 0 : if (allocations.count(note) > 0) {
171 0 : res = allocations[note];
172 : } else {
173 0 : LOGD("no voice allocated for note %p", note);
174 : }
175 :
176 : LOGN("note %p voice %d res %d", note, static_cast<int>(note->voice), res);
177 0 : return res;
178 : }
179 : } // namespace mu::iex::tabledit
|