Skip to content

Only transpose when instrument transposition has changed#32951

Open
miiizen wants to merge 1 commit intomusescore:masterfrom
miiizen:32393-enharmonicTransp
Open

Only transpose when instrument transposition has changed#32951
miiizen wants to merge 1 commit intomusescore:masterfrom
miiizen:32393-enharmonicTransp

Conversation

@miiizen
Copy link
Copy Markdown
Contributor

@miiizen miiizen commented Apr 8, 2026

Resolves: #32393

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Walkthrough

The MasterNotationParts::replaceInstrument method now computes newTranspose from the new instrument and invokes engraving::Transpose::transpositionChanged only when oldTranspose != newTranspose. Previously, transpositionChanged was called unconditionally after updating the instrument in master and excerpt parts.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal with only 'Resolves: #32393', missing most template sections like summary, commit messages, testing details, and confirmation checkboxes. Expand the PR description to include the motivation for changes, relevant template checklist items, and details about testing performed.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: the code now only calls transpositionChanged when transpose actually changes, rather than unconditionally.
Linked Issues check ✅ Passed The code change directly addresses the root cause of issue #32393 by preventing unwanted transposition recalculation when instrument properties are edited without actual transpose changes.
Out of Scope Changes check ✅ Passed The 4-line change is focused solely on the transposition logic in MasterNotationParts::replaceInstrument, directly tied to resolving the enharmonic spelling reversion bug.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/notation/internal/masternotationparts.cpp`:
- Around line 202-207: oldTranspose is being read after the part's instrument is
mutated so it may already reflect newInstrument.transpose(), causing the
equality check with newTranspose to incorrectly skip calling
engraving::Transpose::transpositionChanged(...). Fix by capturing
mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() :
mu::engraving::Interval(0,0) before you replace the part's instrument (before
the code that assigns or swaps in newInstrument), keep newTranspose =
newInstrument.transpose() as is, and then perform the oldTranspose !=
newTranspose check and call engraving::Transpose::transpositionChanged(score(),
part, Part::MAIN_INSTRUMENT_TICK, oldTranspose) afterward.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fc9f7437-df79-4d82-9e35-b09deaf5c277

📥 Commits

Reviewing files that changed from the base of the PR and between 214fea5 and 6add974.

📒 Files selected for processing (1)
  • src/notation/internal/masternotationparts.cpp

Comment on lines +202 to +207
mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() : mu::engraving::Interval(0, 0);
mu::engraving::Interval newTranspose = newInstrument.transpose();
// this also transposes all linked parts
engraving::Transpose::transpositionChanged(score(), part, Part::MAIN_INSTRUMENT_TICK, oldTranspose);
if (oldTranspose != newTranspose) {
engraving::Transpose::transpositionChanged(score(), part, Part::MAIN_INSTRUMENT_TICK, oldTranspose);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Capture oldTranspose before instrument replacement to avoid skipping required transposition.

oldTranspose on Line 202 is read after Line 196 mutates the part instrument, so it can already be the new value. In real transposition changes, this can make the Line 205 guard false and incorrectly skip transpositionChanged(...).

💡 Proposed fix
-    NotationParts::replaceInstrument(instrumentKey, newInstrument, newStaffType);
-
-    for (const INotationPartsPtr& parts : excerptsParts()) {
-        parts->replaceInstrument(instrumentKey, newInstrument, newStaffType);
-    }
-
-    mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() : mu::engraving::Interval(0, 0);
-    mu::engraving::Interval newTranspose = newInstrument.transpose();
+    mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() : mu::engraving::Interval(0, 0);
+    mu::engraving::Interval newTranspose = newInstrument.transpose();
+
+    NotationParts::replaceInstrument(instrumentKey, newInstrument, newStaffType);
+
+    for (const INotationPartsPtr& parts : excerptsParts()) {
+        parts->replaceInstrument(instrumentKey, newInstrument, newStaffType);
+    }
     // this also transposes all linked parts
-    if (oldTranspose != newTranspose) {
+    if (part && oldTranspose != newTranspose) {
         engraving::Transpose::transpositionChanged(score(), part, Part::MAIN_INSTRUMENT_TICK, oldTranspose);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/notation/internal/masternotationparts.cpp` around lines 202 - 207,
oldTranspose is being read after the part's instrument is mutated so it may
already reflect newInstrument.transpose(), causing the equality check with
newTranspose to incorrectly skip calling
engraving::Transpose::transpositionChanged(...). Fix by capturing
mu::engraving::Interval oldTranspose = part ? part->instrument()->transpose() :
mu::engraving::Interval(0,0) before you replace the part's instrument (before
the code that assigns or swaps in newInstrument), keep newTranspose =
newInstrument.transpose() as is, and then perform the oldTranspose !=
newTranspose check and call engraving::Transpose::transpositionChanged(score(),
part, Part::MAIN_INSTRUMENT_TICK, oldTranspose) afterward.

@miiizen miiizen force-pushed the 32393-enharmonicTransp branch from 6add974 to 170bd98 Compare April 8, 2026 13:57
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/notation/internal/masternotationparts.cpp (1)

206-208: ⚠️ Potential issue | 🔴 Critical

Add a null guard for part before calling transpositionChanged.

transpositionChanged() dereferences part; current guard allows a null part path when oldTranspose != newTranspose, which can crash.

Proposed fix
-    if (oldTranspose != newTranspose) {
+    if (part && oldTranspose != newTranspose) {
         engraving::Transpose::transpositionChanged(score(), part, Part::MAIN_INSTRUMENT_TICK, oldTranspose);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/notation/internal/masternotationparts.cpp` around lines 206 - 208, The
call to engraving::Transpose::transpositionChanged(score(), part,
Part::MAIN_INSTRUMENT_TICK, oldTranspose) can dereference a nullptr because we
only check oldTranspose != newTranspose; add a null guard for the part pointer
before invoking transpositionChanged (e.g., check that part is non-null with if
(part) or bail out/return when part is null) so that transpositionChanged is
only called when part is valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/notation/internal/masternotationparts.cpp`:
- Around line 206-208: The call to
engraving::Transpose::transpositionChanged(score(), part,
Part::MAIN_INSTRUMENT_TICK, oldTranspose) can dereference a nullptr because we
only check oldTranspose != newTranspose; add a null guard for the part pointer
before invoking transpositionChanged (e.g., check that part is non-null with if
(part) or bail out/return when part is null) so that transpositionChanged is
only called when part is valid.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ea70b09b-1aeb-4e03-b273-3a29816ad6ef

📥 Commits

Reviewing files that changed from the base of the PR and between 6add974 and 170bd98.

📒 Files selected for processing (1)
  • src/notation/internal/masternotationparts.cpp

@davidstephengrant
Copy link
Copy Markdown
Contributor

@miiizen Replace instrument... from the Staff properties dialog has stopped working.

Screencast.from.2026-04-09.11-43-24.webm

Also, could we refine this further to not reset spelling when changing to an octave transposing instrument with the same transposition pitch class? E.g. Clar. in Bb -> Bass Clarinet in Bb, Alto Sax -> Baritone Sax, etc. (This behaviour is already in place for octave transposing instruments in C, e.g. Flute -> Piccolo in C).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enharmonic note spellings change after editing "Staff/Part properties..."

2 participants