{-# LANGUAGE FlexibleInstances #-} {- Core.hs - For functions judged to be 'core' to tidal functionality. 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 . -} module Sound.Tidal.Core where import Data.Fixed (mod') import Data.List (sortOn) import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe, mapMaybe) import Sound.Tidal.Pattern import Prelude hiding ((*>), (<*)) -- ** Elemental patterns -- | Takes a function of time to values, and turns it into a 'Pattern'. -- Useful for creating continuous patterns such as 'sine' or 'perlin'. -- -- For example, 'saw' is defined as -- -- > saw = sig $ \t -> mod' (fromRational t) 1 sig :: (Time -> a) -> Pattern a sig f = pattern q where q (State (Arc s e) _) | s > e = [] | otherwise = [Event (Context []) Nothing (Arc s e) (f s)] -- | @sine@ - unipolar sinewave. A pattern of continuous values following a -- sinewave with frequency of one cycle, and amplitude from 0 to 1. sine :: (Fractional a) => Pattern a sine = sig $ \t -> (sin_rat ((pi :: Double) * 2 * fromRational t) + 1) / 2 where sin_rat = fromRational . toRational . sin -- | @sine2@ - bipolar sinewave. A pattern of continuous values following a -- sinewave with frequency of one cycle, and amplitude from -1 to 1. sine2 :: (Fractional a) => Pattern a sine2 = sig $ \t -> sin_rat ((pi :: Double) * 2 * fromRational t) where sin_rat = fromRational . toRational . sin -- | @cosine@ - unipolar cosine wave. A pattern of continuous values -- following a cosine with frequency of one cycle, and amplitude from -- 0 to 1. Equivalent to @0.25 ~> sine@. cosine :: (Fractional a) => Pattern a cosine = 0.25 `rotR` sine -- | @cosine2@ - bipolar cosine wave. A pattern of continuous values -- following a cosine with frequency of one cycle, and amplitude from -- -1 to 1. Equivalent to @0.25 ~> sine2@. cosine2 :: (Fractional a) => Pattern a cosine2 = 0.25 `rotR` sine2 -- | @saw@ - unipolar ascending sawtooth wave. A pattern of continuous values -- following a sawtooth with frequency of one cycle, and amplitude from -- 0 to 1. saw :: (Fractional a, Real a) => Pattern a saw = sig $ \t -> mod' (fromRational t) 1 -- | @saw2@ - bipolar ascending sawtooth wave. A pattern of continuous values -- following a sawtooth with frequency of one cycle, and amplitude from -- -1 to 1. saw2 :: (Fractional a, Real a) => Pattern a saw2 = sig $ \t -> mod' (fromRational t) 1 * 2 - 1 -- | @isaw@ like @saw@, but a descending (inverse) sawtooth. isaw :: (Fractional a, Real a) => Pattern a isaw = (1 -) <$> saw -- | @isaw2@ like @saw2@, but a descending (inverse) sawtooth. isaw2 :: (Fractional a, Real a) => Pattern a isaw2 = (* (-1)) <$> saw2 -- | @tri@ - unipolar triangle wave. A pattern of continuous values -- following a triangle wave with frequency of one cycle, and amplitude from -- 0 to 1. tri :: (Fractional a, Real a) => Pattern a tri = fastAppend saw isaw -- | @tri2@ - bipolar triangle wave. A pattern of continuous values -- following a triangle wave with frequency of one cycle, and amplitude from -- -1 to 1. tri2 :: (Fractional a, Real a) => Pattern a tri2 = fastAppend saw2 isaw2 -- | @square@ - unipolar square wave. A pattern of continuous values -- following a square wave with frequency of one cycle, and amplitude from -- 0 to 1. -- | @square@ is like 'sine', for square waves. square :: (Fractional a) => Pattern a square = sig $ \t -> fromIntegral ((floor $ mod' (fromRational t :: Double) 1 * 2) :: Integer) -- | @square2@ - bipolar square wave. A pattern of continuous values -- following a square wave with frequency of one cycle, and amplitude from -- -1 to 1. square2 :: (Fractional a) => Pattern a square2 = sig $ \t -> fromIntegral (floor (mod' (fromRational t :: Double) 1 * 2) * 2 - 1 :: Integer) -- | @envL@ is a 'Pattern' of continuous 'Double' values, representing -- a linear interpolation between 0 and 1 during the first cycle, then -- staying constant at 1 for all following cycles. Possibly only -- useful if you're using something like the retrig function defined -- in tidal.el. envL :: Pattern Double envL = sig $ \t -> max 0 $ min (fromRational t) 1 -- | like 'envL' but reversed. envLR :: Pattern Double envLR = (1 -) <$> envL -- | 'Equal power' version of 'env', for gain-based transitions envEq :: Pattern Double envEq = sig $ \t -> sqrt (sin (pi / 2 * max 0 (min (fromRational (1 - t)) 1))) -- | Equal power reversed envEqR :: Pattern Double envEqR = sig $ \t -> sqrt (cos (pi / 2 * max 0 (min (fromRational (1 - t)) 1))) -- ** Pattern algebra -- class for types that support a left-biased union class Unionable a where union :: a -> a -> a -- default union is just to take the left hand side.. instance Unionable a where union = const instance {-# OVERLAPPING #-} Unionable ValueMap where union = Map.union (|+|) :: (Applicative a, Num b) => a b -> a b -> a b a |+| b = (+) <$> a <*> b (|+) :: (Num a) => Pattern a -> Pattern a -> Pattern a a |+ b = (+) <$> a <* b (+|) :: (Num a) => Pattern a -> Pattern a -> Pattern a a +| b = (+) <$> a *> b (||+) :: (Num a) => Pattern a -> Pattern a -> Pattern a a ||+ b = (+) <$> a <<* b (|++|) :: (Applicative a) => a String -> a String -> a String a |++| b = (++) <$> a <*> b (|++) :: Pattern String -> Pattern String -> Pattern String a |++ b = (++) <$> a <* b (++|) :: Pattern String -> Pattern String -> Pattern String a ++| b = (++) <$> a *> b (||++) :: Pattern String -> Pattern String -> Pattern String a ||++ b = (++) <$> a <<* b (|/|) :: (Applicative a, Fractional b) => a b -> a b -> a b a |/| b = (/) <$> a <*> b (|/) :: (Fractional a) => Pattern a -> Pattern a -> Pattern a a |/ b = (/) <$> a <* b (/|) :: (Fractional a) => Pattern a -> Pattern a -> Pattern a a /| b = (/) <$> a *> b (||/) :: (Fractional a) => Pattern a -> Pattern a -> Pattern a a ||/ b = (/) <$> a <<* b (|*|) :: (Applicative a, Num b) => a b -> a b -> a b a |*| b = (*) <$> a <*> b (|*) :: (Num a) => Pattern a -> Pattern a -> Pattern a a |* b = (*) <$> a <* b (*|) :: (Num a) => Pattern a -> Pattern a -> Pattern a a *| b = (*) <$> a *> b (||*) :: (Num a) => Pattern a -> Pattern a -> Pattern a a ||* b = (*) <$> a <<* b (|-|) :: (Applicative a, Num b) => a b -> a b -> a b a |-| b = (-) <$> a <*> b (|-) :: (Num a) => Pattern a -> Pattern a -> Pattern a a |- b = (-) <$> a <* b (-|) :: (Num a) => Pattern a -> Pattern a -> Pattern a a -| b = (-) <$> a *> b (||-) :: (Num a) => Pattern a -> Pattern a -> Pattern a a ||- b = (-) <$> a <<* b (|%|) :: (Applicative a, Moddable b) => a b -> a b -> a b a |%| b = gmod <$> a <*> b (|%) :: (Moddable a) => Pattern a -> Pattern a -> Pattern a a |% b = gmod <$> a <* b (%|) :: (Moddable a) => Pattern a -> Pattern a -> Pattern a a %| b = gmod <$> a *> b (||%) :: (Moddable a) => Pattern a -> Pattern a -> Pattern a a ||% b = gmod <$> a <<* b (|**|) :: (Applicative a, Floating b) => a b -> a b -> a b a |**| b = (**) <$> a <*> b (|**) :: (Floating a) => Pattern a -> Pattern a -> Pattern a a |** b = (**) <$> a <* b (**|) :: (Floating a) => Pattern a -> Pattern a -> Pattern a a **| b = (**) <$> a *> b (||**) :: (Floating a) => Pattern a -> Pattern a -> Pattern a a ||** b = (**) <$> a <<* b (|>|) :: (Applicative a, Unionable b) => a b -> a b -> a b a |>| b = flip union <$> a <*> b (|>) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a |> b = flip union <$> a <* b (>|) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a >| b = flip union <$> a *> b (||>) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a ||> b = flip union <$> a <<* b (|<|) :: (Applicative a, Unionable b) => a b -> a b -> a b a |<| b = union <$> a <*> b (|<) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a |< b = union <$> a <* b (<|) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a <| b = union <$> a *> b (||<) :: (Unionable a) => Pattern a -> Pattern a -> Pattern a a ||< b = union <$> a <<* b -- Backward compatibility - structure from left, values from right. (#) :: (Unionable b) => Pattern b -> Pattern b -> Pattern b (#) = (|>) -- ** Constructing patterns -- | Turns a list of values into a pattern, playing one of them per cycle. -- The following are equivalent: -- -- > d1 $ n (fromList [0, 1, 2]) # s "superpiano" -- > d1 $ n "<0 1 2>" # s "superpiano" fromList :: [a] -> Pattern a fromList = cat . map pure -- | Turns a list of values into a pattern, playing /all/ of them per cycle. -- The following are equivalent: -- -- > d1 $ n (fastFromList [0, 1, 2]) # s "superpiano" -- > d1 $ n "[0 1 2]" # s "superpiano" fastFromList :: [a] -> Pattern a fastFromList = fastcat . map pure -- | A synonym for 'fastFromList' listToPat :: [a] -> Pattern a listToPat = fastFromList -- | 'fromMaybes; is similar to 'fromList', but allows values to -- be optional using the 'Maybe' type, so that 'Nothing' results in -- gaps in the pattern. -- The following are equivalent: -- > d1 $ n (fromMaybes [Just 0, Nothing, Just 2]) # s "superpiano" -- > d1 $ n "0 ~ 2" # s "superpiano" fromMaybes :: [Maybe a] -> Pattern a fromMaybes = fastcat . map f where f Nothing = silence f (Just x) = pure x -- | A pattern of whole numbers from 0 to the given number, in a single cycle. -- Can be used used to @run@ through a folder of samples in order: -- -- > d1 $ n (run 8) # sound "amencutup" -- -- The first parameter to run can be given as a pattern: -- -- > d1 $ n (run "<4 8 4 6>") # sound "amencutup" run :: (Enum a, Num a, Real a) => Pattern a -> Pattern a run npat = setSteps (toRational <$> pureValue npat) $ npat >>= _run _run :: (Enum a, Num a) => a -> Pattern a _run n = fastFromList [0 .. n - 1] -- | Similar to 'run', but starts from @1@ for the first cycle, successively -- adds a number until it gets up to @n@. -- > d1 $ n (scan 8) # sound "amencutup" scan :: (Enum a, Num a) => Pattern a -> Pattern a scan = (>>= _scan) _scan :: (Enum a, Num a) => a -> Pattern a _scan n = slowcat $ map _run [1 .. n] -- ** Combining patterns -- | Alternate between cycles of the two given patterns -- > d1 $ append (sound "bd*2 sn") (sound "arpy jvbass*2") append :: Pattern a -> Pattern a -> Pattern a append a b = cat [a, b] -- | -- Like 'append', but for a list of patterns. Interlaces them, playing the -- first cycle from each in turn, then the second cycle from each, and so on. It -- concatenates a list of patterns into a new pattern; each pattern in the list -- will maintain its original duration. For example: -- -- > d1 $ cat [sound "bd*2 sn", sound "arpy jvbass*2"] -- > d1 $ cat [sound "bd*2 sn", sound "arpy jvbass*2", sound "drum*2"] -- > d1 $ cat [sound "bd*2 sn", sound "jvbass*3", sound "drum*2", sound "ht mt"] cat :: [Pattern a] -> Pattern a cat [] = silence cat (p : []) = p cat ps = pattern q where n = length ps q st = concatMap (f st) $ arcCyclesZW (arc st) f st a = query (withResultTime (+ offset) p) $ st {arc = Arc (subtract offset (start a)) (subtract offset (stop a))} where p = ps !! i cyc = (floor $ start a) :: Int i = cyc `mod` n offset = (fromIntegral $ cyc - ((cyc - i) `div` n)) :: Time -- | Alias for 'cat' slowCat :: [Pattern a] -> Pattern a slowCat = cat slowcat :: [Pattern a] -> Pattern a slowcat = slowCat -- | Alias for 'append' slowAppend :: Pattern a -> Pattern a -> Pattern a slowAppend = append slowappend :: Pattern a -> Pattern a -> Pattern a slowappend = append -- | Like 'append', but twice as fast -- > d1 $ fastAppend (sound "bd*2 sn") (sound "arpy jvbass*2") fastAppend :: Pattern a -> Pattern a -> Pattern a fastAppend a b = _fast 2 $ append a b fastappend :: Pattern a -> Pattern a -> Pattern a fastappend = fastAppend -- | The same as 'cat', but speeds up the result by the number of -- patterns there are, so the cycles from each are squashed to fit a -- single cycle. -- -- > d1 $ fastcat [sound "bd*2 sn", sound "arpy jvbass*2"] -- > d1 $ fastcat [sound "bd*2 sn", sound "arpy jvbass*2", sound "drum*2"] -- > d1 $ fastcat [sound "bd*2 sn", sound "jvbass*3", sound "drum*2", sound "ht mt"] fastCat :: [Pattern a] -> Pattern a fastCat [p] = p fastCat ps = setSteps t $ _fast (toTime $ length ps) $ cat ps where t = sum <$> mapM steps ps -- where t = fromMaybe (toRational $ length ps) $ ((* (toRational $ length ps)) . foldl1 lcmr) <$> (sequence $ map steps ps) -- | Alias for @fastCat@ fastcat :: [Pattern a] -> Pattern a fastcat = fastCat -- | Similar to @fastCat@, but each pattern is given a relative duration. -- You provide proportionate sizes of the patterns to each other for when they’re -- concatenated into one cycle. The larger the value in the list, the larger -- relative size the pattern takes in the final loop. If all values are equal -- then this is equivalent to fastcat (e.g. the following two code fragments are -- equivalent). -- -- > d1 $ fastcat [s "bd*4", s "hh27*8", s "superpiano" # n 0] -- -- > d1 $ timeCat [ (1, s "bd*4") -- > , (1, s "hh27*8") -- > , (1, s "superpiano" # n 0) -- > ] timeCat :: [(Time, Pattern a)] -> Pattern a timeCat [(_, p)] = p timeCat tps = setSteps (Just total) $ stack $ map (\(s, e, p) -> compressArc (Arc (s / total) (e / total)) p) $ arrange 0 $ filter (\(t, _) -> t > 0) $ tps where total = sum $ map fst tps arrange :: Time -> [(Time, Pattern a)] -> [(Time, Time, Pattern a)] arrange _ [] = [] arrange t ((t', p) : tps') = (t, t + t', p) : arrange (t + t') tps' -- | Alias for @timeCat@ timecat :: [(Time, Pattern a)] -> Pattern a timecat = timeCat -- | @overlay@ combines two 'Pattern's into a new pattern, so that their events -- are combined over time. For example, the following two lines are equivalent: -- -- > d1 $ sound (overlay "bd sn:2" "cp*3") -- > d1 $ sound "[bd sn:2, cp*3]" -- -- @overlay@ is equal to '<>', -- -- > (<>) :: Semigroup a => a -> a -> a -- -- which can thus be used as an infix operator equivalent of 'overlay': -- -- > d1 $ sound ("bd sn:2" <> "cp*3") overlay :: Pattern a -> Pattern a -> Pattern a overlay = (<>) -- | Serialises a pattern so there's only one event playing at any one -- time, making it /monophonic/. Events which start/end earlier are given priority. mono :: Pattern a -> Pattern a mono p = pattern $ \(State a cm) -> flatten $ query p (State a cm) where flatten :: [Event a] -> [Event a] flatten = mapMaybe constrainPart . truncateOverlaps . sortOn whole truncateOverlaps [] = [] truncateOverlaps (e : es) = e : truncateOverlaps (mapMaybe (snip e) es) -- TODO - decide what to do about analog events.. snip a b | start (wholeOrPart b) >= stop (wholeOrPart a) = Just b | stop (wholeOrPart b) <= stop (wholeOrPart a) = Nothing | otherwise = Just b {whole = Just $ Arc (stop $ wholeOrPart a) (stop $ wholeOrPart b)} constrainPart :: Event a -> Maybe (Event a) constrainPart e = do a <- subArc (wholeOrPart e) (part e) return $ e {part = a} -- | 'stack' combines a list of 'Pattern's into a new pattern, so that their -- events are combined over time, i.e., all of the patterns in the list are played -- simultaneously. -- -- > d1 $ stack [ -- > sound "bd bd*2", -- > sound "hh*2 [sn cp] cp future*4", -- > sound "arpy" +| n "0 .. 15" -- > ] -- -- This is particularly useful if you want to apply a function or synth control -- pattern to multiple patterns at once: -- -- > d1 $ whenmod 5 3 (striate 3) $ stack [ -- > sound "bd bd*2", -- > sound "hh*2 [sn cp] cp future*4", -- > sound "arpy" +| n "0 .. 15" -- > ] # speed "[[1 0.8], [1.5 2]*2]/3" stack :: [Pattern a] -> Pattern a stack pats = (foldr overlay silence pats) {steps = t} where t | null pats = Nothing | otherwise = foldl1 lcmr <$> mapM steps pats -- ** Manipulating time -- | Shifts a pattern back in time by the given amount, expressed in cycles (<~) :: Pattern Time -> Pattern a -> Pattern a (<~) = patternify' rotL -- | Shifts a pattern forward in time by the given amount, expressed in cycles (~>) :: Pattern Time -> Pattern a -> Pattern a (~>) = patternify' rotR -- | Slow down a pattern by the factors in the given time pattern, "squeezing" -- the pattern to fit the slot given in the time pattern. It is the slow analogue -- to 'fastSqueeze'. -- -- If the time pattern only has a single value in a cycle, @slowSqueeze@ becomes equivalent to slow. These are equivalent: -- -- > d1 $ slow "<2 4>" $ s "bd*8" -- > d1 $ slowSqueeze "<2 4>" $ s "bd*8" -- -- When the time pattern has multiple values, however, the behavior is a little -- different. Instead, a slowed version of the pattern will be made for each value -- in the time pattern, and they’re all combined together in a cycle according to -- the structure of the time pattern. For example, these are equivalent: -- -- > d1 $ slowSqueeze "2 4 8 16" $ s "bd*8" -- > d1 $ s "bd*4 bd*2 bd bd/2" -- -- as are these: -- -- > d1 $ slowSqueeze "2 4 [8 16]" $ s "bd*8" -- > d1 $ s "bd*4 bd*2 [bd bd/2]" slowSqueeze :: Pattern Time -> Pattern a -> Pattern a slowSqueeze = patternifySqueeze _slow -- | An alias for @slow@ sparsity :: Pattern Time -> Pattern a -> Pattern a sparsity = slow -- | Plays a portion of a pattern, specified by a time arc (start and end time). -- The new resulting pattern is played over the time period of the original pattern. -- -- > d1 $ zoom (0.25, 0.75) $ sound "bd*2 hh*3 [sn bd]*2 drum" -- -- In the pattern above, @zoom@ is used with an arc from 25% to 75%. It is -- equivalent to: -- -- > d1 $ sound "hh*3 [sn bd]*2" -- -- Here’s an example of it being used with a conditional: -- -- > d1 $ every 4 (zoom (0.25, 0.75)) $ sound "bd*2 hh*3 [sn bd]*2 drum" zoom :: (Time, Time) -> Pattern a -> Pattern a zoom (s, e) = zoomArc (Arc s e) zoompat :: Pattern Time -> Pattern Time -> Pattern a -> Pattern a zoompat = patternify2 $ curry zoom zoomArc :: Arc -> Pattern a -> Pattern a zoomArc (Arc s e) p | s >= e = nothing | otherwise = withSteps (* d) $ splitQueries $ withResultArc (mapCycle ((/ d) . subtract s)) $ withQueryArc (mapCycle ((+ s) . (* d))) p where d = e - s -- | @fastGap@ is similar to 'fast' but maintains its cyclic alignment, i.e., -- rather than playing the pattern multiple times, it instead leaves a gap in -- the remaining space of the cycle. For example, @fastGap 2 p@ would squash the -- events in pattern @p@ into the first half of each cycle (and the second halves -- would be empty). The factor should be at least 1. fastGap :: Pattern Time -> Pattern a -> Pattern a fastGap = patternify _fastGap -- | An alias for @fastGap@ densityGap :: Pattern Time -> Pattern a -> Pattern a densityGap = fastGap -- | -- @compress@ takes a pattern and squeezes it within the specified time span (i.e. -- the ‘arc’). The new resulting pattern is a sped up version of the original. -- -- > d1 $ compress (1/4, 3/4) $ s "[bd sn]!" -- -- In the above example, the pattern will play in an arc spanning from 25% to 75% -- of the duration of a cycle. It is equivalent to: -- -- > d1 $ s "~ [bd sn]! ~" -- -- Another example, where all events are different: -- -- > d1 $ compress (1/4, 3/4) $ n (run 4) # s "arpy" -- -- It differs from 'zoom' in that it preserves the original pattern but it speeds -- up its events so to match with the new time period. compress :: (Time, Time) -> Pattern a -> Pattern a compress (s, e) = compressArc (Arc s e) compressTo :: (Time, Time) -> Pattern a -> Pattern a compressTo (s, e) = compressArcTo (Arc s e) repeatCycles :: Pattern Int -> Pattern a -> Pattern a repeatCycles = patternify _repeatCycles _repeatCycles :: Int -> Pattern a -> Pattern a _repeatCycles n p = cat (replicate n p) fastRepeatCycles :: Int -> Pattern a -> Pattern a fastRepeatCycles n p = cat (replicate n p) -- | * Higher order functions -- | Functions which work on other functions (higher order functions) -- | @every n f p@ applies the function @f@ to @p@, but only affects -- every @n@ cycles. -- -- It takes three inputs: how often the function should be applied (e.g. 3 to -- apply it every 3 cycles), the function to be applied, and the pattern you are -- applying it to. For example: to reverse a pattern every three cycles (and for -- the other two play it normally) -- -- > d1 $ every 3 rev $ n "0 1 [~ 2] 3" # sound "arpy" -- -- Note that if the function you’re applying requires additional parameters -- itself (such as fast 2 to make a pattern twice as fast), then you’ll need to -- wrap it in parenthesis, like so: -- -- > d1 $ every 3 (fast 2) $ n "0 1 [~ 2] 3" # sound "arpy" -- -- Otherwise, the every function will think it is being passed too many parameters. every :: Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a every tp f p = innerJoin $ (\t -> _every t f p) <$> tp _every :: Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _every 0 _ p = p _every n f p = when ((== 0) . (`mod` n)) f p -- | @every' n o f p@ is like @every n f p@ but with an offset of @o@ cycles. -- -- For example, @every' 3 0 (fast 2)@ will speed up the cycle on cycles 0,3,6,… -- whereas @every' 3 1 (fast 2)@ will transform the pattern on cycles 1,4,7,…. -- -- With this in mind, setting the second argument of @every'@ to 0 gives the -- equivalent every function. For example, every 3 is equivalent to every' 3 0. -- -- The @every@ functions can be used to silence a full cycle or part of a cycle -- by using silent or mask "~". Mask provides additional flexibility to turn on/off -- individual steps. -- -- > d1 $ every 3 silent $ n "2 9 11 2" # s "hh27" -- > d1 $ every 3 (mask "~") $ n "2 9 10 2" # s "hh27" -- > d1 $ every 3 (mask "0 0 0 0") $ n "2 9 11 2" # s "hh27" every' :: Pattern Int -> Pattern Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a every' np op f p = do n <- np; o <- op; _every' n o f p _every' :: Int -> Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _every' n o = when ((== o) . (`mod` n)) -- | @foldEvery ns f p@ applies the function @f@ to @p@, and is applied for -- each cycle in @ns@. -- -- It is similar to chaining multiple @every@ functions together. It transforms -- a pattern with a function, once per any of the given number of cycles. If a -- particular cycle is the start of more than one of the given cycle periods, then -- it it applied more than once. -- -- > d1 $ foldEvery [5,3] (|+ n 1) $ s "moog" # legato 1 -- -- The first moog samples are tuned to C2, C3 and C4. Note how on cycles that are -- multiples of 3 or 5 the pitch is an octave higher, and on multiples of 15 the -- pitch is two octaves higher, as the transformation is applied twice. foldEvery :: [Int] -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a foldEvery ns f p = foldr (`_every` f) p ns -- | -- The given pattern transformation is applied only @when@ the given test function -- returns @True@. The test function will be called with the current cycle as -- a number. -- -- > d1 $ when (elem '4' . show) -- > (striate 4) -- > $ sound "hh hc" -- -- The above will only apply @striate 4@ to the pattern if the current -- cycle number contains the number 4. So the fourth cycle will be -- striated and the fourteenth and so on. Expect lots of striates after -- cycle number 399. when :: (Int -> Bool) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a when test f p = splitQueries $ p {query = apply} where apply st | test (floor $ start $ arc st) = query (f p) st | otherwise = query p st -- | Like 'when', but works on continuous time values rather than cycle numbers. -- The following will apply @# speed 2@ only when the remainder of the current -- @Time@ divided by 2 is less than 0.5: -- -- > d1 $ whenT ((< 0.5) . (flip Data.Fixed.mod' 2)) -- > (# speed 2) -- > $ sound "hh(4,8) hc(3,8)" whenT :: (Time -> Bool) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a whenT test f p = splitQueries $ p {query = apply} where apply st | test (start $ arc st) = query (f p) st | otherwise = query p st _getP_ :: (Value -> Maybe a) -> Pattern Value -> Pattern a _getP_ f pat = filterJust $ f <$> pat _getP :: a -> (Value -> Maybe a) -> Pattern Value -> Pattern a _getP d f pat = fromMaybe d . f <$> pat _cX :: a -> (Value -> Maybe a) -> String -> Pattern a _cX d f s = pattern $ \(State a m) -> queryArc (maybe (pure d) (_getP d f . valueToPattern) $ Map.lookup s m) a _cX_ :: (Value -> Maybe a) -> String -> Pattern a _cX_ f s = pattern $ \(State a m) -> queryArc (maybe silence (_getP_ f . valueToPattern) $ Map.lookup s m) a cF :: Double -> String -> Pattern Double cF d = _cX d getF cF_ :: String -> Pattern Double cF_ = _cX_ getF cF0 :: String -> Pattern Double cF0 = _cX 0 getF cN :: Note -> String -> Pattern Note cN d = _cX d getN cN_ :: String -> Pattern Note cN_ = _cX_ getN cN0 :: String -> Pattern Note cN0 = _cX (Note 0) getN cI :: Int -> String -> Pattern Int cI d = _cX d getI cI_ :: String -> Pattern Int cI_ = _cX_ getI cI0 :: String -> Pattern Int cI0 = _cX 0 getI cB :: Bool -> String -> Pattern Bool cB d = _cX d getB cB_ :: String -> Pattern Bool cB_ = _cX_ getB cB0 :: String -> Pattern Bool cB0 = _cX False getB cR :: Rational -> String -> Pattern Rational cR d = _cX d getR cR_ :: String -> Pattern Rational cR_ = _cX_ getR cR0 :: String -> Pattern Rational cR0 = _cX 0 getR cT :: Time -> String -> Pattern Time cT = cR cT0 :: String -> Pattern Time cT0 = cR0 cT_ :: String -> Pattern Time cT_ = cR_ cS :: String -> String -> Pattern String cS d = _cX d getS cS_ :: String -> Pattern String cS_ = _cX_ getS cS0 :: String -> Pattern String cS0 = _cX "" getS -- Default controller inputs (for MIDI) in0 :: Pattern Double in0 = cF 0 "0" in1 :: Pattern Double in1 = cF 0 "1" in2 :: Pattern Double in2 = cF 0 "2" in3 :: Pattern Double in3 = cF 0 "3" in4 :: Pattern Double in4 = cF 0 "4" in5 :: Pattern Double in5 = cF 0 "5" in6 :: Pattern Double in6 = cF 0 "6" in7 :: Pattern Double in7 = cF 0 "7" in8 :: Pattern Double in8 = cF 0 "8" in9 :: Pattern Double in9 = cF 0 "9" in10 :: Pattern Double in10 = cF 0 "10" in11 :: Pattern Double in11 = cF 0 "11" in12 :: Pattern Double in12 = cF 0 "12" in13 :: Pattern Double in13 = cF 0 "13" in14 :: Pattern Double in14 = cF 0 "14" in15 :: Pattern Double in15 = cF 0 "15" in16 :: Pattern Double in16 = cF 0 "16" in17 :: Pattern Double in17 = cF 0 "17" in18 :: Pattern Double in18 = cF 0 "18" in19 :: Pattern Double in19 = cF 0 "19" in20 :: Pattern Double in20 = cF 0 "20" in21 :: Pattern Double in21 = cF 0 "21" in22 :: Pattern Double in22 = cF 0 "22" in23 :: Pattern Double in23 = cF 0 "23" in24 :: Pattern Double in24 = cF 0 "24" in25 :: Pattern Double in25 = cF 0 "25" in26 :: Pattern Double in26 = cF 0 "26" in27 :: Pattern Double in27 = cF 0 "27" in28 :: Pattern Double in28 = cF 0 "28" in29 :: Pattern Double in29 = cF 0 "29" in30 :: Pattern Double in30 = cF 0 "30" in31 :: Pattern Double in31 = cF 0 "31" in32 :: Pattern Double in32 = cF 0 "32" in33 :: Pattern Double in33 = cF 0 "33" in34 :: Pattern Double in34 = cF 0 "34" in35 :: Pattern Double in35 = cF 0 "35" in36 :: Pattern Double in36 = cF 0 "36" in37 :: Pattern Double in37 = cF 0 "37" in38 :: Pattern Double in38 = cF 0 "38" in39 :: Pattern Double in39 = cF 0 "39" in40 :: Pattern Double in40 = cF 0 "40" in41 :: Pattern Double in41 = cF 0 "41" in42 :: Pattern Double in42 = cF 0 "42" in43 :: Pattern Double in43 = cF 0 "43" in44 :: Pattern Double in44 = cF 0 "44" in45 :: Pattern Double in45 = cF 0 "45" in46 :: Pattern Double in46 = cF 0 "46" in47 :: Pattern Double in47 = cF 0 "47" in48 :: Pattern Double in48 = cF 0 "48" in49 :: Pattern Double in49 = cF 0 "49" in50 :: Pattern Double in50 = cF 0 "50" in51 :: Pattern Double in51 = cF 0 "51" in52 :: Pattern Double in52 = cF 0 "52" in53 :: Pattern Double in53 = cF 0 "53" in54 :: Pattern Double in54 = cF 0 "54" in55 :: Pattern Double in55 = cF 0 "55" in56 :: Pattern Double in56 = cF 0 "56" in57 :: Pattern Double in57 = cF 0 "57" in58 :: Pattern Double in58 = cF 0 "58" in59 :: Pattern Double in59 = cF 0 "59" in60 :: Pattern Double in60 = cF 0 "60" in61 :: Pattern Double in61 = cF 0 "61" in62 :: Pattern Double in62 = cF 0 "62" in63 :: Pattern Double in63 = cF 0 "63" in64 :: Pattern Double in64 = cF 0 "64" in65 :: Pattern Double in65 = cF 0 "65" in66 :: Pattern Double in66 = cF 0 "66" in67 :: Pattern Double in67 = cF 0 "67" in68 :: Pattern Double in68 = cF 0 "68" in69 :: Pattern Double in69 = cF 0 "69" in70 :: Pattern Double in70 = cF 0 "70" in71 :: Pattern Double in71 = cF 0 "71" in72 :: Pattern Double in72 = cF 0 "72" in73 :: Pattern Double in73 = cF 0 "73" in74 :: Pattern Double in74 = cF 0 "74" in75 :: Pattern Double in75 = cF 0 "75" in76 :: Pattern Double in76 = cF 0 "76" in77 :: Pattern Double in77 = cF 0 "77" in78 :: Pattern Double in78 = cF 0 "78" in79 :: Pattern Double in79 = cF 0 "79" in80 :: Pattern Double in80 = cF 0 "80" in81 :: Pattern Double in81 = cF 0 "81" in82 :: Pattern Double in82 = cF 0 "82" in83 :: Pattern Double in83 = cF 0 "83" in84 :: Pattern Double in84 = cF 0 "84" in85 :: Pattern Double in85 = cF 0 "85" in86 :: Pattern Double in86 = cF 0 "86" in87 :: Pattern Double in87 = cF 0 "87" in88 :: Pattern Double in88 = cF 0 "88" in89 :: Pattern Double in89 = cF 0 "89" in90 :: Pattern Double in90 = cF 0 "90" in91 :: Pattern Double in91 = cF 0 "91" in92 :: Pattern Double in92 = cF 0 "92" in93 :: Pattern Double in93 = cF 0 "93" in94 :: Pattern Double in94 = cF 0 "94" in95 :: Pattern Double in95 = cF 0 "95" in96 :: Pattern Double in96 = cF 0 "96" in97 :: Pattern Double in97 = cF 0 "97" in98 :: Pattern Double in98 = cF 0 "98" in99 :: Pattern Double in99 = cF 0 "99" in100 :: Pattern Double in100 = cF 0 "100" in101 :: Pattern Double in101 = cF 0 "101" in102 :: Pattern Double in102 = cF 0 "102" in103 :: Pattern Double in103 = cF 0 "103" in104 :: Pattern Double in104 = cF 0 "104" in105 :: Pattern Double in105 = cF 0 "105" in106 :: Pattern Double in106 = cF 0 "106" in107 :: Pattern Double in107 = cF 0 "107" in108 :: Pattern Double in108 = cF 0 "108" in109 :: Pattern Double in109 = cF 0 "109" in110 :: Pattern Double in110 = cF 0 "110" in111 :: Pattern Double in111 = cF 0 "111" in112 :: Pattern Double in112 = cF 0 "112" in113 :: Pattern Double in113 = cF 0 "113" in114 :: Pattern Double in114 = cF 0 "114" in115 :: Pattern Double in115 = cF 0 "115" in116 :: Pattern Double in116 = cF 0 "116" in117 :: Pattern Double in117 = cF 0 "117" in118 :: Pattern Double in118 = cF 0 "118" in119 :: Pattern Double in119 = cF 0 "119" in120 :: Pattern Double in120 = cF 0 "120" in121 :: Pattern Double in121 = cF 0 "121" in122 :: Pattern Double in122 = cF 0 "122" in123 :: Pattern Double in123 = cF 0 "123" in124 :: Pattern Double in124 = cF 0 "124" in125 :: Pattern Double in125 = cF 0 "125" in126 :: Pattern Double in126 = cF 0 "126" in127 :: Pattern Double in127 = cF 0 "127"