tidal-core-1.9.6: src/Sound/Tidal/Pattern.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-
Pattern.hs - core representation of Tidal patterns
Copyright (C) 2020 Alex McLean and contributors
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library. If not, see <http://www.gnu.org/licenses/>.
-}
module Sound.Tidal.Pattern
( module Sound.Tidal.Pattern,
module Sound.Tidal.Time,
)
where
import Control.Applicative (liftA2)
import Control.DeepSeq (NFData)
import Control.Monad ((>=>))
import Data.Data (Data)
import Data.Fixed (mod')
import Data.List (delete, findIndex, (\\))
import qualified Data.Map.Strict as Map
import Data.Maybe (catMaybes, fromJust, isJust, mapMaybe)
import Data.Typeable (Typeable)
import Data.Word (Word8)
import GHC.Generics (Generic)
import Sound.Tidal.Time
import Prelude hiding ((*>), (<*))
------------------------------------------------------------------------
-- * Types
-- | an Arc and some named control values
data State = State
{ arc :: Arc,
controls :: ValueMap
}
-- | A datatype representing events taking place over time
data Pattern a = Pattern {query :: State -> [Event a], tactus :: Maybe (Pattern Rational), pureValue :: Maybe a}
deriving (Generic, Functor)
instance (NFData a) => NFData (Pattern a)
pattern :: (State -> [Event a]) -> Pattern a
pattern f = Pattern f Nothing Nothing
setTactus :: Maybe (Pattern Rational) -> Pattern a -> Pattern a
setTactus r p = p {tactus = r}
setTactusFrom :: Pattern b -> Pattern a -> Pattern a
setTactusFrom a b = b {tactus = tactus a}
withTactus :: (Rational -> Rational) -> Pattern a -> Pattern a
withTactus f p = p {tactus = fmap (fmap f) $ tactus p}
steps :: Pattern Rational -> Pattern a -> Pattern a
steps target p@(Pattern _ (Just t) _) = setTactus (Just target) $ fast (target / t) p
-- raise error?
steps _ p = p
-- _steps :: Pattern Rational -> Pattern a -> Pattern a
-- _steps = patternify _steps
keepMeta :: Pattern a -> Pattern a -> Pattern a
keepMeta from to = to {tactus = tactus from, pureValue = pureValue from}
keepTactus :: Pattern a -> Pattern b -> Pattern b
keepTactus from to = to {tactus = tactus from}
-- type StateMap = Map.Map String (Pattern Value)
type ControlPattern = Pattern ValueMap
-- * Applicative and friends
instance Applicative Pattern where
-- Repeat the given value once per cycle, forever
pure :: a -> Pattern a
pure v = Pattern q (Just 1) (Just v)
where
q (State a _) =
map
( \a' ->
Event
(Context [])
(Just a')
(sect a a')
v
)
$ cycleArcsInArc a
-- In each of @a <*> b@, @a <* b@ and @a *> b@
-- (using the definitions from this module, not the Prelude),
-- the time structure of the result
-- depends on the structures of both @a@ and @b@.
-- They all result in @Event@s with identical @part@s and @value@s.
-- However, their @whole@s are different.
--
-- For instance, @listToPat [(+1), (+2)] <*> "0 10 100"@
-- gives the following 4-@Event@ cycle:
-- > (0>⅓)|1
-- > (⅓>½)|11
-- > (½>⅔)|12
-- > (⅔>1)|102
-- If we use @<*@ instead, we get this:
-- > (0>⅓)-½|1
-- > 0-(⅓>½)|11
-- > (½>⅔)-1|12
-- > ½-(⅔>1)|102
-- And if we use @*>@, we get this:
-- > (0>⅓)|1
-- > (⅓>½)-⅔|11
-- > ⅓-(½>⅔)|12
-- > (⅔>1)|102
(<*>) a b = (applyPatToPatBoth a b) {tactus = (\a' b' -> lcmr <$> a' <*> b') <$> tactus a <*> tactus b}
-- | Like @<*>@, but the "wholes" come from the left
(<*) :: Pattern (a -> b) -> Pattern a -> Pattern b
(<*) a b = keepTactus a $ applyPatToPatLeft a b
-- | Like @<*>@, but the "wholes" come from the right
(*>) :: Pattern (a -> b) -> Pattern a -> Pattern b
(*>) a b = keepTactus b $ applyPatToPatRight a b
-- | Like @<*>@, but the "wholes" come from the left
(<<*) :: Pattern (a -> b) -> Pattern a -> Pattern b
(<<*) a b = (applyPatToPatSqueeze a b) {tactus = (*) <$> tactus a <*> tactus b}
infixl 4 <*, *>, <<*
applyPatToPat :: (Maybe Arc -> Maybe Arc -> Maybe (Maybe Arc)) -> Pattern (a -> b) -> Pattern a -> Pattern b
applyPatToPat combineWholes pf px = pattern q
where
q st = catMaybes $ concatMap match $ query pf st
where
match ef@(Event (Context c) _ fPart f) =
map
( \ex@(Event (Context c') _ xPart x) ->
do
whole' <- combineWholes (whole ef) (whole ex)
part' <- subArc fPart xPart
return (Event (Context $ c ++ c') whole' part' (f x))
)
(query px $ st {arc = wholeOrPart ef})
applyPatToPatBoth :: Pattern (a -> b) -> Pattern a -> Pattern b
applyPatToPatBoth pf px = pattern q
where
q st = catMaybes $ (concatMap match $ query pf st) ++ (concatMap matchX $ query (filterAnalog px) st)
where
-- match analog events from pf with all events from px
match ef@(Event _ Nothing fPart _) = map (withFX ef) (query px $ st {arc = fPart}) -- analog
-- match digital events from pf with digital events from px
match ef@(Event _ (Just fWhole) _ _) = map (withFX ef) (query (filterDigital px) $ st {arc = fWhole}) -- digital
-- match analog events from px (constrained above) with digital events from px
matchX ex@(Event _ Nothing fPart _) = map (`withFX` ex) (query (filterDigital pf) $ st {arc = fPart}) -- digital
matchX _ = error "can't happen"
withFX ef ex = do
whole' <- subMaybeArc (whole ef) (whole ex)
part' <- subArc (part ef) (part ex)
return (Event (combineContexts [context ef, context ex]) whole' part' (value ef $ value ex))
applyPatToPatLeft :: Pattern (a -> b) -> Pattern a -> Pattern b
applyPatToPatLeft pf px = pattern q
where
q st = catMaybes $ concatMap match $ query pf st
where
match ef = map (withFX ef) (query px $ st {arc = wholeOrPart ef})
withFX ef ex = do
let whole' = whole ef
part' <- subArc (part ef) (part ex)
return (Event (combineContexts [context ef, context ex]) whole' part' (value ef $ value ex))
applyPatToPatRight :: Pattern (a -> b) -> Pattern a -> Pattern b
applyPatToPatRight pf px = pattern q
where
q st = catMaybes $ concatMap match $ query px st
where
match ex = map (`withFX` ex) (query pf $ st {arc = wholeOrPart ex})
withFX ef ex = do
let whole' = whole ex
part' <- subArc (part ef) (part ex)
return (Event (combineContexts [context ef, context ex]) whole' part' (value ef $ value ex))
applyPatToPatSqueeze :: Pattern (a -> b) -> Pattern a -> Pattern b
applyPatToPatSqueeze pf px = squeezeJoin $ (<$> px) <$> pf
-- * Monad and friends
--
-- $monadAndFriends
--
-- Note there are four ways of joining - the default 'unwrap' used by @>>=@, as well
-- as @innerJoin@, @innerJoin@ and @squeezeJoin@.
instance Monad Pattern where
return = pure
p >>= f = unwrap (f <$> p)
-- | Turns a pattern of patterns into a single pattern.
-- (this is actually 'join')
--
-- 1/ For query 'arc', get the events from the outer pattern @pp@
-- 2/ Query the inner pattern using the 'part' of the outer
-- 3/ For each inner event, set the whole and part to be the intersection
-- of the outer whole and part, respectively
-- 4/ Concatenate all the events together (discarding wholes/parts that didn't intersect)
--
-- TODO - what if a continuous pattern contains a discrete one, or vice-versa?
unwrap :: Pattern (Pattern a) -> Pattern a
unwrap pp = pp {query = q, pureValue = Nothing}
where
q st =
concatMap
( \(Event c w p v) ->
mapMaybe (munge c w p) $ query v st {arc = p}
)
(query pp st)
munge oc ow op (Event ic iw ip v') =
do
w' <- subMaybeArc ow iw
p' <- subArc op ip
return (Event (combineContexts [ic, oc]) w' p' v')
-- | Turns a pattern of patterns into a single pattern. Like @unwrap@,
-- but structure only comes from the inner pattern.
innerJoin :: Pattern (Pattern a) -> Pattern a
innerJoin pp = setTactus (Just $ innerJoin' $ filterJust $ tactus <$> pp) $ innerJoin' pp
where
-- \| innerJoin but without tactus manipulation (to avoid recursion)
innerJoin' :: Pattern (Pattern b) -> Pattern b
innerJoin' pp' = pp' {query = q, pureValue = Nothing}
where
q st =
concatMap
(\(Event oc _ op v) -> mapMaybe (munge oc) $ query v st {arc = op})
(query pp' st)
where
munge oc (Event ic iw ip v) =
do
p <- subArc (arc st) ip
p' <- subArc p (arc st)
return (Event (combineContexts [ic, oc]) iw p' v)
-- | Turns a pattern of patterns into a single pattern. Like @unwrap@,
-- but structure only comes from the outer pattern.
outerJoin :: Pattern (Pattern a) -> Pattern a
outerJoin pp = pp {query = q, pureValue = Nothing}
where
q st =
concatMap
( \e ->
mapMaybe (munge (context e) (whole e) (part e)) $ query (value e) st {arc = pure (start $ wholeOrPart e)}
)
(query pp st)
where
munge oc ow op (Event ic _ _ v') =
do
p' <- subArc (arc st) op
return (Event (combineContexts [oc, ic]) ow p' v')
-- | Like @unwrap@, but cycles of the inner patterns are compressed to fit the
-- timespan of the outer whole (or the original query if it's a continuous pattern?)
-- TODO - what if a continuous pattern contains a discrete one, or vice-versa?
-- TODO - tactus
squeezeJoin :: Pattern (Pattern a) -> Pattern a
squeezeJoin pp = pp {query = q, pureValue = Nothing}
where
q st =
concatMap
( \e@(Event c w p v) ->
mapMaybe (munge c w p) $ query (focusArc (wholeOrPart e) v) st {arc = p}
)
(query pp st)
munge oContext oWhole oPart (Event iContext iWhole iPart v) =
do
w' <- subMaybeArc oWhole iWhole
p' <- subArc oPart iPart
return (Event (combineContexts [iContext, oContext]) w' p' v)
_trigJoin :: Bool -> Pattern (Pattern a) -> Pattern a
_trigJoin cycleZero pat_of_pats = pattern q
where
q st =
concatMap
( catMaybes
. ( \(Event oc jow op ov) ->
map
( \(Event ic iw ip iv) ->
do
w <- subMaybeArc jow iw
p <- subArc op ip
return $ Event (combineContexts [ic, oc]) w p iv
)
$ query ((if cycleZero then id else cyclePos) (start (fromJust jow)) `rotR` ov) st
)
)
(query (filterDigital pat_of_pats) st)
trigJoin :: Pattern (Pattern a) -> Pattern a
trigJoin = _trigJoin False
trigZeroJoin :: Pattern (Pattern a) -> Pattern a
trigZeroJoin = _trigJoin True
reset :: Pattern Bool -> Pattern a -> Pattern a
reset bp pat = trigJoin $ (\v -> if v then pat else silence) <$> bp
resetTo :: Pattern Rational -> Pattern a -> Pattern a
resetTo bp pat = trigJoin $ (`rotL` pat) <$> bp
restart :: Pattern Bool -> Pattern a -> Pattern a
restart bp pat = trigZeroJoin $ (\v -> if v then pat else silence) <$> bp
restartTo :: Pattern Rational -> Pattern a -> Pattern a
restartTo bp pat = trigZeroJoin $ (`rotL` pat) <$> bp
-- | * Patterns as numbers
noOv :: String -> a
noOv meth = error $ meth ++ ": not supported for patterns"
instance Eq (Pattern a) where
(==) :: Pattern a -> Pattern a -> Bool
(==) = noOv "(==)"
instance (Ord a) => Ord (Pattern a) where
min :: (Ord a) => Pattern a -> Pattern a -> Pattern a
min = liftA2 min
max :: (Ord a) => Pattern a -> Pattern a -> Pattern a
max = liftA2 max
compare :: (Ord a) => Pattern a -> Pattern a -> Ordering
compare = noOv "compare"
(<=) :: (Ord a) => Pattern a -> Pattern a -> Bool
(<=) = noOv "(<=)"
instance (Num a) => Num (Pattern a) where
negate :: (Num a) => Pattern a -> Pattern a
negate = fmap negate
(+) :: (Num a) => Pattern a -> Pattern a -> Pattern a
(+) = liftA2 (+)
(*) :: (Num a) => Pattern a -> Pattern a -> Pattern a
(*) = liftA2 (*)
fromInteger :: (Num a) => Integer -> Pattern a
fromInteger = pure . fromInteger
abs :: (Num a) => Pattern a -> Pattern a
abs = fmap abs
signum :: (Num a) => Pattern a -> Pattern a
signum = fmap signum
instance (Enum a) => Enum (Pattern a) where
succ :: (Enum a) => Pattern a -> Pattern a
succ = fmap succ
pred :: (Enum a) => Pattern a -> Pattern a
pred = fmap pred
toEnum :: (Enum a) => Int -> Pattern a
toEnum = pure . toEnum
fromEnum :: (Enum a) => Pattern a -> Int
fromEnum = noOv "fromEnum"
enumFrom :: (Enum a) => Pattern a -> [Pattern a]
enumFrom = noOv "enumFrom"
enumFromThen :: (Enum a) => Pattern a -> Pattern a -> [Pattern a]
enumFromThen = noOv "enumFromThen"
enumFromTo :: (Enum a) => Pattern a -> Pattern a -> [Pattern a]
enumFromTo = noOv "enumFromTo"
enumFromThenTo :: (Enum a) => Pattern a -> Pattern a -> Pattern a -> [Pattern a]
enumFromThenTo = noOv "enumFromThenTo"
instance Monoid (Pattern a) where
mempty :: Pattern a
mempty = empty
instance Semigroup (Pattern a) where
(<>) :: Pattern a -> Pattern a -> Pattern a
(<>) !p !p' = pattern $ \st -> query p st ++ query p' st
instance (Num a, Ord a) => Real (Pattern a) where
toRational :: (Num a, Ord a) => Pattern a -> Rational
toRational = noOv "toRational"
instance (Integral a) => Integral (Pattern a) where
quot :: (Integral a) => Pattern a -> Pattern a -> Pattern a
quot = liftA2 quot
rem :: (Integral a) => Pattern a -> Pattern a -> Pattern a
rem = liftA2 rem
div :: (Integral a) => Pattern a -> Pattern a -> Pattern a
div = liftA2 div
mod :: (Integral a) => Pattern a -> Pattern a -> Pattern a
mod = liftA2 mod
toInteger :: (Integral a) => Pattern a -> Integer
toInteger = noOv "toInteger"
quotRem :: (Integral a) => Pattern a -> Pattern a -> (Pattern a, Pattern a)
x `quotRem` y = (x `quot` y, x `rem` y)
divMod :: (Integral a) => Pattern a -> Pattern a -> (Pattern a, Pattern a)
x `divMod` y = (x `div` y, x `mod` y)
instance (Fractional a) => Fractional (Pattern a) where
recip :: (Fractional a) => Pattern a -> Pattern a
recip = fmap recip
fromRational :: (Fractional a) => Rational -> Pattern a
fromRational = pure . fromRational
instance (Floating a) => Floating (Pattern a) where
pi :: (Floating a) => Pattern a
pi = pure pi
sqrt :: (Floating a) => Pattern a -> Pattern a
sqrt = fmap sqrt
exp :: (Floating a) => Pattern a -> Pattern a
exp = fmap exp
log :: (Floating a) => Pattern a -> Pattern a
log = fmap log
sin :: (Floating a) => Pattern a -> Pattern a
sin = fmap sin
cos :: (Floating a) => Pattern a -> Pattern a
cos = fmap cos
asin :: (Floating a) => Pattern a -> Pattern a
asin = fmap asin
atan :: (Floating a) => Pattern a -> Pattern a
atan = fmap atan
acos :: (Floating a) => Pattern a -> Pattern a
acos = fmap acos
sinh :: (Floating a) => Pattern a -> Pattern a
sinh = fmap sinh
cosh :: (Floating a) => Pattern a -> Pattern a
cosh = fmap cosh
asinh :: (Floating a) => Pattern a -> Pattern a
asinh = fmap asinh
atanh :: (Floating a) => Pattern a -> Pattern a
atanh = fmap atanh
acosh :: (Floating a) => Pattern a -> Pattern a
acosh = fmap acosh
instance (RealFrac a) => RealFrac (Pattern a) where
properFraction :: (RealFrac a, Integral b) => Pattern a -> (b, Pattern a)
properFraction = noOv "properFraction"
truncate :: (RealFrac a, Integral b) => Pattern a -> b
truncate = noOv "truncate"
round :: (RealFrac a, Integral b) => Pattern a -> b
round = noOv "round"
ceiling :: (RealFrac a, Integral b) => Pattern a -> b
ceiling = noOv "ceiling"
floor :: (RealFrac a, Integral b) => Pattern a -> b
floor = noOv "floor"
instance (RealFloat a) => RealFloat (Pattern a) where
floatRadix :: (RealFloat a) => Pattern a -> Integer
floatRadix = noOv "floatRadix"
floatDigits :: (RealFloat a) => Pattern a -> Int
floatDigits = noOv "floatDigits"
floatRange :: (RealFloat a) => Pattern a -> (Int, Int)
floatRange = noOv "floatRange"
decodeFloat :: (RealFloat a) => Pattern a -> (Integer, Int)
decodeFloat = noOv "decodeFloat"
encodeFloat :: (RealFloat a) => Integer -> Int -> Pattern a
encodeFloat = ((.) . (.)) pure encodeFloat
exponent :: (RealFloat a) => Pattern a -> Int
exponent = noOv "exponent"
significand :: (RealFloat a) => Pattern a -> Pattern a
significand = noOv "significand"
scaleFloat :: (RealFloat a) => Int -> Pattern a -> Pattern a
scaleFloat n = fmap (scaleFloat n)
isNaN :: (RealFloat a) => Pattern a -> Bool
isNaN = noOv "isNaN"
isInfinite :: (RealFloat a) => Pattern a -> Bool
isInfinite = noOv "isInfinite"
isDenormalized :: (RealFloat a) => Pattern a -> Bool
isDenormalized = noOv "isDenormalized"
isNegativeZero :: (RealFloat a) => Pattern a -> Bool
isNegativeZero = noOv "isNegativeZero"
isIEEE :: (RealFloat a) => Pattern a -> Bool
isIEEE = noOv "isIEEE"
atan2 :: (RealFloat a) => Pattern a -> Pattern a -> Pattern a
atan2 = liftA2 atan2
instance Num ValueMap where
negate :: ValueMap -> ValueMap
negate = (applyFIS negate negate id <$>)
(+) :: ValueMap -> ValueMap -> ValueMap
(+) = Map.unionWith (fNum2 (+) (+))
(*) :: ValueMap -> ValueMap -> ValueMap
(*) = Map.unionWith (fNum2 (*) (*))
fromInteger :: Integer -> ValueMap
fromInteger i = Map.singleton "n" $ VI (fromInteger i)
signum :: ValueMap -> ValueMap
signum = (applyFIS signum signum id <$>)
abs :: ValueMap -> ValueMap
abs = (applyFIS abs abs id <$>)
instance Fractional ValueMap where
recip :: ValueMap -> ValueMap
recip = fmap (applyFIS recip id id)
fromRational :: Rational -> ValueMap
fromRational r = Map.singleton "speed" $ VF (fromRational r)
class Moddable a where
gmod :: a -> a -> a
instance Moddable Double where
gmod :: Double -> Double -> Double
gmod = mod'
instance Moddable Rational where
gmod :: Rational -> Rational -> Rational
gmod = mod'
instance Moddable Note where
gmod :: Note -> Note -> Note
gmod (Note a) (Note b) = Note (mod' a b)
instance Moddable Int where
gmod :: Int -> Int -> Int
gmod = mod
instance Moddable ValueMap where
gmod :: ValueMap -> ValueMap -> ValueMap
gmod = Map.unionWith (fNum2 mod mod')
instance Floating ValueMap where
pi :: ValueMap
pi = noOv "pi"
exp :: ValueMap -> ValueMap
exp _ = noOv "exp"
log :: ValueMap -> ValueMap
log _ = noOv "log"
sin :: ValueMap -> ValueMap
sin _ = noOv "sin"
cos :: ValueMap -> ValueMap
cos _ = noOv "cos"
asin :: ValueMap -> ValueMap
asin _ = noOv "asin"
acos :: ValueMap -> ValueMap
acos _ = noOv "acos"
atan :: ValueMap -> ValueMap
atan _ = noOv "atan"
sinh :: ValueMap -> ValueMap
sinh _ = noOv "sinh"
cosh :: ValueMap -> ValueMap
cosh _ = noOv "cosh"
asinh :: ValueMap -> ValueMap
asinh _ = noOv "asinh"
acosh :: ValueMap -> ValueMap
acosh _ = noOv "acosh"
atanh :: ValueMap -> ValueMap
atanh _ = noOv "atanh"
------------------------------------------------------------------------
-- * Internal/fundamental functions
empty :: Pattern a
empty = Pattern {query = const [], tactus = Just 1, pureValue = Nothing}
silence :: Pattern a
silence = empty
nothing :: Pattern a
nothing = empty {tactus = Just 0}
queryArc :: Pattern a -> Arc -> [Event a]
queryArc p a = query p $ State a Map.empty
-- | Splits queries that span cycles. For example `query p (0.5, 1.5)` would be
-- turned into two queries, `(0.5,1)` and `(1,1.5)`, and the results
-- combined. Being able to assume queries don't span cycles often
-- makes transformations easier to specify.
splitQueries :: Pattern a -> Pattern a
splitQueries p = p {query = \st -> concatMap (\a -> query p st {arc = a}) $ arcCyclesZW (arc st)}
-- | Apply a function to the arcs/timespans (both whole and parts) of the result
withResultArc :: (Arc -> Arc) -> Pattern a -> Pattern a
withResultArc f pat =
pat
{ query = map (\(Event c w p e) -> Event c (f <$> w) (f p) e) . query pat
}
-- | Apply a function to the time (both start and end of the timespans
-- of both whole and parts) of the result
withResultTime :: (Time -> Time) -> Pattern a -> Pattern a
withResultTime f = withResultArc (\(Arc s e) -> Arc (f s) (f e))
withResultStart :: (Time -> Time) -> Pattern a -> Pattern a
withResultStart f pat = withResultArc (\(Arc s e) -> Arc (f s) (f s + (e - s))) pat
-- | Apply a function to the timespan of the query
withQueryArc :: (Arc -> Arc) -> Pattern a -> Pattern a
withQueryArc f pat = pat {query = query pat . (\(State a m) -> State (f a) m)}
-- | Apply a function to the time (both start and end) of the query
withQueryTime :: (Time -> Time) -> Pattern a -> Pattern a
withQueryTime f pat = withQueryArc (\(Arc s e) -> Arc (f s) (f e)) pat
withQueryStart :: (Time -> Time) -> Pattern a -> Pattern a
withQueryStart f pat = withQueryArc (\(Arc s e) -> Arc (f s) (f s + (e - s))) pat
-- | Apply a function to the control values of the query
withQueryControls :: (ValueMap -> ValueMap) -> Pattern a -> Pattern a
withQueryControls f pat = pat {query = query pat . (\(State a m) -> State a (f m))}
-- | @withEvent f p@ returns a new @Pattern@ with each event mapped over
-- function @f@.
withEvent :: (Event a -> Event b) -> Pattern a -> Pattern b
withEvent f p = p {query = map f . query p, pureValue = Nothing}
-- | @withEvent f p@ returns a new @Pattern@ with each value mapped over
-- function @f@.
withValue :: (a -> b) -> Pattern a -> Pattern b
withValue f pat = withEvent (fmap f) pat
-- | @withEvent f p@ returns a new @Pattern@ with f applied to the resulting list of events for each query
-- function @f@.
withEvents :: ([Event a] -> [Event b]) -> Pattern a -> Pattern b
withEvents f p = p {query = f . query p, pureValue = Nothing}
-- | @withPart f p@ returns a new @Pattern@ with function @f@ applied
-- to the part.
withPart :: (Arc -> Arc) -> Pattern a -> Pattern a
withPart f = withEvent (\(Event c w p v) -> Event c w (f p) v)
_extract :: (Value -> Maybe a) -> String -> ControlPattern -> Pattern a
_extract f name pat = filterJust $ withValue (Map.lookup name >=> f) pat
-- | Extract a pattern of integer values by from a control pattern, given the name of the control
extractI :: String -> ControlPattern -> Pattern Int
extractI = _extract getI
-- | Extract a pattern of floating point values by from a control pattern, given the name of the control
extractF :: String -> ControlPattern -> Pattern Double
extractF = _extract getF
-- | Extract a pattern of string values by from a control pattern, given the name of the control
extractS :: String -> ControlPattern -> Pattern String
extractS = _extract getS
-- | Extract a pattern of boolean values by from a control pattern, given the name of the control
extractB :: String -> ControlPattern -> Pattern Bool
extractB = _extract getB
-- | Extract a pattern of rational values by from a control pattern, given the name of the control
extractR :: String -> ControlPattern -> Pattern Rational
extractR = _extract getR
-- | Extract a pattern of note values by from a control pattern, given the name of the control
extractN :: String -> ControlPattern -> Pattern Note
extractN = _extract getN
compressArc :: Arc -> Pattern a -> Pattern a
compressArc (Arc s e) p
| s > e = empty
| s > 1 || e > 1 = empty
| s < 0 || e < 0 = empty
| otherwise = s `rotR` _fastGap (1 / (e - s)) p
compressArcTo :: Arc -> Pattern a -> Pattern a
compressArcTo (Arc s e) = compressArc (Arc (cyclePos s) (e - sam s))
focusArc :: Arc -> Pattern a -> Pattern a
focusArc (Arc s e) p = s `rotR` _fast (1 / (e - s)) ((sam s) `rotL` p)
-- | Speed up a pattern by the given time pattern.
--
-- For example, the following will play the sound pattern @"bd sn kurt"@ twice as
-- fast (i.e., so it repeats twice per cycle), and the vowel pattern three times
-- as fast:
--
-- > d1 $ sound (fast 2 "bd sn kurt")
-- > # fast 3 (vowel "a e o")
--
-- The first parameter can be patterned to, for example, play the pattern at twice
-- the speed for the first half of each cycle and then four times the speed for the
-- second half:
--
-- > d1 $ fast "2 4" $ sound "bd sn kurt cp"
fast :: Pattern Time -> Pattern a -> Pattern a
fast t pat = patternify' _fast t pat
-- | @fastSqueeze@ speeds up a pattern by a time pattern given as input,
-- squeezing the resulting pattern inside one cycle and playing the original
-- pattern at every repetition.
--
-- To better understand how it works, compare it with 'fast':
--
-- >>> fast "1 2" $ s "bd sn"
-- (0>½)|s: "bd"
-- (½>¾)|s: "bd"
-- (¾>1)|s: "sn"
--
-- This will give @bd@ played in the first half cycle, and @bd sn@ in the second
-- half. On the other hand, using fastSqueeze;
--
-- >>> print $ fastSqueeze "1 2" $ s "bd sn"
-- (0>¼)|s: "bd"
-- (¼>½)|s: "sn"
-- (½>⅝)|s: "bd"
-- (⅝>¾)|s: "sn"
-- (¾>⅞)|s: "bd"
-- (⅞>1)|s: "sn"
--
-- The original pattern will play in the first half, and two repetitions of the
-- original pattern will play in the second half. That is, every repetition
-- contains the whole pattern.
--
-- If the time pattern has a single value, it becomes equivalent to 'fast':
--
-- > d1 $ fastSqueeze 2 $ s "bd sn"
-- > d1 $ fast 2 $ s "bd sn"
-- > d1 $ s "[bd sn]*2"
fastSqueeze :: Pattern Time -> Pattern a -> Pattern a
fastSqueeze = patternifySqueeze _fast
-- | An alias for @fast@
density :: Pattern Time -> Pattern a -> Pattern a
density = fast
_fast :: Time -> Pattern a -> Pattern a
_fast rate pat
| rate == 0 = silence
| rate < 0 = rev $ _fast (negate rate) pat
| otherwise = keepTactus pat $ withResultTime (/ rate) $ withQueryTime (* rate) pat
-- | Slow down a pattern by the given time pattern.
--
-- For example, the following will play the sound pattern @"bd sn kurt"@ twice as
-- slow (i.e., so it repeats once every two cycles), and the vowel pattern three
-- times as slow:
--
-- > d1 $ sound (slow 2 "bd sn kurt")
-- > # slow 3 (vowel "a e o")
slow :: Pattern Time -> Pattern a -> Pattern a
slow = patternify _slow
_slow :: Time -> Pattern a -> Pattern a
_slow 0 _ = silence
_slow r p = _fast (1 / r) p
_fastGap :: Time -> Pattern a -> Pattern a
_fastGap 0 _ = empty
_fastGap r p =
splitQueries
$ withResultArc
( \(Arc s e) ->
Arc
(sam s + ((s - sam s) / r'))
(sam s + ((e - sam s) / r'))
)
$ p {query = f}
where
r' = max r 1
-- zero width queries of the next sam should return zero in this case..
f st@(State a _)
| start a' == nextSam (start a) = []
| otherwise = query p st {arc = a'}
where
mungeQuery t = sam t + min 1 (r' * cyclePos t)
a' = (\(Arc s e) -> Arc (mungeQuery s) (mungeQuery e)) a
-- | Shifts a pattern back in time by the given amount, expressed in cycles.
--
-- This will skip to the fourth cycle:
--
-- > do
-- > resetCycles
-- > d1 $ rotL 4 $ seqP
-- > [ (0, 12, sound "bd bd*2")
-- > , (4, 12, sound "hh*2 [sn cp] cp future*4")
-- > , (8, 12, sound (samples "arpy*8" (run 16)))
-- > ]
--
-- Useful when building and testing out longer sequences.
rotL :: Time -> Pattern a -> Pattern a
rotL t p = withResultTime (subtract t) $ withQueryTime (+ t) p
-- | Shifts a pattern forward in time by the given amount, expressed in cycles.
-- Opposite of 'rotL'.
rotR :: Time -> Pattern a -> Pattern a
rotR t = rotL (negate t)
-- | @rev p@ returns @p@ with the event positions in each cycle reversed (or
-- mirrored).
--
-- For example rev @"1 [~ 2] ~ 3"@ is equivalent to rev @"3 ~ [2 ~] 1"@.
--
-- Note that @rev@ reverses on a cycle-by-cycle basis. This means that @rev (slow
-- 2 "1 2 3 4")@ would actually result in @(slow 2 "2 1 4 3")@. This is because the
-- @slow 2@ makes the repeating pattern last two cycles, each of which is reversed
-- independently.
--
-- In practice rev is generally used with conditionals, for example with every:
--
-- > d1 $ every 3 rev $ n "0 1 [~ 2] 3" # sound "arpy"
--
-- or 'jux':
--
-- > d1 $ jux rev $ n (iter 4 "0 1 [~ 2] 3") # sound "arpy"
rev :: Pattern a -> Pattern a
rev p =
keepMeta p $
splitQueries $
p
{ query = \st ->
map makeWholeAbsolute $
mapParts (mirrorArc (midCycle $ arc st)) $
map
makeWholeRelative
( query
p
st
{ arc = mirrorArc (midCycle $ arc st) (arc st)
}
)
}
where
makeWholeRelative :: Event a -> Event a
makeWholeRelative e@Event {whole = Nothing} = e
makeWholeRelative (Event c (Just (Arc s e)) p'@(Arc s' e') v) =
Event c (Just $ Arc (s' - s) (e - e')) p' v
makeWholeAbsolute :: Event a -> Event a
makeWholeAbsolute e@Event {whole = Nothing} = e
makeWholeAbsolute (Event c (Just (Arc s e)) p'@(Arc s' e') v) =
Event c (Just $ Arc (s' - e) (e' + s)) p' v
midCycle :: Arc -> Time
midCycle (Arc s _) = sam s + 0.5
mapParts :: (Arc -> Arc) -> [Event a] -> [Event a]
mapParts f es = (\(Event c w p' v) -> Event c w (f p') v) <$> es
-- Returns the `mirror image' of a 'Arc' around the given point in time
mirrorArc :: Time -> Arc -> Arc
mirrorArc mid' (Arc s e) = Arc (mid' - (e - mid')) (mid' + (mid' - s))
-- | Mark values in the first pattern which match with at least one
-- value in the second pattern.
matchManyToOne :: (b -> a -> Bool) -> Pattern a -> Pattern b -> Pattern (Bool, b)
matchManyToOne f pa pb = pa {query = q, pureValue = Nothing}
where
q st = map match $ query pb st
where
match ex@(Event xContext xWhole xPart x) =
Event (combineContexts $ xContext : map context as') xWhole xPart (any (f x . value) as', x)
where
as' = as $ start $ wholeOrPart ex
as s = query pa $ fQuery s
fQuery s = st {arc = Arc s s}
-- ** Event filters
-- | Remove events from patterns that to not meet the given test
filterValues :: (a -> Bool) -> Pattern a -> Pattern a
filterValues f p = p {query = filter (f . value) . query p}
-- | Turns a pattern of 'Maybe' values into a pattern of values,
-- dropping the events of 'Nothing'.
filterJust :: Pattern (Maybe a) -> Pattern a
filterJust p = fromJust <$> filterValues isJust p
filterWhen :: (Time -> Bool) -> Pattern a -> Pattern a
filterWhen test p = p {query = filter (test . wholeStart) . query p}
filterOnsets :: Pattern a -> Pattern a
filterOnsets p = p {query = filter (\e -> eventPartStart e == wholeStart e) . query (filterDigital p)}
filterEvents :: (Event a -> Bool) -> Pattern a -> Pattern a
filterEvents f p = p {query = filter f . query p}
filterDigital :: Pattern a -> Pattern a
filterDigital = filterEvents isDigital
filterAnalog :: Pattern a -> Pattern a
filterAnalog = filterEvents isAnalog
playFor :: Time -> Time -> Pattern a -> Pattern a
playFor s e pat = pattern $ \st -> maybe [] (\a -> query pat (st {arc = a})) $ subArc (Arc s e) (arc st)
-- | Splits a pattern into a list containing the given 'n' number of
-- patterns. Each one plays every 'n'th cycle, successfully offset by
-- a cycle.
separateCycles :: Int -> Pattern a -> [Pattern a]
separateCycles n pat = map (\i -> skip $ rotL (toRational i) pat) [0 .. n - 1]
where
n' = toRational n
skip pat' = splitQueries $ withResultStart (\t -> (sam t / n') + cyclePos t) $ withQueryStart (\t -> (sam t * n') + cyclePos t) $ pat'
-- ** Temporal parameter helpers
patternify :: (t1 -> t2 -> Pattern a) -> Pattern t1 -> t2 -> Pattern a
patternify f (Pattern _ _ (Just a)) b = f a b
patternify f pa p = innerJoin $ (`f` p) <$> pa
-- versions that preserve the tactus
patternify' :: (b -> Pattern c -> Pattern a) -> Pattern b -> Pattern c -> Pattern a
patternify' f pa p = (patternify f pa p) {tactus = tactus p}
patternify2 :: (a -> b -> c -> Pattern d) -> Pattern a -> Pattern b -> c -> Pattern d
patternify2 f (Pattern _ _ (Just a)) (Pattern _ _ (Just b)) c = f a b c
patternify2 f a b p = innerJoin $ (\x y -> f x y p) <$> a <*> b
patternify2' :: (a -> b -> Pattern c -> Pattern d) -> Pattern a -> Pattern b -> Pattern c -> Pattern d
patternify2' f a b p = patternify2 f a b p
patternify3 :: (a -> b -> c -> Pattern d -> Pattern e) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d -> Pattern e)
patternify3 f (Pattern _ _ (Just a)) (Pattern _ _ (Just b)) (Pattern _ _ (Just c)) d = f a b c d
patternify3 f a b c p = innerJoin $ (\x y z -> f x y z p) <$> a <*> b <*> c
patternify3' :: (a -> b -> c -> Pattern d -> Pattern e) -> (Pattern a -> Pattern b -> Pattern c -> Pattern d -> Pattern e)
patternify3' f a b c p = keepTactus p $ patternify3 f a b c p
patternifySqueeze :: (a -> Pattern b -> Pattern c) -> (Pattern a -> Pattern b -> Pattern c)
patternifySqueeze f tv p = squeezeJoin $ (`f` p) <$> tv
-- ** Context
combineContexts :: [Context] -> Context
combineContexts = Context . concatMap contextPosition
setContext :: Context -> Pattern a -> Pattern a
setContext c pat = keepMeta pat $ withEvents (map (\e -> e {context = c})) pat
withContext :: (Context -> Context) -> Pattern a -> Pattern a
withContext f pat = keepMeta pat $ withEvents (map (\e -> e {context = f $ context e})) pat
-- A hack to add to manipulate source code to add calls to
-- 'deltaContext' around strings, so events from mininotation know
-- where they are within a whole tidal pattern
deltaMini :: String -> String
deltaMini = outside 0 0
where
outside :: Int -> Int -> String -> String
outside _ _ [] = []
outside column line ('"' : xs) =
"(deltaContext "
++ show column
++ " "
++ show line
++ " \""
++ inside (column + 1) line xs
outside _ line ('\n' : xs) = '\n' : outside 0 (line + 1) xs
outside column line (x : xs) = x : outside (column + 1) line xs
inside :: Int -> Int -> String -> String
inside _ _ [] = []
inside column line ('"' : xs) = '"' : ')' : outside (column + 1) line xs
inside _ line ('\n' : xs) = '\n' : inside 0 (line + 1) xs
inside column line (x : xs) = x : inside (column + 1) line xs
class Stringy a where
deltaContext :: Int -> Int -> a -> a
instance Stringy (Pattern a) where
deltaContext :: Int -> Int -> Pattern a -> Pattern a
deltaContext column line pat = withEvents (map (\e -> e {context = f $ context e})) pat
where
f :: Context -> Context
f (Context xs) = Context $ map (\((bx, by), (ex, ey)) -> ((bx + column, by + line), (ex + column, ey + line))) xs
-- deltaContext on an actual (non overloaded) string is a no-op
instance Stringy String where
deltaContext :: Int -> Int -> String -> String
deltaContext _ _ = id
-- ** Events
-- | Some context for an event, currently just position within sourcecode
data Context = Context {contextPosition :: [((Int, Int), (Int, Int))]}
deriving (Eq, Ord, Generic)
instance NFData Context
-- | An event is a value that's active during a timespan. If a whole
-- is present, the part should be equal to or fit inside it.
data EventF a b = Event
{ context :: Context,
whole :: Maybe a,
part :: a,
value :: b
}
deriving (Eq, Ord, Functor, Generic)
instance (NFData a, NFData b) => NFData (EventF a b)
type Event a = EventF (ArcF Time) a
-- * Event utilities
isAnalog :: Event a -> Bool
isAnalog (Event {whole = Nothing}) = True
isAnalog _ = False
isDigital :: Event a -> Bool
isDigital = not . isAnalog
-- | `True` if an `Event`'s starts is within given `Arc`
onsetIn :: Arc -> Event a -> Bool
onsetIn a e = isIn a (wholeStart e)
-- | Returns a list of events, with any adjacent parts of the same whole combined
defragParts :: (Eq a) => [Event a] -> [Event a]
defragParts [] = []
defragParts [e] = [e]
defragParts (e : es)
| isJust i = defraged : defragParts (delete e' es)
| otherwise = e : defragParts es
where
i = findIndex (isAdjacent e) es
e' = es !! fromJust i
defraged = Event (context e) (whole e) u (value e)
u = hull (part e) (part e')
-- | Returns 'True' if the two given events are adjacent parts of the same whole
isAdjacent :: (Eq a) => Event a -> Event a -> Bool
isAdjacent e e' =
(whole e == whole e')
&& (value e == value e')
&& ( (stop (part e) == start (part e'))
|| (stop (part e') == start (part e))
)
wholeOrPart :: Event a -> Arc
wholeOrPart (Event {whole = Just a}) = a
wholeOrPart e = part e
-- | Get the onset of an event's 'whole'
wholeStart :: Event a -> Time
wholeStart = start . wholeOrPart
-- | Get the offset of an event's 'whole'
wholeStop :: Event a -> Time
wholeStop = stop . wholeOrPart
-- | Get the onset of an event's 'whole'
eventPartStart :: Event a -> Time
eventPartStart = start . part
-- | Get the offset of an event's 'part'
eventPartStop :: Event a -> Time
eventPartStop = stop . part
-- | Get the timespan of an event's 'part'
eventPart :: Event a -> Arc
eventPart = part
eventValue :: Event a -> a
eventValue = value
eventHasOnset :: Event a -> Bool
eventHasOnset e
| isAnalog e = False
| otherwise = start (fromJust $ whole e) == start (part e)
-- TODO - Is this used anywhere? Just tests, it seems
-- TODO - support 'context' field
toEvent :: (((Time, Time), (Time, Time)), a) -> Event a
toEvent (((ws, we), (ps, pe)), v) = Event (Context []) (Just $ Arc ws we) (Arc ps pe) v
-- Resolves higher order VState values to plain values, by passing through (and changing) state
resolveState :: ValueMap -> [Event ValueMap] -> (ValueMap, [Event ValueMap])
resolveState sMap [] = (sMap, [])
resolveState sMap (e : es) = (sMap'', (e {value = v'}) : es')
where
f sm (VState v) = v sm
f sm v = (sm, v)
(sMap', v')
| eventHasOnset e = Map.mapAccum f sMap (value e) -- pass state through VState functions
| otherwise = (sMap, Map.filter notVState $ value e) -- filter out VState values without onsets
(sMap'', es') = resolveState sMap' es
notVState (VState _) = False
notVState _ = True
-- ** Values
-- | Polymorphic values
data Value
= VS {svalue :: String}
| VF {fvalue :: Double}
| VN {nvalue :: Note}
| VR {rvalue :: Rational}
| VI {ivalue :: Int}
| VB {bvalue :: Bool}
| VX {xvalue :: [Word8]} -- Used for OSC 'blobs'
| VPattern {pvalue :: Pattern Value}
| VList {lvalue :: [Value]}
| VState {statevalue :: ValueMap -> (ValueMap, Value)}
deriving (Typeable, Generic)
class Valuable a where
toValue :: a -> Value
instance NFData Value
type ValueMap = Map.Map String Value
-- | Note is Double, but with a different parser
newtype Note = Note {unNote :: Double}
deriving (Typeable, Data, Generic, Eq, Ord, Enum, Num, Fractional, Floating, Real, RealFrac)
instance NFData Note
instance Show Note where
show :: Note -> String
show n = (show . unNote $ n) ++ "n (" ++ pitchClass ++ octave ++ ")"
where
pitchClass = pcs !! mod noteInt 12
octave = show $ div noteInt 12 + 5
noteInt = round . unNote $ n
pcs = ["c", "cs", "d", "ds", "e", "f", "fs", "g", "gs", "a", "as", "b"]
instance Valuable String where
toValue :: String -> Value
toValue a = VS a
instance Valuable Double where
toValue :: Double -> Value
toValue a = VF a
instance Valuable Rational where
toValue :: Rational -> Value
toValue a = VR a
instance Valuable Int where
toValue :: Int -> Value
toValue a = VI a
instance Valuable Bool where
toValue :: Bool -> Value
toValue a = VB a
instance Valuable Note where
toValue :: Note -> Value
toValue a = VN a
instance Valuable [Word8] where
toValue :: [Word8] -> Value
toValue a = VX a
instance Valuable [Value] where
toValue :: [Value] -> Value
toValue a = VList a
instance Eq Value where
(==) :: Value -> Value -> Bool
(VS x) == (VS y) = x == y
(VB x) == (VB y) = x == y
(VF x) == (VF y) = x == y
(VI x) == (VI y) = x == y
(VN x) == (VN y) = x == y
(VR x) == (VR y) = x == y
(VX x) == (VX y) = x == y
(VF x) == (VI y) = x == fromIntegral y
(VI y) == (VF x) = x == fromIntegral y
(VF x) == (VR y) = toRational x == y
(VR y) == (VF x) = toRational x == y
(VI x) == (VR y) = toRational x == y
(VR y) == (VI x) = toRational x == y
_ == _ = False
instance Ord Value where
compare :: Value -> Value -> Ordering
compare (VS x) (VS y) = compare x y
compare (VB x) (VB y) = compare x y
compare (VF x) (VF y) = compare x y
compare (VN x) (VN y) = compare (unNote x) (unNote y)
compare (VI x) (VI y) = compare x y
compare (VR x) (VR y) = compare x y
compare (VX x) (VX y) = compare x y
compare (VS _) _ = LT
compare _ (VS _) = GT
compare (VB _) _ = LT
compare _ (VB _) = GT
compare (VX _) _ = LT
compare _ (VX _) = GT
compare (VF x) (VI y) = compare x (fromIntegral y)
compare (VI x) (VF y) = compare (fromIntegral x) y
compare (VR x) (VI y) = compare x (fromIntegral y)
compare (VI x) (VR y) = compare (fromIntegral x) y
compare (VF x) (VR y) = compare x (fromRational y)
compare (VR x) (VF y) = compare (fromRational x) y
compare (VN x) (VI y) = compare x (fromIntegral y)
compare (VI x) (VN y) = compare (fromIntegral x) y
compare (VN x) (VR y) = compare (unNote x) (fromRational y)
compare (VR x) (VN y) = compare (fromRational x) (unNote y)
compare (VF x) (VN y) = compare x (unNote y)
compare (VN x) (VF y) = compare (unNote x) y
-- you can't really compare patterns, state or lists..
compare (VPattern _) (VPattern _) = EQ
compare (VPattern _) _ = GT
compare _ (VPattern _) = LT
compare (VState _) (VState _) = EQ
compare (VState _) _ = GT
compare _ (VState _) = LT
compare (VList _) (VList _) = EQ
compare (VList _) _ = GT
compare _ (VList _) = LT
-- | General utilities..
-- | Apply one of three functions to a Value, depending on its type
applyFIS :: (Double -> Double) -> (Int -> Int) -> (String -> String) -> Value -> Value
applyFIS f _ _ (VF f') = VF (f f')
applyFIS f _ _ (VN (Note f')) = VN (Note $ f f')
applyFIS _ f _ (VI i) = VI (f i)
applyFIS _ _ f (VS s) = VS (f s)
applyFIS f f' f'' (VState x) = VState (fmap (applyFIS f f' f'') . x)
applyFIS _ _ _ v = v
-- | Apply one of two functions to a pair of Values, depending on their types (int
-- or float; strings and rationals are ignored)
fNum2 :: (Int -> Int -> Int) -> (Double -> Double -> Double) -> Value -> Value -> Value
fNum2 fInt _ (VI a) (VI b) = VI (fInt a b)
fNum2 _ fFloat (VF a) (VF b) = VF (fFloat a b)
fNum2 _ fFloat (VN (Note a)) (VN (Note b)) = VN (Note $ fFloat a b)
fNum2 _ fFloat (VF a) (VN (Note b)) = VN (Note $ fFloat a b)
fNum2 _ fFloat (VN (Note a)) (VF b) = VN (Note $ fFloat a b)
fNum2 _ fFloat (VI a) (VF b) = VF (fFloat (fromIntegral a) b)
fNum2 _ fFloat (VF a) (VI b) = VF (fFloat a (fromIntegral b))
fNum2 fInt fFloat (VState a) b = VState (fmap (\a' -> fNum2 fInt fFloat a' b) . a)
fNum2 fInt fFloat a (VState b) = VState (fmap (fNum2 fInt fFloat a) . b)
fNum2 _ _ x _ = x
getI :: Value -> Maybe Int
getI (VI i) = Just i
getI (VR x) = Just $ floor x
getI (VF x) = Just $ floor x
getI _ = Nothing
getF :: Value -> Maybe Double
getF (VF f) = Just f
getF (VR x) = Just $ fromRational x
getF (VI x) = Just $ fromIntegral x
getF _ = Nothing
getN :: Value -> Maybe Note
getN (VN n) = Just n
getN (VF f) = Just $ Note f
getN (VR x) = Just $ Note $ fromRational x
getN (VI x) = Just $ Note $ fromIntegral x
getN _ = Nothing
getS :: Value -> Maybe String
getS (VS s) = Just s
getS _ = Nothing
getB :: Value -> Maybe Bool
getB (VB b) = Just b
getB _ = Nothing
getR :: Value -> Maybe Rational
getR (VR r) = Just r
getR (VF x) = Just $ toRational x
getR (VI x) = Just $ toRational x
getR _ = Nothing
getBlob :: Value -> Maybe [Word8]
getBlob (VX xs) = Just xs
getBlob _ = Nothing
getList :: Value -> Maybe [Value]
getList (VList vs) = Just vs
getList _ = Nothing
valueToPattern :: Value -> Pattern Value
valueToPattern (VPattern pat) = pat
valueToPattern v = pure v
--- functions relating to chords/patterns of lists
sameDur :: Event a -> Event a -> Bool
sameDur e1 e2 = (whole e1 == whole e2) && (part e1 == part e2)
groupEventsBy :: (Eq a) => (Event a -> Event a -> Bool) -> [Event a] -> [[Event a]]
groupEventsBy _ [] = []
groupEventsBy f (e : es) = eqs : groupEventsBy f (es \\ eqs)
where
eqs = e : [x | x <- es, f e x]
-- assumes that all events in the list have same whole/part
collectEvent :: [Event a] -> Maybe (Event [a])
collectEvent [] = Nothing
collectEvent l@(e : _) = Just $ e {context = con, value = vs}
where
con = unionC $ map context l
vs = map value l
unionC [] = Context []
unionC ((Context is) : cs) = Context (is ++ iss)
where
Context iss = unionC cs
collectEventsBy :: (Eq a) => (Event a -> Event a -> Bool) -> [Event a] -> [Event [a]]
collectEventsBy f es = remNo $ map collectEvent (groupEventsBy f es)
where
remNo [] = []
remNo (Nothing : cs) = remNo cs
remNo ((Just c) : cs) = c : remNo cs
-- | collects all events satisfying the same constraint into a list
collectBy :: (Eq a) => (Event a -> Event a -> Bool) -> Pattern a -> Pattern [a]
collectBy f = withEvents (collectEventsBy f)
-- | collects all events occuring at the exact same time into a list
collect :: (Eq a) => Pattern a -> Pattern [a]
collect = collectBy sameDur
uncollectEvent :: Event [a] -> [Event a]
uncollectEvent e = [e {value = value e !! i, context = resolveContext i (context e)} | i <- [0 .. length (value e) - 1]]
where
resolveContext i (Context xs) = if length xs <= i then Context [] else Context [xs !! i]
uncollectEvents :: [Event [a]] -> [Event a]
uncollectEvents = concatMap uncollectEvent
-- | merges all values in a list into one pattern by stacking the values
uncollect :: Pattern [a] -> Pattern a
uncollect = withEvents uncollectEvents