Skip to content

Allow grace notes to be attached to rests#34213

Open
manolo wants to merge 25 commits into
musescore:mainfrom
manolo:19701-grace-notes-on-rests
Open

Allow grace notes to be attached to rests#34213
manolo wants to merge 25 commits into
musescore:mainfrom
manolo:19701-grace-notes-on-rests

Conversation

@manolo

@manolo manolo commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Resolves: #19701

MuseScore stores grace notes on Chord, so they can only be attached to notes, never to rests. This makes it impossible to notate common figures such as a grace note (or a "grace note after") that occurs after a rest and before a barline, as in the final measures of Liza Lehmann's "You mustn't swim till you're six weeks old" cited in the issue. The only workaround so far was to attach the grace to an invisible non playing note in a higher voice.

This PR moves grace note storage and the grace note API from Chord up to the shared ChordRest base, so a rest can host grace notes just like a note. The change is threaded through the whole pipeline: native read/write, layout, selection, navigation, editing, playback, part generation, and the MusicXML, MEI, MNX, Capella, OVE and Braille file formats.

Prior art

Grace notes attached to rests or placed before barlines have long been supported in other notation software like Encore and Dorico, while Sibelius and Finale rely on workarounds. MuseScore previously lacked this capability, which limited accurate import of existing scores. Adding support aligns MuseScore with established tools and improves compatibility with other formats.

Summary of changes

  • Engraving: move grace note storage and the grace API from Chord to ChordRest.
  • Native format: read and write grace notes attached to rests.
  • Layout: give a rest's grace notes their note lines, stems, size, beams and geometry, and unify the grace note layout path for chord and rest hosts so they cannot drift.
  • Interaction: select and navigate grace notes on a rest, add them to a selected rest, and accept a grace note dropped from the palette onto a rest.
  • Editing: apply transpose, clef change re pitching, enharmonic respelling and voice operations to a rest's grace notes.
  • Playback: render a rest's grace notes in the playback model and in the compat MIDI renderer.
  • Parts: clone grace notes hosted by a rest into generated parts.
  • File formats: import and export grace notes on rests for MusicXML, MEI, MNX, Capella and OVE, and export them in Braille.

Results

Here 2 screenshots of the xml score provided in the forum article imported in musescore before and after this change.

Before:
Screenshot 2026-07-15 at 17 35 57

After:
Screenshot 2026-07-15 at 08 22 09

  • I signed the CLA (regular contributor)
  • The title of the PR describes the problem it addresses.
  • Each commit's message describes its purpose and effects, and references the issue it resolves. If changes are extensive, there is a sequence of easily reviewable commits.
  • The code in the PR follows the coding rules.
  • I understand all aspects of the code I'm contributing and I'm able to explain it if requested.
  • The code compiles and runs on my machine, preferably after each commit individually. I have manually tested and verified that my changes fulfil their intended purpose.
  • No prior attempts to resolve this problem exist, or if they do, I listed them in my PR description and described how I avoided repeating past mistakes.
  • There are no unnecessary changes.
  • I created a unit test or vtest to verify the changes I made (if applicable).

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1b87454f-e656-4a15-a1df-06b950b7d464

📥 Commits

Reviewing files that changed from the base of the PR and between 6480525 and 3831888.

📒 Files selected for processing (24)
  • src/braille/internal/braille.cpp
  • src/braille/tests/braille_tests.cpp
  • src/braille/tests/data/testGraceOnRest.mscx
  • src/braille/tests/data/testGraceOnRest_ref.brf
  • src/engraving/compat/midi/compatmidirenderinternal.cpp
  • src/engraving/dom/chordrest.cpp
  • src/engraving/editing/edit.cpp
  • src/engraving/editing/edittie.cpp
  • src/engraving/rw/read500/measureread.cpp
  • src/importexport/capella/internal/capella.cpp
  • src/importexport/mei/internal/meiexporter.cpp
  • src/importexport/mei/internal/meiexporter.h
  • src/importexport/mei/internal/meiimporter.cpp
  • src/importexport/mei/internal/meiimporter.h
  • src/importexport/mei/tests/data/gracenote-03.mei
  • src/importexport/mei/tests/data/gracenote-03.mscx
  • src/importexport/mei/tests/mei_tests.cpp
  • src/importexport/mnx/internal/export/mnxexportsequences.cpp
  • src/importexport/mnx/internal/import/mnximportpartmeasures.cpp
  • src/importexport/musicxml/internal/export/exportmusicxml.cpp
  • src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp
  • src/importexport/musicxml/tests/data/testGraceAfterOnRest.xml
  • src/importexport/musicxml/tests/musicxml_tests.cpp
  • src/importexport/ove/internal/importove.cpp
🚧 Files skipped from review as they are similar to previous changes (22)
  • src/importexport/musicxml/tests/data/testGraceAfterOnRest.xml
  • src/importexport/mei/internal/meiexporter.h
  • src/importexport/mei/tests/data/gracenote-03.mscx
  • src/braille/tests/data/testGraceOnRest_ref.brf
  • src/braille/tests/data/testGraceOnRest.mscx
  • src/engraving/editing/edittie.cpp
  • src/importexport/mei/tests/data/gracenote-03.mei
  • src/importexport/mnx/internal/export/mnxexportsequences.cpp
  • src/braille/tests/braille_tests.cpp
  • src/importexport/mei/internal/meiimporter.h
  • src/engraving/rw/read500/measureread.cpp
  • src/importexport/mnx/internal/import/mnximportpartmeasures.cpp
  • src/importexport/musicxml/tests/musicxml_tests.cpp
  • src/engraving/editing/edit.cpp
  • src/braille/internal/braille.cpp
  • src/importexport/musicxml/internal/export/exportmusicxml.cpp
  • src/importexport/mei/internal/meiexporter.cpp
  • src/engraving/dom/chordrest.cpp
  • src/importexport/ove/internal/importove.cpp
  • src/engraving/compat/midi/compatmidirenderinternal.cpp
  • src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp
  • src/importexport/mei/internal/meiimporter.cpp

📝 Walkthrough

Walkthrough

Grace notes are generalized from chord-only attachments to ChordRest attachments, enabling rests to host grace-before and grace-after notes. Storage, cloning, editing, navigation, layout, playback, Braille rendering, and import/export paths are updated accordingly. New regression tests and score fixtures cover rest-hosted grace-note engraving, playback, MIDI rendering, Braille output, MEI import, and MusicXML import/export.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: allowing grace notes to be attached to rests.
Description check ✅ Passed The description matches the template, includes Resolves #19701, motivation, summary, and a completed checklist.
Linked Issues check ✅ Passed The changes implement the issue goal by supporting grace notes on rests across engraving, editing, playback, and import/export paths.
Out of Scope Changes check ✅ Passed No clearly unrelated changes stand out; the touched files all support the grace-notes-on-rests objective.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. No linked repositories were analyzed; skipped musescore/muse_framework.git.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/engraving/dom/chord.cpp (1)

2017-2018: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Move grace note clearance to ChordRest::removeMarkings.

Now that ChordRest assumes ownership of grace notes, clearing them during removeMarkings should be elevated to ChordRest::removeMarkings(bool). Leaving it here in Chord::removeMarkings means that if a Rest is cloned and its markings are requested to be removed (e.g., when splitting a rest over a barline or subdividing its duration), the grace notes will not be cleared and will instead be improperly duplicated onto the new rest segments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/dom/chord.cpp` around lines 2017 - 2018, Move the grace-note
deletion and clearing from Chord::removeMarkings into
ChordRest::removeMarkings(bool), ensuring the base implementation clears
graceNotes() for both Chord and Rest. Remove the now-redundant cleanup from
Chord::removeMarkings while preserving the existing marking-removal behavior.
src/engraving/editing/cmd.cpp (1)

971-977: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Transfer grace notes to replacement rests.

Currently, the logic to transfer grace notes from a replaced ChordRest (cr) is placed inside the else block (which handles creating a new Chord). If the replacement element is a Rest (isRest is true), this block is skipped, causing any existing grace notes on the original chord or rest to be orphaned or lost during the replacement.

Since the goal of this PR is to enable grace notes on rests, replacing a chord with a rest should preserve its attached grace notes. Move this transfer logic outside the else block so it applies to both new chords and new rests via the ncr pointer.

💡 Proposed fix
                 chord->setStemDirection(stemDirection);
                 chord->add(note);
-                if (cr) {
-                    // transfer grace notes from the replaced chord or rest to the new chord
-                    std::vector<Chord*> graceNotes = cr->graceNotes();
-                    for (Chord* grace : graceNotes) {
-                        undoChangeParent(grace, chord, chord->staffIdx());
-                    }
-                }
                 note->setNval(nval, tick);
                 if (forceAccidental) {
                     int tpc = style().styleB(Sid::concertPitch) ? nval.tpc1 : nval.tpc2;
                     AccidentalVal alter = tpc2alter(tpc);
                     AccidentalType at = Accidental::value2subtype(alter);
                     Accidental* a = Factory::createAccidental(note);
                     a->setAccidentalType(at);
                     a->setRole(AccidentalRole::USER);
                     note->add(a);
                 }
 
                 ncr = chord;
                 if (i + 1 < n) {
                     tie = Factory::createTie(this->dummy());
                     tie->setStartNote(note);
                     tie->setTick(tie->startNote()->tick());
                     tie->setTrack(track);
                     note->setTieFor(tie);
                 }
             }
+            if (cr) {
+                // transfer grace notes from the replaced chord or rest to the new chord or rest
+                std::vector<Chord*> graceNotes = cr->graceNotes();
+                for (Chord* grace : graceNotes) {
+                    undoChangeParent(grace, ncr, ncr->staffIdx());
+                }
+            }
             if (tuplet) {
                 ncr->setTuplet(tuplet);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/editing/cmd.cpp` around lines 971 - 977, Move the grace-note
transfer logic currently guarded by the new-chord else block to the shared
replacement path after both chord and rest creation, using the replacement `ncr`
pointer as the parent. Ensure grace notes from `cr` are transferred for both
chord and rest replacements, while preserving the existing `undoChangeParent`
behavior.
🧹 Nitpick comments (2)
src/engraving/rendering/score/chordlayout.cpp (1)

632-632: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Avoid unnecessary vector copy.

item->graceNotesBefore() returns a reference to GraceNotesGroup (which inherits from std::vector<Chord*>). Assigning it to std::vector<Chord*> by value triggers the vector's copy constructor. Use const auto& to bind it by reference and avoid the unnecessary heap allocation.

Consider applying the same optimization to item->graceNotesAfter() at line 646.

♻️ Proposed refactor
-    std::vector<Chord*> graceNotesBefore = item->graceNotesBefore();
+    const auto& graceNotesBefore = item->graceNotesBefore();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engraving/rendering/score/chordlayout.cpp` at line 632, Update the local
declarations for item->graceNotesBefore() and item->graceNotesAfter() in the
chord layout flow to bind the returned groups by const reference using const
auto&, avoiding vector copies while preserving read-only behavior.
src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp (1)

2773-2773: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use nullptr instead of 0 for pointer initialization.

For consistency with modern C++ standards and the rest of the codebase, prefer nullptr.

♻️ Proposed refactor
-    ChordRest* prevChord = 0;         // previous chord or rest
+    ChordRest* prevChord = nullptr;   // previous chord or rest
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp` at line
2773, Update the prevChord pointer initialization to use nullptr instead of 0,
preserving its existing role as the previous chord-or-rest sentinel.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/braille/internal/braille.cpp`:
- Line 2157: Update the condition surrounding chord->isGrace() to verify
chord->explicitParent() is non-null before calling isChordRest(), while
preserving the existing behavior for attached grace notes.

In `@src/engraving/compat/midi/compatmidirenderinternal.cpp`:
- Around line 1230-1246: Update collectRestGraces in the rest-handling block to
calculate per-grace timing offsets instead of using default CollectNoteParams.
Stagger each grace note with progressively negative graceOffsetOn values and set
graceOffsetOff to shorten its duration, matching the timing behavior established
by collectGraceBeforeChordEvents and GraceChordCtx::buildRestGraceCtxList for
both graceNotesBefore and graceNotesAfter.

In `@src/engraving/dom/chordrest.cpp`:
- Around line 583-592: In the CHORD case, validate gc->graceIndex() before
inserting into m_graceNotes. Clamp the index to m_graceNotes.size() so malformed
or stale state appends safely, then preserve the existing setFlag, insert, and
added flow.

In `@src/engraving/editing/edit.cpp`:
- Line 3783: Update the linked grace-chord handling around the destination
`ChordRest` and `pc` resolution so rest-hosted grace chords are not rejected by
the non-grace chord check. Accept a destination ChordRest, select its indexed
grace chord, and require the destination to be a Chord only for non-grace
inputs.

In `@src/engraving/editing/edittie.cpp`:
- Around line 140-145: Update the tie-target selection around searchTieNote and
the host check so a grace-to-grace tie is resolved first, including when the
current grace note’s host is a rest. Only skip the operation when no valid next
grace-note target exists; preserve the existing chord-host behavior for ties to
a main note.

In `@src/engraving/rw/read500/measureread.cpp`:
- Around line 318-323: Update the MMRest handling branch around the graceNotes
loop to clear the pending graceNotes collection after attaching all notes to the
rest, matching the normal Rest path and preventing them from carrying into the
next element.

In `@src/importexport/capella/internal/capella.cpp`:
- Around line 533-551: Update attachPendingGraceNotes in
src/importexport/capella/internal/capella.cpp (lines 533-551) and
src/importexport/ove/internal/importove.cpp (lines 1507-1521) to remove matching
grace notes from graceNotes with removeAt(ii), attach them, and retain
voice-mismatched notes without deleting or clearing them. Add
qDeleteAll(graceNotes) at the end of readCapVoice before the Pass II comment and
at the end of convertNotes to clean up any notes still unattached when each
import scope ends.

In `@src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp`:
- Around line 7320-7323: The grace-chord state must be cleared whenever
addGraceChordsBefore consumes gcl. In
src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp lines
7320-7323 and 7279-7286, reset gac to 0 immediately before the respective
addGraceChordsBefore call, preserving the existing grace-chord processing.

---

Outside diff comments:
In `@src/engraving/dom/chord.cpp`:
- Around line 2017-2018: Move the grace-note deletion and clearing from
Chord::removeMarkings into ChordRest::removeMarkings(bool), ensuring the base
implementation clears graceNotes() for both Chord and Rest. Remove the
now-redundant cleanup from Chord::removeMarkings while preserving the existing
marking-removal behavior.

In `@src/engraving/editing/cmd.cpp`:
- Around line 971-977: Move the grace-note transfer logic currently guarded by
the new-chord else block to the shared replacement path after both chord and
rest creation, using the replacement `ncr` pointer as the parent. Ensure grace
notes from `cr` are transferred for both chord and rest replacements, while
preserving the existing `undoChangeParent` behavior.

---

Nitpick comments:
In `@src/engraving/rendering/score/chordlayout.cpp`:
- Line 632: Update the local declarations for item->graceNotesBefore() and
item->graceNotesAfter() in the chord layout flow to bind the returned groups by
const reference using const auto&, avoiding vector copies while preserving
read-only behavior.

In `@src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp`:
- Line 2773: Update the prevChord pointer initialization to use nullptr instead
of 0, preserving its existing role as the previous chord-or-rest sentinel.
🪄 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: CHILL

Plan: Pro

Run ID: 7a11cf03-9dd7-4ed9-aef1-370283ca2c26

📥 Commits

Reviewing files that changed from the base of the PR and between bd0e155 and eaeffdb.

📒 Files selected for processing (74)
  • src/braille/internal/braille.cpp
  • src/braille/tests/braille_tests.cpp
  • src/braille/tests/data/testGraceOnRest.mscx
  • src/braille/tests/data/testGraceOnRest_ref.brf
  • src/engraving/compat/midi/compatmidirender.cpp
  • src/engraving/compat/midi/compatmidirenderinternal.cpp
  • src/engraving/dom/chord.cpp
  • src/engraving/dom/chord.h
  • src/engraving/dom/chordrest.cpp
  • src/engraving/dom/chordrest.h
  • src/engraving/dom/excerpt.cpp
  • src/engraving/dom/rest.cpp
  • src/engraving/dom/score.h
  • src/engraving/dom/select.cpp
  • src/engraving/dom/slur.cpp
  • src/engraving/dom/utils.cpp
  • src/engraving/editing/clonevoice.cpp
  • src/engraving/editing/cmd.cpp
  • src/engraving/editing/edit.cpp
  • src/engraving/editing/editclef.cpp
  • src/engraving/editing/editenharmonicspelling.cpp
  • src/engraving/editing/edittie.cpp
  • src/engraving/editing/navigation.cpp
  • src/engraving/editing/transpose.cpp
  • src/engraving/playback/playbackeventsrenderer.cpp
  • src/engraving/playback/playbackeventsrenderer.h
  • src/engraving/playback/playbackmodel.cpp
  • src/engraving/playback/renderers/chordarticulationsrenderer.cpp
  • src/engraving/playback/renderers/chordarticulationsrenderer.h
  • src/engraving/playback/renderers/gracechordcontext.cpp
  • src/engraving/playback/renderers/gracechordcontext.h
  • src/engraving/playback/renderingcontext.h
  • src/engraving/rendering/score/beamlayout.cpp
  • src/engraving/rendering/score/beamlayout.h
  • src/engraving/rendering/score/chordlayout.cpp
  • src/engraving/rendering/score/chordlayout.h
  • src/engraving/rendering/score/masklayout.cpp
  • src/engraving/rendering/score/measurelayout.cpp
  • src/engraving/rendering/score/modifydom.cpp
  • src/engraving/rendering/score/modifydom.h
  • src/engraving/rendering/score/scorehorizontalviewlayout.cpp
  • src/engraving/rendering/score/segmentlayout.cpp
  • src/engraving/rendering/score/slurtielayout.cpp
  • src/engraving/rendering/score/systemlayout.cpp
  • src/engraving/rendering/score/tlayout.cpp
  • src/engraving/rw/read500/measureread.cpp
  • src/engraving/rw/read500/read500.cpp
  • src/engraving/rw/write/twrite.cpp
  • src/engraving/tests/midi/midirenderer_data/grace_after_rest.mscx
  • src/engraving/tests/midi/midirenderer_tests.cpp
  • src/engraving/tests/note_data/grace-on-rest-ref.mscx
  • src/engraving/tests/note_tests.cpp
  • src/engraving/tests/playback/playbackeventsrenderer_data/rest_with_grace_notes/rest_with_grace_notes.mscx
  • src/engraving/tests/playback/playbackeventsrendering_tests.cpp
  • src/importexport/capella/internal/capella.cpp
  • src/importexport/mei/internal/meiexporter.cpp
  • src/importexport/mei/internal/meiexporter.h
  • src/importexport/mei/internal/meiimporter.cpp
  • src/importexport/mei/internal/meiimporter.h
  • src/importexport/mei/tests/data/gracenote-03.mei
  • src/importexport/mei/tests/data/gracenote-03.mscx
  • src/importexport/mei/tests/mei_tests.cpp
  • src/importexport/mnx/internal/export/mnxexportsequences.cpp
  • src/importexport/mnx/internal/import/mnximportpartmeasures.cpp
  • src/importexport/musicxml/internal/export/exportmusicxml.cpp
  • src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp
  • src/importexport/musicxml/internal/import/importmusicxmlpass2.h
  • src/importexport/musicxml/tests/data/testGraceAfterOnRest.xml
  • src/importexport/musicxml/tests/data/testGraceBeforeRest.xml
  • src/importexport/musicxml/tests/data/testGraceBeforeRest_ref.mscx
  • src/importexport/musicxml/tests/data/testGraceRestSkipped.xml
  • src/importexport/musicxml/tests/data/testGraceRestSkipped_ref.mscx
  • src/importexport/musicxml/tests/musicxml_tests.cpp
  • src/importexport/ove/internal/importove.cpp
💤 Files with no reviewable changes (1)
  • src/engraving/dom/chord.h

Comment thread src/braille/internal/braille.cpp Outdated
Comment thread src/engraving/compat/midi/compatmidirenderinternal.cpp
Comment thread src/engraving/dom/chordrest.cpp
Comment thread src/engraving/editing/edit.cpp Outdated
Comment thread src/engraving/editing/edittie.cpp Outdated
Comment thread src/engraving/rw/read500/measureread.cpp
Comment thread src/importexport/capella/internal/capella.cpp Outdated
Comment thread src/importexport/musicxml/internal/import/importmusicxmlpass2.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/importexport/capella/internal/capella.cpp`:
- Around line 533-541: Add qDeleteAll(graceNotes) at the end of the first-pass
loop in readCapVoice, after all Capella note-object cases are processed and
before calculating endTick, so any remaining unattached grace notes are deleted.
🪄 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: CHILL

Plan: Pro

Run ID: 8c090080-5d9b-41f1-b873-28dc8a221078

📥 Commits

Reviewing files that changed from the base of the PR and between eaeffdb and c19f2e8.

📒 Files selected for processing (5)
  • src/braille/internal/braille.cpp
  • src/engraving/dom/chordrest.cpp
  • src/engraving/editing/edit.cpp
  • src/engraving/editing/edittie.cpp
  • src/importexport/capella/internal/capella.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/braille/internal/braille.cpp
  • src/engraving/editing/edittie.cpp
  • src/engraving/editing/edit.cpp
  • src/engraving/dom/chordrest.cpp

Comment thread src/importexport/capella/internal/capella.cpp
@miiizen miiizen self-assigned this Jul 15, 2026
manolo and others added 13 commits July 16, 2026 11:42
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Repair the grace-note handling that the review suggestions left malformed
and extend it for rest hosts:
- findLinkedChord accepts a ChordRest destination and resolves the linked
  grace chord by index, so grace notes on a rest are cloned into parts.
- cmdAddTie ties a grace-before note to the next grace note when one
  follows, falling back to the host main note only for chord hosts, so
  consecutive grace notes on a rest can be tied.
- the read500 MMRest branch attaches and clears any pending grace notes,
  mirroring the normal rest path so they do not carry into the next element.
Rest-hosted grace notes were emitted with default timing, so they all
sounded at the rest onset for the full rest length. Stagger them in
sequence, mirroring the placement of GraceChordCtx::buildRestGraceCtxList:
grace-before packs from the rest start, grace-after ends at the rest end,
and the group is capped at half the rest. Each grace keeps its own
play-event length and is shifted later as a whole so onTime stays below
offTime.
Fix the handling of grace chords that are queued until their host note or
rest is read:
- Capella and OVE keep voice-mismatched grace chords queued instead of
  deleting them, and free any that remain unattached at the end of the
  voice/measure to avoid both data loss and leaks.
- MusicXML resets the grace-after count before appending grace-before
  chords, so a bypassed grace-after step cannot leak a stale count into
  the next note.
@manolo
manolo force-pushed the 19701-grace-notes-on-rests branch from 6480525 to 3831888 Compare July 16, 2026 09:49
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.

Allow grace notes to be attached to rests

3 participants