packages feed

music-score 1.8 → 1.8.1

raw patch · 33 files changed

+543/−530 lines, 33 filesdep −data-defaultdep −profunctorsdep −voiddep ~lilyponddep ~music-dynamics-literaldep ~music-pitch-literalPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: data-default, profunctors, void

Dependency ranges changed: lilypond, music-dynamics-literal, music-pitch-literal, musicxml2

API changes (from Hackage documentation)

- Music.Score.Export.Lilypond: instance Default LilypondOptions
- Music.Score.Meta.Pickup: instance Default Pickup
- Music.Score.Meta.RehearsalMark: instance Default RehearsalMark
- Music.Score.Meta.Tempo: instance Default Tempo
- Music.Time.Internal.Quantize: Beat :: Duration -> a -> Rhythm a
- Music.Time.Internal.Quantize: Dotted :: Int -> (Rhythm a) -> Rhythm a
- Music.Time.Internal.Quantize: Group :: [Rhythm a] -> Rhythm a
- Music.Time.Internal.Quantize: Tuplet :: Duration -> (Rhythm a) -> Rhythm a
- Music.Time.Internal.Quantize: data Rhythm a
- Music.Time.Internal.Quantize: dotMod :: Int -> Duration
- Music.Time.Internal.Quantize: drawRhythm :: Show a => Rhythm a -> String
- Music.Time.Internal.Quantize: instance AdditiveGroup (Rhythm a)
- Music.Time.Internal.Quantize: instance Eq a => Eq (Rhythm a)
- Music.Time.Internal.Quantize: instance Foldable Rhythm
- Music.Time.Internal.Quantize: instance Functor Rhythm
- Music.Time.Internal.Quantize: instance HasDuration (Rhythm a)
- Music.Time.Internal.Quantize: instance Monoid (Rhythm a)
- Music.Time.Internal.Quantize: instance Monoid RhythmContext
- Music.Time.Internal.Quantize: instance Semigroup (Rhythm a)
- Music.Time.Internal.Quantize: instance Show a => Show (Rhythm a)
- Music.Time.Internal.Quantize: instance VectorSpace (Rhythm a)
- Music.Time.Internal.Quantize: mapWithDur :: (Duration -> a -> b) -> Rhythm a -> Rhythm b
- Music.Time.Internal.Quantize: quantize :: Tiable a => [(Duration, a)] -> Either String (Rhythm a)
- Music.Time.Internal.Quantize: rewrite :: Rhythm a -> Rhythm a
+ Music.Score.Export.Lilypond: instance Monoid LilypondOptions
+ Music.Score.Internal.Quantize: Beat :: Duration -> a -> Rhythm a
+ Music.Score.Internal.Quantize: Dotted :: Int -> (Rhythm a) -> Rhythm a
+ Music.Score.Internal.Quantize: Group :: [Rhythm a] -> Rhythm a
+ Music.Score.Internal.Quantize: Tuplet :: Duration -> (Rhythm a) -> Rhythm a
+ Music.Score.Internal.Quantize: data Rhythm a
+ Music.Score.Internal.Quantize: dotMod :: Int -> Duration
+ Music.Score.Internal.Quantize: drawRhythm :: Show a => Rhythm a -> String
+ Music.Score.Internal.Quantize: instance AdditiveGroup (Rhythm a)
+ Music.Score.Internal.Quantize: instance Eq a => Eq (Rhythm a)
+ Music.Score.Internal.Quantize: instance Foldable Rhythm
+ Music.Score.Internal.Quantize: instance Functor Rhythm
+ Music.Score.Internal.Quantize: instance HasDuration (Rhythm a)
+ Music.Score.Internal.Quantize: instance Monoid (Rhythm a)
+ Music.Score.Internal.Quantize: instance Monoid RhythmContext
+ Music.Score.Internal.Quantize: instance Semigroup (Rhythm a)
+ Music.Score.Internal.Quantize: instance Show a => Show (Rhythm a)
+ Music.Score.Internal.Quantize: instance VectorSpace (Rhythm a)
+ Music.Score.Internal.Quantize: mapWithDur :: (Duration -> a -> b) -> Rhythm a -> Rhythm b
+ Music.Score.Internal.Quantize: quantize :: Tiable a => [(Duration, a)] -> Either String (Rhythm a)
+ Music.Score.Internal.Quantize: rewrite :: Rhythm a -> Rhythm a
+ Music.Score.Meta.Annotations: getAnnotation :: Annotation -> [String]
+ Music.Time.Meta: metaTypes :: HasMeta a => a -> [String]
+ Music.Time.Score: hasOverlappingEvents :: Score a -> Bool
+ Music.Time.Score: removeRests :: Score (Maybe a) -> Score a
- Music.Score.Meta.Annotations: withAnnotations :: HasText a => ([String] -> Score a -> Score a) -> Score a -> Score a
+ Music.Score.Meta.Annotations: withAnnotations :: ([String] -> Score a -> Score a) -> Score a -> Score a

Files

music-score.cabal view
@@ -1,6 +1,6 @@  name:                   music-score-version:                1.8+version:                1.8.1 author:                 Hans Hoglund maintainer:             Hans Hoglund license:                BSD3@@ -26,15 +26,13 @@                         lens                    >= 4.6      && < 4.7,                         process                 >= 1.2 && < 1.3,                         containers,-                        void,                         nats,          -                        data-default,                         average                 >= 0.6 && < 1,                         semigroups              >= 0.13.0.1 && < 1,-                        contravariant           >= 1.2 && < 2,                         comonad                 >= 4.2.2 && < 5,+                        contravariant           >= 1.2 && < 2,                         bifunctors              >= 4.1.1.1 && < 5,-                        profunctors             >= 4.2.0.1 && < 5,+                        -- profunctors             >= 4.2.0.1 && < 5,                         distributive            >= 0.4.4 && < 5,                         adjunctions             >= 4.2 && < 5,                         transformers            >= 0.3.0.0 && < 0.5,@@ -46,10 +44,10 @@                         HCodecs                 >= 0.5 && < 0.6,                         vector-space            >= 0.8.7 && < 0.9,                         vector-space-points     >= 0.2 && < 0.3,-                        musicxml2               == 1.8,-                        lilypond                == 1.8,-                        music-pitch-literal     == 1.8,-                        music-dynamics-literal  == 1.8,+                        musicxml2               == 1.8.1,+                        lilypond                == 1.8.1,+                        music-pitch-literal     == 1.8.1,+                        music-dynamics-literal  == 1.8.1,                         prettify,                         parsec     exposed-modules:    Data.Clipped@@ -130,7 +128,7 @@                         Music.Time.Internal.Convert                         Music.Time.Internal.Util                         Music.Time.Internal.Transform-                        Music.Time.Internal.Quantize+                        Music.Score.Internal.Quantize                         Music.Score.Internal.Instances                         Music.Score.Internal.Util                         Music.Score.Internal.Export
src/Data/Clipped.hs view
@@ -30,7 +30,7 @@  ----- import Data.Fixed-import           Data.Default+-- import           Data.Default import           Data.Ratio  import           Control.Applicative
src/Music/Score/Export/Backend.hs view
@@ -47,7 +47,6 @@ import           Control.Comonad               (Comonad (..), extract) import           Control.Applicative import           Data.Colour.Names             as Color-import           Data.Default import           Data.Foldable                 (Foldable) import qualified Data.Foldable import           Data.Functor.Couple@@ -59,7 +58,7 @@ import qualified Music.MusicXml.Simple         as MusicXml import           Music.Score.Internal.Export   hiding (MVoice) import           System.Process-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize import qualified Text.Pretty                   as Pretty import qualified Data.List import           Music.Score.Internal.Util (composed, unRatio, swap, retainUpdates)
src/Music/Score/Export/Lilypond.hs view
@@ -58,12 +58,11 @@ import           Data.AffineSpace import           Data.Bifunctor import           Data.Colour.Names                       as Color-import           Data.Default import           Data.Either import           Data.Foldable                           (Foldable) import           Data.Functor.Adjunction                 (unzipR) import           Data.Functor.Context-import           Data.Functor.Contravariant+-- import           Data.Functor.Contravariant import           Data.Functor.Couple import qualified Data.List import           Data.Maybe@@ -91,6 +90,8 @@                                                           unRatio, withPrevNext) import           Music.Score.Meta import           Music.Score.Meta.Time+import           Music.Score.Meta.Title+import           Music.Score.Meta.Attribution import           Music.Score.Part import           Music.Score.Phrases import           Music.Score.Slide@@ -98,7 +99,7 @@ import           Music.Score.Ties import           Music.Score.Tremolo import           Music.Time-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize  import qualified Music.Lilypond                          as Lilypond @@ -574,14 +575,15 @@ -- Convert a score to a Lilypond representation and write to a file. -- writeLilypond :: HasLilypond a => FilePath -> a -> IO ()-writeLilypond = writeLilypond' def+writeLilypond = writeLilypond' mempty  data LilypondOptions   = LyInlineFormat   | LyScoreFormat -instance Default LilypondOptions where-  def = LyInlineFormat+instance Monoid LilypondOptions where+  mempty  = LyInlineFormat+  mappend = const  -- | -- Convert a score to a Lilypond representation and write to a file.@@ -591,7 +593,7 @@   where     -- title    = fromMaybe "" $ flip getTitleAt 0                  $ metaAtStart sc     -- composer = fromMaybe "" $ flip getAttribution "composer"     $ metaAtStart sc-    title = ""+    title    = ""     composer = ""     -- TODO generalize metaAtStart! @@ -638,7 +640,7 @@ -- 'writeLilypond' that may not work well on all platforms.) -- openLilypond :: HasLilypond a => a -> IO ()-openLilypond = openLilypond' def+openLilypond = openLilypond' mempty  -- | -- Typeset a score using Lilypond and open it. (This is simple wrapper around
src/Music/Score/Export/Midi.hs view
@@ -45,7 +45,6 @@ import           Control.Comonad               (Comonad (..), extract) import           Control.Applicative import           Data.Colour.Names             as Color-import           Data.Default import           Data.Foldable                 (Foldable) import qualified Data.Foldable import           Data.Functor.Couple@@ -56,7 +55,7 @@ import qualified Music.MusicXml.Simple         as MusicXml import           Music.Score.Internal.Export   hiding (MVoice) import           System.Process-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize import qualified Text.Pretty                   as Pretty import qualified Data.List import           Music.Score.Internal.Util (composed, unRatio, swap, retainUpdates)
src/Music/Score/Export/MusicXml.hs view
@@ -53,7 +53,6 @@ import           Data.AffineSpace import           Data.Bifunctor import           Data.Colour.Names                       as Color-import           Data.Default import           Data.Foldable                           (Foldable) import qualified Data.Foldable import           Data.Functor.Couple@@ -95,7 +94,7 @@ import           Music.Score.Ties import           Music.Score.Tremolo import           Music.Time-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize  import qualified Text.Pretty                             as Pretty import qualified Music.MusicXml.Simple                   as MusicXml
src/Music/Score/Export/NoteList.hs view
@@ -42,7 +42,6 @@ import           Control.Comonad               (Comonad (..), extract) import           Control.Applicative import           Data.Colour.Names             as Color-import           Data.Default import           Data.Foldable                 (Foldable) import qualified Data.Foldable import           Data.Functor.Couple@@ -53,7 +52,7 @@ import qualified Music.MusicXml.Simple         as MusicXml import           Music.Score.Internal.Export   hiding (MVoice) import           System.Process-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize import qualified Text.Pretty                   as Pretty import qualified Data.List import           Music.Score.Internal.Util (composed, unRatio, swap, retainUpdates)
src/Music/Score/Export/SuperCollider.hs view
@@ -50,7 +50,6 @@ import           Control.Comonad               (Comonad (..), extract) import           Control.Applicative import           Data.Colour.Names             as Color-import           Data.Default import           Data.Foldable                 (Foldable) import qualified Data.Foldable import           Data.Functor.Couple@@ -61,7 +60,7 @@ import qualified Music.MusicXml.Simple         as MusicXml import           Music.Score.Internal.Export   hiding (MVoice) import           System.Process-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize import qualified Text.Pretty                   as Pretty import qualified Data.List import           Music.Score.Internal.Util (composed, unRatio, swap, retainUpdates)
src/Music/Score/Internal/Export.hs view
@@ -54,7 +54,7 @@ import           Music.Score.Dynamics import           Music.Score.Part import           Music.Score.Pitch-import           Music.Time.Internal.Quantize+import           Music.Score.Internal.Quantize import           Music.Score.Ties import           Music.Score.Meta.Time import           Music.Time
src/Music/Score/Internal/Instances.hs view
@@ -32,7 +32,6 @@ import           Control.Lens             hiding (part, transform) import           Control.Monad import           Data.AffineSpace-import           Data.Default import           Data.Monoid.Average import           Data.Foldable import           Data.Functor.Adjunction  (unzipR)
+ src/Music/Score/Internal/Quantize.hs view
@@ -0,0 +1,444 @@++{-# LANGUAGE DeriveFoldable             #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# 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)+--+-- Rhythmical quantization.+--+-------------------------------------------------------------------------------------++module Music.Score.Internal.Quantize (+        -- * Rhythm type+        Rhythm(..),+        mapWithDur,++        -- * Quantization+        quantize,+        rewrite,+        dotMod,+        +        -- * Utility+        drawRhythm+  ) where++import           Prelude             hiding (concat, concatMap, foldl, foldr,+                                      mapM, maximum, minimum, sum)++import           Control.Applicative+import           Control.Lens        (over, (^.), _Left)+import           Control.Monad       (MonadPlus (..), ap, join)+import           Data.Either+import           Data.Foldable+import           Data.Function       (on)+import qualified Data.List           as List+import           Data.Maybe+import           Data.Ord            (comparing)+import           Data.Ratio+import           Data.Semigroup+import           Data.Traversable+import           Data.Tree+import           Data.VectorSpace++import           Text.Parsec         hiding ((<|>))+import           Text.Parsec.Pos++import           Music.Score.Ties+import           Music.Score.Internal.Util+import           Music.Time++data Rhythm a+  = Beat       Duration a                    -- d is divisible by 2+  | Group      [Rhythm a]                    --+  | Dotted     Int (Rhythm a)                -- n > 0.+  | Tuplet     Duration (Rhythm a)           -- d is an emelent of 'konstTuplets'.+  deriving (Eq, Show, Functor, Foldable)+  -- RInvTuplet  Duration (Rhythm a)++getBeatValue :: Rhythm a -> a+getBeatValue (Beat d a) = a+getBeatValue _          = error "getBeatValue: Not a beat"++getBeatDuration :: Rhythm a -> Duration+getBeatDuration (Beat d a) = d+getBeatDuration _          = error "getBeatValue: Not a beat"++-- TODO return voice+realize :: Rhythm a -> [Note a]+realize (Beat d a)      = [(d, a)^.note]+realize (Group rs)      = rs >>= realize+realize (Dotted n r)    = dotMod n `stretch` realize r+realize (Tuplet n r)    = n `stretch` realize r++-- rhythmToTree :: Rhythm a -> Tree (String, Maybe a)+-- rhythmToTree = go+--     where+--         go (Beat d a)     = Node ("beat "  ++ showD d, Just a) []+--         go (Group rs)     = Node ("group", Nothing) (fmap rhythmToTree rs)+--         go (Dotted n r)   = Node ("dotted " ++ show n, Nothing) [rhythmToTree r]+--         go (Tuplet n r)   = Node ("tuplet " ++ showD n, Nothing) [rhythmToTree r]+--         showD = show . toRational+--+-- drawRhythm :: Show a => Rhythm a -> String+-- drawRhythm = drawTree . fmap (uncurry (++) <<< (++ " ") *** show) . rhythmToTree++rhythmToTree :: Rhythm a -> Tree String+rhythmToTree = go+  where+    go (Beat d a)     = Node ("" ++ showD d) []+    go (Group rs)     = Node ("") (fmap rhythmToTree rs)+    go (Dotted n r)   = Node (replicate n '.') [rhythmToTree r]+    go (Tuplet n r)   = Node ("*^ " ++ showD n) [rhythmToTree r]+    showD = (\x -> show (numerator x) ++ "/" ++ show (denominator x)) . toRational++drawRhythm :: Show a => Rhythm a -> String+drawRhythm = drawTree . rhythmToTree++mapWithDur :: (Duration -> a -> b) -> Rhythm a -> Rhythm b+mapWithDur f = go+  where+    go (Beat d x)            = Beat d (f d x)+    go (Dotted n (Beat d x)) = Dotted n $ Beat d (f (dotMod n * d) x)+    go (Group rs)            = Group $ fmap (mapWithDur f) rs+    go (Tuplet m r)          = Tuplet m (mapWithDur f r)        ++instance Semigroup (Rhythm a) where+  (<>) = mappend++-- Catenates using 'Group'+instance Monoid (Rhythm a) where+  mempty = Group []+  Group as `mappend` Group bs   =  Group (as <> bs)+  r        `mappend` Group bs   =  Group ([r] <> bs)+  Group as `mappend` r          =  Group (as <> [r])+  a        `mappend` b          =  Group [a, b]++instance HasDuration (Rhythm a) where+  _duration (Beat d _)        = d+  _duration (Dotted n a)      = _duration a * dotMod n+  _duration (Tuplet c a)      = _duration a * c+  _duration (Group as)        = sum (fmap _duration as)++instance AdditiveGroup (Rhythm a) where+  zeroV   = error "No zeroV for (Rhythm a)"+  (^+^)   = error "No ^+^ for (Rhythm a)"+  negateV = error "No negateV for (Rhythm a)"++instance VectorSpace (Rhythm a) where+  type Scalar (Rhythm a) = Duration+  a *^ Beat d x = Beat (a*d) x+  -- TODO how does this preserve the invariant?++Beat d x `subDur` d' = Beat (d-d') x+++{-+    Rhythm rewrite laws (all up to realization equality)++    Note: Just sketching, needs more formal treatment.+++    Group [Group xs ...] = Group [xs ...]+        [JoinGroup]++    Tuplet m (Tuplet n x) = Tuplet (m * n) x+        [NestTuplet]++    Tuplet m (Group [a,b ...]) = Group [Tuplet m a, Tuplet m b ...]+        [DistributeTuplet]+        This is only OK in certain contexts! Which?++-}++rewrite :: Rhythm a -> Rhythm a+rewrite = rewriteR . rewrite1++rewriteR = go where+  go (Beat d a)     = Beat d a+  go (Group rs)     = Group (fmap (rewriteR . rewrite1) rs)+  go (Dotted n r)   = Dotted n ((rewriteR . rewrite1) r)+  go (Tuplet n r)   = Tuplet n ((rewriteR . rewrite1) r)++rewrite1 = tupletDot . splitTupletIfLongEnough . singleGroup++-- | Removes single-note groups+singleGroup :: Rhythm a -> Rhythm a+singleGroup orig@(Group [x]) = x+singleGroup orig             = orig++-- | Removes dotted notes in 2/3 tuplets.+tupletDot :: Rhythm a -> Rhythm a+tupletDot orig@(Tuplet ((unRatio.realToFrac) -> (2,3)) (Dotted 1 x)) = x+tupletDot orig                                                       = orig++splitTupletIfLongEnough :: Rhythm a -> Rhythm a+splitTupletIfLongEnough r = if _duration r > (1/2) then splitTuplet r else r+-- TODO should compare against beat duration, not just (1/4)++-- | Splits a tuplet iff it contans a group which can be split into two halves whose+--   duration have the ratio 1/2, 1 or 1/2.+splitTuplet :: Rhythm a -> Rhythm a+splitTuplet orig@(Tuplet n (Group xs)) = case trySplit xs of+  Nothing       -> orig+  Just (as, bs) -> Tuplet n (Group as) <> Tuplet n (Group bs)+splitTuplet orig = orig++trySplit :: [Rhythm a] -> Maybe ([Rhythm a], [Rhythm a])+trySplit = firstJust . fmap g . splits+  where+    g (part1, part2)+      | (sum . fmap _duration) part1 `rel` (sum . fmap _duration) part2 = Just (part1, part2)+      | otherwise = Nothing+    rel x y+      | x   == y   = True+      | x   == y*2 = True+      | x*2 == y   = True+      | otherwise  = False++-- |+-- Given a list, return a list of all possible splits.+--+-- >>> splits [1,2,3]+-- [([],[1,2,3]),([1],[2,3]),([1,2],[3]),([1,2,3],[])]+--+splits :: [a] -> [([a],[a])]+splits xs = List.inits xs `zip` List.tails xs+++-- | Return the first @Just@ value, if any.+firstJust :: [Maybe a] -> Maybe a+firstJust = listToMaybe . fmap fromJust . List.dropWhile isNothing+++quantize :: Tiable a => [(Duration, a)] -> Either String (Rhythm a)+quantize = quantize' (atEnd rhythm)++testQuantize :: [Duration] -> IO ()+testQuantize x = case fmap rewrite $ quantize' (atEnd rhythm) $ fmap (\x -> (x,())) $ x of+  Left e -> error e+  Right x -> putStrLn $ drawRhythm x+++konstNumDotsAllowed :: [Int]+konstNumDotsAllowed = [1..2]++konstBounds :: [Duration]+konstBounds = [ 1/2, 1/4, 1/8, 1/16 ]++konstTuplets :: [Duration]+konstTuplets = [ 2/3, 4/5, 4/7, 8/9, 8/11, 8/13, 8/15, 16/17, 16/18, 16/19, 16/21, 16/23 ]++konstMaxTupletNest :: Int+konstMaxTupletNest = 1+++data RhythmContext = RhythmContext {++      -- Time scaling of the current note (from dots and tuplets).+      timeMod    :: Duration,++      -- Time subtracted from the current rhythm (from ties).+      timeSub    :: Duration,++      -- Number of tuplets above the current note (default 0).+      tupleDepth :: Int+  }++instance Monoid RhythmContext where+  mempty = RhythmContext { timeMod = 1, timeSub = 0, tupleDepth = 0 }+  a `mappend` _ = a++modifyTimeMod :: (Duration -> Duration) -> RhythmContext -> RhythmContext+modifyTimeMod f (RhythmContext tm ts td) = RhythmContext (f tm) ts td++modifyTimeSub :: (Duration -> Duration) -> RhythmContext -> RhythmContext+modifyTimeSub f (RhythmContext tm ts td) = RhythmContext tm (f ts) td++modifyTupleDepth :: (Int -> Int) -> RhythmContext -> RhythmContext+modifyTupleDepth f (RhythmContext tm ts td) = RhythmContext tm ts (f td)+++++++-- |+-- A @RhytmParser a b@ converts (Voice a) to b.+type RhythmParser a b = Parsec [(Duration, a)] RhythmContext b++quantize' :: Tiable a => RhythmParser a b -> [(Duration, a)] -> Either String b+quantize' p = over _Left show . runParser p mempty ""+++++++rhythm :: Tiable a => RhythmParser a (Rhythm a)+rhythm = Group <$> many1 (rhythm' <|> bound)++rhythmNoBound :: Tiable a => RhythmParser a (Rhythm a)+rhythmNoBound = Group <$> many1 rhythm'++rhythm' :: Tiable a => RhythmParser a (Rhythm a)+rhythm' = mzero+  <|> beat+  <|> dotted+  <|> tuplet++-- Matches a beat divisible by 2 (notated)+-- beat :: Tiable a => RhythmParser a (Rhythm a)+-- beat = do+--     RhythmContext tm ts _ <- getState+--     (\d -> (d^/tm) `subDur` ts) <$> match (\d _ ->+--         d - ts > 0  &&  isPowerOf 2 (d / tm - ts))++beat :: Tiable a => RhythmParser a (Rhythm a)+beat = do+  RhythmContext tm ts _ <- getState+  match' $ \d x ->+      let d2 = d / tm - ts+      in (d2, x) `assuming` (d - ts > 0 && isPowerOf2 d2)+++-- | Matches a dotted rhythm+dotted :: Tiable a => RhythmParser a (Rhythm a)+dotted = msum . fmap dotted' $ konstNumDotsAllowed++-- | Matches a bound rhythm+bound :: Tiable a => RhythmParser a (Rhythm a)+bound = msum . fmap bound' $ konstBounds++-- | Matches a tuplet+tuplet :: Tiable a => RhythmParser a (Rhythm a)+tuplet = msum . fmap tuplet' $ konstTuplets++++++dotted' :: Tiable a => Int -> RhythmParser a (Rhythm a)+dotted' n = do+  modifyState $ modifyTimeMod (* dotMod n)+  a <- beat+  modifyState $ modifyTimeMod (/ dotMod n)+  return (Dotted n a)++-- | Return the scaling applied to a note with the given number of dots (i.e. 3/2, 7/4 etc).+dotMod :: Int -> Duration+dotMod n = dotMods !! (n-1)++-- [3/2, 7/4, 15/8, 31/16 ..]+dotMods :: [Duration]+dotMods = zipWith (/) (fmap pred $ drop 2 times2) (drop 1 times2)+  where+      times2 = iterate (*2) 1++bound' :: Tiable a => Duration -> RhythmParser a (Rhythm a)+bound' d = do+  modifyState $ modifyTimeSub (+ d)+  a <- beat+  modifyState $ modifyTimeSub (subtract d)+  let (b,c) = toTied $ getBeatValue a++  -- TODO doesn't know order+  return $ Group [Beat (getBeatDuration a) b, Beat d c]++-- tuplet' 2/3 for triplet, 4/5 for quintuplet etc+tuplet' :: Tiable a => Duration -> RhythmParser a (Rhythm a)+tuplet' d = do+  RhythmContext _ _ depth <- getState+  onlyIf (depth < konstMaxTupletNest) $ do+      modifyState $ modifyTimeMod (* d)+                  . modifyTupleDepth succ+      a <- rhythmNoBound+      modifyState $ modifyTimeMod (/ d)+                  . modifyTupleDepth pred+      return (Tuplet d a)+++-------------------------------------------------------------------------------------++-- | Similar to 'many1', but tries longer sequences before trying one.+-- many1long :: Stream s m t => ParsecT s u m a -> ParsecT s u m [a]+-- many1long p = try (many2 p) <|> fmap return p++-- | Similar to 'many1', but applies the parser 2 or more times.+-- many2 :: Stream s m t => ParsecT s u m a -> ParsecT s u m [a]+-- many2 p = do { x <- p; xs <- many1 p; return (x : xs) }++-- Matches a (_duration, value) pair iff the predicate matches, returns beat+match :: Tiable a => (Duration -> a -> Bool) -> RhythmParser a (Rhythm a)+match p = tokenPrim show next test+  where+      show x        = ""+      next pos _ _  = updatePosChar pos 'x'+      test (d,x)    = if p d x then Just (Beat d x) else Nothing++-- Matches a (_duration, value) pair iff the predicate matches, returns beat+match' :: Tiable a => (Duration -> a -> Maybe (Duration, b)) -> RhythmParser a (Rhythm b)+match' f = tokenPrim show next test+  where+      show x        = ""+      next pos _ _  = updatePosChar pos 'x'+      test (d,x)    = case f d x of+          Nothing     -> Nothing+          Just (d,x)  -> Just $ Beat d x++-- |+-- Succeed only if the entire input is consumed.+--+atEnd :: RhythmParser a b -> RhythmParser a b+atEnd p = do+  x <- p+  notFollowedBy' anyToken' <?> "end of input"+  return x+  where+      notFollowedBy' p = try $ (try p >> unexpected "") <|> return ()+      anyToken'        = tokenPrim (const "") (\pos _ _ -> pos) Just++onlyIf :: MonadPlus m => Bool -> m b -> m b+onlyIf b p = if b then p else mzero++-- | Just x or Nothing+assuming :: a -> Bool -> Maybe a+assuming x b = if b then Just x else Nothing+++{-+isDivisibleBy2 :: RealFrac a => a -> Bool+isDivisibleBy2 x = isInt x && even (round x)++isInt :: RealFrac a => a -> Bool+isInt x = x == fromInteger (round x)+-}++logBaseR :: forall a . (RealFloat a, Floating a) => Rational -> Rational -> a+logBaseR k n | isInfinite (fromRational n :: a)      = logBaseR k (n/k) + 1+logBaseR k n | isDenormalized (fromRational n :: a)  = logBaseR k (n*k) - 1+logBaseR k n | otherwise                             = logBase (fromRational k) (fromRational n)+++-- divides     = isDivisibleBy+-- divisibleBy = flip isDivisibleBy++-- As it sounds, do NOT use infix+-- Only works for simple n such as 2 or 3, TODO determine+isPowerOf :: Duration -> Duration -> Bool+isPowerOf n = (== 0.0) . snd . properFraction . logBaseR (toRational n) . toRational++isPowerOf2 :: Duration -> Bool+isPowerOf2 = isPowerOf 2+
src/Music/Score/Meta/Annotations.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -28,6 +27,7 @@  module Music.Score.Meta.Annotations (         Annotation,+        getAnnotation,         annotate,         annotateSpan,         showAnnotations,@@ -40,7 +40,7 @@ import           Data.Semigroup import           Data.String import           Data.Typeable-import           Data.Void+-- import           Data.Void  import           Music.Score.Meta import           Music.Score.Part@@ -76,6 +76,6 @@ showAnnotations' prefix = withAnnotations (flip $ \s -> foldr (text . (prefix ++ )) s)  -- | Handle the annotations in a score.-withAnnotations :: HasText a => ([String] -> Score a -> Score a) -> Score a -> Score a+withAnnotations :: ([String] -> Score a -> Score a) -> Score a -> Score a withAnnotations f = withMeta (f . getAnnotation) 
src/Music/Score/Meta/Attribution.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -64,7 +63,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Barline.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -46,7 +45,6 @@  import           Control.Lens              (view) import           Control.Monad.Plus-import           Data.Default import           Data.Foldable             (Foldable) import qualified Data.Foldable             as F import qualified Data.List                 as List@@ -60,7 +58,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Clef.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -55,7 +54,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Extract.hs view
@@ -11,7 +11,6 @@ import Music.Score.Meta.Title import Music.Score.Meta.Time import Music.Score.Meta.Key--- import Music.Score.Meta.Pickup import Music.Score.Meta.Tempo import Music.Score.Part 
src/Music/Score/Meta/Fermata.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -42,7 +41,6 @@  import           Control.Lens              (view) import           Control.Monad.Plus-import           Data.Default import           Data.Foldable             (Foldable) import qualified Data.Foldable             as F import qualified Data.List                 as List@@ -56,7 +54,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Key.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -58,7 +57,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Pickup.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -39,7 +38,6 @@  import           Control.Lens              (view) import           Control.Monad.Plus-import           Data.Default import           Data.Foldable             (Foldable) import qualified Data.Foldable             as F import qualified Data.List                 as List@@ -53,7 +51,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta@@ -70,14 +67,16 @@     deriving (Eq, Ord, Typeable) -- name level(0=standard) +{- instance Default Pickup where     def = Pickup Nothing 0+-}  instance Semigroup Pickup where     Pickup n1 l1 <> Pickup n2 l2 = Pickup (n1 <> n2) (l1 `max` l2)  instance Monoid Pickup where-    mempty  = def+    mempty  = Pickup Nothing 0     mappend = (<>)  instance Show Pickup where
src/Music/Score/Meta/RehearsalMark.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -43,7 +42,6 @@  import           Control.Lens              (view) import           Control.Monad.Plus-import           Data.Default import           Data.Foldable             (Foldable) import qualified Data.Foldable             as F import qualified Data.List                 as List@@ -57,7 +55,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta@@ -74,14 +71,16 @@     deriving (Eq, Ord, Typeable) -- name level(0=standard) +{- instance Default RehearsalMark where     def = RehearsalMark Nothing 0+-}  instance Semigroup RehearsalMark where     RehearsalMark n1 l1 <> RehearsalMark n2 l2 = RehearsalMark (n1 <> n2) (l1 `max` l2)  instance Monoid RehearsalMark where-    mempty  = def+    mempty  = RehearsalMark Nothing 0     mappend = (<>)  instance Show RehearsalMark where
src/Music/Score/Meta/Tempo.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -60,7 +59,6 @@ import           Control.Lens import           Control.Monad.Plus import           Data.AffineSpace-import           Data.Default import           Data.Foldable             (Foldable) import qualified Data.Foldable             as F import qualified Data.List                 as List@@ -75,7 +73,6 @@ import qualified Data.Traversable          as T import           Data.Typeable import           Data.VectorSpace-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta@@ -115,8 +112,10 @@ showR' ((unRatio -> (x, 1))) = show x showR' ((unRatio -> (x, y))) = "(" ++ show x ++ "/" ++ show y ++ ")" +{- instance Default Tempo where     def = mempty+-}  instance Semigroup Tempo where     (<>) = mappend
src/Music/Score/Meta/Time.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -70,7 +69,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Meta/Title.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE DeriveTraversable          #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-}-{-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -64,7 +63,6 @@ import           Data.Traversable          (Traversable) import qualified Data.Traversable          as T import           Data.Typeable-import           Data.Void  import           Music.Pitch.Literal import           Music.Score.Meta
src/Music/Score/Part.hs view
@@ -73,7 +73,7 @@ import           Control.Comonad import           Control.Lens                  hiding (parts, transform) import           Control.Monad.Plus-import           Data.Default+-- import           Data.Default import           Data.Foldable import           Data.Functor.Couple import qualified Data.List                     as List
src/Music/Score/Phrases.hs view
@@ -60,13 +60,12 @@ import           Data.AffineSpace import           Data.Bifunctor import           Data.Colour.Names          as Color-import           Data.Default import           Data.Either import           Data.Either import           Data.Foldable              (Foldable) import           Data.Functor.Adjunction    (unzipR) import           Data.Functor.Context-import           Data.Functor.Contravariant+import           Data.Functor.Contravariant (Op(..)) import           Data.Functor.Couple import qualified Data.List                  as List import qualified Data.List
src/Music/Score/Pitch.hs view
@@ -52,6 +52,9 @@         pitches',                  -- * Transposition+        PitchPair,+        AffinePair,+        Transposable,         up,         down,         above,@@ -80,14 +83,8 @@          -- -- * Intervals         -- augmentIntervals,-         -- TODO pitchIs, to write filter pitchIs ... etc-        -- TODO gliss etc--        PitchPair,-        AffinePair,-        Transposable,-        +        -- TODO gliss etc           ) where  import           Control.Applicative@@ -430,7 +427,7 @@ -- -- Not to be confused with matrix transposition. ----- >>> up m3 c+-- >>> up m3 (c :: Pitch) -- eb -- -- >>> up _P5 [c,d,e :: Pitch]@@ -448,7 +445,7 @@ -- -- Not to be confused with matrix transposition. ----- >>> down m3 c+-- >>> down m3 (c :: Pitch) -- a -- -- >>> down _P5 [c,d,e]@@ -461,7 +458,7 @@ -- | -- Add the given interval above. ----- >>> above _P8 [c]+-- >>> above _P8 [c :: Pitch] -- [c,c'] -- above :: (Semigroup a, Transposable a) => Interval a -> a -> a@@ -471,7 +468,7 @@ -- | -- Add the given interval below. ----- >>> below _P8 [c]+-- >>> below _P8 [c :: Pitch] -- [c,c_] -- below :: (Semigroup a, Transposable a) => Interval a -> a -> a@@ -491,7 +488,7 @@ -- | -- Transpose up by the given number of octaves. ----- >>> octavesUp 2 c+-- >>> octavesUp 2 (c :: Pitch) -- c'' -- -- >>> octavesUp 1 [c,d,e]@@ -507,7 +504,7 @@ -- | -- Transpose down by the given number of octaves. ----- >>> octavesDown 2 c+-- >>> octavesDown 2 (c :: Pitch) -- c__ -- -- >>> octavesDown 1 [c,d,e]
src/Music/Score/Ties.hs view
@@ -45,7 +45,7 @@ import           Control.Monad import           Control.Monad.Plus import           Data.AffineSpace-import           Data.Default+-- import           Data.Default import           Data.Foldable           hiding (concat) import           Data.Functor.Adjunction (unzipR) import qualified Data.List               as List
src/Music/Time/Duration.hs view
@@ -42,7 +42,7 @@ import           Data.NumInstances    () import           Data.Semigroup       hiding () import           Data.VectorSpace     hiding (Sum (..))-import           Data.Functor.Contravariant+-- import           Data.Functor.Contravariant ()  -- | -- Class of values that have a duration.
src/Music/Time/Event.hs view
@@ -1,4 +1,5 @@ +{-# LANGUAGE CPP                        #-} {-# LANGUAGE DeriveDataTypeable         #-} {-# LANGUAGE DeriveFoldable             #-} {-# LANGUAGE DeriveFunctor              #-}@@ -44,6 +45,7 @@ import           Data.Functor.Compose import           Control.Monad.Compose import           Control.Comonad+import           Data.Distributive (distribute) import           Control.Lens             hiding (Indexable, Level, above,                                            below, index, inside, parts,                                            reversed, transform, (<|), (|>))@@ -78,7 +80,8 @@ -- @ -- --- TODO+#ifndef GHCI+-- TODO move instance Traversable AddMeta where   traverse = annotated instance Eq1 AddMeta where@@ -89,6 +92,7 @@   compare1 = compare instance Ord a => Ord1 (Couple a) where   compare1 = compare+ instance Num (f (g a)) => Num (Compose f g a) where   Compose a + Compose b = Compose (a + b)   Compose a - Compose b = Compose (a - b)@@ -110,8 +114,10 @@   xs >>= f = Compose $ mbind (getCompose . f) (getCompose xs) instance (Comonad f, Comonad g) => Comonad (Compose f g) where   extract (Compose f) = (extract . extract) f+  duplicate = error "No Comonad Compose.duplicate (in Music.Time.Event)"   -- TODO duplicate-  +#endif+ newtype Event a = Event { getEvent :: Compose AddMeta (Couple Span) a }   deriving (     Eq,@@ -120,7 +126,7 @@     Foldable,     Applicative,     Monad,-    Comonad,+    -- Comonad,     Traversable,          Functor,@@ -164,6 +170,11 @@  instance (Show a, Transformable a) => Show (Event a) where   show x = show (x^.from event) ++ "^.event"++instance Comonad Event where+  extract e   = e^.eventValue+  duplicate e = set meta (e^.meta) $ (e^.eventSpan,e)^.event+    -- | View a event as a pair of the original value and the transformation (and vice versa). event :: Iso (Span, a) (Span, b) (Event a) (Event b)
− src/Music/Time/Internal/Quantize.hs
@@ -1,444 +0,0 @@--{-# LANGUAGE DeriveFoldable             #-}-{-# LANGUAGE DeriveFunctor              #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# 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)------ Rhythmical quantization.-------------------------------------------------------------------------------------------module Music.Time.Internal.Quantize (-        -- * Rhythm type-        Rhythm(..),-        mapWithDur,--        -- * Quantization-        quantize,-        rewrite,-        dotMod,-        -        -- * Utility-        drawRhythm-  ) where--import           Prelude             hiding (concat, concatMap, foldl, foldr,-                                      mapM, maximum, minimum, sum)--import           Control.Applicative-import           Control.Lens        (over, (^.), _Left)-import           Control.Monad       (MonadPlus (..), ap, join)-import           Data.Either-import           Data.Foldable-import           Data.Function       (on)-import qualified Data.List           as List-import           Data.Maybe-import           Data.Ord            (comparing)-import           Data.Ratio-import           Data.Semigroup-import           Data.Traversable-import           Data.Tree-import           Data.VectorSpace--import           Text.Parsec         hiding ((<|>))-import           Text.Parsec.Pos--import           Music.Score.Ties-import           Music.Score.Internal.Util-import           Music.Time--data Rhythm a-  = Beat       Duration a                    -- d is divisible by 2-  | Group      [Rhythm a]                    ---  | Dotted     Int (Rhythm a)                -- n > 0.-  | Tuplet     Duration (Rhythm a)           -- d is an emelent of 'konstTuplets'.-  deriving (Eq, Show, Functor, Foldable)-  -- RInvTuplet  Duration (Rhythm a)--getBeatValue :: Rhythm a -> a-getBeatValue (Beat d a) = a-getBeatValue _          = error "getBeatValue: Not a beat"--getBeatDuration :: Rhythm a -> Duration-getBeatDuration (Beat d a) = d-getBeatDuration _          = error "getBeatValue: Not a beat"---- TODO return voice-realize :: Rhythm a -> [Note a]-realize (Beat d a)      = [(d, a)^.note]-realize (Group rs)      = rs >>= realize-realize (Dotted n r)    = dotMod n `stretch` realize r-realize (Tuplet n r)    = n `stretch` realize r---- rhythmToTree :: Rhythm a -> Tree (String, Maybe a)--- rhythmToTree = go---     where---         go (Beat d a)     = Node ("beat "  ++ showD d, Just a) []---         go (Group rs)     = Node ("group", Nothing) (fmap rhythmToTree rs)---         go (Dotted n r)   = Node ("dotted " ++ show n, Nothing) [rhythmToTree r]---         go (Tuplet n r)   = Node ("tuplet " ++ showD n, Nothing) [rhythmToTree r]---         showD = show . toRational------ drawRhythm :: Show a => Rhythm a -> String--- drawRhythm = drawTree . fmap (uncurry (++) <<< (++ " ") *** show) . rhythmToTree--rhythmToTree :: Rhythm a -> Tree String-rhythmToTree = go-  where-    go (Beat d a)     = Node ("" ++ showD d) []-    go (Group rs)     = Node ("") (fmap rhythmToTree rs)-    go (Dotted n r)   = Node (replicate n '.') [rhythmToTree r]-    go (Tuplet n r)   = Node ("*^ " ++ showD n) [rhythmToTree r]-    showD = (\x -> show (numerator x) ++ "/" ++ show (denominator x)) . toRational--drawRhythm :: Show a => Rhythm a -> String-drawRhythm = drawTree . rhythmToTree--mapWithDur :: (Duration -> a -> b) -> Rhythm a -> Rhythm b-mapWithDur f = go-  where-    go (Beat d x)            = Beat d (f d x)-    go (Dotted n (Beat d x)) = Dotted n $ Beat d (f (dotMod n * d) x)-    go (Group rs)            = Group $ fmap (mapWithDur f) rs-    go (Tuplet m r)          = Tuplet m (mapWithDur f r)        --instance Semigroup (Rhythm a) where-  (<>) = mappend---- Catenates using 'Group'-instance Monoid (Rhythm a) where-  mempty = Group []-  Group as `mappend` Group bs   =  Group (as <> bs)-  r        `mappend` Group bs   =  Group ([r] <> bs)-  Group as `mappend` r          =  Group (as <> [r])-  a        `mappend` b          =  Group [a, b]--instance HasDuration (Rhythm a) where-  _duration (Beat d _)        = d-  _duration (Dotted n a)      = _duration a * dotMod n-  _duration (Tuplet c a)      = _duration a * c-  _duration (Group as)        = sum (fmap _duration as)--instance AdditiveGroup (Rhythm a) where-  zeroV   = error "No zeroV for (Rhythm a)"-  (^+^)   = error "No ^+^ for (Rhythm a)"-  negateV = error "No negateV for (Rhythm a)"--instance VectorSpace (Rhythm a) where-  type Scalar (Rhythm a) = Duration-  a *^ Beat d x = Beat (a*d) x-  -- TODO how does this preserve the invariant?--Beat d x `subDur` d' = Beat (d-d') x---{--    Rhythm rewrite laws (all up to realization equality)--    Note: Just sketching, needs more formal treatment.---    Group [Group xs ...] = Group [xs ...]-        [JoinGroup]--    Tuplet m (Tuplet n x) = Tuplet (m * n) x-        [NestTuplet]--    Tuplet m (Group [a,b ...]) = Group [Tuplet m a, Tuplet m b ...]-        [DistributeTuplet]-        This is only OK in certain contexts! Which?---}--rewrite :: Rhythm a -> Rhythm a-rewrite = rewriteR . rewrite1--rewriteR = go where-  go (Beat d a)     = Beat d a-  go (Group rs)     = Group (fmap (rewriteR . rewrite1) rs)-  go (Dotted n r)   = Dotted n ((rewriteR . rewrite1) r)-  go (Tuplet n r)   = Tuplet n ((rewriteR . rewrite1) r)--rewrite1 = tupletDot . splitTupletIfLongEnough . singleGroup---singleGroup :: Rhythm a -> Rhythm a-singleGroup orig@(Group [x]) = x-singleGroup orig             = orig---- | Removes dotted notes in 2/3 tuplets.-tupletDot :: Rhythm a -> Rhythm a-tupletDot orig@(Tuplet ((unRatio.realToFrac) -> (2,3)) (Dotted 1 x)) = x-tupletDot orig                                                       = orig--splitTupletIfLongEnough :: Rhythm a -> Rhythm a-splitTupletIfLongEnough r = if _duration r > (1/2) then splitTuplet r else r--- TODO should compare against beat duration, not just (1/4)---- | Splits a tuplet iff it contans a group which can be split into two halves whose---   duration have the ratio 1/2, 1 or 1/2.-splitTuplet :: Rhythm a -> Rhythm a-splitTuplet orig@(Tuplet n (Group xs)) = case trySplit xs of-  Nothing       -> orig-  Just (as, bs) -> Tuplet n (Group as) <> Tuplet n (Group bs)-splitTuplet orig = orig--trySplit :: [Rhythm a] -> Maybe ([Rhythm a], [Rhythm a])-trySplit = firstJust . fmap g . splits-  where-    g (part1, part2)-      | (sum . fmap _duration) part1 `rel` (sum . fmap _duration) part2 = Just (part1, part2)-      | otherwise = Nothing-    rel x y-      | x   == y   = True-      | x   == y*2 = True-      | x*2 == y   = True-      | otherwise  = False---- |--- Given a list, return a list of all possible splits.------ >>> splits [1,2,3]--- [([],[1,2,3]),([1],[2,3]),([1,2],[3]),([1,2,3],[])]----splits :: [a] -> [([a],[a])]-splits xs = List.inits xs `zip` List.tails xs----- | Return the first @Just@ value, if any.-firstJust :: [Maybe a] -> Maybe a-firstJust = listToMaybe . fmap fromJust . List.dropWhile isNothing---quantize :: Tiable a => [(Duration, a)] -> Either String (Rhythm a)-quantize = quantize' (atEnd rhythm)--testQuantize :: [Duration] -> IO ()-testQuantize x = case fmap rewrite $ quantize' (atEnd rhythm) $ fmap (\x -> (x,())) $ x of-  Left e -> error e-  Right x -> putStrLn $ drawRhythm x---konstNumDotsAllowed :: [Int]-konstNumDotsAllowed = [1..2]--konstBounds :: [Duration]-konstBounds = [ 1/2, 1/4, 1/8, 1/16 ]--konstTuplets :: [Duration]-konstTuplets = [ 2/3, 4/5, 4/7, 8/9, 8/11, 8/13, 8/15, 16/17, 16/18, 16/19, 16/21, 16/23 ]--konstMaxTupletNest :: Int-konstMaxTupletNest = 1---data RhythmContext = RhythmContext {--      -- Time scaling of the current note (from dots and tuplets).-      timeMod    :: Duration,--      -- Time subtracted from the current rhythm (from ties).-      timeSub    :: Duration,--      -- Number of tuplets above the current note (default 0).-      tupleDepth :: Int-  }--instance Monoid RhythmContext where-  mempty = RhythmContext { timeMod = 1, timeSub = 0, tupleDepth = 0 }-  a `mappend` _ = a--modifyTimeMod :: (Duration -> Duration) -> RhythmContext -> RhythmContext-modifyTimeMod f (RhythmContext tm ts td) = RhythmContext (f tm) ts td--modifyTimeSub :: (Duration -> Duration) -> RhythmContext -> RhythmContext-modifyTimeSub f (RhythmContext tm ts td) = RhythmContext tm (f ts) td--modifyTupleDepth :: (Int -> Int) -> RhythmContext -> RhythmContext-modifyTupleDepth f (RhythmContext tm ts td) = RhythmContext tm ts (f td)--------- |--- A @RhytmParser a b@ converts (Voice a) to b.-type RhythmParser a b = Parsec [(Duration, a)] RhythmContext b--quantize' :: Tiable a => RhythmParser a b -> [(Duration, a)] -> Either String b-quantize' p = over _Left show . runParser p mempty ""-------rhythm :: Tiable a => RhythmParser a (Rhythm a)-rhythm = Group <$> many1 (rhythm' <|> bound)--rhythmNoBound :: Tiable a => RhythmParser a (Rhythm a)-rhythmNoBound = Group <$> many1 rhythm'--rhythm' :: Tiable a => RhythmParser a (Rhythm a)-rhythm' = mzero-  <|> beat-  <|> dotted-  <|> tuplet---- Matches a beat divisible by 2 (notated)--- beat :: Tiable a => RhythmParser a (Rhythm a)--- beat = do---     RhythmContext tm ts _ <- getState---     (\d -> (d^/tm) `subDur` ts) <$> match (\d _ ->---         d - ts > 0  &&  isPowerOf 2 (d / tm - ts))--beat :: Tiable a => RhythmParser a (Rhythm a)-beat = do-  RhythmContext tm ts _ <- getState-  match' $ \d x ->-      let d2 = d / tm - ts-      in (d2, x) `assuming` (d - ts > 0 && isPowerOf2 d2)----- | Matches a dotted rhythm-dotted :: Tiable a => RhythmParser a (Rhythm a)-dotted = msum . fmap dotted' $ konstNumDotsAllowed---- | Matches a bound rhythm-bound :: Tiable a => RhythmParser a (Rhythm a)-bound = msum . fmap bound' $ konstBounds---- | Matches a tuplet-tuplet :: Tiable a => RhythmParser a (Rhythm a)-tuplet = msum . fmap tuplet' $ konstTuplets------dotted' :: Tiable a => Int -> RhythmParser a (Rhythm a)-dotted' n = do-  modifyState $ modifyTimeMod (* dotMod n)-  a <- beat-  modifyState $ modifyTimeMod (/ dotMod n)-  return (Dotted n a)---- | Return the scaling applied to a note with the given number of dots (i.e. 3/2, 7/4 etc).-dotMod :: Int -> Duration-dotMod n = dotMods !! (n-1)---- [3/2, 7/4, 15/8, 31/16 ..]-dotMods :: [Duration]-dotMods = zipWith (/) (fmap pred $ drop 2 times2) (drop 1 times2)-  where-      times2 = iterate (*2) 1--bound' :: Tiable a => Duration -> RhythmParser a (Rhythm a)-bound' d = do-  modifyState $ modifyTimeSub (+ d)-  a <- beat-  modifyState $ modifyTimeSub (subtract d)-  let (b,c) = toTied $ getBeatValue a--  -- TODO doesn't know order-  return $ Group [Beat (getBeatDuration a) b, Beat d c]---- tuplet' 2/3 for triplet, 4/5 for quintuplet etc-tuplet' :: Tiable a => Duration -> RhythmParser a (Rhythm a)-tuplet' d = do-  RhythmContext _ _ depth <- getState-  onlyIf (depth < konstMaxTupletNest) $ do-      modifyState $ modifyTimeMod (* d)-                  . modifyTupleDepth succ-      a <- rhythmNoBound-      modifyState $ modifyTimeMod (/ d)-                  . modifyTupleDepth pred-      return (Tuplet d a)-------------------------------------------------------------------------------------------- | Similar to 'many1', but tries longer sequences before trying one.--- many1long :: Stream s m t => ParsecT s u m a -> ParsecT s u m [a]--- many1long p = try (many2 p) <|> fmap return p---- | Similar to 'many1', but applies the parser 2 or more times.--- many2 :: Stream s m t => ParsecT s u m a -> ParsecT s u m [a]--- many2 p = do { x <- p; xs <- many1 p; return (x : xs) }---- Matches a (_duration, value) pair iff the predicate matches, returns beat-match :: Tiable a => (Duration -> a -> Bool) -> RhythmParser a (Rhythm a)-match p = tokenPrim show next test-  where-      show x        = ""-      next pos _ _  = updatePosChar pos 'x'-      test (d,x)    = if p d x then Just (Beat d x) else Nothing---- Matches a (_duration, value) pair iff the predicate matches, returns beat-match' :: Tiable a => (Duration -> a -> Maybe (Duration, b)) -> RhythmParser a (Rhythm b)-match' f = tokenPrim show next test-  where-      show x        = ""-      next pos _ _  = updatePosChar pos 'x'-      test (d,x)    = case f d x of-          Nothing     -> Nothing-          Just (d,x)  -> Just $ Beat d x---- |--- Succeed only if the entire input is consumed.----atEnd :: RhythmParser a b -> RhythmParser a b-atEnd p = do-  x <- p-  notFollowedBy' anyToken' <?> "end of input"-  return x-  where-      notFollowedBy' p = try $ (try p >> unexpected "") <|> return ()-      anyToken'        = tokenPrim (const "") (\pos _ _ -> pos) Just--onlyIf :: MonadPlus m => Bool -> m b -> m b-onlyIf b p = if b then p else mzero---- | Just x or Nothing-assuming :: a -> Bool -> Maybe a-assuming x b = if b then Just x else Nothing---{--isDivisibleBy2 :: RealFrac a => a -> Bool-isDivisibleBy2 x = isInt x && even (round x)--isInt :: RealFrac a => a -> Bool-isInt x = x == fromInteger (round x)--}--logBaseR :: forall a . (RealFloat a, Floating a) => Rational -> Rational -> a-logBaseR k n | isInfinite (fromRational n :: a)      = logBaseR k (n/k) + 1-logBaseR k n | isDenormalized (fromRational n :: a)  = logBaseR k (n*k) - 1-logBaseR k n | otherwise                             = logBase (fromRational k) (fromRational n)----- divides     = isDivisibleBy--- divisibleBy = flip isDivisibleBy---- As it sounds, do NOT use infix--- Only works for simple n such as 2 or 3, TODO determine-isPowerOf :: Duration -> Duration -> Bool-isPowerOf n = (== 0.0) . snd . properFraction . logBaseR (toRational n) . toRational--isPowerOf2 :: Duration -> Bool-isPowerOf2 = isPowerOf 2-
src/Music/Time/Internal/Util.hs view
@@ -54,7 +54,7 @@ import qualified Data.Monoid import qualified Data.List import qualified Data.Ratio-import Data.Functor.Contravariant+import Data.Functor.Contravariant (Equivalence(..), contramap)  -- | Divide a list into parts of maximum length n. -- > category : List
src/Music/Time/Meta.hs view
@@ -55,6 +55,7 @@         getMeta,         mapMeta,         setMeta,+        metaTypes,         applyMeta,         setMetaAttr,         setMetaTAttr,@@ -84,7 +85,7 @@ import qualified Data.Set                  as Set import           Data.String import           Data.Typeable-import           Data.Void+-- import           Data.Void import           Data.Functor.Couple  import           Music.Time.Internal.Util@@ -136,7 +137,7 @@   rev = id  -- Meta is Transformable because the contents of the map is transformable-newtype Meta = Meta (Map String Attribute)+newtype Meta = Meta { _getMeta :: Map String Attribute }   deriving (Transformable, Reversible, Splittable)  instance Semigroup Meta where@@ -206,6 +207,11 @@ -- | Map over meta-data. mapMeta :: HasMeta a => (Meta -> Meta) -> a -> a mapMeta = over meta++-- | Show the types of meta-data attachd to this value.+--   Useful for debugging.+metaTypes :: HasMeta a => a -> [String]+metaTypes x = Map.keys $ _getMeta $ x^.meta  -- | Apply meta-information by combining it with existing meta-information. applyMeta :: HasMeta a => Meta -> a -> a
src/Music/Time/Score.hs view
@@ -46,10 +46,14 @@         -- * Simultaneous         -- TODO check for overlapping values etc         -- simult,+        hasOverlappingEvents,         simultaneous,          -- * Normalize         normalizeScore,+        removeRests,+        +        -- * Utility         printEras,          -- * Unsafe versions@@ -395,6 +399,9 @@     reset x = set onset (view onset x `max` 0) x     normalizeScoreDurations = over (events . each . era) normalizeSpan +removeRests :: Score (Maybe a) -> Score a+removeRests = mcatMaybes+ -- TODO version that reverses the values where appropriate -- Use over (events . each) normalizeEvent or similar @@ -435,3 +442,22 @@ simultaneous :: (Transformable a, Semigroup a) => Score a -> Score a simultaneous = fmap (sconcat . NonEmpty.fromList) . simultaneous' +++hasOverlappingEvents :: Score a -> Bool+hasOverlappingEvents = anyDistinctOverlaps . toListOf (events.each.era)++hasDuplicates :: Eq a => [a] -> Bool+hasDuplicates xs = List.nub xs /= xs++anyDistinctOverlaps :: [Span] -> Bool+anyDistinctOverlaps xs = hasDuplicates xs || anyOverlaps xs+  where+    anyOverlaps = foldr (||) False . combined overlaps+-- If the span list has duplicates, we have overlaps.++combined :: Eq a => (a -> a -> b) -> [a] -> [b]+combined f as = mcatMaybes [if x == y then Nothing else Just (x `f` y) | x <- as, y <- as]++squared :: (a -> a -> b) -> [a] -> [b]+squared f as = [x `f` y | x <- as, y <- as]