music-score-1.7: src/Music/Score/Phrases.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund 2012-2014
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-- This module provides phrase-wise traversal.
--
-- /Warning/ This module is experimental.
--
-------------------------------------------------------------------------------------
module Music.Score.Phrases (
-- * HasPhrases class
HasPhrases(..),
HasPhrases',
phrases,
phrases',
-- * Phrase types etc
Phrase,
MVoice,
PVoice,
-- ** Utility
mvoicePVoice,
unsafeMvoicePVoice,
singleMVoice,
) where
import Control.Applicative
import Control.Exception (assert)
import Control.Lens
import Control.Monad.Plus
import Data.AffineSpace
import qualified Data.List as List
import Data.Maybe
import Data.Ord
import Data.Semigroup
import Music.Score.Part
import Music.Score.Convert
import Music.Time
-- |
-- For a phrase, we simply use a voice without rests.
--
-- To represent a sequence of phrases we provide two equivalent representations:
--
-- * 'MVoice' is a sequence of notes/chords or rests. All consecutive non-rests consitute a phrase.
--
-- * 'PVoice' is a sequence of phrases or durations.
--
type Phrase a = Voice a
-- |
-- A sequence of phrases or rests, represented as notes or rests.
--
-- Each consecutive sequence of non-rest elements is considered to be a phrase.
-- For an explicit representation of the phrase structure, see 'mvoicePVoice'.
--
type MVoice a = Voice (Maybe a)
-- |
-- A sequence of phrases or rests, represented with explicit phrase structure.
--
type PVoice a = [Either Duration (Phrase a)]
{-
class HasPhrases a b | a -> b where
mvoices :: Traversal' a (MVoice b)
instance HasPhrases (MVoice a) a where
mvoices = id
instance HasPhrases (PVoice a) a where
-- Note: This is actually OK in 'phrases', as that just becomes (id . each . _Right)
mvoices = from unsafeMvoicePVoice
instance (HasPart' a, Transformable a, Ord (Part a)) => HasPhrases (Score a) a where
mvoices = extracted . each . singleMVoice
-}
-- |
-- Classes that provide a phrase traversal.
--
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)
-- | Traverses all phrases in a voice.
instance HasPhrases (MVoice a) (MVoice b) a b where
mvoices = id
-- | Traverses all phrases in a voice.
instance HasPhrases (PVoice a) (PVoice b) a b where
-- Note: This is actually OK in 'phr', as that just becomes (id . each . _Right)
mvoices = from unsafeMvoicePVoice
-- | Traverses all phrases in each voice, using 'extracted'.
instance (HasPart' a, {-HasPart a b, -}{-Transformable 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
{-
Phrase traversal for score:
phrasesS :: (Ord (Part a), HasPart' a, Transformable a) => Traversal' (Score a) (Phrase a)
phrasesS = extracted . each . singleMVoice . mvoicePVoice . each . _Right
More generally:
-}
-- |
-- A simple generic phrase-traversal.
--
phrases' :: HasPhrases' s a => Traversal' s (Phrase a)
phrases' = phrases
-- |
-- A generic phrase-traversal.
--
phrases :: HasPhrases s t a b => Traversal s t (Phrase a) (Phrase b)
phrases = mvoices . mvoicePVoice . each . _Right
-- |
-- View an 'MVoice' as a 'PVoice'.
--
mvoicePVoice :: Lens (MVoice a) (MVoice b) (PVoice a) (PVoice b)
mvoicePVoice = unsafeMvoicePVoice
-- TODO meta
-- |
-- View an 'MVoice' as a 'PVoice' and vice versa.
--
-- This a valid 'Iso' up to meta-data equivalence.
--
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 unsafeEventsV) (^.from unsafeEventsV) )
. groupDiff' (isJust . snd)
. view eventsV
voiceToRest :: MVoice a -> Duration
voiceToRest = sumOf (eventsV.each._1) . fmap (\x -> assert (isNothing x) x)
-- TODO just _duration
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
-- TODO failure
-- TODO why Transformable?
singleMVoice :: {-Transformable a =>-} Prism (Score a) (Score b) (MVoice a) (MVoice b)
singleMVoice = iso scoreToVoice voiceToScore'
where
scoreToVoice :: {-Transformable a =>-} Score a -> MVoice a
scoreToVoice = (^. voice) . fmap (^. stretched) . fmap throwTime . addRests .
-- TODO
List.sortBy (comparing (^._1))
-- end TODO
. (^. events)
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 = scat . fmap g . (^. stretcheds) where g = (^. stretchedValue) . fmap return
voiceToScore' :: MVoice a -> Score a
voiceToScore' = mcatMaybes . voiceToScore
instance (Transformable a, Transformable b) => Cons (Phrase a) (Phrase b) a b where
_Cons = undefined
-- instance (Transformable a, Transformable b) => Snoc (Phrase a) (Phrase b) a b where
-- _Snoc = prism' pure (preview lastV)
-- TODO move
-- |
-- Group contigous sequences matching/not-matching the predicate.
--
-- >>> groupDiff (== 0) [0,1,2,3,5,0,0,6,7]
-- [[0],[1,2,3,5],[0,0],[6,7]]
--
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)