subsequence.held_notes

Held-note tracking for live MIDI note input.

HeldNotes maintains the set of MIDI notes a player is currently holding on a note_input keyboard, so an arpeggiator (or any pattern) can read the live pitch set each cycle via p.held_notes().

It is a tiny, dependency-free state machine. All of its state lives on the sequencer loop thread: the mido callback thread only appends raw note events to a deque, which the loop drains and feeds here. Because it is never touched from two threads, it needs no locking — and because it takes the current time as an argument (rather than reading the clock itself), it is fully deterministic and trivial to unit-test.

Two smoothing behaviours guard against the arp dropping to silence:

  • ``release_ms`` debounce — a just-released note lingers in the held set for a short window, so the momentary all-keys-up gap during a hand-position changeover does not register as “nothing held”.

  • ``latch`` — the held set persists after release until a new chord is started (the first key pressed after every key is up replaces it), like a hardware arp’s latch / a sustain pedal. Under latch the release_ms window is unused — latch dominates.

Module Contents

class subsequence.held_notes.HeldNotes(release_ms: float = 0.0, latch: bool = False)[source]

The live set of notes held on a note_input keyboard.

Create a held-note tracker.

Parameters:
  • release_ms – How long (milliseconds) a released note keeps counting as held, smoothing the gap during hand-position changes. 0 removes a note the instant its note-off arrives. Ignored when latch is True.

  • latch – When True, the held set persists after release until the next chord is started.

note_off(pitch: int, now: float) None[source]

Register a note-off (now = a perf_counter timestamp).

note_on(pitch: int, velocity: int, now: float) None[source]

Register a note-on (now = a perf_counter timestamp).

snapshot(now: float) List[int][source]

Return the currently held MIDI notes, sorted ascending.

Combines notes physically down, any still within the release_ms debounce window at now, and the latched chord. Expired release-window notes are pruned as a side effect.