tidal 1.0.10 → 1.0.11
raw patch · 15 files changed
+340/−111 lines, 15 filesdep +bytestringdep +clock
Dependencies added: bytestring, clock
Files
- BootTidal.hs +57/−0
- CHANGELOG.md +19/−0
- src/Sound/Tidal/Carabiner.hs +66/−0
- src/Sound/Tidal/Context.hs +1/−0
- src/Sound/Tidal/Control.hs +27/−1
- src/Sound/Tidal/Params.hs +10/−1
- src/Sound/Tidal/Pattern.hs +62/−17
- src/Sound/Tidal/Stream.hs +16/−9
- src/Sound/Tidal/Tempo.hs +10/−5
- src/Sound/Tidal/Transition.hs +21/−12
- src/Sound/Tidal/UI.hs +31/−10
- src/Sound/Tidal/Version.hs +1/−1
- test/Sound/Tidal/UITest.hs +5/−0
- tidal.cabal +5/−1
- tidal.el +9/−54
+ BootTidal.hs view
@@ -0,0 +1,57 @@+:set -XOverloadedStrings+:set prompt ""+:set prompt-cont ""++import Sound.Tidal.Context++-- total latency = oLatency + cFrameTimespan+tidal <- startTidal (superdirtTarget {oLatency = 0.1, oAddress = "127.0.0.1", oPort = 57120}) (defaultConfig {cFrameTimespan = 1/20})++let p = streamReplace tidal+let hush = streamHush tidal+let list = streamList tidal+let mute = streamMute tidal+let unmute = streamUnmute tidal+let solo = streamSolo tidal+let unsolo = streamUnsolo tidal+let once = streamOnce tidal+let asap = once+let nudgeAll = streamNudgeAll tidal+let all = streamAll tidal+let resetCycles = streamResetCycles tidal+let setcps = asap . cps+let xfade i = transition tidal True (Sound.Tidal.Transition.xfadeIn 4) i+let xfadeIn i t = transition tidal True (Sound.Tidal.Transition.xfadeIn t) i+let histpan i t = transition tidal True (Sound.Tidal.Transition.histpan t) i+let wait i t = transition tidal True (Sound.Tidal.Transition.wait t) i+let waitT i f t = transition tidal True (Sound.Tidal.Transition.waitT f t) i+let jump i = transition tidal True (Sound.Tidal.Transition.jump) i+let jumpIn i t = transition tidal True (Sound.Tidal.Transition.jumpIn t) i+let jumpIn' i t = transition tidal True (Sound.Tidal.Transition.jumpIn' t) i+let jumpMod i t = transition tidal True (Sound.Tidal.Transition.jumpMod t) i+let mortal i lifespan release = transition tidal True (Sound.Tidal.Transition.mortal lifespan release) i+let interpolate i = transition tidal True (Sound.Tidal.Transition.interpolate) i+let interpolateIn i t = transition tidal True (Sound.Tidal.Transition.interpolateIn t) i+let clutch i = transition tidal True (Sound.Tidal.Transition.clutch) i+let clutchIn i t = transition tidal True (Sound.Tidal.Transition.clutchIn t) i+let anticipate i = transition tidal True (Sound.Tidal.Transition.anticipate) i+let anticipateIn i t = transition tidal True (Sound.Tidal.Transition.anticipateIn t) i+let forId i t = transition tidal False (Sound.Tidal.Transition.mortalOverlay t) i+let d1 = p 1+let d2 = p 2+let d3 = p 3+let d4 = p 4+let d5 = p 5+let d6 = p 6+let d7 = p 7+let d8 = p 8+let d9 = p 9+let d10 = p 10+let d11 = p 11+let d12 = p 12+let d13 = p 13+let d14 = p 14+let d15 = p 15+let d16 = p 16++:set prompt "tidal> "
CHANGELOG.md view
@@ -1,5 +1,24 @@ # TidalCycles log of changes +## 1.0.11 - as yet unnamed++2019-04-17 Alex McLean <alex@slab.org>+ * Add `bite` function for slicing patterns (rather than samples)+ * Tweak tidal.el to attempt to infer location of default BootTidal.hs+ * Skip time (forward or backward) if the reference clock jumps suddenly+ * Fix `fit` - @bgold-cosmos+ * Remove 'asap'+ * Add cB for boolean control input+ * `pickF` for choosing between functions with a pattern of integers+ * `select` for choosing between list of patterns with a floating point pattern+ * `squeeze` for choosing between list of patterns with a pattern of integers, where patterns are squeezed into the integer event duration+ * `splice` for choosing between slices of a pattern, where the slices are squeezed into event duration+ * Ord and Eq instances for value type @bgold-cosmos+ * `trigger` - support for resetting envelopes on evaluation+ * Support for rational event values+ * Tweak how `*>` and `<*` deal with analog patterns+ * Caribiner link bridge support+ ## 1.0.10 - This machine also kills fascists * Add exports to Sound.Tidal.Scales for `getScale` and `scaleTable`
+ src/Sound/Tidal/Carabiner.hs view
@@ -0,0 +1,66 @@+module Sound.Tidal.Carabiner where++import Network.Socket hiding (send, sendTo, recv, recvFrom)+import Network.Socket.ByteString (send, recv)+import qualified Data.ByteString.Char8 as B8+import Control.Concurrent (forkIO, threadDelay, takeMVar, putMVar)+import qualified Sound.Tidal.Stream as S+import Sound.Tidal.Tempo+import System.Clock+import Text.Read (readMaybe)+import Control.Monad (when, forever)+import Data.Maybe (isJust, fromJust)+import qualified Sound.OSC.FD as O++port = 17000++carabiner :: S.Stream -> Int -> Double -> IO Socket+carabiner tidal bpc latency = do sock <- client tidal bpc latency "127.0.0.1" 17000+ sendMsg sock "status\n"+ return sock++client :: S.Stream -> Int -> Double -> String -> Int -> IO Socket+client tidal bpc latency host port = withSocketsDo $+ do addrInfo <- getAddrInfo Nothing (Just host) (Just $ show port)+ let serverAddr = head addrInfo+ sock <- socket (addrFamily serverAddr) Stream defaultProtocol+ connect sock (addrAddress serverAddr)+ _ <- forkIO $ listener tidal bpc latency sock+ -- sendMsg sock "status\n"+ -- threadDelay 10000000+ return sock++listener :: S.Stream -> Int -> Double -> Socket -> IO ()+listener tidal bpc latency sock =+ forever $ do rMsg <- recv sock 1024+ let msg = B8.unpack rMsg+ (name:_:ws) = words msg+ pairs = pairs' ws+ pairs' (a:b:xs) = (a,b):(pairs' xs)+ pairs' _ = []+ act tidal bpc latency name pairs++act :: S.Stream -> Int -> Double -> String -> [(String, String)] -> IO ()+act tidal bpc latency "status" pairs+ = do let start = (lookup ":start" pairs >>= readMaybe) :: Maybe Integer+ bpm = (lookup ":bpm" pairs >>= readMaybe) :: Maybe Double+ beat = (lookup ":beat" pairs >>= readMaybe) :: Maybe Double+ when (and [isJust start, isJust bpm, isJust beat]) $ do+ nowM <- getTime Monotonic+ nowO <- O.time+ let m = (fromIntegral $ sec nowM) + ((fromIntegral $ nsec nowM)/1000000000)+ d = nowO - m+ start' = ((fromIntegral $ fromJust start) / 1000000)+ startO = start' + d+ cyc = toRational $ (fromJust beat) / (fromIntegral bpc)+ tempo <- takeMVar (S.sTempoMV tidal)+ let tempo' = tempo {atTime = startO + latency,+ atCycle = 0,+ cps = ((fromJust bpm) / 60) / (fromIntegral bpc)+ }+ putMVar (S.sTempoMV tidal) $ tempo'+act _ _ _ name _ = putStr $ "Unhandled thingie " ++ name++sendMsg :: Socket -> String -> IO ()+sendMsg sock msg = do send sock $ B8.pack msg+ return ()
src/Sound/Tidal/Context.hs view
@@ -4,6 +4,7 @@ import Data.Ratio as C +import Sound.Tidal.Carabiner as C import Sound.Tidal.Config as C import Sound.Tidal.Control as C import Sound.Tidal.Core as C
src/Sound/Tidal/Control.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings, FlexibleContexts #-} module Sound.Tidal.Control where @@ -367,10 +367,17 @@ msec :: Fractional a => Pattern a -> Pattern a msec p = ((realToFrac . (/1000)) <$> cF 1 "_cps") *| p +trigger :: Show a => a -> Pattern b -> Pattern b+trigger k pat = pat {query = q}+ where q st = query (rotR (offset st) pat) st+ offset st = fromMaybe 0 $ Map.lookup ctrl (controls st) >>= getR+ ctrl = "_t_" ++ show k+ cI :: String -> Pattern Int cI s = Pattern Analog $ \(State a m) -> maybe [] (f a) $ Map.lookup s m where f a (VI v) = [Event a a v] f a (VF v) = [Event a a (floor v)]+ f a (VR v) = [Event a a (floor v)] f a (VS v) = maybe [] (\v' -> [Event a a v']) (readMaybe v) _cX :: (Arc -> Value -> [Event a]) -> [a] -> String -> Pattern a@@ -380,6 +387,7 @@ _cF :: [Double] -> String -> Pattern Double _cF = _cX f where f a (VI v) = [Event a a (fromIntegral v)]+ f a (VR v) = [Event a a (fromRational v)] f a (VF v) = [Event a a v] f a (VS v) = maybe [] (\v' -> [Event a a v']) (readMaybe v) @@ -390,6 +398,15 @@ cF_ :: String -> Pattern Double cF_ = _cF [] +cB :: Bool -> String -> Pattern Bool+cB b = _cB [b]+_cB :: [Bool] -> String -> Pattern Bool+_cB = _cX f+ where f a (VI v) = [Event a a (v /= 0)]+ f a (VF v) = [Event a a (v >= 0.5)]+ f a (VR v) = [Event a a (v >= (1%2))]+ f a (VS v) = maybe [] (\v' -> [Event a a (v' == "t")]) (readMaybe v)+ cT :: Time -> String -> Pattern Time cT d = (toRational <$>) . cF (fromRational d) cT0 :: String -> Pattern Time@@ -409,6 +426,7 @@ _cS = _cX f where f a (VI v) = [Event a a (show v)] f a (VF v) = [Event a a (show v)]+ f a (VR v) = [Event a a (show v)] f a (VS v) = [Event a a v] cS :: String -> String -> Pattern String cS d = _cS [d]@@ -419,7 +437,9 @@ _cP ds s = innerJoin $ _cX f ds s where f a (VI v) = [Event a a (parseBP_E $ show v)] f a (VF v) = [Event a a (parseBP_E $ show v)]+ f a (VR v) = [Event a a (parseBP_E $ show v)] f a (VS v) = [Event a a (parseBP_E v)]+ cP :: (Enumerable a, Parseable a) => Pattern a -> String -> Pattern a cP d = _cP [d] cP_ :: (Enumerable a, Parseable a) => String -> Pattern a@@ -682,3 +702,9 @@ in126 = cF 0 "126" in127 :: Pattern Double in127 = cF 0 "127"++splice :: Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)+splice bits ipat pat = withEvent f (slice (pure bits) ipat pat) # P.unit "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)
src/Sound/Tidal/Params.hs view
@@ -58,6 +58,11 @@ accelerate :: Pattern Double -> ControlPattern accelerate = pF "accelerate" ++-- | Amplitude; like @gain@, but linear.+amp :: Pattern Double -> ControlPattern+amp = pF "amp"+ -- | a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample. Only takes effect if `release` is also specified. attack :: Pattern Double -> ControlPattern attack = pF "attack"@@ -160,9 +165,13 @@ -} end :: Pattern Double -> ControlPattern end = pF "end"--- | a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder.++-- | a pattern of numbers that specify volume. Values less than 1 make+-- the sound quieter. Values greater than 1 make the sound louder. For+-- the linear equivalent, see @amp@. gain :: Pattern Double -> ControlPattern gain = pF "gain"+ gate :: Pattern Double -> ControlPattern gate = pF "gate" hatgrain :: Pattern Double -> ControlPattern
src/Sound/Tidal/Pattern.hs view
@@ -88,6 +88,10 @@ timeToCycleArc :: Time -> Arc timeToCycleArc t = Arc (sam t) (sam t + 1) +-- | Shifts an arc to the equivalent one that starts during cycle zero+cycleArc :: Arc -> Arc+cycleArc (Arc s e) = Arc (cyclePos s) (cyclePos s + (e-s))+ -- | A list of cycle numbers which are included in the given arc cyclesInArc :: Integral a => Arc -> [a] cyclesInArc (Arc s e)@@ -219,9 +223,43 @@ data Value = VS { svalue :: String } | VF { fvalue :: Double }+ | VR { rvalue :: Rational } | VI { ivalue :: Int }- deriving (Eq,Ord,Typeable,Data)+ deriving (Typeable,Data) +instance Eq Value where+ (VS x) == (VS y) = x == y+ (VF x) == (VF y) = x == y+ (VI x) == (VI y) = x == y+ (VR x) == (VR 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++ (VS _) == _ = False+ _ == (VS _) = False+ +instance Ord Value where+ compare (VS x) (VS y) = compare x y+ compare (VF x) (VF y) = compare x y+ compare (VI x) (VI y) = compare x y+ compare (VR x) (VR y) = compare x y+ compare (VS _) _ = LT+ compare _ (VS _) = 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+ type ControlMap = Map.Map String Value type ControlPattern = Pattern ControlMap @@ -274,41 +312,42 @@ -- | Like <*>, but the structure only comes from the left (<*) :: Pattern (a -> b) -> Pattern a -> Pattern b-(<*) pf@(Pattern Digital _) px = Pattern Digital q+(<*) pf@(Pattern Analog _) px@(Pattern Analog _) = Pattern Analog q where q st = concatMap match $ query pf st- where+ where match (Event fWhole fPart f) = map (Event fWhole fPart . f . value) $- query px $ st {arc = xQuery fWhole}- xQuery (Arc s _) = pure s -- for discrete events, match with the onset+ query px st -- for continuous events, use the original query -(<*) pf px = Pattern Analog q+-- If one of the patterns is digital, treat both as digital.. (TODO - needs extra thought)+(<*) pf px = Pattern Digital q where q st = concatMap match $ query pf st- where+ where match (Event fWhole fPart f) = map (Event fWhole fPart . f . value) $- query px st -- for continuous events, use the original query+ query px $ st {arc = xQuery fWhole}+ xQuery (Arc s _) = pure s -- for discrete events, match with the onset -- | Like <*>, but the structure only comes from the right (*>) :: Pattern (a -> b) -> Pattern a -> Pattern b-(*>) pf px@(Pattern Digital _) = Pattern Digital q+(*>) pf@(Pattern Analog _) px@(Pattern Analog _) = Pattern Analog q where q st = concatMap match $ query px st- where+ where match (Event xWhole xPart x) = map (\e -> Event xWhole xPart (value e x)) $- query pf $ fQuery xWhole- fQuery (Arc s _) = st {arc = pure s} -- for discrete events, match with the onset+ query pf st -- for continuous events, use the original query -(*>) pf px = Pattern Analog q+(*>) pf px = Pattern Digital q where q st = concatMap match $ query px st- where+ where match (Event xWhole xPart x) = map (\e -> Event xWhole xPart (value e x)) $- query pf st -- for continuous events, use the original query+ query pf $ fQuery xWhole+ fQuery (Arc s _) = st {arc = pure s} -- for discrete events, match with the onset infixl 4 <*, *> @@ -380,7 +419,6 @@ do w' <- subArc oWhole iWhole p' <- subArc oPart iPart return (Event w' p' v)- cycleArc (Arc s e) = Arc (cyclePos s) (cyclePos s + (e-s)) noOv :: String -> a noOv meth = error $ meth ++ ": not supported for patterns"@@ -391,6 +429,7 @@ instance TolerantEq Value where (VS a) ~== (VS b) = a == b (VI a) ~== (VI b) = a == b+ (VR a) ~== (VR b) = a == b (VF a) ~== (VF b) = abs (a - b) < 0.000001 _ ~== _ = False @@ -508,6 +547,7 @@ show (VS s) = ('"':s) ++ "\"" show (VI i) = show i show (VF f) = show f ++ "f"+ show (VR r) = show r ++ "r" instance {-# OVERLAPPING #-} Show ControlMap where show m = intercalate ", " $ map (\(name, v) -> name ++ ": " ++ show v) $ Map.toList m@@ -626,9 +666,10 @@ applyFIS f _ _ (VF f') = VF $ f f' applyFIS _ f _ (VI i ) = VI $ f i applyFIS _ _ f (VS s ) = VS $ f s+applyFIS _ _ _ v = v -- | Apply one of two functions to a Value, depending on its type (int--- or float; strings are ignored)+-- 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@@ -647,6 +688,10 @@ getS :: Value -> Maybe String getS (VS s) = Just s getS _ = Nothing++getR :: Value -> Maybe Rational+getR (VR r) = Just r+getR _ = Nothing compressArc :: Arc -> Pattern a -> Pattern a compressArc (Arc s e) p | s > e = empty
src/Sound/Tidal/Stream.hs view
@@ -14,6 +14,7 @@ -- import qualified Data.Bifunctor as BF -- import qualified Data.Bool as B -- import qualified Data.Char as C+import System.IO (hPutStrLn, stderr) import qualified Sound.OSC.FD as O @@ -232,16 +233,24 @@ streamReplace :: Show a => Stream -> a -> ControlPattern -> IO () streamReplace s k pat = E.catch (do let x = queryArc pat (Arc 0 0)+ tempo <- readMVar $ sTempoMV s+ input <- takeMVar $ sInput s+ -- put change time in control input+ now <- O.time+ let cycle = T.timeToCycles tempo now+ putMVar (sInput s) $+ Map.insert ("_t_all") (VR cycle) $ Map.insert ("_t_" ++ show k) (VR cycle) input+ -- update the pattern itself pMap <- seq x $ takeMVar $ sPMapMV s let playState = updatePS $ Map.lookup (show k) pMap putMVar (sPMapMV s) $ Map.insert (show k) playState pMap calcOutput s return () )- (\(e :: E.SomeException) -> putStrLn $ "Error in pattern: " ++ show e+ (\(e :: E.SomeException) -> hPutStrLn stderr $ "Error in pattern: " ++ show e ) where updatePS (Just playState) = do playState {pattern = pat, history = pat:(history playState)}- updatePS Nothing = PlayState pat False False []+ updatePS Nothing = PlayState pat False False [pat] streamMute :: Show a => Stream -> a -> IO () streamMute s k = withPatId s (show k) (\x -> x {mute = True})@@ -258,14 +267,12 @@ streamUnsolo :: Show a => Stream -> a -> IO () streamUnsolo s k = withPatId s (show k) (\x -> x {solo = False}) -streamOnce :: Stream -> Bool -> ControlPattern -> IO ()-streamOnce st asap p+streamOnce :: Stream -> ControlPattern -> IO ()+streamOnce st p = do cMap <- readMVar (sInput st) tempo <- readMVar (sTempoMV st) now <- O.time- let latency target | asap = 0- | otherwise = oLatency target- fakeTempo = T.Tempo {T.cps = T.cps tempo,+ let fakeTempo = T.Tempo {T.cps = T.cps tempo, T.atCycle = 0, T.atTime = now, T.paused = False,@@ -280,7 +287,7 @@ on e = sched tempo $ start $ whole e cpsChanges = map (\e -> (on e - now, Map.lookup "cps" $ value e)) es messages target =- catMaybes $ map (\e -> do m <- toMessage (at e + (latency target)) target fakeTempo e+ catMaybes $ map (\e -> do m <- toMessage (at e + (oLatency target)) target fakeTempo e return $ (at e, m) ) es mapM_ (\(Cx target udp) ->@@ -310,7 +317,7 @@ streamHush :: Stream -> IO () streamHush s = do modifyMVar_ (sOutput s) $ return . const silence- modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {pattern = silence})+ modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {pattern = silence, history = silence:history x}) streamUnmuteAll :: Stream -> IO () streamUnmuteAll s = do modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {mute = False})
src/Sound/Tidal/Tempo.hs view
@@ -98,19 +98,24 @@ tempo <- readMVar tempoMV t <- O.time let frameTimespan = cFrameTimespan config- delta = logicalNow - t- -- 'now' comes from clock ticks, nothing to do with cycles logicalT ticks' = start st + fromIntegral ticks' * frameTimespan logicalNow = logicalT $ ticks st + 1- -- the tempo is just used to convert logical time to cycles+ -- Wait maximum of two frames+ delta = min (frameTimespan * 2) (logicalNow - t) e = timeToCycles tempo logicalNow s = if starting st && synched tempo then timeToCycles tempo (logicalT $ ticks st) else P.stop $ nowArc st- st' = st {ticks = ticks st + 1, nowArc = P.Arc s e,+ when (t < logicalNow) $ threadDelay (floor $ delta * 1000000)+ t' <- O.time+ let actualTick = floor $ (t' - start st) / frameTimespan+ -- reset ticks if ahead/behind by 4 or more+ newTick | (abs $ actualTick - ticks st) > 4 = actualTick+ | otherwise = (ticks st) + 1+ st' = st {ticks = newTick,+ nowArc = P.Arc s e, starting = not (synched tempo) }- when (t < logicalNow) $ threadDelay (floor $ delta * 1000000) callback tempoMV st' loop tempoMV st'
src/Sound/Tidal/Transition.hs view
@@ -20,27 +20,36 @@ import Sound.Tidal.Utils (enumerate) -- Evaluation of pat is forced so exceptions are picked up here, before replacing the existing pattern.-transition :: Show a => Stream -> (Time -> [ControlPattern] -> ControlPattern) -> a -> ControlPattern -> IO ()-transition stream f patId !pat = do pMap <- takeMVar (sPMapMV stream)- let playState = updatePS $ Map.lookup (show patId) pMap- pat' <- transition' $ history playState- let pMap' =- Map.insert (show patId) (playState {pattern = pat'}) pMap- putMVar (sPMapMV stream) pMap'- calcOutput stream- return ()+-- the "historyFlag" determines if the new pattern should be placed on the history stack or not+transition :: Show a => Stream -> Bool -> (Time -> [ControlPattern] -> ControlPattern) -> a -> ControlPattern -> IO ()+transition stream historyFlag f patId !pat =+ do pMap <- takeMVar (sPMapMV stream)+ let playState = updatePS $ Map.lookup (show patId) pMap+ pat' <- transition' $ appendPat (not historyFlag) (history playState)+ let pMap' = Map.insert (show patId) (playState {pattern = pat'}) pMap+ putMVar (sPMapMV stream) pMap'+ calcOutput stream+ return () where- updatePS (Just playState) = playState {history = pat:(history playState)}+ appendPat flag = if flag then (pat:) else id+ updatePS (Just playState) = playState {history = (appendPat historyFlag) (history playState)} updatePS Nothing = PlayState {pattern = silence, mute = False, solo = False,- history = pat:silence:[]+ history = (appendPat historyFlag) (silence:[]) } transition' context = do tempo <- readMVar $ sTempoMV stream now <- O.time let c = timeToCycles tempo now return $ f c context +mortalOverlay :: Time -> Time -> [Pattern a] -> Pattern a+mortalOverlay _ _ [] = silence+mortalOverlay t now (pat:ps) = overlay (pop ps) (playFor s (s+t) pat) where+ pop [] = silence+ pop (x:xs) = x+ s = sam (now - fromIntegral (floor now `mod` floor t)) + sam t+ {-| Washes away the current pattern after a certain delay by applying a function to it over time, then switching over to the next pattern to which another function is applied.@@ -114,7 +123,7 @@ -- | Sharp `jump` transition at next cycle boundary where cycle mod n == 0 jumpMod :: Int -> Time -> [ControlPattern] -> ControlPattern-jumpMod n now = jumpIn ((n-1) - ((floor now) `mod` n)) now+jumpMod n now = jumpIn' ((n-1) - ((floor now) `mod` n)) now -- | Degrade the new pattern over time until it ends in silence mortal :: Time -> Time -> Time -> [ControlPattern] -> ControlPattern
src/Sound/Tidal/UI.hs view
@@ -161,7 +161,7 @@ {- | Like @choose@, but works on an a list of tuples of values and weights @-sound "superpiano(3,8)" # note (choose [("a",1), ("e",0.5), ("g",2), ("c",1)])+sound "superpiano(3,8)" # note (wchoose [("a",1), ("e",0.5), ("g",2), ("c",1)]) @ In the above example, the "a" and "c" notes are twice as likely to@@ -1101,7 +1101,7 @@ stretch :: Pattern a -> Pattern a -- TODO - should that be whole or part? stretch p = splitQueries $ p {query = q}- where q st = query (zoomArc (enclosingArc $ map whole $ query p (st {arc = Arc (sam s) (nextSam s)})) p) st+ where q st = query (zoomArc (cycleArc $ enclosingArc $ map whole $ query p (st {arc = Arc (sam s) (nextSam s)})) p) st where s = start $ arc st {- | `fit'` is a generalization of `fit`, where the list is instead constructed by using another integer pattern to slice up a given pattern. The first argument is the number of cycles of that latter pattern to use when slicing. It's easier to understand this with a few examples:@@ -1388,7 +1388,6 @@ where a = filterValues id stitch b = filterValues not stitch - stutter :: Integral i => i -> Time -> Pattern a -> Pattern a stutter n t p = stack $ map (\i -> (t * fromIntegral i) `rotR` p) [0 .. (n-1)] @@ -1599,13 +1598,12 @@ maskedWeft = mask (every 2 rev $ _fast (n % 2) $ fastCat [silence, pure True]) weftP maskedWarp = mask (every 2 rev $ _fast (n % 2) $ fastCat [pure True, silence]) warpP -_select :: Double -> [Pattern a] -> Pattern a-_select f ps = ps !! floor (max 0 (min 1 f) * fromIntegral (length ps - 1))- -- | chooses between a list of patterns, using a pattern of floats (from 0-1) select :: Pattern Double -> [Pattern a] -> Pattern a select = tParam _select +_select :: Double -> [Pattern a] -> Pattern a+_select f ps = ps !! floor (max 0 (min 1 f) * fromIntegral (length ps - 1)) -- | chooses between a list of functions, using a pattern of floats (from 0-1) selectF :: Pattern Double -> [Pattern a -> Pattern a] -> Pattern a -> Pattern a@@ -1614,6 +1612,13 @@ _selectF :: Double -> [Pattern a -> Pattern a] -> Pattern a -> Pattern a _selectF f ps p = (ps !! floor (max 0 (min 0.999999 f) * fromIntegral (length ps))) p +-- | chooses between a list of functions, using a pattern of integers+pickF :: Pattern Int -> [Pattern a -> Pattern a] -> Pattern a -> Pattern a+pickF pi fs pat = innerJoin $ (\i -> _pickF i fs pat) <$> pi++_pickF :: Int -> [Pattern a -> Pattern a] -> Pattern a -> Pattern a+_pickF i fs p = (fs !!! i) p+ -- | @contrast p f f' p'@ splits controlpattern @p'@ in two, applying -- the function @f@ to one and @f'@ to the other. This depends on -- whether events in it contains values matching with those in @p@.@@ -1729,7 +1734,7 @@ swap :: Eq a => [(a, b)] -> Pattern a -> Pattern b swap things p = filterJust $ (`lookup` things) <$> p -{- @coat@ | +{- @soak@ | applies a function to a pattern and cats the resulting pattern, then continues applying the function until the depth is reached this can be used to create a pattern that wanders away from @@ -1740,14 +1745,30 @@ soak depth f pattern = cat $ take depth $ iterate f pattern deconstruct :: Int -> Pattern String -> String-deconstruct n p = intercalate " " $ map showStep $ toList n p+deconstruct n p = intercalate " " $ map showStep $ toList p where showStep :: [String] -> String showStep [] = "~" showStep [x] = x showStep xs = "[" ++ (intercalate ", " xs) ++ "]"- toList :: Int -> Pattern a -> [[a]]- toList n pat = map (\(s,e) -> map value $ queryArc (_segment n' pat) (Arc s e)) arcs+ toList :: Pattern a -> [[a]]+ toList pat = map (\(s,e) -> map value $ queryArc (_segment n' pat) (Arc s e)) arcs where breaks = [0, (1/n') ..] arcs = zip (take n breaks) (drop 1 breaks) n' = fromIntegral n++{- @bite@ n ipat pat |+ slices a pattern `pat` into `n` pieces, then uses the `ipat` pattern of integers to index into those slices.+ So `bite 4 "0 2*2" (run 8)` is the same as `"[0 1] [4 5]*2"`.+-}++bite :: Int -> Pattern Int -> Pattern a -> Pattern a+bite n ipat pat = squeezeJoin $ zoompat <$> ipat+ where zoompat i = zoom (i'/(fromIntegral n), (i'+1)/(fromIntegral n)) pat+ where i' = fromIntegral $ i `mod` n++{- @squeeze@ ipat pats | uses a pattern of integers to index into a list of patterns.+-}+squeeze :: Pattern Int -> [Pattern a] -> Pattern a+squeeze _ [] = silence+squeeze ipat pats = squeezeJoin $ (pats !!!) <$> ipat
src/Sound/Tidal/Version.hs view
@@ -1,4 +1,4 @@ module Sound.Tidal.Version where tidal_version :: String-tidal_version = "1.0.7"+tidal_version = "1.0.11"
test/Sound/Tidal/UITest.hs view
@@ -237,3 +237,8 @@ (wedge (0) (s "ho ho:2 ho:3 hc") (rev $ s "ho ho:2 ho:3 hc")) (rev $ s "ho ho:2 ho:3 hc") + describe "bite" $ do+ it "can slice a pattern into bits" $ do+ compareP (Arc 0 4)+ (bite 4 "0 2*2" (Sound.Tidal.Core.run 8))+ ("[0 1] [4 5]*2" :: Pattern Int)
tidal.cabal view
@@ -1,5 +1,5 @@ name: tidal-version: 1.0.10+version: 1.0.11 synopsis: Pattern language for improvised music -- description: homepage: http://tidalcycles.org/@@ -13,6 +13,7 @@ build-type: Simple cabal-version: >=1.10 tested-with: GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.3+data-files: BootTidal.hs Extra-source-files: README.md CHANGELOG.md tidal.el @@ -26,6 +27,7 @@ default-language: Haskell2010 Exposed-modules: Sound.Tidal.Bjorklund+ Sound.Tidal.Carabiner Sound.Tidal.Chords Sound.Tidal.Config Sound.Tidal.Control@@ -59,6 +61,8 @@ , bifunctors < 5.6 , transformers < 0.5.7 , template-haskell >= 2.10.0.0 && < 2.15+ , bytestring < 0.11+ , clock < 0.8 if !impl(ghc >= 8.4.1) build-depends: semigroups == 0.18.*
tidal.el view
@@ -57,6 +57,14 @@ () "*Arguments to the haskell interpreter (default=none).") +(defvar tidal-boot-script-path+ (concat (substring+ (shell-command-to-string+ "ghc-pkg describe $(ghc-pkg latest tidal) | grep data-dir | cut -f2 -d' '") 0 -1)+ "/BootTidal.hs")+ "*Full path to BootTidal.hs (inferred by introspecting ghc-pkg package db)."+ )+ (defvar tidal-literate-p t "*Flag to indicate if we are in literate mode (default=t).")@@ -87,60 +95,7 @@ nil tidal-interpreter-arguments) (tidal-see-output))- (tidal-send-string ":set prompt \"\"")- (if (string< tidal-interpreter-version "8.2.0")- (tidal-send-string ":set prompt2 \"\"")- (tidal-send-string ":set prompt-cont \"\""))- (tidal-send-string ":set -XOverloadedStrings-import Sound.Tidal.Context-tidal <- startMulti [superdirtTarget {oLatency = 0.1, oAddress = \"127.0.0.1\", oPort = 57120}] (defaultConfig {cFrameTimespan = 1/20, cCtrlListen = True})-let p = streamReplace tidal-let hush = streamHush tidal-let list = streamList tidal-let mute = streamMute tidal-let unmute = streamUnmute tidal-let solo = streamSolo tidal-let unsolo = streamUnsolo tidal-let once = streamOnce tidal False-let asap = streamOnce tidal True-let nudgeAll = streamNudgeAll tidal-let all = streamAll tidal-let resetCycles = streamResetCycles tidal-let setcps = asap . cps-let xfade i = transition tidal (Sound.Tidal.Transition.xfadeIn 4) i-let xfadeIn i t = transition tidal (Sound.Tidal.Transition.xfadeIn t) i-let histpan i t = transition tidal (Sound.Tidal.Transition.histpan t) i-let wait i t = transition tidal (Sound.Tidal.Transition.wait t) i-let waitT i f t = transition tidal (Sound.Tidal.Transition.waitT f t) i-let jump i = transition tidal (Sound.Tidal.Transition.jump) i-let jumpIn i t = transition tidal (Sound.Tidal.Transition.jumpIn t) i-let jumpIn' i t = transition tidal (Sound.Tidal.Transition.jumpIn' t) i-let jumpMod i t = transition tidal (Sound.Tidal.Transition.jumpMod t) i-let mortal i lifespan release = transition tidal (Sound.Tidal.Transition.mortal lifespan release) i-let interpolate i = transition tidal (Sound.Tidal.Transition.interpolate) i-let interpolateIn i t = transition tidal (Sound.Tidal.Transition.interpolateIn t) i-let clutch i = transition tidal (Sound.Tidal.Transition.clutch) i-let clutchIn i t = transition tidal (Sound.Tidal.Transition.clutchIn t) i-let anticipate i = transition tidal (Sound.Tidal.Transition.anticipate) i-let anticipateIn i t = transition tidal (Sound.Tidal.Transition.anticipateIn t) i-let d1 = p 1-let d2 = p 2 . (|< orbit 1)-let d3 = p 3 . (|< orbit 2)-let d4 = p 4 . (|< orbit 3)-let d5 = p 5 . (|< orbit 4)-let d6 = p 6 . (|< orbit 5)-let d7 = p 7 . (|< orbit 6)-let d8 = p 8 . (|< orbit 7)-let d9 = p 9 . (|< orbit 8)-let d10 = p 10-let d11 = p 11-let d12 = p 12-let d13 = p 13-let d14 = p 14-let d15 = p 15-let d16 = p 16- ")- (tidal-send-string ":set prompt \"tidal> \"")+ (tidal-send-string (concat ":script " tidal-boot-script-path)) ) (defun tidal-see-output ()