ScoreFlowController

timelines.ScoreFlowController(measures, *, name_generator=None)

Score flow controller specialized for score data (MeasureData).

ScoreFlowController computes Flow paths from MeasureData, which contains measure-level flow control information (mc, mn, next[], volta, etc.).

The algorithm operates at the section level: 1. Derives atomic sections from next[] arrays OR accepts from partitura 2. Uses flow control markers + volta attributes to execute flow logic 3. Groups atomic sections into playthrough sections per FlowMode

The algorithm follows the ‘next’ field in measure data, using visit counts to choose which branch to take at repeat points. This matches the ms3 unfolding algorithm.

Attributes

Name Type Description
measures The source MeasureData.

Public API

  • get_sections(mode=None): Get sections (None=atomic, else playthrough)
  • iter_sections(mode=None): Iterate over sections
  • iter_units(): Iterate over MeasureUnits (folded skeleton)
  • compute_flow(mode): Compute Flow for the given mode

Examples

>>> controller = ScoreFlowController(measure_data)
>>> flow = controller.compute_flow()
>>> print(f"Unfolded: {flow.unfolded_length} measures")
>>> # Get atomic sections (folded structure)
>>> for sec in controller.get_sections():
...     print(f"{sec.id}: MC [{sec.mc_start},{sec.mc_end})")
>>> # Get playthrough sections for DEFAULT mode
>>> for sec in controller.get_sections(FlowMode.default):
...     print(f"MC [{sec.mc_start},{sec.mc_end})")
>>> # Iterate over MeasureUnits
>>> for unit in controller.iter_units():
...     print(f"MC {unit.mc}: next={unit.next}, jump_from={unit.jump_from}")

Methods

Name Description
check_invariants Check structural invariants of the atomic flow graph.
compute_all_flows Compute all possible Flows.
compute_flow Compute a single Flow using the specified mode.
create_flow_map Create a FlowMap in QB-space from a computed Flow.
create_flow_map_for_mode Convenience method to create a FlowMap for a specific mode.
diagram Show folded score map with atomic sections and flow control markers.
from_atomic_sections Initialize directly from atomic sections (e.g., from partitura).
get_atomic_section_coordinates Return a mapping of atomic section IDs to their start coordinates.
get_breaks Return all Break events derived from the score’s flow control.
get_flow_control_registry Return a FlowControlRegistry populated from this controller.
get_jumps Return all Jump events derived from the score’s flow control.
get_markers Return all named target markers (segno / coda / fine instances).
get_section_boundary_coordinates Return quarterbeat coordinates where section breaks occur.
get_sections Get list of sections.
iter_atomic_sections Iterate over atomic (indivisible) sections.
iter_sections Iterate over sections.
iter_units Iterate over MeasureUnits (the folded score skeleton).

check_invariants

timelines.ScoreFlowController.check_invariants()

Check structural invariants of the atomic flow graph.

The controller’s posture toward a malformed flow is detect-and-report, not crash: this method returns a list of :class:FlowDiagnostic describing every violation it finds, and an empty list when the flow is well-formed.

Currently checks the volta-follows-volta invariant: in the atomic flow graph a volta section can never have a to edge to another volta section. A prima volta’s only out-edge is the repeat back-edge (to the repeat-start, a non-volta section); a seconda volta is reached only from the repeat-start and continues into the music that follows the bracket. Two flow-adjacent voltas therefore indicate a malformed next array — most often a jump target that resolved to the wrong ending. This is the to (flow) edge relation, NOT score-order adjacency: volta sections are naturally adjacent in MC order, which is correct; they must not be connected by a to edge.

Returns

Name Type Description
list[FlowDiagnostic] One FlowDiagnostic(kind="volta_follows_volta", ...) per
list[FlowDiagnostic] offending edge, naming the source and destination section ids; an
list[FlowDiagnostic] empty list when no invariant is violated.

compute_all_flows

timelines.ScoreFlowController.compute_all_flows()

Compute all possible Flows.

For scores with optional repeats, this computes all valid paths.

Returns

Name Type Description
list[Flow] List of Flow objects.

compute_flow

timelines.ScoreFlowController.compute_flow(mode=None)

Compute a single Flow using the specified mode.

Parameters

Name Type Description Default
mode FlowMode | None The FlowMode to use. None is equivalent to ATOMIC. None

Returns

Name Type Description
Flow Computed Flow object.

create_flow_map

timelines.ScoreFlowController.create_flow_map(flow=None)

Create a FlowMap in QB-space from a computed Flow.

Overrides the base class to always use QB-space coordinates, since ScoreFlowController has MeasureUnit data.

Parameters

Name Type Description Default
flow Flow | None The Flow to create a map from. If None, computes DEFAULT flow. None

Returns

Name Type Description
FlowMap FlowMap with QB-space source coordinates.

create_flow_map_for_mode

timelines.ScoreFlowController.create_flow_map_for_mode(mode=FlowMode.default)

Convenience method to create a FlowMap for a specific mode.

Parameters

Name Type Description Default
mode FlowMode The FlowMode to use. FlowMode.default

Returns

Name Type Description
FlowMap FlowMap wrapping the computed Flow.

diagram

timelines.ScoreFlowController.diagram(
    width=70,
    unicode=True,
    show_graph=True,
    show_legend=True,
    mode='auto',
)

Show folded score map with atomic sections and flow control markers.

Parameters

Name Type Description Default
width int Total width of the diagram in characters. 70
unicode bool Use Unicode characters (True) or ASCII fallback (False). True
show_graph bool Whether to show section transition graph. True
show_legend bool Whether to show flow control event legend. True
mode str Rendering mode — "auto", "full", "sections", or "table". See :func:flow_control_diagram for details. 'auto'

Returns

Name Type Description
'Diagram' Diagram object (displays as ASCII in terminal, rich HTML in Jupyter).

from_atomic_sections

timelines.ScoreFlowController.from_atomic_sections(
    sections,
    measures=None,
    *,
    name_generator=None,
)

Initialize directly from atomic sections (e.g., from partitura).

Parameters

Name Type Description Default
sections list[AtomicSection] List of AtomicSection objects. required
measures EventData | None Optional MeasureData for detailed step computation. None
name_generator SegmentNameGenerator | None Strategy for labelling atomic sections. Stored for consistency with the regular constructor; the pre-built sections are not relabelled. Defaults to a fresh SegmentNameGenerator. None

Returns

Name Type Description
'ScoreFlowController' ScoreFlowController with pre-built atomic sections.

get_atomic_section_coordinates

timelines.ScoreFlowController.get_atomic_section_coordinates(flow=None)

Return a mapping of atomic section IDs to their start coordinates.

Each key is the section’s label (e.g. "A", "B", …) and the value is the quarterbeat coordinate of the section’s first measure.

When flow is provided the coordinates are unfolded (i.e. the running quarterbeat position in the playthrough order), which is required for cross-group coordinate transfer via an AlignmentBundle whose WarpMaps are built from unfolded note matches. Without flow, the folded quarterbeats from the measure lookup are returned (the coordinate resets at every repeat start).

Parameters

Name Type Description Default
flow 'Flow | None' A Flow computed from this controller (e.g. via compute_flow(FlowMode.default)). If given, unfolded coordinates are returned. None

Returns

Name Type Description
dict[str, Fraction] Ordered dict mapping section ID to quarterbeat start coordinate.

Raises

Name Type Description
RuntimeError If the measure lookup has not been built (controller created without MeasureData).

Examples

>>> controller = ScoreFlowController(measures)
>>> controller.get_atomic_section_coordinates()
{'A': Fraction(0, 1), 'B': Fraction(32, 1), ...}
>>> flow = controller.compute_flow(FlowMode.default)
>>> controller.get_atomic_section_coordinates(flow=flow)
{'A': Fraction(0, 1), 'B': Fraction(64, 1), ...}

get_breaks

timelines.ScoreFlowController.get_breaks()

Return all Break events derived from the score’s flow control.

Currently emits one Break per measure flagged as a section break. Each Break is positioned at the START coordinate of its MC and carries a label indicating the source MC.

Returns

Name Type Description
list['Break'] List of timetoalign.timelines.flowcontrol.Break objects, sorted
list['Break'] by coordinate.

get_flow_control_registry

timelines.ScoreFlowController.get_flow_control_registry()

Return a FlowControlRegistry populated from this controller.

Bundles the breaks, jumps, and named markers into a single object, which is the canonical registry shape used elsewhere in the library.

get_jumps

timelines.ScoreFlowController.get_jumps()

Return all Jump events derived from the score’s flow control.

Emits one Jump per repeat-end, plus one per D.S./D.C./to-coda instruction (including the _al_coda and _al_fine variants). Each jump’s from_coordinate is the END of the originating MC and its to_coordinate is the START of the destination MC.

Returns

Name Type Description
list['Jump'] List of timetoalign.timelines.flowcontrol.Jump objects, sorted
list['Jump'] by their from-coordinate.

get_markers

timelines.ScoreFlowController.get_markers()

Return all named target markers (segno / coda / fine instances).

Returns

Name Type Description
list[tuple[str, int, 'Coordinate']] List of (name, mc, coordinate) tuples, in MC order. name
list[tuple[str, int, 'Coordinate']] is the marker instance name (e.g., "segno", "coda",
list[tuple[str, int, 'Coordinate']] "codab", "fine"); coordinate is the start of the MC.

get_section_boundary_coordinates

timelines.ScoreFlowController.get_section_boundary_coordinates()

Return quarterbeat coordinates where section breaks occur.

A section break (as opposed to repeat/volta/jump boundaries) is a structural marker that separates large-scale sections such as movements. Each returned coordinate is the start (quarterbeat) of the first MC after the break, i.e. the point where the new section begins.

The list does not include 0 (start of the piece) nor the end.

Returns

Name Type Description
list[Fraction] Sorted list of quarterbeat boundary coordinates.

Raises

Name Type Description
RuntimeError If the measure lookup has not been built (controller created without MeasureData).

Examples

>>> controller = ScoreFlowController(measures)
>>> controller.get_section_boundary_coordinates()
[Fraction(305, 1), Fraction(1291, 1), Fraction(3125, 2)]

get_sections

timelines.ScoreFlowController.get_sections(mode=None)

Get list of sections.

This is the unified API for retrieving sections. Replaces the old get_atomic_sections() method with added support for playthrough sections.

Parameters

Name Type Description Default
mode FlowMode | None If None, returns AtomicSections (default). If specified, returns PlaythroughSections for that mode. None

Returns

Name Type Description
list[AtomicSection] | list[PlaythroughSection] List of AtomicSection if mode is None, otherwise list of PlaythroughSection.

Examples

>>> # Get atomic sections (folded structure)
>>> for sec in controller.get_sections():
...     print(f"{sec.id}: MC [{sec.mc_start},{sec.mc_end})")
>>> # Get playthrough sections for DEFAULT mode (unfolded)
>>> for sec in controller.get_sections(FlowMode.default):
...     print(f"MC [{sec.mc_start},{sec.mc_end})")

iter_atomic_sections

timelines.ScoreFlowController.iter_atomic_sections()

Iterate over atomic (indivisible) sections.

Each section is a tuple (start, end) representing a contiguous portion that cannot be split by flow control. For ScoreFlowController, these are MC-based coordinates from AtomicSections.

Yields

Name Type Description
tuple[Fraction, Fraction] Tuples of (start_mc, end_mc) as Fractions.

iter_sections

timelines.ScoreFlowController.iter_sections(mode=None)

Iterate over sections.

Parameters

Name Type Description Default
mode FlowMode | None If None, iterates over AtomicSections (default). If specified, iterates over PlaythroughSections for that mode. None

Yields

Name Type Description
AtomicSection | PlaythroughSection AtomicSection objects if mode is None, otherwise PlaythroughSection objects.

Examples

>>> # Iterate over atomic sections (folded structure)
>>> for sec in controller.iter_sections():
...     print(f"{sec.id}: MC [{sec.mc_start},{sec.mc_end})")
>>> # Iterate over playthrough sections (unfolded traversal)
>>> for sec in controller.iter_sections(FlowMode.default):
...     print(f"MC [{sec.mc_start},{sec.mc_end})")

iter_units

timelines.ScoreFlowController.iter_units()

Iterate over MeasureUnits (the folded score skeleton).

Yields

Name Type Description
MeasureUnit MeasureUnit objects in MC order.

Examples

>>> for unit in controller.iter_units():
...     print(f"MC {unit.mc}: {unit.mn}, next={unit.next}")