hmt-0.18: Music/Theory/Z/TTO.hs
-- | Generalised twelve-tone operations on un-ordered pitch-class sets with arbitrary Z.
module Music.Theory.Z.TTO where
import Data.List {- base -}
import Data.Maybe {- base -}
import qualified Text.Parsec as P {- parsec -}
import qualified Text.Parsec.String as P {- parsec -}
import qualified Music.Theory.Parse as Parse {- hmt -}
import Music.Theory.Z {- hmt -}
-- * TTO
-- | Twelve-tone operator, of the form TMI.
data TTO t = TTO {tto_T :: t,tto_M :: t,tto_I :: Bool}
deriving (Eq,Show)
-- | T0
tto_identity :: Num t => TTO t
tto_identity = TTO 0 1 False
-- | Pretty printer. It is an error here is M is not 1 or 5.
tto_pp :: (Show t,Num t,Eq t) => TTO t -> String
tto_pp (TTO t m i) =
concat ['T' : show t
,if m == 1 then "" else if m == 5 then "M" else error "tto_pp: M?"
,if i then "I" else ""]
-- | Parser for TTO, requires value for M (ordinarily 5 for 12-tone TTO).
p_tto :: Integral t => t -> P.GenParser Char () (TTO t)
p_tto m_mul = do
_ <- P.char 'T'
t <- Parse.parse_int
m <- Parse.is_char 'M'
i <- Parse.is_char 'I'
P.eof
return (TTO t (if m then m_mul else 1) i)
-- | Parser, transposition must be decimal.
--
-- > map (tto_pp . tto_parse 5) (words "T5 T3I T11M T9MI") == ["T5","T3I","T11M","T9MI"]
tto_parse :: Integral i => i -> String -> TTO i
tto_parse m = either (\e -> error ("tto_parse failed\n" ++ show e)) id . P.parse (p_tto m) ""
-- | Set M at TTO.
tto_M_set :: Integral t => t -> TTO t -> TTO t
tto_M_set m (TTO t _ i) = TTO t m i
-- * Z
-- | The set of all 'TTO', given 'Z'.
--
-- > length (z_tto_univ 5 z12) == 48
-- > map tto_pp (z_tto_univ 5 z12)
z_tto_univ :: Integral t => t -> Z t -> [TTO t]
z_tto_univ m_mul z = [TTO t m i | m <- [1,m_mul], i <- [False,True], t <- z_univ z]
-- | Apply TTO to pitch-class.
--
-- > map (z_tto_f z12 (tto_parse 5 "T1M")) [0,1,2,3] == [1,6,11,4]
z_tto_f :: Integral t => Z t -> TTO t -> (t -> t)
z_tto_f z (TTO t m i) =
let i_f = if i then z_negate z else id
m_f = if m == 1 then id else z_mul z m
t_f = if t > 0 then z_add z t else id
in t_f . m_f . i_f
-- | 'nub' of 'sort' of 'z_tto_f'. (nub because M may be 0).
--
-- > z_tto_apply z12 (tto_parse 5 "T1M") [0,1,2,3] == [1,4,6,11]
z_tto_apply :: Integral t => Z t -> TTO t -> [t] -> [t]
z_tto_apply z o = nub . sort . map (z_tto_f z o)
-- | Find 'TTO's that map pc-set /x/ to pc-set /y/ given /m/ and /z/.
--
-- > map tto_pp (z_tto_rel 5 z12 [0,1,2,3] [1,4,6,11]) == ["T1M","T4MI"]
z_tto_rel :: (Ord t,Integral t) => t -> Z t -> [t] -> [t] -> [TTO t]
z_tto_rel m z x y =
let f o = if z_tto_apply z o x == y then Just o else Nothing
in mapMaybe f (z_tto_univ m z)
-- * PLAIN
-- | 'nub' of 'sort' of 'z_mod' of /z/.
--
-- > z_pcset z12 [1,13] == [1]
-- > map (z_pcset z12) [[0,6],[6,12],[12,18]] == replicate 3 [0,6]
z_pcset :: (Integral t,Ord t) => Z t -> [t] -> [t]
z_pcset z = nub . sort . map (z_mod z)
-- | Transpose by n.
--
-- > z_tto_tn z12 4 [1,5,6] == [5,9,10]
-- > z_tto_tn z12 4 [0,4,8] == [0,4,8]
z_tto_tn :: Integral i => Z i -> i -> [i] -> [i]
z_tto_tn z n = sort . map (z_add z n)
-- | Invert about n.
--
-- > z_tto_invert z12 6 [4,5,6] == [6,7,8]
-- > z_tto_invert z12 0 [0,1,3] == [0,9,11]
z_tto_invert :: Integral i => Z i -> i -> [i] -> [i]
z_tto_invert z n = sort . map (\p -> z_sub z n (z_sub z p n))
-- | Composition of 'z_tto_invert' about @0@ and 'z_tto_tn'.
--
-- > z_tto_tni z12 4 [1,5,6] == [3,10,11]
-- > (z_tto_invert z12 0 . z_tto_tn z12 4) [1,5,6] == [2,3,7]
z_tto_tni :: Integral i => Z i -> i -> [i] -> [i]
z_tto_tni z n = z_tto_tn z n . z_tto_invert z 0
-- | Modulo-z multiplication
--
-- > z_tto_mn z12 11 [0,1,4,9] == z_tto_invert z12 0 [0,1,4,9]
z_tto_mn :: Integral i => Z i -> i -> [i] -> [i]
z_tto_mn z n = sort . map (z_mul z n)
-- | M5, ie. 'mn' @5@.
--
-- > z_tto_m5 z12 [0,1,3] == [0,3,5]
z_tto_m5 :: Integral i => Z i -> [i] -> [i]
z_tto_m5 z = z_tto_mn z 5
-- * SEQUENCE
-- | T-related sets of /p/.
z_tto_t_related_seq :: Integral i => Z i -> [i] -> [[i]]
z_tto_t_related_seq z p = map (\q -> z_tto_tn z q p) [0..11]
-- | Unique elements of 'z_tto_t_related_seq'.
--
-- > length (z_tto_t_related z12 [0,1,3]) == 12
-- > z_tto_t_related z12 [0,3,6,9] == [[0,3,6,9],[1,4,7,10],[2,5,8,11]]
z_tto_t_related :: Integral i => Z i -> [i] -> [[i]]
z_tto_t_related z = nub . z_tto_t_related_seq z
-- | T\/I-related set of /p/.
z_tto_ti_related_seq :: Integral i => Z i -> [i] -> [[i]]
z_tto_ti_related_seq z p = z_tto_t_related z p ++ z_tto_t_related z (z_tto_invert z 0 p)
-- | Unique elements of 'z_tto_ti_related_seq'.
--
-- > length (z_tto_ti_related z12 [0,1,3]) == 24
-- > z_tto_ti_related z12 [0,3,6,9] == [[0,3,6,9],[1,4,7,10],[2,5,8,11]]
z_tto_ti_related :: Integral i => Z i -> [i] -> [[i]]
z_tto_ti_related z = nub . z_tto_ti_related_seq z