OctaveSpanTracker class

Resolves WHICH OctaveMark is in force at each point of an element stream.

ADR-003 fixed the meaning of Pitch: it is always the SOUNDING pitch, and an octave displacement changes only WHERE THE NOTE IS PRINTED. That rule already lived in exactly one place for the clef axis (StaffPositionCalculator.calculate subtracting clef.octaveShift * 7); the bracket axis — 8va/8vb/15ma/15mb/22da/22db — was never wired up at all. Measured before this class existed: C6 printed at staffPosition 8 / Y 12.0 with NO mark, and at staffPosition 8 / Y 12.0 under every one of the six bracket types — all six identical, i.e. OctaveMark.octaveShift had zero consumers. After: C6 under 8va prints at staffPosition 1 / Y 54.0 and under 8vb at staffPosition 15 / Y -30.0.

Activation rule (this is the contract every consumer relies on)

  1. A mark becomes active at the point it appears in the element stream — not at the head of its measure. Notes written before the bracket in the same bar keep the previous displacement, exactly the way a mid-measure Clef change is handled by the layout engine.
  2. A mark stays active until the END of its OctaveMark.endMeasure, or until another OctaveMark appears (the new one simply replaces it — there is no nesting in the model).
  3. Degenerate spans. Both MusicXML and MEI importers build the mark with startMeasure: 0, endMeasure: 0 because neither format carries the span length on the START element: MusicXML closes an <octave-shift> with a separate type="stop" direction (which _musicXmlOctaveShift deliberately maps to null, so no element is emitted) and MEI puts the end in @endid. When endMeasure <= startMeasure the span is therefore treated as ending at the end of the measure the mark was found in — the conservative choice, since over-extending a bracket silently transposes music the author never marked. Widening that to true multi-measure spans requires the importers to record the stop; until they do, an imported 8va displaces its own bar only.

The tracker is single-pass and order-sensitive: feed it every element of the staff exactly once, in document order, and call reset before re-walking.

It is only correct for a MONOPHONIC element stream. A polyphonic bar serialises voice 1 in full before voice 2, so a single-pass walk gives the same marking a different meaning depending on which voice the author typed it in — see OctaveSpanTimeline, which is what the layout engine and the renderers use, and which resolves the span by musical TIME instead.

Constructors

OctaveSpanTracker()

Properties

active OctaveMark?
The mark currently in force, or null outside every span.
no setter
hashCode int
The hash code for this object.
no setterinherited
octaveShift int
Displacement in octaves in force right now (0 outside every span).
no setter
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

advance(MusicalElement element, {int measureIndex = 0}) int
Advances the tracker to element (which lives in measure measureIndex) and returns the displacement in force AT that element.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
reset() → void
Clears all span state. Call before re-walking the same staff.
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited