| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Harmonic.Framework.Builder
Contents
- Modifier-Based Generation API
- genP Paradigm (strata-first)
- Generation Modifiers
- Generation Configuration
- Positional Generation (legacy/internal)
- Positional Generation with Diagnostics
- Positional Generation with Print Output
- Unified Positional Interface
- Diagnostics Types
- Harmonic Context (R constraints)
- Context Modifiers
- Configuration
- Internal functions (exposed for testing)
Description
This module implements the main generation loop that connects:
- R (Rules): HarmonicContext constraints via Filter module
- E (Evaluation): Database-derived composer probabilities + dissonance
- T (Traversal): gamma-distributed sampling over the graph walk
Academic Lineage
Data Science In The Creative Process (South, 2018): Wiggins' Creative Systems Framework <R,T,E> as the architectural blueprint. The Builder orchestrates the R→E→T pipeline where R constrains the search space, E scores candidates via database-derived probabilities, and T selects via gamma-distributed probabilistic traversal.
Unified Generation Interface
The module provides three public generation functions with identical type signatures:
genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression genStandard :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression genVerbose :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
All three functions:
* Return IO Progression (NOT tuples)
* Print diagnostics as side effects based on verbosity level
* Enable seamless switching between verbosity levels without code changes
Verbosity Levels
- 0 - Silent
genSilent: No diagnostic output. Use when you only want the progression.
- 1 - Standard
genStandard: Prints per-step diagnostics including: * Prior and posterior cadence states * Candidate pool composition (graph candidates, fallback candidates) * Top candidates with scores * Selected candidate source (graph or fallback) * Rendered chord names- 2 - Verbose
genVerbose: Prints everything from Standard plus: * TRANSFORM TRACE: Complete render pipeline (intervals, transposition, zero-form, naming) * ADVANCE TRACE: Root motion computation with pitch class arithmetic * Verification: DB stored name vs computed name
Legacy Diagnostic Functions
For backward compatibility, the module still exports:
* generate', gen', genWith' - Returns (Progression, GenerationDiagnostics) tuple
* generate'', gen'', genWith'' - Returns (Progression, GenerationDiagnostics) tuple with max diagnostics
Use these when you need to programmatically extract diagnostics rather than printing them.
Score Composition Details
Fallback candidates are scored using the formula:
chordDiss = Hindemith vertical dissonance (6-50 range) motionDiss = Root motion dissonance (1-6 range, vector-based) gammaDraw = Entropy-based random perturbation (~0-5, shape=1.01) badness = chordDiss × motionDiss × (gammaDraw + 1) score = 10000 - badness
The multiplicative formula spreads scores organically without artificial limits. Full 660-candidate pool (12 roots × C(11,2) pairs) ensures maximum variety.
The database is treated as abstract/pitch-agnostic. Root notes are computed at runtime based on movement intervals from a user-defined starting CadenceState.
Filter Notation (from original README)
Overtones/Pitch Set Filter
- Fundamental pitches (derives overtones):
"E A D G"(bass tuning) - Individual pitches with prime:
"E'""A'"A - Combined:
"G E' A' A#'"(G overtones + E, A, A# pitches) - Wildcard:
"*"(all pitches)
Tonality (Key) Filter
Root Notes Filter
- Pitches:
"E F# G" - Key signature:
"1b","#","##"(D major) - Wildcard:
"*"(all roots)
Synopsis
- gen :: GenConfig
- gen' :: GenConfig
- gen'' :: GenConfig
- genGrid :: GenConfig
- gen4 :: GenConfig
- gen4' :: GenConfig
- gen4'' :: GenConfig
- quad :: GenConfig -> GenConfig
- genFrom :: ProgressionContext -> Int -> Int -> GenConfig
- genFrom' :: ProgressionContext -> Int -> Int -> GenConfig
- genFrom'' :: ProgressionContext -> Int -> Int -> GenConfig
- genP :: StrataLabel -> GenConfig
- genP' :: StrataLabel -> GenConfig
- genP'' :: StrataLabel -> GenConfig
- genI :: GenConfig
- genII :: GenConfig
- genIII :: GenConfig
- genIV :: GenConfig
- genV :: GenConfig
- genVI :: GenConfig
- genVII :: GenConfig
- genVIII :: GenConfig
- genIX :: GenConfig
- genX :: GenConfig
- genXI :: GenConfig
- genI' :: GenConfig
- genII' :: GenConfig
- genIII' :: GenConfig
- genIV' :: GenConfig
- genV' :: GenConfig
- genVI' :: GenConfig
- genVII' :: GenConfig
- genVIII' :: GenConfig
- genIX' :: GenConfig
- genX' :: GenConfig
- genXI' :: GenConfig
- genI'' :: GenConfig
- genII'' :: GenConfig
- genIII'' :: GenConfig
- genIV'' :: GenConfig
- genV'' :: GenConfig
- genVI'' :: GenConfig
- genVII'' :: GenConfig
- genVIII'' :: GenConfig
- genIX'' :: GenConfig
- genX'' :: GenConfig
- genXI'' :: GenConfig
- cue :: CadenceState -> GenConfig -> GenConfig
- len :: Int -> GenConfig -> GenConfig
- seek :: String -> GenConfig -> IO ProgressionContext
- entropy :: Double -> GenConfig -> GenConfig
- tonal :: HarmonicContext -> GenConfig -> GenConfig
- relStrata :: String -> GenConfig -> GenConfig
- absStrata :: String -> GenConfig -> GenConfig
- sameBoost :: Double -> GenConfig -> GenConfig
- flipBoost :: Double -> GenConfig -> GenConfig
- triBoost :: Double -> GenConfig -> GenConfig
- attempt :: Int -> Int -> GenConfig -> GenConfig
- viability :: Double -> GenConfig -> GenConfig
- data GenConfig = GenConfig {
- _gcCue :: IO CadenceState
- _gcLen :: Int
- _gcSeek :: String
- _gcEntropy :: Double
- _gcTonal :: HarmonicContext
- _gcVerbosity :: Verbosity
- _gcMode :: GenMode
- _gcLenOverride :: Maybe Int
- _gcRelStrata :: Maybe [Int]
- _gcAbsStrata :: Maybe [StrataLabel]
- _gcBoostSame :: Double
- _gcBoostFlip :: Double
- _gcBoostTri :: Double
- _gcQuad :: Bool
- _gcMaxAttempts :: Int
- _gcViableTarget :: Int
- _gcViabilityFloor :: Double
- data GenMode
- data Verbosity
- defaultGenConfig :: GenConfig
- execGenConfig :: GenConfig -> IO Progression
- execGenConfigPC :: GenConfig -> IO ProgressionContext
- generate :: CadenceState -> Int -> Text -> Double -> HarmonicContext -> IO Progression
- generateWith :: GeneratorConfig -> CadenceState -> Int -> Text -> Double -> HarmonicContext -> IO Progression
- genWith :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- generate' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- genWith' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- generate'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- genWith'' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- genPrint :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genPrint' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genPrint'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genSilent' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genStandard :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genStandard' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genVerbose :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genVerbose' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- printDiagnostics :: Int -> GenerationDiagnostics -> IO ()
- data StepDiagnostic = StepDiagnostic {
- sdStepNumber :: Int
- sdPriorCadence :: String
- sdPriorRoot :: String
- sdPriorRootPC :: Int
- sdSelectedDbIntervals :: String
- sdSelectedDbMovement :: String
- sdSelectedDbFunctionality :: String
- sdGraphCount :: Int
- sdGraphTop6 :: [(String, Double)]
- sdFallbackCount :: Int
- sdFallbackTop6 :: [(String, Double, Double, Double, Double)]
- sdPoolSize :: Int
- sdEntropyUsed :: Double
- sdGammaIndex :: Int
- sdSelectedFrom :: String
- sdPosteriorRoot :: String
- sdPosteriorRootPC :: Int
- sdRenderedChord :: Maybe String
- sdTransformTrace :: Maybe TransformTrace
- sdAdvanceTrace :: Maybe AdvanceTrace
- sdTristrataIdx :: Maybe Int
- sdTristrata :: Maybe Tristrata
- sdStrataLabel :: Maybe StrataLabel
- sdMode :: Maybe Mode
- sdStrataChroma :: Maybe [PitchClass]
- sdModeChroma :: Maybe [PitchClass]
- sdSoftBoost :: Maybe Double
- sdHarmonicRootPC :: Maybe Int
- sdParentKey :: Maybe (PitchClass, ScaleFamily)
- sdModeResult :: Maybe ModeResult
- sdBarSpelling :: Maybe EnharmonicSpelling
- sdFusion :: Maybe FusionDiag
- data GenerationDiagnostics = GenerationDiagnostics {}
- data TransformTrace = TransformTrace {
- ttRawDbIntervals :: String
- ttRawDbMovement :: String
- ttRawDbFunctionality :: String
- ttRootPC :: Int
- ttRootNoteName :: String
- ttTones :: [Int]
- ttTransposedPitches :: [Int]
- ttNormalizedPs :: [Int]
- ttZeroForm :: [Int]
- ttDetectedRoot :: String
- ttFunctionality :: String
- ttFinalChord :: String
- ttStoredFunc :: String
- data AdvanceTrace = AdvanceTrace {}
- data HarmonicContext = HarmonicContext {
- _hcOvertones :: Text
- _hcKey :: Text
- _hcRoots :: Text
- _hcDrift :: Drift
- _hcInversionSpacing :: Int
- _hcPedal :: Text
- _hcTristrata :: Text
- harmonicContext :: Text -> Text -> Text -> HarmonicContext
- hContext :: HarmonicContext
- data Drift
- hcOvertones :: String -> HarmonicContext -> HarmonicContext
- hcKey :: String -> HarmonicContext -> HarmonicContext
- hcRoots :: String -> HarmonicContext -> HarmonicContext
- dissonant :: HarmonicContext -> HarmonicContext
- consonant :: HarmonicContext -> HarmonicContext
- invSkip :: Int -> HarmonicContext -> HarmonicContext
- hcPedal :: String -> HarmonicContext -> HarmonicContext
- hcTristrata :: String -> HarmonicContext -> HarmonicContext
- data GeneratorConfig = GeneratorConfig {}
- defaultConfig :: GeneratorConfig
- matchesContext :: HarmonicContext -> CadenceState -> Cadence -> Bool
- parseComposersWithOrder :: Text -> [(Text, Double)]
- makePortmanteau :: Text -> Maybe Text
- extractByPosition :: Int -> Int -> Text -> Double -> Text
- takeFromBeginning :: Text -> Double -> Text
- takeFromEnd :: Text -> Double -> Text
- takeFromMiddle :: Text -> Double -> Text
- printHeader :: Text -> Double -> HarmonicContext -> IO ()
Modifier-Based Generation API
Generation config with header + grid output (default).
s <- seek "*" $ gen s <- seek "*" $ cue start $ tonal ctx $ len 4 $ entropy 0.3 $ gen
Static grid: repeats the cue chord for len bars. No database access.
s <- seek "*" $ cue start $ len 4 $ genGrid
quad :: GenConfig -> GenConfig Source #
Switch on the gen4 family: every generated bar carries a 4-note chord.
Each step first selects a triad exactly as plain gen (graph + fallback,
R filters, gamma draw), then fuses in one more R-valid palette tone —
ranked consonant-first by full-chord dissonance, drawn at the same
entropy. The walk continues from the fused chord's most-consonant
embedded triad, so the added tone can reinterpret the harmony and steer
the next step, while every graph key stays corpus-shaped (generation
stays online). A triad cue is fused once so output is uniformly 4-note;
a 4-note lead' cue passes through untouched.
Composes with the usual modifier chain. Never applies to the strata
family (genP) — strata progressions stay 3-5-7.
s <- seek "*" $ cue start $ entropy 0.4 $ quad gen'
genFrom :: ProgressionContext -> Int -> Int -> GenConfig Source #
Regenerate a range of bars within an existing progression. The cue is inferred from the bar before the start position (wrapping).
FAMILY-AWARE: regeneration always produces uniform states of the family the source progression already is — families never mix.
pcProvenance = Just _— strata-aware path: regenerates all three layers + provenance in lockstep, with one-step lookahead at thee → e+1seam to keep the spliced bar sequence walk-graph valid underallowedNext.- uniform 4-note triad layer (gen4 source) — regen bars come out 4-note
(
_gcQuadset automatically). - uniform 3-note (gen source) — plain triad regen.
- hand-mixed cardinalities — regenerated as plain triads with a printed notice (hand-mixed material is the human aberration channel; regen does not amplify it).
s' <- seek "*" $ entropy 0.3 $ genFrom s 2 3
s' <- seek "*" $ cue start $ genFrom s 2 3 -- override inferred cue
s' <- seek "*" $ len 6 $ genFrom s 2 3 -- expand range
s' <- seek "*" $ genFrom' s 2 3 -- Standard per-step trace
s' <- seek "*" $ genFrom'' s 2 3 -- Verbose trace (+ scoreboard with attempt)
genFrom' :: ProgressionContext -> Int -> Int -> GenConfig Source #
Standard-verbosity alias of genFrom. Mirrors gen', genP' and genI'.
genFrom'' :: ProgressionContext -> Int -> Int -> GenConfig Source #
Verbose-verbosity alias of genFrom. Mirrors gen'', genP'' and genI''.
genP Paradigm (strata-first)
genP :: StrataLabel -> GenConfig Source #
Strata-first generation entrypoint. Seeded by a StrataLabel; produces
a ProgressionContext with distinct triad, strata, and mode layers and
pcProvenance = Just ….
s <- seek "none" $ cue start $ len 6 $ genP VI
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Silent-verbosity genP aliases, one per Roman numeral — genI pins the
starting tristrata to I, genII to II, and so on through genXI.
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ genI
Three verbosities throughout, by the usual prime convention: genI silent,
genI' standard, genI'' verbose.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
genVIII' :: GenConfig Source #
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Standard-verbosity Roman numeral aliases: per-step musical context plus
the grid. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
genIII'' :: GenConfig Source #
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
genVII'' :: GenConfig Source #
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
genVIII'' :: GenConfig Source #
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Verbose-verbosity Roman numeral aliases: full traces, the grid, and the
multi-attempt scoreboard when paired with attempt. See genI.
Generation Modifiers
cue :: CadenceState -> GenConfig -> GenConfig Source #
Set starting state.
s <- seek "*" $ cue start $ gen
len :: Int -> GenConfig -> GenConfig Source #
Set progression length (number of chords).
s <- seek "*" $ len 8 $ gen
seek :: String -> GenConfig -> IO ProgressionContext Source #
Set composer blend and execute. Terminal modifier — produces IO
ProgressionContext. For legacy gen-family configs, all three layers
duplicate the generated triad progression and pcProvenance is Nothing;
the genP paradigm produces distinct strata/mode layers with Just
provenance.
s <- seek "*" $ gen
s <- seek "bach:70 debussy:30" $ cue start $ len 4 $ gen
s <- seek "none" $ cue start $ len 6 $ genVI
entropy :: Double -> GenConfig -> GenConfig Source #
Set entropy in [0, 1] — mapped affinely to the gamma sampler's shape
(shape = 1 + entropy * 9). Higher values = more unusual choices.
s <- seek "*" $ entropy 0.5 $ gen
tonal :: HarmonicContext -> GenConfig -> GenConfig Source #
Set harmonic context (R constraints).
s <- seek "*" $ tonal (hcKey "0#" $ hContext) $ gen
relStrata :: String -> GenConfig -> GenConfig Source #
Per-bar position within the dynamically-changing active tristrata. Elements
∈ {1,2,3} cycle circularly. Sets _gcLenOverride to the parsed list length
so _gcLen doesn't need to be set explicitly; len applied later clears the
override (last-writer-wins).
s <- seek "none" $ relStrata "1 1 2 2 3 3" $ genVI -- 6 bars
absStrata :: String -> GenConfig -> GenConfig Source #
Per-bar absolute strata label across all tristratas. Elements are Roman
numerals I..XI cycling circularly. Sets _gcLenOverride to the parsed
list length.
s <- seek "none" $ absStrata "I V X" $ genI -- 3 bars
sameBoost :: Double -> GenConfig -> GenConfig Source #
Override the same-strata continuity boost multiplier. Values below 1.0 favour candidates whose strata matches the previous bar's. Default 0.90. Pass 1.0 to disable the bias.
s <- seek "none" $ sameBoost 0.5 $ genVI -- strong same-strata pull
flipBoost :: Double -> GenConfig -> GenConfig Source #
Override the flip-flop boost multiplier (candidates matching the grandparent strata when the current /= previous). Default 0.80.
triBoost :: Double -> GenConfig -> GenConfig Source #
Override the same-tristrata continuity boost multiplier. Values below 1.0 favour candidates whose active tristrata matches the previous bar's. Default 0.70 (strongest of the three).
attempt :: Int -> Int -> GenConfig -> GenConfig Source #
Run multi-attempt rank-and-select generation: produce up to maxAttempts
candidate progressions, stop early once viableTarget viable attempts
(all bars ModeOk) have been collected, then return the highest-scoring
one. Scoring blends root motion, voice leading, and mode validity via
defaultWeightsOffline.
s <- seek "*" $ attempt 3 24 $ entropy 0.4 $ gen -- best of up to 24
Defaults are attempt 1 1 — i.e. the modifier is a no-op when omitted,
preserving legacy single-pass behaviour.
viability :: Double -> GenConfig -> GenConfig Source #
Set the viability quality floor used by attempt. An attempt is
viable iff psModeValidity >= 1.0 (structural invariant) and
totalScore >= floor. Default is 0.5; passing 0.0 recovers the
original structural-only viability.
s <- seek "*" $ viability 0.65 $ attempt 3 24 $ gen
Tune downward if attempt N K frequently fails to collect N viable
within K (raise K or lower the floor); tune upward if K is being hit
consistently with mediocre-quality picks (lower K or raise the floor).
Generation Configuration
Configuration for the modifier-based generation API.
Built via modifier chains:
s <- seek "*" $ cue start $ tonal ctx $ len 4 $ entropy 0.3 $ gen
Constructors
| GenConfig | |
Fields
| |
Generation mode.
Constructors
| Fresh | Standard gen (new progression) |
| FromProg Progression !Int !Int | Regenerate range in existing triad layer |
| FromProgPC ProgressionContext !Int !Int | Regenerate range in a strata-aware context (preserves all three layers + provenance) |
| GridMode | Static repetition of cue chord |
| StrataMode StrataLabel |
|
defaultGenConfig :: GenConfig Source #
Default generation configuration.
cue: random root, major triad len: 4 seek: "*" (all composers) entropy: 0.2 tonal: hContext (chromatic)
execGenConfig :: GenConfig -> IO Progression Source #
Execute a GenConfig, producing a progression.
Thin wrapper that calls execGenConfigWithDiag (pure compute) and then
emits the appropriate diagnostics + header + grid via emitFinalised.
Single-pass callers see byte-identical output to today.
execGenConfigPC :: GenConfig -> IO ProgressionContext Source #
Terminal executor producing a ProgressionContext.
When _gcMaxAttempts > 1, dispatches through generateBest for
rank-and-select multi-attempt generation; otherwise runs a single pass.
The single-pass dispatch reads _gcMode: StrataMode runs the strata-
first traversal producing distinct layers; all other modes fall through
to execGenConfig and wrap the resulting triad Progression via
fromProgression.
Positional Generation (legacy/internal)
Arguments
| :: CadenceState | Starting state (root + quality) |
| -> Int | Number of chords |
| -> Text | Composer blend string |
| -> Double | Entropy (gamma shape) |
| -> HarmonicContext | R constraints |
| -> IO Progression |
Generate a harmonic progression from a starting state.
Arguments: * start: Initial CadenceState (defines starting root and quality) * len: Number of chords to generate * composerStr: Composer blend string ("bach:70 debussy:30") * entropy: Gamma shape parameter (higher = more unusual choices) * context: HarmonicContext filters (R constraints)
Algorithm: 1. Parse composer weights 2. For each step: query graph, apply R filter, apply E weights, gamma select 3. Apply voice leading optimization to the complete chain
Returns: Progression type (Phase B)
generateWith :: GeneratorConfig -> CadenceState -> Int -> Text -> Double -> HarmonicContext -> IO Progression Source #
Generate with custom configuration
Simplified algorithm: 1. Start with user-provided CadenceState 2. For each step: build candidate pool, gamma-select next cadence 3. Candidate pool = graph transitions (filtered) + consonanceFallback (unlimited — the pool is never truncated)
genWith :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
String-friendly generateWith for TidalCycles live coding.
Positional Generation with Diagnostics
generate' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics) Source #
Generate a progression returning both result and diagnostics (internal).
This is the core internal function that generates progressions and collects standard-level diagnostics (per-step candidate pools, selections, rendered chords).
For most users, use the unified interface instead:
* genSilent - for silent generation
* genStandard - for standard diagnostics
* genVerbose - for verbose diagnostics
genWith' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics) Source #
Generate with custom configuration, returning diagnostics tuple (internal).
generate'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics) Source #
Generate a progression with maximum diagnostic traces (internal).
Like 'generate'' but populates full transform and advance traces for debugging. This function collects: * All standard diagnostics (per-step candidate pools, selections) * Full transform traces (DB intervals, transposition, normalization, zero-form) * Full advance traces (root motion PC arithmetic, enharmonic spelling)
This is SLOWER than 'generate'' due to extra tracing computation. Use only for debugging chord name discrepancies or voice leading issues.
genWith'' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics) Source #
Generate with custom configuration and maximum diagnostics (internal).
Positional Generation with Print Output
genPrint :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Positional generate with header + grid output (internal).
genPrint' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Positional generate with compact musical summary (internal).
genPrint'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Positional generate with verbose traces (internal).
Unified Positional Interface
genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate a progression with NO diagnostic output (verbosity 0 - silent mode).
genSilent' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Silent mode with custom GeneratorConfig.
genStandard :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate a progression with STANDARD diagnostic output (verbosity 1).
genStandard' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Standard diagnostics with custom GeneratorConfig.
genVerbose :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate a progression with VERBOSE diagnostic output (verbosity 2).
genVerbose' :: GeneratorConfig -> CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Verbose diagnostics with custom GeneratorConfig.
printDiagnostics :: Int -> GenerationDiagnostics -> IO () Source #
Print diagnostics collected during generation.
Selects output level based on verbosity parameter:
- 0 - Silent
- No output
- 1 - Standard
- Per-step candidate pools, selections, rendered chords
- 2 - Verbose
- Standard plus transform and advance traces
Useful for manually reprinting diagnostics after extraction from tuple, or batch processing multiple results at different verbosity levels.
Diagnostics Types
data StepDiagnostic Source #
Diagnostic information for a single generation step
Constructors
| StepDiagnostic | |
Fields
| |
Instances
| Show StepDiagnostic Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> StepDiagnostic -> ShowS # show :: StepDiagnostic -> String # showList :: [StepDiagnostic] -> ShowS # | |
| Eq StepDiagnostic Source # | |
Defined in Harmonic.Framework.Builder.Types Methods (==) :: StepDiagnostic -> StepDiagnostic -> Bool # (/=) :: StepDiagnostic -> StepDiagnostic -> Bool # | |
data GenerationDiagnostics Source #
Complete diagnostics for a generation run
Constructors
| GenerationDiagnostics | |
Fields
| |
Instances
| Show GenerationDiagnostics Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> GenerationDiagnostics -> ShowS # show :: GenerationDiagnostics -> String # showList :: [GenerationDiagnostics] -> ShowS # | |
data TransformTrace Source #
Transform trace captures intermediate values in fromCadenceState → toTriad pipeline. Used for maximum verbosity debugging (gen''). Includes raw DB data plus all transformation stages.
Constructors
| TransformTrace | |
Fields
| |
Instances
| Show TransformTrace Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> TransformTrace -> ShowS # show :: TransformTrace -> String # showList :: [TransformTrace] -> ShowS # | |
| Eq TransformTrace Source # | |
Defined in Harmonic.Framework.Builder.Types Methods (==) :: TransformTrace -> TransformTrace -> Bool # (/=) :: TransformTrace -> TransformTrace -> Bool # | |
data AdvanceTrace Source #
Advance trace captures intermediate values in root advancement. Used for maximum verbosity debugging (gen'').
Constructors
| AdvanceTrace | |
Fields
| |
Instances
| Show AdvanceTrace Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> AdvanceTrace -> ShowS # show :: AdvanceTrace -> String # showList :: [AdvanceTrace] -> ShowS # | |
| Eq AdvanceTrace Source # | |
Defined in Harmonic.Framework.Builder.Types | |
Harmonic Context (R constraints)
data HarmonicContext Source #
Harmonic context defines the Rules (R) that constrain the generative space.
These filters are applied BEFORE database evaluation (R in R→E→T pipeline), limiting which cadences can even be considered as candidates.
Three-part filtering system: * overtones: Pitch candidate set (e.g., "E A D G" for bass tuning overtones) * key: Key filter applied to candidates (e.g., C, "#", "bb" for key signature) * roots: Root/bass candidate set (e.g., "E F# G" for valid root notes)
Filters use "*" as wildcard (match all). Format matches legacy Overtone.hs notation.
Constructors
| HarmonicContext | |
Fields
| |
Instances
| Show HarmonicContext Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> HarmonicContext -> ShowS # show :: HarmonicContext -> String # showList :: [HarmonicContext] -> ShowS # | |
| Eq HarmonicContext Source # | |
Defined in Harmonic.Framework.Builder.Types Methods (==) :: HarmonicContext -> HarmonicContext -> Bool # (/=) :: HarmonicContext -> HarmonicContext -> Bool # | |
harmonicContext :: Text -> Text -> Text -> HarmonicContext Source #
Constructor for HarmonicContext.
Arguments: * overtones: Pitch set filter ("E A D G", C, "*") * key: Key signature filter (C, "#", "bb", Am, "*") * roots: Root notes filter ("E F# G", "1#", "*")
Example: harmonicContext "*" "*" "*" -- No filtering (all candidates) harmonicContext "E A D G" C "*" -- Bass tuning, C major key harmonicContext "*" "#" "E G" -- G major key, E/G roots only
hContext :: HarmonicContext Source #
Default harmonic context for Tidal live coding: all wildcards (chromatic).
Named hContext to avoid collision with TidalCycles' EventF.context field.
Use modifier functions to constrain the context:
ctx = invSkip 2
$ consonant
$ hcRoots "C E G"
$ hcKey "0#"
$ hcOvertones "E A D G"
$ hContext
Context Modifiers
Direction of dissonance drift across a generated progression.
When applied to a HarmonicContext, the generation engine filters the candidate pool at each step so that only chords with equal or greater (Dissonant) or equal or lesser (Consonant) dissonance than the current chord are preferred. Free imposes no constraint (default).
Advisory, not hard: if the drift predicate would empty the pool at a
step, the filter relaxes and that step proceeds unconstrained rather than
reaching an absorbing state (applyDriftFilter in Builder.Core). Note the
predicate is dissonanceScore — an evaluation function acting as a
filter, the documented E-inside-R leak (see ARCHITECTURE §2).
Instances
hcOvertones :: String -> HarmonicContext -> HarmonicContext Source #
Set overtone filter. Default: "*" (all pitches).
hcOvertones "E A D G" $ hContext — bass tuning overtones
hcKey :: String -> HarmonicContext -> HarmonicContext Source #
Set key filter. Default: "*" (chromatic).
hcKey "0#" $ hContext — C major
hcRoots :: String -> HarmonicContext -> HarmonicContext Source #
Set roots/bass filter. Default: "*" (all roots).
hcRoots "C E G" $ hContext — only C, E, G as bass notes
dissonant :: HarmonicContext -> HarmonicContext Source #
Modify context to trend toward increasing dissonance. Each subsequent chord should have dissonance >= the current chord; advisory — relaxes at any step where it would empty the pool.
consonant :: HarmonicContext -> HarmonicContext Source #
Modify context to trend toward decreasing dissonance. Each subsequent chord should have dissonance <= the current chord; advisory — relaxes at any step where it would empty the pool.
invSkip :: Int -> HarmonicContext -> HarmonicContext Source #
Set minimum number of non-inversion states between inversions.
invSkip 0 allows inversions at any step (default, current behaviour).
invSkip 1 requires at least 1 non-inversion between inversions.
invSkip 2 requires at least 2 non-inversions between inversions.
The starting state counts toward the counter (a non-inversion start
means the first generated step may already be an inversion with invSkip 1).
Advisory, not hard: if excluding inversions would empty the pool at a step, the spacing constraint relaxes for that step rather than halting generation.
hcPedal :: String -> HarmonicContext -> HarmonicContext Source #
Require specific pitch classes to be present in every generated chord.
Tokens are note names (C, G, Bb). A trailing ? marks a tone
as preferred rather than required — it is applied when it does not reduce
the candidate pool below a minimum viable size, and relaxed otherwise.
Advisory at the limit: the relaxation chain is preferred → required →
unfiltered (applyPedalFilter in Builder.Core), so even required tones
are dropped as a last resort at a step where enforcing them would leave no
candidates — generation never reaches an absorbing state through a pedal
constraint.
hcPedal C $ hContext — C must appear in every chord
hcPedal "C G" $ hContext — C and G must both appear
hcPedal "C G?" $ hContext — C required, G preferred
hcTristrata :: String -> HarmonicContext -> HarmonicContext Source #
Restrict the active tristrata pool for genP.
"" (default) — all 12 tristrata allowed.
"5" — lock to a single tristrata (here #5, IV-VI-X).
"1 2 5" — whitelist multiple tristrata.
"[1,2,5]" — bracket form accepted.
Parsed via parseTristrataList; unknown tokens are silently discarded.
Configuration
data GeneratorConfig Source #
Configuration for the progression generator.
gcQuad switches on the gen4 family: after each triad selection, the
step fuses one R-valid palette tone into the chord (4-note output) and
the walk continues from the fused chord's most-consonant embedded triad
(see fuseState).
Historical note: the former gcPoolSize field was removed (2026-08-19)
because no generation path ever read it — the candidate pool is
deliberately unlimited (full 660-candidate fallback; see
Core).
Constructors
| GeneratorConfig | |
Instances
| Show GeneratorConfig Source # | |
Defined in Harmonic.Framework.Builder.Types Methods showsPrec :: Int -> GeneratorConfig -> ShowS # show :: GeneratorConfig -> String # showList :: [GeneratorConfig] -> ShowS # | |
| Eq GeneratorConfig Source # | |
Defined in Harmonic.Framework.Builder.Types Methods (==) :: GeneratorConfig -> GeneratorConfig -> Bool # (/=) :: GeneratorConfig -> GeneratorConfig -> Bool # | |
defaultConfig :: GeneratorConfig Source #
Default configuration.
Internal functions (exposed for testing)
matchesContext :: HarmonicContext -> CadenceState -> Cadence -> Bool Source #
Check if a cadence matches the harmonic context filters.
Filter logic (matching legacy behavior): 1. Compute effective overtones: key-filtered overtone palette 2. All chord pitches must be in effective overtones 3. Root must be in resolved roots (handles "key"/"tones" options)
parseComposersWithOrder :: Text -> [(Text, Double)] Source #
Parse composer string preserving input order Returns list of (name, normalized weight) tuples in order of appearance
makePortmanteau :: Text -> Maybe Text Source #
Generate portmanteau from composer string (preserving input order) Takes weighted portions from beginning/middle/end based on POSITION Returns Nothing for "*", empty input, or "none" (offline mode)
extractByPosition :: Int -> Int -> Text -> Double -> Text Source #
Extract characters from name based on position in list
takeFromBeginning :: Text -> Double -> Text Source #
Take a leading fragment of a name, sized by the composer's blend weight. Used for the first name in a blend. Always yields at least one character.
takeFromEnd :: Text -> Double -> Text Source #
Take a trailing fragment, sized by blend weight. Used for the last name in a blend, so the portmanteau ends on a real word ending.
takeFromMiddle :: Text -> Double -> Text Source #
Take a centred fragment, sized by blend weight. Used for names between the first and last in a blend. Ties favour earlier characters.
printHeader :: Text -> Double -> HarmonicContext -> IO () Source #
Print generation header based on composer selection Takes raw Text instead of parsed ComposerWeights, plus entropy