LCOV - code coverage report
Current view: top level - src/engraving/dom - instrument.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 76.6 % 548 420
Test Date: 2026-11-05 04:07:02

            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              : }
        

Generated by: LCOV version 2.0-1