module Music.Score.Ties (
Tiable(..),
TieT(..),
splitTiesAt,
) where
import Control.Applicative
import Data.Bifunctor
import Control.Comonad
import Control.Lens hiding (transform)
import Control.Monad
import Control.Monad.Plus
import Data.AffineSpace
import Data.Foldable hiding (concat)
import Data.Functor.Adjunction (unzipR)
import qualified Data.List as List
import Data.Maybe
import Data.Ratio
import Data.Semigroup
import Data.Typeable
import Data.Monoid.Average
import Data.VectorSpace hiding (Sum, getSum)
import Music.Dynamics.Literal
import Music.Pitch.Literal
import Music.Time
class Tiable a where
beginTie :: a -> a
beginTie = fst . toTied
endTie :: a -> a
endTie = snd . toTied
toTied :: a -> (a, a)
toTied a = (beginTie a, endTie a)
isTieEndBeginning :: a -> (Bool, Bool)
isTieBeginning :: a -> Bool
isTieBeginning = snd . isTieEndBeginning
isTieEnd :: a -> Bool
isTieEnd = fst . isTieEndBeginning
newtype TieT a = TieT { getTieT :: ((Any, Any), a) }
deriving (Eq, Ord, Show, Functor, Foldable, Typeable, Applicative, Monad, Comonad)
instance Wrapped (TieT a) where
type Unwrapped (TieT a) = ((Any, Any), a)
_Wrapped' = iso getTieT TieT
instance Rewrapped (TieT a) (TieT b)
instance Tiable Double where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable Float where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable Char where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable Int where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable Integer where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable () where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable (Ratio a) where { beginTie = id ; endTie = id ; isTieEndBeginning _ = (False, False) }
instance Tiable a => Tiable (TieT a) where
isTieEndBeginning (TieT (ties, _)) = over both getAny $ ties
toTied (TieT ((prevTie, nextTie), a)) = (TieT ((prevTie, Any True), b), TieT ((Any True, nextTie), c))
where (b,c) = toTied a
instance Tiable a => Tiable [a] where
toTied = unzip . fmap toTied
instance Tiable a => Tiable (Behavior a) where
toTied = unzipR . fmap toTied
instance Tiable a => Tiable (c, a) where
isTieEndBeginning = isTieEndBeginning . extract
toTied = unzipR . fmap toTied
instance Tiable a => Tiable (Maybe a) where
isTieEndBeginning = maybe (False, False) isTieEndBeginning
toTied = unzipR . fmap toTied
instance Tiable a => Tiable (Average a) where
isTieEndBeginning = isTieEndBeginning . getAverage
toTied = unzipR . fmap toTied
instance Tiable a => Tiable (Sum a) where
isTieEndBeginning = isTieEndBeginning . getSum
toTied = unzipR . fmap toTied
instance Tiable a => Tiable (Product a) where
isTieEndBeginning = isTieEndBeginning . getProduct
toTied = unzipR . fmap toTied
instance IsPitch a => IsPitch (TieT a) where
fromPitch = pure . fromPitch
instance IsDynamics a => IsDynamics (TieT a) where
fromDynamics = return . fromDynamics
instance Transformable a => Transformable (TieT a) where
transform s = fmap (transform s)
instance Reversible a => Reversible (TieT a) where
rev = fmap rev
instance Num a => Num (TieT a) where
(+) = liftA2 (+)
(*) = liftA2 (*)
() = liftA2 ()
abs = fmap abs
signum = fmap signum
fromInteger = pure . fromInteger
instance Fractional a => Fractional (TieT a) where
recip = fmap recip
fromRational = pure . fromRational
instance Floating a => Floating (TieT a) where
pi = pure pi
sqrt = fmap sqrt
exp = fmap exp
log = fmap log
sin = fmap sin
cos = fmap cos
asin = fmap asin
atan = fmap atan
acos = fmap acos
sinh = fmap sinh
cosh = fmap cosh
asinh = fmap asinh
atanh = fmap atanh
acosh = fmap acos
instance Enum a => Enum (TieT a) where
toEnum = pure . toEnum
fromEnum = fromEnum . extract
instance Bounded a => Bounded (TieT a) where
minBound = pure minBound
maxBound = pure maxBound
instance (Num a, Ord a, Real a) => Real (TieT a) where
toRational = toRational . extract
instance (Real a, Enum a, Integral a) => Integral (TieT a) where
quot = liftA2 quot
rem = liftA2 rem
toInteger = toInteger . extract
splitTiesAt :: Tiable a => [Duration] -> Voice a -> [Voice a]
splitTiesAt barDurs x = fmap ((^. voice) . map (^. note)) $ splitTiesAt' barDurs ((map (^. from note) . (^. notes)) x)
splitTiesAt' :: Tiable a => [Duration] -> [(Duration, a)] -> [[(Duration, a)]]
splitTiesAt' [] _ = []
splitTiesAt' _ [] = []
splitTiesAt' (barDur : rbarDur) occs = case splitDurFor barDur occs of
(barOccs, []) -> barOccs : []
(barOccs, restOccs) -> barOccs : splitTiesAt' rbarDur restOccs
tsplitTiesAt :: [Duration] -> [Duration] -> [[(Duration, Char)]]
tsplitTiesAt barDurs = fmap (map (^. from note) . (^. notes)) . splitTiesAt barDurs . ((^. voice) . map (^. note)) . fmap (\x -> (x,'_'))
splitDurThen :: Tiable a => Duration -> Duration -> (Duration, a) -> [(Duration, a)]
splitDurThen s t x = case splitDur s x of
(a, Nothing) -> [a]
(a, Just b) -> a : splitDurThen t t b
splitDurFor :: Tiable a => Duration -> [(Duration, a)] -> ([(Duration, a)], [(Duration, a)])
splitDurFor remDur [] = ([], [])
splitDurFor remDur (x : xs) = case splitDur remDur x of
(x@(d,_), Nothing) ->
if d < remDur then
first (x:) $ splitDurFor (remDur d) xs
else
([x], xs)
(x@(d,_), Just rest) -> ([x], rest : xs)
tsplitDurFor :: Duration -> [Duration] -> ([(Duration,Char)], [(Duration,Char)])
tsplitDurFor maxDur xs = splitDurFor maxDur $ fmap (\x -> (x,'_')) xs
splitDur :: Tiable a => Duration -> (Duration, a) -> ((Duration, a), Maybe (Duration, a))
splitDur maxDur (d,a)
| maxDur <= 0 = error "splitDur: maxDur must be > 0"
| d <= maxDur = ((d, a), Nothing)
| d > maxDur = ((maxDur, b), Just (d maxDur, c)) where (b,c) = toTied a