| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Harmonic.Interface.Tidal.Bridge
Description
Bridge between the harmonic generation engine and TidalCycles live coding.
Chord selection via mininotation patterns (Pattern Int).
Two arrangement strategies:
arrange— onset-join with kinetics range gating: each note maps through the chord active at its onset time, masked by kinetics signal.arrange'— squeeze with kinetics range gating: each chord slot gets the full input pattern compressed to fit.
Both take a progression modifier (P.Progression -> P.Progression)
and read the base progression from kProg k via innerJoin.
Synopsis
- type VoiceFunction = Progression -> [[Int]]
- voiceRange :: (Int, Int) -> Pattern Int -> Pattern Int
- layerForVoicing :: Layer -> ProgressionContext -> (Bool, Progression)
- warp :: String -> Pattern Int
- rep :: ProgressionContext -> Pattern Time -> Pattern Int
- arrange :: (Double, Double) -> IK -> (Int, Int) -> Layer -> VoiceFunction -> (Progression -> Progression) -> [Pattern Int] -> Pattern ValueMap
- arrange' :: (Double, Double) -> IK -> (Int, Int) -> Layer -> VoiceFunction -> (Progression -> Progression) -> [Pattern Int] -> Pattern ValueMap
- type Tuning = [(NoteName, Int)]
- ecbc :: Tuning
- composite :: (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap
- harmonics :: NoteName -> (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap
- overtoneMap :: ProgressionContext -> Layer -> [Pattern Int] -> IO ()
- overtoneTable :: [Int] -> [(Int, Int, [(NoteName, Int)])]
- compositeSeries :: Tuning -> [Int]
- stringPitches :: (NoteName, Int) -> [Int]
- partialOffset :: Int -> Int
- parallel :: Pattern Note -> ControlPattern -> ControlPattern
- harmonicState :: Layer -> IK -> ControlPattern
- lookupChordAt :: Time -> Pattern Int -> Int
- lookupChord :: ProgressionContext -> Int -> Chord
- lookupProgression :: ProgressionContext -> Pattern Int -> Pattern [Int]
- overlapF :: Int -> Progression -> Progression
- overlapB :: Int -> Progression -> Progression
- overlap :: Int -> Progression -> Progression
- forceAll :: [[Note]] -> ()
Voice Functions
type VoiceFunction = Progression -> [[Int]] Source #
Voice function type: extracts integer pitch sequences from progression
voiceRange :: (Int, Int) -> Pattern Int -> Pattern Int Source #
Filter pattern events by DEGREE-INDEX range — the raw Pattern Int
values before the degree→pitch mapping, NOT MIDI notes. Instrument
range clipping in MIDI space happens later, via
clip.
layerForVoicing :: Layer -> ProgressionContext -> (Bool, Progression) Source #
Map notes through chords using onset-time lookup, with kinetics range gating.
The base progression and chord selection are read from IK.
The modifier function transforms the progression (e.g. overlapF 0, id).
Events are masked by the kinetics signal: only active when kSignal is
within the (lo, hi) range. Form-driven dynamics (kDynamic) are applied
automatically.
Parameter order: context first (kinetics range, IK, MIDI range), then
interactive (voice function, modifier, patterns).
|Project the requested layer from a context together with its voicing
route. Chroma routing: the S/M layers of a genP-provenance context are
THE curated 5/7-PC chroma, and the S/M layers of a chordscale-derived
gen / genJ context are the analysis pentatonic / mode chroma — both are
always voiced by strataModeFlow, lattice semantics: pattern index
i addresses the i-th slot of a fixed pitch lattice grounded on bar 0
and inflected per bar — a held index pedals its pitch across key areas
rather than transposing with each bar's root. The T
layer always honours the user's VoiceFunction, as do the S/M layers
of genE contexts (independent partner triads — ordinary harmony, so
never chroma-routed) and of contexts whose layers are still literal
duplicates (hand-built material without
chordscale).
Routing by provenance / derived-chroma detection replaces the old
first-bar cardinality sniff (isOctaSM), which mis-routed
mixed-cardinality material in both directions (triad-first-bar chroma
escaped to the DP; 4-note-first-bar harmony was captured by the chroma
engine). Derived detection requires BOTH a distinct mode layer AND
every mode bar at chroma cardinality (≥5) — so a subst-downgraded genE
context (distinct but triadic layers) and a bar-substituted derived
context (mixed cardinality after the edit) both fall back to the user's
VoiceFunction rather than half-claiming lattice semantics.
A genE context is excluded whole, combination selectors included. Its
S/M are partner triads, and its SM/TSM unions are polytonal
SONORITIES — three stacked triads, five tones — not scale forms, so the
cyclic DP is the right tool for them and hasBigChroma correctly
declines to reroute a uniform 5-PC progression. Deliberate, not a gap.
Chord Selection Helpers
warp :: String -> Pattern Int Source #
Parse a mininotation chord selection pattern (bar-relative).
The /N divisor specifies the number of bars the pattern spans.
let r = warp "[1 2 3 4]/4" -- 4 chords over 4 bars (1 per bar) let r = warp "[1 2]/8" -- 2 chords over 8 bars (4 bars each)
rep :: ProgressionContext -> Pattern Time -> Pattern Int Source #
Generate a sequential chord selection pattern from a progression. Auto-derives length from the progression. Timing is bar-relative.
let r = rep s4 1 -- 4 chords over 4 bars (1 bar each) let r = rep s4 0.5 -- 4 chords over 2 bars (half bar each)
Arrangement
Arguments
| :: (Double, Double) | Kinetics gate: events pass only while |
| -> IK | Performance context (kinetics + chord selection) |
| -> (Int, Int) | Degree-index trim for the input patterns (scale degrees, not MIDI; instrument-range clipping happens later via clip) |
| -> Layer | Progression layer to voice — single (T | S | M) or synthesized combination (TS | TM | SM | TSM | PT) |
| -> VoiceFunction | Voice function (flow, root, etc.) |
| -> (Progression -> Progression) | Progression modifier (overlapF 0, id, etc.) |
| -> [Pattern Int] | Input patterns to harmonize |
| -> Pattern ValueMap |
Render scale-degree patterns into a playable ControlPattern, reading
pitches from the progression under the given voicing strategy.
The workhorse of the Tidal interface: every orchestral instrument in Harmonic.Interface.Tidal.Orchestra is a thin wrapper around it.
d1 $ arrange (0,1) k (-9,9) T flow id ["0 1 2 3"]
Arguments
| :: (Double, Double) | Kinetics range |
| -> IK | Performance context |
| -> (Int, Int) | Degree-index trim for the input patterns (scale degrees, not MIDI; instrument-range clipping happens later via clip) |
| -> Layer | Progression layer (T | S | M) |
| -> VoiceFunction | Voice function |
| -> (Progression -> Progression) | Progression modifier |
| -> [Pattern Int] | Input patterns to harmonize |
| -> Pattern ValueMap |
Map notes through chords using squeeze, with kinetics range gating.
Same kinetics/modifier pattern as arrange, but uses squeeze strategy:
each chord slot gets the full input pattern compressed to fit.
Natural harmonics (the composite overtone series)
type Tuning = [(NoteName, Int)] Source #
Open strings of an overtone instrument as (name, scientific octave):
(A,1) is A1 = MIDI 33.
The instrument behind hmnx: the Electric Contrabass Cittern as the MPC
harmonics keymap holds it — open strings A2 E3 G3 (thesis §2.1: EAeGB is
E2 A2 E3 G3 B3) and the top string in both Hipshot lever positions, B3 and
C4. B and C are one physical string,
but the keygroup carries both sets of samples, so both are always available
to a pattern; the harmonic context is what limits the space. Fixed on
purpose: the keymap never changes, so no launcher states a tuning.
Arguments
| :: (Double, Double) | Kinetics gate, as |
| -> IK | Performance context |
| -> Layer | Harmonic state: T (chord tones) or S (pentatonic) |
| -> [Pattern Int] | Contours: ints into the bar's viable series |
| -> Pattern ValueMap |
The whole overtone instrument. Pattern ints index the bar's viable
composite series — the harmonics of ecbc whose pitch class the layer
voices — compressed to 0, 1, 2 … ascending and looped by floor-mod, so
"[0,1,2]" is the lowest three available tones from the bottom up and
"[-1,-2,-3]" the highest three from the top down, whatever the size of the
structure in play. A pitch class present in two octaves is two ints. Never an
octave wrap: these are fixed sample pitches. A bar whose voicing has no
instrument tone rests. Polyphonic; harmonics is the per-string filter over
the same mapping. The harmonic context is the only limit on the space:
generating inside the instrument's palette (hcOvertones) keeps every tone
reachable, and overtoneMap shows what each int hits.
, composite (0,1) k T ["~", "[0,1,2]/4"] # ch 11
harmonics :: NoteName -> (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap Source #
One string of ecbc, named by its open note (A E G B C; enharmonics
collapse): the SAME int → pitch mapping as composite, but only the pitches
that string can sound pass — the rest are silent, not remapped — so a string
block isolates or mutes a string and owns its sustain. Within the string the
result is mono' (latest-note priority: a new harmonic damps the previous,
as on the instrument).
, harmonics A (0,1) k T ["~", pat] # ch 11
overtoneMap :: ProgressionContext -> Layer -> [Pattern Int] -> IO () Source #
Print, per chord of a progression, what the ints of the basic series map
to on the instrument — pitch, MIDI number and sources in thesis notation
(E2 = string E, overtone 2; / = alternative sources) — with the
top-down alias beside each int; then the distinct ints of the contour over
its first bar, resolved per chord. Takes the progression context and the
layer the block plays, so what it prints is what composite sounds.
overtoneMap s T ["[0,1,2]/4"]
overtoneTable :: [Int] -> [(Int, Int, [(NoteName, Int)])] Source #
The basic series over a set of voiced pitch classes, on ecbc: (int,
MIDI pitch, sources), a source being (string, overtone number) in the
thesis's numbering — OT1 the fundamental's class (partials 2 and 4), OT2 the
fifth, OT3 the third. Pure core of overtoneMap.
compositeSeries :: Tuning -> [Int] Source #
The composite overtone series (thesis §2.2): every pitch any string of a
tuning can sound as a natural harmonic, ascending, once each.
compositeSeries ecbc is the keymap:
57 64 67 69 71 72 73 74 76 78 79 80 83 84 87 88.
stringPitches :: (NoteName, Int) -> [Int] Source #
The four playable partials of the 2016 thesis — octave, fifth, double octave, third (partials 2 3 4 5) — of one open string, as MIDI pitches.
partialOffset :: Int -> Int Source #
Semitones from a string's fundamental to its n-th partial:
round (12 * logBase 2 n), so partials 2..5 sit at +12 +19 +24 +28.
Parallelism Harmoniser
parallel :: Pattern Note -> ControlPattern -> ControlPattern Source #
Stack fixed-interval parallel voices over an arranged ControlPattern.
The offset pattern is the FULL voice spec in absolute semitones: each note
of pat is replaced by one copy per simultaneous offset, shifted by that
offset. Include 0 to retain the original note; omit it to drop the root.
Comma = simultaneous voices, space = time-sequenced offsets (standard
mininotation, natively evaluated). Applied post-voicing/post-range-filter,
so offsets are not gated by the arrange MIDI range.
parallel "0 7" $ arrange ... -- root + perfect fifth above parallel "7" $ arrange ... -- fifth only (root dropped) parallel "[0,-5,4]" $ arrange ... -- root, fourth below, major third above
Harmonic state for an external note-mapper
harmonicState :: Layer -> IK -> ControlPattern Source #
The chosen layer's pitch classes as held MIDI notes, for a device that locks a played keyboard to the harmony (the rig's MiDiNiNjA X3).
One note per distinct pitch class, MIDI 108-119 (pitch class = note mod 12),
on MIDI channel 10, each held for the length of its chord: the structure
follows the chord grid (snd k), so two chords in a bar give two held sets.
Any layer or combination (T, S, M, TS, ...) and any set size. Degree
indices 0-11 cover the largest set; the indices past a chord's size fold back
onto pitch classes already sent and are dropped, so each chord change costs
one message per pitch class on a DIN line shared with the music.
Each set goes out stateLead ahead of its chord: on the wire before the
chord's own notes, and in place for keys played just before the downbeat.
Channel 10, notes 108-119 sits above everything else on that channel in both rigs: the JV-map kit (0-9, 60-80), metronome and count, the MPC sub (35-48). The X3 consumes these notes before they reach the MPC.
kmap f k lyr = p "kmap" $ f $ harmonicState lyr k (BootTidal)
Chord Lookup
lookupChordAt :: Time -> Pattern Int -> Int Source #
Point-query a chord selection pattern at a specific time.
Returns the chord index (0-indexed) active at time t.
Falls back to chord 0 if no events found.
lookupChord :: ProgressionContext -> Int -> Chord Source #
Lookup a chord from a progression context by index with modulo wrap. Operates on the triad layer (the harmonic content).
lookupProgression :: ProgressionContext -> Pattern Int -> Pattern [Int] Source #
Lookup progression (triad layer) as a pattern of voicings via flow.
Progression Overlap (Re-exports from Arranger)
overlapF :: Int -> Progression -> Progression Source #
Forward overlap: merge pitches from n bars ahead
overlapB :: Int -> Progression -> Progression Source #
Backward overlap: merge pitches from n bars behind
overlap :: Int -> Progression -> Progression Source #
Bidirectional overlap: merge pitches from n bars in both directions