module Music.Score.Phrases (
HasPhrases(..),
HasPhrases',
phrases,
phrases',
Phrase,
MVoice,
PVoice,
TVoice,
mVoicePVoice,
mVoiceTVoice,
pVoiceTVoice,
unsafeMVoicePVoice,
singleMVoice,
mapPhrasesWithPrevAndCurrentOnset,
) where
import Control.Applicative
import Control.Applicative
import Control.Comonad (Comonad (..), extract)
import Control.Exception (assert)
import Control.Lens
import Control.Lens hiding (rewrite)
import Control.Monad
import Control.Monad.Plus
import Data.AffineSpace
import Data.AffineSpace
import Data.Bifunctor
import Data.Colour.Names as Color
import Data.Either
import Data.Either
import Data.Foldable (Foldable)
import Data.Functor.Adjunction (unzipR)
import Data.Functor.Context
import Data.Functor.Contravariant (Op(..))
import Data.Functor.Couple
import qualified Data.List as List
import qualified Data.List
import Data.Maybe
import Data.Maybe
import Data.Ord
import Data.Ratio
import Data.Semigroup
import Data.Semigroup
import Data.Traversable
import Data.Traversable (Traversable, sequenceA)
import Data.VectorSpace hiding (Sum (..))
import System.Process
import Music.Score.Part
import Music.Time
import Music.Time.Internal.Convert
import Music.Time.Internal.Util
type Phrase a = Voice a
type MVoice a = Voice (Maybe a)
type PVoice a = [Either Duration (Phrase a)]
type TVoice a = Track (Phrase a)
class HasPhrases s t a b | s -> a, t -> b, s b -> t, t a -> s where
mvoices :: Traversal s t (MVoice a) (MVoice b)
instance HasPhrases (MVoice a) (MVoice b) a b where
mvoices = id
instance HasPhrases (PVoice a) (PVoice b) a b where
mvoices = from unsafeMVoicePVoice
instance (HasPart' a, Ord (Part a)) =>
HasPhrases (Score a) (Score b) a b where
mvoices = extracted . each . singleMVoice
type HasPhrases' s a = HasPhrases s s a a
phrases' :: HasPhrases' s a => Traversal' s (Phrase a)
phrases' = phrases
phrases :: HasPhrases s t a b => Traversal s t (Phrase a) (Phrase b)
phrases = mvoices . mVoicePVoice . each . _Right
mVoicePVoice :: Lens (MVoice a) (MVoice b) (PVoice a) (PVoice b)
mVoicePVoice = unsafeMVoicePVoice
unsafeMVoicePVoice :: Iso (MVoice a) (MVoice b) (PVoice a) (PVoice b)
unsafeMVoicePVoice = iso mvoiceToPVoice pVoiceToMVoice
where
mvoiceToPVoice :: MVoice a -> PVoice a
mvoiceToPVoice =
map ( bimap voiceToRest voiceToPhrase
. bimap (^.from unsafePairs) (^.from unsafePairs) )
. groupDiff' (isJust . snd)
. view pairs
voiceToRest :: MVoice a -> Duration
voiceToRest = sumOf (pairs.each._1) . fmap (\x -> assert (isNothing x) x)
voiceToPhrase :: MVoice a -> Phrase a
voiceToPhrase = fmap fromJust
pVoiceToMVoice :: (PVoice a) -> MVoice a
pVoiceToMVoice = mconcat . fmap (either restToVoice phraseToVoice)
restToVoice :: Duration -> MVoice a
restToVoice d = stretch d $ pure Nothing
phraseToVoice :: Phrase a -> MVoice a
phraseToVoice = fmap Just
singleMVoice :: Prism (Score a) (Score b) (MVoice a) (MVoice b)
singleMVoice = iso scoreToVoice voiceToScore'
where
scoreToVoice :: Score a -> MVoice a
scoreToVoice = (^. voice) . fmap (^. note) . fmap throwTime . addRests .
List.sortBy (comparing (^._1))
. (^. triples)
where
throwTime (t,d,x) = (d,x)
addRests = concat . snd . List.mapAccumL g 0
where
g u (t, d, x)
| u == t = (t .+^ d, [(t, d, Just x)])
| u < t = (t .+^ d, [(u, t .-. u, Nothing), (t, d, Just x)])
| otherwise = error "singleMVoice: Strange prevTime"
voiceToScore :: Voice a -> Score a
voiceToScore = renderAlignedVoice . aligned 0 0
voiceToScore' :: MVoice a -> Score a
voiceToScore' = mcatMaybes . voiceToScore
mapPhrasesWithPrevAndCurrentOnset :: HasPhrases s t a b => (Maybe Time -> Time -> Phrase a -> Phrase b) -> s -> t
mapPhrasesWithPrevAndCurrentOnset f = over (mvoices . mVoiceTVoice) (withPrevAndCurrentOnset f)
withPrevAndCurrentOnset :: (Maybe Time -> Time -> a -> b) -> Track a -> Track b
withPrevAndCurrentOnset f = over placeds (fmap (\(x,y,z) -> fmap (f (fmap placedOnset x) (placedOnset y)) y) . withPrevNext)
where
placedOnset :: Placed a -> Time
placedOnset = view (from placed . _1)
mVoiceTVoice :: Lens (MVoice a) (MVoice b) (TVoice a) (TVoice b)
mVoiceTVoice = mVoicePVoice . pVoiceTVoice
pVoiceTVoice :: Lens (PVoice a) (PVoice b) (TVoice a) (TVoice b)
pVoiceTVoice = lens pVoiceToTVoice (flip tVoiceToPVoice)
where
pVoiceToTVoice :: PVoice a -> TVoice a
pVoiceToTVoice x = mkTrack $ rights $ map (sequenceA) $ firsts (offsetPoints (0::Time)) (withDurationR x)
tVoiceToPVoice :: TVoice a -> PVoice b -> PVoice a
tVoiceToPVoice tv pv = set _rights newPhrases pv
where
newPhrases = toListOf traverse tv
_rights :: Lens [Either a b] [Either a c] [b] [c]
_rights = lens _rightsGet (flip _rightsSet)
where
_rightsGet :: [Either a b] -> [b]
_rightsGet = rights
_rightsSet :: [c] -> [Either a b] -> [Either a c]
_rightsSet cs = sndMapAccumL f cs
where
f cs (Left a) = (cs, Left a)
f (c:cs) (Right b) = (cs, Right c)
f [] (Right _) = error "No more cs"
sndMapAccumL f z = snd . List.mapAccumL f z
firsts :: ([a] -> [b]) -> [(a,c)] -> [(b,c)]
firsts f = uncurry zip . first f . unzipR
mkTrack :: [(Time, a)] -> Track a
mkTrack = view track . map (view placed)
withDurationR :: (Functor f, HasDuration a) => f a -> f (Duration, a)
withDurationR = fmap $ \x -> (_duration x, x)
mapWithDuration :: HasDuration a => (Duration -> a -> b) -> a -> b
mapWithDuration = over dual withDurationL . uncurry
where
withDurationL :: (Contravariant f, HasDuration a) => f (Duration, a) -> f a
withDurationL = contramap $ \x -> (_duration x, x)
dual :: Iso (a -> b) (c -> d) (Op b a) (Op d c)
dual = iso Op getOp
dursToVoice :: [Duration] -> Voice ()
dursToVoice = mconcat . map (\d -> stretch d $ return ())
groupDiff :: (a -> Bool) -> [a] -> [[a]]
groupDiff p [] = []
groupDiff p (x:xs)
| p x = (x : List.takeWhile p xs) : groupDiff p (List.dropWhile p xs)
| not (p x) = (x : List.takeWhile (not . p) xs) : groupDiff p (List.dropWhile (not . p) xs)
groupDiff' :: (a -> Bool) -> [a] -> [Either [a] [a]]
groupDiff' p [] = []
groupDiff' p (x:xs)
| not (p x) = Left (x : List.takeWhile (not . p) xs) : groupDiff' p (List.dropWhile (not . p) xs)
| p x = Right (x : List.takeWhile p xs) : groupDiff' p (List.dropWhile p xs)