Subsystem

subsequence.motifs

The API reference for Motif, Phrase, motif, sentence, period, Degree, ChordTone, Approach, MotifEvent, and ControlEvent.

Motif

class Motif(
    events: typing.Tuple[MotifEvent, ...],
    length: float,
    controls: typing.Tuple[ControlEvent, ...] = (),
    fit: typing.Optional[float] = None,
)

An immutable musical figure: timed note events + control gestures + a length in beats.

Construct via the classmethods (degrees, notes, hits, steps, euclidean, the control-gesture constructors, or from_events) rather than positionally. length is explicit — a trailing rest is meaningful.

Members: accent, answer, cc, cc_ramp, controls, degrees, describe, empty, euclidean, events, fit, from_events, generate, hits, invert, join, length, notes, nrpn, nrpn_ramp, onsets, osc, osc_ramp, pitch_bend, pitch_bend_ramp, pitched, preset, quantize, reverse, rhythm, rotate, rpn, rpn_ramp, slice, stack, steps, stretch, then, transpose, vary, with_velocity

Motif.events

Motif.events: typing.Tuple[MotifEvent, ...]

Motif.length

Motif.length: float

Motif.controls

Motif.controls: typing.Tuple[ControlEvent, ...] = ()

Motif.fit

Motif.fit: typing.Optional[float] = None

Motif.empty

classmethod Motif.empty() -> Motif

The empty motif (zero events, zero length) — the identity for then.

Motif.from_events

classmethod Motif.from_events(
    events: typing.Iterable[MotifEvent],
    length: typing.Optional[float] = None,
    controls: typing.Iterable[ControlEvent] = (),
) -> Motif

Build a motif from explicit events (power use; length defaults to the next whole beat).

Motif.degrees

classmethod Motif.degrees(
    degrees: typing.List[typing.Union[int, Degree, None]],
    beats: typing.Optional[typing.List[float]] = None,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 1.0,
    probabilities: typing.Any = 1.0,
    length: typing.Optional[float] = None,
) -> Motif

A melody written as 1-based scale degrees, one per beat by default.

Elements are ints (1 = tonic, 8 = tonic an octave up), None for a rest (the beat slot still advances), or Degree for octave/ chromatic detail. Resolved against key + scale at placement. Durations default to a full beat (each note holds its slot).

Motif.notes

classmethod Motif.notes(
    notes: typing.List[typing.Union[int, None]],
    beats: typing.Optional[typing.List[float]] = None,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 1.0,
    probabilities: typing.Any = 1.0,
    length: typing.Optional[float] = None,
) -> Motif

A melody written as absolute MIDI note numbers (60 = middle C); None = rest.

Motif.hits

classmethod Motif.hits(
    pitch: typing.Union[int, str],
    beats: typing.List[float],
    length: typing.Optional[float] = None,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 0.1,
    probabilities: typing.Any = 1.0,
) -> Motif

One pitch (usually a drum name) at a list of beat positions — the hit() convention.

Motif.steps

classmethod Motif.steps(
    steps: typing.List[int],
    pitches: typing.Any,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 0.1,
    probabilities: typing.Any = 1.0,
    step_duration: float = 0.25,
    length: typing.Optional[float] = None,
) -> Motif

Grid placement — the sequence() convention: steps are 0-based grid indices (sixteenths by default), pitches a scalar or parallel list of MIDI ints or drum names.

Motif.euclidean

classmethod Motif.euclidean(
    pulses: int,
    steps: int,
    pitch: typing.Union[int, str],
    length: float = 4.0,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 0.1,
    probabilities: typing.Any = 1.0,
) -> Motif

A euclidean rhythm as a value: pulses spread evenly across steps over length beats.

Motif.preset

classmethod Motif.preset(
    name: str,
    pitch: typing.Optional[typing.Union[int, str]] = None,
    length: float = 4.0,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 0.1,
    probabilities: typing.Any = 1.0,
) -> Motif

A named world-rhythm timeline as a value — Motif.preset("son_clave_3_2").

Looks a curated timeline up in the world-rhythm table (clave family, West-African bell patterns, tresillo/cinquillo, samba) and lays its onsets across length beats. Onset positions are exact pulse indices from Toussaint's "The Geometry of Musical Rhythm"; each preset declares its own grid (16 for the clave/4-4 timelines, 12 for the bell patterns) and a default drum voice.

Parameters

Returns

Raises

Example

clave = subsequence.Motif.preset("son_clave_3_2")              # GM "claves"
bell  = subsequence.Motif.preset("bembe", pitch="cowbell")     # 12-pulse

Motif.cc

classmethod Motif.cc(
    control: typing.Union[int, str],
    values: typing.List[int],
    beats: typing.List[float],
    length: typing.Optional[float] = None,
    probabilities: typing.Any = 1.0,
) -> Motif

Discrete CC writes at beat positions — mirrors p.cc(); names resolve at placement.

Motif.cc_ramp

classmethod Motif.cc_ramp(
    control: typing.Union[int, str],
    start: int,
    end: int,
    beat_start: float = 0.0,
    beat_end: typing.Optional[float] = None,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    length: typing.Optional[float] = None,
    probability: float = 1.0,
) -> Motif

A CC value swept startend over a beat range — mirrors p.cc_ramp().

Motif.pitch_bend

classmethod Motif.pitch_bend(
    values: typing.List[float],
    beats: typing.List[float],
    length: typing.Optional[float] = None,
    probabilities: typing.Any = 1.0,
) -> Motif

Discrete pitch-bend writes (-1.0 to 1.0) at beat positions — mirrors p.pitch_bend().

Motif.pitch_bend_ramp

classmethod Motif.pitch_bend_ramp(
    start: float,
    end: float,
    beat_start: float = 0.0,
    beat_end: typing.Optional[float] = None,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    length: typing.Optional[float] = None,
    probability: float = 1.0,
) -> Motif

Pitch bend swept startend (-1.0 to 1.0) over a beat range — mirrors p.pitch_bend_ramp().

Motif.nrpn

classmethod Motif.nrpn(
    parameter: typing.Union[int, str],
    values: typing.List[int],
    beats: typing.List[float],
    fine: bool = False,
    null_reset: bool = True,
    length: typing.Optional[float] = None,
    probabilities: typing.Any = 1.0,
) -> Motif

Discrete NRPN parameter writes at beat positions — mirrors p.nrpn().

Motif.nrpn_ramp

classmethod Motif.nrpn_ramp(
    parameter: typing.Union[int, str],
    start: int,
    end: int,
    beat_start: float = 0.0,
    beat_end: typing.Optional[float] = None,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    fine: bool = True,
    null_reset: bool = True,
    length: typing.Optional[float] = None,
    probability: float = 1.0,
) -> Motif

An NRPN value swept over a beat range — mirrors p.nrpn_ramp().

Motif.rpn

classmethod Motif.rpn(
    parameter: typing.Union[int, subsequence.declarations.RpnParameter],
    values: typing.List[int],
    beats: typing.List[float],
    fine: bool = False,
    null_reset: bool = True,
    length: typing.Optional[float] = None,
    probabilities: typing.Any = 1.0,
) -> Motif

Discrete RPN parameter writes at beat positions — mirrors p.rpn().

Motif.rpn_ramp

classmethod Motif.rpn_ramp(
    parameter: typing.Union[int, subsequence.declarations.RpnParameter],
    start: int,
    end: int,
    beat_start: float = 0.0,
    beat_end: typing.Optional[float] = None,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    fine: bool = True,
    null_reset: bool = True,
    length: typing.Optional[float] = None,
    probability: float = 1.0,
) -> Motif

An RPN value swept over a beat range — mirrors p.rpn_ramp().

Motif.osc

classmethod Motif.osc(
    address: str,
    values: typing.List[float],
    beats: typing.List[float],
    length: typing.Optional[float] = None,
    probabilities: typing.Any = 1.0,
) -> Motif

Discrete OSC float sends at beat positions — mirrors p.osc().

Motif.osc_ramp

classmethod Motif.osc_ramp(
    address: str,
    start: float,
    end: float,
    beat_start: float = 0.0,
    beat_end: typing.Optional[float] = None,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    length: typing.Optional[float] = None,
    probability: float = 1.0,
) -> Motif

An OSC float swept over a beat range — mirrors p.osc_ramp().

Motif.then

Motif.then(other: Motif) -> Motif

Closed sequential concat: glue other after this motif into ONE longer motif.

Motif.join

classmethod Motif.join(motifs: typing.Iterable[Motif]) -> Motif

Fold a list of motifs into one with then (empty list → Motif.empty()).

Motif.generate

classmethod Motif.generate(
    rhythm: typing.Any,
    length: typing.Optional[float] = None,
    scale: typing.Optional[typing.Union[str, typing.Sequence[int]]] = None,
    contour: typing.Optional[str] = None,
    end_on: typing.Optional[typing.Union[int, Degree]] = None,
    cadence: typing.Optional[str] = None,
    pins: typing.Optional[typing.Dict[int, typing.Union[int, Degree]]] = None,
    max_pitches: typing.Optional[int] = None,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 0.25,
    seed: typing.Optional[int] = None,
    rng: typing.Optional[random.Random] = None,
    state: typing.Optional[typing.Any] = None,
    nir_strength: float = 0.5,
    pitch_diversity: float = 0.6,
    tessitura_strength: float = 0.6,
) -> Motif

Generate a melodic motif — rhythm first, pitches walked, a value out.

The melody engine emitting a value: you give the rhythm (an onset list in beats, or another motif whose rhythm to borrow — cross-pattern rhythm reuse is shared values); the engine walks pitches over it through the soft scoring factors (NIR expectation, contour envelope, tessitura regression, diversity), honouring any pins.

The result emits scale degrees (resolved at placement against the composition key/scale), so a generated hook transposes, varies, and develops like a hand-written one. scale= constrains candidate choice only: a name or interval list masks which pitches the walk may use, spelled relative to its best-fit reference (major or minor) — bind it in a composition whose scale matches that family and resolution is exact. An explicit MIDI pitch pool (a list of note numbers) switches to absolute output (the sieve/atonal path).

Parameters

Example

hook = subsequence.Motif.generate(
        rhythm=[0, 1, 1.5, 1.75, 2.5], scale="minor_pentatonic",
        contour="arch", end_on=1, seed=7,
)

Motif.stack

Motif.stack(other: typing.Union[Motif, Phrase]) -> Motif

Parallel merge (the spelled form of &): event union, length = max.

No implicit tiling — a short gesture stacked under a long figure plays once. Phrase operands flatten first.

Motif.slice

Motif.slice(start: float, end: float) -> Motif

A window onto the motif, on its own authority: events starting outside are dropped; durations and ramp spans truncate at the cut (a truncated ramp ends at its interpolated cut value). Beats shift so the window starts at 0.

Motif.reverse

Motif.reverse() -> Motif

Mirror the figure in time; ramps swap direction (a rising sweep falls).

Motif.rotate

Motif.rotate(beats: float) -> Motif

Shift every onset by beats, wrapping modulo the length (spans ride along).

Motif.stretch

Motif.stretch(factor: float) -> Motif

Scale time by factor (2.0 = half-time feel): beats, durations, spans, and length.

Motif.quantize

Motif.quantize(grid: float) -> Motif

Snap note onsets to the nearest multiple of grid beats (control gestures untouched).

An onset exactly midway between grid lines snaps LATER (round half up) — every midpoint moves the same way, the predictable behaviour for a musician. (Python's own round() is half-to-even, which made exact midpoints snap in alternating directions.)

Motif.accent

Motif.accent(beat: float, amount: int = 20) -> Motif

Add amount velocity to every note at the given beat position (0-based beats).

Motif.with_velocity

Motif.with_velocity(
    velocity: subsequence.declarations.VelocityValue,
) -> Motif

Replace every note's velocity (an int, or a (low, high) random range).

Motif.vary

Motif.vary(
    notes: int = 1,
    position: str = 'end',
    seed: typing.Optional[int] = None,
    rng: typing.Optional[random.Random] = None,
    keep_contour: bool = False,
) -> Motif

Replace a few pitches, preserving the rhythm — the smallest variation.

Rhythm, velocities, durations, rests, and control gestures are untouched; only the chosen notes' pitches move (by a small melodic nudge: scale steps for degrees, semitones for MIDI ints).

Parameters

Example

answer = call.vary(notes=1, seed=4)     # same figure, new tail note

Motif.answer

Motif.answer(to: typing.Union[int, Degree] = 1) -> Motif

Call → response: re-aim the tail to a stable degree.

The classic consequent move — the figure repeats but its last pitched note lands home (degree 1 by default; pass to=5 for a half-close, or a full Degree for register control). Everything else — rhythm, the other pitches, velocities, controls — is untouched.

Degree content only: absolute MIDI has no degrees to re-aim (build the call with motif([...])), and drums raise.

Motif.pitched

Motif.pitched(spec: PitchSpec) -> Motif

Replace every pitch with one spec — a kick rhythm becomes a bass line.

"root" / "third" / "fifth" / "seventh" become chord tones; any other string is a drum name; ints are MIDI; Degree / ChordTone / Approach pass through.

Motif.rhythm

Motif.rhythm() -> Motif

Strip pitches (and control gestures): a reusable rhythmic skeleton.

Timing, velocities, durations, and probabilities survive; re-pitch with pitched before placement (placing a skeleton raises).

Motif.onsets

Motif.onsets() -> typing.List[float]

The note onset beats, in order — ready for rhythm-first generation.

Motif.transpose

Motif.transpose(
    steps: typing.Optional[int] = None,
    semitones: typing.Optional[int] = None,
) -> Motif

Transpose pitched content; the keyword names the unit.

steps= moves scale degrees diatonically (the sequencing move) and raises on absolute-MIDI or drum content; semitones= is the literal chromatic form for MIDI ints and degrees. Drum motifs raise on both — a transposed drum name is a different instrument, not a transposition — and a captured drum raises too, because its number still remembers which instrument it came from.

Motif.invert

Motif.invert(pivot: typing.Optional[int] = None) -> Motif

Mirror pitches around a pivot: MIDI content around a MIDI pivot, degree content around a degree pivot (default: the first note's pitch). Drum motifs raise, captured ones included.

Motif.describe

Motif.describe() -> str

A readable one-line summary: length, notes (pitch@beat), and control gestures.

Phrase

class Phrase(
    segments: typing.Iterable[Motif],
    recipe: typing.Optional[_PhraseRecipe] = None,
)

A sequence of Motifs with segmentation preserved.

Segmentation is the unit of editing — it is what development and per-region regeneration operate on. flatten() erases it into one long Motif. Length is the sum of segment lengths.

A phrase made by develop carries its recipe, so reroll can regenerate a region; transforms and hand edits return recipe-less phrases (their notes no longer come from the recipe, so there is nothing honest to regenerate from).

Coerce any iterable of Motifs.

Members: describe, develop, flatten, invert, length, pitched, quantize, recipe, replace, reroll, reverse, rhythm, rotate, segments, slice, stack, stretch, transpose, with_velocity

Phrase.segments

Phrase.segments: typing.Tuple[Motif, ...]

Phrase.recipe

Phrase.recipe: typing.Optional[_PhraseRecipe]

Phrase.length

property Phrase.length: float

Total length in beats (sum of segment lengths).

Phrase.develop

classmethod Phrase.develop(
    motif: Motif,
    bars: int = 8,
    plan: typing.Optional[typing.Union[typing.Sequence[str], str]] = None,
    seed: typing.Optional[int] = None,
    beats_per_bar: float = 4.0,
) -> Phrase

Grow a motif into a phrase by a plan — the phrase generator.

plan follows the standard form. The literal form is a list of unit labelsplan=["a", "a", "a", "b"], equivalently ["a"] * 3 + ["b"]: the first label is the given motif, each new label is a generated contrast unit (the source's rhythm, freshly re-pitched), a repeated label is a restatement, and bars spreads evenly across the units. A bare string is a recipe name from the curated table — plan="call_response" (call, answer, call, varied answer) — reserved for plans whose semantics exceed a label skeleton. A letter string is not a plan: a sequence of labels is a sequence, so it is a list.

The result carries its recipe, so reroll can regenerate a region later.

Parameters

Example

call = subsequence.motif([5, 6, 5, 3, None, 1, 2, 3])
lead = subsequence.Phrase.develop(call, bars=8, plan="call_response", seed=11)

Phrase.reroll

Phrase.reroll(
    bar: typing.Optional[int] = None,
    bars: typing.Optional[typing.Sequence[int]] = None,
    seed: typing.Optional[int] = None,
) -> Phrase

Regenerate only the named bars — rhythm and boundary pitches kept.

Within each named bar, the first and last pitched notes stay (the boundary pins) and the interior pitches re-roll from a fresh per-bar stream salted by seed= (an unseeded call draws a fresh salt, so each call genuinely differs); onsets, durations, velocities, rests, drums, and control gestures are untouched. Segmentation and the recipe survive, so rerolls compose.

Only a phrase that carries a recipe can reroll — a hand-written or transformed phrase raises loudly (its notes no longer come from a generator, so regenerating them would invent music).

Parameters

Example

lead = lead.reroll(bar=7, seed=4)    # only bar 7; rhythm + boundaries kept

Phrase.flatten

Phrase.flatten() -> Motif

Erase segmentation: one long Motif (the monoid homomorphism onto then).

Phrase.stack

Phrase.stack(other: typing.Union[Motif, Phrase]) -> Motif

The spelled form of & — flattens, then merges.

Phrase.slice

Phrase.slice(start: float, end: float) -> Phrase

A window; re-segments at the cut points (partial segments are sliced).

Phrase.replace

Phrase.replace(position: int, motif: Motif) -> Phrase

Replace the segment at a 1-based position (musicians count from one).

Phrase.reverse

Phrase.reverse() -> Phrase

Reverse the whole timeline: segments reverse order AND each reverses internally.

Phrase.rotate

Phrase.rotate(beats: float) -> Phrase

Rotate the whole timeline modulo the total length, then re-segment at the original boundaries.

Phrase.stretch

Phrase.stretch(factor: float) -> Phrase

Scale time in every segment (lengths scale with them).

Phrase.quantize

Phrase.quantize(grid: float) -> Phrase

Snap note onsets segment-wise.

Phrase.with_velocity

Phrase.with_velocity(
    velocity: subsequence.declarations.VelocityValue,
) -> Phrase

Replace every note's velocity, segment-wise.

Phrase.pitched

Phrase.pitched(spec: PitchSpec) -> Phrase

Replace every pitch, segment-wise.

Phrase.rhythm

Phrase.rhythm() -> Phrase

Strip pitches segment-wise: a phrase-shaped skeleton.

Phrase.transpose

Phrase.transpose(
    steps: typing.Optional[int] = None,
    semitones: typing.Optional[int] = None,
) -> Phrase

Transpose every segment (see Motif.transpose).

Phrase.invert

Phrase.invert(pivot: typing.Optional[int] = None) -> Phrase

Mirror pitches in every segment around one pivot (see Motif.invert).

Phrase.describe

Phrase.describe() -> str

A readable summary: total length and each segment on its own line.

motif

motif(
    degrees: typing.List[typing.Union[int, Degree, None]],
    beats: typing.Optional[typing.List[float]] = None,
    velocities: typing.Any = _DEFAULT_VELOCITY,
    durations: typing.Any = 1.0,
    probabilities: typing.Any = 1.0,
    length: typing.Optional[float] = None,
) -> Motif

The lowercase shortcut: a melody as 1-based scale degrees.

subsequence.motif([5, 6, 5, 3]) is Motif.degrees([5, 6, 5, 3]) — relative pitch is the primary form. For absolute MIDI note numbers use Motif.notes([64, 65, 64, 60]); implausibly large ints here raise so a pasted MIDI list fails loud instead of squealing octaves up.

sentence

sentence(
    motif: Motif,
    bars: int = 8,
    cadence: str = 'strong',
    seed: typing.Optional[int] = None,
    beats_per_bar: float = 4.0,
) -> Phrase

The classical sentence, as a thin combinator — idea, idea, drive, close.

Four units: the basic idea stated twice (the presentation), a generated contrast unit (the continuation — the source's rhythm, freshly re-pitched), and a second contrast unit whose tail lands on the cadence's close degree (the cadential close). An 8-bar sentence from a 2-bar idea is the textbook proportion; a shorter idea tiles up to the unit size first.

The melodic side of a cadence only — pair it with the harmonic side (prog.cadence(), Progression.generate(cadence=), or request_cadence()) and the two arrive together.

Parameters

Example

idea = subsequence.motif([5, 6, 5, 3, None, 1, 2, 3])
verse_lead = subsequence.sentence(idea, bars=8, cadence="open", seed=11)

period

period(
    antecedent: typing.Union[Motif, Phrase],
    cadence: str = 'strong',
    beats_per_bar: float = 4.0,
) -> Phrase

The classical period, as a thin combinator — question, then answer.

Two halves: the antecedent with its tail re-aimed to the open half-close (degree 5 — the question), then the same material restated with its tail on the cadence's close degree (the answer). The two halves differ exactly at their closes — the open/closed contrast is the period.

Deterministic: no notes are generated, only the two tail notes re-aim (so there is no seed). Vary the consequent yourself for a looser restatement: period(a).reroll(bar=7, seed=4).

Parameters

Example

idea = subsequence.motif([3, 4, 5, 1, None, 6, 5, 4], length=8)
lead = subsequence.period(idea)        # 16 beats: half-close, then home

Degree

class Degree(step: int, octave: int = 0, chroma: int = 0)

A scale degree — 1-based, resolved against key + scale at placement.

Degree 1 is the tonic; 8 is the tonic an octave up (steps may exceed the scale length and resolve into higher octaves). octave shifts whole octaves; chroma is a chromatic offset in semitones (+1 = sharpened).

Members: chroma, octave, step

Degree.step

Degree.step: int

Degree.octave

Degree.octave: int = 0

Degree.chroma

Degree.chroma: int = 0

ChordTone

class ChordTone(index_or_name: typing.Union[int, str], octave: int = 0)

An index into the current chord's tones — 1-based, resolved at placement.

Accepts an int (1 = root, 2 = third, ...) or one of the names "root" / "third" / "fifth" / "seventh". octave shifts whole octaves.

Normalize a tone name to its 1-based index.

Members: index, octave

ChordTone.index

ChordTone.index: int

ChordTone.octave

ChordTone.octave: int = 0

Approach

class Approach(target: typing.Union[int, Degree, ChordTone])

A half-step approach into a target pitch at the next chord boundary.

Resolves at placement, one semitone below its target (the leading-tone approach); a ChordTone target reads the NEXT chord through the harmony window, so the approach lands as the harmony arrives.

Approach.target

Approach.target: typing.Union[int, Degree, ChordTone]

MotifEvent

class MotifEvent(
    beat: float,
    pitch: PitchSpec,
    velocity: subsequence.declarations.VelocityValue = _DEFAULT_VELOCITY,
    duration: float = 0.25,
    probability: float = 1.0,
    origin: typing.Optional[str] = None,
)

One timed note event inside a Motif.

pitch is a specification: an absolute MIDI int, a drum name string, a Degree, ChordTone, or Approach — or None for a pitch-stripped skeleton event (see Motif.rhythm), which must be re-pitched via Motif.pitched before placement. velocity is an int or a (low, high) random-range tuple.

origin names the drum a pitch was resolved from. Only capture sets it — capture reads notes back as absolute MIDI, so "kick" arrives here as 36 — and it is what lets the pitch-moving methods go on refusing a drum they can no longer see.

Members: beat, duration, origin, pitch, probability, velocity

MotifEvent.beat

MotifEvent.beat: float

MotifEvent.pitch

MotifEvent.pitch: PitchSpec

MotifEvent.velocity

MotifEvent.velocity: subsequence.declarations.VelocityValue = _DEFAULT_VELOCITY

MotifEvent.duration

MotifEvent.duration: float = 0.25

MotifEvent.probability

MotifEvent.probability: float = 1.0

MotifEvent.origin

MotifEvent.origin: typing.Optional[str] = None

ControlEvent

class ControlEvent(
    beat: float,
    signal: ControlSignal,
    start: float,
    end: typing.Optional[float] = None,
    span: float = 0.0,
    shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear',
    probability: float = 1.0,
)

One timed control gesture inside a Motif: a discrete write or a shaped ramp.

A discrete write has end=None and span=0.0; a ramp interpolates startend over span beats through the easing shape. Pulse density (resolution=) is deliberately not stored here — beats and shapes are music; MIDI traffic density is set at the placement call.

Members: beat, end, probability, shape, signal, span, start

ControlEvent.beat

ControlEvent.beat: float

ControlEvent.signal

ControlEvent.signal: ControlSignal

ControlEvent.start

ControlEvent.start: float

ControlEvent.end

ControlEvent.end: typing.Optional[float] = None

ControlEvent.span

ControlEvent.span: float = 0.0

ControlEvent.shape

ControlEvent.shape: typing.Union[subsequence.declarations.EasingCurve, subsequence.easing.EasingFn] = 'linear'

ControlEvent.probability

ControlEvent.probability: float = 1.0