module Music.Score.Meta.Time (
TimeSignature,
time,
compoundTime,
isSimpleTime,
isCompoundTime,
toSimpleTime,
getTimeSignature,
timeSignature,
timeSignatureDuring,
withTimeSignature,
getTimeSignatures,
getTimeSignatureChanges,
getBarDurations,
getBarTimeSignatures,
standardTimeSignature,
) where
import Control.Lens (view, (^.))
import Control.Monad.Plus
import Data.Bifunctor
import Data.Foldable (Foldable)
import qualified Data.Foldable as F
import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Data.Ratio ((%))
import Data.Semigroup
import Data.Set (Set)
import qualified Data.Set as Set
import Data.String
import Data.Traversable (Traversable)
import qualified Data.Traversable as T
import Data.Typeable
import Music.Pitch.Literal
import Music.Score.Meta
import Music.Score.Part
import Music.Score.Pitch
import Music.Score.Internal.Util
import Music.Time hiding (time)
newtype TimeSignature = TimeSignature ([Integer], Integer)
deriving (Eq, Ord, Typeable)
mapNums f (TimeSignature (m,n)) = TimeSignature (f m, n)
mapDenom f (TimeSignature (m,n)) = TimeSignature (m, f n)
isSimple (TimeSignature ([_],_)) = True
isSimple _ = False
getSimple (TimeSignature ([m],n)) = m `div` n
liftRational f = fromRational . f . toRational
liftRational2 f x y = fromRational $ toRational x `f` toRational y
instance Num TimeSignature where
x + y | x `haveSameDenominator` y = concatFrac x y
| otherwise = liftRational2 (+) x y
where
TimeSignature (_,n1) `haveSameDenominator` TimeSignature (_,n2) = n1 == n2
TimeSignature (m1,n) `concatFrac` TimeSignature (m2,_) = TimeSignature (m1 <> m2, n)
x * y | isSimple y = mapNums (fmap (* (getSimple y))) x
| otherwise = liftRational2 (*) x y
negate = liftRational negate
abs = liftRational abs
signum = liftRational signum
fromInteger x = TimeSignature ([x], 1)
instance Fractional TimeSignature where
fromRational (unRatio -> (m, n)) = TimeSignature ([m], n)
x / y | isSimple y = mapDenom (* (getSimple y)) x
| otherwise = liftRational2 (/) x y
instance Real TimeSignature where
toRational (TimeSignature (xs, x)) = sum xs % x
instance Show TimeSignature where
show (TimeSignature ([m], n)) = show m ++ "/" ++ show n
show (TimeSignature (xs, n)) = "(" ++ List.intercalate "+" (fmap show xs) ++ ")/" ++ show n
time :: Integer -> Integer -> TimeSignature
time x y = TimeSignature ([x], y)
compoundTime :: [Integer] -> Integer -> TimeSignature
compoundTime = curry TimeSignature
isSimpleTime :: TimeSignature -> Bool
isSimpleTime (TimeSignature ([_],_)) = True
isSimpleTime _ = False
isCompoundTime :: TimeSignature -> Bool
isCompoundTime = not . isSimpleTime
toSimpleTime :: TimeSignature -> TimeSignature
toSimpleTime = fromRational . toRational
getTimeSignature :: TimeSignature -> ([Integer], Integer)
getTimeSignature (TimeSignature x) = x
timeSignature :: (HasMeta a, HasPosition a, Transformable a) => TimeSignature -> a -> a
timeSignature c x = timeSignatureDuring (0 <-> x^.offset) c x
timeSignatureDuring :: HasMeta a => Span -> TimeSignature -> a -> a
timeSignatureDuring s c = addMetaNote $ view event (s, optionLast c)
getTimeSignatures :: TimeSignature -> Score a -> Reactive TimeSignature
getTimeSignatures def = fmap (fromMaybe def . unOptionLast) . fromMetaReactive . (view meta)
getTimeSignatureChanges :: TimeSignature -> Score a -> [(Time, TimeSignature)]
getTimeSignatureChanges def = updates . getTimeSignatures def
withTimeSignature :: TimeSignature -> (TimeSignature -> Score a -> Score a) -> Score a -> Score a
withTimeSignature def f = withMeta (f . fromMaybe def . unOptionLast)
getBarDurations :: [(TimeSignature, Duration)] -> [Duration]
getBarDurations = fmap realToFrac . getBarTimeSignatures
getBarTimeSignatures :: [(TimeSignature, Duration)] -> [TimeSignature]
getBarTimeSignatures = concatMap $ uncurry getBarTimeSignatures1
getBarTimeSignatures1 :: TimeSignature -> Duration -> [TimeSignature]
getBarTimeSignatures1 ts d = let
(n,r) = numWholeBars ts d
in replic n ts ++ if r > 0 then [standardTimeSignature r] else []
numWholeBars :: TimeSignature -> Duration -> (Integer, Duration)
numWholeBars ts dur = second (* barDur) $ properFraction (dur / barDur) where barDur = realToFrac ts
standardTimeSignature :: Duration -> TimeSignature
standardTimeSignature x = case unRatio (toRational x) of
(2,2) -> time 2 2
(3,2) -> time 3 2
(2,1) -> time 4 2
(5,2) -> time 5 2
(3,1) -> time 6 2
(7,2) -> time 7 2
(1,4) -> time 1 4
(1,2) -> time 2 4
(3,4) -> time 3 4
(1,1) -> time 4 4
(5,4) -> time 5 4
(7,4) -> time 7 4
(1,8) -> time 1 8
(3,8) -> time 3 8
(5,8) -> time 5 8
(7,8) -> time 7 8
_ -> time 4 4
optionLast = Option . Just . Last
unOptionLast = fmap getLast . getOption