subsequence.forms

Forms — the governing values for compositional structure.

Section is the payload home: a frozen (name, bars, energy, key) leaf. Form is a frozen tuple of Sections — constructible from Sections or plain (name, bars) tuples (lists coerce), editable by slot (Form.replace() / Form.insert() / Form.with_energy()), and concatenable with +. Repetition is Python: [verse, verse, chorus] == [verse] * 2 + [chorus] — no letters DSL.

Bind a Form with composition.form(form, at_end="stop"|"hold"|"loop"); generate one from a graph form with composition.form_freeze().

Sections compose as plain Python lists that Form coerces — Section is a leaf, not an operator algebra.

Module Contents

class subsequence.forms.Form(sections: Iterable[Any], key: str | None = None, scale: str | None = None)[source]

A frozen sequence of Sections — the editable, bindable form value.

List-friendly: the constructor coerces ("name", bars) tuples, so Form([("verse", 8), ("chorus", 8)]) and Form([Section("verse", 8), Section("chorus", 8)]) are the same value. Repetition is Python list arithmetic before construction.

A form may carry its own key/scale — the form tier of the key-source chain (Section.key overrides it; it overrides the composition key). A whole AABA in one key with one section borrowing another is Form([...], key="A") plus a Section(..., key="F").

Coerce any iterable of Sections / (name, bars) tuples.

describe() str[source]

A readable one-section-per-line summary.

insert(slot: int, section: Any) Form[source]

Insert a section at a 1-based slot (existing sections shift right).

slot may be len(form) + 1 to append.

replace(slot: int, section: Section | None = None, **changes: Any) Form[source]

Replace the section at a 1-based slot — whole, or by field.

form.replace(3, bars=16) stretches slot 3; form.replace(3, Section("drop", 16, energy=1.0)) swaps it out.

with_energy(energies: Dict[str, float]) Form[source]

Set the energy payload on named sections — {"chorus": 0.9}.

Every section whose name appears in the mapping takes the new value; naming a section the form does not contain raises. Energy ramps ((start, end) tuples) live in composition.energy(), not in the payload — a Section carries one number.

property bars: int[source]

Total length in bars.

class subsequence.forms.Section[source]

One section of a form — the payload home.

name[source]

The section name ("verse").

bars[source]

Length in bars (≥ 1).

energy[source]

The section’s energy level (0.0–1.0; the arranging dial). Read by p.energy and min_energy= gating; a composition.energy() dict overrides it (the dict is the later, performance-level dial).

key[source]

Optional key override — re-anchors key-relative content (degrees, romans, generated material, and key-relative section progressions bound with section_chords) to this section’s tonic. Absolute content (note names, MIDI pitches, frozen chords) is never moved, and chord-relative content (ChordTone, Approach) tracks the sounding chord rather than the key — see the three-intent model in the docs. The live graph engine (harmony(style=...)) stays in the composition key by design (a stateful walk does not transpose mid-stream).

scale[source]

Optional scale/mode override (e.g. "minor") — moves the mode as well as the tonic, so a section can genuinely change to the relative or parallel minor. Falls back to the form’s scale, then the composition’s.