subsequence.composition ======================= .. py:module:: subsequence.composition .. autoapi-nested-parse:: The top-level ``Composition`` — the object a whole piece is built on. ``Composition`` is the single entry point: it owns the clock, the patterns, the harmony and form state, and the MIDI output. You create one, decorate pattern functions onto it with ``@composition.pattern``, and call ``play()``. ``HarmonyView`` and ``ScheduleContext`` here are the read-only views handed to pattern and callback functions at run time. Module Contents --------------- .. py:class:: Composition(output_device: Optional[str] = None, bpm: float = 120, time_signature: Tuple[int, int] = (4, 4), key: Optional[str] = None, scale: Optional[str] = None, seed: Optional[int] = None, record: bool = False, record_filename: Optional[str] = None, zero_indexed_channels: bool = False, latency_ms: float = 0.0) The top-level controller for a musical piece. The ``Composition`` object manages the global clock (Sequencer), the harmonic progression (HarmonicState), the song structure (subsequence.form_state.FormState), and all MIDI patterns. It serves as the main entry point for defining your music. Typical workflow: 1. Initialize ``Composition`` with BPM and Key. 2. Define harmony and form (optional). 3. Register patterns using the ``@composition.pattern`` decorator. 4. Call ``composition.play()`` to start the music. Initialize a new composition. :param output_device: The exact name of the MIDI output port to use, as reported by ``mido.get_output_names()``. Matching is strict — the string must equal an entry in that list verbatim. On Linux/ALSA, names include the client and port IDs (e.g. ``"Scarlett 2i4 USB:Scarlett 2i4 USB MIDI 1 16:0"``); the trailing ``:client:port`` digits are assigned in connection order and can change between reboots or when a virtual port is recreated. To look up the current names:: import mido for n in mido.get_output_names(): print(n) If ``None``, Subsequence auto-discovers — uses the only available device, or prompts to choose if several exist. :param bpm: Initial tempo in beats per minute (default 120). :param key: The root key of the piece (e.g., "C", "F#", "Bb"). Required if you plan to use ``harmony()``. :param scale: The scale/mode of the piece (e.g. "minor", "dorian", or any registered scale name). Used to resolve scale degrees in motifs; defaults to major (ionian) when unset. :param seed: An optional integer for deterministic randomness. When set, every random decision (chord choices, drum probability, etc.) will be identical on every run. :param record: When True, record all MIDI events to a file. :param record_filename: Optional filename for the recording (defaults to timestamp). :param zero_indexed_channels: When False (default), MIDI channels use 1-based numbering (1-16) matching instrument labelling. Channel 10 is drums, the way musicians and hardware panels show it. When True, channels use 0-based numbering (0-15) matching the raw MIDI protocol. :param latency_ms: Physical output latency of the primary device in milliseconds, for delay compensation (default 0.0, must be non-negative). Set this when the primary output sounds late (e.g. a software sampler) so Subsequence delays faster devices to line everything up. See ``midi_output()`` for additional devices. .. rubric:: Example .. code-block:: python comp = subsequence.Composition(bpm=128, key="Eb", seed=123) .. py:method:: cc_forward(cc: int, output: Union[str, Callable], *, channel: Optional[int] = None, output_channel: Optional[int] = None, mode: str = 'instant', input_device: subsequence.midi_utils.DeviceId = None, output_device: subsequence.midi_utils.DeviceId = None) -> None Forward an incoming MIDI CC to the MIDI output in real-time. Unlike ``cc_map()`` which writes incoming CC values to ``composition.data`` for use at pattern rebuild time, ``cc_forward()`` routes the signal directly to the MIDI output — bypassing the pattern cycle entirely. Both ``cc_map()`` and ``cc_forward()`` may be registered for the same CC number; they operate independently. :param cc: Incoming CC number to listen for (0–127). :param output: What to send. Either a **preset string**: - ``"cc"`` — identity forward, same CC number and value. - ``"cc:N"`` — forward as CC number N (e.g. ``"cc:74"``). - ``"pitchwheel"`` — scale 0–127 to -8192..8191 and send as pitch bend. Or a **callable** with signature ``(value: int, channel: int) -> Optional[mido.Message]``. Return a fully formed ``mido.Message`` to send, or ``None`` to suppress. ``channel`` is 0-indexed (the incoming channel). :param channel: If given, only respond to CC messages on this channel. Uses the same numbering convention as ``cc_map()``. ``None`` matches any channel (default). :param output_channel: Override the output channel. ``None`` uses the incoming channel. Uses the same numbering convention as ``pattern()``. :param mode: Dispatch mode: - ``"instant"`` *(default)* — send immediately on the MIDI input callback thread. Lowest latency (~1–5 ms). Instant forwards are **not** recorded when recording is enabled. - ``"queued"`` — inject into the sequencer event queue and send at the next pulse boundary (~0–20 ms at 120 BPM). Queued forwards **are** recorded when recording is enabled. .. rubric:: Example .. code-block:: python comp.midi_input("Arturia KeyStep") # CC 1 → CC 1 (identity, instant) comp.cc_forward(1, "cc") # CC 1 → pitch bend on channel 1, queued (recordable) comp.cc_forward(1, "pitchwheel", output_channel=1, mode="queued") # CC 1 → CC 74, custom channel comp.cc_forward(1, "cc:74", output_channel=2) # Custom transform — remap CC range 0–127 to CC 74 range 40–100 import subsequence.midi as midi comp.cc_forward(1, lambda v, ch: midi.cc(74, int(v / 127 * 60) + 40, channel=ch)) # Forward AND map to data simultaneously — both active on the same CC comp.cc_map(1, "mod_wheel") comp.cc_forward(1, "cc:74") .. py:method:: cc_map(cc: int, data_key: str, channel: Optional[int] = None, min_val: float = 0.0, max_val: float = 1.0, input_device: subsequence.midi_utils.DeviceId = None) -> None Map an incoming MIDI CC to a ``composition.data`` key. When the composition receives a CC message on the configured MIDI input port, the value is scaled from the CC range (0–127) to *[min_val, max_val]* and stored in ``composition.data[data_key]``. This lets hardware knobs, faders, and expression pedals control live parameters without writing any callback code. **Requires** ``midi_input()`` to be called first to open an input port. :param cc: MIDI Control Change number (0–127). :param data_key: The ``composition.data`` key to write. :param channel: If given, only respond to CC messages on this channel. Uses the same numbering convention as ``pattern()`` (1-16 by default, or 0-15 with ``zero_indexed_channels=True``). ``None`` matches any channel (default). :param min_val: Scaled minimum — written when CC value is 0 (default 0.0). :param max_val: Scaled maximum — written when CC value is 127 (default 1.0). :param input_device: Only respond to CC messages from this input device (index or name). ``None`` responds to any input device (default). .. rubric:: Example .. code-block:: python comp.midi_input("Arturia KeyStep") comp.cc_map(74, "filter_cutoff") # knob → 0.0–1.0 comp.cc_map(7, "volume", min_val=0, max_val=127) # volume fader # Multi-device: only listen to CC 74 from the "faders" controller comp.cc_map(74, "filter", input_device="faders") .. py:method:: chords(*, channel: int, progression: subsequence.progressions.ProgressionSource, harmonic_rhythm: subsequence.progressions.HarmonicRhythmSpec, bars: Optional[float] = None, beats: Optional[float] = None, voicing: subsequence.progressions.VoicingSpec = (3, 4), velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_CHORD_VELOCITY, detached: Optional[float] = None, root: int = 60, key: Optional[str] = None, seed: Optional[int] = None, device: subsequence.midi_utils.DeviceId = None, mirrors: Optional[Iterable[subsequence.pattern.MirrorSpec]] = None) -> subsequence.progressions.Progression Declare a self-contained chord part: a progression at a chosen harmonic rhythm. The one-call form of ``p.progression()`` — it registers a pattern on *channel* that plays *progression* across *bars* (or *beats*), each chord lasting a length drawn from *harmonic_rhythm* (the musical term for how often the chords change). It needs no ``composition.harmony()`` call and, with an explicit chord list or a ``key=``, no composition key either — so a drums-plus-one-chord-part sketch stays simple. The progression is realised once, up front, and the same timeline plays every cycle (a stable phrase). That timeline is returned so you can see exactly what was chosen — ``print(comp.chords(...))``. :param channel: MIDI channel for the chord part. :param progression: A chord-graph style name to generate from, or an explicit list of chords (``Chord`` objects or names like ``["Cm7", "Dbmaj7"]``). :param harmonic_rhythm: How long each chord lasts — a number, a list of lengths, or ``between(low, high, step=...)``. See ``p.progression()``. :param bars / beats: Length of the part (defaults to 4 beats if neither is given). ``bars`` uses the composition's time signature. :param voicing: Notes per chord — an int, or a ``(low, high)`` range (e.g. ``(3, 4)``). :param velocity: MIDI velocity, or a ``(low, high)`` tuple for per-voice humanisation. :param detached: Beats of silence before each next chord (``duration = length - detached``). :param root: MIDI root the voicings are centred on (e.g. 48 = C3). :param key: Key for a generated progression; defaults to the composition key. :param seed: Seed for the (otherwise fixed) realisation; defaults to the composition seed, so the part is reproducible. :param device: Optional output-device override. :param mirrors: Optional additional ``(device, channel)`` destinations. :returns: The realised :class:`~subsequence.progressions.Progression`. .. py:method:: clear_tweak(name: str, *param_names: str) -> None Remove tweaked parameters from a running pattern. If no parameter names are given, all tweaks for the pattern are cleared and every ``p.param()`` call reverts to its default. :param name: The function name of the pattern. :param \*param_names: Specific parameter names to clear. If omitted, all tweaks are removed. .. py:method:: clock_output(enabled: bool = True) -> None Send MIDI timing clock to connected hardware. When enabled, Subsequence acts as a MIDI clock master and sends standard clock messages on the output port: a Start message (0xFA) when playback begins, a Clock tick (0xF8) on every pulse (24 PPQN), and a Stop message (0xFC) when playback ends. This allows hardware synthesizers, drum machines, and effect units to slave their tempo to Subsequence automatically. **Note:** Clock output is automatically disabled when ``midi_input()`` is called with ``clock_follow=True``, to prevent a clock feedback loop. :param enabled: Whether to send MIDI clock (default True). .. rubric:: Example .. code-block:: python comp = subsequence.Composition(bpm=120, output_device="...") comp.clock_output() # hardware will follow Subsequence tempo .. py:method:: current_chord() -> Optional[Any] The chord sounding at the playhead, or ``None`` without harmony. Reads the harmony window at the current pulse, so it stays accurate under variable harmonic rhythm and clock lookahead (the engine's ``current_chord`` flips *lookahead* beats early — this does not). Falls back to the engine's chord before playback starts. The chord may be a decorated wrapper (``Am9``, ``C/G``) when the sounding span is spiced; it duck-types the ``Chord`` voicing protocol either way. .. py:method:: display(enabled: bool = True, grid: bool = False, grid_scale: float = 1.0) -> None Enable or disable the live terminal dashboard. When enabled, Subsequence uses a safe logging handler that allows a persistent status line (BPM, Key, Bar, Section, Chord) to stay at the bottom of the terminal while logs scroll above it. :param enabled: Whether to show the display (default True). :param grid: When True, render an ASCII grid visualisation of all running patterns above the status line. The grid updates once per bar, showing which steps have notes and at what velocity. :param grid_scale: Horizontal zoom factor for the grid (default ``1.0``). Higher values add visual columns between grid steps, revealing micro-timing from swing and groove. Snapped to the nearest integer internally for uniform marker spacing. .. py:method:: energy(energies: Dict[str, Union[float, Tuple[float, float]]]) -> None Set per-section energy — the arranging dial, as one plain dict. ``{"verse": 0.5, "chorus": 0.9, "build": (0.3, 1.0)}`` — a float is the section's level; a ``(start, end)`` tuple interpolates across the section (a build). Patterns read ``p.energy`` (0.5 when nothing is configured) and gate themselves, or declare ``min_energy=`` on ``pattern()`` for automatic muting. The dict **overrides** any energy payload carried by bound :class:`~subsequence.forms.Section` values — it is the later, performance-level dial. Re-calling replaces the whole mapping (idempotent, live-reload friendly). Example:: composition.energy({"intro": 0.2, "verse": 0.55, "drop": 0.95}) .. py:method:: form(sections: Union[subsequence.forms.Form, List[Any], Iterator[Tuple[str, int]], Dict[str, Tuple[int, Optional[List[Tuple[str, int]]]]]], loop: bool = False, start: Optional[str] = None, at_end: str = 'stop', key: Optional[str] = None, scale: Optional[str] = None) -> None Define the structure (sections) of the composition. You can define form in four ways: 1. **Form value**: a frozen :class:`~subsequence.forms.Form` of :class:`~subsequence.forms.Section` values — the payload home (energy, key per section); editable, navigable. 2. **Sequence (List)**: a fixed order of ``(name, bars)`` tuples or Sections (lists coerce — they are the same form). 3. **Graph (Dict)**: dynamic transitions based on weights. 4. **Generator**: a Python generator that yields ``(name, bars)`` pairs. Form-value and list forms are **navigable**: ``form_jump()`` and ``form_next()`` work on them (the jump lands on the next occurrence of the name, wrapping). :param sections: The form definition (Form, List, Dict, or Generator). :param loop: Sugar for ``at_end="loop"``. :param start: The section to start with (Graph mode only). :param at_end: What happens when a sequence form runs out — ``"stop"`` (the form finishes and patterns see no section; default), ``"hold"`` (the final section repeats until navigated away from), or ``"loop"`` (start over). Graphs end via their terminal sections instead. :param key: A form-level key — the **form tier** of the key-source chain (``Section.key`` overrides it; it overrides the composition key). Re-anchors key-relative content for the whole form. When *sections* is a ``Form`` value carrying its own ``key``, that value is used unless this argument overrides. :param scale: A form-level scale/mode, paired with ``key``. .. rubric:: Example .. code-block:: python # A simple pop structure comp.form([ ("verse", 8), ("chorus", 8), ("verse", 8), ("chorus", 16) ]) # The same structure with payloads, held open at the end S = subsequence.Section comp.form(subsequence.Form([ S("verse", 8, energy=0.5), S("chorus", 8, energy=0.9), ]), at_end="hold") .. py:method:: form_freeze(sections: Optional[int] = None) -> subsequence.forms.Form Freeze the graph form's walk into an editable :class:`~subsequence.forms.Form`. Walks a **clone** of the live form state — the same RNG state, so the frozen path is exactly the path the live graph would have played — and returns it as a Form value: inspect it, edit it (``path.replace(3, bars=16)``), and rebind it with ``composition.form(path, at_end=...)``. The live form state is untouched (rebinding replaces it). :param sections: Number of sections to freeze. Without it, the walk runs until a terminal section; a graph with no terminal sections requires ``sections=`` explicitly. :raises ValueError: If no graph form is bound (a list form is already a frozen sequence), the form has already finished, or the walk cannot terminate. Example:: composition.form({...}, start="intro") path = composition.form_freeze() # the walk, frozen composition.form(path, at_end="stop") # rebind the editable value .. py:method:: form_jump(section_name: str) -> None Jump the form to a named section immediately. Delegates to :meth:`subsequence.form_state.FormState.jump_to`. Only works when the composition uses graph-mode form (a dict passed to :meth:`form`). The musical effect is heard at the *next pattern rebuild cycle* — already- queued MIDI notes are unaffected. This natural delay means ``form_jump`` is effective without needing explicit quantization. :param section_name: The section to jump to. :raises ValueError: If no form is configured, or the form is not in graph mode, or *section_name* is unknown. Example:: composition.hotkey("c", lambda: composition.form_jump("chorus")) .. py:method:: form_next(section_name: str) -> None Queue the next section — takes effect when the current section ends. Unlike :meth:`form_jump`, this does not interrupt the current section. The queued section replaces the automatically pre-decided next section and takes effect at the natural section boundary. The performer can change their mind by calling ``form_next`` again before the boundary. Delegates to :meth:`subsequence.form_state.FormState.queue_next`. Only works when the composition uses graph-mode form (a dict passed to :meth:`form`). :param section_name: The section to queue. :raises ValueError: If no form is configured, or the form is not in graph mode, or *section_name* is unknown. Example:: composition.hotkey("c", lambda: composition.form_next("chorus")) .. py:method:: freeze(bars: int, end: Optional[Any] = None, pins: Optional[Dict[int, Any]] = None, avoid: Optional[Sequence[Any]] = None, cadence: Optional[str] = None) -> Progression Capture a chord progression from the live harmony engine. Runs the harmony engine forward by *bars* chord changes, records each chord, and returns it as a :class:`Progression` that can be bound to a form section with :meth:`section_chords`. The engine state **advances** — successive ``freeze()`` calls produce a continuing compositional journey so section progressions feel like parts of a whole rather than isolated islands. The hybrid constraints compile into the walk: ``end=`` fixes the last bar ("end on V at bar 8"), ``pins=`` fix any 1-based bar, ``avoid=`` excludes chords throughout. Specs follow the progression-element grammar (ints where diatonic, roman/name strings where chromatic) and resolve against the composition key and scale. A backward feasibility pass guarantees satisfiability before any chord is drawn; the forward walk keeps the engine's real history-dependent weighting. Bar 1 is always the engine's current chord — the journey continues — so ``pins={1: ...}`` may only name it redundantly. :param bars: Number of chords to capture (one per harmony cycle). :param end: The chord at the final bar — ``end="V"`` is the cadential major dominant in minor. :param pins: ``{bar: chord}`` — 1-based fiat positions. :param avoid: Chords excluded from the walk. :param cadence: A cadence name (``"strong"``/``"soft"``/``"open"``/ ``"fakeout"``, theory aliases accepted) — its formula pins the final bars, so the walk approaches the close. Conflicts with ``end=`` or pins on those bars. :returns: A :class:`Progression` with the captured chords and trailing history for NIR continuity. :raises ValueError: If :meth:`harmony` has not been called first, or the constraints are contradictory or unsatisfiable. Example:: composition.harmony(style="functional_major", cycle_beats=4) verse = composition.freeze(8, end="V") # the verse sets up the chorus chorus = composition.freeze(4) # next 4 chords, continuing on composition.section_chords("verse", verse) composition.section_chords("chorus", chorus) .. py:method:: get_tweaks(name: str) -> Dict[str, Any] Return a copy of the current tweaks for a running pattern. :param name: The function name of the pattern. .. py:method:: harmony(style: Optional[Union[str, subsequence.chord_graphs.ChordGraph]] = None, cycle_beats: int = 4, dominant_7th: bool = True, gravity: float = 1.0, nir_strength: float = 0.5, minor_turnaround_weight: float = 0.0, root_diversity: float = subsequence.harmonic_state.DEFAULT_ROOT_DIVERSITY, reschedule_lookahead: float = 1, progression: Optional[Any] = None) -> None Configure the harmonic logic and chord change intervals. Two sources, combinable: a **bound progression** (``progression=`` — a :class:`Progression` value, an element list like ``[1, 6, 3, "bVII7"]``, or chord names) walked span by span on the global clock; and/or a **graph style** stepping live chords. With only a progression bound, it loops on exhaustion; with a style configured too, exhaustion falls through to live stepping (the frozen-replay bridge). Calling with neither argument keeps today's default live engine (``style="functional_major"``). :param style: The harmonic style to use. Built-in: "functional_major" (alias "diatonic_major"), "hooktheory_major" (alias "pop_major"), "turnaround", "aeolian_minor", "phrygian_minor", "lydian_major", "dorian_minor", "chromatic_mediant", "suspended", "mixolydian", "whole_tone", "diminished". See README for full descriptions. :param cycle_beats: How many beats each live chord lasts (default 4). Bound progressions carry their own harmonic rhythm in their spans, so this applies to live stepping only. :param dominant_7th: Whether to include V7 chords (default True). :param gravity: Key gravity (0.0 to 1.0). High values stay closer to the root chord. :param nir_strength: Melodic inertia (0.0 to 1.0). Influences chord movement expectations. :param minor_turnaround_weight: For "turnaround" style, influences major vs minor feel. :param root_diversity: Root-repetition damping (0.0 to 1.0). Each recent chord sharing a candidate's root reduces the weight to 40% at the default (0.4). Set to 1.0 to disable. :param reschedule_lookahead: How many beats in advance to calculate the next chord. :param progression: A progression to bind to the global clock. Key- relative content resolves now, against the composition key and scale (binding freezes one realisation). .. rubric:: Example .. code-block:: python # A moody minor progression that changes every 8 beats comp.harmony(style="aeolian_minor", cycle_beats=8, gravity=0.4) # Manual harmony driving everything — loops forever comp.harmony(progression=subsequence.progression([1, 6, 3, 7])) .. py:method:: hotkey(key: str, action: Callable[[], None], quantize: int = 0, label: Optional[str] = None) -> None Register a single-key shortcut that fires during playback. The listener must be enabled first with :meth:`hotkeys`. Most actions — form jumps, ``composition.data`` writes, and :meth:`tweak` calls — should use ``quantize=0`` (the default). Their musical effect is naturally delayed to the next pattern rebuild cycle, which provides automatic musical quantization without extra configuration. Use ``quantize=N`` for actions where you want an explicit bar-boundary guarantee, such as :meth:`mute` / :meth:`unmute`. The ``?`` key is reserved and cannot be overridden. :param key: A single character trigger (e.g. ``"a"``, ``"1"``, ``" "``). :param action: Zero-argument callable to execute. :param quantize: ``0`` = execute immediately (default). ``N`` = execute on the next global bar number divisible by *N*. :param label: Display name for the ``?`` help listing. Auto-derived from the function name or lambda body if omitted. :raises ValueError: If ``key`` is the reserved ``?`` character, or if ``key`` is not exactly one character. Example:: composition.hotkeys() # Immediate — musical effect happens at next pattern rebuild composition.hotkey("a", lambda: composition.form_jump("chorus")) composition.hotkey("1", lambda: composition.data.update({"mode": "chill"})) # Explicit 4-bar phrase boundary composition.hotkey("s", lambda: composition.mute("drums"), quantize=4) # Named function — label is derived automatically def drop_to_breakdown (): composition.form_jump("breakdown") composition.mute("lead") composition.hotkey("d", drop_to_breakdown) composition.play() .. py:method:: hotkeys(enabled: bool = True) -> None Enable or disable the global hotkey listener. Must be called **before** :meth:`play` to take effect. When enabled, a background thread reads single keystrokes from stdin without requiring Enter. The ``?`` key is always reserved and lists all active bindings. Hotkeys have zero impact on playback when disabled — the listener thread is never started. :param enabled: ``True`` (default) to enable hotkeys; ``False`` to disable. Example:: composition.hotkeys() composition.hotkey("a", lambda: composition.form_jump("chorus")) composition.play() .. py:method:: layer(*builder_fns: Callable, channel: int, beats: Optional[float] = None, bars: Optional[float] = None, steps: Optional[float] = None, step_duration: Optional[float] = None, drum_note_map: Optional[Dict[str, int]] = None, cc_name_map: Optional[Dict[str, int]] = None, nrpn_name_map: Optional[Dict[str, int]] = None, reschedule_lookahead: float = 1, voice_leading: bool = False, device: subsequence.midi_utils.DeviceId = None, mirrors: Optional[Iterable[subsequence.pattern.MirrorSpec]] = None) -> None Combine multiple functions into a single MIDI pattern. This is useful for composing complex patterns out of reusable building blocks (e.g., a 'kick' function and a 'snare' function). See ``pattern()`` for the full description of ``beats``, ``bars``, ``steps``, and ``step_duration``. :param builder_fns: One or more pattern builder functions. :param channel: MIDI channel (1-16, or 0-15 with ``zero_indexed_channels=True``). :param beats: Duration in beats (quarter notes). :param bars: Duration in bars (uses the composition's time signature — 4 beats each in 4/4). :param steps: Step count for step mode. Requires ``step_duration=``. :param step_duration: Duration of one step in beats. Requires ``steps=``. :param drum_note_map: Optional mapping for drum instruments. :param cc_name_map: Optional mapping of CC names to MIDI CC numbers. :param nrpn_name_map: Optional mapping of NRPN parameter names to 14-bit parameter numbers. :param reschedule_lookahead: Beats in advance to compute the next cycle. :param voice_leading: If True, chords use smooth voice leading. :param mirrors: Optional list of additional ``(device, channel)`` destinations to duplicate every event onto. See ``pattern()`` for details. .. py:method:: link(quantum: float = 4.0) -> Composition Enable Ableton Link tempo and phase synchronisation. When enabled, Subsequence joins the local Link session and slaves its clock to the shared network tempo and beat phase. All other Link-enabled apps on the same LAN — Ableton Live, iOS synths, other Subsequence instances — will automatically stay in time. Playback starts on the next bar boundary aligned to the Link quantum, so downbeats stay in sync across all participants. Requires the ``link`` optional extra:: pip install subsequence[link] :param quantum: Beat cycle length. ``4.0`` (default) = one bar in 4/4 time. Change this if your composition uses a different meter. Example:: comp = subsequence.Composition(bpm=120, key="C") comp.link() # join the Link session comp.play() # On another machine / instance: comp2 = subsequence.Composition(bpm=120) comp2.link() # tempo and phase will lock to comp comp2.play() .. note:: ``set_bpm()`` proposes the new tempo to the Link network when Link is active. The network-authoritative tempo is applied on the next pulse, so there may be a brief lag before the change is visible. .. py:method:: live(port: int = 5555) -> None Enable the live coding eval server. This allows you to connect to a running composition using the ``subsequence.live_client`` REPL and hot-swap pattern code or modify variables in real-time. Security: The server executes arbitrary Python in this process — it is **not** a sandbox. It binds to localhost only and is opt-in, but any process on the same machine that can reach the port gains full code execution here. Do not enable it on shared or multi-user hosts, and never expose the port to a network. :param port: The TCP port to listen on (default 5555). .. py:method:: live_info() -> Dict[str, Any] Return a dictionary containing the current state of the composition. Includes BPM, key, current bar, active section, current chord, running patterns, and custom data. .. py:method:: load_patterns(source: str, source_label: str = '') -> None Compile and apply a pattern-source string to the composition. Equivalent to one ``watch()`` reload triggered by save, but with the source presented in-memory rather than on disk. Useful for web / REST handlers that accept pattern uploads from a trusted contributor, or for one-shot session loads with no file backing. Behaviour mirrors ``watch()``: * The source is exec'd into a fresh namespace with ``composition`` and ``subsequence`` in scope. * ``@composition.pattern`` decorators in the source hot-swap their corresponding running patterns in place. * Patterns currently running but **not** declared in the source are unregistered — the source is treated as the full new truth. * If the composition is already playing, the swap happens on the event loop thread; the call blocks until it completes. * If the composition has not yet called ``play()``, the source runs on the caller's thread; decorators populate ``_pending_patterns`` and ``play()`` picks them up in the usual way. Errors are raised so the caller can act on them: * ``SyntaxError`` if ``source`` fails to compile. * The exception raised inside ``exec()`` for any runtime error. * ``RuntimeError`` if called from inside the composition's own event loop thread (would deadlock — see Threading below). In either failure case, existing composition state is preserved — the diff-and-unregister phase is skipped if exec raised, so a half-broken upload cannot tear down working patterns. Threading: Designed to be called from a thread DIFFERENT from the composition's event loop — typically a web-handler worker. Cannot be called from inside the loop itself (a pattern callback, an asyncio task spawned by the composition). From there, ``await composition._apply_source_async(...)`` directly. SECURITY WARNING: ``exec()`` is not sandboxed. The source has full Python access in this process. Only pass source from trusted senders. The built-in blocklist (``help``, ``input``, ``breakpoint``, ``exit``, ``quit``) prevents calls that would stall the event loop; it is not a security boundary. :param source: Python source declaring ``@composition.pattern`` functions. :param source_label: Identifier used in compile errors and tracebacks (appears as the filename in ``SyntaxError`` and ``__file__``- style traceback lines). Default ``""``. .. py:method:: lock(name: str) -> None Pin a named stream: keep its current effective seed and realization. Engine-side state, so it survives live reload (it is never a builder swap): a locked pattern re-deals its stream from the same effective seed on every rebuild, so every cycle realizes identically, and ``reroll()`` refuses with a message until ``unlock()``. :param name: The stream name — usually a pattern name. .. py:method:: midi_input(device: str, clock_follow: bool = False, name: Optional[str] = None) -> None Configure a MIDI input device for external sync and MIDI messages. May be called multiple times to register additional input devices. The first call sets the primary input (device 0). Subsequent calls add additional input devices (device 1, 2, …). Only one device may have ``clock_follow=True``. :param device: The name of the MIDI input port. :param clock_follow: If True, Subsequence will slave its clock to incoming MIDI Ticks. It will also follow MIDI Start/Stop/Continue commands. Only one device can have this enabled at a time. :param name: Optional alias for use with ``cc_map(input_device=…)`` and ``cc_forward(input_device=…)``. When omitted, the raw device name is used. .. rubric:: Example .. code-block:: python # Single controller (unchanged usage) comp.midi_input("Scarlett 2i4", clock_follow=True) # Multiple controllers comp.midi_input("Arturia KeyStep", name="keys") comp.midi_input("Faderfox EC4", name="faders") .. py:method:: midi_output(device: str, name: Optional[str] = None, latency_ms: float = 0.0) -> int Register an additional MIDI output device. The first output device is always the one passed to ``Composition(output_device=…)`` — that is device 0. Each call to ``midi_output()`` adds the next device (1, 2, …). :param device: The exact name of the MIDI output port, as reported by ``mido.get_output_names()``. Matching is strict — partial names and substrings are rejected. See ``Composition.__init__`` for the lookup snippet and a note on ALSA name stability on Linux. :param name: Optional alias for use with ``pattern(device=…)``, ``cc_forward(output_device=…)``, etc. When omitted, the raw device name is used. :param latency_ms: Physical output latency of this device in milliseconds, for delay compensation (default 0.0, must be non-negative). Set this when the device sounds late (e.g. a software sampler) so Subsequence delays faster devices to line everything up. :returns: The integer device index assigned (1, 2, 3, …). .. rubric:: Example .. code-block:: python comp = subsequence.Composition(bpm=120, output_device="MOTU Express") # Returns 1 — use as device=1 or device="integra" comp.midi_output("Roland Integra", name="integra") # A software sampler that sounds 20ms late comp.midi_output("Subsample", name="sampler", latency_ms=20) @comp.pattern(channel=1, beats=4, device="integra") def strings (p): p.note(60, beat=0) .. py:method:: mirror(name: str, device: int, channel: int, drum_note_map: Optional[Dict[str, int]] = None) -> None Add a mirror destination to a running pattern. Every note, CC, pitch bend, NRPN/RPN, program change, SysEx, and drone event the pattern emits will also be sent to ``(device, channel)``, starting from the next cycle rebuild. Idempotent on ``(device, channel)`` — calling with the same destination twice does not double-fan; calling again with a different ``drum_note_map`` re-points it in place. :param name: Function name of the pattern to mirror. :param device: Output device index (the integer returned from ``midi_output()``; 0 = primary device). :param channel: MIDI channel using this composition's numbering convention (1-16 by default; 0-15 if ``zero_indexed_channels=True``). :param drum_note_map: Optional per-destination drum map. When set, mirrored drum hits are re-resolved by name through it, so a named voice lands on this device's own note number — see the README "MIDI mirroring" section. Bandwidth: each mirror adds another full copy of the pattern's events. See the README "MIDI mirroring" section for the tradeoffs. .. py:method:: mute(name: str) -> None Mute a running pattern by name. The pattern continues to 'run' and increment its cycle count in the background, but it will not produce any MIDI notes until unmuted. :param name: The function name of the pattern to mute. .. py:method:: note_input(channel: Optional[int] = None, release_ms: float = 30.0, latch: bool = False, input_device: subsequence.midi_utils.DeviceId = None) -> None Track notes held on a MIDI keyboard for live arpeggiation. Incoming note-on/note-off messages build a live "currently held" set that any pattern reads via ``p.held_notes()`` — typically fed straight to ``p.arpeggio()``. The composition still authors the rhythm and motion; the player's hands supply the pitch set. This is a live *performance* layer over the deterministic, seeded composition: when rendering headlessly there is no input, so ``p.held_notes()`` is empty and seeded output is unchanged. **Requires** ``midi_input()`` to be called first to open an input port. :param channel: If given, only track notes on this channel. Uses the same numbering convention as ``pattern()`` (1-16 by default, or 0-15 with ``zero_indexed_channels=True``). ``None`` tracks any channel (default). :param release_ms: How long (milliseconds) a released note keeps counting as held. This smooths the momentary all-keys-up gap during a hand-position change so the arp does not drop to silence. Default 30.0; set 0.0 to release instantly. Ignored when ``latch`` is True. :param latch: When True, the held set persists after you lift your hands until you play a new chord (the first key after every key is up replaces it) — like a hardware arp's latch. :param input_device: Only track notes from this input device (index or name). ``None`` tracks any input device (default). .. rubric:: Example .. code-block:: python comp.midi_input("Arturia KeyStep") comp.note_input(channel=1, release_ms=30) @comp.pattern(channel=6, beats=4) def arp (p): p.arpeggio(p.held_notes(), direction="up") # rests when silent .. py:method:: on_event(event_name: str, callback: Callable[Ellipsis, Any]) -> None Register a callback for a sequencer event (e.g., "bar", "start", "stop"). .. py:method:: on_section(callback: Callable[Ellipsis, Any]) -> None Register a callback fired on every section change. The callback receives the new :class:`~subsequence.form_state.SectionInfo` (or ``None`` when the form finishes). It fires from the form clock, one lookahead-beat **early** — in time to affect the new section's first patterns — and once at play start for the opening section. Example:: composition.on_section(lambda info: print(f"now: {info.name if info else 'end'}")) .. py:method:: osc(receive_port: int = 9000, send_port: int = 9001, send_host: str = '127.0.0.1', receive_host: str = '0.0.0.0') -> None Enable bi-directional Open Sound Control (OSC). Subsequence will listen for commands (like ``/bpm`` or ``/mute``) and broadcast its internal state (like ``/chord`` or ``/bar``) over UDP. :param receive_port: Port to listen for incoming OSC messages (default 9000). :param send_port: Port to send state updates to (default 9001). :param send_host: The IP address to send updates to (default "127.0.0.1"). :param receive_host: Interface to listen on (default "0.0.0.0" — all interfaces, so external OSC controllers on the LAN can reach it). The listener can change tempo, mute patterns, and write data, so on an untrusted network restrict it with ``receive_host="127.0.0.1"``. .. py:method:: osc_map(address: str, handler: Callable) -> None Register a custom OSC handler. Must be called after :meth:`osc` has been configured. :param address: OSC address pattern to match (e.g. ``"/my/param"``). :param handler: Callable invoked with ``(address, *args)`` when a matching message arrives. Example:: composition.osc() def on_intensity (address, value): composition.data["intensity"] = float(value) composition.osc_map("/intensity", on_intensity) .. py:method:: pattern(channel: int, beats: Optional[float] = None, bars: Optional[float] = None, steps: Optional[float] = None, step_duration: Optional[float] = None, drum_note_map: Optional[Dict[str, int]] = None, cc_name_map: Optional[Dict[str, int]] = None, nrpn_name_map: Optional[Dict[str, int]] = None, reschedule_lookahead: float = 1, voice_leading: bool = False, device: subsequence.midi_utils.DeviceId = None, mirrors: Optional[Iterable[subsequence.pattern.MirrorSpec]] = None, min_energy: Optional[float] = None) -> Callable Register a function as a repeating MIDI pattern. The decorated function will be called once per cycle to 'rebuild' its content. This allows for generative logic that evolves over time. Two ways to specify pattern length: - **Duration mode** (default): use ``beats=`` or ``bars=``. The grid defaults to sixteenth-note resolution. - **Step mode**: use ``steps=`` paired with ``step_duration=``. The grid equals the step count, so ``p.hit_steps()`` indices map directly to steps. :param channel: MIDI channel. By default uses 1-based numbering (1-16). Set ``zero_indexed_channels=True`` on the ``Composition`` to use 0-based numbering (0-15), matching the raw MIDI protocol, instead. :param beats: Duration in beats (quarter notes). ``beats=4`` = 1 bar. :param bars: Duration in bars (uses the composition's time signature — 4 beats each in 4/4). ``bars=2`` = 8 beats. :param steps: Step count for step mode. Requires ``step_duration=``. :param step_duration: Duration of one step in beats (e.g. ``dur.SIXTEENTH``). Requires ``steps=``. :param drum_note_map: Optional mapping for drum instruments. :param cc_name_map: Optional mapping of CC names to MIDI CC numbers. Enables string-based CC names in ``p.cc()`` and ``p.cc_ramp()``. :param nrpn_name_map: Optional mapping of NRPN parameter names (strings) to 14-bit parameter numbers (0–16383). Enables string-based names in ``p.nrpn()`` and ``p.nrpn_ramp()`` — typically a device-specific dictionary (e.g. Sequential Take 5's ``Osc1FreqFine`` → 9). :param reschedule_lookahead: Beats in advance to compute the next cycle. :param voice_leading: If True, chords in this pattern will automatically use inversions that minimize voice movement. :param mirrors: Optional list of additional ``(device, channel)`` destinations to duplicate every event from this pattern onto. Notes, CCs, pitch bend, NRPN/RPN bursts, program changes, SysEx, and drone events are all mirrored; OSC events are not (OSC is not bound to a MIDI port). ``device`` is the integer index returned by ``midi_output()`` (0 = primary). ``channel`` follows this composition's channel-numbering convention. See also ``mirror()`` / ``unmirror()`` for live toggling. :param min_energy: Automatic energy gating — the pattern is silent while the current section's energy (``composition.energy()`` dict, or the bound Section payload) is below this threshold. Composes with ``mute()``: a performer mute always wins. .. rubric:: Example .. code-block:: python @comp.pattern(channel=1, beats=4) def chords (p): p.chord([60, 64, 67], beat=0, velocity=80, duration=3.9) @comp.pattern(channel=1, bars=2) def long_phrase (p): ... @comp.pattern(channel=1, steps=6, step_duration=dur.SIXTEENTH) def riff (p): p.sequence(steps=[0, 1, 3, 5], pitches=60) .. py:method:: phrase_part(*, channel: int, part: Optional[str] = None, root: int = 60, bars: Optional[float] = None, beats: Optional[float] = None, velocity: Optional[Union[int, Tuple[int, int]]] = None, fit: Optional[float] = None, device: subsequence.midi_utils.DeviceId = None, mirrors: Optional[Iterable[subsequence.pattern.MirrorSpec]] = None) -> None Declare a part that plays each section's bound Motif/Phrase. The one-call consumer for :meth:`section_motifs` — it registers a pattern on *channel* that walks whatever value is bound to the current section for *part* (stateless position from the cycle counter, via ``p.phrase()``). A section with no binding for the part is **silent** for that part — bind material or don't; no fallback guessing. :param channel: MIDI channel for the part. :param part: The part label to read from the registry (``None`` = the unlabelled binding). :param root: Register anchor for degree resolution. :param bars / beats: Cycle length of the part (defaults to 4 beats); the phrase is sliced one cycle window at a time. :param velocity: Optional override applied to every note. :param fit: Passed through (active with the melody engine stage). :param device: Optional output-device override. :param mirrors: Optional additional ``(device, channel)`` destinations. Example:: composition.section_motifs("verse", verse_line, part="lead") composition.section_motifs("chorus", chorus_line, part="lead") composition.phrase_part(channel=4, part="lead", root=72, bars=2) .. py:method:: pin_chord(bar: int, chord: Optional[Any]) -> None Force the chord sounding at a bar — fiat over live generation. Whatever the harmonic source (live walk, bound progression, section progression) produces for *bar*, the pinned chord overrides it. Pass ``None`` to remove a pin. :param bar: 1-based bar number (the musician count). :param chord: A chord name, int degree, roman string, ``Chord``, ``PitchSet``, or ``None`` to unpin. A **key-relative** spec (int degree, roman) re-keys like section harmony: it resolves late, against the effective key of the section sounding at that bar (so ``pin_chord(8, "V")`` is the dominant of wherever bar 8 lands). A **concrete** spec (name, ``Chord``, ``PitchSet``) is absolute and never moves. Example:: composition.pin_chord(8, "E7") # the turnaround lands on E7 composition.pin_chord(8, "V") # the dominant of bar 8's section composition.pin_chord(8, None) # let it walk again .. py:method:: play() -> None Start the composition. This call blocks until the program is interrupted (e.g., via Ctrl+C). It initializes the MIDI hardware, launches the background sequencer, and begins playback. .. py:method:: render(bars: Optional[int] = None, filename: str = 'render.mid', max_minutes: Optional[float] = 60.0) -> None Render the composition to a MIDI file without real-time playback. Runs the sequencer as fast as possible (no timing delays) and stops when the first active limit is reached. The result is saved as a standard MIDI file that can be imported into any DAW. All patterns, scheduled callbacks, and harmony logic run exactly as they would during live playback — BPM transitions, generative fills, and probabilistic gates all work in render mode. The only difference is that time is simulated rather than wall-clock driven. :param bars: Number of bars to render, or ``None`` for no bar limit (default ``None``). When both *bars* and *max_minutes* are active, playback stops at whichever limit is reached first. :param filename: Output MIDI filename (default ``"render.mid"``). :param max_minutes: Safety cap on the length of rendered MIDI in minutes (default ``60.0``). Pass ``None`` to disable the time cap — you must then provide an explicit *bars* value. :raises ValueError: If both *bars* and *max_minutes* are ``None``, which would produce an infinite render. .. rubric:: Examples .. code-block:: python # Default: renders up to 60 minutes of MIDI content. composition.render() # Render exactly 64 bars (time cap still active as backstop). composition.render(bars=64, filename="demo.mid") # Render up to 5 minutes of an infinite generative composition. composition.render(max_minutes=5, filename="five_min.mid") # Remove the time cap — must supply bars instead. composition.render(bars=128, max_minutes=None, filename="long.mid") .. py:method:: request_cadence(cadence: str = 'strong', bar: Optional[int] = None) -> None Ask the live engine to approach a cadence arriving at a bar. The request hook: where :meth:`pin_chord` is fiat, this is a *steered approach* — at the next chord boundary the clock plans the remaining changes up to *bar* as a constrained walk through the engine's real weights, pinned to the cadence formula at the tail (``"strong"`` arrives V→I, ``"soft"`` IV→I, ``"open"`` IV→V, ``"fakeout"`` V→vi; theory aliases accepted). The chords still commit one boundary at a time, so the journey continues through the close. One-shot: the request is consumed when planned. Live harmony only — bound/section progressions are data and cannot be steered; a request whose bar passes unserved expires with a warning. If the formula is not walkable from where the harmony stands, the arrival lands by fiat (loudly). Ask at least a pattern-lookahead ahead: patterns may already have rendered against the previously planned chord. :param cadence: The cadence name. :param bar: The 1-based bar the cadence's final chord arrives at (required; in practice ≥ 2 — bar 1 cannot be approached). Example:: composition.request_cadence("open", bar=16) # hang on V at bar 16 .. py:method:: reroll(name: str) -> None Deal a named stream a fresh deterministic seed — try a new variation. Bumps the per-name nonce and prints the new effective seed. The nonce lives only in this process, so the printed seed is what lets a variation you like survive a restart: note it down, or ``lock()`` the name to pin it for the session. Refuses on locked names. :param name: The stream name — usually a pattern name. .. rubric:: Example .. code-block:: python comp.reroll("lead") # prints: reroll('lead') -> effective seed ... .. py:method:: schedule(fn: Callable, cycle_beats: int, reschedule_lookahead: int = 1, wait_for_initial: bool = False, defer: bool = False) -> None Register a custom function to run on a repeating beat-based cycle. Subsequence automatically runs synchronous functions in a thread pool so they don't block the timing-critical MIDI clock. Async functions are run directly on the event loop. :param fn: The function to call. :param cycle_beats: How often to call it (e.g., 4 = every bar). :param reschedule_lookahead: How far in advance to schedule the next call. :param wait_for_initial: If True, run the function once during startup and wait for it to complete before playback begins. This ensures ``composition.data`` is populated before patterns first build. Implies ``defer=True`` for the repeating schedule. :param defer: If True, skip the pulse-0 fire and defer the first repeating call to just before the second cycle boundary. :raises RuntimeError: If called after ``play()`` has started — scheduled tasks register at startup, so a late registration would be silently ignored otherwise. .. py:method:: section_cadence(section_name: str, cadence: Optional[str] = 'strong') -> None Close every pass of a section with a cadence — the standing request. Each time *section_name* is entered, the clock registers a :meth:`request_cadence` arriving at the section's final bar, so the harmony approaches the close as the section ends. Live harmony only: a section with bound chords (:meth:`section_chords`) is data and ignores the registration — its closes are written, not steered. Pass ``None`` to unregister. Example:: composition.form([("verse", 8), ("chorus", 8)]) composition.section_cadence("verse", "open") # every verse hangs on V composition.section_cadence("chorus", "strong") # every chorus lands home .. py:method:: section_chords(section_name: str, progression: Any) -> None Bind a :class:`Progression` to a named form section. Every time *section_name* plays, the harmonic clock walks the progression's spans instead of calling the live engine. Sections without a bound progression continue generating live chords. Accepts a :class:`Progression` value (from :meth:`freeze`, the ``progression()`` factory, or hand-built) or anything the factory accepts — an element list like ``[1, 6, 3, "bVII7"]`` or chord names. **Key-relative content re-keys per occurrence.** A progression written in degrees or romans is *key-relative* content: it resolves late, each time the section plays, against that section's effective key and scale (``Section.key`` > form key > composition key, with mode following the same chain). So a ``Section(key="A")`` plays the same numbered progression a tone higher — its chords and its degrees share one tonic. *Absolute* content — chord names (``"Am"``), :class:`~subsequence.progressions.PitchSet`, and frozen captures from :meth:`freeze` — names exact chords and is never transposed by a key. On exhaustion mid-section the progression loops when no graph style is configured (and always when it contains a ``PitchSet``); with a live engine, exhaustion **falls through to live stepping in the COMPOSITION key** — the live graph engine does not transpose for a section (a stateful walk does not modulate mid-stream), so a re-keyed section that runs out of written chords hands off to composition-key harmony. Bind a full-length progression (or set ``at_end``/loop intent) if you need the whole section in its key. :param section_name: Name of the section as defined in :meth:`form`. :param progression: The progression to bind. :raises ValueError: If a graph-based form has been configured and *section_name* is not one of its sections. List and generator forms yield names lazily, so they cannot be validated here. (A key-relative progression with no resolvable key for the section is caught at :meth:`play`/:meth:`render`, once the form's keys are known.) Example:: composition.section_chords("verse", verse_progression) composition.section_chords("chorus", [1, 6, 3, 7]) # "bridge" is not bound — it generates live chords .. py:method:: section_motifs(section_name: str, value: Any, part: Optional[str] = None) -> None Bind a Motif or Phrase to a named form section (per optional part). Patterns read the binding back with ``p.section_motif(part)`` (or use the one-call :meth:`phrase_part`); a section with no binding for the part is silent for that part — bind material or don't, no fallback guessing. Re-binding is idempotent, so the call is safe in a live file: re-executing on save is the desired rebind. :param section_name: Name of the section as defined in :meth:`form`. :param value: A ``Motif`` or ``Phrase`` (anything exposing ``.length``/``.slice`` places). :param part: Optional part label, so one section can carry several bindings (``"lead"``, ``"bass"``, ...). :raises ValueError: If a graph-based form has been configured and *section_name* is not one of its sections. Example:: composition.section_motifs("verse", verse_line, part="lead") composition.section_motifs("chorus", chorus_line, part="lead") .. py:method:: seed_for(name: str) -> Optional[int] Surface the effective derived seed for a named stream. Works for pattern names and equally for any name you invent for a standalone value generator (``seed=composition.seed_for("hook")``), so its randomness keys off the composition seed without sharing any other consumer's stream. Reflects ``reroll()`` nonces. Returns None when the composition is unseeded. .. rubric:: Example .. code-block:: python hook_seed = composition.seed_for("hook") .. py:method:: set_bpm(bpm: float) -> None Instantly change the tempo. :param bpm: The new tempo in beats per minute. When Ableton Link is active, this proposes the new tempo to the Link network instead of applying it locally. The network-authoritative tempo is picked up on the next pulse. .. py:method:: target_bpm(bpm: float, bars: int, shape: str = 'linear') -> None Smoothly ramp the tempo to a target value over a number of bars. :param bpm: Target tempo in beats per minute. :param bars: Duration of the transition in bars. :param shape: Easing curve name. Defaults to ``"linear"``. ``"ease_in_out"`` or ``"s_curve"`` are recommended for natural- sounding tempo changes. See :mod:`subsequence.easing` for all available shapes. .. rubric:: Example .. code-block:: python # Accelerate to 140 BPM over the next 8 bars with a smooth S-curve comp.target_bpm(140, bars=8, shape="ease_in_out") .. note:: Ignored while Ableton Link is active — the shared session tempo is authoritative. Use ``set_bpm()`` to propose a tempo to the Link network. .. py:method:: transition(before: str, fill: Optional[Any] = None, channel: Optional[int] = None, beat: float = 0.0, mute: Optional[List[str]] = None, beats: Optional[float] = None, drum_note_map: Optional[Dict[str, int]] = None, device: subsequence.midi_utils.DeviceId = None) -> None Declare boundary material — the automatic fill or mute, one line. ``before`` names the incoming section (``"chorus"``), or ``"*"`` for any *different* section (repeats don't fire it). Two actions, combinable: - ``fill=`` (+ ``channel=``, ``beat=``): a Motif played in the last bar before the boundary, starting at ``beat`` of that bar. Drum names resolve through ``drum_note_map=`` if given, otherwise the map is borrowed from a registered pattern on the same channel. - ``mute=`` (+ ``beats=``): pattern names muted over the approach and unmuted at the boundary. Muting is **bar-granular** (the existing rule), so ``beats`` rounds up to whole bars. Performer mutes win: a pattern you muted yourself stays muted. Transitions stack — call once per rule. Registration is additive and idempotent per identical rule. Example:: composition.transition(before="*", fill=FILL, channel=10, beat=2.0) composition.transition(before="drop", mute=["pads"], beats=4) .. py:method:: trigger(fn: Callable, channel: int, beats: Optional[float] = None, bars: Optional[float] = None, steps: Optional[float] = None, step_duration: Optional[float] = None, quantize: float = 0, drum_note_map: Optional[Dict[str, int]] = None, cc_name_map: Optional[Dict[str, int]] = None, nrpn_name_map: Optional[Dict[str, int]] = None, chord: bool = False, device: subsequence.midi_utils.DeviceId = None, mirrors: Optional[Iterable[subsequence.pattern.MirrorSpec]] = None) -> None Trigger a one-shot pattern immediately or on a quantized boundary. This is useful for real-time response to sensors, OSC messages, or other external events. The builder function is called immediately with a fresh PatternBuilder, and the generated events are injected into the queue at the specified quantize boundary. The builder function has the same API as a ``@composition.pattern`` decorated function and can use all PatternBuilder methods: ``p.note()``, ``p.euclidean()``, ``p.arpeggio()``, and so on. See ``pattern()`` for the full description of ``beats``, ``bars``, ``steps``, and ``step_duration``. Default is 1 beat. :param fn: The pattern builder function (same signature as ``@comp.pattern``). :param channel: MIDI channel (1-16, or 0-15 with ``zero_indexed_channels=True``). :param beats: Duration in beats (quarter notes, default 1). :param bars: Duration in bars (uses the composition's time signature — 4 beats each in 4/4). :param steps: Step count for step mode. Requires ``step_duration=``. :param step_duration: Duration of one step in beats. Requires ``steps=``. :param quantize: Snap the trigger to a beat boundary: ``0`` = immediate (default), ``1`` = next beat (quarter note), ``4`` = next bar. Use ``dur.*`` constants from ``subsequence.constants.durations``. :param drum_note_map: Optional drum name mapping for this pattern. :param cc_name_map: Optional mapping of CC names to MIDI CC numbers. :param nrpn_name_map: Optional mapping of NRPN parameter names to 14-bit parameter numbers. :param chord: If ``True``, the builder function receives the current chord as a second parameter (same as ``@composition.pattern``). :param mirrors: Optional list of additional ``(device, channel)`` destinations to fire this one-shot onto in parallel with the primary destination. .. rubric:: Example .. code-block:: python # Immediate single note (channels are 1-16 by default) composition.trigger( lambda p: p.note(60, beat=0, velocity=100, duration=0.5), channel=1 ) # Quantized fill (next bar) — channel 10 is the GM drum channel import subsequence.constants.durations as dur composition.trigger( lambda p: p.euclidean("snare", pulses=7, velocity=90), channel=10, drum_note_map=gm_drums.GM_DRUM_MAP, quantize=dur.WHOLE ) # With chord context — the builder receives the chord as a second # argument when chord=True. composition.trigger( lambda p, chord: p.arpeggio(chord.tones(root=60), spacing=dur.SIXTEENTH), channel=1, quantize=dur.QUARTER, chord=True ) .. py:method:: tuning(source: Optional[Union[str, os.PathLike]] = None, *, cents: Optional[List[float]] = None, ratios: Optional[List[float]] = None, equal: Optional[int] = None, bend_range: float = 2.0, channels: Optional[List[int]] = None, reference_note: int = 60, exclude_drums: bool = True) -> None Set a global microtonal tuning for the composition. The tuning is applied automatically after each pattern rebuild (before the pattern is scheduled). Drum patterns (those registered with a ``drum_note_map``) are excluded by default. Supply exactly one of the source parameters: - ``source``: path to a Scala ``.scl`` file. - ``cents``: list of cent offsets for degrees 1..N (degree 0 = 0.0 is implicit). - ``ratios``: list of frequency ratios (e.g., ``[9/8, 5/4, 4/3, 3/2, 2]``). - ``equal``: integer for N-tone equal temperament (e.g., ``equal=19``). For polyphonic parts, supply a ``channels`` pool. Notes are spread across those MIDI channels so each can carry an independent pitch bend. The synth must be configured to match ``bend_range`` (its pitch-bend range setting in semitones). :param source: Path to a ``.scl`` file. :param cents: Cent offsets for scale degrees 1..N. :param ratios: Frequency ratios for scale degrees 1..N. :param equal: Number of equal divisions of the period. :param bend_range: Synth pitch-bend range in semitones (default ±2). :param channels: Channel pool for polyphonic rotation. :param reference_note: MIDI note mapped to scale degree 0 (default 60 = C4). :param exclude_drums: When True (default), skip patterns that have a ``drum_note_map`` (they use fixed GM pitches, not tuned ones). .. rubric:: Example .. code-block:: python # Quarter-comma meantone from a Scala file comp.tuning("meanquar.scl") # Just intonation from ratios comp.tuning(ratios=[9/8, 5/4, 4/3, 3/2, 5/3, 15/8, 2]) # 19-TET, monophonic comp.tuning(equal=19, bend_range=2.0) # 31-TET with channel rotation for polyphony (channels 1-6) comp.tuning("31tet.scl", channels=[0, 1, 2, 3, 4, 5]) .. py:method:: tweak(name: str, **kwargs: Any) -> None Override parameters for a running pattern. Values set here are available inside the pattern's builder function via ``p.param()``. They persist across rebuilds until explicitly changed or cleared. Changes take effect on the next rebuild cycle. :param name: The function name of the pattern. :param ``**kwargs``: Parameter names and their new values. Example (from the live REPL):: composition.tweak("bass", pitches=[48, 52, 55, 60]) .. py:method:: unlock(name: str) -> None Release a ``lock()``: the stream runs free and ``reroll()`` works again. .. py:method:: unmirror(name: str, device: int, channel: int) -> None Remove a single mirror destination from a running pattern. Matches on ``(device, channel)`` only — any attached ``drum_note_map`` is ignored. Idempotent: silently does nothing if the destination is not currently mirrored. The change applies on the next cycle rebuild. .. py:method:: unmirror_all(name: str) -> None Remove every mirror destination from a running pattern. .. py:method:: unmute(name: str) -> None Unmute a previously muted pattern. .. py:method:: unregister(name: str) -> None Fully remove a running pattern from rotation. Unlike ``mute()`` (which keeps the pattern alive but silent), ``unregister()`` tears the pattern down entirely. It sets ``pattern._removed = True`` so the sequencer's reschedule loop skips re-adding it on the next pulse; sends ``note_off`` for any of the pattern's currently-sounding notes on the primary destination AND on every mirror destination (so drones and sustaining notes stop immediately); and removes the entry from ``_running_patterns`` so it no longer appears in ``live_info()``, the terminal grid, or any other consumer that enumerates running patterns. Already-queued events in the sequencer's event queue play out — note_offs are paired with their note_ons at queue time, so notes end at their natural duration; only drones rely on the targeted ``_stop_pattern_notes`` pass. Idempotent: silently logs a ``debug`` and returns if the pattern is already absent. Useful from both the live REPL (``composition.live()``) and the file watcher (``composition.watch()``), which calls this for any pattern removed from the watched file between reloads. :param name: Function name of the pattern to remove. .. py:method:: watch(path: Union[str, pathlib.Path], poll_interval: float = 0.25) -> None Watch a Python file and reload it into the composition on every save. The watched file is exec'd into a namespace with ``composition`` and ``subsequence`` available. ``@composition.pattern`` decorators inside the file hot-swap their corresponding running patterns in place; patterns whose function bodies have been deleted from the file are unregistered automatically on the next reload (notes stopped, removed from the running-pattern set). An **initial synchronous load** happens here — if the file has a ``SyntaxError`` or doesn't exist at this moment, the exception propagates so the user knows immediately. Subsequent reloads happen on the composition's event loop and tolerate transient errors (logged, skipped). Call BEFORE ``composition.play()``. Reloads happen on the composition's event loop, so all mutations are thread-safe. See the "Live coding via file watching" section of the README for the recommended wrapper-script + live-file split. :param path: Path to the Python file to watch. :param poll_interval: Seconds between ``mtime`` polls (default 0.25 s). Example:: # live_init.py — runs once composition = subsequence.Composition(bpm=120, key="E") composition.harmony(style="aeolian_minor") composition.watch("live_patterns.py") composition.play() .. py:method:: web_ui(http_host: str = '127.0.0.1', ws_host: str = '127.0.0.1') -> None Enable the realtime Web UI Dashboard. When enabled, Subsequence instantiates a WebSocket server that broadcasts the current state, signals, and active patterns (with high-res timing and note data) to any connected browser clients. Both servers bind to localhost by default. Pass ``http_host`` / ``ws_host`` (e.g. "0.0.0.0") to opt into LAN exposure — the dashboard is read-only but broadcasts full composition state, so only do so on a trusted network. .. py:property:: builder_bar :type: int Current bar index used by pattern builders. .. py:property:: form_state :type: Optional[subsequence.form_state.FormState] The active ``subsequence.form_state.FormState``, or ``None`` if ``form()`` has not been called. .. py:property:: harmonic_state :type: Optional[subsequence.harmonic_state.HarmonicState] The active ``HarmonicState``, or ``None`` if ``harmony()`` has not been called. .. py:property:: is_clock_following :type: bool True if either the primary or any additional device is following external clock. .. py:property:: running_patterns :type: Dict[str, Any] The currently active patterns, keyed by name. .. py:property:: seed :type: Optional[int] The composition's random seed, or None when unseeded. When set, every random decision derives deterministically from this value through named streams (see ``seed_for()``), so the same script produces the same music on every run. Assign to set it:: comp.seed = 42 (Formerly the method ``comp.seed(42)`` — the call form is a hard break per the pre-1.0 rename policy.) .. py:property:: sequencer :type: subsequence.sequencer.Sequencer The underlying ``Sequencer`` instance. .. py:class:: HarmonyView(horizon: _HarmonyHorizon, origin_beat: float) Read-only harmony context for one pattern cycle (``p.harmony``). Anchored at the cycle's start beat, so all beat arguments are cycle-relative — ``chord_at(0)`` is the chord at the cycle's first beat (what the two-parameter ``chord`` convention injects), ``chord_at(3.5)`` the chord sounding under beat 3.5 of this cycle. Under bound/frozen progressions the future is data and any beat answers; in live graph mode the window is the current chord plus one pre-committed step, and ``next_chord`` is *planned and revocable*. Anchor the view at a cycle-start beat. .. py:method:: chord_at(beat: float) -> Optional[Any] The chord sounding at *beat* of THIS cycle (0-based beats). .. py:method:: next_chord_at(beat: float) -> Optional[Any] The chord following the one sounding at *beat* of THIS cycle, when known. .. py:property:: chord :type: Optional[Any] The chord at this cycle's start (the cycle-start snapshot). .. py:property:: next_chord :type: Optional[Any] The chord after the current one — for anticipation and approach tones. .. py:property:: until_change :type: Optional[float] Beats from the cycle start until the next chord boundary, when known. .. py:class:: HotkeyBinding A registered keyboard shortcut and its associated action. .. attribute:: key The single character that triggers this binding. .. attribute:: action Zero-argument callable executed when the key fires. .. attribute:: quantize ``0`` = execute immediately; ``N`` = execute on the next global bar divisible by *N*. .. attribute:: label Human-readable description shown by the ``?`` help key. .. py:class:: ScheduleContext Context object passed to ``composition.schedule()`` callbacks whose signature declares a first parameter (conventionally named ``p``). .. attribute:: cycle How many times this callback has been called so far (0-indexed). 0 on the first call, including the blocking ``wait_for_initial`` run. .. py:function:: run_until_stopped(sequencer: subsequence.sequencer.Sequencer) -> None :async: Run the sequencer until a stop signal is received. .. py:function:: schedule_form(sequencer: subsequence.sequencer.Sequencer, form_state: subsequence.form_state.FormState, reschedule_lookahead: float = 1, on_bar: Optional[Callable[[int, bool], None]] = None) -> None :async: Schedule the form state to advance each bar. Emits a ``"section"`` event on the sequencer's emitter at play start and on every section change (one lookahead-beat early, like every form decision), carrying the new :class:`~subsequence.form_state.SectionInfo` (``None`` when the form finishes). ``on_bar`` is the boundary hook — called once per bar with ``(boundary_pulse, section_changed)`` after the form advances; the transition machinery rides it. .. py:function:: schedule_harmonic_clock(sequencer: subsequence.sequencer.Sequencer, get_harmonic_state: Callable[[], Optional[subsequence.harmonic_state.HarmonicState]], horizon: _HarmonyHorizon, bar_beats: float, cycle_beats: int = 4, get_bound_progression: Optional[Callable[[], Optional[Progression]]] = None, get_section_progression: Optional[Callable[[], Optional[Tuple[str, int, int, Optional[Progression]]]]] = None, get_pinned: Optional[Callable[[int], Optional[Any]]] = None, cadence_requests: Optional[Dict[int, str]] = None, resolve_cadence: Optional[Callable[[str], List[subsequence.chords.Chord]]] = None, get_section_cadence: Optional[Callable[[str], Optional[str]]] = None, reschedule_lookahead: float = 1) -> None :async: Schedule the harmonic clock — a span walker over the bound harmony sources. Generalises the old fixed-cycle clock: chords last as long as their spans say, the clock fires at ``min(next span boundary, next bar boundary)`` (so section bookkeeping stays bar-aligned under variable harmonic rhythm), and every realised span is published to *horizon* (the harmony window patterns read through ``p.harmony``). Priority chain per chord boundary: **section progression > composition-bound progression > live ``step()``**. A bound progression loops on exhaustion when no live engine is configured (or when it contains a :class:`~subsequence.progressions.PitchSet`); with a live engine, exhaustion falls through to live stepping — the frozen-replay bridge. In live mode the engine pre-commits one step so the window always holds ``[current, next]``. ``get_harmonic_state``, ``get_bound_progression``, and ``get_pinned`` are evaluated on every tick so mid-playback calls to ``harmony()``, re-binds, and new pins take effect immediately. ``get_section_progression`` returns ``(name, index, bars, Progression|None)`` for the current section (``index`` increments on every entry, so verse→verse re-entry resets correctly) or ``None`` when no form is active. ``cadence_requests`` is the request-hook seam: a mutable ``{bar: name}`` dict (shared with ``Composition.request_cadence``) the live walk steers toward — at the first boundary with a pending request, the remaining changes up to its bar are planned as a constrained walk pinned to the cadence formula (resolved by ``resolve_cadence``) and then committed one boundary at a time. ``get_section_cadence`` turns a section entry into a request arriving at that section's final bar (live sections only). Requests whose bar passes unserved expire with a warning. The clock fires ``reschedule_lookahead`` beats before each boundary — raised by the caller to the maximum pattern lookahead, so the window always covers a pattern's next cycle before it rebuilds. .. py:function:: schedule_patterns(sequencer: subsequence.sequencer.Sequencer, patterns: Iterable[subsequence.pattern.Pattern], start_pulse: int = 0) -> None :async: Schedule a collection of repeating patterns from a shared start pulse. .. py:function:: schedule_task(sequencer: subsequence.sequencer.Sequencer, fn: Callable, cycle_beats: int, reschedule_lookahead: int = 1, defer: bool = False) -> None :async: Schedule a non-blocking repeating task on the sequencer's beat clock. If ``fn`` declares a first parameter named ``p``, it is called with a :class:`ScheduleContext` on every invocation (same behaviour as ``composition.schedule()``). When *defer* is True the backshift fire at pulse 0 is skipped; the first call happens one full *cycle_beats* later. Direct API users who need the equivalent of ``initial=True`` can simply ``await fn()`` themselves before calling this function.