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 "excerpt.h"
24 : #include "instrument.h"
25 : #include "masterscore.h"
26 : #include "part.h"
27 : #include "score.h"
28 :
29 : #include "log.h"
30 :
31 : using namespace mu;
32 :
33 : namespace mu::engraving {
34 : //---------------------------------------------------------
35 : // rebuildMidiMapping
36 : //---------------------------------------------------------
37 :
38 3022 : void MasterScore::rebuildMidiMapping()
39 : {
40 3022 : Score* playbackScore = m_playbackScore ? m_playbackScore : this;
41 3022 : setPlaybackScore(nullptr);
42 :
43 3022 : removeDeletedMidiMapping();
44 3022 : int maxport = updateMidiMapping();
45 3022 : reorderMidiMapping();
46 3022 : rebuildExcerptsMidiMapping();
47 3022 : masterScore()->setMidiPortCount(maxport);
48 :
49 3022 : setPlaybackScore(playbackScore);
50 3022 : }
51 :
52 : //---------------------------------------------------------
53 : // checkMidiMapping
54 : // midi mapping is simple if all ports and channels
55 : // don't decrease and don't have 'holes' except drum tracks
56 : //---------------------------------------------------------
57 :
58 1243 : void MasterScore::checkMidiMapping()
59 : {
60 1243 : m_isSimpleMidiMapping = true;
61 1243 : rebuildMidiMapping();
62 :
63 1243 : std::vector<bool> drum;
64 1243 : drum.reserve(m_midiMapping.size());
65 2836 : for (Part* part : parts()) {
66 3209 : for (const auto& pair : part->instruments()) {
67 1616 : const Instrument* instr = pair.second;
68 3421 : for (size_t j = 0; j < instr->channel().size(); ++j) {
69 1805 : drum.push_back(instr->useDrumset());
70 : }
71 : }
72 : }
73 1243 : int lastChannel = -1; // port*16+channel
74 1243 : int lastDrumPort = -1;
75 1243 : size_t index = 0;
76 2862 : for (const MidiMapping& m : m_midiMapping) {
77 1695 : if (index >= drum.size()) {
78 0 : break;
79 : }
80 1695 : if (drum[index]) {
81 36 : lastDrumPort++;
82 36 : if (m.port() != lastDrumPort) {
83 0 : m_isSimpleMidiMapping = false;
84 76 : return;
85 : }
86 : } else {
87 1659 : lastChannel++;
88 1659 : if (lastChannel % 16 == 9) {
89 3 : lastChannel++;
90 : }
91 1659 : int p = lastChannel / 16;
92 1659 : int c = lastChannel % 16;
93 1659 : if (m.port() != p || m.channel() != c) {
94 76 : m_isSimpleMidiMapping = false;
95 76 : return;
96 : }
97 : }
98 1619 : index++;
99 : }
100 1243 : }
101 :
102 : //---------------------------------------------------------
103 : // getNextFreeMidiMapping
104 : //---------------------------------------------------------
105 :
106 2730 : int MasterScore::getNextFreeMidiMapping(std::set<int>& occupiedMidiChannels, unsigned int& searchMidiMappingFrom, int p, int ch)
107 : {
108 2730 : if (ch != -1 && p != -1) {
109 0 : return p * 16 + ch;
110 2730 : } else if (ch != -1 && p == -1) {
111 448 : for (int port = 0;; port++) {
112 704 : if (!muse::contains(occupiedMidiChannels, port * 16 + ch)) {
113 448 : occupiedMidiChannels.insert(port * 16 + ch);
114 448 : return port * 16 + ch;
115 : }
116 : }
117 2282 : } else if (ch == -1 && p != -1) {
118 0 : for (int channel = 0; channel < 16; channel++) {
119 0 : if (channel != 9 && !muse::contains(occupiedMidiChannels, p * 16 + channel)) {
120 0 : occupiedMidiChannels.insert(p * 16 + channel);
121 0 : return p * 16 + channel;
122 : }
123 : }
124 : }
125 :
126 1123 : for (;; searchMidiMappingFrom++) {
127 3405 : if (searchMidiMappingFrom % 16 != 9 && !muse::contains(occupiedMidiChannels, int(searchMidiMappingFrom))) {
128 2282 : occupiedMidiChannels.insert(searchMidiMappingFrom);
129 2282 : return searchMidiMappingFrom;
130 : }
131 : }
132 : }
133 :
134 : //---------------------------------------------------------
135 : // getNextFreeDrumMidiMapping
136 : //---------------------------------------------------------
137 :
138 45 : int MasterScore::getNextFreeDrumMidiMapping(std::set<int>& occupiedMidiChannels)
139 : {
140 45 : for (int i = 0;; i++) {
141 194 : if (!muse::contains(occupiedMidiChannels, i * 16 + 9)) {
142 45 : occupiedMidiChannels.insert(i * 16 + 9);
143 45 : return i * 16 + 9;
144 : }
145 : }
146 : }
147 :
148 : //---------------------------------------------------------
149 : // rebuildExcerptsMidiMapping
150 : //---------------------------------------------------------
151 :
152 3022 : void MasterScore::rebuildExcerptsMidiMapping()
153 : {
154 3341 : for (Excerpt* ex : excerpts()) {
155 638 : for (Part* p : ex->excerptScore()->parts()) {
156 319 : const Part* masterPart = p->masterPart();
157 319 : if (!masterPart->score()->isMaster()) {
158 0 : LOGW() << "rebuildExcerptsMidiMapping: no part in master score is linked with " << p->partName();
159 0 : continue;
160 : }
161 319 : assert(p->instruments().size() == masterPart->instruments().size());
162 638 : for (const auto&[tick, iMaster] : masterPart->instruments()) {
163 319 : Instrument* iLocal = p->instrument(Fraction::fromTicks(tick));
164 319 : const size_t nchannels = iMaster->channel().size();
165 319 : if (iLocal->channel().size() != nchannels) {
166 : // may happen, e.g., if user changes an instrument
167 2 : (*iLocal) = (*iMaster);
168 2 : continue;
169 : }
170 664 : for (size_t c = 0; c < nchannels; ++c) {
171 347 : InstrChannel* cLocal = iLocal->channel(static_cast<int>(c));
172 347 : const InstrChannel* cMaster = iMaster->channel(static_cast<int>(c));
173 347 : cLocal->setChannel(cMaster->channel());
174 : }
175 : }
176 : }
177 : }
178 3022 : }
179 :
180 : //---------------------------------------------------------
181 : // reorderMidiMapping
182 : // Set mappings in order you see in Add->Instruments
183 : //---------------------------------------------------------
184 :
185 3022 : void MasterScore::reorderMidiMapping()
186 : {
187 : using std::swap;
188 3022 : int sequenceNumber = 0;
189 7432 : for (Part* part : parts()) {
190 8911 : for (const auto& pair : part->instruments()) {
191 4501 : const Instrument* instr = pair.second;
192 9843 : for (InstrChannel* channel : instr->channel()) {
193 5342 : if (m_midiMapping[sequenceNumber].part() == part && m_midiMapping[sequenceNumber].m_masterChannel == channel) {
194 5196 : sequenceNumber++;
195 5196 : continue;
196 : }
197 :
198 146 : const int shouldBe = channel->channel();
199 146 : swap(m_midiMapping[sequenceNumber], m_midiMapping[shouldBe]);
200 146 : m_midiMapping[sequenceNumber].articulation()->setChannel(sequenceNumber);
201 146 : channel->setChannel(sequenceNumber);
202 146 : m_midiMapping[shouldBe].articulation()->setChannel(shouldBe);
203 :
204 146 : sequenceNumber++;
205 : }
206 : }
207 : }
208 3022 : }
209 :
210 : //---------------------------------------------------------
211 : // removeDeletedMidiMapping
212 : // Remove mappings to deleted instruments
213 : //---------------------------------------------------------
214 :
215 3022 : void MasterScore::removeDeletedMidiMapping()
216 : {
217 3022 : int removeOffset = 0;
218 3022 : int mappingSize = int(m_midiMapping.size());
219 6060 : for (int index = 0; index < mappingSize; index++) {
220 3038 : Part* part = midiMapping(index)->part();
221 3038 : if (!muse::contains(parts(), part)) {
222 14 : removeOffset++;
223 23 : continue;
224 : }
225 : // Not all channels could exist
226 3024 : bool channelExists = false;
227 3081 : for (const auto& pair : part->instruments()) {
228 3072 : const Instrument* instr = pair.second;
229 3072 : channelExists = (m_midiMapping[index].articulation()->channel() != -1
230 3072 : && muse::contains(instr->channel(), m_midiMapping[index].m_masterChannel)
231 6144 : && !(m_midiMapping[index].port() == -1 && m_midiMapping[index].channel() == -1));
232 3072 : if (channelExists) {
233 3015 : break;
234 : }
235 : }
236 3024 : if (!channelExists) {
237 9 : removeOffset++;
238 9 : continue;
239 : }
240 : // Let's do a left shift by 'removeOffset' items if necessary
241 3015 : if (index != 0 && removeOffset != 0) {
242 17 : m_midiMapping[index - removeOffset] = std::move(m_midiMapping[index]);
243 :
244 17 : const int chanVal = m_midiMapping[index - removeOffset].articulation()->channel();
245 17 : m_midiMapping[index - removeOffset].articulation()->setChannel(chanVal - removeOffset);
246 : }
247 : }
248 : // We have 'removeOffset' deleted instruments, let's remove their mappings
249 3045 : for (int index = 0; index < removeOffset; index++) {
250 23 : m_midiMapping.pop_back();
251 : }
252 3022 : }
253 :
254 : //---------------------------------------------------------
255 : // updateMidiMapping
256 : // Add mappings to new instruments and repair existing ones
257 : //---------------------------------------------------------
258 :
259 3022 : int MasterScore::updateMidiMapping()
260 : {
261 3022 : int maxport = 0;
262 3022 : std::set<int> occupiedMidiChannels; // each entry is port*16+channel, port range: 0-inf, channel: 0-15
263 3022 : unsigned int searchMidiMappingFrom = 0; // makes getting next free MIDI mapping faster
264 :
265 6037 : for (const MidiMapping& mm :m_midiMapping) {
266 3015 : if (mm.port() == -1 || mm.channel() == -1) {
267 448 : continue;
268 : }
269 2567 : occupiedMidiChannels.insert(static_cast<int>(mm.port()) * 16 + (int)mm.channel());
270 2567 : if (maxport < mm.port()) {
271 296 : maxport = mm.port();
272 : }
273 : }
274 :
275 7432 : for (Part* part : parts()) {
276 8911 : for (const auto& pair : part->instruments()) {
277 4501 : const Instrument* instr = pair.second;
278 4501 : const bool useDrumset = instr->useDrumset();
279 9843 : for (InstrChannel* channel : instr->channel()) {
280 5342 : doUpdateMidiMapping(maxport, occupiedMidiChannels, searchMidiMappingFrom, part, channel, useDrumset);
281 : }
282 : }
283 : }
284 :
285 3022 : return maxport;
286 3022 : }
287 :
288 5342 : void MasterScore::doUpdateMidiMapping(int& maxport, std::set<int>& occupiedMidiChannels, unsigned int& searchMidiMappingFrom,
289 : Part* part, InstrChannel* channel, bool useDrumset)
290 : {
291 5342 : bool channelExists = false;
292 33947 : for (const MidiMapping& mapping : m_midiMapping) {
293 31620 : const bool validChannelIndex = channel->channel() >= 0 && channel->channel() < static_cast<int>(m_midiMapping.size());
294 31620 : if (channel == mapping.m_masterChannel && validChannelIndex) {
295 3015 : channelExists = true;
296 3015 : break;
297 : }
298 : }
299 :
300 5342 : if (!channelExists) {
301 : int midiPort;
302 : int midiChannel;
303 :
304 2327 : if (useDrumset) {
305 45 : midiPort = getNextFreeDrumMidiMapping(occupiedMidiChannels) / 16;
306 45 : midiChannel = 9;
307 : } else {
308 2282 : const int nm = getNextFreeMidiMapping(occupiedMidiChannels, searchMidiMappingFrom);
309 2282 : midiPort = nm / 16;
310 2282 : midiChannel = nm % 16;
311 : }
312 :
313 2327 : if (midiPort > maxport) {
314 26 : maxport = midiPort;
315 : }
316 :
317 2327 : addMidiMapping(channel, part, midiPort, midiChannel);
318 2327 : return;
319 : }
320 :
321 : // Channel could already exist, but have unassigned port or channel...
322 3015 : MidiMapping& mapping = m_midiMapping.at(channel->channel());
323 :
324 3015 : if (mapping.port() == -1) {
325 448 : const int nm = getNextFreeMidiMapping(occupiedMidiChannels, searchMidiMappingFrom, -1, mapping.channel());
326 448 : mapping.m_port = nm / 16;
327 448 : return;
328 : }
329 :
330 2567 : if (m_midiMapping[channel->channel()].channel() == -1) {
331 0 : if (useDrumset) {
332 0 : mapping.m_port = getNextFreeDrumMidiMapping(occupiedMidiChannels) / 16;
333 0 : mapping.m_channel = 9;
334 0 : return;
335 : }
336 0 : const int nm = getNextFreeMidiMapping(occupiedMidiChannels, searchMidiMappingFrom, mapping.port());
337 0 : mapping.m_port = nm / 16;
338 0 : mapping.m_channel = nm % 16;
339 : }
340 : }
341 :
342 : //---------------------------------------------------------
343 : // addMidiMapping
344 : //---------------------------------------------------------
345 :
346 3485 : void MasterScore::addMidiMapping(InstrChannel* channel, Part* part, int midiPort, int midiChannel)
347 : {
348 3485 : if (!part->score()->isMaster()) {
349 0 : return;
350 : }
351 :
352 3485 : MidiMapping mm;
353 3485 : mm.m_part = part;
354 3485 : mm.m_masterChannel = channel;
355 3485 : mm.m_articulation.reset(new InstrChannel(*channel));
356 3485 : mm.m_link = PartChannelSettingsLink(mm.articulation(), mm.m_masterChannel, /* excerpt */ false);
357 :
358 3485 : mm.m_port = midiPort;
359 3485 : mm.m_channel = midiChannel;
360 :
361 3485 : const int mscoreChannel = int(m_midiMapping.size());
362 3485 : mm.m_articulation->setChannel(mscoreChannel);
363 3485 : mm.m_masterChannel->setChannel(mscoreChannel);
364 :
365 3485 : m_midiMapping.push_back(std::move(mm));
366 3485 : }
367 :
368 : //---------------------------------------------------------
369 : // updateMidiMapping
370 : //---------------------------------------------------------
371 :
372 16 : void MasterScore::updateMidiMapping(InstrChannel* channel, Part* part, int midiPort, int midiChannel)
373 : {
374 16 : const int c = channel->channel();
375 16 : if (c < 0) {
376 0 : return;
377 : }
378 16 : if (c >= int(masterScore()->midiMapping().size())) {
379 0 : LOGD("Can't set midi channel: midiMapping is empty!");
380 0 : return;
381 : }
382 16 : MidiMapping& mm = m_midiMapping[c];
383 :
384 16 : if (midiChannel != -1) {
385 11 : mm.m_channel = midiChannel;
386 : }
387 16 : if (midiPort != -1) {
388 5 : mm.m_port = midiPort;
389 : }
390 16 : if (part) {
391 16 : mm.m_part = part->masterPart();
392 : }
393 : }
394 : } // namespace mu::engraving
|