Skip to content

feat: model glissando, slide, and wavy-line spanners in mx::api - #414

Merged
webern merged 5 commits into
mainfrom
claude/issue-389-fixes-ipkpot
Aug 23, 2026
Merged

feat: model glissando, slide, and wavy-line spanners in mx::api#414
webern merged 5 commits into
mainfrom
claude/issue-389-fixes-ipkpot

Conversation

@webern

@webern webern commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Human Summary

Adds glissando, slide and wavy-line to mx::api. These were relatively similar to established patterns and thus straightforward.

Summary

glissando and slide were dropped entirely on read and could not be authored: NoteFunctions::parseNotations had an empty break for glissando and a commented-out body for slide, and the write side had no CurveType-style path for either. wavy-line read and wrote as an ordinary MarkType::wavyLine mark carrying only position/print data; core::WavyLine::type() is a required StartStopContinue the writer never set, so every wavy line came out type="start" regardless of source, and a trill extension read as a start/stop pair was written back as two starts that never close.

This adds all three as proper note-attached spanners:

  • GlissandoStart/GlissandoStop (new GlissandoData.h): one family for <glissando> and <slide>, discriminated by a GlissandoType field, following the same pattern CurveType already uses to share one shape between <slur> and <tied>. Carries pairing, the printed text (the element value, e.g. "gliss."), line appearance (line-type/dash-length/space-length), position, font, and color, plus slide-only trill-sound playback (accelerate/beats/first-beat/last-beat).
  • WavyLineStart/WavyLineContinue/WavyLineStop (new WavyLineData.h): <wavy-line> is a start/continue/stop spanner living inside a note's <ornaments>, not <notations> directly. Carries placement, color, an optional SMuFL glyph override, and its own trill-sound playback (start-note/trill-step/two-note-turn/accelerate/beats/second-beat/last-beat).
  • Both go through the writer-side SpannerNumber identity mechanism from Add API-level spanner identity for writer-side number assignment #297/SpannerNumberResolver pools numbers per staff, but number-level scope is the part #350: SpannerNumberResolver gained glissando, slide, and wavyLine number pools, independent from each other and from the existing slur/tie/wedge/octave-shift/bracket/dashes pools, and independent of each other even when <glissando> and <slide> overlap in the stream.
  • mx::impl: new GlissandoFunctions.h (templated over core::Glissando/core::Slide, mirroring CurveFunctions.h) and WavyLineFunctions.{h,cpp}, wired through NoteFunctions, OrnamentsFunctions, and NotationsWriter. Stops are always emitted before starts on a shared note, per the Question about ordering of starts and stops for e.g. slurs #139 rule.
  • Removed MarkType::wavyLine and the dead SlideFunctions stub (already commented out, referencing a SpannerType enum deleted years ago). This is a breaking change: MarkType::wavyLine's mark-based model could not express pairing at all, so it could never be more than a fallback.

Testing

  • New GlissandoApiTest.cpp and WavyLineApiTest.cpp: 13 cases covering round-trip pairing, printed text, line appearance, slide/wavy-line playback attributes, container placement (<notations> vs <ornaments>), stop-before-start ordering on a chained note, independent identity-number pools, and fromXml reads of the synthetic fixtures
  • make test-all green: core roundtrip, core unit, api/impl suite (6159 assertions in 560 test cases), api roundtrip (379 pinned, 0 failed)
  • Discovery went from 366 to 379 passing files; 13 newly-passing files are attributable to this change and pinned in roundtrip-baseline.txt, including the three files the issue names (lysuite/ly33h_Spanners_Glissando.xml, ksuite/k005a_Spanners_Slides.xml, and progress on ksuite/k003a_Ornaments.xml, whose wavy-line mismatch is now fixed though it still fails on an unrelated pre-existing tremolo attribute bug). No file regressed.
  • make fmt-check passes
  • A unity build (CMAKE_UNITY_BUILD=ON, batch size 0) compiles clean

References

Closes #389. Glissando and slide were dropped entirely on read and could
not be authored; a wavy line read and wrote as an unpaired mark that
always emitted type="start", so a trill extension read as a start/stop
pair came back as two starts and never closed.

- New api::GlissandoStart/GlissandoStop (GlissandoData.h), one family for
  <glissando> and <slide> discriminated by a GlissandoType field, matching
  the existing CurveType (slur/tie) pattern. Carries pairing, printed
  text, line appearance, print style, and (for slide) trill-sound
  playback timing.
- New api::WavyLineStart/Continue/Stop (WavyLineData.h) for <wavy-line>, a
  start/continue/stop spanner living inside <ornaments>. Carries
  placement, color, an optional SMuFL glyph override, and trill-sound
  playback.
- Both element families go through the existing SpannerNumber identity
  mechanism (#297, #350): SpannerNumberResolver gained glissando, slide,
  and wavy-line number pools, independent of each other and of the
  existing slur/tie/wedge/octave-shift/bracket/dashes pools.
- mx::impl: new GlissandoFunctions.h (templated over Glissando/Slide,
  mirroring CurveFunctions.h) and WavyLineFunctions.{h,cpp}; wired through
  NoteFunctions, OrnamentsFunctions, and NotationsWriter with correct
  containment (glissando/slide as top-level <notations> children,
  wavy-line inside <ornaments>) and stop-before-start ordering on a
  shared note (#139).
- Removed MarkType::wavyLine (breaking, as the issue anticipated -- its
  mark-based model could not express pairing at all) and the dead
  SlideFunctions stub.
- New GlissandoApiTest.cpp / WavyLineApiTest.cpp: 13 unit tests covering
  round-trip pairing, printed text, line appearance, slide/wavy-line
  playback attributes, container placement, stop-before-start ordering on
  a chained note, and independent identity-number pools, plus fromXml
  reads of the synthetic fixtures.
- roundtrip-baseline.txt gained 13 newly-passing corpus files, including
  the three the issue names (ly33h_Spanners_Glissando.xml,
  k005a_Spanners_Slides.xml, and progress on k003a_Ornaments.xml, which
  still fails on an unrelated pre-existing tremolo bug).

make test-all (core roundtrip, core unit, api-test, api-roundtrip) and
make fmt-check are green; a unity build also compiles clean.
@webern webern added feature new feature request breaking fixes or implementation that require breaking changes api Affects the mx::api layer impl Affects the mx::impl layer ai Issues opened by, or through, a coding agent. labels Aug 23, 2026 — with Claude
Comment thread src/include/mx/api/WavyLineData.h Outdated
Comment thread src/private/mxtest/api/roundtrip-baseline.txt Outdated
webern added 3 commits August 23, 2026 15:07
Signed-off-by: Matthew James Briggs <matthew.james.briggs@gmail.com>
Signed-off-by: Matthew James Briggs <matthew.james.briggs@gmail.com>
@webern

webern commented Aug 23, 2026

Copy link
Copy Markdown
Owner Author

/coverage

@github-actions

Copy link
Copy Markdown
Contributor

Coverage report

Core-dev coverage src/private/mx/core/

Metric Coverage Covered / Total
Lines 77.8% 28524 / 36672
Functions 74.2% 6358 / 8572
Branches 50.7% 22686 / 44753

API coverage src/private/mx/{api,impl,utility}/

Metric Coverage Covered / Total
Lines 88.4% 9106 / 10304
Functions 81.4% 3901 / 4793
Branches 54.8% 7788 / 14222

Core HTML report | API HTML report

Commit 67f829f433c63f83bcc2379c3c4b5cf21139c303.

The emscripten CI job (#392) started failing after the glissando/slide/
wavy-line spanner writer was added (#389): mxwrite crashed under Node with
"RuntimeError: memory access out of bounds" inside
mx::impl::NotationsWriter::getNotations(), even on a note with zero
notations.

Root cause: Emscripten's default wasm stack is 64 KiB, and the write path
is a deep, unoptimized (Debug build) C++ call chain -- DocumentManager ->
ScoreWriter -> PartWriter -> MeasureWriter -> NoteWriter ->
NotationsWriter -- that was already close to that limit. Extracting the
new glissando/slide and wavy-line loops into their own methods (mirroring
NotationsWriter's existing addArticulation/addOrnament/addTechnical
pattern) shrinks getNotations()'s own frame, but the crash persisted even
in the extracted method: the call chain's *cumulative* depth was the real
constraint, not any one function's frame size. Bisecting confirmed the
default 64 KiB stack was only marginally insufficient (as little as ~80
KiB sufficed); CMakeLists.txt now requests 1 MiB via emscripten's
-sSTACK_SIZE link flag, leaving comfortable headroom for further growth
in this call chain (e.g. #412, which plans to extend the same writer).

Verified locally with the pinned emsdk 6.0.6 toolchain: reproduced the
crash on the pre-fix commit, confirmed the extraction method alone did
not fix it, bisected the minimum working -sSTACK_SIZE, and confirmed a
clean `make wasm-test` (mxread/mxwrite/mxhide) passes with the
CMakeLists.txt change in place. `make api-test` (560 cases, 6159
assertions) still passes natively, confirming the NotationsWriter
extraction is behavior-preserving.
@webern
webern merged commit e8cf684 into main Aug 23, 2026
8 checks passed
@webern
webern deleted the claude/issue-389-fixes-ipkpot branch August 23, 2026 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai Issues opened by, or through, a coding agent. api Affects the mx::api layer breaking fixes or implementation that require breaking changes feature new feature request impl Affects the mx::impl layer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

api: model note-attached paired spanners (glissando, slide, wavy-line)

1 participant