theHarmonicAlgorithm-3.0.0: Real-time harmonic progression generation for TidalCycles live performance
Safe HaskellSafe-Inferred
LanguageHaskell2010

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    0      0.0   0.0   s
  , rh   16      0.2   0.3   s    -- elements layer in
  , rh'  28      0.35  0.55  s    -- riser: sparse but loud, then snap
  , rh   32      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

Types

data FormNode Source #

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

Instances

Instances details
Show FormNode Source # 
Instance details

Defined in Harmonic.Interface.Tidal.Form

Eq FormNode Source # 
Instance details

Defined in Harmonic.Interface.Tidal.Form

data FormTime Source #

A node's position in time — wall-clock Secs or musical Bars (4/4). Resolved to Tidal cycles at realization (see formK). Mix freely in one form.

Constructors

Secs Double 
Bars Double 

Instances

Instances details
Show FormTime Source # 
Instance details

Defined in Harmonic.Interface.Tidal.Form

Eq FormTime Source # 
Instance details

Defined in Harmonic.Interface.Tidal.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).

Constructors

Smooth 
Snap 

Instances

Instances details
Show Transition Source # 
Instance details

Defined in Harmonic.Interface.Tidal.Form

Eq Transition Source # 
Instance details

Defined in Harmonic.Interface.Tidal.Form

data Kinetics Source #

Realized form: continuous and discrete signals for live performance.

Constructors

Kinetics 

Fields

  • kSignal :: Pattern Double

    Kinetics level 0-1 (continuous interpolated)

  • kDynamic :: Pattern Double

    Dynamic envelope 0-1 (continuous interpolated)

  • kProg :: Pattern ProgressionContext

    Active 3-layer progression (step function)

  • kLoopSecs :: Double

    Form total duration in seconds; 0 = atemporal (single-node iK or lK). Consumers like the 4-char-display helper read this to drive a wall-clock counter that wraps every kLoopSecs.

  • kCps :: Double

    Cycles per second at form construction (= bpm/60). Used by the display broadcaster to convert cycle time → seconds. Stays coherent with Tidal's actual cps because both are derived from the same bpm on every launcher re-evaluation.

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, rh/rh' take bars (rehearsal marks, 4/4); unprimed = smooth transition, primed = snap. at is unchanged from before.

at 0 0 0 s seconds, smooth rh 8 0.5 0.5 s bars, smooth at' 60 1 1 s seconds, snap rh' 16 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, rh/rh' take bars (rehearsal marks, 4/4); unprimed = smooth transition, primed = snap. at is unchanged from before.

at 0 0 0 s seconds, smooth rh 8 0.5 0.5 s bars, smooth at' 60 1 1 s seconds, snap rh' 16 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, rh/rh' take bars (rehearsal marks, 4/4); unprimed = smooth transition, primed = snap. at is unchanged from before.

at 0 0 0 s seconds, smooth rh 8 0.5 0.5 s bars, smooth at' 60 1 1 s seconds, snap rh' 16 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, rh/rh' take bars (rehearsal marks, 4/4); unprimed = smooth transition, primed = snap. at is unchanged from before.

at 0 0 0 s seconds, smooth rh 8 0.5 0.5 s bars, smooth at' 60 1 1 s seconds, snap rh' 16 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")

lK Source #

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.

Primitives

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.

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).

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.