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 "instrument.h"
24 :
25 : #include "drumset.h"
26 : #include "instrtemplate.h"
27 : #include "masterscore.h"
28 : #include "stringdata.h"
29 : #include "textbase.h"
30 :
31 : #include "log.h"
32 :
33 : using namespace mu;
34 : using namespace mu::engraving;
35 :
36 : namespace mu::engraving {
37 : //: Channel name for otherwise unnamed channels
38 : const char* InstrChannel::DEFAULT_NAME = "normal";
39 : const char* InstrChannel::HARMONY_NAME = "harmony";
40 : const char* InstrChannel::PALM_MUTE_NAME = "palmmute";
41 :
42 : Instrument InstrumentList::defaultInstrument;
43 :
44 : //---------------------------------------------------------
45 : // operator
46 : //---------------------------------------------------------
47 :
48 0 : bool MidiArticulation::operator==(const MidiArticulation& i) const
49 : {
50 0 : return (i.name == name) && (i.velocity == velocity) && (i.gateTime == gateTime);
51 : }
52 :
53 : //---------------------------------------------------------
54 : // Instrument
55 : //---------------------------------------------------------
56 :
57 5962 : Instrument::Instrument(String id)
58 : {
59 5962 : m_id = id;
60 5962 : InstrChannel* a = new InstrChannel;
61 5962 : a->setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
62 5962 : m_channel.push_back(a);
63 :
64 5962 : m_minPitchA = MIN_PITCH;
65 5962 : m_maxPitchA = MAX_PITCH;
66 5962 : m_minPitchP = MIN_PITCH;
67 5962 : m_maxPitchP = MAX_PITCH;
68 5962 : m_useDrumset = false;
69 5962 : m_drumset = 0;
70 5962 : m_singleNoteDynamics = true;
71 5962 : }
72 :
73 911 : Instrument::Instrument(const Instrument& i)
74 : {
75 911 : m_id = i.m_id;
76 911 : m_soundId = i.m_soundId;
77 911 : m_instrumentLabel = i.m_instrumentLabel;
78 911 : m_trackName = i.m_trackName;
79 911 : m_minPitchA = i.m_minPitchA;
80 911 : m_maxPitchA = i.m_maxPitchA;
81 911 : m_minPitchP = i.m_minPitchP;
82 911 : m_maxPitchP = i.m_maxPitchP;
83 911 : m_transpose = i.m_transpose;
84 911 : m_musicXmlId = i.m_musicXmlId;
85 911 : m_stringData = i.m_stringData;
86 911 : m_drumset = 0;
87 911 : setDrumset(i.m_drumset);
88 911 : m_useDrumset = i.m_useDrumset;
89 911 : m_stringData = i.m_stringData;
90 911 : m_midiActions = i.m_midiActions;
91 911 : m_articulation = i.m_articulation;
92 911 : m_singleNoteDynamics = i.m_singleNoteDynamics;
93 911 : m_channel.reserve(i.m_channel.size());
94 2030 : for (InstrChannel* c : i.m_channel) {
95 1119 : m_channel.push_back(new InstrChannel(*c));
96 : }
97 911 : m_clefType = i.m_clefType;
98 911 : m_trait = i.m_trait;
99 911 : m_glissandoStyle = i.m_glissandoStyle;
100 911 : }
101 :
102 508 : void Instrument::operator=(const Instrument& i)
103 : {
104 508 : muse::DeleteAll(m_channel);
105 508 : m_channel.clear();
106 508 : delete m_drumset;
107 :
108 508 : m_id = i.m_id;
109 508 : m_soundId = i.m_soundId;
110 508 : m_instrumentLabel = i.m_instrumentLabel;
111 508 : m_trackName = i.m_trackName;
112 508 : m_minPitchA = i.m_minPitchA;
113 508 : m_maxPitchA = i.m_maxPitchA;
114 508 : m_minPitchP = i.m_minPitchP;
115 508 : m_maxPitchP = i.m_maxPitchP;
116 508 : m_transpose = i.m_transpose;
117 508 : m_musicXmlId = i.m_musicXmlId;
118 508 : m_stringData = i.m_stringData;
119 508 : m_drumset = 0;
120 508 : setDrumset(i.m_drumset);
121 508 : m_useDrumset = i.m_useDrumset;
122 508 : m_stringData = i.m_stringData;
123 508 : m_midiActions = i.m_midiActions;
124 508 : m_articulation = i.m_articulation;
125 508 : m_singleNoteDynamics = i.m_singleNoteDynamics;
126 508 : m_channel.reserve(i.m_channel.size());
127 1188 : for (InstrChannel* c : i.m_channel) {
128 680 : m_channel.push_back(new InstrChannel(*c));
129 : }
130 508 : m_clefType = i.m_clefType;
131 508 : m_trait = i.m_trait;
132 508 : m_glissandoStyle = i.m_glissandoStyle;
133 508 : }
134 :
135 : //---------------------------------------------------------
136 : // ~Instrument
137 : //---------------------------------------------------------
138 :
139 1426 : Instrument::~Instrument()
140 : {
141 1426 : muse::DeleteAll(m_channel);
142 1426 : delete m_drumset;
143 1426 : m_drumset = nullptr;
144 1426 : }
145 :
146 915 : String Instrument::recognizeMusicXmlId() const
147 : {
148 : // Return a MusicXML "Sound ID", which is essentially an instrument ID.
149 : // See https://github.com/w3c/musicxml/blob/gh-pages/schema/sounds.xml
150 915 : static const String defaultMusicXmlId(u"keyboard.piano");
151 915 : static const String defaultMusicXmlPercussionId(u"drum.group"); // our General MIDI Percussion
152 :
153 915 : std::vector<String> nameList;
154 915 : nameList.reserve(3);
155 :
156 915 : nameList.push_back(m_trackName);
157 915 : nameList.push_back(m_instrumentLabel.longName());
158 915 : nameList.push_back(m_instrumentLabel.shortName());
159 :
160 915 : const InstrumentTemplate* tmplByName = mu::engraving::searchTemplateForInstrNameList(nameList, m_useDrumset);
161 :
162 915 : if (tmplByName && !tmplByName->musicXmlId.isEmpty()) {
163 571 : return tmplByName->musicXmlId;
164 : }
165 :
166 344 : const InstrChannel* channel = this->channel(0);
167 :
168 344 : if (!channel) {
169 0 : return defaultMusicXmlId;
170 : }
171 :
172 344 : const InstrumentTemplate* tmplMidiProgram = mu::engraving::searchTemplateForMidiProgram(channel->bank(), channel->program(),
173 344 : m_useDrumset);
174 :
175 344 : if (tmplMidiProgram && !tmplMidiProgram->musicXmlId.isEmpty()) {
176 321 : return tmplMidiProgram->musicXmlId;
177 : }
178 :
179 23 : return m_useDrumset ? defaultMusicXmlPercussionId : defaultMusicXmlId;
180 915 : }
181 :
182 1297 : String Instrument::recognizeId() const
183 : {
184 : // When reading a score created with pre-3.6, MuseScore's instrument ID
185 : // isn't saved in the score file, so we must try to guess the ID based on
186 : // the MusicXML ID, which is saved. However, MusicXML IDs are not unique,
187 : // so we must also consider other data to find the best match, preferring
188 : // more generic instruments when there's a tie.
189 :
190 : // In these cases, the best and/or most generic match is already known,
191 : // and we don't want to risk returning something else.
192 1297 : if (m_musicXmlId == u"drum.group") {
193 4 : return u"percussion-synthesizer"; // General MIDI Percussion (most generic kit)
194 1293 : } else if (m_musicXmlId == u"drum.group.set") {
195 170 : return u"drumset"; // Large Drum Kit (most generic drum kit)
196 1123 : } else if (m_musicXmlId.startsWith(u"mdl.")) {
197 : // Use fixed mapping for MDL1 instruments to ensure we get the
198 : // marching versions (e.g. "marching-snare" and not "snare-drum").
199 : // See https://github.com/musescore/mdl/blob/master/resources/instruments/mdl_1_3_0.xml
200 0 : if (m_musicXmlId == u"mdl.drum.snare-drum") {
201 0 : return u"marching-snare";
202 0 : } else if (m_musicXmlId == u"mdl.drum.tenor-drum") {
203 0 : return u"marching-tenor-drums";
204 0 : } else if (m_musicXmlId == u"mdl.drum.bass-drum") {
205 0 : return u"marching-bass-drums";
206 0 : } else if (m_musicXmlId == u"mdl.metal.cymbal.crash") {
207 0 : return u"marching-cymbals";
208 0 : } else if (m_musicXmlId == u"mdl.drum.group.set") {
209 0 : return u"drumset";
210 : }
211 : }
212 :
213 : // Several instruments have MusicXML ID "strings.group". Let's use the
214 : // value of controller 32 of the "arco" channel to distinguish them.
215 1123 : const String arco = String(u"arco");
216 1123 : const bool groupHack = m_musicXmlId == String(u"strings.group");
217 1123 : const int idxref = channelIdx(arco);
218 1123 : const int val32ref = (idxref < 0) ? -1 : channel(idxref)->bank();
219 :
220 : // For other instruments, consider how closely the instrument data
221 : // matches each of our templates. Use the ID that gives the best match.
222 1123 : String fallback; // ID that gave the best match so far
223 1123 : int bestMatchStrength = 0; // higher when ID is a better match
224 :
225 14599 : for (const InstrumentGroup* g : instrumentGroups) {
226 716183 : for (const InstrumentTemplate* it : g->instrumentTemplates) {
227 703186 : if (it->musicXmlId != m_musicXmlId) {
228 699731 : continue;
229 : }
230 3455 : if (groupHack) {
231 5 : if (fallback.isEmpty()) {
232 : // Instrument "Strings" doesn't have bank defined so
233 : // if no "strings.group" instrument with requested bank
234 : // is found, assume "Strings".
235 1 : fallback = it->id;
236 : }
237 20 : for (const InstrChannel& chan : it->channel) {
238 15 : if ((chan.name() == arco) && (chan.bank() == val32ref)) {
239 0 : return it->id;
240 : }
241 : }
242 : } else {
243 : int matchStrength = 0
244 3450 : + ((minPitchP() == it->minPitchP) ? 1 : 0)
245 3450 : + ((minPitchA() == it->minPitchA) ? 1 : 0)
246 3450 : + ((maxPitchA() == it->maxPitchA) ? 1 : 0)
247 3450 : + ((maxPitchP() == it->maxPitchP) ? 1 : 0);
248 3450 : const int perfectMatchStrength = 4;
249 3450 : assert(matchStrength <= perfectMatchStrength);
250 3450 : if (fallback.isEmpty() || matchStrength > bestMatchStrength) {
251 : // Set a fallback ID or update it because we've found a better one.
252 1225 : fallback = it->id;
253 1225 : bestMatchStrength = matchStrength;
254 1225 : if (bestMatchStrength == perfectMatchStrength) {
255 479 : break; // stop looking for matches
256 : }
257 2225 : } else if ((matchStrength == bestMatchStrength) && (it->id.size() < fallback.size())) {
258 : // Update fallback ID because we've found a shorter one that is equally good.
259 : // Shorter IDs tend to correspond to more generic instruments (e.g. "piano"
260 : // vs. "grand-piano") so it's better to use a shorter one if unsure.
261 121 : fallback = it->id;
262 : }
263 : }
264 : }
265 : }
266 :
267 1123 : if (!fallback.isEmpty()) {
268 1123 : return fallback;
269 : }
270 :
271 0 : return m_useDrumset
272 : ? u"percussion-synthesizer" // General MIDI Percussion (most generic kit)
273 0 : : u"piano";
274 1123 : }
275 :
276 97 : int Instrument::recognizeMidiProgram() const
277 : {
278 97 : const InstrumentTemplate* tp = searchTemplate(m_id);
279 :
280 97 : if (tp && !tp->channel.empty() && tp->channel[0].program() >= 0) {
281 97 : return tp->channel[0].program();
282 : }
283 :
284 0 : return 0;
285 : }
286 :
287 : //---------------------------------------------------------
288 : // action
289 : //---------------------------------------------------------
290 :
291 0 : NamedEventList* Instrument::midiAction(const String& s, int channelIdx) const
292 : {
293 : // first look in channel list
294 :
295 0 : for (const NamedEventList& a : m_channel[channelIdx]->midiActions) {
296 0 : if (s == a.name) {
297 0 : return const_cast<NamedEventList*>(&a);
298 : }
299 : }
300 :
301 0 : for (const NamedEventList& a : m_midiActions) {
302 0 : if (s == a.name) {
303 0 : return const_cast<NamedEventList*>(&a);
304 : }
305 : }
306 0 : return 0;
307 : }
308 :
309 : //---------------------------------------------------------
310 : // Channel
311 : //---------------------------------------------------------
312 :
313 20355 : InstrChannel::InstrChannel()
314 : {
315 162840 : for (int i = 0; i < int(A::INIT_COUNT); ++i) {
316 142485 : m_init.push_back(MidiCoreEvent());
317 : }
318 20355 : m_synti = u"Fluid"; // default synthesizer
319 20355 : m_channel = -1;
320 20355 : m_program = -1;
321 20355 : m_bank = 0;
322 20355 : m_volume = defaultVolume;
323 20355 : m_pan = 64; // actually 63.5 for center
324 20355 : m_chorus = 0;
325 20355 : m_reverb = 0;
326 20355 : m_color = DEFAULT_COLOR;
327 20355 : }
328 :
329 : //---------------------------------------------------------
330 : // initList
331 : //---------------------------------------------------------
332 :
333 1966 : std::vector<MidiCoreEvent>& InstrChannel::initList() const
334 : {
335 1966 : if (m_mustUpdateInit) {
336 1787 : updateInitList();
337 1787 : m_mustUpdateInit = false;
338 : }
339 1966 : return m_init;
340 : }
341 :
342 : //---------------------------------------------------------
343 : // setVolume
344 : //---------------------------------------------------------
345 :
346 962 : void InstrChannel::setVolume(char value)
347 : {
348 962 : if (m_volume != value) {
349 434 : m_volume = value;
350 434 : firePropertyChanged(Prop::VOLUME);
351 : }
352 962 : m_mustUpdateInit = true;
353 962 : }
354 :
355 : //---------------------------------------------------------
356 : // setPan
357 : //---------------------------------------------------------
358 :
359 850 : void InstrChannel::setPan(char value)
360 : {
361 850 : if (m_pan != value) {
362 595 : m_pan = value;
363 595 : firePropertyChanged(Prop::PAN);
364 : }
365 850 : m_mustUpdateInit = true;
366 850 : }
367 :
368 : //---------------------------------------------------------
369 : // setChorus
370 : //---------------------------------------------------------
371 :
372 168 : void InstrChannel::setChorus(char value)
373 : {
374 168 : if (m_chorus != value) {
375 81 : m_chorus = value;
376 81 : firePropertyChanged(Prop::CHORUS);
377 : }
378 168 : m_mustUpdateInit = true;
379 168 : }
380 :
381 : //---------------------------------------------------------
382 : // setReverb
383 : //---------------------------------------------------------
384 :
385 168 : void InstrChannel::setReverb(char value)
386 : {
387 168 : if (m_reverb != value) {
388 80 : m_reverb = value;
389 80 : firePropertyChanged(Prop::REVERB);
390 : }
391 168 : m_mustUpdateInit = true;
392 168 : }
393 :
394 : //---------------------------------------------------------
395 : // setName
396 : //---------------------------------------------------------
397 :
398 30312 : void InstrChannel::setName(const String& value)
399 : {
400 30312 : if (m_name != value) {
401 20413 : m_name = value;
402 20413 : firePropertyChanged(Prop::NAME);
403 : }
404 30312 : }
405 :
406 : //---------------------------------------------------------
407 : // setSynti
408 : //---------------------------------------------------------
409 :
410 811 : void InstrChannel::setSynti(const String& value)
411 : {
412 811 : if (m_synti != value) {
413 0 : m_synti = value;
414 0 : firePropertyChanged(Prop::SYNTI);
415 : }
416 811 : }
417 :
418 : //---------------------------------------------------------
419 : // setColor
420 : //---------------------------------------------------------
421 :
422 0 : void InstrChannel::setColor(int value)
423 : {
424 0 : if (m_color != value) {
425 0 : m_color = value;
426 0 : firePropertyChanged(Prop::COLOR);
427 : }
428 0 : }
429 :
430 : //---------------------------------------------------------
431 : // setProgram
432 : //---------------------------------------------------------
433 :
434 16022 : void InstrChannel::setProgram(int value)
435 : {
436 16022 : if (m_program != value) {
437 15396 : m_program = value;
438 15396 : firePropertyChanged(Prop::PROGRAM);
439 : }
440 16022 : m_mustUpdateInit = true;
441 16022 : }
442 :
443 : //---------------------------------------------------------
444 : // setBank
445 : //---------------------------------------------------------
446 :
447 4209 : void InstrChannel::setBank(int value)
448 : {
449 4209 : if (m_bank != value) {
450 2552 : m_bank = value;
451 2552 : firePropertyChanged(Prop::BANK);
452 : }
453 4209 : m_mustUpdateInit = true;
454 4209 : }
455 :
456 : //---------------------------------------------------------
457 : // setChannel
458 : //---------------------------------------------------------
459 :
460 11564 : void InstrChannel::setChannel(int value)
461 : {
462 11564 : if (m_channel != value) {
463 7692 : m_channel = value;
464 7692 : firePropertyChanged(Prop::CHANNEL);
465 : }
466 11564 : }
467 :
468 : //---------------------------------------------------------
469 : // setUserBankController
470 : //---------------------------------------------------------
471 :
472 2648 : void InstrChannel::setUserBankController(bool val)
473 : {
474 2648 : if (m_userBankController != val) {
475 2579 : m_userBankController = val;
476 2579 : firePropertyChanged(Prop::USER_BANK_CONTROL);
477 : }
478 2648 : }
479 :
480 : //---------------------------------------------------------
481 : // switchExpressive
482 : // Switches channel from non-expressive to expressive patch or vice versa
483 : // This works only with MuseScore General soundfont
484 : //---------------------------------------------------------
485 :
486 0 : void InstrChannel::switchExpressive(Synthesizer* synth, bool expressive, bool force /* = false */)
487 : {
488 : UNUSED(synth);
489 : UNUSED(expressive);
490 : UNUSED(force);
491 : //! TODO Needs poting to MU4
492 0 : NOT_IMPLEMENTED;
493 :
494 : // if ((_userBankController && !force) || !synth) {
495 : // return;
496 : // }
497 :
498 : // // Don't try to switch if we already have done so
499 : // if (expressive == _switchedToExpressive) {
500 : // return;
501 : // }
502 :
503 : // // Check that we're actually changing the MuseScore General soundfont
504 : // const auto fontsInfo = synth->soundFontsInfo();
505 : // if (fontsInfo.empty()) {
506 : // return;
507 : // }
508 : // const auto& info = fontsInfo.front();
509 : // if (!info.fontName.contains("MS Basic", Qt::CaseInsensitive)) {
510 : // LOGD().nospace() << "Soundfont '" << info.fontName << "' is not MuseScore General, cannot update expressive";
511 : // return;
512 : // }
513 :
514 : // // Work out where the new expressive patch will be
515 : // // All expressive instruments are +1 bank higher than the
516 : // // normal counterparts, except on bank 0, where they are placed on bank 17
517 : // // and on bank 8, which uses bank 18 instead.
518 : // int searchBankNum;
519 : // int newBankNum;
520 : // if (expressive) {
521 : // int relativeBank = bank() % 129;
522 : // if (relativeBank == 0) {
523 : // newBankNum = 17;
524 : // } else if (relativeBank == 8) {
525 : // newBankNum = 18;
526 : // } else {
527 : // newBankNum = relativeBank + 1;
528 : // }
529 : // _switchedToExpressive = true;
530 : // } else {
531 : // int relativeBank = bank() % 129;
532 : // if (relativeBank == 17) {
533 : // newBankNum = 0;
534 : // } else if (relativeBank == 18) {
535 : // newBankNum = 8;
536 : // } else {
537 : // newBankNum = relativeBank - 1;
538 : // }
539 : // _switchedToExpressive = false;
540 : // }
541 :
542 : // // Floor bank num to multiple of 129 and add new num to get bank num of new patch
543 : // searchBankNum = (bank() / 129) * 129 + newBankNum;
544 : // const auto& pl = synth->getPatchInfo();
545 : // for (const MidiPatch* p : pl) {
546 : // if (p->synti == "Fluid") {
547 : // if (searchBankNum == p->bank && program() == p->prog) {
548 : // setBank(p->bank);
549 : // return;
550 : // }
551 : // }
552 : // }
553 0 : }
554 :
555 : //---------------------------------------------------------
556 : // updateInitList
557 : //---------------------------------------------------------
558 :
559 1787 : void InstrChannel::updateInitList() const
560 : {
561 1787 : MidiCoreEvent e;
562 1787 : if (m_program != -1) {
563 1704 : e.setType(ME_CONTROLLER);
564 1704 : e.setDataA(CTRL_PROGRAM);
565 1704 : e.setDataB(m_program);
566 1704 : m_init[int(A::PROGRAM)] = e;
567 : }
568 :
569 1787 : e.setData(ME_CONTROLLER, CTRL_HBANK, (m_bank >> 7) & 0x7f);
570 1787 : m_init[int(A::HBANK)] = e;
571 :
572 1787 : e.setData(ME_CONTROLLER, CTRL_LBANK, m_bank & 0x7f);
573 1787 : m_init[int(A::LBANK)] = e;
574 :
575 1787 : e.setData(ME_CONTROLLER, CTRL_VOLUME, volume());
576 1787 : m_init[int(A::VOLUME)] = e;
577 :
578 1787 : e.setData(ME_CONTROLLER, CTRL_PANPOT, pan());
579 1787 : m_init[int(A::PAN)] = e;
580 :
581 1787 : e.setData(ME_CONTROLLER, CTRL_CHORUS_SEND, chorus());
582 1787 : m_init[int(A::CHORUS)] = e;
583 :
584 1787 : e.setData(ME_CONTROLLER, CTRL_REVERB_SEND, reverb());
585 1787 : m_init[int(A::REVERB)] = e;
586 1787 : }
587 :
588 : //---------------------------------------------------------
589 : // addListener
590 : //---------------------------------------------------------
591 :
592 8827 : void InstrChannel::addListener(ChannelListener* l)
593 : {
594 8827 : m_notifier.addListener(l);
595 8827 : }
596 :
597 : //---------------------------------------------------------
598 : // removeListener
599 : //---------------------------------------------------------
600 :
601 0 : void InstrChannel::removeListener(ChannelListener* l)
602 : {
603 0 : m_notifier.removeListener(l);
604 0 : }
605 :
606 : //---------------------------------------------------------
607 : // PartChannelSettingsLink
608 : //---------------------------------------------------------
609 :
610 8827 : PartChannelSettingsLink::PartChannelSettingsLink(InstrChannel* main, InstrChannel* bound, bool excerpt)
611 8827 : : m_main(main), m_bound(bound), m_excerpt(excerpt)
612 : {
613 : // Maybe it would be good to assign common properties if the link
614 : // is constructed in non-excerpt mode. But it is not currently
615 : // necessary as playback channels are currently recreated on each
616 : // MIDI remapping.
617 :
618 8827 : main->addListener(this);
619 8827 : }
620 :
621 : //---------------------------------------------------------
622 : // PartChannelSettingsLink
623 : //---------------------------------------------------------
624 :
625 7536 : PartChannelSettingsLink::PartChannelSettingsLink(PartChannelSettingsLink&& other)
626 : : ChannelListener(), // swap() will set the notifier instead
627 7536 : m_main(nullptr), m_bound(nullptr), m_excerpt(false)
628 : {
629 7536 : swap(*this, other);
630 7536 : }
631 :
632 : //---------------------------------------------------------
633 : // PartChannelSettingsLink::operator=
634 : //---------------------------------------------------------
635 :
636 3794 : PartChannelSettingsLink& PartChannelSettingsLink::operator=(PartChannelSettingsLink&& other)
637 : {
638 3794 : if (this != &other) {
639 3794 : swap(*this, other);
640 : }
641 3794 : return *this;
642 : }
643 :
644 : //---------------------------------------------------------
645 : // swap
646 : //---------------------------------------------------------
647 :
648 11330 : void swap(PartChannelSettingsLink& l1, PartChannelSettingsLink& l2)
649 : {
650 11330 : mu::engraving::swap(static_cast<ChannelListener&>(l1), static_cast<ChannelListener&>(l2));
651 : using std::swap;
652 11330 : swap(l1.m_main, l2.m_main);
653 11330 : swap(l1.m_bound, l2.m_bound);
654 11330 : swap(l1.m_excerpt, l2.m_excerpt);
655 11330 : }
656 :
657 : //---------------------------------------------------------
658 : // PartChannelSettingsLink::applyProperty
659 : //---------------------------------------------------------
660 :
661 3792 : void PartChannelSettingsLink::applyProperty(InstrChannel::Prop p, const InstrChannel* from, InstrChannel* to)
662 : {
663 3792 : switch (p) {
664 0 : case InstrChannel::Prop::VOLUME:
665 0 : to->setVolume(from->volume());
666 0 : break;
667 0 : case InstrChannel::Prop::PAN:
668 0 : to->setPan(from->pan());
669 0 : break;
670 0 : case InstrChannel::Prop::CHORUS:
671 0 : to->setChorus(from->chorus());
672 0 : break;
673 0 : case InstrChannel::Prop::REVERB:
674 0 : to->setReverb(from->reverb());
675 0 : break;
676 0 : case InstrChannel::Prop::NAME:
677 0 : to->setName(from->name());
678 0 : break;
679 0 : case InstrChannel::Prop::PROGRAM:
680 0 : to->setProgram(from->program());
681 0 : break;
682 0 : case InstrChannel::Prop::BANK:
683 0 : to->setBank(from->bank());
684 0 : break;
685 0 : case InstrChannel::Prop::COLOR:
686 0 : to->setColor(from->color());
687 0 : break;
688 0 : case InstrChannel::Prop::SYNTI:
689 0 : to->setSynti(from->synti());
690 0 : break;
691 3792 : case InstrChannel::Prop::CHANNEL:
692 3792 : to->setChannel(from->channel());
693 3792 : break;
694 0 : case InstrChannel::Prop::USER_BANK_CONTROL:
695 0 : to->setUserBankController(from->userBankController());
696 0 : break;
697 : }
698 3792 : }
699 :
700 : //---------------------------------------------------------
701 : // PartChannelSettingsLink::propertyChanged
702 : //---------------------------------------------------------
703 :
704 3792 : void PartChannelSettingsLink::propertyChanged(InstrChannel::Prop p)
705 : {
706 3792 : applyProperty(p, m_main, m_bound);
707 3792 : }
708 :
709 : //---------------------------------------------------------
710 : // channelIdx
711 : //---------------------------------------------------------
712 :
713 3286 : int Instrument::channelIdx(const String& s) const
714 : {
715 3286 : int idx = 0;
716 7107 : for (const InstrChannel* a : m_channel) {
717 3904 : if (a->name().isEmpty() && s == InstrChannel::DEFAULT_NAME) {
718 83 : return idx;
719 : }
720 3904 : if (s == a->name()) {
721 83 : return idx;
722 : }
723 3821 : ++idx;
724 : }
725 3203 : return -1;
726 : }
727 :
728 : //---------------------------------------------------------
729 : // getVelocityMultiplier
730 : //---------------------------------------------------------
731 :
732 0 : double Instrument::getVelocityMultiplier(const String& name) const
733 : {
734 0 : for (const MidiArticulation& a : m_articulation) {
735 0 : if (a.name == name) {
736 0 : return double(a.velocity) / 100;
737 : }
738 : }
739 0 : return 1;
740 : }
741 :
742 : //---------------------------------------------------------
743 : // updateGateTime
744 : //---------------------------------------------------------
745 :
746 319 : void Instrument::updateGateTime(int* gateTime, const String& name) const
747 : {
748 1187 : for (const MidiArticulation& a : m_articulation) {
749 981 : if (a.name == name) {
750 : // Imagine ["staccato", "accent"] articulations
751 : // accent will override the gate time,
752 : // so we have to take the minimum value from all articulations
753 113 : *gateTime = std::min(*gateTime, a.gateTime);
754 113 : break;
755 : }
756 : }
757 319 : }
758 :
759 : //---------------------------------------------------------
760 : // switchExpressive
761 : //---------------------------------------------------------
762 :
763 0 : void Instrument::switchExpressive(MasterScore* score, Synthesizer* synth, bool expressive, bool force /* = false */)
764 : {
765 : // Only switch to expressive where necessary
766 0 : if (!synth || (expressive && !singleNoteDynamics())) {
767 0 : return;
768 : }
769 :
770 0 : for (InstrChannel* c : channel()) {
771 0 : c->switchExpressive(synth, expressive, force);
772 0 : if (score->playbackChannel(c)) {
773 0 : score->playbackChannel(c)->switchExpressive(synth, expressive, force);
774 : }
775 : }
776 : }
777 :
778 4334 : bool Instrument::isVocalInstrument() const
779 : {
780 4334 : String instrumentFamily = family();
781 8668 : return instrumentFamily == u"voices" || instrumentFamily == u"voice-groups";
782 4334 : }
783 :
784 387 : bool Instrument::isNormallyMultiStaveInstrument() const
785 : {
786 387 : String instrumentFamily = family();
787 387 : return instrumentFamily == u"keyboards"
788 66 : || instrumentFamily == u"organs"
789 66 : || instrumentFamily == u"keyboard-percussion"
790 66 : || instrumentFamily == u"harps"
791 840 : || instrumentFamily == u"accordions";
792 387 : }
793 :
794 1879 : GlissandoStyle Instrument::glissandoStyle() const
795 : {
796 1879 : return m_glissandoStyle;
797 : }
798 :
799 17 : void Instrument::setGlissandoStyle(GlissandoStyle style)
800 : {
801 17 : m_glissandoStyle = style;
802 17 : }
803 :
804 : //---------------------------------------------------------
805 : // operator==
806 : //---------------------------------------------------------
807 :
808 0 : bool Instrument::operator==(const Instrument& i) const
809 : {
810 0 : bool equal = i.m_instrumentLabel == m_instrumentLabel;
811 0 : equal &= i.m_trackName == m_trackName;
812 0 : equal &= i.m_id == m_id;
813 0 : equal &= i.m_soundId == m_soundId;
814 0 : equal &= i.m_musicXmlId == m_musicXmlId;
815 :
816 0 : equal &= i.m_minPitchA == m_minPitchA;
817 0 : equal &= i.m_maxPitchA == m_maxPitchA;
818 0 : equal &= i.m_minPitchP == m_minPitchP;
819 0 : equal &= i.m_maxPitchP == m_maxPitchP;
820 0 : equal &= i.m_transpose.diatonic == m_transpose.diatonic;
821 0 : equal &= i.m_transpose.chromatic == m_transpose.chromatic;
822 :
823 0 : equal &= i.m_useDrumset == m_useDrumset;
824 0 : if (i.m_drumset && m_drumset) {
825 0 : equal &= *i.m_drumset == *m_drumset;
826 : } else {
827 0 : equal &= i.m_drumset == m_drumset;
828 : }
829 0 : equal &= *i.stringData() == *stringData();
830 :
831 0 : equal &= i.m_midiActions == m_midiActions;
832 0 : equal &= i.m_articulation == m_articulation;
833 :
834 0 : if (i.m_channel.size() == m_channel.size()) {
835 0 : for (size_t cur = 0; cur < m_channel.size(); cur++) {
836 0 : if (*i.m_channel[cur] != *m_channel[cur]) {
837 0 : return false;
838 : }
839 : }
840 : } else {
841 0 : return false;
842 : }
843 :
844 0 : equal &= i.m_clefType == m_clefType;
845 :
846 0 : equal &= i.m_singleNoteDynamics == m_singleNoteDynamics;
847 :
848 0 : return equal;
849 : }
850 :
851 0 : bool Instrument::operator!=(const Instrument& i) const
852 : {
853 0 : return !(*this == i);
854 : }
855 :
856 : //---------------------------------------------------------
857 : // isDifferentInstrument
858 : /// Checks if the passed instrument is a different instrument.
859 : /// Does not compare channels.
860 : //---------------------------------------------------------
861 :
862 0 : bool Instrument::isDifferentInstrument(const Instrument& i) const
863 : {
864 0 : return this->operator!=(i);
865 : }
866 :
867 4721 : String Instrument::family() const
868 : {
869 4721 : static const String NO_FAMILY = u"-";
870 :
871 4721 : if (m_familyCache.empty()) {
872 622 : auto search = searchTemplateIndexForId(m_id);
873 :
874 622 : if (search.instrTemplate) {
875 622 : m_familyCache = search.instrTemplate->familyId();
876 : } else {
877 0 : m_familyCache = NO_FAMILY;
878 : }
879 : }
880 :
881 4721 : return m_familyCache == NO_FAMILY ? String() : m_familyCache;
882 : }
883 :
884 869 : String Instrument::group() const
885 : {
886 869 : static const String NO_GROUP = u"-";
887 :
888 869 : if (m_groupCache.empty()) {
889 126 : auto search = searchTemplateIndexForId(m_id);
890 :
891 126 : if (search.instrTemplate) {
892 121 : m_groupCache = search.instrTemplate->groupId;
893 : } else {
894 5 : m_groupCache = NO_GROUP;
895 : }
896 : }
897 :
898 869 : return m_groupCache == NO_GROUP ? String() : m_groupCache;
899 : }
900 :
901 : //---------------------------------------------------------
902 : // setUseDrumset
903 : //---------------------------------------------------------
904 :
905 397 : void Instrument::setUseDrumset(bool val)
906 : {
907 397 : m_useDrumset = val;
908 397 : if (val && !m_drumset) {
909 184 : m_drumset = new Drumset(*smDrumset);
910 : }
911 397 : }
912 :
913 : //---------------------------------------------------------
914 : // setDrumset
915 : //---------------------------------------------------------
916 :
917 1649 : void Instrument::setDrumset(const Drumset* ds)
918 : {
919 1649 : delete m_drumset;
920 1649 : if (ds) {
921 273 : m_useDrumset = true;
922 273 : m_drumset = new Drumset(*ds);
923 : } else {
924 1376 : m_useDrumset = false;
925 1376 : m_drumset = 0;
926 : }
927 1649 : }
928 :
929 2775 : size_t Instrument::cleffTypeCount() const
930 : {
931 2775 : return m_clefType.size();
932 : }
933 :
934 : //---------------------------------------------------------
935 : // clefType
936 : //---------------------------------------------------------
937 :
938 1090 : ClefTypeList Instrument::clefType(size_t staffIdx) const
939 : {
940 1090 : if (staffIdx >= m_clefType.size()) {
941 49 : if (m_clefType.empty()) {
942 45 : return ClefTypeList();
943 : }
944 4 : return m_clefType[0];
945 : }
946 1041 : return m_clefType[staffIdx];
947 : }
948 :
949 : //---------------------------------------------------------
950 : // setClefType
951 : //---------------------------------------------------------
952 :
953 1978 : void Instrument::setClefType(size_t staffIdx, const ClefTypeList& c)
954 : {
955 4310 : while (m_clefType.size() <= staffIdx) {
956 2332 : m_clefType.push_back(ClefTypeList());
957 : }
958 1978 : m_clefType[staffIdx] = c;
959 1978 : }
960 :
961 : //---------------------------------------------------------
962 : // minPitchP
963 : //---------------------------------------------------------
964 :
965 6385 : int Instrument::minPitchP() const
966 : {
967 6385 : return m_minPitchP;
968 : }
969 :
970 : //---------------------------------------------------------
971 : // maxPitchP
972 : //---------------------------------------------------------
973 :
974 6385 : int Instrument::maxPitchP() const
975 : {
976 6385 : return m_maxPitchP;
977 : }
978 :
979 : //---------------------------------------------------------
980 : // minPitchA
981 : //---------------------------------------------------------
982 :
983 6385 : int Instrument::minPitchA() const
984 : {
985 6385 : return m_minPitchA;
986 : }
987 :
988 : //---------------------------------------------------------
989 : // maxPitchA
990 : //---------------------------------------------------------
991 :
992 6385 : int Instrument::maxPitchA() const
993 : {
994 6385 : return m_maxPitchA;
995 : }
996 :
997 : //---------------------------------------------------------
998 : // musicXmlId
999 : //---------------------------------------------------------
1000 :
1001 4508 : String Instrument::musicXmlId() const
1002 : {
1003 4508 : return m_musicXmlId;
1004 : }
1005 :
1006 : //---------------------------------------------------------
1007 : // instrument
1008 : //---------------------------------------------------------
1009 :
1010 39611 : const Instrument* InstrumentList::instrument(int tick) const
1011 : {
1012 39611 : if (empty()) {
1013 0 : return &defaultInstrument;
1014 : }
1015 39611 : auto i = upper_bound(tick);
1016 39611 : if (i == begin()) {
1017 0 : return &defaultInstrument;
1018 : }
1019 39611 : --i;
1020 39611 : return i->second;
1021 : }
1022 :
1023 : //---------------------------------------------------------
1024 : // instrument
1025 : //---------------------------------------------------------
1026 :
1027 795362 : Instrument* InstrumentList::instrument(int tick)
1028 : {
1029 795362 : if (empty()) {
1030 0 : return &defaultInstrument;
1031 : }
1032 795362 : auto i = upper_bound(tick);
1033 795362 : if (i == begin()) {
1034 0 : return &defaultInstrument;
1035 : }
1036 795362 : --i;
1037 795362 : return i->second;
1038 : }
1039 :
1040 : //---------------------------------------------------------
1041 : // setInstrument
1042 : //---------------------------------------------------------
1043 :
1044 5513 : void InstrumentList::setInstrument(Instrument* instr, int tick)
1045 : {
1046 5513 : if (!insert({ tick, instr }).second) {
1047 2398 : (*this)[tick] = instr;
1048 : }
1049 5513 : }
1050 :
1051 41 : bool InstrumentList::contains(const String& instrumentId) const
1052 : {
1053 41 : for (const auto& pair : *this) {
1054 41 : if (pair.second->id() == instrumentId) {
1055 41 : return true;
1056 : }
1057 : }
1058 :
1059 0 : return false;
1060 : }
1061 :
1062 : //---------------------------------------------------------
1063 : // trackName
1064 : //---------------------------------------------------------
1065 :
1066 6010 : String Instrument::trackName() const
1067 : {
1068 6010 : return m_trackName;
1069 : }
1070 :
1071 3600 : void Instrument::setTrackName(const String& s)
1072 : {
1073 3600 : m_trackName = s;
1074 3600 : }
1075 :
1076 0 : String Instrument::nameAsXmlText() const
1077 : {
1078 0 : return m_instrumentLabel.longName();
1079 : }
1080 :
1081 104 : String Instrument::nameAsPlainText() const
1082 : {
1083 104 : return TextBase::unEscape(m_instrumentLabel.longName());
1084 : }
1085 :
1086 0 : String Instrument::abbreviatureAsXmlText() const
1087 : {
1088 0 : return m_instrumentLabel.shortName();
1089 : }
1090 :
1091 103 : String Instrument::abbreviatureAsPlainText() const
1092 : {
1093 103 : return TextBase::unEscape(m_instrumentLabel.shortName());
1094 : }
1095 :
1096 799 : Instrument Instrument::fromTemplate(const InstrumentTemplate* templ)
1097 : {
1098 799 : Instrument instrument(templ->id);
1099 799 : instrument.setSoundId(templ->soundId);
1100 799 : instrument.setAmateurPitchRange(templ->minPitchA, templ->maxPitchA);
1101 799 : instrument.setProfessionalPitchRange(templ->minPitchP, templ->maxPitchP);
1102 799 : instrument.setInstrumentName(templ->instrumentName);
1103 :
1104 799 : instrument.setTrackName(templ->trackName);
1105 799 : instrument.setTranspose(templ->transpose);
1106 799 : instrument.setMusicXmlId(templ->musicXmlId);
1107 799 : instrument.m_useDrumset = templ->useDrumset;
1108 :
1109 799 : if (templ->useDrumset) {
1110 27 : instrument.setDrumset(templ->drumset ? templ->drumset : smDrumset);
1111 : }
1112 :
1113 2095 : for (staff_idx_t i = 0; i < templ->staffCount; ++i) {
1114 1296 : instrument.setClefType(i, templ->clefTypes[i]);
1115 : }
1116 :
1117 799 : instrument.setMidiActions(templ->midiActions);
1118 799 : instrument.setArticulation(templ->midiArticulations);
1119 799 : instrument.m_channel.clear();
1120 :
1121 1790 : for (const InstrChannel& c : templ->channel) {
1122 991 : instrument.m_channel.push_back(new InstrChannel(c));
1123 : }
1124 :
1125 799 : if (instrument.m_channel.empty()) {
1126 0 : InstrChannel* fallbackChannel = new InstrChannel;
1127 0 : fallbackChannel->setName(String::fromUtf8(InstrChannel::DEFAULT_NAME));
1128 0 : fallbackChannel->setProgram(instrument.recognizeMidiProgram());
1129 0 : instrument.m_channel.push_back(fallbackChannel);
1130 : }
1131 :
1132 799 : instrument.setStringData(templ->stringData);
1133 799 : instrument.setSingleNoteDynamics(templ->singleNoteDynamics);
1134 799 : instrument.setTrait(templ->trait);
1135 799 : instrument.m_glissandoStyle = templ->glissandoStyle;
1136 :
1137 799 : return instrument;
1138 0 : }
1139 :
1140 478 : Trait Instrument::trait() const
1141 : {
1142 478 : return m_trait;
1143 : }
1144 :
1145 1277 : void Instrument::setTrait(const Trait& trait)
1146 : {
1147 1277 : m_trait = trait;
1148 1277 : }
1149 :
1150 19 : bool Instrument::isPrimary() const
1151 : {
1152 19 : return m_isPrimary;
1153 : }
1154 :
1155 16037 : void Instrument::setIsPrimary(bool isPrimary)
1156 : {
1157 16037 : m_isPrimary = isPrimary;
1158 16037 : }
1159 :
1160 4150 : void Instrument::updateInstrumentId()
1161 : {
1162 4150 : if (m_musicXmlId.isEmpty()) {
1163 915 : m_musicXmlId = recognizeMusicXmlId();
1164 : }
1165 :
1166 4150 : if (m_id.isEmpty()) {
1167 1297 : m_id = recognizeId();
1168 : }
1169 4150 : }
1170 :
1171 : //---------------------------------------------------------
1172 : // Instrument::playbackChannel
1173 : //---------------------------------------------------------
1174 :
1175 103 : const InstrChannel* Instrument::playbackChannel(int idx, const MasterScore* score) const
1176 : {
1177 103 : return score->playbackChannel(channel(idx));
1178 : }
1179 :
1180 : //---------------------------------------------------------
1181 : // Instrument::playbackChannel
1182 : //---------------------------------------------------------
1183 :
1184 0 : InstrChannel* Instrument::playbackChannel(int idx, MasterScore* score)
1185 : {
1186 0 : return score->playbackChannel(channel(idx));
1187 : }
1188 :
1189 : //---------------------------------------------------------
1190 : // getSingleNoteDynamicsFromTemplate
1191 : //---------------------------------------------------------
1192 :
1193 3295 : bool Instrument::getSingleNoteDynamicsFromTemplate() const
1194 : {
1195 3295 : String templateName = trackName().toLower().replace(u" ", u"-").replace(u"♭", u"b");
1196 3295 : const InstrumentTemplate* tp = searchTemplate(templateName);
1197 3295 : if (tp) {
1198 2458 : return tp->singleNoteDynamics;
1199 : }
1200 837 : return true;
1201 3295 : }
1202 :
1203 : //---------------------------------------------------------
1204 : // setSingleNoteDynamicsFromTemplate
1205 : //---------------------------------------------------------
1206 :
1207 1533 : void Instrument::setSingleNoteDynamicsFromTemplate()
1208 : {
1209 1533 : setSingleNoteDynamics(getSingleNoteDynamicsFromTemplate());
1210 1533 : }
1211 : }
|