Skip to content

feat(rendering): add per-beat rest display position via beat.restDisplayTone, beat.restDisplayOctave#2721

Open
roscopeeco wants to merge 1 commit into
CoderLine:developfrom
roscopeeco:feature/rest-position-v2
Open

feat(rendering): add per-beat rest display position via beat.restDisplayTone, beat.restDisplayOctave#2721
roscopeeco wants to merge 1 commit into
CoderLine:developfrom
roscopeeco:feature/rest-position-v2

Conversation

@roscopeeco
Copy link
Copy Markdown

Adds Beat.restDisplayTone (Tone enum) and Beat.restDisplayOctave to allow specifying the vertical position of a rest on the staff on a per-beat basis. The AlphaTex restdisplaypitch tag (e.g. r.4{restdisplaypitch E4}) sets these values. AccidentalHelper.calculateRestDisplaySteps converts the pitch to rendering steps, aligned to match the legacy RestPosition enum spacing (+0.5).

Includes 77 visual tests covering treble, bass, alto and tenor clefs across staff positions, durations, multi-voice scenarios, and variable line counts.

This PR only includes TypeScript changes

Issues

Fixes #

Proposed changes

Checklist

  • [ X] I consent that this change becomes part of alphaTab under it's current or any future open source license
  • Changes are implemented
  • [ X] New tests were added

Further details

  • This is a breaking change
  • [ X] This change will require update of the documentation/website

return ApplyNodeResult.Applied;
case 'restdisplaypitch': {
const pitchText = (p.arguments!.arguments[0] as AlphaTexTextNode).text.toUpperCase();
const toneChar = pitchText[0] as keyof typeof Tone;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This code cannot be transpiled to C# and Kotlin.
You can reuse alphaTab.model.Tuning to parse such strings

Comment thread packages/alphatab/src/model/Tone.ts Outdated
Comment on lines +6 to +14
export enum Tone {
C = 0,
D = 2,
E = 4,
F = 5,
G = 7,
A = 9,
B = 11
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea with the enum is good, but it is in conflict with how alphaTab handles tones at all other places. I'd prefer consistency (using raw numnber properties) until we can introduce such an enum consistently at all places.

}

const spelling = ModelUtils.resolveSpelling(bar.keySignature, noteValue, NoteAccidentalMode.Default);
return AccidentalHelper.calculateNoteSteps(bar.clef, spelling) + 0.5;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+ 0.5 seems like a workaround indicating to a problem elsewhere. Can we eliminate or move this to the actual place where things where the need for the workaround gets clear?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The issue of adding 0.5 is needed because the glyph baseline data contained in bravura_metadata.json varies per glyph.
For example, the black note head is perfectly centred vertically [0.5, -0.5] , but the rest glyphs all have different off-centre base lines.

The 0.5 is used to calculate the perfect positioning of the half note rest, so everything is adjusted by 0.5,
but visually the 0.5 aligns the half note perfectly.

Do we want all rests, quarter note, eighth note, 16th note, 32nd etc. notes to be aligned perfectly centred visually on the Y axis,either on the line or in the space, or is it just the half note that needs to be visually perfect?

If all rests need to be visually perfectly centered, then we'd need to include the Bravura metadata coordinates in the
calculation. Creating a helper function to do this.

We could call this after calling calculateRestDisplaySteps and add the result, the return value, to replace the 0.5 hard coded value.

Comment on lines +4 to +15
type ClefData = {
tex: string;
filler: string;
positions: [string, string][];
primaryFiller: string;
secondaryFiller: string;
multiVoicePrimary: [string, string];
multiVoiceSecondary: [string, string];
};

// Staff positions from bottom to top for each clef: [pitch, label]
const clefs: Record<string, ClefData> = {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please write only code which is safe for transpilation.

https://alphatab.net/docs/contributing#toolchain

…tch tag

Adds Beat.restDisplayTone (Tone enum) and Beat.restDisplayOctave to allow
specifying the vertical position of a rest on the staff on a per-beat basis.
The AlphaTex restdisplaypitch tag (e.g. r.4{restdisplaypitch E4}) sets these
values. AccidentalHelper.calculateRestDisplaySteps converts the pitch to
rendering steps, aligned to match the legacy RestPosition enum spacing (+0.5).

Includes 77 visual tests covering treble, bass, alto and tenor clefs across
all staff positions, durations, multi-voice scenarios, and variable line counts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@roscopeeco roscopeeco force-pushed the feature/rest-position-v2 branch from 9625031 to 146eb34 Compare June 1, 2026 18:01
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.

2 participants