{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings, FlexibleContexts, BangPatterns #-}
module Sound.Tidal.Control where
import Prelude hiding ((<*), (*>))
import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe, isJust, fromJust)
import Data.Ratio
import Sound.Tidal.Pattern
import Sound.Tidal.Core
import Sound.Tidal.UI
import qualified Sound.Tidal.Params as P
import Sound.Tidal.Utils
spin :: Pattern Int -> ControlPattern -> ControlPattern
spin = tParam _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 :: Pattern Int -> ControlPattern -> ControlPattern
chop = tParam _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 = withEvents (concatMap chopEvent)
where
chopEvent :: Event ControlMap -> [Event ControlMap]
chopEvent (Event c (Just w) p' v) = map (chomp c v (length $ chopArc w n)) $ arcs w p'
chopEvent _ = []
arcs w' p' = numberedArcs p' $ chopArc w' n
numberedArcs :: Arc -> [Arc] -> [(Int, (Arc, Arc))]
numberedArcs p' as = map ((fromJust <$>) <$>) $ filter (isJust . snd . snd) $ enumerate $ map (\a -> (a, subArc p' a)) as
chomp :: Context -> ControlMap -> Int -> (Int, (Arc, Arc)) -> Event ControlMap
chomp c v n' (i, (w,p')) = Event c (Just w) p' (Map.insert "begin" (VF b') $ Map.insert "end" (VF e') v)
where b = fromMaybe 0 $ do v' <- Map.lookup "begin" v
getF v'
e = fromMaybe 1 $ do v' <- Map.lookup "end" v
getF v'
d = e-b
b' = ((fromIntegral i/fromIntegral n') * d) + b
e' = ((fromIntegral (i+1) / fromIntegral n') * d) + b
striate :: Pattern Int -> ControlPattern -> ControlPattern
striate = tParam _striate
_striate :: Int -> ControlPattern -> ControlPattern
_striate 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) -> ControlMap -> ControlMap
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'
striateBy :: Pattern Int -> Pattern Double -> ControlPattern -> ControlPattern
striateBy = tParam2 _striateBy
striate' :: Pattern Int -> Pattern Double -> ControlPattern -> ControlPattern
striate' = striateBy
_striateBy :: Int -> Double -> ControlPattern -> ControlPattern
_striateBy n f p = fastcat $ map (offset . fromIntegral) [0 .. n-1]
where offset i = p # P.begin (pure (slot * i) :: Pattern Double) # P.end (pure ((slot * i) + f) :: Pattern Double)
slot = (1 - f) / fromIntegral n
gap :: Pattern Int -> ControlPattern -> ControlPattern
gap = tParam _gap
_gap :: Int -> ControlPattern -> ControlPattern
_gap n p = _fast (toRational n) (cat [pure 1, silence]) |>| _chop n p
weave :: Time -> ControlPattern -> [ControlPattern] -> ControlPattern
weave t p ps = weave' t p (map (#) ps)
weaveWith :: Time -> Pattern a -> [Pattern a -> Pattern a] -> Pattern a
weaveWith t p fs | l == 0 = silence
| otherwise = _slow t $ stack $ map (\(i, f) -> (fromIntegral i % l) `rotL` _fast t (f (_slow t p))) (zip [0 :: Int ..] fs)
where l = fromIntegral $ length fs
weave' :: Time -> Pattern a -> [Pattern a -> Pattern a] -> Pattern a
weave' = weaveWith
interlace :: ControlPattern -> ControlPattern -> ControlPattern
interlace a b = weave 16 (P.shape (sine * 0.9)) [a, b]
slice :: Pattern Int -> Pattern Int -> ControlPattern -> ControlPattern
slice pN pI p = P.begin b # P.end e # p
where b = div' <$> pI <* pN
e = (\i n -> div' i n + div' 1 n) <$> pI <* pN
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 :: Pattern Int -> ControlPattern -> ControlPattern
randslice = tParam $ \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 = ev {value = Map.insert "speed" (VF d) (value ev)}
where d = sz / (fromRational $ (wholeStop ev) - (wholeStart ev))
sz = 1/(fromIntegral bits)
splice :: Pattern Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)
splice bitpat ipat pat = innerJoin $ (\bits -> _splice bits ipat pat) <$> bitpat
loopAt :: Pattern Time -> ControlPattern -> ControlPattern
loopAt n p = slow n p |* P.speed (fromRational <$> (1/n)) # P.unit (pure "c")
hurry :: Pattern Rational -> ControlPattern -> ControlPattern
hurry !x = (|* P.speed (fromRational <$> x)) . fast x
smash :: Pattern Int -> [Pattern Time] -> ControlPattern -> Pattern ControlMap
smash n xs p = slowcat $ map (`slow` p') xs
where p' = striate n p
smash' :: Int -> [Pattern Time] -> ControlPattern -> Pattern ControlMap
smash' n xs p = slowcat $ map (`slow` p') xs
where p' = _chop n p
stut :: Pattern Integer -> Pattern Double -> Pattern Rational -> ControlPattern -> ControlPattern
stut = tParam3 _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 -)
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
stut' :: Pattern Int -> Pattern Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
stut' = stutWith
sec :: Fractional a => Pattern a -> Pattern a
sec p = (realToFrac <$> cF 1 "_cps") *| p
msec :: Fractional a => Pattern a -> Pattern a
msec p = ((realToFrac . (/1000)) <$> cF 1 "_cps") *| p
_trigger :: Show a => Bool -> a -> Pattern b -> Pattern b
_trigger quant k pat = pat {query = q}
where q st = query ((offset st) ~> pat) st
f | quant = (fromIntegral :: Int -> Rational) . round
| otherwise = id
offset st = fromMaybe (pure 0) $ do p <- Map.lookup ctrl (controls st)
return $ ((f . fromMaybe 0 . getR) <$> p)
ctrl = "_t_" ++ show k
trigger :: Show a => a -> Pattern b -> Pattern b
trigger = _trigger False
qtrigger :: Show a => a -> Pattern b -> Pattern b
qtrigger = _trigger True
qt :: Show a => a -> Pattern b -> Pattern b
qt = qtrigger
reset :: Show a => a -> Pattern b -> Pattern b
reset k pat = pat {query = q}
where q st = query ((offset st) ~> (when (<=0) (const silence) pat)) st
f = (fromIntegral :: Int -> Rational) . floor
offset st = fromMaybe (pure 0) $ do p <- Map.lookup ctrl (controls st)
return $ ((f . fromMaybe 0 . getR) <$> p)
ctrl = "_t_" ++ show k
splat :: Pattern Int -> ControlPattern -> ControlPattern -> ControlPattern
splat slices epat pat = (chop slices pat) # bite 1 (const 0 <$> pat) epat