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