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

Harmonic.Framework.Builder.PolyGen

Description

The foundation progression (T layer) is a plain gen walk, byte-identical to it: same chain builder, same R constraints, same entropy dial. Two partner triad chains (S/M layers) are then walked over the finished foundation, one bar at a time. Each partner draws from a fresh transition list fetched from ITS OWN previous state — every partner bar is a corpus-valid continuation of its own layer history — filtered to the polytonal overlap rules against that bar's foundation triad:

  • each partner shares exactly 2 pitch classes with the foundation bar;
  • the three triads union to exactly 5 pitch classes.

Those two rules admit exactly two geometries per bar, and the traversal chooses freely between them: COMMON-DYAD (all three triads share one dyad; every layer pair sounds 4 tones) and BASE-ANCHORED (the partners share different dyads of the foundation; T+S and T+M sound 4 tones, S+M sounds the full pentad). The hub-tone shape (three different dyads through one tone) unions to 4 and is excluded.

Partners honour the harmonic-space constraints only — key, allowed roots, overtones — through the same R predicate as the walk (matchesContextWithTarget with no bass target). Root-motion direction specs, drift, pedal and inversion spacing bind the foundation alone: the foundation owns the bass whenever it is present, and the partner layers stay free to diverge.

Selection: jointly valid (S, M) pairs are ranked by summed own-list rank and drawn with ONE entropy-scaled gamma draw over the pair pool (the two-stage per-layer alternative saturates the entropy dial at the ~8- candidate second pool; the joint pool sits at ~100-324 where the dial is monotone — measured in archive/analysis/poly_seq.md). Supply relaxes down tiers when a list runs dry: one side from the space-constrained pure enumeration over all 220 absolute 3-PC sets, then both, then the unconstrained enumeration — the last is total, so partner selection can never fail (the study measured the list tier alone at 100% over 2,100 live steps; archive/analysis/poly_chain.md).

S/M identity is assigned once, after generation: the chain with the lower whole-layer dissonance total becomes S. Per-bar assignment would swap chain membership at ~45% of bar boundaries and destroy the very layer identity the chains provide.

Synopsis

Documentation

runPolyGen :: GenConfig -> IO (ProgressionContext, GenerationDiagnostics) Source #

Execute a PolyMode config: foundation walk (byte-identical to gen), then the partner pass, then S/M assignment.

runPolyGenFrom :: ProgressionContext -> Int -> Int -> GenConfig -> IO (ProgressionContext, GenerationDiagnostics) Source #

Regenerate a contiguous range of bars within an existing polytonal context. The foundation range regenerates exactly like a gen regen (cue = the bar before the range, inferred by genFrom); both partner chains regenerate over it, seeded from the KEPT partner bars before the range, so every regenerated partner bar continues its own layer's history. The source's S/M labelling is preserved — a partial regen never reorders chains (that would relabel the kept bars).

Seam: when the regen does not cover the whole progression, the final regenerated bar's joint pool is additionally filtered to pairs whose partner states can continue onto the kept next partner bars as real graph edges — relaxed when empty (the study measured list-tier supply at 100%, so the filter is a preference, not a wall).