packages feed

tidal-core-1.10.0: src/Sound/Tidal/Control.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}

module Sound.Tidal.Control where

{-
    Control.hs - Functions which concern control patterns, which are
    patterns of hashmaps, used for synth control values.

    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/>.
-}

import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe)
import Data.Ratio ((%))
import Sound.Tidal.Core
  ( cF,
    cat,
    fastcat,
    overlay,
    sine,
    slowcat,
    stack,
    (#),
    (*|),
    (|*),
    (|>|),
  )
import qualified Sound.Tidal.Params as P
import Sound.Tidal.Pattern
import Sound.Tidal.Pattern.Types (patternTimeID)
import Sound.Tidal.UI (bite, _irand)
import Prelude hiding ((*>), (<*))

-- | `spin` will "spin" and layer up a pattern the given number of times,
-- with each successive layer offset in time by an additional @1/n@ of a cycle,
-- and panned by an additional @1/n@. The result is a pattern that seems to spin
-- around. This function work well on multichannel systems.
--
-- > d1 $ slow 3
-- >    $ spin 4
-- >    $ sound "drum*3 tabla:4 [arpy:2 ~ arpy] [can:2 can:3]"
spin :: Pattern Int -> ControlPattern -> ControlPattern
spin = patternify _spin

_spin :: Int -> ControlPattern -> ControlPattern
_spin copies p =
  stack $
    map
      ( \i ->
          let offset = toInteger i % toInteger copies
           in offset
                `rotL` p
                # P.pan (pure $ fromRational offset)
      )
      [0 .. (copies - 1)]

-- | `chop` granularises every sample in place as it is played, turning a
--  pattern of samples into a pattern of sample parts. Can be used to explore
--  granular synthesis.
--
--  Use an integer value to specify how many granules each sample is chopped into:
--
--  > d1 $ chop 16 $ sound "arpy arp feel*4 arpy*4"
--
--  Different values of @chop@ can yield very different results, depending on the
--  samples used:
--
--  > d1 $ chop 16 $ sound (samples "arpy*8" (run 16))
--  > d1 $ chop 32 $ sound (samples "arpy*8" (run 16))
--  > d1 $ chop 256 $ sound "bd*4 [sn cp] [hh future]*2 [cp feel]"
--
--  You can also use @chop@ (or 'striate') with very long samples to cut them into short
--  chunks and pattern those chunks. The following cuts a sample into 32 parts, and
--  plays it over 8 cycles:
--
--  > d1 $ loopAt 8 $ chop 32 $ sound "bev"
--
--  The 'loopAt' takes care of changing the speed of sample playback so that the
--  sample fits in the given number of cycles perfectly. As a result, in the above
--  the granules line up perfectly, so you can’t really hear that the sample has
--  been cut into bits. Again, this becomes more apparent when you do further
--  manipulations of the pattern, for example 'rev' to reverse the order of the cut
--  up bits:
--
--  > d1 $ loopAt 8 $ rev $ chop 32 $ sound "bev"
chop :: Pattern Int -> ControlPattern -> ControlPattern
chop = patternify _chop

chopArc :: Arc -> Int -> [Arc]
chopArc (Arc s e) n = map (\i -> Arc (s + (e - s) * (fromIntegral i / fromIntegral n)) (s + (e - s) * (fromIntegral (i + 1) / fromIntegral n))) [0 .. n - 1]

_chop :: Int -> ControlPattern -> ControlPattern
_chop n pat = keepSteps (withSteps (* toRational n) pat) $ squeezeJoin $ f <$> pat
  where
    f v = fastcat $ map (pure . rangemap v) slices
    rangemap v (b, e) = Map.union (fromMaybe (makeMap (b, e)) $ merge v (b, e)) v
    merge :: ValueMap -> (Double, Double) -> Maybe ValueMap
    merge v (b, e) = do
      b' <- Map.lookup "begin" v >>= getF
      e' <- Map.lookup "end" v >>= getF
      let d = e' - b'
      return $ makeMap (b' + b * d, b' + e * d)
    makeMap (b, e) = Map.fromList [("begin", VF b), ("end", VF $ e)]
    slices = map (\i -> (frac i, frac $ i + 1)) [0 .. n - 1]
    frac i = fromIntegral i / fromIntegral n

-- | Striate is a kind of granulator, cutting samples into bits in a similar to
-- chop, but the resulting bits are organised differently. For example:
--
-- > d1 $ striate 3 $ sound "ho ho:2 ho:3 hc"
--
-- This plays the loop the given number of times, but triggers progressive portions
-- of each sample. So in this case it plays the loop three times, the first
-- time playing the first third of each sample, then the second time playing the
-- second third of each sample, and lastly playing the last third of each sample.
-- Replacing @striate@ with 'chop' above, one can hear that the ''chop' version
-- plays the bits from each chopped-up sample in turn, while @striate@ "interlaces"
-- the cut up bits of samples together.
--
-- You can also use @striate@ with very long samples, to cut them into short
-- chunks and pattern those chunks. This is where things get towards granular
-- synthesis. The following cuts a sample into 128 parts, plays it over 8 cycles
-- and manipulates those parts by reversing and rotating the loops:
--
-- > d1 $  slow 8 $ striate 128 $ sound "bev"
striate :: Pattern Int -> ControlPattern -> ControlPattern
striate = patternify _striate

_striate :: Int -> ControlPattern -> ControlPattern
_striate n p = keepSteps (withSteps (* toRational n) p) $ fastcat $ map offset [0 .. n - 1]
  where
    offset i = mergePlayRange (fromIntegral i / fromIntegral n, fromIntegral (i + 1) / fromIntegral n) <$> p

mergePlayRange :: (Double, Double) -> ValueMap -> ValueMap
mergePlayRange (b, e) cm = Map.insert "begin" (VF ((b * d') + b')) $ Map.insert "end" (VF ((e * d') + b')) cm
  where
    b' = fromMaybe 0 $ Map.lookup "begin" cm >>= getF
    e' = fromMaybe 1 $ Map.lookup "end" cm >>= getF
    d' = e' - b'

-- |
-- The @striateBy@ function is a variant of `striate` with an extra
-- parameter which specifies the length of each part. The @striateBy@
-- function still scans across the sample over a single cycle, but if
-- each bit is longer, it creates a sort of stuttering effect. For
-- example the following will cut the @bev@ sample into 32 parts, but each
-- will be 1/16th of a sample long:
--
-- > d1 $ slow 32 $ striateBy 32 (1/16) $ sound "bev"
--
-- Note that `striate` and @striateBy@ use the `begin` and `end` parameters
-- internally. This means that you probably shouldn't also specify `begin` or
-- `end`.
striateBy :: Pattern Int -> Pattern Double -> ControlPattern -> ControlPattern
striateBy = patternify2 _striateBy

-- | DEPRECATED, use 'striateBy' instead.
striate' :: Pattern Int -> Pattern Double -> ControlPattern -> ControlPattern
striate' = striateBy

_striateBy :: Int -> Double -> ControlPattern -> ControlPattern
_striateBy n f p = keepSteps (withSteps (* toRational n) p) $ fastcat $ map (offset . fromIntegral) [0 .. n - 1]
  where
    offset i = mergePlayRange (slot * i, (slot * i) + f) <$> p
    slot = (1 - f) / fromIntegral (n - 1)

-- | `gap` is similar to `chop` in that it granualizes every sample in place as it is played,
-- but every other grain is silent. Use an integer value to specify how many granules
-- each sample is chopped into:
--
-- > d1 $ gap 8 $ sound "jvbass"
-- > d1 $ gap 16 $ sound "[jvbass drum:4]"
gap :: Pattern Int -> ControlPattern -> ControlPattern
gap = patternify _gap

_gap :: Int -> ControlPattern -> ControlPattern
_gap n p = _fast (toRational n) (cat [pure 1, silence]) |>| _chop n p

-- |
--  @weave@ applies one control pattern to a list of other control patterns, with
--  a successive time offset. It uses an `OscPattern` to apply the function at
--  different levels to each pattern, creating a weaving effect. For example:
--
--  > d1 $ weave 16 (pan sine)
--  >      [ sound "bd sn cp"
--  >      , sound "casio casio:1"
--  >      , sound "[jvbass*2 jvbass:2]/2"
--  >      , sound "hc*4"
--  >      ]
--
--  In the above, the @pan sine@ control pattern is slowed down by the given
--  number of cycles, in particular 16, and applied to all of the given sound
--  patterns. What makes this interesting is that the @pan@ control pattern is
--  successively offset for each of the given sound patterns; because the @pan@ is
--  closed down by 16 cycles, and there are four patterns, they are ‘spread out’,
--  i.e. with a gap of four cycles. For this reason, the four patterns seem to
--  chase after each other around the stereo field. Try listening on headphones to
--  hear this more clearly.
--
--  You can even have it the other way round, and have the effect parameters chasing
--  after each other around a sound parameter, like this:
--
--  > d1 $ weave 16 (sound "arpy" >| n (run 8))
--  >      [ vowel "a e i"
--  >      , vowel "i [i o] o u"
--  >      , vowel "[e o]/3 [i o u]/2"
--  >      , speed "1 2 3"
--  >      ]
weave :: Time -> ControlPattern -> [ControlPattern] -> ControlPattern
weave t p ps = weave' t p (map (#) ps)

-- |
--  @weaveWith@ is similar to the above, but weaves with a list of functions, rather
--  than a list of controls. For example:
--
--  > d1 $ weaveWith 3 (sound "bd [sn drum:2*2] bd*2 [sn drum:1]")
--  >      [ fast 2
--  >      , (# speed "0.5")
--  >      , chop 16
--  >      ]
weaveWith :: Time -> Pattern a -> [Pattern a -> Pattern a] -> Pattern a
weaveWith t p fs
  | l == 0 = silence
  | otherwise = _slow t $ stack $ zipWith (\i f -> (fromIntegral i % l) `rotL` _fast t (f (_slow t p))) [0 :: Int ..] fs
  where
    l = fromIntegral $ length fs

-- | An old alias for 'weaveWith'.
weave' :: Time -> Pattern a -> [Pattern a -> Pattern a] -> Pattern a
weave' = weaveWith

-- |
-- (A function that takes two ControlPatterns, and blends them together into
-- a new ControlPattern. An ControlPattern is basically a pattern of messages to
-- a synthesiser.)
--
-- Shifts between the two given patterns, using distortion.
--
-- Example:
--
-- > d1 $ interlace (sound  "bd sn kurt") (every 3 rev $ sound  "bd sn:2")
interlace :: ControlPattern -> ControlPattern -> ControlPattern
interlace a b = weave 16 (P.shape (sine * 0.9)) [a, b]

{-
{- | Just like `striate`, but also loops each sample chunk a number of times specified in the second argument.
The primed version is just like `striateBy`, where the loop count is the third argument. For example:

> d1 $ striateL' 3 0.125 4 $ sound "feel sn:2"

Like `striate`, these use the `begin` and `end` parameters internally, as well as the `loop` parameter for these versions.
-}
striateL :: Pattern Int -> Pattern Int -> ControlPattern -> ControlPattern
striateL = tParam2 _striateL

striateL' :: Pattern Int -> Pattern Double -> Pattern Int -> ControlPattern -> ControlPattern
striateL' = tParam3 _striateL'

_striateL :: Int -> Int -> ControlPattern -> ControlPattern
_striateL n l p = _striate n p # loop (pure $ fromIntegral l)
_striateL' n f l p = _striateBy n f p # loop (pure $ fromIntegral l)

en :: [(Int, Int)] -> Pattern String -> Pattern String
en ns p = stack $ map (\(i, (k, n)) -> _e k n (samples p (pure i))) $ enumerate ns

-}

-- | @slice@ is similar to 'chop' and 'striate', in that it’s used to slice
--  samples up into bits. The difference is that it allows you to rearrange those
--  bits as a pattern.
--
--  > d1 $ slice 8 "7 6 5 4 3 2 1 0"
--  >    $ sound "breaks165"
--  >    # legato 1
--
--  The above slices the sample into eight bits, and then plays them backwards,
--  equivalent of applying rev $ chop 8. Here’s a more complex example:
--
--  > d1 $ slice 8 "[<0*8 0*2> 3*4 2 4] [4 .. 7]"
--  >    $ sound "breaks165"
--  >    # legato 1
slice :: Pattern Int -> Pattern Int -> ControlPattern -> ControlPattern
slice pN pI p = P.begin b # P.end e # p
  where
    b = div' <$> pI <* pN
    e = b + pWidth
    pWidth = (\x -> 1.0 / fromIntegral x) <$> pN
    div' :: Int -> Int -> Double
    div' num den = fromIntegral (num `mod` den) / fromIntegral den

_slice :: Int -> Int -> ControlPattern -> ControlPattern
_slice n i p =
  p
    # P.begin (pure $ fromIntegral i / fromIntegral n)
    # P.end (pure $ fromIntegral (i + 1) / fromIntegral n)

-- |
--  @randslice@ chops the sample into the given number of pieces and then plays back
--  a random one each cycle:
--
--  > d1 $ randslice 32 $ sound "bev"
--
--  Use 'fast' to get more than one per cycle:
--
--  > d1 $ fast 4 $ randslice 32 $ sound "bev"
randslice :: Pattern Int -> ControlPattern -> ControlPattern
randslice = patternify $ \n p -> keepSteps (withSteps (* (toRational n)) $ p) $ innerJoin $ (\i -> _slice n i p) <$> _irand n

_splice :: Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)
_splice bits ipat pat = withEvent f (slice (pure bits) ipat pat) # P.unit (pure "c")
  where
    f ev = case Map.lookup "speed" (value ev) of
      (Just (VF s)) -> ev {value = Map.insert "speed" (VF $ d * s) (value ev)} -- if there is a speed parameter already present
      _ -> ev {value = Map.insert "speed" (VF d) (value ev)}
      where
        d = sz / fromRational (wholeStop ev - wholeStart ev)
        sz = 1 / fromIntegral bits

-- |
--  @splice@ is similar to 'slice', but the slices are automatically pitched up or down
--  to fit their ‘slot’.
--
--  > d1 $ splice 8 "[<0*8 0*2> 3*4 2 4] [4 .. 7]" $ sound "breaks165"
splice :: Pattern Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)
splice bitpat ipat pat = setStepsFrom bitpat $ innerJoin $ (\bits -> _splice bits ipat pat) <$> bitpat

-- |
--  @loopAt@ makes a sample fit the given number of cycles. Internally, it
--  works by setting the `unit` parameter to @"c"@, changing the playback
--  speed of the sample with the `speed` parameter, and setting setting
--  the `density` of the pattern to match.
--
--  > d1 $ loopAt 4 $ sound "breaks125"
--
--  It’s a good idea to use this in conjuction with 'chop', so the break is chopped
--  into pieces and you don’t have to wait for the whole sample to start/stop.
--
--  > d1 $ loopAt 4 $ chop 32 $ sound "breaks125"
--
--  Like all Tidal functions, you can mess about with this considerably. The below
--  example shows how you can supply a pattern of cycle counts to @loopAt@:
--
--  > d1 $ juxBy 0.6 (|* speed "2")
--  >    $ slowspread (loopAt) [4,6,2,3]
--  >    $ chop 12
--  >    $ sound "fm:14"
loopAt :: Pattern Time -> ControlPattern -> ControlPattern
loopAt n p = slow n p |* P.speed (fromRational <$> (1 / n)) # P.unit (pure "c")

-- |
--  @hurry@ is similiar to 'fast' in that it speeds up a pattern, but it also
--  increases the speed control by the same factor. So, if you’re triggering
--  samples, the sound gets higher in pitch. For example:
--
--  > d1 $ every 2 (hurry 2) $ sound "bd sn:2 ~ cp"
hurry :: Pattern Rational -> ControlPattern -> ControlPattern
hurry !x = (|* P.speed (fromRational <$> x)) . fast x

-- | @smash@ is a combination of `spread` and `striate` — it cuts the samples
-- into the given number of bits, and then cuts between playing the loop
-- at different speeds according to the values in the list. So this:
--
-- > d1 $ smash 3 [2,3,4] $ sound "ho ho:2 ho:3 hc"
--
-- is a bit like this:
--
-- > d1 $ spread (slow) [2,3,4] $ striate 3 $ sound "ho ho:2 ho:3 hc"
--
-- This is quite dancehall:
--
-- > d1 $ ( spread' slow "1%4 2 1 3"
-- >      $ spread (striate) [2,3,4,1]
-- >      $ sound "sn:2 sid:3 cp sid:4"
-- >      )
-- >    # speed "[1 2 1 1]/2"
smash :: Pattern Int -> [Pattern Time] -> ControlPattern -> Pattern ValueMap
smash n xs p = slowcat $ map (`slow` p') xs
  where
    p' = striate n p

-- | An altenative form of `smash`, which uses `chop` instead of `striate`.
--
--  Compare the following variations:
--
--  > d1 $ smash 6 [2,3,4] $ sound "ho ho:2 ho:3 hc"
--  > d1 $ smash' 6 [2,3,4] $ sound "ho ho:2 ho:3 hc"
--  > d1 $ smash 12 [2,3,4] $ s "bev*4"
--  > d1 $ smash' 12 [2,3,4] $ s "bev*4"
smash' :: Int -> [Pattern Time] -> ControlPattern -> ControlPattern
smash' n xs p = slowcat $ map (`slow` p') xs
  where
    p' = _chop n p

-- |
--    Applies a type of delay to a pattern.
--    It has three parameters, which could be called @depth@, @time@ and @feedback@.
--    @depth@ is and integer, and @time@ and @feedback@ are floating point numbers.
--
--    This adds a bit of echo:
--
--    > d1 $ echo 4 0.2 0.5 $ sound "bd sn"
--
--    The above results in 4 echos, each one 50% quieter than the last, with 1/5th of a cycle between them.
--
--    It is possible to reverse the echo:
--
--    > d1 $ echo 4 (-0.2) 0.5 $ sound "bd sn"
echo :: Pattern Integer -> Pattern Rational -> Pattern Double -> ControlPattern -> ControlPattern
echo = patternify3' _echo

_echo :: Integer -> Rational -> Double -> ControlPattern -> ControlPattern
_echo count time feedback p = _echoWith count time (|* P.gain (pure $ feedback)) p

-- |
--  @echoWith@ is similar to 'echo', but instead of just decreasing volume to
--  produce echoes, @echoWith@ applies a function each step and overlays the
--  result delayed by the given time.
--
--  > d1 $ echoWith 2 "1%3" (# vowel "{a e i o u}%2") $ sound "bd sn"
--
--  In this case there are two _overlays_ delayed by 1/3 of a cycle, where each
--  has the 'vowel' filter applied.
--
--  > d1 $ echoWith 4 (1/6) (|* speed "1.5") $ sound "arpy arpy:2"
--
--  In the above, three versions are put on top, with each step getting higher in
--  pitch as @|* speed "1.5"@ is successively applied.
echoWith :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
echoWith n t f p = innerJoin $ (\a b -> _echoWith a b f p) <$> n <* t

_echoWith :: (Num n, Ord n) => n -> Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
_echoWith count time f p
  | count <= 1 = p
  | otherwise = overlay (f (time `rotR` _echoWith (count - 1) time f p)) p

-- | DEPRECATED, use 'echo' instead
stut :: Pattern Integer -> Pattern Double -> Pattern Rational -> ControlPattern -> ControlPattern
stut = patternify3' _stut

_stut :: Integer -> Double -> Rational -> ControlPattern -> ControlPattern
_stut count feedback steptime p = stack (p : map (\x -> ((x % 1) * steptime) `rotR` (p |* P.gain (pure $ scalegain (fromIntegral x)))) [1 .. (count - 1)])
  where
    scalegain =
      (+ feedback) . (* (1 - feedback)) . (/ fromIntegral count) . (fromIntegral count -)

-- | DEPRECATED, use 'echoWith' instead
stutWith :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
stutWith n t f p = innerJoin $ (\a b -> _stutWith a b f p) <$> n <* t

_stutWith :: (Num n, Ord n) => n -> Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
_stutWith count steptime f p
  | count <= 1 = p
  | otherwise = overlay (f (steptime `rotR` _stutWith (count - 1) steptime f p)) p

-- | DEPRECATED, use 'echoWith' instead
stut' :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
stut' = stutWith

-- | Turns a pattern of seconds into a pattern of (rational) cycle durations
sec :: (Fractional a) => Pattern a -> Pattern a
sec p = (realToFrac <$> cF 1 "_cps") *| p

-- | Turns a pattern of milliseconds into a pattern of (rational)
-- cycle durations, according to the current cps.
msec :: (Fractional a) => Pattern a -> Pattern a
msec p = (realToFrac . (/ 1000) <$> cF 1 "_cps") *| p

-- | Align the start of a pattern with the time a pattern is evaluated,
-- rather than the global start time. Because of this, the pattern will
-- probably not be aligned to the pattern grid.
trigger :: Pattern a -> Pattern a
trigger = triggerWith id

-- | (Alias @__qt__@) Quantise trigger. Aligns the start of the pattern
-- with the next cycle boundary. For example, this pattern will fade in
-- starting with the next cycle after the pattern is evaluated:
--
-- > d1 $ qtrigger $ s "hh(5, 8)" # amp envL
--
-- Note that the pattern will start playing immediately. The /start/ of the
-- pattern aligns with the next cycle boundary, but events will play before
-- if the pattern has events at negative timestamps (which most loops do).
-- These events can be filtered out, for example:
--
-- > d1 $ qtrigger $ filterWhen (>= 0) $ s "hh(5, 8)"
--
-- Alternatively, you can use 'wait' to achieve the same result:
--
-- > wait 1 1 $ s "bd hh hh hh"
qtrigger :: Pattern a -> Pattern a
qtrigger = ctrigger

-- | Alias for 'qtrigger'.
qt :: Pattern a -> Pattern a
qt = qtrigger

-- | Ceiling trigger. Aligns the start of a pattern to the next cycle
-- boundary, just like 'qtrigger'.
ctrigger :: Pattern a -> Pattern a
ctrigger = triggerWith $ (fromIntegral :: Int -> Rational) . ceiling

-- | Rounded trigger. Aligns the start of a pattern to the nearest cycle
-- boundary, either next or previous.
rtrigger :: Pattern a -> Pattern a
rtrigger = triggerWith $ (fromIntegral :: Int -> Rational) . round

-- | Floor trigger. Aligns the start of a pattern to the previous cycle
-- boundary.
ftrigger :: Pattern a -> Pattern a
ftrigger = triggerWith $ (fromIntegral :: Int -> Rational) . floor

-- | (Alias @__mt__@) Mod trigger. Aligns the start of a pattern to the
--  next cycle boundary where the cycle is evenly divisible by a given
--  number. 'qtrigger' is equivalent to @mtrigger 1@.
--
--  In the following example, when activating the @d1@ pattern, it will start at the
--  same time as the next clap, even if it has to wait for 3 cycles. Once activated,
--  the @arpy@ sound will play on every cycle, just like any other pattern:
--
--  > do
--  >   resetCycles
--  >   d2 $ every 4 (# s "clap") $ s "bd"
--
--  > d1 $ mtrigger 4 $ filterWhen (>=0) $ s "arpy"
mtrigger :: Int -> Pattern a -> Pattern a
mtrigger n = triggerWith $ fromIntegral . nextMod
  where
    nextMod t = n * ceiling (t / (fromIntegral n))

-- | Alias for 'mtrigger'.
mt :: Int -> Pattern a -> Pattern a
mt = mtrigger

-- | This aligns the start of a pattern to some value relative to the
--  time the pattern is evaluated. The provided function maps the evaluation
--  time (on the global cycle clock) to a new time, and then @triggerWith@
--  aligns the pattern's start to the time that's returned.
--
--  This is a more flexible triggering function. In fact, all the other trigger
--  functions are defined based on @triggerWith@. For example, 'trigger' is just
--  @triggerWith id@.
--
--  In the next example, use @d1@ as a metronome, and play with different values
--  (from 0 to 1) on the @const@ expression. You’ll notice how the @clap@ is
--  displaced from the beginning of each cycle to the end, as the number increases:
--
--  > d1 $ s "bd hh!3"
--  >
--  > d2 $ triggerWith (const 0.1) $ s "clap"
--
--  This last example is equivalent to this:
--
--  > d2 $ rotR 0.1 $ s "clap"
triggerWith :: (Time -> Time) -> Pattern a -> Pattern a
triggerWith f pat = pat {query = q}
  where
    q st = query (rotR (offset st) pat) st
    offset st =
      fromMaybe 0 $
        f
          <$> (Map.lookup patternTimeID (controls st) >>= getR)

splat :: Pattern Int -> ControlPattern -> ControlPattern -> ControlPattern
splat slices epat pat = chop slices pat # bite 1 (const 0 <$> pat) epat