theHarmonicAlgorithm-3.0.0: Real-time harmonic progression generation for TidalCycles live performance
Safe HaskellSafe-Inferred
LanguageHaskell2010

Harmonic.Rules.Constraints.Overtone

Description

This module implements the Rules (R) component of the Creative Systems Framework for constraining the "search space" of possible harmonies.

Academic Lineage

The Harmonic Algorithm (South, 2016), Section Two: the exhaustive combinatorial charts of 3-note overtone combinations across 12 chromatic bass notes for EAeGB, EAeGC, and EADG tunings. This module is the computational realisation of those charts.

Data Science In The Creative Process (South, 2018): the overtoneSets function is ported from the MusicData module (lines 382-385).

Design

Constructive generation: only valid combinations are produced in the first place, avoiding O(n³) generate-then-filter.

The overtone series provides the "palette" of available tones, and the combinatorial generator produces all valid 3-note subsets rooted on a specified fundamental. The annotateOvertones function provides reverse-mapping from pitch classes back to string/overtone sources, using the thesis notation (E3/e1, G1+3).

Synopsis

Triad Generation

possibleTriads :: (Int, [Int]) -> [[Int]] Source #

Generate all possible triads rooted on a given fundamental.

Input: (fundamental pitch class, available overtone pitch classes) Output: List of triads, each as [root, tone1, tone2] where: * Root is the specified fundamental * tone1 < tone2 (sorted) * Neither tone equals root

This is the workhorse function called during ingestion to derive harmonic interpretations from YCACL slices.

Ported from legacy MusicData.hs (lines 388-391): possibleTriads'' (r, ps) = let fund = (x -> [x]) . fromIntegral $ r in overtoneSets 3 fund ps

possibleTriads'' :: (Integral a, Num a) => (a, [a]) -> [[a]] Source #

Legacy alias for possibleTriads. Matches the signature from MusicData.hs for smooth migration: possibleTriads'' :: (Integral a, Num a) => (a, [a]) -> [[a]]

This version converts to/from Int internally to maintain type safety while preserving the polymorphic signature for backward compatibility.

possibleTriadsFrom :: PitchClass -> [PitchClass] -> [[PitchClass]] Source #

Alternative signature taking PitchClasses

overtoneSets :: (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]] Source #

Generate all valid subsets of size n from a fundamental and overtone palette.

CONSTRUCTIVE: This function directly builds valid sets rather than generating all and filtering. Each set contains: * Exactly one element from the fundamental list * Exactly (n-1) elements from the overtone list * No duplication of the fundamental in the overtone selection

Ported from legacy MusicData.hs (lines 382-385): overtoneSets n rs ps = [ i:j | i <- rs, j sort <$ (nCr $ n-1) ps, not $ i elem j]

Combination Utilities

nCr :: Int -> [a] -> [[a]] Source #

Generate all combinations of size n from a list. This is the mathematical "n choose r" operation.

Implementation uses direct recursion for clarity: * nCr 0 xs = [[]] -- One way to choose nothing * nCr n [] = [] -- Can't choose from empty * nCr n (x:xs) = with x ++ without x

Ported from legacy MusicData.hs nCr function.

combinations :: Int -> [a] -> [[a]] Source #

Alias for nCr with more descriptive name

Triad Selection

rankedTriads :: (Int, [Int]) -> [[Int]] Source #

Generate triads ranked by consonance (most consonant first). Uses Hindemith dissonance scores from the Dissonance module.

topTriads :: Int -> (Int, [Int]) -> [[Int]] Source #

Get the top N most consonant triads from a fundamental/overtone pair. Used by the multi-triad branching logic (3/2/1 weighting).

Returns at most n triads, or fewer if not enough valid triads exist.

Overtone Annotation

annotateOvertones :: [(String, Int)] -> [Int] -> [(Int, [(String, Int)])] Source #

Annotate pitch classes with their possible overtone sources from a tuning.

For each pitch in the chord, finds all (stringName, overtoneNumber) pairs where that string's overtone series contains the pitch class.

Overtone numbering follows the thesis convention: OT1 = fundamental (offset 0), OT2 = P5 (offset 7), OT3 = M3 (offset 4) Annotation covers OT1-OT3 — the distinct pitch classes of the playable tapped-harmonic domain.

Example: annotateOvertones [(E,4),(A,9),(D,2),(G,7)] [11,7,2] -- → [(11,[(E,2),(G,3)]), (7,[(G,1)]), (2,[(D,1),(G,2)])]

formatOvertoneAnnotation :: [(String, Int)] -> [Int] -> (Int -> String) -> String Source #

Format overtone annotation for a chord as a display string.

Uses thesis notation: "/" separates alternative sources from different strings "+" connects multiple overtone numbers from the same string

Example output: "{B: E2/D4, G: G1, D: E5/D1}"

formatOvertoneAnnotationPipe :: [(String, Int)] -> [Int] -> (Int -> String) -> String Source #

Format overtone annotation in pipe-delimited style for inline display. Produces: "overtones=| Bb: E2 | D: G3/A4 |" (empty string if no annotations)