\begin{code}
-- | 
-- Maintainer : silva.samuel@alumni.uminho.pt
-- Stability  : experimental
-- Portability: portable
-- This module implements Rhythm Motive
module Music.Analysis.Abstract.Rhythm where
import Music.Analysis.PF ((><), swap, p2, split, p1, grd, (-|-), assocl,
    mapL, cataL, e2m, hyloL)
import Music.Analysis.Abstract.Settings (Settings, getNumber, changeText, 
    fromList, number, priority)
import Music.Analysis.Abstract.Motive (Motive, fromMotive, toMotive,
    mapMotive, cataMotive)
import Music.Analysis.Base (Number, Delta, Text, 
    IntegerNumber, RatioNumber, toRatio, toInteger)
import Data.Maybe (maybe)
import Data.Function (id, const, (.))
import Data.Ord (Ord(..))
import Data.Eq (Eq(..))
import Data.Tuple (uncurry)
import Data.List ((++), zip, tail, init, unzip)
import Prelude (Num(..), (/), Show(..),(^), read) 
\end{code} RhythmNode is defined as pair of Delta, variation on durations, and number of dots. Mandatory settings are: \begin{description} \item[TempoPitch] by default is number 4, \item[TempoNumber] by default is number 60, \item[CompassUp] by default is number 4 \item[CompassDown] by default is number 4. \end{description} \begin{code}
-- * Types
-- | Rhythm node
type RhythmNode = (Delta, Dots)
--type RhythmAbsolute = (Number, Dots)
--type RhythmRelative = (Delta, Dots)
type RhythmAbsolute = (RatioNumber, Dots)
type RhythmRelative = (RatioNumber, Dots)
-- | Dots is defined by number.
-- Only Integers and positive numbers are allowed.
type Dots = IntegerNumber
type Duration = Number
data DurationClass = Whole 
                   | Half 
                   | Quarter 
                   | Eighth 
                   | Th16
                   | Th32 
                   | Th64 
                   | UnkownDuration Text
    deriving (Eq, Show)
-- | sefault settings
settings :: Settings
settings = fromList [
                ("TempoPitch",  number 4    priority),
                ("TempoNumber", number 60   priority),
                ("CompassUp",   number 4    priority),
                ("CompassDown", number 4    priority)]
\end{code} \begin{code}
-- * Auxiliary functions
-- | computes duration /PW/
durationNode :: RhythmNode -> Number
durationNode = 
    hyloL 
        (maybe 0 (uncurry (+))) 
        (e2m . (const () -|- split p1 ((/2) >< pred)) . grd ((<=0).p2)) .
      (id >< succ)
        where pred x = x - 1
              succ x = x + 1    
-- | computes compass duration
compass :: (Number,Number) -> Number
compass (x,y) = x/y
\end{code} These functions are combinators over Rhythm. \begin{code}
-- | changes duration
tempo :: RatioNumber -> Motive RhythmAbsolute -> Motive RhythmAbsolute
tempo n = mapMotive (const ((*n) >< id))
-- | computes duration
duration :: Motive RhythmNode -> Number
duration = p2 . 
    cataMotive 0 (const (uncurry (+) . (durationNode >< id)))
-- | reverse
reverse :: Motive RhythmNode -> Motive RhythmNode
reverse = toMotive . 
    cataMotive [] (const (uncurry (++) . swap . ( (:[]) >< id)))
-- | symmetric trsnformation
symmetric :: RatioNumber -> Motive RhythmAbsolute -> Motive RhythmAbsolute
symmetric n = mapMotive (const ((n +) . (/n) >< id))
\end{code} Relative function assume that Motive have Absolute Type, and change it into Relative Type. Absolute function is computed using Point-Free approach. Actually, it built initial lists from nodes and reverse reversed result, summing it. Attention: Relative and absolute functions use wrong initial number from settings. \begin{code}
-- | Computes relative Rhythm
relative :: Motive RhythmAbsolute -> Motive RhythmRelative
relative = toMotive .
    split (changeText "Type" "Relative" . p1)
        (mapL (split (uncurry (/) . (p1 >< p1)) (p2 . p1)) .
        uncurry zip . (p2 >< uncurry (:) . (initial >< id)) .
        split id id . 
        (maybe (toRatio 4) (toRatio . toInteger) . 
            getNumber "CompassDown" >< id))
    . fromMotive 
    where initial :: RatioNumber -> RhythmAbsolute
          initial = split id (const 0)
-- | Absolute PF
absolute :: Motive RhythmRelative -> Motive RhythmAbsolute
absolute = toMotive .
    split (changeText "Type" "Absolute" . p1) 
        (uncurry zip . (tail . 
            (mapL (cataL (maybe (toRatio 1) (uncurry (*))))) . 
            (hyloL (maybe [] (uncurry (++) . swap . ((:[]) >< id))) 
                   (e2m . ( const () -|- split id init) . grd (==[]))) .
            (uncurry (:)) 
            >< id) .
        (assocl . (id >< unzip)) .
        (maybe (toRatio 4) (toRatio.toInteger) . getNumber "CompassDown" >< id))
     . fromMotive
\end{code} \begin{code}
durationNumber :: DurationClass -> Duration
durationNumber Whole = 4
durationNumber Half  = 2
durationNumber Quarter = 1
durationNumber Eighth = 0.5
durationNumber Th16  = 0.25
durationNumber Th32  = 0.125
durationNumber Th64  = 0.0625
durationNumber (UnkownDuration _) = 1
\end{code} \begin{code}
-- |
durationTotalNumber :: (DurationClass, Dots) -> Duration
durationTotalNumber =
    hyloL 
        (maybe 0 (uncurry (+))) 
        (e2m . (const () -|- split p1 ((/2) >< pred)) . grd ((<=0).p2)) .
      (durationNumber >< succ)
        where pred x = x - 1
              succ x = x + 1 
-- |
getDurationClass :: (Duration,Dots) -> DurationClass
getDurationClass (4,_) = Whole
getDurationClass (2,_) = Half
getDurationClass (1,_) = Quarter
getDurationClass (0.5,_) = Eighth
getDurationClass (0.25,_) = Th16
getDurationClass (0.125,_) = Th32
getDurationClass (0.0625,_) = Th64
getDurationClass (b,0) = UnkownDuration (show b)
getDurationClass (t,i) = getDurationClass (((2^i) / ((2^(i+1)) - 1)) * t, 0)
-- |
getDuration :: (DurationClass, Dots) -> Duration
getDuration =
    hyloL 
        (maybe 0 (uncurry (+))) 
        (e2m . (const () -|- split p1 ((/2) >< pred)) . grd ((<=0).p2)) .
      (durationNumber >< succ)
        where pred x = x - 1
              succ x = x + 1 
-- |
getDuration_aux1 :: DurationClass -> Duration
getDuration_aux1 Whole = 4
getDuration_aux1 Half = 2
getDuration_aux1 Quarter = 1
getDuration_aux1 Eighth = 0.5
getDuration_aux1 Th16 = 0.25
getDuration_aux1 Th32 = 0.125
getDuration_aux1 Th64 = 0.0625
getDuration_aux1 (UnkownDuration x) = read x
\end{code}