LCOV - code coverage report
Current view: top level - src/engraving/dom - instrchange.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 68.3 % 101 69
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 "instrchange.h"
      24              : 
      25              : #include "translation.h"
      26              : 
      27              : #include "../editing/editclef.h"
      28              : #include "../editing/editinstrumentchange.h"
      29              : #include "../editing/editkeysig.h"
      30              : #include "../editing/transaction/transaction.h"
      31              : #include "../editing/transpose.h"
      32              : 
      33              : #include "keysig.h"
      34              : #include "measure.h"
      35              : #include "mscore.h"
      36              : #include "part.h"
      37              : #include "score.h"
      38              : #include "segment.h"
      39              : #include "staff.h"
      40              : 
      41              : #include "log.h"
      42              : 
      43              : using namespace mu;
      44              : 
      45              : namespace mu::engraving {
      46              : //---------------------------------------------------------
      47              : //   instrumentChangeStyle
      48              : //---------------------------------------------------------
      49              : 
      50              : static const ElementStyle instrumentChangeStyle {
      51              :     { Sid::instrumentChangePlacement,          Pid::PLACEMENT },
      52              :     { Sid::instrumentChangeMinDistance,        Pid::MIN_DISTANCE },
      53              : };
      54              : 
      55              : //---------------------------------------------------------
      56              : //   InstrumentChange
      57              : //---------------------------------------------------------
      58              : 
      59           33 : InstrumentChange::InstrumentChange(EngravingObject* parent)
      60           33 :     : TextBase(ElementType::INSTRUMENT_CHANGE, parent, TextStyleType::INSTRUMENT_CHANGE, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      61              : {
      62           33 :     initElementStyle(&instrumentChangeStyle);
      63           33 :     m_instrument = new Instrument();
      64           33 : }
      65              : 
      66           22 : InstrumentChange::InstrumentChange(const Instrument& i, EngravingObject* parent)
      67           22 :     : TextBase(ElementType::INSTRUMENT_CHANGE, parent, TextStyleType::INSTRUMENT_CHANGE, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
      68              : {
      69           22 :     initElementStyle(&instrumentChangeStyle);
      70           22 :     m_instrument = new Instrument(i);
      71           22 : }
      72              : 
      73            7 : InstrumentChange::InstrumentChange(const InstrumentChange& is)
      74            7 :     : TextBase(is)
      75              : {
      76            7 :     m_instrument = new Instrument(*is.m_instrument);
      77            7 :     m_init = is.m_init;
      78            7 : }
      79              : 
      80          122 : InstrumentChange::~InstrumentChange()
      81              : {
      82           61 :     delete m_instrument;
      83          122 : }
      84              : 
      85           34 : void InstrumentChange::setInstrument(const Instrument& i)
      86              : {
      87           34 :     *m_instrument = i;
      88              :     //delete _instrument;
      89              :     //_instrument = new Instrument(i);
      90           34 : }
      91              : 
      92            1 : void InstrumentChange::setupInstrument(const Instrument* instrument, bool updateText)
      93              : {
      94            1 :     if (!m_init) {
      95            1 :         return;
      96              :     }
      97              : 
      98            1 :     Transaction& tx = score()->transactionManager()->currentOrDummyTransaction();
      99              : 
     100            1 :     Fraction tickStart = segment()->tick();
     101            1 :     Part* part = staff()->part();
     102            1 :     Interval oldV = part->instrument(tickStart)->transpose();
     103            1 :     Interval oldKv = staff()->transpose(tickStart);
     104            1 :     Interval v = instrument->transpose();
     105            1 :     bool concPitch = style().styleB(Sid::concertPitch);
     106              : 
     107              :     // change the clef for each staff
     108            2 :     for (size_t i = 0; i < part->nstaves(); i++) {
     109            2 :         ClefType oldClefType = concPitch ? part->instrument(tickStart)->clefType(i).concertClef
     110            1 :                                : part->instrument(tickStart)->clefType(i).transposingClef;
     111            2 :         ClefType newClefType = concPitch ? instrument->clefType(i).concertClef
     112            1 :                                : instrument->clefType(i).transposingClef;
     113              :         // Introduce cleff change only if the new clef *symbol* is different from the old one
     114            1 :         if (ClefInfo::symId(oldClefType) != ClefInfo::symId(newClefType)) {
     115              :             // If instrument change is at the start of a measure, use the measure as the element, as this will place the instrument change before the barline.
     116            0 :             EngravingItem* element = rtick().isZero() ? toEngravingItem(findMeasure()) : toEngravingItem(this);
     117            0 :             EditClef::undoChangeClef(tx, score(), part->staff(i), element, newClefType, true);
     118              :         }
     119              :     }
     120              : 
     121              :     // Change key signature if necessary. CAUTION: not necessary in case of octave-transposing!
     122            1 :     if ((v.chromatic - oldV.chromatic) % 12) {
     123            0 :         for (size_t i = 0; i < part->nstaves(); i++) {
     124            0 :             if (!part->staff(i)->keySigEvent(tickStart).isAtonal()) {
     125            0 :                 KeySigEvent ks;
     126              :                 // Check, if some key signature is already there, if no, mark new one "for instrument change"
     127            0 :                 Segment* seg = segment()->prev1(SegmentType::KeySig);
     128            0 :                 voice_idx_t voice = part->staff(i)->idx() * VOICES;
     129            0 :                 KeySig* ksig = seg ? toKeySig(seg->element(voice)) : nullptr;
     130            0 :                 bool forInstChange = !(ksig && ksig->tick() == tickStart && !ksig->generated());
     131            0 :                 ks.setForInstrumentChange(forInstChange);
     132            0 :                 Key cKey = part->staff(i)->concertKey(tickStart);
     133            0 :                 ks.setConcertKey(cKey);
     134            0 :                 EditKeySig::undoChangeKeySig(tx, score(), part->staff(i), tickStart, ks);
     135            0 :             }
     136              :         }
     137              :     }
     138              : 
     139              :     // change instrument in all linked scores
     140            2 :     for (EngravingObject* se : linkList()) {
     141            1 :         InstrumentChange* lic = static_cast<InstrumentChange*>(se);
     142            1 :         Instrument* newInstrument = new Instrument(*instrument);
     143            1 :         tx.push(new ChangeInstrument(lic, newInstrument));
     144            1 :     }
     145              : 
     146              :     // transpose for current score only
     147              :     // this automatically propagates to linked scores
     148            1 :     if (part->instrument(tickStart)->transpose() != oldV) {
     149            0 :         auto i = part->instruments().upper_bound(tickStart.ticks());              // find(), ++i
     150            0 :         Fraction tickEnd;
     151            0 :         if (i == part->instruments().end()) {
     152            0 :             tickEnd = Fraction(-1, 1);
     153              :         } else {
     154            0 :             tickEnd = Fraction::fromTicks(i->first);
     155              :         }
     156            0 :         Transpose::transpositionChanged(tx, score(), part, oldKv, tickStart, tickEnd);
     157              :     }
     158              : 
     159            1 :     if (!updateText) {
     160            1 :         return;
     161              :     }
     162              : 
     163              :     //: The text of an "instrument change" marking. It is an instruction to the player to switch to another instrument.
     164            0 :     const String newInstrChangeText = muse::mtrc("engraving", "To %1").arg(instrument->trackName());
     165            0 :     undoChangeProperty(Pid::TEXT, TextBase::plainToXmlText(newInstrChangeText));
     166            0 : }
     167              : 
     168              : //---------------------------------------------------------
     169              : //   keySigs
     170              : //---------------------------------------------------------
     171              : 
     172            5 : std::vector<KeySig*> InstrumentChange::keySigs(bool all) const
     173              : {
     174            5 :     std::vector<KeySig*> keysigs;
     175            5 :     Segment* seg = segment()->prev1(SegmentType::KeySig);
     176            5 :     if (seg) {
     177            2 :         voice_idx_t startVoice = part()->staff(0)->idx() * VOICES;
     178            2 :         voice_idx_t endVoice = part()->staff(part()->nstaves() - 1)->idx() * VOICES;
     179            2 :         Fraction t = tick();
     180            4 :         for (voice_idx_t i = startVoice; i <= endVoice; i += VOICES) {
     181            2 :             KeySig* ks = toKeySig(seg->element(i));
     182            2 :             if (ks && (all || ks->forInstrumentChange()) && ks->tick() == t) {
     183            1 :                 keysigs.push_back(ks);
     184              :             }
     185              :         }
     186              :     }
     187            5 :     return keysigs;
     188            0 : }
     189              : 
     190              : //---------------------------------------------------------
     191              : //   clefs
     192              : //---------------------------------------------------------
     193              : 
     194            1 : std::vector<Clef*> InstrumentChange::clefs() const
     195              : {
     196            1 :     std::vector<Clef*> clefs;
     197            1 :     Segment* seg = segment()->prev1(SegmentType::Clef);
     198            1 :     if (seg) {
     199            0 :         voice_idx_t startVoice = part()->staff(0)->idx() * VOICES;
     200            0 :         voice_idx_t endVoice = part()->staff(part()->nstaves() - 1)->idx() * VOICES;
     201            0 :         Fraction t = tick();
     202            0 :         for (voice_idx_t i = startVoice; i <= endVoice; i += VOICES) {
     203            0 :             Clef* clef = toClef(seg->element(i));
     204            0 :             if (clef && clef->forInstrumentChange() && clef->tick() == t) {
     205            0 :                 clefs.push_back(clef);
     206              :             }
     207              :         }
     208              :     }
     209            1 :     return clefs;
     210            0 : }
     211              : 
     212              : //---------------------------------------------------------
     213              : //   propertyDefault
     214              : //---------------------------------------------------------
     215              : 
     216          255 : engraving::PropertyValue InstrumentChange::propertyDefault(Pid propertyId) const
     217              : {
     218          255 :     switch (propertyId) {
     219           24 :     case Pid::TEXT_STYLE:
     220           24 :         return TextStyleType::INSTRUMENT_CHANGE;
     221          231 :     default:
     222          231 :         return TextBase::propertyDefault(propertyId);
     223              :     }
     224              : }
     225              : }
        

Generated by: LCOV version 2.0-1