Repository navigation
feat(render): write chords and multiple voices on numbered staves - #3017
Merged
Merged
Conversation
Numbered (jianpu) staves write every chord note and every voice instead of a single number of the first voice. - Chords stack their numbers upwards from the line of single numbers. Every number keeps its own octave dots, accidental and augmentation dot; underlines and dashes belong to the lowest number. - Every voice is written on its own row, voice 1 on top, aligned in time. A system without further voices keeps a single row; an empty first voice keeps its blank row. Further rows use the secondary voice colour, their time signature copy included. - The numbers sit on half-line steps of the renderer's line grid. Each bar collects what its chords and rows need; the first bar of a staff merges the needs of the system when the bars are scaled, and every bar moves its numbers onto the shared steps, so chord numbers and rows line up along a system. - Rows are spaced by their content: what hangs below the upper row, two staff spaces, and what rises above the lower row, paired per bar. Time signature copies count in the bars that write them. - The time signature and repeat dots are written in front of every row. - Ties are drawn per note, effect slurs once per chord. - Low octave dots go below the duration underlines; the underline spanning a group sits directly below the numbers. - NumberedStaffConfig.notes with NumberedNoteMode (AllNotes, SingleNote) and the alphaTex property `notes`; SingleNote writes the highest note of voice 1. - Line renderers expose staff rows for bar-level glyphs (time signatures, repeat dots, bar lines, dead-slapped crosses). - The horizontal layout scales the bars of its system once it holds all of them, as the page layouts do. The staff bottom is reset and registered with the staff overflows. Refs #2437
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
AI-authored disclosure (
alphatab-ai-authored-v1)Portions of this content were authored by an AI agent. The agent has read
AGENTS.md and the human submitter accepts responsibility for
compliance with the rules in that document.
Issues
Fixes #2437
Proposed changes
Numbered (jianpu) staves wrote a single number per beat and only the first voice, as Guitar Pro does. Real jianpu writes chords and several parts, so alphaTab dropped musical content. Numbered staves now write every chord note and every voice, following printed jianpu: 朝鲜歌曲集(简谱版), 音乐出版社 1957; 战地新歌, 1970s. Piano jianpu sheets served as a secondary source.
Notation
NumberedStaffConfig.notes = NumberedNoteMode.SingleNote. In alphaTex this is\staffDisplay numbered { notes singleNote }or\defaultStaffDisplay. It writes the highest note of voice 1.Layout
Slots. Every number sits on a half-line step of the numbered renderer's existing (invisible) line grid, like a note head on a five-line staff. The renderer's line count follows from the rows and chord positions it needs. Bar lines, staff height, skylines and overflows then come from the existing code.
Alignment along a system. Each bar records what its chords and rows need while it is laid out. When the bars of a system are scaled, the first bar of the staff merges these needs. They are shared through the staff's existing shared layout data, and every bar moves its numbers onto the shared steps. Chord numbers and rows therefore line up along a system, and the horizontal spacing is not affected.
Row spacing. Rows are spaced by their content, per bar:
Where a time signature is written in front of both rows, the rows are at least its height plus the staff content padding apart. The system takes the maximum over its bars. Printed jianpu keeps about 1.6–1.9 digit heights between the rows; two staff spaces is about 1.5.
Generic engine changes (please review these specifically)
LineBarRendererexposes staff rows (staffRowLineCount,staffRowCount,getStaffRowOffset). Header bar lines, repeat dots and dead-slapped crosses are laid out within the first row. Defaults keep the current behaviour for every other notation.HorizontalScreenLayoutscales the bars of its system once the system holds all of them, as the page layouts already do. Before, it scaled every bar right after adding it.RenderStaff.staffBottomis reset and registered together with the staff overflows. Before, it was a running maximum that also kept bars reverted from a system.BarRendererBase.remeasureVertically()(protected) measures overflows again after a vertical move, without a new layout.API (additive, nothing removed)
NumberedNoteMode { AllNotes = 0, SingleNote = 1 }NumberedStaffConfig.notes(defaultAllNotesviaRenderStylesheet.numberedConfig)EngravingSettings.numberedChordNoteGapnoteson\staffDisplay numberedand\defaultStaffDisplay, with import and exportThe default display changes visibly: numbered staves now show all chord notes and voices. Single note mode restores one number per beat. This is listed under the upgrade notes of the 1.10 release notes.
Tests
NumberedChordsunit tests, rendered with the visual-test alphaSkia setup so values match on TS, C# and Kotlin. They assert explicit positions through the bounds lookup:IncrementalResize: numbered voices and chord slots. Each resize step equals a fresh render.ScaleToWidthCallCount: one call per renderer and cycle for numbered staves, in page and horizontal layout.numbered-chords,numbered-chords-grace-dead,numbered-chords-tuplets,numbered-chord-ties,numbered-voices,numbered-voices-sparse,numbered-voices-4,numbered-single-note;numbered,numbered-durations,numbered-tuplets,numbered-whole-rests,general/colors,colors-disabled,colors-voice-cascade,tremolo-flags-mixed.notes.develop. Measured with V8's sampling heap profiler onnumbered.gp.Checklist
AI authorship disclosure
(
alphatab-ai-authored-v1) is present at the top of this body, and I havepersonally reviewed every change and can explain each one
Further details
website/