module Music.Time.Score (
Score,
score,
notes,
events,
singleNote,
unsafeNotes,
unsafeEvents,
simult,
simultaneous,
normalizeScore,
printEras,
mapWithSpan,
filterWithSpan,
mapFilterWithSpan,
mapEvents,
filterEvents,
mapFilterEvents,
) where
import Data.AffineSpace
import Data.AffineSpace.Point
import qualified Data.List.NonEmpty as NonEmpty
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Ratio
import Data.Semigroup
import Data.Set (Set)
import qualified Data.Set as Set
import Data.VectorSpace
import Data.Functor.Adjunction (unzipR)
import Music.Time.Juxtapose (scat)
import Music.Time.Meta
import Music.Time.Note
import Music.Time.Reverse
import Music.Time.Split
import Music.Time.Stretched
import Music.Time.Voice
import Control.Applicative
import Control.Comonad
import Control.Lens hiding (Indexable, Level, above, below,
index, inside, parts, reversed,
transform, (<|), (|>))
import Control.Monad
import Control.Monad.Compose
import Control.Monad.Plus
import Data.Foldable (Foldable)
import qualified Data.Foldable as Foldable
import qualified Data.List as List
import qualified Data.Ord as Ord
import Data.Semigroup hiding ()
import Data.Traversable (Traversable)
import qualified Data.Traversable as T
import Data.Typeable
import Data.VectorSpace hiding (Sum (..))
import Music.Dynamics.Literal
import Music.Pitch.Literal
import Music.Time.Internal.Util
type ScoreNote a = Note a
newtype Score a = Score { getScore' :: (Meta, NScore a) }
deriving (Functor, Semigroup, Monoid, Foldable, Traversable, Typeable)
instance Wrapped (Score a) where
type Unwrapped (Score a) = (Meta, NScore a)
_Wrapped' = iso getScore' Score
instance Rewrapped (Score a) (Score b) where
instance Applicative Score where
pure = return
(<*>) = ap
instance Monad Score where
return = (^. _Unwrapped') . return . return
xs >>= f = (^. _Unwrapped') $ mbind ((^. _Wrapped') . f) ((^. _Wrapped') xs)
instance Alternative Score where
empty = mempty
(<|>) = mappend
instance MonadPlus Score where
mzero = mempty
mplus = mappend
instance FunctorWithIndex Span Score where
imap f = over (_Wrapped._2) $ imap f
instance FoldableWithIndex Span Score where
ifoldMap f (Score (m,x)) = ifoldMap f x
instance TraversableWithIndex Span Score where
itraverse f (Score (m,x)) = fmap (\x -> Score (m,x)) $ itraverse f x
instance Transformable (Score a) where
transform t (Score (m,x)) = Score (transform t m, transform t x)
instance Reversible a => Reversible (Score a) where
rev (Score (m,x)) = Score (rev m, rev x)
instance Splittable a => Splittable (Score a) where
split t (Score (m,x)) = (Score (m1,x1), Score (m2,x2))
where
(m1, m2) = split t m
(x1, x2) = split t x
instance HasPosition (Score a) where
_onset (Score (_,x)) = _onset x
_offset (Score (_,x)) = _offset x
_position (Score (_,x)) = _position x
instance HasDuration (Score a) where
_duration (Score (_,x)) = _duration x
instance IsPitch a => IsPitch (Score a) where
fromPitch = pure . fromPitch
instance IsInterval a => IsInterval (Score a) where
fromInterval = pure . fromInterval
instance IsDynamics a => IsDynamics (Score a) where
fromDynamics = pure . fromDynamics
instance Enum a => Enum (Score a) where
toEnum = return . toEnum
fromEnum = list 0 (fromEnum . head) . Foldable.toList
instance Num a => Num (Score a) where
fromInteger = return . fromInteger
abs = fmap abs
signum = fmap signum
(+) = error "Not implemented"
() = error "Not implemented"
(*) = error "Not implemented"
instance AdditiveGroup (Score a) where
zeroV = error "Not implemented"
(^+^) = error "Not implemented"
negateV = error "Not implemented"
instance VectorSpace (Score a) where
type Scalar (Score a) = Duration
d *^ s = d `stretch` s
instance HasMeta (Score a) where
meta = _Wrapped . _1
newtype NScore a = NScore { getNScore :: [ScoreNote a] }
deriving (Functor, Foldable, Traversable, Semigroup, Monoid, Typeable, Show, Eq)
instance Wrapped (NScore a) where
type Unwrapped (NScore a) = [ScoreNote a]
_Wrapped' = iso getNScore NScore
instance Rewrapped (NScore a) (NScore b)
instance Applicative NScore where
pure = return
(<*>) = ap
instance Monad NScore where
return = (^. _Unwrapped) . pure . pure
xs >>= f = (^. _Unwrapped) $ mbind ((^. _Wrapped') . f) ((^. _Wrapped') xs)
instance Alternative NScore where
empty = mempty
(<|>) = mappend
instance MonadPlus NScore where
mzero = mempty
mplus = mappend
instance FunctorWithIndex Span NScore where
imap = undefined
instance FoldableWithIndex Span NScore where
ifoldMap = undefined
instance TraversableWithIndex Span NScore where
itraverse = undefined
instance Transformable (NScore a) where
transform t (NScore xs) = NScore (fmap (transform t) xs)
instance Reversible a => Reversible (NScore a) where
rev (NScore xs) = NScore (fmap rev xs)
instance HasPosition (NScore a) where
_onset = safeMinimum . fmap _onset . view _Wrapped'
_offset = safeMaximum . fmap _offset . view _Wrapped'
safeMinimum xs = if null xs then 0 else minimum xs
safeMaximum xs = if null xs then 0 else maximum xs
instance HasDuration (NScore a) where
_duration x = _offset x .-. _onset x
instance Splittable a => Splittable (NScore a) where
split t (NScore notes) = over both (NScore . mfilter (not . isEmptyNote)) $ unzip $ map (\x -> splitAbs (0 .+^ t) x) notes
where
isEmptyNote :: Note a -> Bool
isEmptyNote = isEmptySpan . view era
isEmptySpan :: Span -> Bool
isEmptySpan (view range -> (t, u)) = t == u
score :: Getter [Note a] (Score a)
score = from unsafeNotes
notes :: Lens (Score a) (Score b) [Note a] [Note b]
notes = _Wrapped . _2 . _Wrapped . sorted
where
sorted = iso (List.sortBy (Ord.comparing _onset)) (List.sortBy (Ord.comparing _onset))
unsafeNotes :: Iso (Score a) (Score b) [Note a] [Note b]
unsafeNotes = _Wrapped . noMeta . _Wrapped . sorted
where
sorted = iso (List.sortBy (Ord.comparing _onset)) (List.sortBy (Ord.comparing _onset))
noMeta = iso extract return
unsafeEvents :: Iso (Score a) (Score b) [(Time, Duration, a)] [(Time, Duration, b)]
unsafeEvents = iso _getScore _score
where
_score :: [(Time, Duration, a)] -> Score a
_score = mconcat . fmap (uncurry3 event)
where
event t d x = (delay (t .-. 0) . stretch d) (return x)
_getScore :: Score a -> [(Time, Duration, a)]
_getScore =
fmap (\(view delta -> (t,d),x) -> (t,d,x)) .
List.sortBy (Ord.comparing fst) .
Foldable.toList .
fmap (view $ from note) .
reifyScore
singleNote :: Prism' (Score a) (Note a)
singleNote = unsafeNotes . single
mapScore :: (Note a -> b) -> Score a -> Score b
mapScore f = over (_Wrapped._2) (mapNScore f)
where
mapNScore f = over (_Wrapped.traverse) (extend f)
reifyScore :: Score a -> Score (Note a)
reifyScore = over (_Wrapped . _2 . _Wrapped) $ fmap duplicate
events :: Lens (Score a) (Score b) [(Time, Duration, a)] [(Time, Duration, b)]
events = notes . through event event
mapWithSpan :: (Span -> a -> b) -> Score a -> Score b
mapWithSpan f = mapScore (uncurry f . view (from note))
filterWithSpan :: (Span -> a -> Bool) -> Score a -> Score a
filterWithSpan f = mapFilterWithSpan (partial2 f)
mapFilterWithSpan :: (Span -> a -> Maybe b) -> Score a -> Score b
mapFilterWithSpan f = mcatMaybes . mapWithSpan f
mapEvents :: (Time -> Duration -> a -> b) -> Score a -> Score b
mapEvents f = mapWithSpan (uncurry f . view delta)
filterEvents :: (Time -> Duration -> a -> Bool) -> Score a -> Score a
filterEvents f = mapFilterEvents (partial3 f)
mapFilterEvents :: (Time -> Duration -> a -> Maybe b) -> Score a -> Score b
mapFilterEvents f = mcatMaybes . mapEvents f
normalizeScore :: Score a -> Score a
normalizeScore = reset . absDurations
where
reset x = set onset (view onset x `max` 0) x
absDurations = over (notes.each.era.delta._2) abs
printEras :: Score a -> IO ()
printEras = mapM_ print . toListOf (notes.each.era)
eras :: Transformable a => Score a -> [Span]
eras = toListOf (notes . each . era)
chordEvents :: Transformable a => Span -> Score a -> [a]
chordEvents s = fmap extract . filter ((== s) . view era) . view notes
simultaneous' :: Transformable a => Score a -> Score [a]
simultaneous' sc = (^. from unsafeEvents) vs
where
es = List.nub $ eras sc
evs = fmap (`chordEvents` sc) es
vs = zipWith (\(view delta -> (t,d)) a -> (t,d,a)) es evs
simultaneous :: (Transformable a, Semigroup a) => Score a -> Score a
simultaneous = fmap (sconcat . NonEmpty.fromList) . simultaneous'
simult :: Transformable a => Lens (Score a) (Score b) (Score [a]) (Score [b])
simult = iso simultaneous' mscatter