subsequence.voicings ==================== .. py:module:: subsequence.voicings .. autoapi-nested-parse:: Chord inversions and voice leading. Provides functions for rotating chord intervals into different inversions and choosing the smoothest voicing between consecutive chords. Voice leading minimises the total semitone movement so that chord pads sound connected rather than jumping around the keyboard. .. rubric:: Example .. code-block:: python # Manual inversion from subsequence.voicings import invert_chord first_inv = invert_chord([0, 4, 7], inversion=1) # [4, 7, 12] # Automatic voice leading across a pattern @composition.pattern(channel=0, length=4, voice_leading=True) def chords (p, chord): p.chord(chord, root=52, velocity=90, sustain=True) Module Contents --------------- .. py:class:: VoiceLeadingState Track the previous voicing across chord changes. Each pattern that uses voice leading gets its own instance so that a bass line and a pad can voice-lead independently. .. rubric:: Example .. code-block:: python state = VoiceLeadingState() voicing1 = state.next([0, 4, 7], 60) # root position (no previous) voicing2 = state.next([0, 3, 7], 60) # picks closest inversion to voicing1 Start with no previous voicing. .. py:method:: next(intervals: List[int], root_midi: int) -> List[int] Choose the smoothest voicing and update state. :param intervals: Chord intervals in semitones from root :param root_midi: MIDI note number for the chord root :returns: MIDI note numbers for the chosen voicing .. py:function:: invert_chord(intervals: List[int], inversion: int) -> List[int] Rotate chord intervals to produce an inversion. Inversion 0 is root position. Inversion 1 raises the bottom note by an octave (first inversion). Wraps around for inversions >= the number of notes. :param intervals: Chord intervals in semitones from root (e.g., ``[0, 4, 7]``) :param inversion: Which inversion to produce (0 = root position) :returns: Rotated interval list, still measured from the original chord root, so adding any root yields the same chord with a different bass note. .. rubric:: Example .. code-block:: python invert_chord([0, 4, 7], 0) # [0, 4, 7] - root position invert_chord([0, 4, 7], 1) # [4, 7, 12] - first inversion (E-G-C) invert_chord([0, 4, 7], 2) # [7, 12, 16] - second inversion (G-C-E) .. py:function:: voice_lead(intervals: List[int], root_midi: int, previous_voicing: Optional[List[int]]) -> List[int] Find the inversion closest to a previous voicing. Tries every inversion, in the nearest octaves, and picks the candidate with the smallest total semitone movement from ``previous_voicing``. Voices are compared *positionally* (voice ``i`` to voice ``i``), so this picks the best inversion rather than the globally optimal voice reassignment. If ``previous_voicing`` is ``None`` or the chord sizes differ, returns root position. :param intervals: Chord intervals in semitones from root (e.g., ``[0, 4, 7]``) :param root_midi: MIDI note number for the chord root :param previous_voicing: MIDI note numbers of the previous chord, or ``None`` :returns: MIDI note numbers for the best voicing