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

Harmonic.Rules.Types.ProgressionContext

Description

A ProgressionContext bundles three bar-aligned Progression layers — triads, pentatonic strata, and diatonic modes — together with optional per-bar provenance tracking which tristrata and strata each bar was drawn from. Every family fills the layers: genP walks curated strata/mode chroma and carries Just provenance; genE fills partner triads; plain gen and genJ derive a pentatonic S and a mode M by chordscale key-area analysis (chordscale), provenance Nothing. Only raw fromProgression output (hand-built material not passed through chordscale) still duplicates one progression across all three layers.

Synopsis

Documentation

data Layer Source #

Layer tag selecting one of the three progression layers — or a combination of them — at a call site.

Single layers:

  • T — Triad layer (cadential harmony, the Rules.Types.Progression result).
  • S — Strata layer (genP: 5-note pentatonic chroma; genE: the less dissonant partner triad).
  • M — Mode layer (genP: 7-note diatonic chroma; genE: the more dissonant partner triad).

Combinations (synthesized per bar by layer):

  • TS / TM / SM — pointwise pitch-class union of two layers.
  • TSM — union of all three.
  • PT — pivot tones: the pitch classes common to ALL three layers.

A merged bar is rooted on its LOWEST constituent layer's bar root (T before S before M) — the foundation owns the bass whenever it is present. On a polytonal (genE) context TS/TM always union to 4 tones, TSM to 5, and PT holds 2 tones on common-dyad bars or the single hub tone on base-anchored bars. On a chordscale-derived gen/genJ context S is the bar's pentatonic, M its mode, and PT the chord tones the pentatonic keeps. On contexts whose layers still duplicate one progression (hand-built material without chordscale) every combination degrades to that progression.

M replaces the plan's D to avoid clashing with NoteName (D natural), which is re-exported via Lib for live-coding use.

Constructors

T 
S 
M 
TS 
TM 
SM 
TSM 
PT 

data Family Source #

Generation family a progression belongs to. Families never mix: regeneration (genFrom) always reproduces the source's family, and fuse of differing families downgrades to FTriad. Family drives dispatch in genFrom and in the walking-bass interface: FJazz contexts walk with a per-bar bass vocabulary (restored fifths, passing extensions, avoid-tone protection), FStrata walks via provenance chroma, and everything else — including downgraded mixes — walks the plain per-bar tone sets at whatever cardinality each bar carries.

  • FTriad — plain triadic walk (gen).
  • FExtended — uniform 4-note hand-built material (lead' cues, hand-spliced progressions); arrives only by inference, never stamped.
  • FStrata — strata-first family (genP/genI; carries provenance).
  • FJazz — jazz Change-graph walk (genJ; variable arity 3-6).
  • FPoly — polytonal three-triad family (genE): T carries the foundation walk, S/M carry partner triad chains sharing tones with it per bar. Stamped explicitly by the producer; inference never yields it.

Constructors

FTriad 
FExtended 
FStrata 
FJazz 
FPoly 

data ProgressionContext Source #

Three bar-aligned progression layers with optional per-bar provenance.

Invariant: progLength triadLayer == progLength strataLayer == progLength modeLayer, and when pcProvenance = Just sq, Seq.length sq == progLength triadLayer.

layer :: Layer -> ProgressionContext -> Progression Source #

Project a ProgressionContext to a single Progression by layer tag. Single tags project a stored layer; combination tags synthesize per-bar unions (rooted on the lowest constituent layer's bar — T before S before M, so the foundation owns the merged bass whenever present); PT synthesizes the per-bar intersection of all three layers. Total for every family; on duplicated-layer contexts (the raw fromProgression fallback) every combination collapses to the stored progression.

pcLength :: ProgressionContext -> Int Source #

Bar count — equal across all three layers by invariant.

fromProgression :: Progression -> ProgressionContext Source #

Wrap a single Progression as a ProgressionContext by duplicating it into all three layers with no provenance — the RAW constructor. The gen and genJ producers post-process the result with chordscale to derive real S/M layers; hand-built material stays duplicated until passed through the same combinator.

liftPC :: (Progression -> Progression) -> ProgressionContext -> ProgressionContext Source #

Apply a Progression-transforming function pointwise across all three layers. Drops provenance, and with it any FStrata claim — a strata context without provenance is no longer strata material, so the family normalizes in step. Bar-alignment-preserving combinators should use liftPCAligned instead, which carries provenance (and the family) through.

liftPCAligned :: (forall a. Seq a -> Seq a) -> ProgressionContext -> ProgressionContext Source #

Class 1 combinator support: apply ONE bar-order permutation/replication to all three layers AND the provenance sequence in lockstep. The rank-2 argument cannot inspect elements (parametricity), so it can only rearrange or duplicate whole bars — per-bar pairings (bar ↔ provenance, foundation ↔ partners) survive intact and the family stamp stays true. Chordscale-derived S/M layers (gen / genJ) permute with their bars, so each bar keeps its own mode/pentatonic; the key BOUNDARIES they encode reflect the pre-permutation whole-progression analysis — re-apply chordscale for a fresh reading.

liftPCSubst :: (Progression -> Progression) -> ProgressionContext -> ProgressionContext Source #

Bar substitution breaks both the provenance-backed (FStrata) and the structural (FPoly) family invariants — the result walks and voices as plain material, and the tag must not claim otherwise. Chordscale-derived S/M layers are mapped like any other bars and go stale the same way — re-apply chordscale after bar-substituting edits.

normalizeFamily :: ProgressionContext -> ProgressionContext Source #

FStrata is meaningless without the provenance that defines it; the normalized form keeps genFrom dispatch and voicing routing honest. Invariant established type-wide: pcFamily == FStrata ⟹ pcProvenance == Just _.

pcSplice :: ProgressionContext -> Int -> Int -> ProgressionContext -> ProgressionContext Source #

Splice a range of bars within a ProgressionContext, replacing the triad / strata / mode layers and the pcProvenance sequence in lockstep.

Range is 1-indexed and wrap-aware (mirrors spliceProgression):

  • Non-wrapping (start <= end): replaces positions start..end.
  • Wrapping (start > end): replaces start..N and 1..end.

The triad layer's movement seam is fixed via spliceProgression; strata / mode layers don't carry meaningful Movement state (their Cadence is built with Movement = Unison — see mkChromaCS in Builder) so they use a plain sequence splice.

Provenance follows the same geometry. When either side has pcProvenance = Nothing, the result is Nothing (the splice can't reconstruct provenance from a layer-only source).

Caller is responsible for ensuring the inserted ProgressionContext has length equal to the range size.