| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Harmonic.Interface.Tidal.Form
Description
Encodes macro-level compositional arc as programmable structure, realised as TidalCycles patterns, looping endlessly.
A form is a list of nodes. Each node fixes a point in time and the two signals that drive everything downstream: kinetics (how active the music is) and dynamics (how loud). Instruments gate themselves on kinetics, so the arc is written once and every line follows it.
Nodes come in two time bases — at takes wall-clock seconds, rh takes
bars (rehearsal marks). Primed variants (at', rh') snap rather than
interpolate, for a hard cut into a new section:
form = -- time k d [ rh 1 0.0 0.0 s , rh 17 0.2 0.3 s -- elements layer in , rh' 29 0.35 0.55 s -- riser: sparse but loud, then snap , rh 33 1.0 1.0 s -- drop ]
The form is compiled into an IK — the performance context every
instrument reads — by pairing it with a tempo and a chord-selection
pattern:
k = iK tempo form (warp "[1 2 3 4]/4")
The simplest useful form is a single constant node, which holds one progression at full kinetics forever:
form = [ at 0 1.0 1.0 s ]
Synopsis
- data FormNode = FormNode {}
- data FormTime
- data Transition
- data Kinetics = Kinetics {}
- type IK = (Kinetics, Pattern Int)
- at :: Double -> Double -> Double -> ProgressionContext -> FormNode
- at' :: Double -> Double -> Double -> ProgressionContext -> FormNode
- rh :: Double -> Double -> Double -> ProgressionContext -> FormNode
- rh' :: Double -> Double -> Double -> ProgressionContext -> FormNode
- iK :: Double -> [FormNode] -> Pattern Int -> IK
- lK :: Pattern Double -> Pattern Double -> ProgressionContext -> Pattern Int -> IK
- formK :: Double -> [FormNode] -> Kinetics
- beatsPerBar :: Double
- ki :: (Double, Double) -> IK -> Pattern a -> Pattern a
- slate :: (Double, Double) -> IK -> [Pattern a] -> Pattern a
- mark :: Double -> IK -> [Pattern a] -> Pattern a
- mark' :: Double -> IK -> [Pattern a] -> Pattern a
- markbar :: Double -> IK -> [Pattern a] -> Pattern a
- marksec :: Double -> IK -> [Pattern a] -> Pattern a
- markAt :: FormTime -> IK -> [Pattern a] -> Pattern a
- kinPick :: IK -> [Pattern a] -> Pattern a
- isolate :: (Int, Int) -> IK -> IK
- isolate' :: (Int, Int) -> IK -> IK
- withForm :: IK -> (ProgressionContext -> Pattern ValueMap) -> Pattern ValueMap
Types
A node in a form definition: a point in time with kinetics level, dynamic level, active progression, and the transition style of the section starting here.
Constructors
| FormNode | |
Fields
| |
A node's position in time — wall-clock Secs elapsed (0 = the start) or
musical Bars (4/4), numbered from 1 as the 12 Step counts them (bar 1 =
the start). Resolved to Tidal cycles at realization (see formK). Mix freely
in one form.
data Transition Source #
How a section moves to the next node: Smooth (ramped) or Snap
(hold this node's value, then jump on the next node's exact time).
Instances
| Show Transition Source # | |
Defined in Harmonic.Interface.Tidal.Form Methods showsPrec :: Int -> Transition -> ShowS # show :: Transition -> String # showList :: [Transition] -> ShowS # | |
| Eq Transition Source # | |
Defined in Harmonic.Interface.Tidal.Form | |
Realized form: continuous and discrete signals for live performance.
Constructors
| Kinetics | |
Fields
| |
type IK = (Kinetics, Pattern Int) Source #
Performance context: Kinetics bundled with chord selection pattern.
Reduces parameter threading — r and k are always passed together.
Construction
at :: Double -> Double -> Double -> ProgressionContext -> FormNode Source #
Form node builders. Time unit and transition are orthogonal:
at/at' take wall-clock seconds elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 0.9 0.9 s bars, snap
at' :: Double -> Double -> Double -> ProgressionContext -> FormNode Source #
Form node builders. Time unit and transition are orthogonal:
at/at' take wall-clock seconds elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 0.9 0.9 s bars, snap
rh :: Double -> Double -> Double -> ProgressionContext -> FormNode Source #
Form node builders. Time unit and transition are orthogonal:
at/at' take wall-clock seconds elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 0.9 0.9 s bars, snap
rh' :: Double -> Double -> Double -> ProgressionContext -> FormNode Source #
Form node builders. Time unit and transition are orthogonal:
at/at' take wall-clock seconds elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 0.9 0.9 s bars, snap
iK :: Double -> [FormNode] -> Pattern Int -> IK Source #
Construct performance context from BPM, form nodes, and chord selection.
k = iK tempo [at 0 0 0 s, at 30 1 1 s] (warp "[1 2 3 4]/8")
Arguments
| :: Pattern Double | Kinetics signal (0-1, live) |
| -> Pattern Double | Dynamics signal (0-1, live) |
| -> ProgressionContext | Active 3-layer progression |
| -> Pattern Int | Chord-selection pattern |
| -> IK |
Live kinetics: build IK from reactive kinetics/dynamics signals. Bypasses form interpolation — use when the envelope is driven by live input (e.g. MIDI CC) rather than a static keyframed form.
k = lK exP exP s r -- pedal drives both kinetics and dynamics
Realization
formK :: Double -> [FormNode] -> Kinetics Source #
Realize a form definition into Kinetics signals at a given BPM.
Single-node forms produce constant signals (global state).
Multi-node forms produce per-segment signals — smooth (ramp) or snap (step)
per each node's fnTrans — and a step-function progression, looping at the
form's total duration. Time is resolved from each node's FormTime.
Contract details:
- The LAST node is the loop terminator, not a playable section: segments
run between consecutive node pairs, and the final node's time sets
kLoopSecs. Its kinetics/dynamic values only shape the ramp INTO it. - Nodes are sorted by resolved time before use, so a moved rehearsal mark cannot produce negative-width segments. Nodes sharing a time yield a zero-width segment (the earlier one is inaudible).
formK bpm []yields silence on every signal — safe to evaluate mid-edit; nothing sounds until a node exists.- Smooth ramps render at 16 steps per SEGMENT regardless of length
(see
formSignal) — a long segment crosses kinetics thresholds on a correspondingly coarse grid.
Primitives
beatsPerBar :: Double Source #
Beats per bar for Bars resolution. 4/4, matching BootTidal's bar helper
(1 bar = 4 cycles, since cps = bpm/60 makes a cycle one beat).
ki :: (Double, Double) -> IK -> Pattern a -> Pattern a Source #
Range gate: mask a pattern by kinetics signal level. Events pass only when kSignal is within the (lo, hi) range.
slate :: (Double, Double) -> IK -> [Pattern a] -> Pattern a Source #
Gated stack: stack patterns and gate by kinetics range.
markbar :: Double -> IK -> [Pattern a] -> Pattern a Source #
markAt in bars (see the doctrine there).
markAt :: FormTime -> IK -> [Pattern a] -> Pattern a Source #
Form-relative cue: place patterns ONCE per form loop at a form time. The
list is stacked (the shape of slate); each pattern's first cycle is squeezed
into the one-beat window starting at that time, so a plain one-note pattern
fires exactly on the beat and "1 1" gives two hits within it. Loops in
lockstep with kSignal — the same slow loopCycles wrap as formSignal —
so a cue and the kinetics can never drift apart. NOT kinetics-gated: a
placement is absolute (the launcher's |* vel d still applies).
Time-unit doctrine: unprimed = bars, primed = seconds. mark takes musical
bar numbers as written for rh nodes (bar 1 = cycle 0, the number the 12 Step
shows); mark' takes seconds elapsed as for at nodes (the 12 Step's seconds
counter reads the same) and, like them, assumes cps has been constant since
cycle 0. markbar / marksec are synonyms.
Atemporal forms (single node, lK) fire once at absolute cycles. A time at
or beyond the loop end, or no cps, is silence.
, mark 34 k [ pad 1 |* vel 0.9 ] -- speech sample on the drop
kinPick :: IK -> [Pattern a] -> Pattern a Source #
Kinetics-windowed dispatch: partition [0,1] into N equal windows of width 1/N, where N = length pats, and play only the pattern whose window contains the current kSignal. Windows are derived at call time; any N >= 1 works (N=1 collapses to "always play this pattern").
Boundaries belong to the lower window: window i covers
(i/N, (i+1)/N], with window 0 also including 0. So at the boundary
between window i and window i+1, the lower window plays.
N=2 → [0, 1/2], (1/2, 1] N=3 → [0, 1/3], (1/3, 2/3], (2/3, 1]
Empty list → silence. Outside [0,1] → silence (no window matches).
isolate :: (Int, Int) -> IK -> IK Source #
A degree range for every line the launcher starts, applied in k:
k = isolate (-4,2) $ iK tempo form (rep s 4)
isolate keeps degree indices lo..hi (inclusive), as a line's own
(-9,9) does; isolate' keeps only those outside it (lo and hi dropped).
Each line's own range still applies, so the two intersect, and calls
compose: isolate (-9,4) . isolate' (0,0) is -9..4 without degree 0.
Lines that don't voice degree indices (drums, walking bass, subKick,
harmonics, kmap) are unaffected.
isolate' :: (Int, Int) -> IK -> IK Source #
A degree range for every line the launcher starts, applied in k:
k = isolate (-4,2) $ iK tempo form (rep s 4)
isolate keeps degree indices lo..hi (inclusive), as a line's own
(-9,9) does; isolate' keeps only those outside it (lo and hi dropped).
Each line's own range still applies, so the two intersect, and calls
compose: isolate (-9,4) . isolate' (0,0) is -9..4 without degree 0.
Lines that don't voice degree indices (drums, walking bass, subKick,
harmonics, kmap) are unaffected.
withForm :: IK -> (ProgressionContext -> Pattern ValueMap) -> Pattern ValueMap Source #
Bridge helper: apply a function taking ProgressionContext to a Kinetics context.
Uses innerJoin to reactively switch when the form changes progressions.