module Reactive.Banana.MIDI.Time where
import qualified Reactive.Banana.Bunch.Combinators as RB
import qualified Numeric.NonNegative.Class as NonNeg
import Control.Applicative (Const(Const), )
import Data.Monoid (Monoid, mempty, mappend, )
import Data.Semigroup (Semigroup, (<>), )
import Data.Tuple.HT (mapPair, mapSnd, )
import Data.Ord.HT (comparing, )
import Data.Eq.HT (equating, )
import Prelude hiding (div, )
newtype T m t a = Cons (Const a (m t))
instance Show a => Show (T m t a) where
showsPrec n x =
showParen (n>10) $
showString "Time.cons " . shows (decons x)
instance Eq a => Eq (T m t a) where
(==) = equating decons
instance Ord a => Ord (T m t a) where
compare = comparing decons
cons :: a -> T m t a
cons = Cons . Const
decons :: T m t a -> a
decons (Cons (Const a)) = a
relative ::
(Ord a, Monoid a) =>
String -> a -> T m Relative a
relative name t =
if t>=mempty
then cons t
else error $ name ++ ": negative time"
data Absolute = Absolute
data Relative = Relative
newtype Seconds = Seconds {unSeconds :: Rational}
deriving (Show, Eq, Ord)
newtype Ticks = Ticks {unTicks :: Integer}
deriving (Show, Eq, Ord)
instance Semigroup Seconds where
Seconds x <> Seconds y = Seconds $ x+y
instance Monoid Seconds where
mempty = Seconds 0
mappend = (<>)
instance Semigroup Ticks where
Ticks x <> Ticks y = Ticks $ x+y
instance Monoid Ticks where
mempty = Ticks 0
mappend = (<>)
instance (Semigroup a) => Semigroup (T m t a) where
x <> y = cons $ decons x <> decons y
instance (Monoid a) => Monoid (T m t a) where
mempty = cons mempty
mappend x y = cons $ mappend (decons x) (decons y)
class RelativeC t where
instance RelativeC Relative where
class (Ord a, Monoid a) => Split a where
split :: a -> a -> (a, (Bool, a))
instance Split Seconds where
split = NonNeg.splitDefault unSeconds Seconds
instance Split Ticks where
split = NonNeg.splitDefault unTicks Ticks
instance (RelativeC t, Split a) => NonNeg.C (T m t a) where
split x y =
mapPair (cons, mapSnd cons) $ split (decons x) (decons y)
class RB.MonadMoment m => Timed m where
ticksFromSeconds :: T m t Seconds -> m (T m t Ticks)
class Quantity a where
ticksFromAny :: (Timed m) => T m t a -> m (T m t Ticks)
instance Quantity Seconds where
ticksFromAny = ticksFromSeconds
instance Quantity Ticks where
ticksFromAny = return
consRel :: String -> Rational -> T m Relative Seconds
consRel msg x =
if x>=0
then cons $ Seconds x
else error $ msg ++ ": negative number"
inc ::
(Monoid a) =>
T m Relative a -> T m t a -> T m t a
inc dt t = cons $ mappend (decons t) (decons dt)
subSat ::
Split a => T m t a -> T m t a -> T m Relative a
subSat t1 t0 =
let (b,d) = snd $ split (decons t0) (decons t1)
in cons $ if b then d else mempty
scale, scaleCeiling :: Double -> T m Relative Ticks -> T m Relative Ticks
scale k t =
cons $ Ticks $ round $ toRational k * getTicks t
scaleCeiling k t =
cons $ Ticks $ ceiling $ toRational k * getTicks t
scaleInt :: Integral i => i -> T m Relative Ticks -> T m Relative Ticks
scaleInt k t =
cons $ Ticks $ getTicks t * fromIntegral k
div :: T m Relative Ticks -> T m Relative Ticks -> Double
div dt1 dt0 = getTicks dt1 / getTicks dt0
getTicks :: Num a => T m Relative Ticks -> a
getTicks = fromInteger . unTicks . decons