theHarmonicAlgorithm-3.1.1: Real-time harmonic progression generation for TidalCycles live performance
Safe HaskellNone
LanguageGHC2021

Harmonic.Interface.Tidal.LineHarmony

Description

Wraps walkLine in the cache + innerJoin pattern used by arrange:

  • The chord-selection pattern is resolved into a PERFORMED bar sequence at eval time (resolvePerformedSeq); the walk runs over the bars in performed order, so warp/rep reorderings and repeats are walked as the audience hears them. Non-periodic selections fall back to stored order with a printed notice.
  • Progressions seen on kProg are pre-materialised once into walking- bass lines keyed by (triads, chroma sources, performed sequence), forced eagerly so synthesis stays off the audio thread. The forcing horizon is 'Arc 0 1000' — a key first seen beyond cycle 1000 is synthesised on demand (with a printed notice).
  • innerJoin switches lines reactively when the form changes progression.
  • The input pattern list is dispatched by kinPick: [0,1] is partitioned into N equal windows (N = length of the list) and only the pattern whose window contains the current kinetics signal plays. Output is scaled by kDynamic and the user-supplied dynamics scalar (applied once here — launchers must not multiply by the same dynamic again).

Each integer in a pattern selects a 1-indexed beat position (1..4). Values outside [1..4] shift by full octaves, matching the div/mod convention of arrange: 5 → beat 1 +12, 0 → beat 4 −12, -1 → beat 3 −12.

In a launcher, the walking line is an ordinary block alongside the orchestral ones:

walk f k d = p "walk" $ f
  $ lineHarmony d k fund ["1 2 3 4"]

(dynamics scalar, context, beat-1 voicing, then the beat-position patterns; the scalar multiplies the form's kDynamic internally — do not also apply |* vel d.)

Because each integer picks a beat position, the pattern controls which beats of the generated line sound — "1 ~ 3 ~" plays a half-time version of the same line rather than a different line.

Synopsis

Documentation

lineHarmony Source #

Arguments

:: Pattern Double

Dynamics scalar (amp multiplier)

-> IK

Performance context (kinetics + chord-selection)

-> VoiceFunction

Beat-1 voicing (fund or root). Only the bass pitch class of each bar is read, so a solving strategy buys nothing here: grid returns the root by its own invariant, at the price of a voicing solve

-> [Pattern Int]

Polyphonic layers (1-indexed beat positions)

-> Pattern ValueMap 

Walking-bass arrangement with kinetics gating.

Fixed to the double-bass register (E1..C3, MIDI 28..48) inside walkLine; the emitted Tidal note values are pre-shifted by tidalNoteOffset so this range is audibly true at default synth tuning — no |- oct n compensation needed. Runtime register shifts via |+ oct n / |- oct n on the launcher side still compose normally.

The walk adapts to the generation family of the context it is given. For octatripentatonic progressions (pcProvenance = Just), the Pass-3 connector pool is reweighted: strata pitches (5 PCs) are most preferred, overlap (cyclic union of adjacent chord-PCs) is neutral, mode pitches (7 PCs) are admissible with a mild penalty, and chromatic ±1 approaches outside any of those sets are removed entirely. For jazz progressions (pcFamily = FJazz) each bar carries a BassVocab derived from its chord symbol, so the line reads the harmony as a bass player does — the fifth a 13th chord omits is restored, an altered dominant's #5 replaces the natural 5, and notated colour alterations stay off strong beats. gen and genE progressions walk their plain per-bar tone sets (for genE that is the foundation layer — the layer that owns the bass); connector tones draw on the key-area palette (barPalettes) — the same analysis behind the chordscale S/M layers.

Entropy is derived internally from the progression's harmonic character.

resolvePerformedSeq :: Pattern Int -> Maybe [Int] Source #

Resolve the performed bar sequence from a chord-selection pattern. Succeeds when the pattern is bar-quantised (one constant value per 4-cycle bar window) and periodic with the smallest period P <= 64 bars; the result is the raw 1-indexed selector values of one period, in performed order. 'rep s 1' resolves to [1..n]; 'rep s N' to each value N times; 'warp "[..]/k"' to the written sequence. Degraded (?), euclidean, and sub-bar selections return Nothing, and the walk falls back to stored bar order with a one-line notice.