module HarmTrace.Base.Chord.Analysis (
analyseTriad
, analyseTetra
, toTriad
, toMajMinChord
, Third (..)
, Fifth (..)
, Sevth (..)
, analyseThird
, analyseFifth
, analyseSevth
, toMode
, toMajMin
, toClassType
, isSus2
, isSus4
, transposeRoot
, transposeSD
, toChordDegree
, toScaleDegree
, intervalToPitch
, icToInterval
, toChord
) where
import HarmTrace.Base.Chord.Datatypes
import HarmTrace.Base.Chord.PitchClass
import HarmTrace.Base.Chord.Intervals
import HarmTrace.Base.Chord.Internal
import Data.IntSet ( IntSet, toAscList, member, (\\) )
toClassType :: Chord a -> ClassType
toClassType (Chord _r sh [] _b) = shToClassType sh
toClassType c = analyseDegClassType . toIntSet $ c
analyseDegClassType :: IntSet -> ClassType
analyseDegClassType degs =
case (analyseThird degs, analyseFifth degs, analyseSevth degs) of
(MinThird, DimFifth , DimSev) -> DimClass
(MajThird, _ , MinSev) -> DomClass
(_ , AugFifth , _ ) -> DomClass
(MajThird, DimFifth , _ ) -> DomClass
(MajThird, _ , _ ) -> MajClass
(MinThird, PerfFifth, _ ) -> MinClass
(MinThird, _ , _ ) -> MinClass
(NoThird, _ , _ ) -> NoClass
shToClassType :: Shorthand -> ClassType
shToClassType Maj = MajClass
shToClassType Min = MinClass
shToClassType Dim = DimClass
shToClassType Aug = DomClass
shToClassType Maj7 = MajClass
shToClassType Min7 = MinClass
shToClassType Sev = DomClass
shToClassType Dim7 = DimClass
shToClassType HDim7 = MinClass
shToClassType MinMaj7 = MinClass
shToClassType Aug7 = DomClass
shToClassType Maj6 = MajClass
shToClassType Min6 = MinClass
shToClassType Nin = DomClass
shToClassType Maj9 = MajClass
shToClassType Min9 = MinClass
shToClassType Five = NoClass
shToClassType Sus2 = NoClass
shToClassType Sus4 = NoClass
shToClassType SevSus4 = NoClass
shToClassType None = NoClass
shToClassType Min11 = MinClass
shToClassType Eleven = DomClass
shToClassType Min13 = MinClass
shToClassType Maj13 = MajClass
shToClassType Thirteen = DomClass
data Third = MajThird | MinThird | NoThird deriving (Eq, Show)
data Fifth = DimFifth | PerfFifth | AugFifth | NoFifth deriving (Eq, Show)
data Sevth = DimSev | MinSev | MajSev | NoSev deriving (Eq, Show)
triadToSh :: Triad -> Shorthand
triadToSh t = case t of
MajTriad -> Maj
MinTriad -> Min
AugTriad -> Aug
DimTriad -> Dim
NoTriad -> None
analyseTetra :: IntSet -> Shorthand
analyseTetra is = case (analyseTriad is, analyseSevth is) of
(MajTriad, MinSev) -> Sev
(MajTriad, MajSev) -> Maj7
(MinTriad, MinSev) -> Min7
(MinTriad, MajSev) -> MinMaj7
(DimTriad, MinSev) -> HDim7
(DimTriad, DimSev) -> Dim7
(AugTriad, MinSev) -> Aug7
(t , NoSev ) -> triadToSh t
_ -> None
toTriad :: Chord a -> Triad
toTriad NoChord = error "toTriad: a NoChord has no triad to analyse"
toTriad UndefChord = error "toTriad: a UndefChord has no triad to analyse"
toTriad (Chord _r sh [] _b) = shToTriad sh
toTriad c = analyseTriad . toIntSet $ c
analyseTriad :: IntSet -> Triad
analyseTriad is =
case (analyseThird is, analyseFifth is) of
(MajThird, PerfFifth) -> MajTriad
(MajThird, AugFifth ) -> AugTriad
(MajThird, DimFifth ) -> NoTriad
(MinThird, PerfFifth) -> MinTriad
(MinThird, AugFifth ) -> NoTriad
(MinThird, DimFifth ) -> DimTriad
(NoThird, _ ) -> NoTriad
(_ , NoFifth ) -> NoTriad
analyseThird :: IntSet -> Third
analyseThird is
| member 4 is = MajThird
| member 3 is = MinThird
| otherwise = NoThird
analyseFifth :: IntSet -> Fifth
analyseFifth is
| member 7 is = PerfFifth
| member 6 is = DimFifth
| member 8 is = AugFifth
| otherwise = NoFifth
analyseSevth :: IntSet -> Sevth
analyseSevth is
| member 10 is = MinSev
| member 11 is = MajSev
| member 9 is = DimSev
| otherwise = NoSev
shToTriad :: Shorthand -> Triad
shToTriad Maj = MajTriad
shToTriad Min = MinTriad
shToTriad Dim = DimTriad
shToTriad Aug = AugTriad
shToTriad Maj7 = MajTriad
shToTriad Min7 = MinTriad
shToTriad Sev = MajTriad
shToTriad Dim7 = DimTriad
shToTriad HDim7 = DimTriad
shToTriad MinMaj7 = MinTriad
shToTriad Aug7 = AugTriad
shToTriad Maj6 = MajTriad
shToTriad Min6 = MinTriad
shToTriad Nin = MajTriad
shToTriad Maj9 = MajTriad
shToTriad Min9 = MinTriad
shToTriad Five = NoTriad
shToTriad Sus2 = NoTriad
shToTriad Sus4 = NoTriad
shToTriad SevSus4 = NoTriad
shToTriad None = NoTriad
shToTriad Min11 = MinTriad
shToTriad Eleven = MajTriad
shToTriad Min13 = MinTriad
shToTriad Maj13 = MajTriad
shToTriad Thirteen = MajTriad
toMode :: Triad -> Mode
toMode MajTriad = MajMode
toMode MinTriad = MinMode
toMode t = error ( "HarmTrace.Base.MusicRep.toMode: cannot convert "
++ " triad to mode: " ++ show t)
toMajMin :: Triad -> ClassType
toMajMin MajTriad = MajClass
toMajMin MinTriad = MinClass
toMajMin AugTriad = MajClass
toMajMin DimTriad = MinClass
toMajMin NoTriad = NoClass
toMajMinChord :: ChordLabel -> ChordLabel
toMajMinChord NoChord = NoChord
toMajMinChord UndefChord = UndefChord
toMajMinChord c@(Chord r _ _ b) = case toMajMin (toTriad c) of
MajClass -> Chord r Maj [] b
MinClass -> Chord r Min [] b
NoClass -> UndefChord
_ -> error ("HarmTrace.Base.MusicRep.toMajMinChord"
++ " unexpected chord " ++ show c)
isSus2 :: ChordLabel -> Bool
isSus2 c = let is = toIntSet c in member 2 is
&& not (member 3 is)
&& not (member 4 is)
&& not (member 5 is)
isSus4 :: ChordLabel -> Bool
isSus4 c = let is = toIntSet c in not (member 2 is)
&& not (member 3 is)
&& not (member 4 is)
&& (member 5 is)
toChordDegree :: Key -> ChordLabel -> ChordDegree
toChordDegree k (Chord r sh a b) = Chord (toScaleDegree k r) sh a b
toChordDegree _ c =
error("HarmTrace.Base.Chord.Analysis: cannot create scale degree for " ++ show c)
toScaleDegree :: Key -> Root -> ScaleDegree
toScaleDegree (Key kr _) cr =
scaleDegrees!!(((toPitchClass cr) (toPitchClass kr)) `mod` 12)
transposeRoot :: Root -> Int -> Root
transposeRoot deg sem = transpose roots deg sem
transposeSD :: ScaleDegree -> Int -> ScaleDegree
transposeSD deg sem = transpose scaleDegrees deg sem
transpose :: Diatonic a => [Note a] -> Note a -> Int -> Note a
transpose ns n sem = ns !! ((sem + (toPitchClass n)) `mod` 12)
intervalToPitch :: Root -> Interval -> Root
intervalToPitch r = pcToRoot . intValToPitchClss r
toChord :: Root -> IntSet -> Maybe Interval -> Chord Root
toChord r is mi = Chord r sh add (maybe (Note Nat I1) id mi)
where add = map (Add . icToInterval) $ toAscList (is \\ shToIntSet sh)
sh = analyseTetra is