{-# LANGUAGE DeriveGeneric #-}
module Harmonic.Rules.Constraints.Overtone
(
possibleTriads
, possibleTriads''
, possibleTriadsFrom
, overtoneSets
, nCr
, combinations
, rankedTriads
, topTriads
, annotateOvertones
, formatOvertoneAnnotation
, formatOvertoneAnnotationPipe
) where
import GHC.Generics (Generic)
import Data.List (sort, nub, sortBy, intercalate)
import Data.Function (on)
import Harmonic.Rules.Types.Pitch (PitchClass(..), mkPitchClass, unPitchClass)
import Harmonic.Evaluation.Scoring.Dissonance (dissonanceLevel, mostConsonant, rankByConsonance)
nCr :: Int -> [a] -> [[a]]
nCr :: forall a. Int -> [a] -> [[a]]
nCr Int
0 [a]
_ = [[]]
nCr Int
_ [] = []
nCr Int
n (a
x:[a]
xs) = ([a] -> [a]) -> [[a]] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:) (Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
nCr (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) [a]
xs) [[a]] -> [[a]] -> [[a]]
forall a. [a] -> [a] -> [a]
++ Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
nCr Int
n [a]
xs
combinations :: Int -> [a] -> [[a]]
combinations :: forall a. Int -> [a] -> [[a]]
combinations = Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
nCr
overtoneSets :: (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]]
overtoneSets :: forall a. (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]]
overtoneSets Int
n [a]
roots [a]
overtones =
[ a
root a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
overtoneSet
| a
root <- [a]
roots
, [a]
overtoneSet <- [a] -> [a]
forall a. Ord a => [a] -> [a]
sort ([a] -> [a]) -> [[a]] -> [[a]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> [a] -> [[a]]
forall a. Int -> [a] -> [[a]]
nCr (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) [a]
overtones
, a
root a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [a]
overtoneSet
]
possibleTriads :: (Int, [Int]) -> [[Int]]
possibleTriads :: (Int, [Int]) -> [[Int]]
possibleTriads (Int
root, [Int]
overtones) =
let fundList :: [Int]
fundList = [Int
root Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12]
availableOvertones :: [Int]
availableOvertones = (Int -> Bool) -> [Int] -> [Int]
forall a. (a -> Bool) -> [a] -> [a]
filter (\Int
x -> Int
x Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
root Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12) [Int]
overtones
in Int -> [Int] -> [Int] -> [[Int]]
forall a. (Eq a, Ord a) => Int -> [a] -> [a] -> [[a]]
overtoneSets Int
3 [Int]
fundList ([Int] -> [Int]
forall a. Eq a => [a] -> [a]
nub ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12) [Int]
availableOvertones)
possibleTriadsFrom :: PitchClass -> [PitchClass] -> [[PitchClass]]
possibleTriadsFrom :: PitchClass -> [PitchClass] -> [[PitchClass]]
possibleTriadsFrom PitchClass
root [PitchClass]
overtones =
let rootInt :: Int
rootInt = PitchClass -> Int
unPitchClass PitchClass
root
overtoneInts :: [Int]
overtoneInts = (PitchClass -> Int) -> [PitchClass] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map PitchClass -> Int
unPitchClass [PitchClass]
overtones
triads :: [[Int]]
triads = (Int, [Int]) -> [[Int]]
possibleTriads (Int
rootInt, [Int]
overtoneInts)
in ([Int] -> [PitchClass]) -> [[Int]] -> [[PitchClass]]
forall a b. (a -> b) -> [a] -> [b]
map ((Int -> PitchClass) -> [Int] -> [PitchClass]
forall a b. (a -> b) -> [a] -> [b]
map Int -> PitchClass
mkPitchClass) [[Int]]
triads
rankedTriads :: (Int, [Int]) -> [[Int]]
rankedTriads :: (Int, [Int]) -> [[Int]]
rankedTriads (Int, [Int])
input = [[Int]] -> [[Int]]
rankByConsonance ([[Int]] -> [[Int]]) -> [[Int]] -> [[Int]]
forall a b. (a -> b) -> a -> b
$ (Int, [Int]) -> [[Int]]
possibleTriads (Int, [Int])
input
topTriads :: Int -> (Int, [Int]) -> [[Int]]
topTriads :: Int -> (Int, [Int]) -> [[Int]]
topTriads Int
n (Int, [Int])
input = Int -> [[Int]] -> [[Int]]
forall a. Int -> [a] -> [a]
take Int
n ([[Int]] -> [[Int]]) -> [[Int]] -> [[Int]]
forall a b. (a -> b) -> a -> b
$ (Int, [Int]) -> [[Int]]
rankedTriads (Int, [Int])
input
countPossibleTriads :: (Int, [Int]) -> Int
countPossibleTriads :: (Int, [Int]) -> Int
countPossibleTriads = [[Int]] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([[Int]] -> Int)
-> ((Int, [Int]) -> [[Int]]) -> (Int, [Int]) -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, [Int]) -> [[Int]]
possibleTriads
possibleTriads'' :: (Integral a, Num a) => (a, [a]) -> [[a]]
possibleTriads'' :: forall a. (Integral a, Num a) => (a, [a]) -> [[a]]
possibleTriads'' (a
r, [a]
ps) =
let intResult :: [[Int]]
intResult = (Int, [Int]) -> [[Int]]
possibleTriads (a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
r, (a -> Int) -> [a] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral [a]
ps)
in ([Int] -> [a]) -> [[Int]] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map ((Int -> a) -> [Int] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral) [[Int]]
intResult
annotateOvertones :: [(String, Int)] -> [Int] -> [(Int, [(String, Int)])]
annotateOvertones :: [(String, Int)] -> [Int] -> [(Int, [(String, Int)])]
annotateOvertones [(String, Int)]
tuning [Int]
pitches = (Int -> (Int, [(String, Int)]))
-> [Int] -> [(Int, [(String, Int)])]
forall a b. (a -> b) -> [a] -> [b]
map Int -> (Int, [(String, Int)])
annotate [Int]
pitches
where
otOffsets :: [(Int, Int)]
otOffsets = [Int] -> [Int] -> [(Int, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int
1..Int
3] [Int
0, Int
7, Int
4 :: Int]
annotate :: Int -> (Int, [(String, Int)])
annotate Int
p = (Int
p, ((String, Int) -> [(String, Int)])
-> [(String, Int)] -> [(String, Int)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Int -> (String, Int) -> [(String, Int)]
forall {a}. Int -> (a, Int) -> [(a, Int)]
sourcesFor Int
p) [(String, Int)]
tuning)
sourcesFor :: Int -> (a, Int) -> [(a, Int)]
sourcesFor Int
p (a
name, Int
fund) =
[ (a
name, Int
otNum)
| (Int
otNum, Int
offset) <- [(Int, Int)]
otOffsets
, (Int
p Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
fund) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
12 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
offset
]
formatOvertoneAnnotation :: [(String, Int)] -> [Int] -> (Int -> String) -> String
formatOvertoneAnnotation :: [(String, Int)] -> [Int] -> (Int -> String) -> String
formatOvertoneAnnotation [(String, Int)]
tuning [Int]
pitches Int -> String
pcToName =
let annotated :: [(Int, [(String, Int)])]
annotated = [(String, Int)] -> [Int] -> [(Int, [(String, Int)])]
annotateOvertones [(String, Int)]
tuning [Int]
pitches
entries :: [String]
entries = [ Int -> String
pcToName Int
p String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [(String, Int)] -> String
forall {a}. Show a => [(String, a)] -> String
formatSources [(String, Int)]
sources
| (Int
p, [(String, Int)]
sources) <- [(Int, [(String, Int)])]
annotated
, Bool -> Bool
not ([(String, Int)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(String, Int)]
sources)
]
in if [String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
entries then String
"" else String
"{" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
entries String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"}"
where
formatSources :: [(String, a)] -> String
formatSources [(String, a)]
sources =
let grouped :: [(String, [a])]
grouped = [(String, a)] -> [(String, [a])]
forall {a} {a}. Eq a => [(a, a)] -> [(a, [a])]
groupByString [(String, a)]
sources
in String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"/" [ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ [a] -> String
forall {a}. Show a => [a] -> String
formatNums [a]
nums | (String
name, [a]
nums) <- [(String, [a])]
grouped ]
formatNums :: [a] -> String
formatNums [a
n] = a -> String
forall a. Show a => a -> String
show a
n
formatNums [a]
ns = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"+" ((a -> String) -> [a] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map a -> String
forall a. Show a => a -> String
show [a]
ns)
groupByString :: [(a, a)] -> [(a, [a])]
groupByString [(a, a)]
sources =
let names :: [a]
names = [a] -> [a]
forall a. Eq a => [a] -> [a]
nub [a
name | (a
name, a
_) <- [(a, a)]
sources]
in [ (a
name, [a
num | (a
n, a
num) <- [(a, a)]
sources, a
n a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
name]) | a
name <- [a]
names ]
formatOvertoneAnnotationPipe :: [(String, Int)] -> [Int] -> (Int -> String) -> String
formatOvertoneAnnotationPipe :: [(String, Int)] -> [Int] -> (Int -> String) -> String
formatOvertoneAnnotationPipe [(String, Int)]
tuning [Int]
pitches Int -> String
pcToName =
let annotated :: [(Int, [(String, Int)])]
annotated = [(String, Int)] -> [Int] -> [(Int, [(String, Int)])]
annotateOvertones [(String, Int)]
tuning [Int]
pitches
entries :: [String]
entries = [ Int -> String
pcToName Int
p String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [(String, Int)] -> String
forall {a}. Show a => [(String, a)] -> String
formatSources [(String, Int)]
sources
| (Int
p, [(String, Int)]
sources) <- [(Int, [(String, Int)])]
annotated
, Bool -> Bool
not ([(String, Int)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(String, Int)]
sources)
]
in if [String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
entries then String
"" else String
"overtones=| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\String
e -> String
e String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" | ") [String]
entries
where
formatSources :: [(String, a)] -> String
formatSources [(String, a)]
sources =
let grouped :: [(String, [a])]
grouped = [(String, a)] -> [(String, [a])]
forall {a} {a}. Eq a => [(a, a)] -> [(a, [a])]
groupByString [(String, a)]
sources
in String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"/" [ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ [a] -> String
forall {a}. Show a => [a] -> String
formatNums [a]
nums | (String
name, [a]
nums) <- [(String, [a])]
grouped ]
formatNums :: [a] -> String
formatNums [a
n] = a -> String
forall a. Show a => a -> String
show a
n
formatNums [a]
ns = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"+" ((a -> String) -> [a] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map a -> String
forall a. Show a => a -> String
show [a]
ns)
groupByString :: [(a, a)] -> [(a, [a])]
groupByString [(a, a)]
sources =
let names :: [a]
names = [a] -> [a]
forall a. Eq a => [a] -> [a]
nub [a
name | (a
name, a
_) <- [(a, a)]
sources]
in [ (a
name, [a
num | (a
n, a
num) <- [(a, a)]
sources, a
n a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
name]) | a
name <- [a]
names ]