{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# 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.Score.Meta ( module Music.Time.Meta, -- * Meta-events addMetaNote, fromMetaReactive, metaAt, metaAtStart, withMeta, withMetaAtStart, ) where import Control.Applicative import Control.Lens hiding (parts, perform) import Control.Monad import Control.Monad.Plus import Data.Bifunctor import Data.AffineSpace import Data.AffineSpace.Point import Data.Foldable (Foldable (..)) import Data.Maybe import Data.Ord import Data.Ratio import Data.Semigroup import Data.String import Data.Traversable import Data.VectorSpace import Music.Score.Part import Music.Score.Internal.Util import Music.Time import Music.Time.Meta import Music.Time.Reactive import qualified Data.Foldable as Foldable import qualified Data.List as List addMetaNote :: forall a b . (AttributeClass a, HasMeta b) => Event a -> b -> b addMetaNote x = applyMeta $ wrapTMeta $ noteToReactive x fromMetaReactive :: forall a b . AttributeClass b => Meta -> Reactive b fromMetaReactive = fromMaybe mempty . unwrapMeta metaAt :: AttributeClass b => Time -> Score a -> b metaAt x = (`atTime` x) . runScoreMeta metaAtStart :: AttributeClass b => Score a -> b metaAtStart x = _onset x `metaAt` x withMeta :: AttributeClass a => (a -> Score b -> Score b) -> Score b -> Score b withMeta f x = let m = (view meta) x r = fromMetaReactive m in case splitReactive r of Left a -> f a x Right ((a, t), bs, (u, c)) -> (meta .~) m $ mapBefore t (f a) $ (composed $ fmap (\(view (from event) -> (s, a)) -> mapDuring s $ f a) $ bs) $ mapAfter u (f c) $ x withMetaAtStart :: AttributeClass a => (a -> Score b -> Score b) -> Score b -> Score b withMetaAtStart f x = let m = view meta x in f (fromMetaReactive m `atTime` _onset x) x -- JUNK withSpan :: Score a -> Score (Span, a) withSpan = mapTriples (\t d x -> (t >-> d,x)) withTime = mapTriples (\t d x -> (t, x)) inSpan t' (view range -> (t,u)) = t <= t' && t' < u mapBefore :: Time -> (Score a -> Score a) -> Score a -> Score a mapDuring :: Span -> (Score a -> Score a) -> Score a -> Score a mapAfter :: Time -> (Score a -> Score a) -> Score a -> Score a mapBefore t f x = let (y,n) = (fmap snd `bimap` fmap snd) $ mpartition (\(t2,x) -> t2 < t) (withTime x) in (f y <> n) mapDuring s f x = let (y,n) = (fmap snd `bimap` fmap snd) $ mpartition (\(t,x) -> t `inSpan` s) (withTime x) in (f y <> n) mapAfter t f x = let (y,n) = (fmap snd `bimap` fmap snd) $ mpartition (\(t2,x) -> t2 >= t) (withTime x) in (f y <> n) -- Transform the score with the current value of some meta-information -- Each "update chunk" of the meta-info is processed separately runScoreMeta :: forall a b . AttributeClass b => Score a -> Reactive b runScoreMeta = fromMetaReactive . (view meta) noteToReactive :: Monoid a => Event a -> Reactive a noteToReactive n = (pure <$> n) `activate` pure mempty activate :: Event (Reactive a) -> Reactive a -> Reactive a activate (view (from event) -> (view range -> (start,stop), x)) y = y `turnOn` (x `turnOff` y) where turnOn = switchR start turnOff = switchR stop