music-score-1.6: src/Music/Score/Track.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund 2012
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-- Provides the 'Track' type.
--
-------------------------------------------------------------------------------------
module Music.Score.Track (
-- * Track type
Track,
track',
track,
-- mkTrack,
-- getTrack,
) where
import Control.Applicative
import Control.Arrow
import Control.Lens
import Control.Monad
import Control.Monad.Compose
import Data.AffineSpace.Point
import Data.Foldable (Foldable (..), foldMap)
import qualified Data.Foldable as F
import qualified Data.List as List
import Data.PairMonad ()
import Data.Semigroup
import Data.Traversable (Traversable (..))
import qualified Data.Traversable as T
import Data.Typeable
import Data.VectorSpace hiding (Sum)
import Test.QuickCheck (Arbitrary (..), Gen (..))
import Music.Dynamics.Literal
import Music.Pitch.Literal
import Music.Score.Pitch
import Music.Score.Util
import Music.Time
-- |
-- A track is a list of events with explicit onset.
--
-- Track is a 'Monoid' under parallel composition. 'mempty' is the empty track
-- and 'mappend' interleaves values.
--
-- Track is a 'Monad'. 'return' creates a track containing a single value at time
-- zero, and '>>=' transforms the values of a track, allowing the addition and
-- removal of values relative to the time of the value. Perhaps more intuitively,
-- 'join' delays each inner track to start at the offset of an outer track, then
-- removes the intermediate structure.
--
-- > let t = Track [(0, 65),(1, 66)]
-- >
-- > t >>= \x -> Track [(0, 'a'), (10, toEnum x)]
-- >
-- > ==> Track {getTrack = [ (0.0, 'a'),
-- > (1.0, 'a'),
-- > (10.0, 'A'),
-- > (11.0, 'B') ]}
--
-- Track is an instance of 'VectorSpace' using parallel composition as addition,
-- and time scaling as scalar multiplication.
--
newtype Track a = Track { getTrack' :: [Occ a] }
deriving (Eq, Ord, Show, Functor, Foldable, Typeable, Traversable, Monoid, Semigroup, Delayable, Stretchable)
{-
instance Semigroup (Track a) where
(<>) = mappend
-- Equivalent to the derived Monoid, except for the sorted invariant.
instance Monoid (Track a) where
mempty = Track []
Track as `mappend` Track bs = Track (as `m` bs)
where
m = mergeBy (comparing fst)
-}
instance Wrapped (Track a) where
type Unwrapped (Track a) = [Occ a]
_Wrapped' = iso getTrack' Track
instance Applicative Track where
pure = return
(<*>) = ap
instance Monad Track where
return = (^. _Unwrapped') . return . return
xs >>= f = (^. _Unwrapped') $ mbind ((^. _Wrapped') . f) ((^. _Wrapped') xs)
instance Alternative Track where
empty = mempty
(<|>) = mappend
instance MonadPlus Track where
mzero = mempty
mplus = mappend
instance HasOnset (Track a) where
onset (Track a) = list origin (onset . head) a
instance IsPitch a => IsPitch (Track a) where
fromPitch = pure . fromPitch
instance IsDynamics a => IsDynamics (Track a) where
fromDynamics = pure . fromDynamics
instance IsInterval a => IsInterval (Track a) where
fromInterval = pure . fromInterval
type instance Pitch (Track a) = Pitch a
instance (HasSetPitch a b, Transformable (Pitch (Track a)), Transformable (Pitch (Track b))) => HasSetPitch (Track a) (Track b) where
type SetPitch g (Track a) = Track (SetPitch g a)
-- FIXME this is wrong, need to behave like __mapPitch'
__mapPitch f = fmap (__mapPitch f)
-- |
-- Create a voice from a list of occurences.
--
track' :: Iso' [(Time, a)] (Track a)
track' = track
-- |
-- Create a voice from a list of occurences.
--
track :: Iso [(Time, a)] [(Time, b)] (Track a) (Track b)
track = iso mkTrack getTrack
where
mkTrack = Track . fmap (uncurry occ . first (fmap realToFrac))
getTrack = fmap (first (fmap realToFrac) . getOcc) . getTrack'
newtype Occ a = Occ (Sum Time, a)
deriving (Eq, Ord, Show, {-Read, -}Functor, Applicative, Monad, Foldable, Traversable)
occ t x = Occ (Sum t, x)
getOcc (Occ (Sum t, x)) = (t, x)
instance Delayable (Occ a) where
delay n (Occ (s,x)) = Occ (delay n s, x)
instance Stretchable (Occ a) where
stretch n (Occ (s,x)) = Occ (stretch n s, x)
instance HasOnset (Occ a) where
onset (Occ (s,x)) = onset s