module Harmonic.Rules.Constraints.Overtone
(
possibleTriads
, possibleTriads''
, possibleTriadsFrom
, overtoneSets
, nCr
, combinations
, annotateOvertones
, formatOvertoneAnnotation
, formatOvertoneAnnotationPipe
) where
import Data.List (sort, nub, intercalate)
import Harmonic.Rules.Types.Pitch (PitchClass(..), mkPitchClass, unPitchClass)
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
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 :: [([Char], Int)] -> [Int] -> [(Int, [([Char], Int)])]
annotateOvertones [([Char], Int)]
tuning [Int]
pitches = (Int -> (Int, [([Char], Int)]))
-> [Int] -> [(Int, [([Char], Int)])]
forall a b. (a -> b) -> [a] -> [b]
map Int -> (Int, [([Char], 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, [([Char], Int)])
annotate Int
p = (Int
p, (([Char], Int) -> [([Char], Int)])
-> [([Char], Int)] -> [([Char], Int)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Int -> ([Char], Int) -> [([Char], Int)]
forall {a}. Int -> (a, Int) -> [(a, Int)]
sourcesFor Int
p) [([Char], 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 :: [([Char], Int)] -> [Int] -> (Int -> [Char]) -> [Char]
formatOvertoneAnnotation [([Char], Int)]
tuning [Int]
pitches Int -> [Char]
pcToName =
let annotated :: [(Int, [([Char], Int)])]
annotated = [([Char], Int)] -> [Int] -> [(Int, [([Char], Int)])]
annotateOvertones [([Char], Int)]
tuning [Int]
pitches
entries :: [[Char]]
entries = [ Int -> [Char]
pcToName Int
p [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
": " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [([Char], Int)] -> [Char]
forall {a}. Show a => [([Char], a)] -> [Char]
formatSources [([Char], Int)]
sources
| (Int
p, [([Char], Int)]
sources) <- [(Int, [([Char], Int)])]
annotated
, Bool -> Bool
not ([([Char], Int)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [([Char], Int)]
sources)
]
in if [[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
entries then [Char]
"" else [Char]
"{" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
", " [[Char]]
entries [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"}"
where
formatSources :: [([Char], a)] -> [Char]
formatSources [([Char], a)]
sources =
let grouped :: [([Char], [a])]
grouped = [([Char], a)] -> [([Char], [a])]
forall {a} {a}. Eq a => [(a, a)] -> [(a, [a])]
groupByString [([Char], a)]
sources
in [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"/" [ [Char]
name [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [a] -> [Char]
forall {a}. Show a => [a] -> [Char]
formatNums [a]
nums | ([Char]
name, [a]
nums) <- [([Char], [a])]
grouped ]
formatNums :: [a] -> [Char]
formatNums [a
n] = a -> [Char]
forall a. Show a => a -> [Char]
show a
n
formatNums [a]
ns = [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"+" ((a -> [Char]) -> [a] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map a -> [Char]
forall a. Show a => a -> [Char]
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 :: [([Char], Int)] -> [Int] -> (Int -> [Char]) -> [Char]
formatOvertoneAnnotationPipe [([Char], Int)]
tuning [Int]
pitches Int -> [Char]
pcToName =
let annotated :: [(Int, [([Char], Int)])]
annotated = [([Char], Int)] -> [Int] -> [(Int, [([Char], Int)])]
annotateOvertones [([Char], Int)]
tuning [Int]
pitches
entries :: [[Char]]
entries = [ Int -> [Char]
pcToName Int
p [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
": " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [([Char], Int)] -> [Char]
forall {a}. Show a => [([Char], a)] -> [Char]
formatSources [([Char], Int)]
sources
| (Int
p, [([Char], Int)]
sources) <- [(Int, [([Char], Int)])]
annotated
, Bool -> Bool
not ([([Char], Int)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [([Char], Int)]
sources)
]
in if [[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
entries then [Char]
"" else [Char]
"overtones=| " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ ([Char] -> [Char]) -> [[Char]] -> [Char]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\[Char]
e -> [Char]
e [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" | ") [[Char]]
entries
where
formatSources :: [([Char], a)] -> [Char]
formatSources [([Char], a)]
sources =
let grouped :: [([Char], [a])]
grouped = [([Char], a)] -> [([Char], [a])]
forall {a} {a}. Eq a => [(a, a)] -> [(a, [a])]
groupByString [([Char], a)]
sources
in [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"/" [ [Char]
name [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [a] -> [Char]
forall {a}. Show a => [a] -> [Char]
formatNums [a]
nums | ([Char]
name, [a]
nums) <- [([Char], [a])]
grouped ]
formatNums :: [a] -> [Char]
formatNums [a
n] = a -> [Char]
forall a. Show a => a -> [Char]
show a
n
formatNums [a]
ns = [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"+" ((a -> [Char]) -> [a] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map a -> [Char]
forall a. Show a => a -> [Char]
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 ]