subsequence.intervals¶
Interval and scale definitions, plus the helpers that resolve them.
Holds INTERVAL_DEFINITIONS (named scales and chords as semitone lists) and
the functions that work against it — scale_notes, scale_pitch_classes,
quantize_pitch, register_scale and friends.
Module Contents¶
- subsequence.intervals.get_diatonic_intervals(scale_notes: List[int], intervals: List[int] | None = None, mode: str = 'scale') List[List[int]][source]¶
Construct diatonic chords from a scale.
- subsequence.intervals.get_intervals(name: str) List[int][source]¶
Return a named interval list from the registry.
- subsequence.intervals.quantize_pitch(pitch: int, scale_pcs: Sequence[int]) int[source]¶
Snap a MIDI pitch to the nearest note in the given scale.
Searches outward in semitone steps from the input pitch. When two notes are equidistant (e.g. C# between C and D in C major), the upward direction is preferred.
- Parameters:
pitch – MIDI note number to quantize.
scale_pcs – Pitch classes accepted by the scale (0–11). Typically the output of
scale_pitch_classes().
- Returns:
A MIDI note number that lies within the scale.
Example
# Snap C# (61) to C (60) in C major scale = scale_pitch_classes(0, "ionian") # [0, 2, 4, 5, 7, 9, 11] quantize_pitch(61, scale) # → 60
- subsequence.intervals.register_scale(name: str, intervals: List[int], qualities: List[str] | None = None) None[source]¶
Register a custom scale for use with
p.snap_to_scale()andscale_pitch_classes().Built-in scale names (e.g.
"minor","hirajoshi") cannot be overwritten. Custom names may be re-registered freely — live reload re-runs registration on every save, so this must not raise.- Parameters:
name – Scale name (used in
p.snap_to_scale(key, name)). Must not be the name of a built-in scale.intervals – Semitone offsets from the root (e.g.
[0, 2, 3, 7, 8]for Hirajōshi). Must be whole numbers, start with 0, ascend strictly, and stay within 0–11.qualities – Optional chord quality per scale degree (e.g.
["minor", "major", "minor", "major", "diminished"]). Required only if you want to use the scale withdiatonic_chords()ordiatonic_chord_sequence().
- Raises:
ValueError – If name is a built-in scale, or intervals / qualities fail the rules above.
Example:
import subsequence subsequence.register_scale("raga_bhairav", [0, 1, 4, 5, 7, 8, 11]) @comp.pattern(channel=0, length=4) def melody (p): p.note(60, beat=0) p.snap_to_scale("C", "raga_bhairav")
- subsequence.intervals.scale_notes(key: str, mode: str = 'ionian', low: int = 60, high: int = 72, count: int | None = None) List[int][source]¶
Return MIDI note numbers for a scale within a pitch range.
- Parameters:
key – Scale root as a note name (
"C","F#","Bb", etc.). This acts as a pitch-class filter only — it determines which semitone positions (0–11) are valid members of the scale, but does not affect which octave notes are drawn from. Notes are selected starting fromlowupward;keycontrols which notes are kept, not where the sequence starts. To guarantee the first returned note is the root,lowmust be a MIDI number whose pitch class matcheskey. When starting from an arbitrary MIDI number, derive the key name withsubsequence.chords.PC_TO_NOTE_NAME[root_pitch % 12].mode – Scale mode name. Supports all keys of
SCALE_MODE_MAP(e.g."ionian","dorian","natural_minor","major_pentatonic"). Useregister_scale()for custom scales.low – Lowest MIDI note (inclusive). When
countis set, this is the starting note from which the scale ascends. If ``low`` is not a member of the scale defined by ``key``, it is silently skipped and the first returned note will be the next in-scale pitch abovelow.high – Highest MIDI note (inclusive). Ignored when
countis set.count – Exact number of notes to return. Notes ascend from
lowthrough successive scale degrees, cycling into higher octaves as needed. WhenNone(default), all scale tones betweenlowandhighare returned.
- Returns:
Sorted list of MIDI note numbers.
Examples
import subsequence import subsequence.constants.midi_notes as notes # C major: all tones from middle C to C5 subsequence.scale_notes("C", "ionian", low=notes.C4, high=notes.C5) # → [60, 62, 64, 65, 67, 69, 71, 72] # E natural minor (aeolian) across one octave subsequence.scale_notes("E", "aeolian", low=notes.E2, high=notes.E3) # → [40, 42, 43, 45, 47, 48, 50, 52] # 15 notes of A minor pentatonic ascending from A3 subsequence.scale_notes("A", "minor_pentatonic", low=notes.A3, count=15) # → [57, 60, 62, 64, 67, 69, 72, 74, 76, 79, 81, 84, 86, 88, 91] # Misalignment: key="E" but low=C4 — first note is C, not E subsequence.scale_notes("E", "minor", low=60, count=4) # → [60, 62, 64, 66] (C D E F# — all in E natural minor, but starts on C) # Fix: derive key name from root_pitch so low is always in the scale root_pitch = 64 # E4 key = subsequence.chords.PC_TO_NOTE_NAME[root_pitch % 12] # → "E" subsequence.scale_notes(key, "minor", low=root_pitch, count=4) # → [64, 66, 67, 69] (E F# G A — starts on the root)
- subsequence.intervals.scale_pitch_classes(key_pc: int, mode: str = 'ionian') List[int][source]¶
Return the pitch classes (0–11) that belong to a key and mode.
- Parameters:
key_pc – Root pitch class (0 = C, 1 = C#/Db, …, 11 = B).
mode – Scale mode name. Supports all keys of
DIATONIC_MODE_MAP(e.g."ionian","dorian","minor","harmonic_minor").
- Returns:
Pitch classes in scale-degree order, starting from the root (length varies by mode). Values wrap mod-12, so the list is not numerically sorted for non-C roots.
Example
# C major pitch classes scale_pitch_classes(0, "ionian") # → [0, 2, 4, 5, 7, 9, 11] # A minor pitch classes scale_pitch_classes(9, "aeolian") # → [9, 11, 0, 2, 4, 5, 7] (mod-12)