{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} ------------------------------------------------------------------------------------- -- | -- Copyright : (c) Hans Hoglund 2012-2014 -- -- License : BSD-style -- -- Maintainer : hans@hanshoglund.se -- Stability : experimental -- Portability : non-portable (TF,GNTD) -- ------------------------------------------------------------------------------------- module Music.Time.Reactive ( -- * Reactive type Reactive, -- * Construction initial, final, intermediate, discrete, updates, occs, atTime, splitReactive, -- * Combinators switchR, trimR, -- * Sampling and discretization continous, continousWith, sample, -- TODO -- window, -- windowed, ) where -- Reactive values, or piecewise functions of time. -- -- Similar to Conal's definition in , -- but defined in negative time as well. Its semantics function is either 'occs' @&&&@ '?' -- /or/ 'initial' @&&&@ 'updates', where 'intial' is the value from negative infinity -- to the first update. -- -- TODO integrate better in the library -- import Music.Time.Behavior import Music.Time.Bound import Music.Time.Note import Music.Time.Reverse import Music.Time.Segment import Music.Time.Split import Music.Pitch.Alterable import Music.Pitch.Augmentable import Music.Pitch.Literal import Control.Applicative import Control.Arrow (first, second, (&&&), (***)) import Control.Lens hiding (Indexable, Level, above, below, index, inside, parts, reversed, transform, (<|), (|>)) import Control.Monad import Control.Monad.Plus import Data.Distributive import Data.Functor.Rep import Data.Functor.Rep.Lens import qualified Data.List as List import Data.Semigroup hiding () import Data.Typeable import Music.Dynamics.Literal import Music.Pitch.Literal -- | -- Forms an applicative as per 'Behavior', but only switches at discrete points. -- -- The semantics are given by -- -- @ -- type Reactive a = (a, Time, Voice a) -- @ -- newtype Reactive a = Reactive { getReactive :: ([Time], Behavior a) } deriving (Functor, Semigroup, Monoid, Typeable) -- -- TODO Define a more compact representation and reimplement Behavior as (Reactive Segment). -- -- Possible approach: -- -- * Implement PosReactive (no negative values) and define Reactive = Delayed (PosReactive) -- -- * Implement liftA2 for PosReactive (preferably with a single traversal) -- instance Transformable (Reactive a) where transform s (Reactive (t,r)) = Reactive (transform s t, transform s r) instance Wrapped (Reactive a) where type Unwrapped (Reactive a) = ([Time], Behavior a) _Wrapped' = iso getReactive Reactive instance Rewrapped (Reactive a) (Reactive b) instance Applicative Reactive where pure = pureDefault (<*>) = apDefault instance IsPitch a => IsPitch (Reactive a) where fromPitch = pure . fromPitch instance IsInterval a => IsInterval (Reactive a) where fromInterval = pure . fromInterval instance IsDynamics a => IsDynamics (Reactive a) where fromDynamics = pure . fromDynamics instance Alterable a => Alterable (Reactive a) where sharpen = fmap sharpen flatten = fmap flatten instance Augmentable a => Augmentable (Reactive a) where augment = fmap augment diminish = fmap diminish (view _Wrapped -> (tf, rf)) `apDefault` (view _Wrapped -> (tx, rx)) = view _Unwrapped (tf <> tx, rf <*> rx) pureDefault = view _Unwrapped . pure . pure -- | -- Get the initial value. -- initial :: Reactive a -> a initial r = r `atTime` minB (occs r) where -- If there are no updates, just use value at time 0 -- Otherwise pick an arbitrary time /before/ the first value -- It looks strange but it works minB [] = 0 minB (x:_) = x - 1 -- | Get the time of all updates and the value switched to at this point. updates :: Reactive a -> [(Time, a)] updates r = (\t -> (t, r `atTime` t)) <$> (List.sort . List.nub) (occs r) renderR :: Reactive a -> (a, [(Time, a)]) renderR = initial &&& updates occs :: Reactive a -> [Time] occs = fst . (^. _Wrapped') -- | Split a reactive into notes, as well as the values before and after the first/last update splitReactive :: Reactive a -> Either a ((a, Time), [Note a], (Time, a)) splitReactive r = case updates r of [] -> Left (initial r) (t,x):[] -> Right ((initial r, t), [], (t, x)) (t,x):xs -> Right ((initial r, t), fmap mkNote $ mrights (res $ (t,x):xs), head $ mlefts (res $ (t,x):xs)) where mkNote (t,u,x) = (t <-> u, x)^.note -- Always returns a 0 or more Right followed by one left res :: [(Time, a)] -> [Either (Time, a) (Time, Time, a)] res rs = let (ts,xs) = unzip rs in flip fmap (withNext ts `zip` xs) $ \ ((t, mu), x) -> case mu of Nothing -> Left (t, x) Just u -> Right (t, u, x) -- lenght xs == length (withNext xs) withNext :: [a] -> [(a, Maybe a)] withNext = go where go [] = [] go [x] = [(x, Nothing)] go (x:y:rs) = (x, Just y) : withNext (y : rs) {-# DEPRECATED updates "" #-} {-# DEPRECATED occs "" #-} {-# DEPRECATED splitReactive "" #-} {-# DEPRECATED atTime "" #-} atTime :: Reactive a -> Time -> a atTime = (!) . snd . (^. _Wrapped') -- | -- Get the final value. -- final :: Reactive a -> a final (renderR -> (i,[])) = i final (renderR -> (i,xs)) = snd $ last xs -- | @switch t a b@ behaves as @a@ before time @t@, then as @b@. switchR :: Time -> Reactive a -> Reactive a -> Reactive a switchR t (Reactive (tx, bx)) (Reactive (ty, by)) = Reactive $ (,) (filter (< t) tx <> [t] <> filter (> t) ty) (switch t bx by) trimR :: Monoid a => Span -> Reactive a -> Reactive a trimR (view range -> (t, u)) x = switchR t mempty (switchR u x mempty) -- | -- Get all intermediate values. -- intermediate :: Transformable a => Reactive a -> [Note a] intermediate (updates -> []) = [] intermediate (updates -> xs) = fmap (\((t1, x), (t2, _)) -> (t1 <-> t2, x)^.note) $ withNext $ xs where withNext xs = zip xs (tail xs) -- | -- Realize a 'Reactive' value as a discretely changing behavior. -- discrete :: Reactive a -> Behavior a discrete = continous . fmap pure -- | -- Realize a 'Reactive' value as an continous behavior. -- -- See also 'concatSegment' and 'concatB'. -- continous :: Reactive (Segment a) -> Behavior a -- | -- Realize a 'Reactive' value as an continous behavior. -- -- See also 'concatSegment' and 'concatB'. -- continousWith :: Segment (a -> b) -> Reactive a -> Behavior b continousWith f x = continous $ liftA2 (<*>) (pure f) (fmap pure x) -- | -- Sample a 'Behavior' into a reactive. -- sample :: [Time] -> Behavior a -> Reactive a -- TODO linear approximation (continous, sample) = error "Not implemented: (continous, sample)" window :: [Time] -> Behavior a -> Reactive (Segment a) windowed :: Iso (Behavior a) (Behavior b) (Reactive (Segment a)) (Reactive (Segment b)) (window, windowed) = error "Not implemented: (window, windowed)" {- -- Fre monad of ? {- data Score s a = SOne a | SPlus s [Score a] -} newtype Trans s a = Trans (s, [a]) deriving (Functor) instance Monoid s => Monad (Trans s) where return = Trans . return . return -- TODO the usual >>= type Score s a = Free (Trans s) a viewScore :: Monoid s => Score s a -> [(s, a)] viewScore x = case retract x of Trans (s,as) -> zip (repeat s) as -- Free monad of (a,a) {- data Tree a = One a | Plus (Tree a) (Tree a) -} data Pair a = Pair a a deriving (Functor) newtype MaybePair a = MaybePair (Maybe (Pair a)) deriving (Functor) -- Use compose type Tree a = Free MaybePair a -- CPS-version of Tree newtype Search a = Search { getSearch :: forall r . (a -> Tree r) -> Tree r } -}