| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Harmonic.Evaluation.Scoring.Dissonance
Description
This module implements the Evaluation (E) component of the Creative Systems Framework for consonance/dissonance assessment.
Academic Lineage
Paul Hindemith, The Craft of Musical Composition (1937): the interval
class consonance ranking that underlies the dissVect weighting vector.
Data Science In The Creative Process (South, 2018): the computational
implementation as dissVect = [16,8,4,2,1,24] (MusicData.hs lines 394-401).
Weighting Vector
dissVect = [16,8,4,2,1,24] maps interval classes to *dissonance*
weights — higher is more dissonant:
- Minor second/major seventh (ic 1): 16
- Major second/minor seventh (ic 2): 8
- Minor third/major sixth (ic 3): 4
- Major third/minor sixth (ic 4): 2
- Perfect fourth/perfect fifth (ic 5): 1 (most consonant)
- Tritone (ic 6): 24 (most dissonant)
(Matches the authoritative table on hindemithVector below; an earlier
version of this header had the interval names shifted one class up.)
Synopsis
- dissonanceLevel :: [Int] -> (Integer, [Int])
- dissonanceScore :: [Int] -> Integer
- hindemithVector :: [Integer]
- intervalVector :: [Int] -> [Integer]
- intervalClass :: Int -> Int
- rootMotionVector :: [Integer]
- rootMotionScore :: Int -> Integer
- mostConsonant :: [[Int]] -> [Int]
- rankByConsonance :: [[Int]] -> [[Int]]
Core Dissonance Calculation
dissonanceLevel :: [Int] -> (Integer, [Int]) Source #
Calculate dissonance level for a pitch set. Returns (dissonance score, original pitches) for sorting/selection.
The calculation: 1. Compute interval vector [ic1, ic2, ic3, ic4, ic5, ic6] 2. Dot product with Hindemith weights [16, 8, 4, 2, 1, 24] 3. Special case: if only one interval class is present and it's the fifth, subtract 1 (bonus for "pure" fifths) 4. Special case: if interval vector is all zeros except one slot, return 27 (penalty for degenerate sets)
VERBATIM from legacy MusicData.hs (lines 394-401):
dissonanceLevel xs
| countElem iVect 0 == 5 = (27, xs)
| elem (7+head xs) xs = (subtract 1 $ sum $ zipWith (*) dissVect iVect, xs)
| otherwise = (sum $ zipWith (*) dissVect iVect, xs)
dissonanceScore :: [Int] -> Integer Source #
Simplified dissonance score (just the number, not paired with input)
Hindemith Vector
hindemithVector :: [Integer] Source #
The Hindemith dissonance weighting vector. Based on Paul Hindemith's ranking of interval classes.
Interval class mapping: Index 0 → ic 1 (minor 2nd / major 7th) → weight 16 Index 1 → ic 2 (major 2nd / minor 7th) → weight 8 Index 2 → ic 3 (minor 3rd / major 6th) → weight 4 Index 3 → ic 4 (major 3rd / minor 6th) → weight 2 Index 4 → ic 5 (perfect 4th / perfect 5th) → weight 1 Index 5 → ic 6 (tritone) → weight 24
Interval Analysis
intervalVector :: [Int] -> [Integer] Source #
Calculate the interval vector for a pitch class set. Returns counts for interval classes [1..6].
The interval vector is the "fingerprint" of a pitch set, counting how many times each interval class appears between all pairs of pitches.
Ported from legacy MusicData.hs (lines 349-358)
intervalClass :: Int -> Int Source #
Calculate the interval class (0-6) for an interval in semitones. Interval classes fold intervals larger than a tritone to their complement.
Root Motion Scoring
rootMotionVector :: [Integer] Source #
Root motion scoring vector for fallback generation. Ranks interval classes by smoothness (lower = smoother movement).
Interval class mapping: Index 0 → ic 1 (m2/M7) → weight 3 (stepwise) Index 1 → ic 2 (M2/m7) → weight 3 (stepwise) Index 2 → ic 3 (m3/M6) → weight 4 (moderate leap) Index 3 → ic 4 (M3/m6) → weight 4 (moderate leap) Index 4 → ic 5 (P4/P5) → weight 1 (strong harmonic motion) Index 5 → ic 6 (TT) → weight 6 (avoid)
Special cases: ic 0 (Unison/Pedal) → weight 2 (encourages harmonic rhythm)
rootMotionScore :: Int -> Integer Source #
Score root motion interval by smoothness. Returns smoothness penalty (lower = better movement).
Examples: rootMotionScore 0 == 2 (pedal - slight penalty) rootMotionScore 7 == 1 (P5 - strongest movement) rootMotionScore 5 == 1 (P4 - strong movement) rootMotionScore 1 == 3 (m2 - stepwise) rootMotionScore 2 == 3 (M2 - stepwise) rootMotionScore 6 == 6 (TT - avoid)
Selection
mostConsonant :: [[Int]] -> [Int] Source #
Select the most consonant option from a list of pitch sets. Used by triad generation to pick the "best" interpretation.
Ported from legacy MusicData.hs (lines 403-406):
mostConsonant xs = triadChoice . sortFst $ dissonanceLevel $ xs
where triadChoice xs = (snd . head . sortFst) xs
sortFst xs = List.sortBy (compare on fst) xs
rankByConsonance :: [[Int]] -> [[Int]] Source #
Rank a list of pitch sets by consonance (most consonant first)