music-score-1.7: src/Music/Time/Reactive.hs
{-# 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 <http://conal.net/blog/posts/reactive-normal-form>,
-- 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 }
-}