Skip to content

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

Description

@rpatters1

What

MusicXML attaches three paired spanners to notes inside <notations>:

  • <glissando type="start|stop"> — the discrete case; defaults to a wavy line
  • <slide type="start|stop"> — continuous between the two pitches; defaults to a solid line
  • <ornaments><wavy-line type="start|continue|stop"> — trill extensions and vibrato lines

mx::api models none of them as spanners.

Glissando and slide are dropped on read and cannot be authored. mx::core models both
elements and NotationsChoice admits them, but NoteFunctions::parseNotations has an empty
break for Kind::glissando and a commented-out body for Kind::slide
(src/private/mx/impl/NoteFunctions.cpp:249-257), with a dead SlideFunctions.cpp stub behind it.
On the write side CurveType offers only slur and tie
(src/include/mx/api/CurveData.h:45-50), and NotationsWriter skips any entry in curveStarts,
curveContinuations, or curveStops whose type is neither (NotationsWriter.cpp:112, 133, 154),
so the generic curve path cannot carry them either. NoteAttachmentData has commented-out
spannerStarts / spannerStops vectors (src/include/mx/api/NoteAttachmentData.h:24-25), but
they are not a design in waiting: they are typed std::vector<SpannerData>, a flat
enum-discriminated struct that was deliberately replaced by today's SpannerStart / SpannerStop
in 781a461 (2016). The vectors referenced the deleted type and were commented out rather than
retyped. SlideFunctions::parseSlide is the same story — added already commented out, still naming
the long-dead api::SpannerType::slide.

Wavy lines lose their pairing. MarkType::wavyLine reads and writes as an ordinary ornament
mark carrying position and print data only. core::WavyLine::type() is a required
StartStopContinue that the writer never sets (NotationsWriter.cpp:698-703), so it falls back to
its default tag, start. Every wavy line mx writes therefore comes out type="start": a trill
extension read in as a start/stop pair is written back as two starts, and the line never closes.
number and smufl are neither read nor written. The reader is symmetric —
OrnamentsFunctions.cpp:128-131 keeps position and print and discards the rest.

Why

Glissandi, slides, and trill/vibrato extensions are ordinary notation, not edge cases. Today an
mx::api consumer cannot see them at all in a file that has them, and an author has no way to write
one.

The information is already available and is being thrown away in mx::impl on the way to
mx::api. mx::core models the full attribute set of all three elements, including the printed
text on <glissando> and <slide> — the element value, which is where a label such as gliss.
lives.

The checked-in corpus shows real-world use, not just synthetic coverage (data/corpus.xml):
wavy-line in 16 files, 14 of them wild; slide in 8, 6 wild; glissando in 6, 4 wild. Wild usage
of number and line-type is common, which is precisely the pairing and appearance information the
api cannot currently express. None of these files appear in
src/private/mxtest/api/roundtrip-baseline.txt, so none of this is defended by the api round-trip
gate. data/api.features.xml already marks all three support="partial".

Requirements

  1. Pairing. Read and write start/stop for glissando and slide, and start/continue/stop
    for wavy-line, attached to the notes that carry them. Two wavy lines on one note (a stop and a
    start) must be expressible.
  2. Identity and number. Simultaneously open spanners of the same kind are distinguished by
    number. This must go through the existing writer-side identity mechanism (SpannerNumber,
    Add API-level spanner identity for writer-side number assignment #297, SpannerNumberResolver pools numbers per staff, but number-level scope is the part #350) rather than asking the caller to assign numbers; each element gets its own per-part
    numbering pool. SpannerNumberClass in SpannerNumberResolver.cpp has no member for any of the
    three today.
  3. Printed text. <glissando> and <slide> carry text content printed alongside the line. It
    needs a home in the api; mx::core exposes it as the element value.
  4. Line appearance. line-type, dash-length, space-length on glissando and slide.
    api::LineData already models these and impl/LineFunctions.h already reads and writes them
    generically.
  5. Print style. Position, font, and color on all three; placement and smufl additionally on
    wavy-line.
  6. Playback. The trill-sound group that mx::core already models: accelerate, beats,
    first-beat, last-beat on slide; start-note, trill-step, two-note-turn, accelerate,
    beats, second-beat, last-beat on wavy-line.
  7. Correct container on write. <glissando> and <slide> are top-level <notations> children;
    <wavy-line> sits inside <ornaments>.
  8. Stop before start on a shared note. MusicXML requires that when several elements with the
    same tag appear on one note, their document order match score order — an incoming stop precedes
    an outgoing start. This was fixed for slurs and ties in Question about ordering of starts and stops for e.g. slurs #139 and applies equally to all three of
    these elements, most visibly to a chain of glissandi.
  9. Round-trip evidence. The existing synthetic fixtures (data/synthetic/glissando.3.0.xml,
    slide.3.0.xml, wavy-line.3.0.xml, and the later-version siblings) plus representative wild
    files such as data/lysuite/ly33h_Spanners_Glissando.xml,
    data/ksuite/k005a_Spanners_Slides.xml, and data/ksuite/k003a_Ornaments.xml should end up
    pinned in roundtrip-baseline.txt.

Scope and compatibility

Design to be worked out separately. The three elements should be solved together — they are the same
problem, and solving them one at a time invites three incompatible shapes.

Expect this to be breaking. MarkType::wavyLine is a public enumerator whose mark-based model
cannot express pairing at all; it is likely to be superseded or deprecated by whatever the spanner
model turns out to be.

<glissando> and <slide> differ only in what they imply about the pitch motion between the two
notes, and carry the same attributes. The api should not force a consumer to handle them as two
unrelated features.

References

Same layer and shape as #291 (continuation tie) and #324 (unmodeled direction-type stubs). Spanner
identity and numbering background in #297 and #350. The stop-before-start ordering rule in
requirement 8 is the same one #139 established for slurs and ties.

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiAffects the mx::api layerfeaturenew feature request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions