feat(rendering): add per-beat rest display position via beat.restDisplayTone, beat.restDisplayOctave#2721
Conversation
| return ApplyNodeResult.Applied; | ||
| case 'restdisplaypitch': { | ||
| const pitchText = (p.arguments!.arguments[0] as AlphaTexTextNode).text.toUpperCase(); | ||
| const toneChar = pitchText[0] as keyof typeof Tone; |
There was a problem hiding this comment.
This code cannot be transpiled to C# and Kotlin.
You can reuse alphaTab.model.Tuning to parse such strings
| export enum Tone { | ||
| C = 0, | ||
| D = 2, | ||
| E = 4, | ||
| F = 5, | ||
| G = 7, | ||
| A = 9, | ||
| B = 11 | ||
| } |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
+ 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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@Danielku15 any thoughts on this? Also, the other three changes have been made.
There was a problem hiding this comment.
The issue of adding 0.5 is needed because the glyph baseline data contained in bravura_metadata.json varies per glyph.
This is on purpose. SMuFL specifies that the baseline y=0 should be where the staffline is.
- https://w3c-cg.github.io/smufl/latest/tables/rests.html#implementation-notes
- https://danielku15.github.io/smufl-viewer/?search=rest
Also for noteheads the baseline indicates the staffline.
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 we have a C4 note, and manually position a rest at C4. the rest glyph will be placed on the same staff-line following the common rest placement and offsets as usual.
calculateRestDisplaySteps should not divert from the general placement logic. adding 0.5 seem to be the wrong choice for this method.
What indeed could be is that some legacy/tech-debt areas in alphaTab interfere with the placement. If that's the case I can have a deeper look at the surroundings. In case of doubts you could always craft a MusicXML file and open it in MusicXML to check the placement logic.
A MusicXML example where we place rests at the same pitch as notes.
<note default-x="80.21" default-y="-50">
<pitch>
<step>C</step>
<octave>4</octave>
</pitch>
<duration>1</duration>
<voice>1</voice>
<type>quarter</type>
<stem>up</stem>
</note>
<note default-x="117.3" default-y="-20">
<rest>
<display-step>C</display-step>
<display-octave>4</display-octave>
</rest>
<duration>1</duration>
<voice>1</voice>
<type>quarter</type>
</note>
<note default-x="154.4" default-y="10">
<pitch>
<step>A</step>
<octave>5</octave>
</pitch>
<duration>1</duration>
<voice>1</voice>
<type>quarter</type>
<stem>down</stem>
</note>
<note default-x="191.49" default-y="-20">
<rest>
<display-step>A</display-step>
<display-octave>5</display-octave>
</rest>
<duration>1</duration>
<voice>1</voice>
<type>quarter</type>
</note>There was a problem hiding this comment.
Okay, as I stated previously, the reason for the offset is to handle the half note rest, which visually does not look correct. If that adjustment is not made, it may not be visually perceptible for some of the other rests but for a half note, it does not position correctly with the standard calculation.
See the test below for half note positioning as is:
packages/alphatab/test-data/visual-tests/rest-position/alto/rest-position-multi-voice-secondary-only-F3-line-1.png
My solution would be to implement a per rest adjustment value to compensate for the Smuffle glypths off-center placement that would be applied in calculateRestDisplaySteps.
If no adjustment is made then the half note and possibly the whole note rest will look incorrect.
I don't think there's anything wrong with your calculations in alphaTab. It's just an artifact of the off center positioning of the glyphs.
I'll leave it to you to determine what needs to be done,
| 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> = { |
There was a problem hiding this comment.
Please write only code which is safe for transpilation.
…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>
9625031 to
146eb34
Compare
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
Further details