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

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

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)