Version: 1.8.3 (also reproduced on 1.8.4)
Summary
alphaTex pairs {slur <id>} in file order, not time order. Every voice of a staff is written as a block, so a slur that starts in a later-written voice and ends on a note in an earlier-written voice is read backwards: its "origin" sits in a later bar than its "destination". This shape is common in piano music, where a slur runs from an inner voice up into the top voice. The renderer then:
- throws when the reversed slur crosses a line break, with either
TypeError: Cannot read properties of undefined (reading 'barRenderers') in TieGlyph.doLayout (called from ScoreBarRenderer._finalizeTies), or
TypeError: Cannot read properties of null (reading 'getBeatDirection') in ScoreSlurGlyph.calculateTieDirection
- otherwise draws a path with
NaN control points (nothing sensible appears)
Whether it throws depends on where line breaks fall, so the same score can render at one width and crash at another.
Minimal repro (alphaTex)
\track "P"
\staff {score} \tuning piano \clef G2 \ts (4 4)
\voice
:1 c4 |
:1 e4{slur a} |
\voice
:1 g4{slur a} |
:1 r |
The slur is meant to run from the voice-2 G4 in bar 1 to the voice-1 E4 in bar 2.
ScoreRenderer (SVG engine, enableLazyLoading = false), width 300: throws ...reading 'barRenderers'.
- Width 600 and wider: renders, but the slur path contains
NaN.
It also breaks a round trip through alphaTab's own exporter
A MusicXML file with the same cross-voice slur, loaded with ScoreLoader, renders correctly. Passing it through AlphaTexExporter then AlphaTexImporter and rendering reproduces the crash above. So alphaTex, as exported by alphaTab, cannot currently carry this slur.
A workaround that confirms the cause
After readScore(), walk the notes. For any note whose slurDestination is earlier (bar index, then displayStart) than the note itself, swap origin and destination, then call score.finish(settings). With only that change:
- the repro renders at every width with no
NaN;
- on a corpus of 744 converted classical piano movements, clean renders at four widths (600–1600 px) went from 543 to 711.
Two possible fixes:
- the alphaTex importer orders each slur pair by position after reading; or
- the syntax gains a way to mark which note begins the slur.
In either case, calculateTieDirection should not assume lookupStartBeatRenderer() is non-null. From reading develop, #2800 changes the line-break branch of TieGlyph.doLayout but leaves that call as it is; I have not run develop to confirm.
Version: 1.8.3 (also reproduced on 1.8.4)
Summary
alphaTex pairs
{slur <id>}in file order, not time order. Every voice of a staff is written as a block, so a slur that starts in a later-written voice and ends on a note in an earlier-written voice is read backwards: its "origin" sits in a later bar than its "destination". This shape is common in piano music, where a slur runs from an inner voice up into the top voice. The renderer then:TypeError: Cannot read properties of undefined (reading 'barRenderers')inTieGlyph.doLayout(called fromScoreBarRenderer._finalizeTies), orTypeError: Cannot read properties of null (reading 'getBeatDirection')inScoreSlurGlyph.calculateTieDirectionNaNcontrol points (nothing sensible appears)Whether it throws depends on where line breaks fall, so the same score can render at one width and crash at another.
Minimal repro (alphaTex)
The slur is meant to run from the voice-2 G4 in bar 1 to the voice-1 E4 in bar 2.
ScoreRenderer(SVG engine,enableLazyLoading = false), width 300: throws...reading 'barRenderers'.NaN.It also breaks a round trip through alphaTab's own exporter
A MusicXML file with the same cross-voice slur, loaded with
ScoreLoader, renders correctly. Passing it throughAlphaTexExporterthenAlphaTexImporterand rendering reproduces the crash above. So alphaTex, as exported by alphaTab, cannot currently carry this slur.A workaround that confirms the cause
After
readScore(), walk the notes. For any note whoseslurDestinationis earlier (bar index, thendisplayStart) than the note itself, swap origin and destination, then callscore.finish(settings). With only that change:NaN;Two possible fixes:
In either case,
calculateTieDirectionshould not assumelookupStartBeatRenderer()is non-null. From readingdevelop, #2800 changes the line-break branch ofTieGlyph.doLayoutbut leaves that call as it is; I have not run develop to confirm.