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}")