{-# 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