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)
- 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.
- 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).
- Degenerate spans. Both MusicXML and MEI importers build the mark with
startMeasure: 0, endMeasure: 0because neither format carries the span length on the START element: MusicXML closes an<octave-shift>with a separatetype="stop"direction (which_musicXmlOctaveShiftdeliberately maps tonull, so no element is emitted) and MEI puts the end in@endid. WhenendMeasure <= startMeasurethe 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
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 measuremeasureIndex) 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