subsequence.pattern_builder =========================== .. py:module:: subsequence.pattern_builder .. autoapi-nested-parse:: ``PatternBuilder`` (the ``p`` inside a pattern) — the note-placement surface. This is the ``p`` handed to every ``@composition.pattern`` function: the verbs for placing notes, drums, chords, motifs and phrases, plus articulation, the transforms, and the algorithmic and MIDI mixins it inherits. It renders into the plain data types in ``pattern``. Module Contents --------------- .. py:class:: BarCycle(bar: int, length: int) Position of the current bar within a repeating cycle of bars. Returned by :meth:`PatternBuilder.bar_cycle`. Provides readable, musician-friendly properties for bar-position logic without raw modulo arithmetic. .. attribute:: bar Zero-indexed bar within the cycle (0 … length−1). .. attribute:: length The cycle length in bars passed to :meth:`PatternBuilder.bar_cycle`. .. py:property:: first :type: bool True on the first bar of the cycle (``bar == 0``). .. py:property:: last :type: bool True on the last bar of the cycle (``bar == length − 1``). .. py:property:: progress :type: float 0.0 on bar 0, rising each bar. For a 4-bar cycle: 0.0, 0.25, 0.5, 0.75. Useful for gradual intensity curves or as a noise/LFO seed. :type: Fractional progress through the cycle .. py:class:: PatternBuilder(pattern: subsequence.pattern.Pattern, cycle: int, conductor: Optional[subsequence.conductor.Conductor] = 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, section: Any = None, bar: int = 0, rng: Optional[random.Random] = None, tweaks: Optional[Dict[str, Any]] = None, default_grid: int = 16, data: Optional[Dict[str, Any]] = None, key: Optional[str] = None, scale: Optional[str] = None, time_signature: Tuple[int, int] = (4, 4), held_notes: Optional[subsequence.held_notes.HeldNotes] = None, harmony: Optional[Any] = None, section_motifs: Optional[Dict[Tuple[str, Optional[str]], Any]] = None, energy: float = 0.5) Bases: :py:obj:`subsequence.pattern_algorithmic.PatternAlgorithmicMixin`, :py:obj:`subsequence.pattern_midi.PatternMidiMixin` The musician's 'palette' for creating musical content. A ``PatternBuilder`` instance (commonly named ``p``) is passed to every pattern function. It provides methods for placing notes, generating rhythms, and transforming the resulting sequence (e.g., swinging, reversing, or transposing). Rhythm in Subsequence is typically expressed in **beats** (where 1.0 is a quarter note) or **steps** (subdivisions of a pattern). Initialize the builder with pattern context, cycle count, and optional section info. :param pattern: The ``Pattern`` instance this builder populates. :param cycle: Zero-based rebuild counter. :param conductor: Optional ``Conductor`` for time-varying signals. :param drum_note_map: Optional mapping of drum names to MIDI notes. :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 (0–16383). Used by ``p.nrpn()`` and ``p.nrpn_ramp()`` for symbolic access — typically a device-specific dictionary (e.g. Sequential Take 5's ``Osc1FreqFine`` → 9). :param section: Current ``SectionInfo`` (or ``None``). :param bar: Global bar count. :param rng: Optional seeded ``Random`` for reproducibility. :param tweaks: Per-pattern overrides set via ``composition.tweak()``. :param default_grid: Number of grid slots used by ``hit_steps()``, ``sequence()``, and ``rotate()`` when no explicit ``grid`` is passed. Normally set automatically from the decorator's ``beats``/``bars``/``steps`` and ``step_duration`` parameters. :param data: Shared state dict from the parent ``Composition`` (same object as ``composition.data``). Read and write via ``p.data`` for cross-pattern communication and external data access. Patterns rebuild in definition order; when two patterns share the same ``length``, a writer defined earlier in source is guaranteed to run before a reader defined later in the same cycle. :param key: The composition's key (e.g. ``"C"``), used by ``p.progression()`` to generate chords from a graph style and by ``p.motif()`` to resolve scale degrees. ``None`` when the composition has no key set. :param scale: The composition's scale/mode name (e.g. ``"minor"``), read via ``p.scale`` and used to resolve scale degrees in ``p.motif()``. ``None`` means ionian/major. :param time_signature: The composition's time signature, read via ``p.time_signature``; powers the metric-weight table. :param section_motifs: Optional reference to the composition's section-motif registry, read by ``p.section_motif()``. :param harmony: Optional read-only harmony window view for this cycle (``p.harmony``) — ``p.harmony.chord``, ``chord_at(beat)``, ``next_chord``, ``until_change``. ``None`` until the harmonic clock has published a window. :param held_notes: Optional live held-note tracker from ``composition.note_input()``. Read via ``p.held_notes()``. ``None`` when no note input was declared (and when rendering headlessly), so the accessor returns an empty list. :param energy: The current section's energy level (0.0–1.0), read via ``p.energy`` — the arranging dial. 0.5 when no energy source is configured. .. py:method:: apply_tuning(tuning: subsequence.tuning.Tuning, bend_range: float = 2.0, channels: Optional[List[int]] = None, reference_note: int = 60) -> PatternBuilder Apply a microtonal tuning to this pattern via pitch bend injection. For each note in the pattern, the nearest 12-TET MIDI pitch is computed and a pitchwheel ``CcEvent`` is injected at the note's onset to shift the synthesiser to the exact tuned frequency. Existing pitch bend events (from ``p.portamento()``, ``p.slide()``, etc.) are shifted additively so they still work correctly within the tuned pitch space. For polyphonic patterns, supply a ``channels`` pool. Notes will be spread across those channels so each can carry an independent pitch bend. For monophonic patterns, leave ``channels=None``. The synthesiser's pitch-bend range must match ``bend_range``. Most synths default to ±2 semitones. For tunings that deviate more than one semitone from 12-TET, increase ``bend_range`` (e.g., 12 or 24) and configure the synth to match. :param tuning: The :class:`~subsequence.tuning.Tuning` to apply. :param bend_range: Synth pitch-bend range in semitones (default ±2). :param channels: Channel pool for polyphonic rotation. ``None`` keeps all notes on the pattern's own channel. :param reference_note: MIDI note number that maps to scale degree 0. Default 60 (middle C). .. rubric:: Example .. code-block:: python from subsequence import Tuning meantone = Tuning.from_scl("meanquar.scl") @composition.pattern(channel=1, beats=4) def melody (p): p.seq("x x x x", pitch=60) p.apply_tuning(meantone, bend_range=2.0) .. py:method:: arpeggio(notes: Any, root: Optional[int] = None, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, count: Optional[int] = None, inversion: int = 0, beat: float = 0.0, span: Optional[float] = None, spacing: float = 0.25, duration: Optional[float] = None, direction: str = 'up', seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Arpeggiate a chord (or a list of pitches) — cycle the notes one at a time at regular beat intervals. Like ``chord()`` and ``strum()``, the first argument can be a chord — the ``chord`` passed to your pattern function, or any chord from ``p.progression()`` — and ``root`` / ``count`` / ``inversion`` voice it exactly as they do. So "play this as a chord, a strum, or an arpeggio" is a one-word verb swap:: for chord, start, length in p.progression("phrygian_minor", harmonic_rhythm=...): p.arpeggio(chord, root=48, beat=start, span=length, spacing=0.25, count=4) Pass a list of pitches instead to arpeggiate something that isn't a chord (a scale fragment, a custom voicing). Unlike a held ``chord()``, an arpeggio is a stream of single notes, so it has no ``sustain`` / ``legato`` / ``detached`` — use ``duration`` for how long each note rings and ``span`` for how much of the bar the figure fills. An empty pitch list rests (places nothing), so a live arpeggiator over ``p.held_notes()`` is simply silent when no keys are held:: p.arpeggio(p.held_notes(), direction="up") :param notes: A chord to arpeggiate (anything with a ``.tones()`` method — the pattern's ``chord``, or a chord from ``p.progression()``), or a list of MIDI note numbers (e.g. ``60``) / drum-name strings when the pattern has a ``drum_note_map``. For pitched note *names* use the integer constants in ``subsequence.constants.midi_notes`` (e.g. ``notes.C4``). In the list form, a drum name the map lacks is dropped (warned once); a string with no map at all still raises. :param root: MIDI root note for the chord form (e.g. 48), exactly as ``chord()``. Required for a chord; not used for a plain pitch list. :param velocity: MIDI velocity for all notes (default 100 — arpeggios sit in the melodic-line velocity bucket, not the softened-chord bucket; pass ``velocity=90`` to match ``chord()``), or a ``(low, high)`` tuple for a fresh random draw per note. :param count: Number of voices for the chord form (cycles tones into higher octaves if larger than the chord's natural size). Chord form only. :param inversion: Chord inversion for the chord form (ignored when voice leading is on). Chord form only. :param beat: Beat to start the figure at (default 0.0 = the start of the pattern). Use it to place an arpeggio over one progression chord. :param span: How many beats the figure fills, starting at ``beat`` (default: to the end of the pattern). Pass the chord's ``length`` from a progression loop to confine the arpeggio to its slot. :param spacing: Time between each note in beats (default 0.25 = 16th note). :param duration: Note duration in beats. Defaults to ``spacing`` (each note fills its slot exactly). :param direction: Order in which the notes are cycled: - ``"up"`` — lowest to highest, then wrap (default). - ``"down"`` — highest to lowest, then wrap. - ``"up_down"`` — ascend then descend (ping-pong), cycling. - ``"random"`` — shuffled once per call using *rng*. :param rng: Random number generator used when ``direction="random"``. Defaults to ``self.rng`` (the pattern's seeded RNG). .. rubric:: Example .. code-block:: python # Arpeggiate the pattern's current chord, four voices ascending p.arpeggio(chord, root=60, count=4, spacing=0.25) # A plain list of pitches — ping-pong: C E G E C E G E ... p.arpeggio([60, 64, 67], spacing=0.25, direction="up_down") # One chord of a progression, confined to its slot, humanised p.arpeggio(chord, root=48, beat=start, span=length, velocity=(60, 95)) .. py:method:: bar_cycle(length: int) -> BarCycle Return the current bar's position within a repeating cycle of bars. A thin wrapper around ``p.bar % length`` that replaces opaque modulo arithmetic with readable, musician-friendly properties. :param length: The cycle length in bars (e.g., 4, 8, 16). :returns: A :class:`BarCycle` with ``.bar``, ``.first``, ``.last``, and ``.progress`` properties. .. rubric:: Example .. code-block:: python # Every 4 bars (replaces: if p.bar % 4 == 0) if p.bar_cycle(4).first: p.hit_steps("snare_1", [0, 8], velocity=110) # Last bar of every 16-bar cycle (replaces: if p.bar % 16 == 15) if p.bar_cycle(16).last: p.euclidean("hi_hat_open", 3) # Build intensity over an 8-bar arc intensity = p.bar_cycle(8).progress # 0.0 → 0.875 p.velocity_shape(low=int(40 + 40 * intensity), high=100) .. py:method:: broken_chord(chord_obj: Any, root: int, order: List[int], spacing: float = 0.25, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_CHORD_VELOCITY, duration: Optional[float] = None, inversion: int = 0, beat: float = 0.0, span: Optional[float] = None) -> PatternBuilder Play a chord as an arpeggio in a specific or random order. This generates the chord tones and maps them according to the provided ``order`` list of indices, then delegates to ``arpeggio()``. It is ideal for broken chords or random chord-tone melodies. Because the order is a list of node indices, the number of generated tones is automatically set to ``max(order) + 1`` to ensure all indices are valid. Higher indices will cycle into the next octave. :param chord_obj: The chord to play (usually from ``p.section.chord``). :param root: MIDI root note (e.g., 60 for Middle C). :param order: List of indices into the chord tones array, dictating playback order. :param spacing: Time between each note in beats (default 0.25 = 16th note). :param velocity: MIDI velocity for all notes (default 90 — broken_chord is a chord voice, so it sits in the softer chord velocity bucket like ``chord()`` and ``strum()``), or a ``(low, high)`` tuple for a fresh random draw per note. :param duration: Note duration in beats. Defaults to ``spacing``. :param inversion: Specific chord inversion (ignored if voice leading is on). :param beat: Beat to start the broken chord at (default 0.0). :param span: How many beats to fill from ``beat`` (default: to the end of the pattern). Like ``arpeggio()``, use it to place a broken chord over one chord of a progression. Example:: # A 5-note broken chord using a predefined pattern p.broken_chord(chord, root=60, order=[4, 0, 2, 1, 3], spacing=0.25) # A fully random broken chord using the pattern's deterministic RNG order = list(range(5)) p.rng.shuffle(order) p.broken_chord(chord, root=60, order=order) .. py:method:: build_velocity_ramp(low: int, high: int, shape: str = 'linear', grid: Optional[int] = None) -> List[int] Build a per-step velocity list that ramps from *low* to *high*. A musician-friendly shortcut for the common pattern of generating a fixed-length velocity sweep using an easing curve. Returns ``List[int]`` ready to pass directly to ``velocities=`` parameters. :param low: Velocity at the first step (0–127). :param high: Velocity at the last step (0–127). :param shape: Easing curve name (see ``subsequence.easing``). Common values: ``"linear"``, ``"ease_in"``, ``"ease_out"``, ``"ease_in_out"``. Defaults to ``"linear"``. :param grid: Number of steps (defaults to ``p.grid``). :returns: ``List[int]`` of length ``grid``, values clamped to 0–127. Example:: # Snare roll that swells into a downbeat p.sequence( steps=range(16), pitches="snare_1", durations=0.1, velocities=p.build_velocity_ramp(25, 100, "ease_in"), ) # Fade-out ghost fill p.ghost_fill("snare_1", 1, velocity=p.build_velocity_ramp(80, 20, "ease_out"), bias="sixteenths", no_overlap=True) .. py:method:: capture(beat: float = 0.0, span: float = 4.0) -> subsequence.motifs.Motif Read the notes placed so far back out as a :class:`~subsequence.motifs.Motif`. The captured motif is **absolute MIDI and lossy by design**: relative specs (degrees, chord tones) do not survive resolution, timing is pulse-truncated, probabilities have already rolled, and control gestures are not captured. The round trip is generate → place → capture → hand-edit → rebind. :param beat: Window start within the pattern. :param span: Window length in beats (also the captured motif's length). .. py:method:: chord(chord_obj: Any, root: int, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_CHORD_VELOCITY, sustain: bool = False, duration: float = 1.0, inversion: int = 0, count: Optional[int] = None, legato: Optional[float] = None, detached: Optional[float] = None, beat: float = 0.0) -> PatternBuilder Place a chord at ``beat`` (the start of the pattern by default). Note: If the pattern was registered with ``voice_leading=True``, this method automatically chooses the best inversion. :param chord_obj: The chord to play (usually the ``chord`` parameter passed to your pattern function). :param root: MIDI root note (e.g., 60 for Middle C). :param velocity: MIDI velocity (default 90), or a ``(low, high)`` tuple for a fresh random draw per chord tone (each voice gets a slightly different velocity — useful for humanising the "fingers" feel). :param sustain: If True, the notes last for the entire pattern duration. Mutually exclusive with ``legato`` and ``detached``. :param duration: Note duration in beats (default 1.0). Ignored when ``legato`` or ``detached`` is set, since those recalculate durations. :param inversion: Specific chord inversion (ignored if voice leading is on). :param count: Number of notes to play (cycles tones if higher than the chord's natural size). :param legato: If given, calls ``p.legato(ratio)`` after placing the chord, stretching each note to fill ``ratio`` of the gap to the next note. Mutually exclusive with ``sustain`` and ``detached``. :param detached: If given, the chord rings until ``detached`` beats before the next cycle — equivalent to setting ``duration = pattern.length - detached``. Use this for a declarative polyphony-safety margin so the chord always releases before the next chord begins. Mutually exclusive with ``sustain`` and ``legato``. :param beat: Beat offset to place the chord at (default 0.0 = the start of the pattern). ``sustain`` and ``detached`` still measure their ring from the pattern length, not from ``beat`` — when placing several positioned chords (e.g. over a progression) set ``duration`` explicitly instead. Example:: # Shorthand for: p.chord(...) then p.legato(0.9) p.chord(chord, root=root, velocity=85, count=4, legato=0.9) # Hold the chord almost the full cycle, releasing 0.25 beats # before the next chord begins. p.chord(chord, root=root, velocity=85, count=5, detached=0.25) .. py:method:: detached(beats: float = 0.05) -> PatternBuilder Shorten note durations so a guaranteed silence precedes the next onset. The complement of :meth:`legato`. For every placed note, the duration is shrunk so that at least ``beats`` beats of silence remain before the next note begins (wrapping around to the first note for the last one). Use this when you want a clean detached articulation, or as a polyphony-safety margin between chord transitions on a monophonic or voice-limited synth. :param beats: Minimum gap in beats before the next onset (default 0.05 — roughly 25 ms at 120 BPM). Must be positive. Example:: # Bassline on a mono synth: each 16th note ends 0.05 beats # before the next, so the synth never retriggers mid-note. p.arpeggio(chord.tones(36, count=4), spacing=0.25).detached() # Explicit larger gap for a longer release tail. p.melody(state, spacing=0.25).detached(0.1) .. py:method:: drone(pitch: Union[int, str], beat: float = 0.0, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY) -> PatternBuilder A musical alias for ``note_on``. Places a raw Note On event without a duration, typically used for sustained notes that span multiple cycles. Must be silenced later using ``drone_off()``. :param pitch: MIDI note number (0-127) or a drum name string. :param beat: The beat position (0.0 is the start). :param velocity: MIDI velocity (0-127, default 100), or a ``(low, high)`` tuple for a single random draw. .. py:method:: drone_off(pitch: Union[int, str]) -> PatternBuilder A musical alias for ``note_off``. Places a raw Note Off event at beat 0.0. Used to stop a sequence started by ``drone()``. :param pitch: MIDI note number (0-127) or a drum name string. .. py:method:: dropout(probability: float, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Randomly remove notes from the pattern. This operates on all notes currently placed in the builder. :param probability: The chance (0.0 to 1.0) of each pulse POSITION being removed — all notes sharing that position (a chord's voices, layered drums) live or die together. .. py:method:: duck_map(steps: Iterable[int], floor: float = 0.0, grid: Optional[int] = None) -> List[float] Build a per-step velocity multiplier list for sidechain-style ducking. Returns a list of floats, one per grid step: ``floor`` at each trigger step in ``steps``, ``1.0`` everywhere else. Pass the result to ``p.data`` for another pattern to read, then apply with ``p.scale_velocities()``. :param steps: Grid indices that trigger ducking (e.g. kick hit positions). :param floor: Multiplier written at trigger steps. ``0.0`` = full silence, ``1.0`` = no effect. Values in between give partial ducking. :param grid: Grid resolution (defaults to ``p.grid``). :returns: ``List[float]`` of length ``grid``. Example:: # Full duck on kick hits p.data["kick_sc"] = p.duck_map(kick_steps) # Softer duck p.data["kick_sc"] = p.duck_map(kick_steps, floor=0.3) # Velocity-proportional: deeper duck for harder kicks p.data["kick_sc"] = p.duck_map(kick_steps, floor=1.0 - (velocity / 127)) .. py:method:: duration(beats: float) -> PatternBuilder Set every note's duration to a fixed length in beats. This overrides any existing note durations, acting as a global 'gate time' relative to the beat (1.0 = a quarter note). Short values clip notes tight; long values let them ring. For a guaranteed gap before each next onset regardless of note spacing, use :meth:`detached`; for a classic staccato articulation, either a short fixed value (``p.duration(0.1)``) or ``p.detached()`` works. :param beats: Fixed note duration in beats (relative to a quarter note). 0.5 = eighth-note length, 0.25 = sixteenth-note length. Must be positive. .. py:method:: every(n: int, fn: Callable[[PatternBuilder], None]) -> PatternBuilder Apply a transformation every Nth cycle. :param n: The cycle frequency (e.g., 4 = every 4th bar). :param fn: A function (often a lambda) that receives the builder and calls further methods. .. rubric:: Example .. code-block:: python # Reverse every 4th bar p.every(4, lambda p: p.reverse()) .. py:method:: groove(template: subsequence.groove.Groove, strength: float = 1.0) -> PatternBuilder Apply a groove template to all notes in the pattern. A groove is a repeating pattern of per-step timing offsets and optional velocity adjustments. It gives a pattern its characteristic rhythmic feel - swing, shuffle, MPC pocket, or any custom shape. Construct a groove with one of the factory methods: - ``Groove.swing(percent)`` - simple swing by percentage (or use the ``p.swing()`` shortcut for common cases) - ``Groove.from_agr(path)`` - import timing from an Ableton .agr file - ``Groove(offsets=[...], grid=0.25, velocities=[...])`` - fully custom ``p.groove()`` is a post-build transform - call it after all notes have been placed. It pairs well with ``p.randomize()`` for structured feel plus organic micro-variation. :param template: A ``Groove`` instance defining the timing/velocity template. :param strength: How much of the groove to apply (0.0-1.0). 0.0 = no effect, 1.0 = full groove. Blends timing offsets and velocity deviation proportionally - equivalent to Ableton's TimingAmount and VelocityAmount dials. Example:: groove = subsequence.Groove.swing(percent=57) @composition.pattern(channel=10, beats=4) def drums (p): p.hit_steps("kick", [0, 8], velocity=100) p.hit_steps("hh", range(16), velocity=80) p.groove(groove) # full strength p.groove(groove, strength=0.5) # half-strength blend .. py:method:: held_notes() -> List[int] Return the MIDI notes currently held on the ``note_input`` keyboard. The notes are sorted ascending. Pass the result straight to ``p.arpeggio()`` to arpeggiate whatever the player is holding — ``p.arpeggio(p.held_notes())`` rests when no keys are down. Returns an empty list when no ``note_input()`` source was declared and when rendering headlessly (so seeded output stays deterministic). The set is sampled once per rebuild; ``note_input(release_ms=…)`` smooths the gap during hand-position changes so the arp does not drop out, and ``note_input(latch=True)`` holds the chord until you play a new one. .. py:method:: hit(pitch: Union[int, str], beats: List[float], velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, duration: float = 0.1) -> PatternBuilder Place multiple short 'hits' at a list of beat positions. :param pitch: MIDI note number or drum name. :param beats: List of beat positions. :param velocity: MIDI velocity (0-127), or a ``(low, high)`` tuple for a fresh random draw per hit. :param duration: Note duration in beats. .. rubric:: Example .. code-block:: python p.hit("snare", [1, 3]) # Standard backbeat p.hit("snare", [1, 3], velocity=(80, 110)) # Human velocity range .. py:method:: hit_steps(pitch: Union[int, str], steps: List[int], velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, duration: float = 0.1, grid: Optional[int] = None, probability: float = 1.0, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Place short hits at specific step (grid) positions. :param pitch: MIDI note number or drum name. :param steps: A list of grid indices (0 to ``grid - 1``). :param velocity: MIDI velocity (0-127), or a ``(low, high)`` tuple for a fresh random draw per step. :param duration: Note duration in beats. :param grid: How many grid slots the pattern is divided into. Defaults to the pattern's ``default_grid`` (set from the decorator's ``steps``/``step_duration``, or sixteenth-note resolution when ``unit`` is omitted). :param probability: Chance (0.0 to 1.0) that each hit will play. :param rng: Optional random generator (overrides the pattern's seed). .. rubric:: Example .. code-block:: python # Typical sixteenth-note hi-hats with some probability variation p.hit_steps("hh", range(16), velocity=70, probability=0.8) # Humanised hi-hats — each step gets a fresh random velocity. p.hit_steps("hh", range(16), velocity=(40, 90)) .. py:method:: invert(pivot: int = 60) -> PatternBuilder Invert all pitches around a pivot note. .. py:method:: legato(ratio: float = 1.0) -> PatternBuilder Adjust note durations to fill the gap until the next note. :param ratio: How much of the gap to fill (0.0 to 1.0). 1.0 is full legato, < 1.0 is staccato. .. py:method:: motif(m: subsequence.motifs.Motif, beat: float = 0.0, span: Optional[float] = None, root: int = 60, velocity: Optional[Union[int, Tuple[int, int]]] = None, fit: Optional[float] = None, fit_weights: Optional[List[float]] = None, resolution: Optional[int] = None) -> PatternBuilder Place an immutable :class:`~subsequence.motifs.Motif` onto the pattern. Note events route through the universal ``note()`` funnel (drum names, mirrors, velocity tuples all work); control gestures emit through the same machinery as ``cc()`` / ``cc_ramp()`` / ``pitch_bend()`` / ``nrpn()`` / ``osc()``. Pitch specs resolve here, late: ints are MIDI, strings are drum names, scale degrees resolve against the composition key + scale anchored near ``root=``. Per-event probabilities roll fresh each cycle against the pattern's seeded stream. :param m: The motif value (anything exposing ``.events`` / ``.length`` places; ``.controls`` is read when present). :param beat: Where the motif starts within the pattern. :param span: Clamp — events whose onset falls at or beyond *span* beats into the motif are dropped (the ``arpeggio()`` convention). :param root: Register anchor for scale-degree resolution: the tonic lands at its nearest instance to this MIDI note (ties resolve upward) and the melody keeps its written contour from there. :param velocity: Optional override applied to every note (otherwise each event's own velocity is used). :param fit: The chord-tones-on-strong-beats dial, 0.0–1.0: resolved Degree/int pitches landing on strong beats (metric weight >= 0.5) snap to the nearest chord tone with this probability. Defaults to the motif's own ``fit`` (0.7 on generated motifs, none on hand-written ones — typed degrees are sacred); inactive without a chord context. ChordTone and Approach events never snap — their harmony reading is inherent (an Approach's chromaticism is the point). :param fit_weights: Custom per-step metric weight list (the ``build_ghost_bias`` precedent) for additive or non-isochronous meters; defaults to the time signature's table. :param resolution: Pulses between control-ramp messages (defaults to each control verb's own default). Kept out of the value by design: beats and shapes are music, traffic density is wire. .. py:method:: note(pitch: Union[int, str], beat: float, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, duration: float = 0.25) -> PatternBuilder Place a single MIDI note at a specific beat position. A drum name is carried through to the mirror fan-out so each device can re-resolve it through its own ``drum_note_map``. A name no destination maps (not in the pattern's own map nor any mirror's) is dropped and warned once — it does not raise — so device maps can legitimately lack voices others have. (A string pitch with **no** ``drum_note_map`` at all is still a configuration error and raises.) :param pitch: MIDI note number (0-127) or a drum name string from the pattern's ``drum_note_map``. :param beat: The beat position (0.0 is the start). Negative values wrap from the end (e.g., -1.0 is one beat before the end). :param velocity: MIDI velocity (0-127, default 100), or a ``(low, high)`` tuple for a single random draw. :param duration: Note duration in beats (default 0.25). .. rubric:: Example .. code-block:: python p.note(60, beat=0, velocity=110) # Middle C on beat 1 p.note("kick", beat=1.0) # Kick on beat 2 p.note(67, beat=-0.5, duration=0.5) # G on the 'and' of the last beat .. py:method:: note_off(pitch: Union[int, str], beat: float) -> PatternBuilder Place an explicit Note Off event to silence a drone. :param pitch: MIDI note number (0-127) or a drum name string. :param beat: The beat position (0.0 is the start). A drum name this device's ``drum_note_map`` lacks is dropped (warned once) rather than raising; with no ``drum_note_map`` at all it raises. .. py:method:: note_on(pitch: Union[int, str], beat: float, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY) -> PatternBuilder Place an explicit Note On event without a duration. Useful for drones or infinite sustains. Must be paired with a ``note_off()`` later to silence the note. :param pitch: MIDI note number (0-127) or a drum name string. :param beat: The beat position (0.0 is the start). :param velocity: MIDI velocity (0-127, default 100), or a ``(low, high)`` tuple for a single random draw. A drum name this device's ``drum_note_map`` lacks is dropped (warned once) rather than raising — consistent with the step-note methods. A string pitch with no ``drum_note_map`` at all is still a configuration error and raises. .. py:method:: param(name: str, default: Any = None) -> Any Read a tweakable parameter for this pattern. Returns the value set via ``composition.tweak()`` if one exists, otherwise returns ``default``. :param name: The parameter name. :param default: The value to return if no tweak is active. Example:: @composition.pattern(channel=1, beats=4) def bass (p): pitches = p.param("pitches", [60, 64, 67, 72]) p.sequence(steps=[0, 4, 8, 12], pitches=pitches) .. py:method:: phrase(value: Any, root: int = 60, velocity: Optional[Union[int, Tuple[int, int]]] = None, fit: Optional[float] = None, resolution: Optional[int] = None, align: str = 'pattern', offset: float = 0.0) -> PatternBuilder Place this cycle's window of a Phrase — position computed, never stored. The playback position is stateless arithmetic over the engine's own counters: ``pos = (p.cycle * pattern_length + offset) % phrase.length`` — deterministic under live reload, ``form_jump``, and render, with zero new state. A pattern shorter than the phrase walks through it cycle by cycle; deliberately mismatched lengths are phase drift (polymeter against the phrase). When the cycle window crosses the phrase's end, the phrase loops. Patterns that should own the phrase's length call ``p.set_length(phrase.length)`` once instead. :param value: A Phrase (or any value with ``.length``/``.slice``; a Motif places its window directly). :param root: Register anchor for degree resolution (see ``motif()``). :param velocity: Optional override applied to every note. :param fit: Passed through to ``motif()`` (active with the melody engine stage). :param resolution: Control-ramp pulse density (see ``motif()``). :param align: ``"pattern"`` (default) counts pattern cycles; ``"section"`` uses the bar within the current form section, so the phrase restarts when the section does. :param offset: Beats added to the computed position (a phase shift). .. rubric:: Example .. code-block:: python @comp.pattern(channel=4, bars=2) def lead (p): p.phrase(lead_line, root=72) .. py:method:: progression(source: subsequence.progressions.ProgressionSource, harmonic_rhythm: subsequence.progressions.HarmonicRhythmSpec, key: Optional[str] = None, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> subsequence.progressions.Progression Realise a chord progression across the pattern, returning it to place yourself. Returns a freshly realised :class:`~subsequence.progressions.Progression` — an iterable of ``(chord, start, length)`` events laying a progression end-to-end across the pattern's length, each chord given a length drawn from *harmonic_rhythm* (the musical term for how often the chords change). You loop over it and play each chord however you like — block, strummed, or arpeggiated:: for chord, start, length in p.progression("phrygian_minor", harmonic_rhythm=between(WHOLE, 3 * WHOLE, step=WHOLE), seed=7): p.strum(chord, root=48, beat=start, duration=length - 0.25, spacing=0.04, count=4) This is the **part-level** progression seam: it re-realises a fresh value each rebuild (the breathing behaviour), runs entirely outside the global harmonic clock — so a part can inhabit its own harmonic world (polytonality) or move faster than the clock's span floor — and never advances engine state. For a one-call block-chord part with no loop, use ``composition.chords()``. :param source: A built-in chord-graph style name (e.g. ``"phrygian_minor"``) to *generate* a progression; an explicit element list — ints where diatonic, name or roman strings (``["Cm7", 6, "bVII"]``), ``Chord`` objects — cycled to fill the pattern; or a :class:`~subsequence.progressions.Progression` value (its spans cycled, decoration preserved). :param harmonic_rhythm: How long each chord lasts, in beats. One of: a single number (static); a list of lengths (a shaped rhythm such as ``[WHOLE, HALF, HALF]``, cycled per chord); or ``between(low, high, step=...)`` for a bounded, optionally-quantised random length. :param key: Key for styles and key-relative elements (degrees/romans); defaults to the composition's key. :param seed: If given, the progression is realised from a fresh ``Random(seed)`` so it is identical on every cycle (a fixed phrase). When omitted, the pattern's own RNG is used, so it can vary per cycle (still reproducible under a composition seed). :returns: A ``Progression`` you can iterate as ``(chord, start, length)`` tuples (or read via ``.events()`` / ``print()``). .. py:method:: randomize(timing: float = 0.03, velocity: float = 0.0, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Add random variations to note timing and velocity. Introduces small imperfections — the micro-variations that distinguish a played performance from a perfectly quantized sequence. Called with no arguments, only timing variation is applied (velocity defaults to 0.0 — no change). Pass a velocity value to also randomise dynamics: # Timing only (default) p.randomize() # Both axes p.randomize(timing=0.04, velocity=0.08) # Stronger feel p.randomize(timing=0.08, velocity=0.15) Resolution note: the sequencer runs at 24 PPQN. At 120 BPM, one pulse ≈ 20ms. Timing shifts smaller than roughly 0.04 beats may have no audible effect because they round to zero pulses. Recommended range: timing=0.02–0.08, velocity=0.05–0.15. When the composition has a seed set, ``p.rng`` is deterministic, so ``p.randomize()`` produces the same result on every run. :param timing: Maximum timing offset in beats (e.g. 0.05 = ±1.2 pulses at 24 PPQN). Notes shift by a random amount within ``[-timing, +timing]`` beats. Clamped to pulse 0 at the lower bound. :param velocity: Maximum velocity scale factor (0.0 to 1.0). Each note's velocity is multiplied by a random value in ``[1 - velocity, 1 + velocity]``, clamped to 1–127. :param rng: Random instance to use. Defaults to ``self.rng`` (seeded when the composition has a seed). .. py:method:: repeat(pitch: Union[int, str], spacing: float, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, duration: float = 0.25) -> PatternBuilder Repeat a note at a fixed beat interval for the whole pattern. The classic 'Note Repeat' of MPC, Push, and Maschine fame: one pitch firing at a steady rate — running hi-hats, a pulsing bass note, a metronome click. :param pitch: MIDI note number or drum name. :param spacing: Time between each note in beats (0.25 = sixteenth notes). :param velocity: MIDI velocity (default 100), or a ``(low, high)`` tuple for a fresh random draw per note. :param duration: Note duration in beats. .. rubric:: Example .. code-block:: python p.repeat("hh", spacing=0.25) # sixteenth notes p.repeat("hh", spacing=0.25, velocity=(40, 80)) # humanised .. py:method:: reverse() -> PatternBuilder Flip the pattern backwards in time (retrograde). .. py:method:: rotate(steps: int, grid: Optional[int] = None) -> PatternBuilder Rotate the pattern by a number of grid steps, wrapping around. Notes pushed past the end of the pattern re-enter at the start (and vice versa for negative values) — the step-sequencer rotation familiar from Euclidean rhythm tools. :param steps: Positive values rotate later in time, negative values earlier. :param grid: The grid resolution. Defaults to the pattern's ``default_grid`` (derived from the decorator's ``beats``/``steps`` and ``step_duration``). .. py:method:: scale_velocities(factors: Sequence[float], grid: Optional[int] = None) -> PatternBuilder Scale note velocities by a per-step multiplier list. Each note's velocity is multiplied by the factor at the corresponding grid step index. A factor of ``1.0`` leaves the velocity unchanged; ``0.0`` silences the note; ``0.5`` halves it. :param factors: Per-step multipliers, one float per grid step. Values outside ``[0.0, 1.0]`` are valid — result is clamped to ``[0, 127]`` after scaling. :param grid: Grid resolution (defaults to ``p.grid``). Must match the length of ``factors``. :returns: ``self`` for fluent chaining. Example:: # Sidechain ducking: silence bass on kick steps, full volume elsewhere. kick_steps = {0, 4, 8, 12} p.data["kick_sc"] = [0.0 if s in kick_steps else 1.0 for s in range(p.grid)] # In the bass pattern: p.scale_velocities(p.data.get("kick_sc", [1.0] * p.grid)) .. py:method:: section_motif(part: Optional[str] = None) -> Optional[Any] The Motif/Phrase bound to the current section (and part), or ``None``. Reads the ``composition.section_motifs()`` registry for the section currently playing. A section with no binding returns ``None`` — bind material or rest; no fallback guessing:: @comp.pattern(channel=4, bars=2) def lead (p): line = p.section_motif("lead") if line is not None: p.phrase(line, root=72) .. py:method:: seq(notation: str, pitch: Union[str, int, None] = None, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Build a pattern using an expressive string-based 'mini-notation'. The notation distributes events evenly across the current pattern length. **Syntax:** - ``x y z``: Items separated by spaces are distributed across the bar. - ``[a b]``: Groups items into a single subdivided step. - ``~`` or ``.``: A rest. - ``_``: Extends the previous note (sustain). - ``x?0.6``: Probability suffix — fires with the given probability (0.0–1.0). :param notation: The mini-notation string. :param pitch: If provided, all symbols in the string are triggers for this specific pitch. If ``None``, symbols are interpreted as pitches (e.g., "60" or "kick"). :param velocity: MIDI velocity (default 100), or a ``(low, high)`` tuple for a fresh random draw per event. .. rubric:: Example .. code-block:: python # Simple kick rhythm p.seq("kick . [kick kick] .") # Subdivided melody p.seq("60 [62 64] 67 60") # Ghost snare: snare on 2 and 4, ghost note 50% of the time p.seq(". snare?0.5 . snare") .. py:method:: sequence(steps: List[int], pitches: Union[int, str, List[Union[int, str]]], velocities: Union[int, Tuple[int, int], List[int]] = subsequence.constants.velocity.DEFAULT_VELOCITY, durations: Union[float, List[float]] = 0.1, grid: Optional[int] = None, probability: float = 1.0, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder A multi-parameter step sequencer. Define which grid steps fire, and then provide a list of pitches, velocities, and durations. If you provide a list for any parameter, Subsequence will step through it as it places each note. :param steps: List of grid indices to trigger. An empty list is a no-op — no notes are placed and the builder is returned unchanged (handy when probabilistic gating rejects every step). :param pitches: Pitch or list of pitches. :param velocities: Velocity (default 100), ``(low, high)`` tuple for a fresh random draw per step, or a list of velocities cycled per step. :param durations: Duration or list of durations (default 0.1). :param grid: Grid resolution. Defaults to the pattern's ``default_grid`` (derived from the decorator's ``beats``/``steps`` and ``unit``). .. py:method:: set_length(length: float) -> PatternBuilder Dynamically change the length of the pattern. The new length takes effect immediately for any subsequent notes placed in the current builder call, and will be used by the sequencer for next cycle's scheduling. :param length: New pattern length in beats (e.g., 4.0 for a bar). Returns ``self`` for fluent chaining. .. py:method:: signal(name: str) -> float Read a conductor signal at the current bar. Shorthand for ``p.c.get(name, p.bar * 4)``. Returns 0.0 if no conductor is attached or the signal is not defined. .. py:method:: silence(beat: float = 0.0) -> PatternBuilder Sends an 'All Notes Off' (CC 123) and 'All Sound Off' (CC 120) message on the pattern's channel to immediately silence any ringing notes or drones. :param beat: The beat position (0.0 is the start). .. py:method:: snap_to_scale(key: str, mode: str = 'ionian', strength: float = 1.0, seed: Optional[int] = None, rng: Optional[random.Random] = None) -> PatternBuilder Snap all notes in the pattern to the nearest pitch in a scale. Useful after generative or sensor-driven pitch work (random walks, mapping data values to note numbers, etc.) to ensure every note lands on a musically valid scale degree. The snap is applied in place; notes already on a scale degree are left unchanged. When a note falls equidistant between two scale tones, the upward direction is preferred. :param key: Root note name (e.g. ``"C"``, ``"F#"``, ``"Bb"``). :param mode: Scale mode. Any key in :data:`subsequence.intervals.DIATONIC_MODE_MAP` is accepted: ``"ionian"`` (default), ``"dorian"``, ``"minor"``, ``"harmonic_minor"``, etc. :param strength: Probability that each note is snapped (0.0–1.0). At 1.0 (default), every note snaps to the scale. At 0.0, no notes are affected. Values in between create melodies that are mostly in key with occasional chromatic passing tones. Uses the pattern's seeded RNG for reproducibility. .. rubric:: Example .. code-block:: python @composition.pattern(channel=1, beats=4) def melody (p): for beat in range(16): pitch = 60 + random.randint(-5, 5) p.note(pitch, beat=beat * 0.25) p.snap_to_scale("G", "dorian", strength=0.8) .. py:method:: stretch(factor: float) -> PatternBuilder Stretch the pattern in time, scaling note positions and durations. ``stretch(2.0)`` makes everything twice as long (half speed) — what theorists call *augmentation*; ``stretch(0.5)`` squeezes the pattern into half the time (double speed) — *diminution*. Any positive factor works: ``stretch(2/3)`` compresses a dotted feel into straight time, for example. Notes whose start lands past the end of the pattern are dropped, and compression leaves the freed space empty — the pattern is not tiled to fill it. Durations scale without clipping, so a stretched note may ring past the pattern's end exactly like a legato note, and ``stretch(1.0)`` is a true no-op. Positions and durations truncate to the pulse grid (matching ``note()``'s beat-to-pulse truncation). :param factor: Time multiplier. Greater than 1.0 slows the pattern down, less than 1.0 speeds it up. Must be positive. .. py:method:: strum(chord_obj: Any, root: int, velocity: Union[int, Tuple[int, int]] = subsequence.constants.velocity.DEFAULT_CHORD_VELOCITY, sustain: bool = False, duration: float = 1.0, inversion: int = 0, count: Optional[int] = None, spacing: float = 0.05, direction: str = 'up', legato: Optional[float] = None, detached: Optional[float] = None, beat: float = 0.0) -> PatternBuilder Play a chord with a small time offset between each note (strum effect). Works exactly like ``chord()`` but staggers the notes instead of playing them simultaneously. The first note lands on ``beat`` (0 by default); subsequent notes are delayed by ``spacing`` beats each. :param chord_obj: The chord to play (usually the ``chord`` parameter passed to your pattern function). :param root: MIDI root note (e.g., 60 for Middle C). :param velocity: MIDI velocity (default 90), or a ``(low, high)`` tuple for a fresh random draw per strum note. :param sustain: If True, the notes last for the entire pattern duration. Mutually exclusive with ``legato`` and ``detached``. :param duration: Note duration in beats (default 1.0). Ignored when ``legato`` or ``detached`` is set, since those recalculate durations. :param inversion: Specific chord inversion (ignored if voice leading is on). :param count: Number of notes to play (cycles tones if higher than the chord's natural size). :param spacing: Time in beats between each note onset (default 0.05). :param direction: ``"up"`` for low-to-high, ``"down"`` for high-to-low. :param beat: Beat offset for the first note (default 0.0); the stagger is added on top. ``sustain``/``detached`` ring from the pattern length, not from ``beat`` — set ``duration`` explicitly when placing positioned strums. :param legato: If given, calls ``p.legato(ratio)`` after placing the chord, stretching each note to fill ``ratio`` of the gap to the next note. Mutually exclusive with ``sustain`` and ``detached``. :param detached: If given, every strum note rings with a uniform duration of ``pattern.length - detached - (count - 1) * spacing``. The last note ends exactly ``detached`` beats before the next cycle; earlier notes end proportionally sooner, so releases are staggered in the same shape as the placements (the hand lifts the way it landed). Polyphony-safe: guarantees nothing from this strum is still sounding when the next chord begins. Mutually exclusive with ``sustain`` and ``legato``. Example:: # Gentle upward strum with legato p.strum(chord, root=52, velocity=85, spacing=0.06, legato=0.95) # Fast downward strum p.strum(chord, root=52, direction="down", spacing=0.03) # Five-voice strum with a 0.25-beat safety gap before the # next chord — won't exhaust polyphony on a 5-voice synth. p.strum(chord, root=48, count=5, spacing=0.1, detached=0.25) .. py:method:: swing(percent: float = 57.0, grid: float = 0.25, strength: float = 1.0) -> PatternBuilder Apply swing feel to all notes in the pattern. A shortcut for ``p.groove(Groove.swing(percent, grid), strength)``. Swing is a groove where every other grid note is delayed - the simplest way to give a mechanical pattern a pushed, human feel. 50% is perfectly straight (no swing). 57% is the Ableton default (a gentle shuffle). 67% is classic triplet swing. :param percent: Swing amount as a percentage (50-75 is the useful range). 50 = straight, 57 = moderate shuffle, 67 ≈ triplet swing. :param grid: Grid size in beats (0.25 = 16th notes, 0.5 = 8th notes). :param strength: How much swing to apply (0.0-1.0). 0.0 = no effect, 1.0 = full swing at the given percent. Useful for dialling back the feel without changing the swing percentage. Example:: p.hit_steps("hh", range(16), velocity=80) p.swing(57) # gentle 16th-note shuffle p.swing(57, strength=0.5) # half-strength — subtler feel .. py:method:: transpose(semitones: int) -> PatternBuilder Shift all note pitches up or down. :param semitones: Positive for up, negative for down. .. py:method:: velocity_shape(low: int = subsequence.constants.velocity.VELOCITY_SHAPE_LOW, high: int = subsequence.constants.velocity.VELOCITY_SHAPE_HIGH) -> PatternBuilder Apply organic velocity variation to all notes in the pattern. Uses a van der Corput sequence to distribute velocities evenly across the specified range, which often sounds more 'human' than purely random velocity variation. :param low: Minimum velocity (default 64). :param high: Maximum velocity (default 127). .. py:property:: c :type: Optional[subsequence.conductor.Conductor] Alias for self.conductor. .. py:property:: grid :type: int Number of grid slots in this pattern (e.g. 16 for a 4-beat sixteenth-note pattern).