This module implements generic Motive. This specifications fits Melodic Motive,
Rhythm Motive and so on.
module Music.Analysis.Abstract.Motive where
import Music.Analysis.PF ((><), cataL, mapL, p1, p2, split,
anaL, grd, (-|-), e2m)
import Music.Analysis.Abstract.Settings (Settings, union, empty)
import Data.Tuple (curry, uncurry)
import Data.List (zip, head, tail)
import Data.Function ((.), id, const)
import Data.Maybe (maybe)
import Data.Eq (Eq(..))
import Prelude (Show, Read)
Basic definition is product between Settings and Sequence of nodes. These nodes
contains music information.
data Motive a = Motive (Settings, [a])
deriving (Eq, Show, Read)
mkMotive :: Settings -> [a] -> Motive a
mkMotive = curry Motive
fromMotive :: Motive a -> (Settings, [a])
fromMotive (Motive x) = x
toMotive :: (Settings, [a]) -> Motive a
toMotive = Motive
Function meta is responsible for update Settings, nodes will be same.
Next functions, like cataMotive and mapMotive are catamorphism and mapping
applied to Motive data type. These functions doesn't change metadata at
meta :: (Settings -> Settings) -> Motive a -> Motive a
meta f = toMotive . (f >< id) . fromMotive
cataMotive :: b -> (Settings -> (a, b) -> b) -> Motive a -> (Settings, b)
cataMotive z f = split p1 (\(s,x) -> cataL (maybe z (f s)) x) . fromMotive
mapMotive :: (Settings -> a -> b) -> Motive a -> Motive b
mapMotive f = toMotive . (split p1 (\(s,x) -> mapL (f s) x)) . fromMotive
These functions are used to reusing functions.
joinMotivePair :: (Motive a, Motive b) -> Motive (a,b)
(uncurry union >< uncurry zip) .
split (p1 >< p1) (p2 >< p2) .
(fromMotive >< fromMotive)
splitMotivePair :: Motive (a,b) -> (Motive a, Motive b)
splitMotivePair = split (mapMotive (const p1)) (mapMotive (const p2))
joinMotiveList :: [Motive a] -> Motive [a]
cataL (maybe (empty,) (\((a,b),(c,d)) -> (a`union`c, b:d))) .
splitMotiveList :: Eq a => Motive [a] -> [Motive a]
mapL toMotive .
anaL (e2m . (const () -|- split (id >< head) (id >< tail)) . grd ((==) . p2)) .