subsequence.held_notes ====================== .. py:module:: subsequence.held_notes .. autoapi-nested-parse:: 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 --------------- .. py:class:: HeldNotes(release_ms: float = 0.0, latch: bool = False) The live set of notes held on a ``note_input`` keyboard. Create a held-note tracker. :param 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. :param latch: When True, the held set persists after release until the next chord is started. .. py:method:: note_off(pitch: int, now: float) -> None Register a note-off (``now`` = a perf_counter timestamp). .. py:method:: note_on(pitch: int, velocity: int, now: float) -> None Register a note-on (``now`` = a perf_counter timestamp). .. py:method:: snapshot(now: float) -> List[int] 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.