| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Harmonic.Lib
Description
Overview
One import gives a performance script everything it needs: the generative engine, the music-theory primitives it reasons over, and the TidalCycles bridge that turns its output into sound.
import Harmonic.Lib
Quick start
Generation reads as a chain of modifiers applied to a generator. Each modifier narrows what the engine may choose; the generator at the end of the chain runs the walk:
tempo = 87
ctx = invSkip 1
$ hcOvertones "E A D G"
$ hcKey "2#"
$ hContext
start <- lead "C maj"
s <- seek "*" $ cue start $ tonal ctx $ len 8 $ entropy 0.4 $ attempt 3 12 $ gen
seek picks the corpus — "*" for all composers, "bach" for one,
"bach:30 debussy:70" for a weighted blend, "none" to run offline with
no Neo4j. attempt generates several candidates and keeps the best.
The result is then played by describing a form and handing it to instruments:
form = [ at 0 1.0 1.0 s ]
do
let k = iK tempo form (warp "[1 2 3 4]/4")
mapM_ id [ hush, setbpm tempo
, p "strings" $ stack
[ violin1 T (0,1) k voiceLines flow Soprano
, cello T (0,1) k voiceLines flow Tenor8vb
, contrabass T (0,1) k voiceLines grid Bass8vb
]
]
Verbosity
Every generator has three tiers, marked by the prime suffix — the same convention used throughout the library for tiers and variants:
gen— the chord grid onlygen'— per-step musical context and the gridgen''— full traces, the grid, and the multi-attempt scoreboard
The tiers have identical types, so switching verbosity never changes the
surrounding code. The same holds for genP / genP' / genP'',
genFrom / genFrom' / genFrom'', and the Roman numeral aliases.
For time-unit pairs the prime means the unit: unprimed = bars, primed =
seconds (display / display', mark / mark'). Form nodes are a
different axis — rh / at name the unit and the prime is the Snap
transition.
Where to go next
- Harmonic.Framework.Builder — the generation engine and every modifier
- Harmonic.Interface.Tidal.Orchestra — the 15 orchestral instruments
- Harmonic.Interface.Tidal.Arranger — voicing strategies and rearranging
- Harmonic.Interface.Tidal.Form — form and kinetics
Legacy positional interface
Predating the modifier chain, genSilent, genStandard and genVerbose
take their arguments positionally and share one signature:
genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
They remain supported, but note that initCadenceState silently truncates
chords of more than three pitch classes to a triad; lead is the
better-behaved way to build a starting state.
Synopsis
- gen :: GenConfig
- gen' :: GenConfig
- gen'' :: GenConfig
- genGrid :: GenConfig
- genGrid' :: GenConfig
- genGrid'' :: GenConfig
- genFrom :: ProgressionContext -> Int -> Int -> GenConfig
- genFrom' :: ProgressionContext -> Int -> Int -> GenConfig
- genFrom'' :: ProgressionContext -> Int -> Int -> GenConfig
- chordscale :: ProgressionContext -> ProgressionContext
- renderChordscaleReport :: ProgressionContext -> Maybe String
- chordscaleReport :: ProgressionContext -> IO ()
- genE :: GenConfig
- genE' :: GenConfig
- genE'' :: GenConfig
- polyLayerViews :: ProgressionContext -> Maybe [(String, Progression)]
- genEReport :: ProgressionContext -> IO ()
- genJ :: GenConfig
- genJ' :: GenConfig
- genJ'' :: GenConfig
- genP :: StrataLabel -> GenConfig
- genP' :: StrataLabel -> GenConfig
- genP'' :: StrataLabel -> GenConfig
- renderTristrataReport :: ProgressionContext -> Maybe String
- genPReport :: ProgressionContext -> IO ()
- 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
- steer :: Double -> GenConfig -> GenConfig
- data GenConfig = GenConfig {
- _gcCue :: IO CadenceState
- _gcCueExplicit :: Bool
- _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
- _gcSteer :: Double
- _gcMaxAttempts :: Int
- _gcViableTarget :: Int
- _gcViabilityFloor :: Double
- data GenMode
- data Verbosity
- defaultGenConfig :: GenConfig
- execGenConfig :: GenConfig -> IO Progression
- execGenConfigPC :: GenConfig -> IO ProgressionContext
- genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genStandard :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genVerbose :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genPrint :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genPrint' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- genPrint'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression
- data HarmonicContext = HarmonicContext {
- _hcOvertones :: Text
- _hcKey :: Text
- _hcRoots :: Text
- _hcDrift :: Drift
- _hcInversionSpacing :: Int
- _hcPedal :: Text
- _hcTristrata :: Text
- harmonicContext :: String -> String -> String -> 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
- module Harmonic.Rules.Types.Pitch
- data Movement
- = Asc PitchClass
- | Desc PitchClass
- | Unison
- | Tritone
- | Empty
- type Functionality = String
- toFunctionality :: [PitchClass] -> Functionality
- toFunctionalityChord :: [PitchClass] -> Functionality
- data Chord = Chord {}
- data Cadence = Cadence {}
- toMovement :: PitchClass -> PitchClass -> Movement
- fromMovement :: Movement -> PitchClass
- data EnharmonicSpelling
- data EnharmonicPreference
- enharmonicPreference :: PitchClass -> EnharmonicPreference
- selectEnharm :: EnharmonicSpelling -> PitchClass -> PitchClass -> EnharmonicSpelling
- defaultEnharm :: PitchClass -> EnharmonicSpelling
- spellingToPreference :: EnharmonicSpelling -> EnharmonicPreference
- enharmonicFunc :: EnharmonicSpelling -> PitchClass -> NoteName
- inferSpelling :: [Int] -> EnharmonicSpelling
- noteNameImpliesSpelling :: NoteName -> Maybe EnharmonicSpelling
- isAmbiguousPattern :: [Int] -> Bool
- data ChordState = ChordState {
- stateChord :: Chord
- stateRoot :: NoteName
- data CadenceState = CadenceState {}
- toTriad :: (PitchClass -> NoteName) -> [Int] -> Chord
- flatTriad :: [Int] -> Chord
- sharpTriad :: [Int] -> Chord
- toChord :: (PitchClass -> NoteName) -> [Int] -> Chord
- flatChord :: [Int] -> Chord
- sharpChord :: [Int] -> Chord
- toCadence :: (Chord, Chord) -> Cadence
- fromChordState :: ChordState -> Chord
- fromCadenceState :: CadenceState -> Chord
- initCadenceState :: Int -> String -> [Int] -> CadenceState
- mkCadenceStatePCs :: NoteName -> Movement -> [Int] -> CadenceState
- walkTriadCadence :: Cadence -> Cadence
- walkTriadState :: CadenceState -> CadenceState
- data ToTriadTrace = ToTriadTrace {
- tttRawDbIntervals :: String
- tttRawDbMovement :: String
- tttRawDbFunctionality :: String
- tttRootPC :: Int
- tttRootNoteName :: String
- tttTones :: [Int]
- tttTransposedPitches :: [Int]
- tttNormalizedPs :: [Int]
- tttZeroForm :: [Int]
- tttDetectedRoot :: String
- tttFunctionality :: String
- tttFinalChord :: String
- tttStoredFunc :: String
- fromCadenceStateTraced :: CadenceState -> (Chord, ToTriadTrace)
- constructCadence :: (String, String) -> Cadence
- corpusFunctionality :: [PitchClass] -> Functionality
- deconstructCadence :: Cadence -> (Movement, [PitchClass])
- rootNote :: Chord -> NoteName
- inversions :: [PitchClass] -> [[PitchClass]]
- isInversion :: Cadence -> Bool
- normalForm :: [PitchClass] -> [PitchClass]
- primeForm :: [PitchClass] -> [PitchClass]
- zeroFormPC :: [PitchClass] -> [PitchClass]
- module Harmonic.Evaluation.Scoring.Dissonance
- module Harmonic.Rules.Constraints.Overtone
- voiceLeadingCost :: [Int] -> [Int] -> Int
- totalCost :: [[Int]] -> Int
- cyclicCost :: [[Int]] -> Int
- voiceMovement :: Int -> Int -> Int
- minimalMovement :: PitchClass -> PitchClass -> Int
- allVoicings :: [Int] -> [[Int]]
- initialCompact :: Int -> [Int] -> [Int]
- solveRoot :: [[Int]] -> [[Int]]
- solveFlow :: [[Int]] -> [[Int]]
- module Harmonic.Rules.Types.Progression
- module Harmonic.Rules.Types.Scale
- module Harmonic.Rules.Types.ProgressionContext
- module Harmonic.Traversal.Probabilistic
- overtones :: String -> [PitchClass]
- key :: String -> [PitchClass]
- funds :: String -> [PitchClass]
- tuning :: String -> [PitchClass]
- wildcard :: String -> Bool
- parseOvertones :: Text -> [PitchClass]
- parseKey :: Text -> [PitchClass]
- parseFunds :: Text -> [PitchClass]
- parseTuning :: Text -> [PitchClass]
- isWildcard :: Text -> Bool
- parseTuningNamed :: Text -> [(String, Int)]
- module Harmonic.Evaluation.Database.Query
- connectNeo4j :: IO DbConn
- module Harmonic.Rules.Import.CSV
- module Harmonic.Rules.Import.Transform
- type VoiceFunction = Progression -> [[Int]]
- voiceRange :: (Int, Int) -> Pattern Int -> Pattern Int
- arrange :: (Double, Double) -> IK -> (Int, Int) -> Layer -> VoiceFunction -> (Progression -> Progression) -> [Pattern Int] -> Pattern ValueMap
- arrange' :: (Double, Double) -> IK -> (Int, Int) -> Layer -> VoiceFunction -> (Progression -> Progression) -> [Pattern Int] -> Pattern ValueMap
- parallel :: Pattern Note -> ControlPattern -> ControlPattern
- harmonicState :: Layer -> IK -> ControlPattern
- warp :: String -> Pattern Int
- rep :: ProgressionContext -> Pattern Time -> Pattern Int
- lookupChordAt :: Time -> Pattern Int -> Int
- lookupChord :: ProgressionContext -> Int -> Chord
- lookupProgression :: ProgressionContext -> Pattern Int -> Pattern [Int]
- overlapF :: Int -> Progression -> Progression
- type Tuning = [(NoteName, Int)]
- ecbc :: Tuning
- composite :: (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap
- harmonics :: NoteName -> (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap
- overtoneMap :: ProgressionContext -> Layer -> [Pattern Int] -> IO ()
- overtoneTable :: [Int] -> [(Int, Int, [(NoteName, Int)])]
- compositeSeries :: Tuning -> [Int]
- stringPitches :: (NoteName, Int) -> [Int]
- partialOffset :: Int -> Int
- data FormNode = FormNode {}
- data FormTime
- data Transition
- data Kinetics = Kinetics {}
- type IK = (Kinetics, Pattern Int)
- at :: Double -> Double -> Double -> ProgressionContext -> FormNode
- at' :: Double -> Double -> Double -> ProgressionContext -> FormNode
- rh :: Double -> Double -> Double -> ProgressionContext -> FormNode
- rh' :: Double -> Double -> Double -> ProgressionContext -> FormNode
- iK :: Double -> [FormNode] -> Pattern Int -> IK
- lK :: Pattern Double -> Pattern Double -> ProgressionContext -> Pattern Int -> IK
- formK :: Double -> [FormNode] -> Kinetics
- ki :: (Double, Double) -> IK -> Pattern a -> Pattern a
- slate :: (Double, Double) -> IK -> [Pattern a] -> Pattern a
- withForm :: IK -> (ProgressionContext -> Pattern ValueMap) -> Pattern ValueMap
- isolate :: (Int, Int) -> IK -> IK
- isolate' :: (Int, Int) -> IK -> IK
- mark :: Double -> IK -> [Pattern a] -> Pattern a
- mark' :: Double -> IK -> [Pattern a] -> Pattern a
- markbar :: Double -> IK -> [Pattern a] -> Pattern a
- marksec :: Double -> IK -> [Pattern a] -> Pattern a
- markAt :: FormTime -> IK -> [Pattern a] -> Pattern a
- rotate :: Int -> ProgressionContext -> ProgressionContext
- excerpt :: Int -> Int -> ProgressionContext -> ProgressionContext
- insert :: CadenceState -> Int -> ProgressionContext -> ProgressionContext
- switch :: Int -> Int -> ProgressionContext -> ProgressionContext
- clone :: Int -> Int -> ProgressionContext -> ProgressionContext
- extract :: Int -> ProgressionContext -> CadenceState
- transposeP :: Int -> ProgressionContext -> ProgressionContext
- reverse :: ProgressionContext -> ProgressionContext
- fuse :: [ProgressionContext] -> ProgressionContext
- fuse2 :: ProgressionContext -> ProgressionContext -> ProgressionContext
- interleave :: ProgressionContext -> ProgressionContext -> ProgressionContext
- expandP :: Int -> ProgressionContext -> ProgressionContext
- progOverlap :: Int -> Progression -> Progression
- progOverlapF :: Int -> Progression -> Progression
- progOverlapB :: Int -> Progression -> Progression
- grid :: Progression -> [[Int]]
- flow :: Progression -> [[Int]]
- lite :: Progression -> [[Int]]
- literal :: Progression -> [[Int]]
- root :: Progression -> [[Int]]
- fromChords :: [[Int]] -> ProgressionContext
- prog :: [[Int]] -> ProgressionContext
- subKick :: Pattern Double -> IK -> (Progression -> [[Int]]) -> (Time, String, String, String) -> Pattern ValueMap
- holdToNext :: Time -> Pattern Bool -> Pattern a -> Pattern a
- fund :: Progression -> [[Int]]
- noteoff :: Time -> Pattern Bool -> Pattern Bool
- son32 :: Pattern Bool
- son23 :: Pattern Bool
- rumba32 :: Pattern Bool
- rumba23 :: Pattern Bool
- bossa32 :: Pattern Bool
- bossa23 :: Pattern Bool
- bellpat32 :: Pattern Bool
- bellpat23 :: Pattern Bool
- lineHarmony :: Pattern Double -> IK -> VoiceFunction -> [Pattern Int] -> Pattern ValueMap
- data ScaleSource
- = ExplicitScale [[Int]]
- | HarmonyAsScale Progression
- | HarmonyWithOverlap Progression (Int -> Progression -> Progression)
- melodyStateFrom :: ScaleSource -> Progression
- lead :: String -> IO CadenceState
- lead' :: String -> IO CadenceState
- leadJ :: String -> IO CadenceState
- parseLeadTokens :: String -> [LeadToken]
- data LeadToken
- module Harmonic.Interface.Tidal.Instruments
- module Harmonic.Interface.Tidal.Orchestra
- module Harmonic.Interface.Tidal.Utils
- module Harmonic.Interface.Tidal.Motif
- module Harmonic.Interface.Tidal.Display
- module Harmonic.Interface.Tidal.Devices.S1
- module Harmonic.Interface.Tidal.Devices.P6
- module Harmonic.Interface.Tidal.Devices.RC10R
- module Harmonic.Interface.Tidal.Devices.JV1010
- module Harmonic.Config
- generate :: CadenceState -> Int -> Text -> Double -> HarmonicContext -> IO Progression
- generateWith :: CadenceState -> Int -> Text -> Double -> HarmonicContext -> IO Progression
- generate' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- generate'' :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO (Progression, GenerationDiagnostics)
- 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
- sdPoly :: Maybe PolyDiag
- 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 {}
Primary interface for live coding
The modifier-based generation API. Each generator comes in three verbosity tiers, marked by the prime suffix:
gen— header + grid outputgen'— compact summarygen''— verbose traces
Modifiers compose right-to-left onto a generator:
s <- seek "*" $ attempt 3 12 $ entropy 0.4 $ gen
Triadic generation
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
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).
A cue override applies to every family EXCEPT FStrata: the strata
regen must seed from the source's own bar to keep the walk-graph seam
valid, so it ignores the override.
FAMILY-AWARE: regeneration produces states of the family the source
progression already is — families never mix. The one downgrade is
deliberate: hand-built extended (FExtended) sources regenerate as
triads (with a printed notice), so the result re-infers as mixed
material rather than pretending the fusion family still exists.
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.FPoly(genE source) — polytonal path: regenerates the foundation range plus both partner chains, seeded from the kept partner bars before the range; the S/M labelling of the source is preserved (chains are never reordered by a partial regen).- uniform 4-note hand-built material — regenerated bars come back as plain triads with a printed notice.
- 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''.
Chordscale layers (gen / genJ)
gen and genJ contexts carry derived S/M layers: whole-progression
key-area analysis assigns every bar a key (major, or composite minor)
and realises it as the mode on the bar's root (M, 7 tones) and the
best-fitting anhemitonic pentatonic (S, 5 tones). Automatic at
generation; apply chordscale by hand to lead'-built contexts.
chordscaleReport prints the per-bar key / form / mode / pentatonic.
chordscale :: ProgressionContext -> ProgressionContext Source #
Fill the S (pentatonic) and M (mode) layers of a context from the
key-area analysis of its triad layer. Identity on FStrata and
FPoly contexts, whose layers are already meaningful. Layer bars are
built exactly like genP's aux layers (Unison movement, chroma
expressed as intervals from the bar root), so they voice through the
same chroma engine (strataModeFlow)
and print as mode names via the
7-PC display path. The M bar is rooted on the bar's harmonic root (the
mode OF the bar — every tier of the override ladder contains it); the S
bar is rooted on the PENTATONIC's own root — a pentatonic legitimately
excludes the chord root (the pent-on-the-fifth over a maj7 does so by
design), and the set is named from its own root. (Bar roots seed only
bar 0's lattice in the chroma engine; pattern indices then track their
lattice slot, not the bar root.)
Provenance stays Nothing (provenance is strata-specific and gates
strata regen / scoring).
The derived bars are forced to normal form as part of the result's
WHNF: ProgressionContext fields are lazy, and the first thing to
inspect an unforced layer can be the derived Eq inside an arrange /
lineHarmony cache lookup — i.e. the audio thread. Generated contexts
are forced when the generator prints them, so their analysis always
lands at generation time; a hand-applied chordscale evaluates at its
own first use — print it (or run
chordscaleReport on it) at build time to
keep the work off the performance path.
chordscaleReport :: ProgressionContext -> IO () Source #
Live-coding helper: print the chordscale report for a gen / genJ
result alongside the standard Show output.
The genE paradigm (polytonal)
Three simultaneous triad progressions from one walk: a foundation
(T) plus two partner chains (S/M) sharing 2 pitch classes with it per
bar and unioning to 5. Read pairs, the pentad and the pivot tones
through the Layer selectors (TS/TM/SM/TSM/PT); genEReport prints
every view. See USER_GUIDE section 20 and documents/POLYTONAL.md.
The genE paradigm: polytonal three-layer generation. The T layer is a
foundation walk byte-identical to gen (all R constraints apply to it
alone — the foundation owns the bass); the S/M layers are partner triad
chains, each a corpus-valid walk of its own, sharing exactly 2 pitch
classes with the foundation per bar and unioning to exactly 5. The
traversal chooses freely per bar between the two admitted geometries —
common-dyad (every layer pair sounds 4 tones) and base-anchored (pairs
with the foundation sound 4, S+M sounds the pentad). Partners honour
key/roots/overtones but never direction specs or strata machinery.
Combine layers at the pattern surface via the Layer selectors
(TS/TM/SM/TSM/PT). Cues are triadic.
s <- seek "*" $ len 8 $ entropy 0.3 $ genE'
polyLayerViews :: ProgressionContext -> Maybe [(String, Progression)] Source #
Every layer view of a polytonal context, labelled: single layers,
pair unions, the pentad, the pivot tones. Returns Nothing for other
families — the labels are genE-specific (partner chains, pentad, pivot
dyad). gen/genJ layer content is printed by
chordscaleReport, genP's by
genPReport.
genEReport :: ProgressionContext -> IO () Source #
Live-coding helper: print every layer view of a polytonal context as progression grids. Use at the REPL between takes to see what each pattern-level layer selection will sound.
The genJ paradigm (jazz Change graph)
Generation over the jazz corpus graph: variable-arity leadsheet
harmony (3-6 tones), cued by leadJ, walked by the same modifier
chain as every family. See USER_GUIDE section 21.
Generate over the jazz (Change) graph: the genJ family. Same
modifier chain as gen (seek "*" $ cue start $ len 8 $ entropy 0.3 $ genJ);
the seek spec resolves against BOTH corpora (see
Harmonic.Framework.Builder.JazzGen for the full semantics: jazz
names blend, classical names steer via the steer dial, "*" walks
the whole corpus, "none" is refused — the jazz graph has no offline
mode). Progressions carry variable-arity chords (3-6 tones) straight
from the corpus vocabulary, stamped FJazz so genFrom
regenerates them jazz-natively and attempt ranks them against the
Change graph. tonal constraints apply through the same R filter
as every family; a step they empty is relaxed with a notice (there is
deliberately no fallback pool).
The genP paradigm (strata-first)
Three-layer generation (triad / strata / mode). The roman-numeral
aliases pin the starting tristrata; genPReport prints the per-bar
provenance.
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 ….
Uncued, the starting chord is drawn from inside the stratum itself — a
five-tone set admits few triads, and the ordinary whole-corpus cue almost
never lands in one. A cue you pass with cue is honoured as given; one
that escapes the stratum is reported with the triads that would have
fitted, rather than silently replaced.
s <- seek "none" $ len 6 $ genP VI
s <- seek "none" $ cue start $ len 6 $ genP VI
renderTristrataReport :: ProgressionContext -> Maybe String Source #
Render a multi-line report of the per-bar provenance of a
ProgressionContext: for each bar, the tristrata index, its three strata,
and the selected strata for that bar. Returns Nothing when the context
has no provenance (e.g., a legacy gen result).
genPReport :: ProgressionContext -> IO () Source #
Live-coding helper: print a pretty tristrata report for a
genP result alongside the standard Show
output. Takes the bound context directly, mirroring
genEReport. Useful at the REPL
for sanity-checking a strata walk.
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. Every family returns distinct layers: genP
carries curated strata/mode chroma with Just provenance, genE
partner triads, and gen/genJ chordscale-derived pentatonic/mode
layers (chordscale is applied in
the dispatch fallthrough) with pcProvenance Nothing.
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 (>= 0) — the gamma sampler targets rank entropy * 10
in the scored pool, so 0 usually takes the top candidate, 0.5 wanders
around the 5th, 1 around the 10th; values above 1 reach deeper still.
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
Polytonal (genE) attempts rank differently,
because they are three progressions rather than one: each layer is scored
in its own right (half the weight on the foundation, a quarter on each
partner) and a divergence axis rewards the layers standing apart — the
point of the family. See PolyScore. Raising maxAttempts raises both
the quality and the divergence of what comes back.
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).
steer :: Double -> GenConfig -> GenConfig Source #
Set the genJ classical-steer boost strength (default 3.0). Applies
only when the seek spec names classical composers: a jazz candidate
containing one of the steer blend's top recommended triads has its
score multiplied by up to (1 + strength). 0 disables steering
influence entirely. Initial calibration — tune by ear.
s <- seek "debussy" $ steer 6 $ len 8 $ genJ'
Generation types
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 |
|
| JazzMode |
|
| FromProgJ ProgressionContext !Int !Int | Regenerate range in a jazz-family context |
| PolyMode |
|
| FromProgPoly ProgressionContext !Int !Int | Regenerate range in a polytonal context (foundation + partner chains, seam-aware) |
defaultGenConfig :: GenConfig Source #
Default generation configuration.
cue: random root, major triad — but an uncued run resolves its cue
context-aware at run time: inside the tonal context for gen /
genE / grid (tonalStartCue),
from the stratum for genP, from the Change graph for genJ
len: 4
seek: "*" (all composers)
entropy: 0.2
tonal: hContext (chromatic)
execGenConfig :: GenConfig -> IO Progression Source #
Execute a GenConfig, producing a progression.
Progression-typed view of execGenConfigPC: one dispatcher and one
emission path for every family, and the attempt loop (_gcMaxAttempts)
is honoured rather than silently ignored.
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)
genSilent :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate with custom configuration and maximum diagnostics (internal).
Generate a progression with NO diagnostic output (verbosity 0 - silent mode).
genStandard :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate a progression with STANDARD diagnostic output (verbosity 1).
genVerbose :: CadenceState -> Int -> String -> Double -> HarmonicContext -> IO Progression Source #
Generate a progression with VERBOSE diagnostic output (verbosity 2).
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).
Context and configuration
A HarmonicContext constrains what the generator may choose. Build one
by chaining modifiers onto hContext:
ctx = invSkip 1 $ hcOvertones "E A D G" $ hcPedal "E?" $ hContext
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 :: String -> String -> String -> 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
String (not Text) so editor plugins that wrap literals in Tidal's
deltaContext (which has a Stringy String instance but no Text one)
can pass filter strings straight through.
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
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.
Core music types
module Harmonic.Rules.Types.Pitch
Movement represents bass motion by a musical interval
Constructors
| Asc PitchClass | Ascending by n semitones (1-5) |
| Desc PitchClass | Descending by n semitones (1-5) |
| Unison | Pedal (no movement) |
| Tritone | Movement by 6 semitones |
| Empty | Placeholder for invalid/missing movement |
Instances
type Functionality = String Source #
Chord quality/functionality as a string (e.g., "maj", "min7", "dim") Preserved as String for compatibility with legacy naming conventions
toFunctionality :: [PitchClass] -> Functionality Source #
Derive functionality name from a pitch set using the legacy triad nameFunc This is for TRIADS (exactly 3 pitch classes)
toFunctionalityChord :: [PitchClass] -> Functionality Source #
Derive functionality name from a pitch set using the chord nameFunc This is for CHORDS (any number of pitch classes, extended harmonies)
A Chord is an abstract pitch structure: root note name, functionality,
and pitch content as integers, bass first (register-capable). NOTE:
toTriad's base case stores ABSOLUTE pitch classes here, not
root-relative intervals (F major reads back as [5, 9, 0] — pinned by
BridgeSpec); treat the field as "pitches as constructed", never
assume root-relative.
Constructors
| Chord | |
Fields | |
Instances
| Generic Chord Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Associated Types
| |||||
| Show Chord Source # | |||||
| Eq Chord Source # | |||||
| Ord Chord Source # | |||||
| type Rep Chord Source # | |||||
Defined in Harmonic.Rules.Types.Harmony type Rep Chord = D1 ('MetaData "Chord" "Harmonic.Rules.Types.Harmony" "theHarmonicAlgorithm-3.1.1-JBKSxRG0idi9DGVvI173xL" 'False) (C1 ('MetaCons "Chord" 'PrefixI 'True) (S1 ('MetaSel ('Just "chordNoteName") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NoteName) :*: (S1 ('MetaSel ('Just "chordFunctionality") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Functionality) :*: S1 ('MetaSel ('Just "chordIntervals") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Integer])))) | |||||
A Cadence is a movement type combined with a target chord quality. Abstract: defines "approach by descending 5th to a major chord" without specifying which root pitch.
Constructors
| Cadence | |
Fields | |
Instances
| Generic Cadence Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Associated Types
| |||||
| Show Cadence Source # | |||||
| Eq Cadence Source # | |||||
| Ord Cadence Source # | |||||
Defined in Harmonic.Rules.Types.Harmony | |||||
| type Rep Cadence Source # | |||||
Defined in Harmonic.Rules.Types.Harmony type Rep Cadence = D1 ('MetaData "Cadence" "Harmonic.Rules.Types.Harmony" "theHarmonicAlgorithm-3.1.1-JBKSxRG0idi9DGVvI173xL" 'False) (C1 ('MetaCons "Cadence" 'PrefixI 'True) (S1 ('MetaSel ('Just "cadenceFunctionality") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Functionality) :*: (S1 ('MetaSel ('Just "cadenceMovement") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Movement) :*: S1 ('MetaSel ('Just "cadenceIntervals") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [PitchClass])))) | |||||
toMovement :: PitchClass -> PitchClass -> Movement Source #
Convert two pitch classes to a Movement (bass motion direction/distance) Ported from legacy MusicData.hs toMovement
Movement direction is determined by the shorter path around the pitch class circle. Example: C(0) to G(7) = descending by 5 (shorter path is 5 semitones down via P 12) G(7) to C(0) = ascending by 5 (shorter path is 5 semitones up to P 12 ≡ P 0)
fromMovement :: Movement -> PitchClass Source #
Convert Movement back to PitchClass interval
data EnharmonicSpelling Source #
Enharmonic spelling preference (sharp or flat) Stored in CadenceState to enable consistent spelling throughout a progression
Constructors
| SharpSpelling | |
| FlatSpelling |
Instances
| Generic EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Associated Types
Methods from :: EnharmonicSpelling -> Rep EnharmonicSpelling x # to :: Rep EnharmonicSpelling x -> EnharmonicSpelling # | |||||
| Read EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods readsPrec :: Int -> ReadS EnharmonicSpelling # readList :: ReadS [EnharmonicSpelling] # | |||||
| Show EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods showsPrec :: Int -> EnharmonicSpelling -> ShowS # show :: EnharmonicSpelling -> String # showList :: [EnharmonicSpelling] -> ShowS # | |||||
| Eq EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods (==) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # (/=) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # | |||||
| Ord EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods compare :: EnharmonicSpelling -> EnharmonicSpelling -> Ordering # (<) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # (<=) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # (>) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # (>=) :: EnharmonicSpelling -> EnharmonicSpelling -> Bool # max :: EnharmonicSpelling -> EnharmonicSpelling -> EnharmonicSpelling # min :: EnharmonicSpelling -> EnharmonicSpelling -> EnharmonicSpelling # | |||||
| type Rep EnharmonicSpelling Source # | |||||
Defined in Harmonic.Rules.Types.Harmony | |||||
data EnharmonicPreference Source #
Convert EnharmonicSpelling to the actual function (PitchClass -> NoteName) |Classify a pitch class by its natural enharmonic preference. C (PC 0) is AMBIGUOUS - it adopts the pairing's preference (prior or posterior). All other pitch classes have definite preferences.
Constructors
| FlatPref | |
| SharpPref | |
| AmbiguousPref |
Instances
| Show EnharmonicPreference Source # | |
Defined in Harmonic.Rules.Types.Harmony Methods showsPrec :: Int -> EnharmonicPreference -> ShowS # show :: EnharmonicPreference -> String # showList :: [EnharmonicPreference] -> ShowS # | |
| Eq EnharmonicPreference Source # | |
Defined in Harmonic.Rules.Types.Harmony Methods (==) :: EnharmonicPreference -> EnharmonicPreference -> Bool # (/=) :: EnharmonicPreference -> EnharmonicPreference -> Bool # | |
enharmonicPreference :: PitchClass -> EnharmonicPreference Source #
Get the natural enharmonic preference for a pitch class. C (0) is ambiguous and should be handled specially. F#/Gb (6) is always treated as sharp (F#).
selectEnharm :: EnharmonicSpelling -> PitchClass -> PitchClass -> EnharmonicSpelling Source #
Determine enharmonic spelling for posterior root given prior's actual spelling and both pitch classes.
Rules: (1) SAME PITCH CLASS: Always use prior's actual spelling (the root hasn't changed!) - Example: D# → D# keep Sharp, Eb → Eb keep Flat (2) C IS FLEXIBLE: When C is prior or posterior, the OTHER pitch's preference can override - Prior is C + posterior is definite: adopt posterior's preference - Posterior is C + prior is definite: adopt prior's actual spelling - Both C: persist current (no preference to guide) (3) BOTH DEFINITE (different pitch classes): Consensus-based switching a) If prior's actual spelling matches posterior's preference → persist (consensus on choice) b) If both have SAME preference AND it differs from prior's actual → switch (consensus to switch) c) Otherwise: persist prior's actual spelling (disagreement or already aligned)
Examples: D# → D# (same PC) → persist prior's actual spelling (Sharp or Flat, doesn't matter which) D# (actual Sharp) → E (SharpPref) → both prefer Sharp, actual differs → switch to Sharp!
defaultEnharm :: PitchClass -> EnharmonicSpelling Source #
Default enharmonic spelling for initial state based on root pitch class. C defaults to flat; all others use their natural preference.
spellingToPreference :: EnharmonicSpelling -> EnharmonicPreference Source #
Convert enharmonic spelling to the corresponding preference (for symmetry).
enharmonicFunc :: EnharmonicSpelling -> PitchClass -> NoteName Source #
Select the spelling function for a given EnharmonicSpelling — sharp
or flat. Used to render a pitch class as a note name consistently across a
progression, so a single chord grid does not mix F# and Gb.
inferSpelling :: [Int] -> EnharmonicSpelling Source #
Infer enharmonic spelling from absolute pitches (bass first). Three-layer system: Layer 1: 3-set match — bass matches AND remaining 2 PCs are subset of chord Layer 2: 2-set match — bass matches AND remaining 1 PC is subset of chord Layer 3: Root fallback — defaultEnharm on bass PC
noteNameImpliesSpelling :: NoteName -> Maybe EnharmonicSpelling Source #
Detect if a NoteName carries an explicit sharp/flat preference. Sharp variants (C', D', F', G', A') → Just SharpSpelling Flat variants (Db, Eb, Gb, Ab, Bb) → Just FlatSpelling Natural notes (C, D, E, F, G, A, B) → Nothing (use inference)
isAmbiguousPattern :: [Int] -> Bool Source #
Check if a chord's absolute pitches match an ambiguous pattern.
data ChordState Source #
A ChordState is a Chord with a concrete root pitch (anchored in pitch space).
Constructors
| ChordState | |
Fields
| |
Instances
| Generic ChordState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Associated Types
| |||||
| Show ChordState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods showsPrec :: Int -> ChordState -> ShowS # show :: ChordState -> String # showList :: [ChordState] -> ShowS # | |||||
| Eq ChordState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony | |||||
| type Rep ChordState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony type Rep ChordState = D1 ('MetaData "ChordState" "Harmonic.Rules.Types.Harmony" "theHarmonicAlgorithm-3.1.1-JBKSxRG0idi9DGVvI173xL" 'False) (C1 ('MetaCons "ChordState" 'PrefixI 'True) (S1 ('MetaSel ('Just "stateChord") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Chord) :*: S1 ('MetaSel ('Just "stateRoot") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NoteName))) | |||||
data CadenceState Source #
A CadenceState is a Cadence with a concrete root pitch and enharmonic spelling. This represents "descend by 5th to G major (flat spelling)" rather than abstract. The enharmonic spelling ensures consistent note naming throughout a progression.
Constructors
| CadenceState | |
Fields | |
Instances
| Generic CadenceState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Associated Types
| |||||
| Show CadenceState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony Methods showsPrec :: Int -> CadenceState -> ShowS # show :: CadenceState -> String # showList :: [CadenceState] -> ShowS # | |||||
| Eq CadenceState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony | |||||
| type Rep CadenceState Source # | |||||
Defined in Harmonic.Rules.Types.Harmony type Rep CadenceState = D1 ('MetaData "CadenceState" "Harmonic.Rules.Types.Harmony" "theHarmonicAlgorithm-3.1.1-JBKSxRG0idi9DGVvI173xL" 'False) (C1 ('MetaCons "CadenceState" 'PrefixI 'True) (S1 ('MetaSel ('Just "stateCadence") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Cadence) :*: (S1 ('MetaSel ('Just "stateCadenceRoot") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 NoteName) :*: S1 ('MetaSel ('Just "stateSpelling") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 EnharmonicSpelling)))) | |||||
toTriad :: (PitchClass -> NoteName) -> [Int] -> Chord Source #
Build a Triad from an enharmonic function and integer list TRIADS: exactly 3 pitch classes. If >3 input, reduces to best triad. Uses elaborate inversion detection from legacy MusicData.hs
sharpTriad :: [Int] -> Chord Source #
Shortcut with sharp spelling for triads
toChord :: (PitchClass -> NoteName) -> [Int] -> Chord Source #
Build a Chord from an enharmonic function and integer list CHORDS: preserves all pitches, uses chord-specific naming for extended harmonies Ported from legacy MusicData.hs toChord (lines 896-941)
sharpChord :: [Int] -> Chord Source #
Shortcut with sharp spelling for chords
fromChordState :: ChordState -> Chord Source #
Convert ChordState to the underlying Chord
fromCadenceState :: CadenceState -> Chord Source #
Convert CadenceState back to a Chord (applying the root and spelling)
initCadenceState :: Int -> String -> [Int] -> CadenceState Source #
Initialize a CadenceState from movement, note name, and quality intervals. Enharmonic spelling is inferred from the chord's absolute pitch content.
mkCadenceStatePCs :: NoteName -> Movement -> [Int] -> CadenceState Source #
Build a CadenceState from a root, movement, and root-relative intervals,
preserving full cardinality. The non-truncating counterpart to
initCadenceState, which routes through flatTriad / toCadence and
silently reduces >3 PCs to a triad. Intervals are root-relative
(0 = the root; inserted if missing); stored in zero form (sorted,
deduped mod 12). Functionality dispatches on cardinality: <=3 uses the
triad namer, 4+ the chord namer. Spelling from the typed root when it
implies one (Eb → flat), otherwise inferred from absolute pitch content.
walkTriadCadence :: Cadence -> Cadence Source #
Project a Cadence onto its most consonant ROOTED embedded triad.
Identity for intervals (every corpus-generated cadence). For3:
enumerates the 3-subsets that keep the root ([0,a,b] over the nonzero
intervals), picks the least dissonant via the Layer-B mostConsonant
replica, and renames with the triad namer. Root, movement, and (via
walkTriadState) spelling are preserved, so the projected cadence's
show is always a corpus-shaped graph key. This is the walk shadow for
>3-note material (regen over hand-built extended progressions, the genJ
classical steer): a hand-built added tone can reinterpret the harmony
(e.g. [0,3,6]+7 projects to [0,3,7] minor) and thereby steer the next
step.
walkTriadState :: CadenceState -> CadenceState Source #
State-level walk projection: walkTriadCadence on the cadence, root
and spelling untouched. Identity for triad states.
data ToTriadTrace Source #
Trace of toTriad transformation steps
Constructors
| ToTriadTrace | |
Fields
| |
Instances
| Show ToTriadTrace Source # | |
Defined in Harmonic.Rules.Types.Harmony Methods showsPrec :: Int -> ToTriadTrace -> ShowS # show :: ToTriadTrace -> String # showList :: [ToTriadTrace] -> ShowS # | |
| Eq ToTriadTrace Source # | |
Defined in Harmonic.Rules.Types.Harmony | |
fromCadenceStateTraced :: CadenceState -> (Chord, ToTriadTrace) Source #
Convert CadenceState to Chord with full transformation trace. Used for maximum verbosity diagnostics (gen''). Includes raw DB data plus all transformation stages.
constructCadence :: (String, String) -> Cadence Source #
Construct a Cadence from DB text format (movement string, chord string)
Example: constructCadence ("desc 3", "[P 0,P 3,P 8]") => Cadence { cadenceFunctionality = "maj_1stInv", cadenceMovement = Desc (P 3), ... }
corpusFunctionality :: [PitchClass] -> Functionality Source #
Name a zero-form triad exactly as the corpus stores it. The graph's
node keys (show = movement + functionality) carry the names below;
any cadence whose show is used as a fetch key MUST be named through
here, or the walk silently drops to fallback-only after landing on
that form (a latent bug fixed 2026-08-19: constructCadence
previously named via bare toFunctionality, so every graph-selected
inversion candidate — [0,3,8] read back as min#5 instead of the
stored maj_1stInv — produced a keyless next fetch).
The table is the complete 55-form vocabulary transcribed verbatim from
a live corpus dump (2026-08-19,
MATCH (c:Cadence) RETURN DISTINCT c.chord, c.show). It reflects the
LEGACY naming rules the database was ingested under — including the
forms where the modernised nameFuncTriad deliberately diverges
([0,2,7] is stored sus4_1stInv, modern says sus2; [0,5,10] is
stored sus4_2ndInv, modern says 7sus4) — so it cannot be derived
from the current namers. If the corpus is ever re-ingested, regenerate
this table from the same query.
Non-triad input (fewer/more than 3 PCs) falls back to the current namers — such sets are never corpus keys.
deconstructCadence :: Cadence -> (Movement, [PitchClass]) Source #
Deconstruct a Cadence to DB text format (movement, pitch class list) Returns the Movement and [PitchClass] for serialization
inversions :: [PitchClass] -> [[PitchClass]] Source #
Generate all cyclic inversions of a pitch set (in zero form) Each inversion is transformed to zero form for proper comparison
isInversion :: Cadence -> Bool Source #
Check if a cadence is an inversion (1st or 2nd) based on zero-form intervals.
Patterns match those in detectInversion.
normalForm :: [PitchClass] -> [PitchClass] Source #
Normalize a pitch set to its most compact form Ported from legacy MusicData.hs normalForm
primeForm :: [PitchClass] -> [PitchClass] Source #
Compute prime form (transposition and inversion invariant) Ported from legacy MusicData.hs primeForm
zeroFormPC :: [PitchClass] -> [PitchClass] Source #
Zero-form for pitch classes Subtracts the FIRST element (not minimum) then sorts, matching legacy behavior |Normalize pitch class set to zero form by subtracting first element then sorting. Examples: [P 4, P 7, P 11] → [P 0, P 3, P 7] (maj7 in root position) [P 8, P 11, P 3] → [P 0, P 3, P 7] (maj7 in 2nd inversion) Used by all Cadence construction paths (DB, toCadence, fallback) for semantic consistency.
Voice leading (cyclic DP paradigms)
voiceLeadingCost :: [Int] -> [Int] -> Int Source #
Calculate the voice leading cost between two chords.
Cost components: * Base: sum of absolute MIDI movements per voice. * Parallel penalty: +3 for each parallel perfect 5th / octave between ANY voice pair (not just adjacent), when at least one voice moves. * Large leap penalty: +2 per voice moving > 4 semitones. * Register-exchange penalty: +4 per adjacent voice pair where both voices move ≥5 semitones in opposite directions (split-leap pattern producing register inversion). Note: not classical "voice crossing" — sorted MIDI voicings have no voice identity to cross — but the same musical effect of register-swapping leaps. * Contrary motion bonus: −1 per voice pair (any pair) where both voices move ≤4 semitones in opposite directions (modest divergence). * Stepwise motion bonus: −1 per stepping voice (movement ∈ {1, 2}) when ≥2 voices step. Single-voice steps contribute 0.
Magnitudes calibrated to compose: contrary motion and register exchange are deliberately disjoint by magnitude (≤4 vs ≥5 thresholds), aligning with the leap-penalty trigger so the same motion is never both rewarded and penalised. They also differ in pair scope by design: register exchange scans ADJACENT pairs only (a register swap is a neighbouring-voices phenomenon), while contrary motion rewards ANY pair.
The total is floored at 1 for any actual motion (from /= to): bonuses
could otherwise exceed base + penalties and drive a moving transition
below the held-chord cost of 0 — inverting the musical preference. A
held chord ("available static movement") is always strictly cheapest;
bonuses still discriminate among positive-cost alternatives. The floor
applies to the TOTAL only — component bonuses stay un-clamped inside
the sum (e.g. [0,4,7]→[-1,2,7] = base 3 + stepwise −1 = 2).
Cross-cardinality transitions (mixed-set seams: lead' cues, hand-built
fromChords material) are costed by optimal monotone padding: the smaller
sorted voicing is expanded by duplicating tones per the minimal-distance
non-crossing alignment in which every voice of BOTH chords participates
(alignVoices), then the full cost above runs verbatim on the aligned
pair. An extra voice pays exactly its distance to the tone it splits
from (a literal unison doubling is free); no voice can appear or vanish
unpenalised. Historical note: this branch was a flat 999 sentinel,
which in the DP acted as "ignore this edge" — seam registers were
decided by downstream edges alone and the cyclic wrap objective was
silently disabled on mixed material.
Voicings must be SORTED ascending — list position is voice order for
both the alignment and the parallel-perfect scan. Every production
caller sorts before calling.
cyclicCost :: [[Int]] -> Int Source #
Calculate cyclic cost: total cost including wrap-around from last to first. This is essential for loop-aware optimization.
From the evaluation document:
"Adding wrap-around cost to the voice leading solver solves the drift
issue elegantly. It forces the algorithm to find a path that is not
just locally optimal, but topologically closed."
voiceMovement :: Int -> Int -> Int Source #
Calculate the movement for a single voice between two concrete pitches. Movement is measured in absolute semitones (not mod 12 since we're working with concrete pitches in the [0,36] range).
minimalMovement :: PitchClass -> PitchClass -> Int Source #
Calculate minimal movement between two pitch class values (mod 12). Exported API; currently unused inside the engine (kept for REPL and downstream use).
allVoicings :: [Int] -> [[Int]] Source #
Generate all valid voicings for a pitch-class set of any cardinality.
Each pitch class is placed at its pitchPlacements octaves (PCs 0-6
get 2 placements in [12, 30]; PCs 7-11 get 3 in [7, 35]) — candidate
count is the per-PC placement product (2^a·3^b: typically 12 for a
triad, 36 for a tetrad, 72 for a 5-PC set), NOT 3^N. The key-dependent
window asymmetry is a known calibration; widening it changes every
solved voicing globally and is deferred behind a before/after
listening protocol.
Results are sorted low-to-high and deduplicated.
Historical note: this was hard-coded to 3 notes, with non-triads falling
back to the single candidate [sort pcs] pinned in octave [0,11] — which
collapsed bars 2..n of any multi-bar 4+-note progression ~2 octaves below
bar 1 under flow/grid (bar 1 goes through initialCompact, the rest
through here, then normalizeByFirstRoot applies one uniform shift).
initialCompact :: Int -> [Int] -> [Int] Source #
Create initial compact voicing: root in bass in target octave (12-23), upper voices stacked compactly above.
solveRoot :: [[Int]] -> [[Int]] Source #
Solve with root always in bass (root paradigm).
Filters candidates at each position to only those where bass note mod 12 == root PC.
Result is normalized so first chord's root is in [-12,-1].
Memoised: see memoSolve.
solveFlow :: [[Int]] -> [[Int]] Source #
Solve FLOW paradigm using cyclic DP:
Smoothest voice leading with any inversion allowed for bars 1..n-1.
Bar 0 is anchored to the compact root-position voicing
(initialCompact) so the progression's starting register is
predictable and normalizeByFirstRoot has a stable anchor.
Voice crossings permitted in subsequent bars for optimal smoothness.
Result is normalized so first chord's root is in [-12, -1].
Memoised: see memoSolve.
Progressions and scales
module Harmonic.Rules.Types.Scale
Interactive behaviour
Filter functions
String-friendly versions for TidalCycles
overtones :: String -> [PitchClass] Source #
Parse overtones from a String (Tidal-friendly) Example: overtones "E A D G" -> bass tuning overtones
key :: String -> [PitchClass] Source #
Parse key from a String (Tidal-friendly) Example: key "#" -> G major, key "bb" -> Bb major
funds :: String -> [PitchClass] Source #
Parse fundamentals from a String (Tidal-friendly) Example: funds "E F# G" -> [4, 6, 7]
Text versions
parseOvertones :: Text -> [PitchClass] Source #
Parse overtones with 3 overtones (default: root, P5, M3)
parseFunds :: Text -> [PitchClass] Source #
Parse fundamentals with 3 overtones (default)
parseTuning :: Text -> [PitchClass] Source #
Parse tuning with 3 overtones (default: root, P5, M3 — the distinct pitch classes of the playable tapped-harmonic domain)
isWildcard :: Text -> Bool Source #
Check if a filter string is a wildcard (matches everything)
Overtone annotation support
parseTuningNamed :: Text -> [(String, Int)] Source #
Parse a tuning string preserving string names for overtone annotation. Case is preserved for string identification (uppercase = lower octave, lowercase = higher octave per thesis convention).
Examples:
"E A D G" → [(E,4), (A,9), (D,2), (G,7)]
"E A e G B" → [(E,4), (A,9), ("e",4), (G,7), (B,11)]
"*" → [] (wildcard has no named strings)
Database interface
connectNeo4j :: IO DbConn Source #
Connect to the local Neo4j from Harmonic.Config (override with the
HA_NEO4J_URL environment variable, e.g. http://localhost:7477 to
point a REPL at a scratch container). Probes the server with RETURN 1
so an unreachable database surfaces the error here, at connect time —
matching what every online generation path expects.
Ingestion pipeline
Corpus ingestion. Not needed to play — see Harmonic.Rules.Import.CSV.
module Harmonic.Rules.Import.CSV
TidalCycles interface
Pattern-level operations
type VoiceFunction = Progression -> [[Int]] Source #
Voice function type: extracts integer pitch sequences from progression
voiceRange :: (Int, Int) -> Pattern Int -> Pattern Int Source #
Filter pattern events by DEGREE-INDEX range — the raw Pattern Int
values before the degree→pitch mapping, NOT MIDI notes. Instrument
range clipping in MIDI space happens later, via
clip.
Arguments
| :: (Double, Double) | Kinetics gate: events pass only while |
| -> IK | Performance context (kinetics + chord selection) |
| -> (Int, Int) | Degree-index trim for the input patterns (scale degrees, not MIDI; instrument-range clipping happens later via clip) |
| -> Layer | Progression layer to voice — single (T | S | M) or synthesized combination (TS | TM | SM | TSM | PT) |
| -> VoiceFunction | Voice function (flow, root, etc.) |
| -> (Progression -> Progression) | Progression modifier (overlapF 0, id, etc.) |
| -> [Pattern Int] | Input patterns to harmonize |
| -> Pattern ValueMap |
Render scale-degree patterns into a playable ControlPattern, reading
pitches from the progression under the given voicing strategy.
The workhorse of the Tidal interface: every orchestral instrument in Harmonic.Interface.Tidal.Orchestra is a thin wrapper around it.
d1 $ arrange (0,1) k (-9,9) T flow id ["0 1 2 3"]
Arguments
| :: (Double, Double) | Kinetics range |
| -> IK | Performance context |
| -> (Int, Int) | Degree-index trim for the input patterns (scale degrees, not MIDI; instrument-range clipping happens later via clip) |
| -> Layer | Progression layer (T | S | M) |
| -> VoiceFunction | Voice function |
| -> (Progression -> Progression) | Progression modifier |
| -> [Pattern Int] | Input patterns to harmonize |
| -> Pattern ValueMap |
Map notes through chords using squeeze, with kinetics range gating.
Same kinetics/modifier pattern as arrange, but uses squeeze strategy:
each chord slot gets the full input pattern compressed to fit.
parallel :: Pattern Note -> ControlPattern -> ControlPattern Source #
Stack fixed-interval parallel voices over an arranged ControlPattern.
The offset pattern is the FULL voice spec in absolute semitones: each note
of pat is replaced by one copy per simultaneous offset, shifted by that
offset. Include 0 to retain the original note; omit it to drop the root.
Comma = simultaneous voices, space = time-sequenced offsets (standard
mininotation, natively evaluated). Applied post-voicing/post-range-filter,
so offsets are not gated by the arrange MIDI range.
parallel "0 7" $ arrange ... -- root + perfect fifth above parallel "7" $ arrange ... -- fifth only (root dropped) parallel "[0,-5,4]" $ arrange ... -- root, fourth below, major third above
harmonicState :: Layer -> IK -> ControlPattern Source #
The chosen layer's pitch classes as held MIDI notes, for a device that locks a played keyboard to the harmony (the rig's MiDiNiNjA X3).
One note per distinct pitch class, MIDI 108-119 (pitch class = note mod 12),
on MIDI channel 10, each held for the length of its chord: the structure
follows the chord grid (snd k), so two chords in a bar give two held sets.
Any layer or combination (T, S, M, TS, ...) and any set size. Degree
indices 0-11 cover the largest set; the indices past a chord's size fold back
onto pitch classes already sent and are dropped, so each chord change costs
one message per pitch class on a DIN line shared with the music.
Each set goes out stateLead ahead of its chord: on the wire before the
chord's own notes, and in place for keys played just before the downbeat.
Channel 10, notes 108-119 sits above everything else on that channel in both rigs: the JV-map kit (0-9, 60-80), metronome and count, the MPC sub (35-48). The X3 consumes these notes before they reach the MPC.
kmap f k lyr = p "kmap" $ f $ harmonicState lyr k (BootTidal)
warp :: String -> Pattern Int Source #
Parse a mininotation chord selection pattern (bar-relative).
The /N divisor specifies the number of bars the pattern spans.
let r = warp "[1 2 3 4]/4" -- 4 chords over 4 bars (1 per bar) let r = warp "[1 2]/8" -- 2 chords over 8 bars (4 bars each)
rep :: ProgressionContext -> Pattern Time -> Pattern Int Source #
Generate a sequential chord selection pattern from a progression. Auto-derives length from the progression. Timing is bar-relative.
let r = rep s4 1 -- 4 chords over 4 bars (1 bar each) let r = rep s4 0.5 -- 4 chords over 2 bars (half bar each)
lookupChordAt :: Time -> Pattern Int -> Int Source #
Point-query a chord selection pattern at a specific time.
Returns the chord index (0-indexed) active at time t.
Falls back to chord 0 if no events found.
lookupChord :: ProgressionContext -> Int -> Chord Source #
Lookup a chord from a progression context by index with modulo wrap. Operates on the triad layer (the harmonic content).
lookupProgression :: ProgressionContext -> Pattern Int -> Pattern [Int] Source #
Lookup progression (triad layer) as a pattern of voicings via flow.
overlapF :: Int -> Progression -> Progression Source #
Forward overlap: merge pitches from n bars ahead
type Tuning = [(NoteName, Int)] Source #
Open strings of an overtone instrument as (name, scientific octave):
(A,1) is A1 = MIDI 33.
The instrument behind hmnx: the Electric Contrabass Cittern as the MPC
harmonics keymap holds it — open strings A2 E3 G3 (thesis §2.1: EAeGB is
E2 A2 E3 G3 B3) and the top string in both Hipshot lever positions, B3 and
C4. B and C are one physical string,
but the keygroup carries both sets of samples, so both are always available
to a pattern; the harmonic context is what limits the space. Fixed on
purpose: the keymap never changes, so no launcher states a tuning.
Arguments
| :: (Double, Double) | Kinetics gate, as |
| -> IK | Performance context |
| -> Layer | Harmonic state: T (chord tones) or S (pentatonic) |
| -> [Pattern Int] | Contours: ints into the bar's viable series |
| -> Pattern ValueMap |
The whole overtone instrument. Pattern ints index the bar's viable
composite series — the harmonics of ecbc whose pitch class the layer
voices — compressed to 0, 1, 2 … ascending and looped by floor-mod, so
"[0,1,2]" is the lowest three available tones from the bottom up and
"[-1,-2,-3]" the highest three from the top down, whatever the size of the
structure in play. A pitch class present in two octaves is two ints. Never an
octave wrap: these are fixed sample pitches. A bar whose voicing has no
instrument tone rests. Polyphonic; harmonics is the per-string filter over
the same mapping. The harmonic context is the only limit on the space:
generating inside the instrument's palette (hcOvertones) keeps every tone
reachable, and overtoneMap shows what each int hits.
, composite (0,1) k T ["~", "[0,1,2]/4"] # ch 11
harmonics :: NoteName -> (Double, Double) -> IK -> Layer -> [Pattern Int] -> Pattern ValueMap Source #
One string of ecbc, named by its open note (A E G B C; enharmonics
collapse): the SAME int → pitch mapping as composite, but only the pitches
that string can sound pass — the rest are silent, not remapped — so a string
block isolates or mutes a string and owns its sustain. Within the string the
result is mono' (latest-note priority: a new harmonic damps the previous,
as on the instrument).
, harmonics A (0,1) k T ["~", pat] # ch 11
overtoneMap :: ProgressionContext -> Layer -> [Pattern Int] -> IO () Source #
Print, per chord of a progression, what the ints of the basic series map
to on the instrument — pitch, MIDI number and sources in thesis notation
(E2 = string E, overtone 2; / = alternative sources) — with the
top-down alias beside each int; then the distinct ints of the contour over
its first bar, resolved per chord. Takes the progression context and the
layer the block plays, so what it prints is what composite sounds.
overtoneMap s T ["[0,1,2]/4"]
overtoneTable :: [Int] -> [(Int, Int, [(NoteName, Int)])] Source #
The basic series over a set of voiced pitch classes, on ecbc: (int,
MIDI pitch, sources), a source being (string, overtone number) in the
thesis's numbering — OT1 the fundamental's class (partials 2 and 4), OT2 the
fifth, OT3 the third. Pure core of overtoneMap.
compositeSeries :: Tuning -> [Int] Source #
The composite overtone series (thesis §2.2): every pitch any string of a
tuning can sound as a natural harmonic, ascending, once each.
compositeSeries ecbc is the keymap:
57 64 67 69 71 72 73 74 76 78 79 80 83 84 87 88.
stringPitches :: (NoteName, Int) -> [Int] Source #
The four playable partials of the 2016 thesis — octave, fifth, double octave, third (partials 2 3 4 5) — of one open string, as MIDI pitches.
partialOffset :: Int -> Int Source #
Semitones from a string's fundamental to its n-th partial:
round (12 * logBase 2 n), so partials 2..5 sit at +12 +19 +24 +28.
Form and kinetics
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
| |
A node's position in time — wall-clock Secs elapsed (0 = the start) or
musical Bars (4/4), numbered from 1 as the 12 Step counts them (bar 1 =
the start). Resolved to Tidal cycles at realization (see formK). Mix freely
in one 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).
Instances
| Show Transition Source # | |
Defined in Harmonic.Interface.Tidal.Form Methods showsPrec :: Int -> Transition -> ShowS # show :: Transition -> String # showList :: [Transition] -> ShowS # | |
| Eq Transition Source # | |
Defined in Harmonic.Interface.Tidal.Form | |
Realized form: continuous and discrete signals for live performance.
Constructors
| Kinetics | |
Fields
| |
type IK = (Kinetics, Pattern Int) Source #
Performance context: Kinetics bundled with chord selection pattern.
Reduces parameter threading — r and k are always passed together.
at :: Double -> Double -> Double -> ProgressionContext -> FormNode Source #
Form node builders. Time unit and transition are orthogonal:
at/at' take wall-clock seconds elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 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 elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 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 elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 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 elapsed, rh/rh' take musical bar
numbers (rehearsal marks, 4/4, bar 1 = the start of the form);
unprimed = smooth transition, primed = snap.
at 0 0 0 s seconds, smooth rh 10 0.5 0.5 s bars, smooth
at' 60 1 1 s seconds, snap rh' 18 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")
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
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.
Contract details:
- The LAST node is the loop terminator, not a playable section: segments
run between consecutive node pairs, and the final node's time sets
kLoopSecs. Its kinetics/dynamic values only shape the ramp INTO it. - Nodes are sorted by resolved time before use, so a moved rehearsal mark cannot produce negative-width segments. Nodes sharing a time yield a zero-width segment (the earlier one is inaudible).
formK bpm []yields silence on every signal — safe to evaluate mid-edit; nothing sounds until a node exists.- Smooth ramps render at 16 steps per SEGMENT regardless of length
(see
formSignal) — a long segment crosses kinetics thresholds on a correspondingly coarse grid.
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.
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.
isolate :: (Int, Int) -> IK -> IK Source #
A degree range for every line the launcher starts, applied in k:
k = isolate (-4,2) $ iK tempo form (rep s 4)
isolate keeps degree indices lo..hi (inclusive), as a line's own
(-9,9) does; isolate' keeps only those outside it (lo and hi dropped).
Each line's own range still applies, so the two intersect, and calls
compose: isolate (-9,4) . isolate' (0,0) is -9..4 without degree 0.
Lines that don't voice degree indices (drums, walking bass, subKick,
harmonics, kmap) are unaffected.
isolate' :: (Int, Int) -> IK -> IK Source #
A degree range for every line the launcher starts, applied in k:
k = isolate (-4,2) $ iK tempo form (rep s 4)
isolate keeps degree indices lo..hi (inclusive), as a line's own
(-9,9) does; isolate' keeps only those outside it (lo and hi dropped).
Each line's own range still applies, so the two intersect, and calls
compose: isolate (-9,4) . isolate' (0,0) is -9..4 without degree 0.
Lines that don't voice degree indices (drums, walking bass, subKick,
harmonics, kmap) are unaffected.
markbar :: Double -> IK -> [Pattern a] -> Pattern a Source #
markAt in bars (see the doctrine there).
markAt :: FormTime -> IK -> [Pattern a] -> Pattern a Source #
Form-relative cue: place patterns ONCE per form loop at a form time. The
list is stacked (the shape of slate); each pattern's first cycle is squeezed
into the one-beat window starting at that time, so a plain one-note pattern
fires exactly on the beat and "1 1" gives two hits within it. Loops in
lockstep with kSignal — the same slow loopCycles wrap as formSignal —
so a cue and the kinetics can never drift apart. NOT kinetics-gated: a
placement is absolute (the launcher's |* vel d still applies).
Time-unit doctrine: unprimed = bars, primed = seconds. mark takes musical
bar numbers as written for rh nodes (bar 1 = cycle 0, the number the 12 Step
shows); mark' takes seconds elapsed as for at nodes (the 12 Step's seconds
counter reads the same) and, like them, assumes cps has been constant since
cycle 0. markbar / marksec are synonyms.
Atemporal forms (single node, lK) fire once at absolute cycles. A time at
or beyond the loop end, or no cps, is silence.
, mark 34 k [ pad 1 |* vel 0.9 ] -- speech sample on the drop
Arranger functions (voicing paradigms)
rotate :: Int -> ProgressionContext -> ProgressionContext Source #
Rotate a progression by n bars (positive = left, negative = right). Bar-aligned: provenance and family travel with the bars.
excerpt :: Int -> Int -> ProgressionContext -> ProgressionContext Source #
Extract bars start to end (1-indexed, inclusive). Bar-aligned: provenance and family travel with the bars.
insert :: CadenceState -> Int -> ProgressionContext -> ProgressionContext Source #
Insert a CadenceState at position (1-indexed), replacing the existing one. Bar substitution: the foreign bar breaks strata/polytonal invariants, so provenance drops and those families downgrade.
switch :: Int -> Int -> ProgressionContext -> ProgressionContext Source #
Switch two bars at positions m and n (1-indexed).
Treated as substitution (drops provenance, downgrades strata/polytonal
families); promotable to the aligned tier via a lockstep update
pair if per-bar identity is ever wanted here.
clone :: Int -> Int -> ProgressionContext -> ProgressionContext Source #
Clone bar m to position n (overwrites n with contents of m).
Treated as substitution — see switch.
extract :: Int -> ProgressionContext -> CadenceState Source #
Extract a single CadenceState at index (1-indexed, modulo wrap) from the triad layer
transposeP :: Int -> ProgressionContext -> ProgressionContext Source #
Transpose a progression by n semitones. Strata provenance is anchored to ABSOLUTE pitch classes (the octatripentatonic universe has no transposition closure), so provenance and family survive only octave shifts; any other interval drops them.
reverse :: ProgressionContext -> ProgressionContext Source #
Reverse a progression. Bar-aligned: provenance and family travel.
fuse :: [ProgressionContext] -> ProgressionContext Source #
Fuse multiple progressions into one (concatenation)
fuse2 :: ProgressionContext -> ProgressionContext -> ProgressionContext Source #
Binary fuse for convenience in live coding
interleave :: ProgressionContext -> ProgressionContext -> ProgressionContext Source #
Interleave two progressions (alternating chords) Example: interleave [A,B,C] [X,Y,Z] = [A,X,B,Y,C,Z]
expandP :: Int -> ProgressionContext -> ProgressionContext Source #
Expand a progression by repeating the WHOLE progression n times
(expandP 2 on C F G A yields C F G A C F G A).
Bar-aligned replication: provenance and family travel.
progOverlap :: Int -> Progression -> Progression Source #
Bidirectional overlap: merge pitches from n bars in both directions
progOverlapF :: Int -> Progression -> Progression Source #
Forward-only overlap: merge pitches from n bars ahead
progOverlapB :: Int -> Progression -> Progression Source #
Backward-only overlap: merge pitches from n bars behind
grid :: Progression -> [[Int]] Source #
GRID paradigm: Root locked in bass with smooth compact voice leading. Uses cyclic DP to find globally optimal voicings. First chord starts compact with root in bass; all subsequent chords maintain root in bass with minimal voice movement.
flow :: Progression -> [[Int]] Source #
FLOW paradigm: Smoothest voice leading with any inversion allowed. Uses cyclic DP to find globally optimal voicings. Voice crossings permitted for optimal smoothness; bass doesn't need to be the root if an inversion provides smoother voice leading.
lite :: Progression -> [[Int]] Source #
LITE paradigm: Literal voicings with first-root normalization. Returns pitches as stored, but normalized so first chord's root is in [-12,-1]. No voice leading optimization applied (only octave normalization).
literal :: Progression -> [[Int]] Source #
Alias for lite (legacy compatibility)
root :: Progression -> [[Int]] Source #
ROOT paradigm: Root note only (root pitch class per chord). Extracts the root note (first element, mod 12) from each chord. Returns as single-element lists in [0,11] range.
Explicit progression construction
fromChords :: [[Int]] -> ProgressionContext Source #
Construct a Progression from explicit pitch-class sets.
This is the main function for composing/arranging workflow (not generation).
Takes an enharmonic spelling and a list of chord pitch-class sets,
returns a Progression ready for arrange.
Example:
fromChords [[0,4,7], [5,9,0], [7,11,2]]
--> C major → F major → G major
prog :: [[Int]] -> ProgressionContext Source #
Legacy alias for fromChords (matches legacy prog function)
Groove interface (drums and sub bass)
subKick :: Pattern Double -> IK -> (Progression -> [[Int]]) -> (Time, String, String, String) -> Pattern ValueMap Source #
Groove interface: kick and sub bass locked to the harmony, with chord
selection and kinetics gating from IK.
Sub on/off patterns and the kick pattern are bar-relative:
"[1]/2" = one onset every 2 bars, "1*4" = 4 kicks per bar.
Each sub note is held by its own duration: from its onset to the next kill
boundary — the manual offs (subOffPat) and, for maxDur < 1, each onset
shifted by maxDur*4 — emitted with legato 1 so the note-off lands on the
boundary. The hold is by note length, not by a CC64 pedal: the MPC sub
program does not treat CC64 as a damper (a MIDI note-off arriving under a
held pedal is stranded), so no CC64 is emitted here at all.
Chord selection uses innerJoin — we WANT new note-ons when the chord
changes. Sub is gated at (0.1, 1) and kick at (0.2, 1) via ki. The
launcher dynamic is the only dynamic applied — subKick deliberately
ignores the form's kDynamic envelope, so the sub holds its level while the
orchestra swells and ducks around it.
holdToNext :: Time -> Pattern Bool -> Pattern a -> Pattern a Source #
Hold each onset of pat until the next onset in bounds, rewriting the
event whole to [onset, nextBoundary) (continuation fragments across cycles
are preserved). A boundary is where the note must stop, so the note stops
exactly there. Pair with # legato 1 so the emitted MIDI note-off lands on the
boundary. Values are sampled once at the onset and held for the whole note
(no mid-note re-sampling). look is the lookahead/lookback in cycles; note
lengths must not exceed it.
fund :: Progression -> [[Int]] Source #
Extract harmonic roots regardless of inversion — the harmonic
FUNDAMENTAL uniformly, where root
is uniformly the sounding bass. Triads go through inversion detection
(a first-inversion bar reports its harmonic root, not its bass). Bars
of more than three tones ask the jazz namer for the rotation: a slash
structure (Bb7 over Ab) reports its true root (Bb), matching the
chart-convention grid name; a root-position extended chord — and any
set the namer cannot read — keeps its stored anchor. The walking bass
is unaffected either way: it anchors on the stored root internally
and reads a voice function only for beat-1 pitch choice.
noteoff :: Time -> Pattern Bool -> Pattern Bool Source #
Truncate each gate onset's note length to at most 1/n of a bar (bar = 4
cycles), else extend it to the next onset. Only True onsets sound; a
truncated tail is a rest; onsets are not moved. Pair with # legato 1 on
sustaining instruments to hear the length. Precondition: n > 0.
Bar patterns are written "/4" (1 cycle = 1 beat, 1 bar = 4 cycles), so
e.g. noteoff 4 caps each hit at a quarter note (1 cycle):
noteoff 4 "[[1 0 0 0] [0 0 0 0] [1 0 0 0] [1 0 0 0]]\/4" == "[1 0 1 1]\/4"
son32 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
son23 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
rumba32 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
rumba23 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
bossa32 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
bossa23 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
bellpat32 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
bellpat23 :: Pattern Bool Source #
Explicit 16-step (2-bar) boolean grids, one stroke per position, for
clave and cascara feels. Use with struct or mask on any instrument or
drum part.
32 is the 3-2 orientation; 23 is the 2-3 rotation (2 <~ over the
/4 two-bar span swaps the halves). Onset positions are 1-indexed.
Walking-bass line interface
Arguments
| :: Pattern Double | Dynamics scalar (amp multiplier) |
| -> IK | Performance context (kinetics + chord-selection) |
| -> VoiceFunction | Beat-1 voicing ( |
| -> [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.
Scale source (switch mechanism)
data ScaleSource Source #
Scale source for melody mapping. Enables flexible melody construction by allowing harmony (with optional overlap) to serve as the scale source instead of explicit scale definitions.
Constructors
| ExplicitScale [[Int]] | User-defined scale per chord |
| HarmonyAsScale Progression | Use harmony chords as scales |
| HarmonyWithOverlap Progression (Int -> Progression -> Progression) | Use harmony with overlap function applied |
melodyStateFrom :: ScaleSource -> Progression Source #
Create melody state from scale source. Converts a ScaleSource into a Progression suitable for melody arrangement.
Starting state construction
lead :: String -> IO CadenceState Source #
Construct a CadenceState from a human-readable string.
Parses root, quality, and movement from space-separated tokens. Unspecified components fall through to randomness. Prints "root quality" to the console after construction.
Examples:
start <- lead "E min (5)" -- E minor, ascending 5th
start <- lead "E min" -- E minor, random movement
start <- lead "min" -- random root, minor quality, random movement
start <- lead E -- E, random quality, random movement
start <- lead "" -- fully random
start <- lead "(5)" -- random root and quality, fixed movement 5
lead' :: String -> IO CadenceState Source #
Construct a CadenceState from an explicit list of note names —
the arbitrary-cardinality counterpart to lead. The first note is the
root/bass; the rest become root-relative intervals (any count, so
hand-built 4-note cues and beyond are first-class). Never truncates:
builds via mkCadenceStatePCs, so all pitch content survives into the
cue. Enharmonics follow the typed accidentals (Eb spells flat,
D sharp; double accidentals accepted and resolved). An optional
(N) token fixes the approach movement, otherwise it is randomized
exactly like lead. Unrecognized tokens are reported and skipped;
with no valid notes at all, falls back to fully random lead.
Examples:
start <- lead' "Eb Gb Bb Db" -- Eb m7, random movement
start <- lead' "A C E G (5)" -- A m7, ascending 5th approach
start <- lead' "C E G" -- plain triad, same as lead "C maj"
leadJ :: String -> IO CadenceState Source #
Construct a CadenceState from a leadsheet chord symbol — the jazz
counterpart to lead, for cueing genJ.
Accepts the Bunks corpus grammar: root, quality, optional slash bass
(Cm7, EbM7, G7b9, "Dm7/G"). Qualities occupy the jazz
namespace (qualityIntervals), entirely
separate from lead's triadic quality table, so triadic behaviour is
untouched. A notated slash bass is honoured exactly: unioned into the
pitch-class set and made the anchor, so "Dm7/G" cues from G with the
full Dm7 sounding above it. An optional (N) token fixes the approach
movement, otherwise it is randomized exactly like lead; either way
the movement shapes bar 1's stored metadata only — a fresh walk
departs from the functionality's hub node regardless. An
unparseable symbol is reported and falls back to the corpus workhorse
C m7.
Examples:
start <- leadJ Cm7 -- C m7, random movement
start <- leadJ "Dm7/G" -- G anchor, Dm7 above (a 9sus4 set)
start <- leadJ "EbM7 (5)" -- Eb maj7, ascending 5th approach
parseLeadTokens :: String -> [LeadToken] Source #
Parse a lead string into a list of typed tokens. Each space-separated token is independently classified as root, quality, or movement.
Instruments and orchestra
module Harmonic.Config
Internal (advanced use only)
Tuple-returning versions for manual diagnostics extraction.
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 :: 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)
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
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.
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.
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 | |