| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
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
- possibleTriads :: (Int, [Int]) -> [[Int]]
- possibleTriads'' :: (Integral a, Num a) => (a, [a]) -> [[a]]
- possibleTriadsFrom :: PitchClass -> [PitchClass] -> [[PitchClass]]
- overtoneSets :: (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]]
- nCr :: Int -> [a] -> [[a]]
- combinations :: Int -> [a] -> [[a]]
- rankedTriads :: (Int, [Int]) -> [[Int]]
- topTriads :: Int -> (Int, [Int]) -> [[Int]]
- annotateOvertones :: [(String, Int)] -> [Int] -> [(Int, [(String, Int)])]
- formatOvertoneAnnotation :: [(String, Int)] -> [Int] -> (Int -> String) -> String
- formatOvertoneAnnotationPipe :: [(String, Int)] -> [Int] -> (Int -> String) -> String
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}"