Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 96 additions & 28 deletions src/engraving/dom/instrchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#include "instrchange.h"
#include "instrtemplate.h"

#include "translation.h"

Expand Down Expand Up @@ -54,26 +55,39 @@
//---------------------------------------------------------

InstrumentChange::InstrumentChange(EngravingItem* parent)
: TextBase(ElementType::INSTRUMENT_CHANGE, parent, TextStyleType::INSTRUMENT_CHANGE, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
: TextBase(ElementType::INSTRUMENT_CHANGE, parent,
TextStyleType::INSTRUMENT_CHANGE,
ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
{
initElementStyle(&instrumentChangeStyle);
m_instrument = new Instrument();
m_instrument = nullptr;
m_defaultClefs.clear(); // leave empty — setupInstrument() will fill it
m_init = true;
}


InstrumentChange::InstrumentChange(const Instrument& i, EngravingItem* parent)
: TextBase(ElementType::INSTRUMENT_CHANGE, parent, TextStyleType::INSTRUMENT_CHANGE, ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
: TextBase(ElementType::INSTRUMENT_CHANGE, parent,
TextStyleType::INSTRUMENT_CHANGE,
ElementFlag::MOVABLE | ElementFlag::ON_STAFF)
{
initElementStyle(&instrumentChangeStyle);
m_instrument = new Instrument(i);
m_defaultClefs.clear(); // leave empty — setupInstrument() will fill it
m_init = true;
}



InstrumentChange::InstrumentChange(const InstrumentChange& is)
: TextBase(is)
{
m_instrument = new Instrument(*is.m_instrument);
m_init = is.m_init;
m_defaultClefs = is.m_defaultClefs;
}


InstrumentChange::~InstrumentChange()
{
delete m_instrument;
Expand All @@ -82,43 +96,94 @@
void InstrumentChange::setInstrument(const Instrument& i)
{
*m_instrument = i;
//delete _instrument;
//_instrument = new Instrument(i);
}

void InstrumentChange::setupInstrument(const Instrument* instrument)
{
// ------------------------------------------------------------
// 1. Detect if there is any previous IC on this staff
// ------------------------------------------------------------
bool hasPrevIC = false;
{
Segment* s = segment()->prev1(); // previous segment of ANY type
int staffIdx = staff()->idx() * VOICES;

while (s) {
EngravingItem* e = s->element(staffIdx);
if (e && e->type() == ElementType::INSTRUMENT_CHANGE) {
hasPrevIC = true;
break;
}
s = s->prev1();
}
}

// ------------------------------------------------------------
// 2. Legacy "initial IC" behaviour:
// Only applies when:
// - there is NO previous IC on this staff
// - AND this IC is at tick 0
// - AND <init> was true (old files)
// ------------------------------------------------------------
bool isInitialIC = !hasPrevIC && segment()->tick().isZero();

if (isInitialIC && m_init) {
// Old scores: the initial IC at tick 0 should NOT apply changes.
return;
}

// ------------------------------------------------------------
// 3. New ICs: setupInstrument() must run exactly once.
// If m_init is false, this IC has already been processed.
// ------------------------------------------------------------
if (!m_init) {
return;
}

// Mark this IC as processed
m_init = false;

// ------------------------------------------------------------
// 4. CRITICAL: The IC must own its instrument BEFORE any clef/key logic.
// This prevents the staff's base instrument from being modified.
// ------------------------------------------------------------
// If this IC has no instrument yet, initialize it from the staff's current instrument
if (!m_instrument) {
const Instrument* staffInstr = staff()->part()->instrument(segment()->tick());
m_instrument = new Instrument(*staffInstr);
}

// If the caller passed a valid instrument, update the IC's instrument
if (instrument) {
*m_instrument = *instrument;
}


setInstrument(*instrument);

// ---------------------------------------------------------
// ORIGINAL 4.6.5 LOGIC STARTS HERE (unchanged)
// ---------------------------------------------------------

if (!segment()) {
return;
}

// REMOVE THE SECOND setInstrument() — it caused the regression
// setInstrument(*instrument);

Fraction tickStart = segment()->tick();
Part* part = staff()->part();
Interval oldV = part->instrument(tickStart)->transpose();
Interval oldKv = staff()->transpose(tickStart);
Interval v = instrument->transpose();
bool concPitch = style().styleB(Sid::concertPitch);

Check warning on line 180 in src/engraving/dom/instrchange.cpp

View workflow job for this annotation

GitHub Actions / build (linux_arm64)

unused variable ‘concPitch’ [-Wunused-variable]

Check warning on line 180 in src/engraving/dom/instrchange.cpp

View workflow job for this annotation

GitHub Actions / build (linux_x64)

unused variable ‘concPitch’ [-Wunused-variable]

// change the clef for each staff
for (size_t i = 0; i < part->nstaves(); i++) {
ClefType oldClefType = concPitch ? part->instrument(tickStart)->clefType(i).concertClef
: part->instrument(tickStart)->clefType(i).transposingClef;
ClefType newClefType = concPitch ? instrument->clefType(i).concertClef
: instrument->clefType(i).transposingClef;
// Introduce cleff change only if the new clef *symbol* is different from the old one
if (ClefInfo::symId(oldClefType) != ClefInfo::symId(newClefType)) {
// 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.
EngravingItem* element = rtick().isZero() ? toEngravingItem(findMeasure()) : toEngravingItem(this);
score()->undoChangeClef(part->staff(i), element, newClefType, true);
}
}

// Change key signature if necessary. CAUTION: not necessary in case of octave-transposing!
if ((v.chromatic - oldV.chromatic) % 12) {
for (size_t i = 0; i < part->nstaves(); i++) {
if (!part->staff(i)->keySigEvent(tickStart).isAtonal()) {
KeySigEvent ks;
// Check, if some key signature is already there, if no, mark new one "for instrument change"
Segment* seg = segment()->prev1(SegmentType::KeySig);
voice_idx_t voice = part->staff(i)->idx() * VOICES;
KeySig* ksig = seg ? toKeySig(seg->element(voice)) : nullptr;
Expand All @@ -131,17 +196,18 @@
}
}

// change instrument in all linked scores
for (EngravingObject* se : linkList()) {
InstrumentChange* lic = static_cast<InstrumentChange*>(se);
Instrument* newInstrument = new Instrument(*instrument);
lic->score()->undo(new ChangeInstrument(lic, newInstrument));
auto* lic = static_cast<InstrumentChange*>(se);

if (lic == this)
continue; // skip self

lic->setInstrument(*instrument);
}

// transpose for current score only
// this automatically propagates to linked scores
if (part->instrument(tickStart)->transpose() != oldV) {
auto i = part->instruments().upper_bound(tickStart.ticks()); // find(), ++i
auto i = part->instruments().upper_bound(tickStart.ticks());
Fraction tickEnd;
if (i == part->instruments().end()) {
tickEnd = Fraction(-1, 1);
Expand All @@ -151,9 +217,11 @@
Transpose::transpositionChanged(score(), part, oldKv, tickStart, tickEnd);
}

//: The text of an "instrument change" marking. It is an instruction to the player to switch to another instrument.
const String newInstrChangeText = muse::mtrc("engraving", "To %1").arg(instrument->trackName());
undoChangeProperty(Pid::TEXT, TextBase::plainToXmlText(newInstrChangeText));

// REMOVE THIS — it breaks Inspector edits
// m_init = false;
}

//---------------------------------------------------------
Expand Down Expand Up @@ -213,4 +281,4 @@
return TextBase::propertyDefault(propertyId);
}
}
}
}
4 changes: 4 additions & 0 deletions src/engraving/dom/instrchange.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,14 @@ class InstrumentChange final : public TextBase

bool placeMultiple() const override { return false; }

const std::vector<ClefTypeList>& defaultClefs() const { return m_defaultClefs; }
void setDefaultClefs(const std::vector<ClefTypeList>& c) { m_defaultClefs = c; }

private:

Instrument* m_instrument = nullptr; // Staff holds ownership if part of score
bool m_init = false; // Set if the instrument has been set by the user, as there is no other way to tell.
std::vector<ClefTypeList> m_defaultClefs;
};
} // namespace mu::engraving
#endif
2 changes: 1 addition & 1 deletion src/engraving/dom/staff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1914,4 +1914,4 @@ void Staff::setIsLinesInvisible(const Fraction& tick, bool val)
{
staffType(tick)->setInvisible(val);
}
}
}
21 changes: 13 additions & 8 deletions src/engraving/editing/editinstrumentchange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,23 @@ void ChangeInstrument::flip(EditData*)
{
Part* part = is->staff()->part();
Fraction tickStart = is->segment()->tick();
Instrument* oi = is->instrument(); //new Instrument(*is->instrument());

// set instrument in both part and instrument change element
is->setInstrument(instrument); //*instrument
part->setInstrument(instrument, tickStart);
// Copy the instrument value
Instrument newInst = *instrument;

// update score
// Apply to InstrumentChange (deep copy into m_instrument)
is->setInstrument(newInst);

// Apply to Part (Part takes ownership of a NEW heap instance)
part->setInstrument(new Instrument(newInst), tickStart);

// Configure clefs, keys, transposition, etc.
is->setupInstrument(&newInst);

// Update score
is->masterScore()->rebuildMidiMapping();
is->masterScore()->updateChannel();
is->score()->setInstrumentsChanged(true);
is->triggerLayoutAll();

// remember original instrument
instrument = oi;
}

1 change: 0 additions & 1 deletion src/engraving/editing/editpart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ bool EditPart::replaceInstrumentAtTick(Score* score, Part* part, const Fraction&
if (!instrumentChange) {
return false;
}

instrumentChange->setInit(true);
instrumentChange->setupInstrument(&newInstrument);
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/engraving/rw/read460/tread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4696,4 +4696,4 @@ void TRead::readSystemDividers(Score* score, XmlReader& e, ReadContext& ctx)
e.unknown();
}
}
}
}
2 changes: 1 addition & 1 deletion src/engraving/rw/write/twrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3667,4 +3667,4 @@ void TWrite::writeSegments(XmlWriter& xml, WriteContext& ctx, track_idx_t strack
xml.endElement(); // </voice>
}
}
}
}
1 change: 0 additions & 1 deletion src/notation/internal/notationinteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2276,7 +2276,6 @@ bool NotationInteraction::selectInstrument(mu::engraving::InstrumentChange* inst
Instrument newInstrument = Instrument::fromTemplate(&val);
instrumentChange->setInit(true);
instrumentChange->setupInstrument(&newInstrument);

if (newInstrument.useDrumset()) {
cleanupDrumsetChanges(instrumentChange);
}
Expand Down
Loading