subsequence.pattern_midi¶
Mixin class providing MIDI and OSC control-message methods for PatternBuilder.
This module is not intended to be used directly. PatternMidiMixin
is inherited by PatternBuilder in pattern_builder.py.
Module Contents¶
- class subsequence.pattern_midi.PatternMidiMixin[source]¶
MIDI control, OSC, and note-correlated pitch bend methods for PatternBuilder.
All methods here operate on
self._pattern(aPatterninstance), which is set byPatternBuilder.__init__.- bend(note: int, amount: float, start: float = 0.0, end: float = 1.0, shape: str | subsequence.easing.EasingFn = 'linear', resolution: int = 1) subsequence.pattern_builder.PatternBuilder[source]¶
Bend a specific note by index.
Generates a pitch bend ramp that covers a fraction of the target note’s duration, then resets to 0.0 at the next note’s onset. Call this after
legato()/detached()/duration()so that note durations are final.- Parameters:
note – Note index (0 = first, -1 = last, etc.).
amount – Target bend normalised to -1.0..1.0 (positive = up). With a standard ±2-semitone pitch wheel range, 0.5 = 1 semitone.
start – Fraction of the note’s duration at which the ramp begins (0.0 = note onset, default).
end – Fraction of the note’s duration at which the ramp ends (1.0 = note end, default).
shape – Easing curve — a name string (e.g.
"ease_in") or any callable mapping [0, 1] → [0, 1]. Defaults to"linear".resolution – Pulses between pitch bend messages.
- Raises:
IndexError – If note is out of range for the current pattern.
Example
p.sequence(steps=[0, 4, 8, 12], pitches=midi_notes.E1) p.legato(0.95) # Bend the last note up one semitone (with ±2 st range), easing in p.bend(note=-1, amount=0.5, shape="ease_in") # Bend the second note down, starting halfway through p.bend(note=1, amount=-0.3, start=0.5)
- cc(control: int | str, value: int, beat: float = 0.0) subsequence.pattern_builder.PatternBuilder[source]¶
Send a single CC message at a beat position.
- Parameters:
control – MIDI CC number (0–127), or a string name resolved via the pattern’s
cc_name_map.value – CC value (0–127); out-of-range values are clamped.
beat – Beat position within the pattern.
- cc_ramp(control: int | str, start: int, end: int, beat_start: float = 0.0, beat_end: float | None = None, resolution: int = 1, shape: str | subsequence.easing.EasingFn = 'linear') subsequence.pattern_builder.PatternBuilder[source]¶
Interpolate a CC value over a beat range.
- Parameters:
control – MIDI CC number (0–127), or a string name resolved via the pattern’s
cc_name_map.start – Starting CC value (0–127).
end – Ending CC value (0–127).
beat_start – Beat position to begin the ramp.
beat_end – Beat position to end the ramp. Defaults to pattern length.
resolution – Pulses between CC messages (1 = every pulse, ~20ms at 120 BPM). Higher values (e.g. 2 or 4) reduce MIDI traffic density but may sound stepped at slow tempos.
shape – Easing curve — a name string (e.g.
"exponential") or any callable that maps [0, 1] → [0, 1]. Defaults to"linear". Seesubsequence.easingfor available shapes.
- nrpn(parameter: int | str, value: int, beat: float = 0.0, fine: bool = False, null_reset: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
Send a single NRPN parameter write at a beat position.
NRPN (Non-Registered Parameter Number) addresses synth-specific parameters that don’t fit into the 128 standard CC slots — Sequential, Korg, Roland, Elektron and others use it heavily for filter cutoff, envelope amounts, oscillator detune, and similar deep parameters. Many such parameters need values beyond 0–127 (e.g. 0–1023, 0–254); set
fine=Truefor full 14-bit precision.Emitted on the pattern’s MIDI channel. To target a different channel (e.g. a per-channel RPN config), define a separate pattern on that channel or use
composition.trigger(channel=…)for a one-shot.- Parameters:
parameter – 14-bit NRPN parameter number (0–16383), or a string resolved via the pattern’s
nrpn_name_map.value – Parameter value. 0–127 if
fine=False; 0–16383 iffine=True.beat – Beat position within the pattern.
fine – If True, send 14-bit value via Data Entry MSB+LSB (CC 6 + CC 38). If False (default), send only Data Entry MSB — sufficient for the common 0–127 range.
null_reset – If True (default), follow with the RPN null sentinel to deselect the active parameter and prevent stray later CC 6 / 38 messages from hitting it.
Example
# Sequential Take 5 fine-tune (14-bit, range 0–1400) p.nrpn(9, 700, fine=True) # Roland JV-1080 reverb level (7-bit) p.nrpn(0x0140, 80)
- nrpn_ramp(parameter: int | str, start: int, end: int, beat_start: float = 0.0, beat_end: float | None = None, resolution: int = 4, shape: str | subsequence.easing.EasingFn = 'linear', fine: bool = True, null_reset: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
Interpolate an NRPN value over a beat range.
The parameter is selected once at
beat_start; subsequent steps emit only Data Entry messages. Synths track the most recently selected NRPN per the spec, so re-selecting per step would just waste bandwidth. Ifnull_reset=Truethe RPN null sentinel is appended once atbeat_end.Mid-ramp parameter persistence: between
beat_startandbeat_endthe synth still has this NRPN selected. Avoid issuingp.cc(6, …)orp.cc(38, …)on the same channel during the ramp window — they would land on the ramped parameter rather than acting as plain data-entry CCs.Bandwidth note: with
fine=True(default) every step emits two CCs. Defaultresolution=4is one update every four pulses (~83 ms at 120 BPM, where one pulse is ~21 ms), which keeps the bus lightly loaded. Increaseresolution(e.g.8) on slow DIN-MIDI links if you hear other messages getting delayed.Emitted on the pattern’s MIDI channel.
- Parameters:
parameter – 14-bit NRPN parameter number, or a string resolved via the pattern’s
nrpn_name_map.start – Starting value (0–16383 when
fine=True, 0–127 when False).end – Ending value.
beat_start – Beat position to begin the ramp.
beat_end – Beat position to end the ramp. Defaults to pattern length.
resolution – Pulses between Data Entry messages (default 4).
shape – Easing curve — string name or callable [0, 1] → [0, 1].
fine – If True (default), use full 14-bit Data Entry MSB+LSB.
null_reset – If True (default), append the null sentinel at the end of the ramp (not per step).
- osc(address: str, *args: Any, beat: float = 0.0) subsequence.pattern_builder.PatternBuilder[source]¶
Send an OSC message at a beat position.
Requires
composition.osc()to be called beforecomposition.play(). If no OSC server is configured the event is silently dropped.- Parameters:
address – OSC address path (e.g.
"/mixer/fader/1").*args – OSC arguments — float, int, str, or bytes.
beat – Beat position within the pattern (default 0.0).
Example
# Enable a chorus effect at beat 2 p.osc("/fx/chorus/enable", 1, beat=2.0) # Set a mixer pan value immediately p.osc("/mixer/pan/1", -0.5)
- osc_ramp(address: str, start: float, end: float, beat_start: float = 0.0, beat_end: float | None = None, resolution: int = 4, shape: str | subsequence.easing.EasingFn = 'linear') subsequence.pattern_builder.PatternBuilder[source]¶
Interpolate an OSC float value over a beat range.
Generates one OSC message per
resolutionpulses, sending the interpolated value toaddressat each step. Useful for smoothly automating mixer faders, effect parameters, and other continuous controls on a remote machine.Requires
composition.osc()to be called beforecomposition.play(). If no OSC server is configured the events are silently dropped.- Parameters:
address – OSC address path (e.g.
"/mixer/fader/1").start – Starting float value.
end – Ending float value.
beat_start – Beat position to begin the ramp (default 0.0).
beat_end – Beat position to end the ramp. Defaults to pattern length.
resolution – Pulses between OSC messages (default 4 — approximately 6 messages per beat at 120 BPM, which is smooth for fader automation while keeping UDP traffic light). Use
resolution=1for pulse-level precision.shape – Easing curve — a name string (e.g.
"ease_in") or any callable that maps [0, 1] → [0, 1]. Defaults to"linear". Seesubsequence.easingfor available shapes.
Example
# Fade a mixer fader up over 4 beats p.osc_ramp("/mixer/fader/1", start=0.0, end=1.0) # Ease in a reverb send over the last 2 beats p.osc_ramp("/fx/reverb/wet", 0.0, 0.8, beat_start=2, beat_end=4, shape="ease_in")
- pitch_bend(value: float, beat: float = 0.0) subsequence.pattern_builder.PatternBuilder[source]¶
Send a single pitch bend message at a beat position.
- Parameters:
value – Pitch bend amount, normalised from -1.0 to 1.0.
beat – Beat position within the pattern.
- pitch_bend_ramp(start: float, end: float, beat_start: float = 0.0, beat_end: float | None = None, resolution: int = 1, shape: str | subsequence.easing.EasingFn = 'linear') subsequence.pattern_builder.PatternBuilder[source]¶
Interpolate pitch bend over a beat range.
- Parameters:
start – Starting pitch bend (-1.0 to 1.0).
end – Ending pitch bend (-1.0 to 1.0).
beat_start – Beat position to begin the ramp.
beat_end – Beat position to end the ramp. Defaults to pattern length.
resolution – Pulses between pitch bend messages (1 = every pulse). Higher values (e.g. 2 or 4) reduce MIDI traffic density but may sound stepped at slow tempos.
shape – Easing curve — a name string (e.g.
"ease_out") or any callable that maps [0, 1] → [0, 1]. Defaults to"linear". Seesubsequence.easingfor available shapes.
- portamento(time: float = 0.15, shape: str | subsequence.easing.EasingFn = 'linear', resolution: int = 1, bend_range: float | None = 2.0, wrap: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
Glide between all consecutive notes using pitch bend.
Generates a pitch bend ramp in the tail of each note, bending toward the next note’s pitch, then resets at the next note’s onset. Call this after
legato()/detached()/duration()so that note durations are final.Most effective on mono instruments where pitch bend is per-channel.
- Parameters:
time – Fraction of each note’s duration used for the glide (default 0.15 — last 15% of the note).
shape – Easing curve. Defaults to
"linear".resolution – Pulses between pitch bend messages.
bend_range – Instrument’s pitch wheel range in semitones (default 2.0 — standard ±2 st). Pairs with intervals larger than this value are skipped. Pass
Noneto disable range checking and always generate the bend (large intervals are clamped to ±1.0).wrap – If
True(default), glide from the last note toward the first note of the next cycle.
Example
p.sequence(steps=[0, 4, 8, 12], pitches=[40, 42, 40, 43]) p.legato(0.95) # Gentle glide across all note transitions p.portamento(time=0.15, shape="ease_in_out") # Wide bend range (synth set to ±12 semitones) p.portamento(time=0.2, bend_range=12) # No range limit — bend as far as MIDI allows p.portamento(time=0.1, bend_range=None)
- program_change(program: int, beat: float = 0.0, bank_msb: int | None = None, bank_lsb: int | None = None) subsequence.pattern_builder.PatternBuilder[source]¶
Send a Program Change message, optionally preceded by bank select.
Switches the instrument patch on this pattern’s MIDI channel. Program numbers follow the General MIDI numbering (0–127, where e.g. 0 = Acoustic Grand Piano, 40 = Violin, 33 = Electric Bass).
To select a patch in a specific bank, provide
bank_msband/orbank_lsb. The bank select CC messages (CC 0 for MSB, CC 32 for LSB) are sent at the same beat position immediately before the program change, in the order the synthesizer expects.- Parameters:
program – Program (patch) number (0–127).
beat – Beat position within the pattern (default 0.0).
bank_msb – Bank select coarse (CC 0), 0–127.
None= omit.bank_lsb – Bank select fine (CC 32), 0–127.
None= omit.
Example
@composition.pattern(channel=1, beats=4) def strings (p): # GM — no bank needed p.program_change(48) # Roland JV-1080 bank 1, patch 48 p.program_change(48, bank_msb=81, bank_lsb=0) # Change patch only at the first bar of each section if p.section.bar == 0: p.program_change(48, bank_msb=1)
- rpn(parameter: int | str, value: int, beat: float = 0.0, fine: bool = False, null_reset: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
Send a single RPN parameter write at a beat position.
RPN (Registered Parameter Number) addresses the small standardised set of parameters defined by the MIDI specification — pitch bend range, master tuning, modulation depth — supported by virtually any MIDI synth. String names resolve via
pymididefs.rpn.RPN_MAPout of the box, no map needed.Standard RPN names:
pitch_bend_sensitivity,channel_fine_tuning,channel_coarse_tuning,tuning_program_select,tuning_bank_select,modulation_depth_range.Emitted on the pattern’s MIDI channel.
- Parameters:
parameter – 14-bit RPN parameter number (0–16383), or one of the standard string names above.
value – Parameter value. 0–127 if
fine=False; 0–16383 iffine=True. Pitch bend sensitivity uses MSB = semitones and LSB = cents, so setfine=Truefor sub-semitone control.beat – Beat position within the pattern.
fine – If True, send 14-bit value via Data Entry MSB+LSB.
null_reset – If True (default), follow with the RPN null sentinel.
Example
# Set pitch bend range to ±12 semitones p.rpn("pitch_bend_sensitivity", 12) # 4 semitones plus 50 cents p.rpn("pitch_bend_sensitivity", 4 * 128 + 50, fine=True)
- rpn_ramp(parameter: int | str, start: int, end: int, beat_start: float = 0.0, beat_end: float | None = None, resolution: int = 4, shape: str | subsequence.easing.EasingFn = 'linear', fine: bool = True, null_reset: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
Interpolate an RPN value over a beat range.
Identical to
nrpn_ramp()but uses CC 101 / 100 for parameter selection. String names resolve viapymididefs.rpn.RPN_MAP. The same mid-ramp persistence note applies: avoid plainp.cc(6, …)on this channel during the ramp window.
- slide(notes: List[int] | None = None, steps: List[int] | None = None, time: float = 0.15, shape: str | subsequence.easing.EasingFn = 'linear', resolution: int = 1, bend_range: float | None = 2.0, wrap: bool = True, extend: bool = True) subsequence.pattern_builder.PatternBuilder[source]¶
TB-303-style selective slide into specific notes.
Like
portamento()but only applies to flagged destination notes. Specify target notes by index (notes=[1, 3]) or by step grid position (steps=[4, 12]). Ifextend=True(default) the preceding note’s duration is extended to meet the slide target, matching the 303’s behaviour where slide notes do not retrigger.Call this after
legato()/detached()/duration()so that note durations are final.- Parameters:
notes – List of note indices to slide into (0 = first). Supports negative indexing. Mutually exclusive with steps.
steps – List of step grid indices to slide into. Converted to pulse positions using
self._default_grid. Mutually exclusive with notes.time – Fraction of the preceding note’s duration used for the glide.
shape – Easing curve. Defaults to
"linear".resolution – Pulses between pitch bend messages.
bend_range – Instrument’s pitch wheel range in semitones (default 2.0). Pairs with larger intervals are skipped. Pass
Noneto disable range checking.wrap – If
True(default), include a wrap-around slide from the last note back toward the first.extend – If
True(default), extend the preceding note’s duration to reach the slide target’s onset — 303-style legato through the glide.
- Raises:
ValueError – If neither notes nor steps is provided.
Example
p.sequence(steps=[0, 4, 8, 12], pitches=[40, 42, 40, 43]) p.legato(0.95) # Slide into the 2nd and 4th notes p.slide(notes=[1, 3], time=0.2, shape="ease_in") # Same using step grid indices p.slide(steps=[4, 12], time=0.2, shape="ease_in") # Slide without extending the preceding note p.slide(notes=[1, 3], extend=False)
- sysex(data: bytes | List[int], beat: float = 0.0) subsequence.pattern_builder.PatternBuilder[source]¶
Send a System Exclusive (SysEx) message at a beat position.
SysEx messages allow deep integration with synthesizers and other hardware: patch dumps, parameter control, and vendor-specific commands. The
dataargument should contain only the inner payload bytes, without the surrounding0xF0/0xF7framing — mido adds those automatically.- Parameters:
data – SysEx payload as
bytesor a list of integers (0–127).beat – Beat position within the pattern (default 0.0).
Example
# GM System On — reset a GM-compatible device to defaults p.sysex([0x7E, 0x7F, 0x09, 0x01])