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