module Sound.Tidal.Chords where {- Chords.hs - For .. chords Copyright (C) 2020, Alex McLean and contributors This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library. If not, see . -} import Data.Maybe import Sound.Tidal.Pattern -- major chords major :: Num a => [a] major = [0,4,7] aug :: Num a => [a] aug = [0,4,8] six :: Num a => [a] six = [0,4,7,9] sixNine :: Num a => [a] sixNine = [0,4,7,9,14] major7 :: Num a => [a] major7 = [0,4,7,11] major9 :: Num a => [a] major9 = [0,4,7,11,14] add9 :: Num a => [a] add9 = [0,4,7,14] major11 :: Num a => [a] major11 = [0,4,7,11,14,17] add11 :: Num a => [a] add11 = [0,4,7,17] major13 :: Num a => [a] major13 = [0,4,7,11,14,21] add13 :: Num a => [a] add13 = [0,4,7,21] -- dominant chords dom7 :: Num a => [a] dom7 = [0,4,7,10] dom9 :: Num a => [a] dom9 = [0,4,7,14] dom11 :: Num a => [a] dom11 = [0,4,7,17] dom13 :: Num a => [a] dom13 = [0,4,7,21] sevenFlat5 :: Num a => [a] sevenFlat5 = [0,4,6,10] sevenSharp5 :: Num a => [a] sevenSharp5 = [0,4,8,10] sevenFlat9 :: Num a => [a] sevenFlat9 = [0,4,7,10,13] nine :: Num a => [a] nine = [0,4,7,10,14] eleven :: Num a => [a] eleven = [0,4,7,10,14,17] thirteen :: Num a => [a] thirteen = [0,4,7,10,14,17,21] -- minor chords minor :: Num a => [a] minor = [0,3,7] diminished :: Num a => [a] diminished = [0,3,6] minorSharp5 :: Num a => [a] minorSharp5 = [0,3,8] minor6 :: Num a => [a] minor6 = [0,3,7,9] minorSixNine :: Num a => [a] minorSixNine = [0,3,9,7,14] minor7flat5 :: Num a => [a] minor7flat5 = [0,3,6,10] minor7 :: Num a => [a] minor7 = [0,3,7,10] minor7sharp5 :: Num a => [a] minor7sharp5 = [0,3,8,10] minor7flat9 :: Num a => [a] minor7flat9 = [0,3,7,10,13] minor7sharp9 :: Num a => [a] minor7sharp9 = [0,3,7,10,14] diminished7 :: Num a => [a] diminished7 = [0,3,6,9] minor9 :: Num a => [a] minor9 = [0,3,7,10,14] minor11 :: Num a => [a] minor11 = [0,3,7,10,14,17] minor13 :: Num a => [a] minor13 = [0,3,7,10,14,17,21] minorMajor7 :: Num a => [a] minorMajor7 = [0,3,7,11] -- other chords one :: Num a => [a] one = [0] five :: Num a => [a] five = [0,7] sus2 :: Num a => [a] sus2 = [0,2,7] sus4 :: Num a => [a] sus4 = [0,5,7] sevenSus2 :: Num a => [a] sevenSus2 = [0,2,7,10] sevenSus4 :: Num a => [a] sevenSus4 = [0,5,7,10] nineSus4 :: Num a => [a] nineSus4 = [0,5,7,10,14] -- questionable chords sevenFlat10 :: Num a => [a] sevenFlat10 = [0,4,7,10,15] nineSharp5 :: Num a => [a] nineSharp5 = [0,1,13] minor9sharp5 :: Num a => [a] minor9sharp5 = [0,1,14] sevenSharp5flat9 :: Num a => [a] sevenSharp5flat9 = [0,4,8,10,13] minor7sharp5flat9 :: Num a => [a] minor7sharp5flat9 = [0,3,8,10,13] elevenSharp :: Num a => [a] elevenSharp = [0,4,7,10,14,18] minor11sharp :: Num a => [a] minor11sharp = [0,3,7,10,14,18] -- | @chordate cs m n@ selects the @n@th "chord" (a chord is a list of Ints) -- from a list of chords @cs@ and transposes it by @m@ -- chordate :: Num b => [[b]] -> b -> Int -> [b] -- chordate cs m n = map (+m) $ cs!!n -- | @enchord chords pn pc@ turns every note in the note pattern @pn@ into -- a chord, selecting from the chord lists @chords@ using the index pattern -- @pc@. For example, @Chords.enchord [Chords.major Chords.minor] "c g" "0 1"@ -- will create a pattern of a C-major chord followed by a G-minor chord. -- enchord :: Num a => [[a]] -> Pattern a -> Pattern Int -> Pattern a -- enchord chords pn pc = flatpat $ (chordate chords) <$> pn <*> pc chordTable :: Num a => [(String, [a])] chordTable = [("major", major), ("maj", major), ("M", major), ("aug", aug), ("plus", aug), ("sharp5", aug), ("six", six), ("6", six), ("sixNine", sixNine), ("six9", sixNine), ("sixby9", sixNine), ("6by9", sixNine), ("major7", major7), ("maj7", major7), ("major9", major9), ("maj9", major9), ("add9", add9), ("major11", major11), ("maj11", major11), ("add11", add11), ("major13", major13), ("maj13", major13), ("add13", add13), ("dom7", dom7), ("dom9", dom9), ("dom11", dom11), ("dom13", dom13), ("sevenFlat5", sevenFlat5), ("7f5", sevenFlat5), ("sevenSharp5", sevenSharp5), ("7s5", sevenSharp5), ("sevenFlat9", sevenFlat9), ("7f9", sevenFlat9), ("nine", nine), ("eleven", eleven), ("11", eleven), ("thirteen", thirteen), ("13", thirteen), ("minor", minor), ("min", minor), ("m", minor), ("diminished", diminished), ("dim", diminished), ("minorSharp5", minorSharp5), ("msharp5", minorSharp5), ("mS5", minorSharp5), ("minor6", minor6), ("min6", minor6), ("m6", minor6), ("minorSixNine", minorSixNine), ("minor69", minorSixNine), ("min69", minorSixNine), ("minSixNine", minorSixNine), ("m69", minorSixNine), ("mSixNine", minorSixNine), ("m6by9", minorSixNine), ("minor7flat5", minor7flat5), ("minor7f5", minor7flat5), ("min7flat5", minor7flat5), ("min7f5", minor7flat5), ("m7flat5", minor7flat5), ("m7f5", minor7flat5), ("minor7", minor7), ("min7", minor7), ("m7", minor7), ("minor7sharp5", minor7sharp5), ("minor7s5", minor7sharp5), ("min7sharp5", minor7sharp5), ("min7s5", minor7sharp5), ("m7sharp5", minor7sharp5), ("m7s5", minor7sharp5), ("minor7flat9", minor7flat9), ("minor7f9", minor7flat9), ("min7flat9", minor7flat9), ("min7f9", minor7flat9), ("m7flat9", minor7flat9), ("m7f9", minor7flat9), ("minor7sharp9", minor7sharp9), ("minor7s9", minor7sharp9), ("min7sharp9", minor7sharp9), ("min7s9", minor7sharp9), ("m7sharp9", minor7sharp9), ("m7s9", minor7sharp9), ("diminished7", diminished7), ("dim7", diminished7), ("minor9", minor9), ("min9", minor9), ("m9", minor9), ("minor11", minor11), ("min11", minor11), ("m11", minor11), ("minor13", minor13), ("min13", minor13), ("m13", minor13), ("minorMajor7", minorMajor7), ("minMaj7", minorMajor7), ("mmaj7", minorMajor7), ("one", one), ("1", one), ("five", five), ("5", five), ("sus2", sus2), ("sus4", sus4), ("sevenSus2", sevenSus2), ("7sus2", sevenSus2), ("sevenSus4", sevenSus4), ("7sus4", sevenSus4), ("nineSus4", nineSus4), ("ninesus4", nineSus4), ("9sus4", nineSus4), ("sevenFlat10", sevenFlat10), ("7f10", sevenFlat10), ("nineSharp5", nineSharp5), ("9sharp5", nineSharp5), ("9s5", nineSharp5), ("minor9sharp5", minor9sharp5), ("minor9s5", minor9sharp5), ("min9sharp5", minor9sharp5), ("min9s5", minor9sharp5), ("m9sharp5", minor9sharp5), ("m9s5", minor9sharp5), ("sevenSharp5flat9", sevenSharp5flat9), ("7s5f9", sevenSharp5flat9), ("minor7sharp5flat9", minor7sharp5flat9), ("m7sharp5flat9", minor7sharp5flat9), ("elevenSharp", elevenSharp), ("11s", elevenSharp), ("minor11sharp", minor11sharp), ("m11sharp", minor11sharp), ("m11s", minor11sharp) ] chordL :: Num a => Pattern String -> Pattern [a] chordL p = (\name -> fromMaybe [] $ lookup name chordTable) <$> p chordList :: String chordList = unwords $ map fst (chordTable :: [(String, [Int])]) data Modifier = Range Int | Drop Int | Invert | Open deriving Eq instance Show Modifier where show (Range i) = "Range " ++ show i show (Drop i) = "Drop " ++ show i show Invert = "Invert" show Open = "Open" applyModifier :: (Enum a, Num a) => Modifier -> [a] -> [a] applyModifier (Range i) ds = take i $ concatMap (\x -> map (+ x) ds) [0,12..] applyModifier Invert [] = [] applyModifier Invert (d:ds) = ds ++ [d+12] applyModifier Open ds = case length ds > 2 of True -> [ (ds !! 0 - 12), (ds !! 2 - 12), (ds !! 1) ] ++ reverse (take (length ds - 3) (reverse ds)) False -> ds applyModifier (Drop i) ds = case length ds < i of True -> ds False -> (ds!!s - 12):(xs ++ drop 1 ys) where (xs,ys) = splitAt s ds s = length ds - i applyModifierPat :: (Num a, Enum a) => Pattern [a] -> Pattern [Modifier] -> Pattern [a] applyModifierPat pat modsP = do ch <- pat ms <- modsP return $ foldl (flip applyModifier) ch ms applyModifierPatSeq :: (Num a, Enum a) => (a -> b) -> Pattern [a] -> [Pattern [Modifier]] -> Pattern [b] applyModifierPatSeq f pat [] = fmap (map f) pat applyModifierPatSeq f pat (mP:msP) = applyModifierPatSeq f (applyModifierPat pat mP) msP chordToPatSeq :: (Num a, Enum a) => (a -> b) -> Pattern a -> Pattern String -> [Pattern [Modifier]] -> Pattern b chordToPatSeq f noteP nameP modsP = uncollect $ do n <- noteP name <- nameP let ch = map (+ n) (fromMaybe [0] $ lookup name chordTable) applyModifierPatSeq f (return ch) modsP -- | turns a given pattern of some Num type, a pattern of chord names and a list of patterns of modifiers into a chord pattern chord :: (Num a, Enum a) => Pattern a -> Pattern String -> [Pattern [Modifier]] -> Pattern a chord = chordToPatSeq id