packages feed

tidal 1.4.9 → 1.5.0

raw patch · 17 files changed

+715/−662 lines, 17 filesdep −mwc-random

Dependencies removed: mwc-random

Files

BootTidal.hs view
@@ -11,11 +11,13 @@ tidal <- startTidal (superdirtTarget {oLatency = 0.1, oAddress = "127.0.0.1", oPort = 57120}) (defaultConfig {cFrameTimespan = 1/20})  :{-let p = streamReplace tidal+let only = (hush >>)+    p = streamReplace tidal     hush = streamHush tidal     list = streamList tidal     mute = streamMute tidal     unmute = streamUnmute tidal+    unmuteAll = streamUnmuteAll tidal     solo = streamSolo tidal     unsolo = streamUnsolo tidal     once = streamOnce tidal
CHANGELOG.md view
@@ -1,5 +1,19 @@ # TidalCycles log of changes +## 1.5.0 - +	* Export drawLineSz @mxmxyz+	* tidal-parse additions (bite, splice, pickF, select, squeeze; fixed slice) @dktr0+	* New, more efficient pseudorandom number generator @dktr0+	* Pattern first someCyclesBy param @bgold-cosmos+	* Refactored, more flexible OSC targetting @yaxu+	* Simplify tidal-boot-script in tidal.el @jamagin+	* Support state substitution in mininotation #530 @yaxu+	* Pattern first parameter of splice #656 @yaxu+	* Pattern first parameter of chew @yaxu+	* add 'x' fraction alias for 1/6 @yaxu+	* add dfb alias for delayfeedback param, dt for delaytime @yaxu+	* add unmuteAll and only to BootTidal.hs @yaxu+ ## 1.4.9 - Housebound spirit 	* Simplify 'show'ing of patterns @yaxu 	* New `draw` function for drawing a pattern of single characters as a text-based diagram,
src/Sound/Tidal/Control.hs view
@@ -13,7 +13,6 @@ import Sound.Tidal.UI import qualified Sound.Tidal.Params as P import Sound.Tidal.Utils-import Sound.Tidal.ParseBP (Parseable, Enumerable, parseBP_E)  {- | `spin` will "spin" a layer up a pattern the given number of times, with each successive layer offset in time by an additional `1/n` of a@@ -264,6 +263,12 @@ randslice :: Pattern Int -> ControlPattern -> ControlPattern randslice = tParam $ \n p -> innerJoin $ (\i -> _slice n i p) <$> irand n +splice :: Pattern Int -> Pattern Int -> ControlPattern -> Pattern (Map.Map String Value)+splice bits ipat pat = withEvent f (slice 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)+ {- | `loopAt` makes a sample fit the given number of cycles. Internally, it works by setting the `unit` parameter to "c", changing the playback@@ -394,324 +399,3 @@         offset st = fromMaybe (pure 0) $ do p <- Map.lookup ctrl (controls st)                                             return $ ((f . fromMaybe 0 . getR) <$> p)         ctrl = "_t_" ++ show k--_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) $ Map.lookup s m) a--_cX_ :: (Value -> Maybe a) -> String -> Pattern a-_cX_ f s = Pattern $ \(State a m) -> queryArc (maybe silence (_getP_ f) $ 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--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--cP :: (Enumerable a, Parseable a) => String -> Pattern a-cP s = innerJoin $ parseBP_E <$> (_cX_ getS s)---- 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"--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/Core.hs view
@@ -6,7 +6,7 @@  import           Data.Fixed (mod') import qualified Data.Map.Strict as Map-+import           Data.Maybe (fromMaybe) import           Sound.Tidal.Pattern  -- ** Elemental patterns@@ -157,7 +157,7 @@ fromList :: [a] -> Pattern a fromList = cat . map pure --- | Turns a list of values into a pattern, playing one of them per cycle.+-- | Turns a list of values into a pattern, playing all of them per cycle. fastFromList :: [a] -> Pattern a fastFromList = fastcat . map pure @@ -424,3 +424,315 @@ 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) $ Map.lookup s m) a++_cX_ :: (Value -> Maybe a) -> String -> Pattern a+_cX_ f s = Pattern $ \(State a m) -> queryArc (maybe silence (_getP_ f) $ 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++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"
src/Sound/Tidal/Params.hs view
@@ -160,6 +160,7 @@ -- | a pattern of numbers from 0 to 1. Sets the length of the delay. delaytime :: Pattern Double -> ControlPattern delaytime = pF "delaytime"+ detune :: Pattern Double -> ControlPattern detune = pF "detune" @@ -381,6 +382,10 @@ -- | formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect. vowel :: Pattern String -> ControlPattern vowel = pS "vowel"++voweli :: Pattern Int -> ControlPattern+voweli = pI "vowel"+ waveloss :: Pattern Double -> ControlPattern waveloss = pF "waveloss" @@ -482,7 +487,7 @@ lbrick = pF "lbrick"  -- aliases-att, bpf, bpq, chdecay, ctf, ctfg, delayfb, delayt, det, gat, hg, hpf, hpq, lag, lbd, lch, lcl, lcp, lcr, lfoc, lfoi+att, bpf, bpq, chdecay, ctf, ctfg, delayfb, dfb, delayt, dt, det, gat, hg, hpf, hpq, lag, lbd, lch, lcl, lcp, lcr, lfoc, lfoi    , lfop, lht, llt, loh, lpf, lpq, lsn, ohdecay, phasdp, phasr, pit1, pit2, pit3, por, rel, sz, sag, scl, scp    , scr, sld, std, stt, sus, tdecay, tremdp, tremr, vcf, vco, voi  :: Pattern Double -> ControlPattern@@ -493,7 +498,9 @@ ctf = cutoff ctfg = cutoffegint delayfb = delayfeedback+dfb = delayfeedback delayt = delaytime+dt = delaytime det = detune gat = gate hg = hatgrain
src/Sound/Tidal/ParseBP.hs view
@@ -54,6 +54,7 @@             | TPat_Elongate Rational (TPat a)             | TPat_Repeat Int (TPat a)             | TPat_EnumFromTo (TPat a) (TPat a)+            | TPat_Var String             deriving (Show)  toPat :: (Parseable a, Enumerable a) => TPat a -> Pattern a@@ -78,6 +79,7 @@            base_first | null pats = pure 0                       | otherwise = pure $ fst $ head pats    TPat_Seq xs -> snd $ resolve_seq xs+   TPat_Var s -> getControl s    _ -> silence  resolve_tpat :: (Enumerable a, Parseable a) => TPat a -> (Rational, Pattern a)@@ -117,10 +119,14 @@ parseTPat :: Parseable a => String -> Either ParseError (TPat a) parseTPat = parseRhythm tPatParser +cP :: (Enumerable a, Parseable a) => String -> Pattern a+cP s = innerJoin $ parseBP_E <$> (_cX_ getS s)+ class Parseable a where   tPatParser :: MyParser (TPat a)   doEuclid :: Pattern Int -> Pattern Int -> Pattern Int -> Pattern a -> Pattern a-  -- toEuclid :: a ->+  getControl :: String -> Pattern a+  getControl _ = silence  class Enumerable a where   fromTo :: a -> a -> Pattern a@@ -137,6 +143,7 @@ instance Parseable Double where   tPatParser = pDouble   doEuclid = euclidOff+  getControl = cF_  instance Enumerable Double where   fromTo = enumFromTo'@@ -145,6 +152,7 @@ instance Parseable String where   tPatParser = pVocable   doEuclid = euclidOff+  getControl = cS_  instance Enumerable String where   fromTo a b = fastFromList [a,b]@@ -153,6 +161,7 @@ instance Parseable Bool where   tPatParser = pBool   doEuclid = euclidOffBool+  getControl = cB_  instance Enumerable Bool where   fromTo a b = fastFromList [a,b]@@ -161,6 +170,7 @@ instance Parseable Int where   tPatParser = pIntegral   doEuclid = euclidOff+  getControl = cI_  instance Enumerable Int where   fromTo = enumFromTo'@@ -169,6 +179,7 @@ instance Parseable Integer where   tPatParser = pIntegral   doEuclid = euclidOff+  getControl = (fmap fromIntegral) . cI_  instance Enumerable Integer where   fromTo = enumFromTo'@@ -177,6 +188,7 @@ instance Parseable Rational where   tPatParser = pRational   doEuclid = euclidOff+  getControl = cR_  instance Enumerable Rational where   fromTo = enumFromTo'@@ -294,8 +306,13 @@ pSingle :: MyParser (TPat a) -> MyParser (TPat a) pSingle f = f >>= pRand >>= pMult +pVar :: MyParser (TPat a)+pVar = wrapPos $ do char '^'+                    name <- many (letter <|> oneOf "0123456789:.-_") <?> "string"+                    return $ TPat_Var name+ pPart :: Parseable a => MyParser (TPat a) -> MyParser (TPat a)-pPart f = do pt <- (pSingle f <|> pPolyIn f <|> pPolyOut f) >>= pE >>= pRand+pPart f = do pt <- (pSingle f <|> pPolyIn f <|> pPolyOut f <|> pVar) >>= pE >>= pRand              spaces -- TODO is this needed?              return pt @@ -506,6 +523,8 @@                     return $ 1/3              <|> do char 'f'                     return $ 0.2+             <|> do char 'x'+                    return $ 1/6  pRational :: MyParser (TPat Rational) pRational = wrapPos $ (TPat_Atom Nothing) <$> pRatio
src/Sound/Tidal/Pattern.hs view
@@ -42,7 +42,7 @@ data ArcF a = Arc   { start :: a   , stop :: a-  } deriving (Eq, Ord, Functor)+  } deriving (Eq, Ord, Functor, Show)  type Arc = ArcF Time @@ -723,7 +723,7 @@ filterValues :: (a -> Bool) -> Pattern a -> Pattern a filterValues f p = p {query = filter (f . value) . query p} --- | Turns a pattern of 'Maybe' values in to a pattern of values,+-- | 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
src/Sound/Tidal/Show.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Sound.Tidal.Show (show, showAll, draw, drawLine) where+module Sound.Tidal.Show (show, showAll, draw, drawLine, drawLineSz) where  import Sound.Tidal.Pattern 
src/Sound/Tidal/Stream.hs view
@@ -7,7 +7,7 @@ import           Control.Concurrent.MVar import           Control.Concurrent import qualified Data.Map.Strict as Map-import           Data.Maybe (fromJust, fromMaybe, isJust, catMaybes)+import           Data.Maybe (fromJust, fromMaybe, catMaybes) import qualified Control.Exception as E -- import Control.Monad.Reader -- import Control.Monad.Except@@ -27,12 +27,9 @@ import           System.Random (getStdRandom, randomR) import           Sound.Tidal.Show () -data TimeStamp = BundleStamp | MessageStamp | NoStamp- deriving (Eq, Show)- data Stream = Stream {sConfig :: Config,                       sInput :: MVar StateMap,-                      sOutput :: MVar ControlPattern,+                      -- sOutput :: MVar ControlPattern,                       sListenTid :: Maybe ThreadId,                       sPMapMV :: MVar PlayMap,                       sTempoMV :: MVar T.Tempo,@@ -42,90 +39,39 @@  type PatId = String -data Cx = Cx {cxTarget :: OSCTarget,-              cxUDP :: O.UDP+data Cx = Cx {cxTarget :: Target,+              cxUDP :: O.UDP,+              cxOSCs :: [OSC]              }+  deriving (Show) -data OSCTarget = OSCTarget {oName :: String,-                            oAddress :: String,-                            oPort :: Int,-                            oPath :: String,-                            oShape :: Maybe [(String, Maybe Value)],-                            oLatency :: Double,-                            oPreamble :: [O.Datum],-                            oTimestamp :: TimeStamp-                           }-                 deriving Show+data StampStyle = BundleStamp+                | MessageStamp+  deriving (Eq, Show) -superdirtTarget :: OSCTarget-superdirtTarget = OSCTarget {oName = "SuperDirt",-                             oAddress = "127.0.0.1",-                             oPort = 57120,-                             oPath = "/play2",-                             oShape = Nothing,-                             oLatency = 0.02,-                             oPreamble = [],-                             oTimestamp = BundleStamp-                            } -dirtTarget :: OSCTarget-dirtTarget = OSCTarget {oName = "Dirt",-                        oAddress = "127.0.0.1",-                        oPort = 7771,-                        oPath = "/play",-                        oShape = Just [("sec", Just $ VI 0),-                                       ("usec", Just $ VI 0),-                                       ("cps", Just $ VF 0),-                                       ("s", Nothing),-                                       ("offset", Just $ VF 0),-                                       ("begin", Just $ VF 0),-                                       ("end", Just $ VF 1),-                                       ("speed", Just $ VF 1),-                                       ("pan", Just $ VF 0.5),-                                       ("velocity", Just $ VF 0.5),-                                       ("vowel", Just $ VS ""),-                                       ("cutoff", Just $ VF 0),-                                       ("resonance", Just $ VF 0),-                                       ("accelerate", Just $ VF 0),-                                       ("shape", Just $ VF 0),-                                       ("kriole", Just $ VI 0),-                                       ("gain", Just $ VF 1),-                                       ("cut", Just $ VI 0),-                                       ("delay", Just $ VF 0),-                                       ("delaytime", Just $ VF (-1)),-                                       ("delayfeedback", Just $ VF (-1)),-                                       ("crush", Just $ VF 0),-                                       ("coarse", Just $ VI 0),-                                       ("hcutoff", Just $ VF 0),-                                       ("hresonance", Just $ VF 0),-                                       ("bandf", Just $ VF 0),-                                       ("bandq", Just $ VF 0),-                                       ("unit", Just $ VS "rate"),-                                       ("loop", Just $ VF 0),-                                       ("n", Just $ VF 0),-                                       ("attack", Just $ VF (-1)),-                                       ("hold", Just $ VF 0),-                                       ("release", Just $ VF (-1)),-                                       ("orbit", Just $ VI 0),-                                       ("id", Just $ VI 0)-                                      ],-                         oLatency = 0.02,-                         oPreamble = [],-                         oTimestamp = MessageStamp-                       }+data Schedule = Pre StampStyle+              | Live+  deriving (Eq, Show) -startStream :: Config -> MVar StateMap -> [OSCTarget] -> IO (MVar ControlPattern, MVar T.Tempo, [Cx])-startStream config sMapMV targets-  = do cxs <- mapM (\target -> do u <- O.openUDP (oAddress target) (oPort target)-                                  return $ Cx {cxUDP = u,-                                               cxTarget = target-                                              }-                   ) targets-       pMV <- newMVar empty-       (tempoMV, _) <- T.clocked config $ onTick config sMapMV pMV cxs-       return $ (pMV, tempoMV, cxs)+data Target = Target {oName :: String,+                      oAddress :: String,+                      oPort :: Int,+                      oLatency :: Double,+                      oWindow :: Maybe Arc,+                      oSchedule :: Schedule+                     }+                 deriving Show +data Args = Named {required :: [String]}+          | ArgList [(String, Maybe Value)]+         deriving Show +data OSC = OSC {path :: String,+                args :: Args+               }+         deriving Show+ data PlayState = PlayState {pattern :: ControlPattern,                             mute :: Bool,                             solo :: Bool,@@ -135,6 +81,93 @@  type PlayMap = Map.Map PatId PlayState +superdirtTarget :: Target+superdirtTarget = Target {oName = "SuperDirt",+                          oAddress = "127.0.0.1",+                          oPort = 57120,+                          oLatency = 0.2,+                          oWindow = Nothing,+                          oSchedule = Pre BundleStamp+                         }++superdirtShape :: OSC+superdirtShape = OSC "/play2" $ Named {required = ["s"]}++dirtTarget :: Target+dirtTarget = Target {oName = "Dirt",+                     oAddress = "127.0.0.1",+                     oPort = 7771,+                     oLatency = 0.02,+                     oWindow = Nothing,+                     oSchedule = Pre MessageStamp+                    }++dirtShape :: OSC+dirtShape = OSC "/play" $ ArgList [("sec", Just $ VI 0),+                                   ("usec", Just $ VI 0),+                                   ("cps", Just $ VF 0),+                                   ("s", Nothing),+                                   ("offset", Just $ VF 0),+                                   ("begin", Just $ VF 0),+                                   ("end", Just $ VF 1),+                                   ("speed", Just $ VF 1),+                                   ("pan", Just $ VF 0.5),+                                   ("velocity", Just $ VF 0.5),+                                   ("vowel", Just $ VS ""),+                                   ("cutoff", Just $ VF 0),+                                   ("resonance", Just $ VF 0),+                                   ("accelerate", Just $ VF 0),+                                   ("shape", Just $ VF 0),+                                   ("kriole", Just $ VI 0),+                                   ("gain", Just $ VF 1),+                                   ("cut", Just $ VI 0),+                                   ("delay", Just $ VF 0),+                                   ("delaytime", Just $ VF (-1)),+                                   ("delayfeedback", Just $ VF (-1)),+                                   ("crush", Just $ VF 0),+                                   ("coarse", Just $ VI 0),+                                   ("hcutoff", Just $ VF 0),+                                   ("hresonance", Just $ VF 0),+                                   ("bandf", Just $ VF 0),+                                   ("bandq", Just $ VF 0),+                                   ("unit", Just $ VS "rate"),+                                   ("loop", Just $ VF 0),+                                   ("n", Just $ VF 0),+                                   ("attack", Just $ VF (-1)),+                                   ("hold", Just $ VF 0),+                                   ("release", Just $ VF (-1)),+                                   ("orbit", Just $ VI 0),+                                   ("id", Just $ VI 0)+                                  ]++startStream :: Config -> [(Target, [OSC])] -> IO Stream+startStream config oscmap +  = do cxs <- mapM (\(target, os) -> do u <- O.openUDP (oAddress target) (oPort target)+                                        return $ Cx {cxUDP = u, cxTarget = target, cxOSCs = os}+                   ) oscmap++       sMapMV <- newMVar Map.empty+       pMapMV <- newMVar Map.empty+       globalFMV <- newMVar id+       listenTid <- ctrlListen sMapMV config+       tempoMV <- newEmptyMVar+       let stream = Stream {sConfig = config,+                            sInput = sMapMV,+                            sListenTid = listenTid,+                            sPMapMV = pMapMV,+                            sTempoMV = tempoMV,+                            sGlobalFMV = globalFMV,+                            sCxs = cxs+                           }+       _ <- T.clocked config tempoMV $ onTick stream+       return stream++startTidal :: Target -> Config -> IO Stream+startTidal target config = startStream config [(target, [superdirtShape])]++startMulti :: [Target] -> Config -> IO ()+startMulti _ _ = putStrLn $ "startMulti has been removed, please check the latest documentation on tidalcycles.org"+ toDatum :: Value -> O.Datum toDatum (VF x) = O.float x toDatum (VI x) = O.int32 x@@ -144,67 +177,65 @@ toDatum (VB False) = O.int32 (0 :: Int) toDatum (VX xs) = O.Blob $ O.blob_pack xs -toData :: OSCTarget -> Event ControlMap -> Maybe [O.Datum]-toData target e-  | isJust (oShape target) = fmap (fmap toDatum) $ sequence $ map (\(n,v) -> Map.lookup n (value e) <|> v) (fromJust $ oShape target)-  | otherwise = Just $ concatMap (\(n,v) -> [O.string n, toDatum v]) $ Map.toList $ value e+toData :: OSC -> Event ControlMap -> Maybe [O.Datum]+toData (OSC {args = ArgList as}) e = fmap (fmap toDatum) $ sequence $ map (\(n,v) -> Map.lookup n (value e) <|> v) as+toData (OSC {args = Named rqrd}) e+  | hasRequired rqrd = Just $ concatMap (\(n,v) -> [O.string n, toDatum v]) $ Map.toList $ value e+  | otherwise = Nothing+  where hasRequired [] = True+        hasRequired xs = null $ filter (not . (`elem` ks)) xs+        ks = Map.keys (value e) -substitutePath :: String -> ControlMap -> String-substitutePath path cm = parse path-  where parse [] = []+substitutePath :: String -> ControlMap -> Maybe String+substitutePath str cm = parse str+  where parse [] = Just []         parse ('{':xs) = parseWord xs-        parse (x:xs) = x:(parse xs)+        parse (x:xs) = do xs' <- parse xs+                          return (x:xs')         parseWord xs | b == [] = getString cm a-                     | otherwise = getString cm a ++ parse (tail b)+                     | otherwise = do v <- getString cm a+                                      xs' <- parse (tail b)+                                      return $ v ++ xs'           where (a,b) = break (== '}') xs -getString :: ControlMap -> String -> String-getString cm s = fromMaybe "" $ do v <- Map.lookup s cm-                                   return $ simpleShow v-                                    where simpleShow :: Value -> String-                                          simpleShow (VS str) = str-                                          simpleShow (VI i) = show i-                                          simpleShow (VF f) = show f-                                          simpleShow (VR r) = show r-                                          simpleShow (VB b) = show b-                                          simpleShow (VX xs) = show xs+getString :: ControlMap -> String -> Maybe String+getString cm s = defaultValue $ simpleShow <$> Map.lookup s cm+                      where simpleShow :: Value -> String+                            simpleShow (VS str) = str+                            simpleShow (VI i) = show i+                            simpleShow (VF f) = show f+                            simpleShow (VR r) = show r+                            simpleShow (VB b) = show b+                            simpleShow (VX xs) = show xs+                            (_, dflt) = break (== '=') s+                            defaultValue :: Maybe String -> Maybe String+                            defaultValue Nothing | null dflt = Nothing+                                                 | otherwise = Just $ tail dflt+                            defaultValue x = x -toMessage :: Config -> Double -> OSCTarget -> T.Tempo -> Event (Map.Map String Value) -> Maybe O.Message-toMessage config t target tempo e = do vs <- toData target addExtra-                                       return $ O.Message (substitutePath (oPath target) (value e)) $ oPreamble target ++ vs-  where on = sched tempo $ start $ wholeOrPart e-        off = sched tempo $ stop $ wholeOrPart e-        cm = value e-        identifier = ((if (start $ wholeOrPart e) == (start $ part e) then "X" else ">")-                      ++ show (start $ wholeOrPart e)-                      ++ "-"-                      ++ show (stop $ wholeOrPart e)-                      ++ "-"-                      ++ getString cm "n"-                      ++ "-"-                      ++ getString cm "note"-                      ++ "-"-                      ++ getString cm "s"-                     )-        delta = off - on-        messageStamp = oTimestamp target == MessageStamp-        -- If there is already cps in the event, the union will preserve that.-        addExtra = (\v -> (Map.union v $ Map.fromList (extra messageStamp)-                          )) <$> e-        addIdentifier | cSendParts config = (("id", VS identifier):)-                      | otherwise = id-        extra False = addIdentifier [("cps", (VF $ T.cps tempo)),-                                     ("delta", VF delta),-                                     ("cycle", VF (fromRational $ start $ wholeOrPart e))-                                    ]-        extra True = timestamp ++ (extra False)-        timestamp = [("sec", VI sec),-                     ("usec", VI usec)-                    ]-        ut = O.ntpr_to_ut t-        sec = floor ut-        usec = floor $ 1000000 * (ut - (fromIntegral sec))+playStack :: PlayMap -> ControlPattern+playStack pMap = stack $ map pattern active+  where active = filter (\pState -> if hasSolo pMap+                                    then solo pState+                                    else not (mute pState)+                        ) $ Map.elems pMap +toOSC :: Double -> Event ControlMap -> T.Tempo -> OSC -> Maybe (Double, O.Message)+toOSC latency e tempo osc = do vs <- toData osc addExtra+                               mungedPath <- substitutePath (path osc) (value e)+                               return (ts, O.Message mungedPath vs)+       where on = sched tempo $ start $ wholeOrPart e+             off = sched tempo $ stop $ wholeOrPart e+             delta = off - on+             -- If there is already cps in the event, the union will preserve that.+             addExtra = (\v -> (Map.union v extra)) <$> e+             extra = Map.fromList [("cps", (VF $ T.cps tempo)),+                                   ("delta", VF delta),+                                   ("cycle", VF (fromRational $ start $ wholeOrPart e))+                                  ]+             ts = on + nudge + latency+             nudge = fromJust $ getF $ fromMaybe (VF 0) $ Map.lookup "nudge" $ value e+ doCps :: MVar T.Tempo -> (Double, Maybe Value) -> IO () doCps tempoMV (d, Just (VF cps)) = do _ <- forkIO $ do threadDelay $ floor $ d * 1000000                                                        -- hack to stop things from stopping !@@ -213,59 +244,105 @@                                       return () doCps _ _ = return () -onTick :: Config -> MVar StateMap -> MVar ControlPattern -> [Cx] -> MVar T.Tempo -> T.State -> IO ()-onTick config sMapMV pMV cxs tempoMV st =-  do p <- readMVar pMV-     sMap <- readMVar sMapMV-     tempo <- takeMVar tempoMV-     let frameEnd = snd $ T.nowTimespan st+onTick :: Stream -> T.State -> IO ()+onTick stream st+  = do doTick False stream st++processCps :: T.Tempo -> [Event ControlMap] -> ([(T.Tempo, Event ControlMap)], T.Tempo)+processCps t [] = ([], t)+-- If an event has a tempo change, that affects the following events..+processCps t (e:evs) = (((t', e):es'), t'')+  where cps' = do x <- Map.lookup "cps" $ value e+                  getF x+        t' = (maybe t (\newCps -> T.changeTempo' t newCps (eventPartStart e)) cps')+        (es', t'') = processCps t' evs++streamOnce :: Stream -> ControlPattern -> IO ()+streamOnce st p = do i <- getStdRandom $ randomR (0, 8192)+                     streamFirst st $ rotL (toRational (i :: Int)) p++streamFirst :: Stream -> ControlPattern -> IO ()+streamFirst stream pat = do now <- O.time+                            tempo <- readMVar (sTempoMV stream)+                            pMapMV <- newMVar $ Map.singleton "fake"+                                      (PlayState {pattern = pat,+                                                  mute = False,+                                                  solo = False,+                                                  history = []+                                                 }+                                      )+                            let cps = T.cps tempo+                                state = T.State {T.ticks = 0,+                                                 T.start = now,+                                                 T.nowTimespan = (now, now + (1/cps)),+                                                 T.nowArc = (Arc 0 1)+                                                }+                            doTick True (stream {sPMapMV = pMapMV}) state++doTick :: Bool -> Stream -> T.State -> IO ()+doTick fake stream st =+  do tempo <- takeMVar (sTempoMV stream)+     pMap <- readMVar (sPMapMV stream)+     sMap <- readMVar (sInput stream)+     -- putStrLn $ show st+     let config = sConfig stream+         cxs = sCxs stream+         cycleNow = T.timeToCycles tempo $ T.start st+         -- If a 'fake' tick, it'll be aligned with cycle zero+         pat | fake = withResultTime (+ cycleNow) $ playStack pMap+             | otherwise = playStack pMap+         frameEnd = snd $ T.nowTimespan st+         -- add cps to state          sMap' = Map.insert "_cps" (pure $ VF $ T.cps tempo) sMap-         es = sortOn (start . part) $ filterOns $ query p (State {arc = T.nowArc st, controls = sMap'})-         filterOns | cSendParts config = id-                   | otherwise = filter eventHasOnset-           -- there should always be a whole (due to the eventHasOnset filter)+         filterOns = filter eventHasOnset+         extraLatency | fake = 0+                      | otherwise = cFrameTimespan config + T.nudged tempo+         --filterOns | cSendParts config = id+         --          | otherwise = filter eventHasOnset+         es = sortOn (start . part) $ filterOns $ query pat (State {arc = T.nowArc st,+                                                                    controls = sMap'+                                                                   }+                                                            )+         -- TODO onset is calculated in toOSC as well..          on e tempo'' = (sched tempo'' $ start $ wholeOrPart e)-         eventNudge e = fromJust $ getF $ fromMaybe (VF 0) $ Map.lookup "nudge" $ value e-         processCps :: T.Tempo -> [Event ControlMap] -> ([(T.Tempo, Event ControlMap)], T.Tempo)-         processCps t [] = ([], t)-         -- If an event has a tempo change, that affects the following-         -- events..-         processCps t (e:evs) = (((t', e):es'), t'')-           where cps' = do x <- Map.lookup "cps" $ value e-                           getF x-                 t' = (maybe t (\newCps -> T.changeTempo' t newCps (eventPartStart e)) cps')-                 (es', t'') = processCps t' evs-         latency target = oLatency target + cFrameTimespan config + T.nudged tempo          (tes, tempo') = processCps tempo es-     mapM_ (\(Cx target udp) -> (do let ms = catMaybes $ map (\(t, e) -> do let nudge = eventNudge e-                                                                            let onset = on e t-                                                                            m <- toMessage config (onset + nudge + latency target) target tempo e-                                                                            -- drop events that have gone out of frame (due to tempo-                                                                            -- changes during the frame)-                                                                            if (onset < frameEnd)-                                                                              then Just (onset + nudge, m)-                                                                              else  Nothing-                                                             ) tes-                                    E.catch (mapM_ (send target (latency target) udp) ms)-                                )-                       (\(e ::E.SomeException)-                        -> putStrLn $ "Failed to send. Is the '" ++ oName target ++ "' target running? " ++ show e-                       )+     mapM_ (\cx@(Cx target _ oscs) ->+              (do let latency = oLatency target + extraLatency+                      ms = concatMap (\(t, e) ->+                                        if (fake || (on e t) < frameEnd)+                                        then catMaybes $ map (toOSC latency e t) oscs+                                        else []+                                     ) tes+                  E.catch $ mapM_ (send cx) ms+              )+              (\(e ::E.SomeException)+                -> putStrLn $ "Failed to send. Is the '" ++ oName target ++ "' target running? " ++ show e+              )            ) cxs-     putMVar tempoMV tempo'+     putMVar (sTempoMV stream) tempo'      return () -send :: O.Transport t => OSCTarget -> Double -> t -> (Double, O.Message) -> IO ()-send target latency u (time, m)-  | oTimestamp target == BundleStamp = O.sendBundle u $ O.Bundle (time + latency) [m]-  | oTimestamp target == MessageStamp = O.sendMessage u m+send :: Cx -> (Double, O.Message) -> IO ()+send cx (time, m)+  | oSchedule target == Pre BundleStamp = O.sendBundle u $ O.Bundle time [m]+  | oSchedule target == Pre MessageStamp = O.sendMessage u $ addtime m   | otherwise = do _ <- forkIO $ do now <- O.time-                                    threadDelay $ floor $ ((time+latency) - now) * 1000000+                                    threadDelay $ floor $ (time - now) * 1000000                                     O.sendMessage u m                    return ()+    where addtime (O.Message mpath params) = O.Message mpath ((O.int32 sec):((O.int32 usec):params))+          ut = O.ntpr_to_ut time+          sec :: Int+          sec = floor ut+          usec :: Int+          usec = floor $ 1000000 * (ut - (fromIntegral sec))+          u = cxUDP cx+          target = cxTarget cx+         -- latency target = oLatency target + cFrameTimespan config + T.nudged tempo  sched :: T.Tempo -> Rational -> Double-sched tempo c = ((fromRational $ c - (T.atCycle tempo)) / T.cps tempo) + (T.atTime tempo)+sched tempo c = ((fromRational $ c - (T.atCycle tempo)) / T.cps tempo)+                + (T.atTime tempo)  -- Interaction @@ -285,12 +362,13 @@                   let hs = hasSolo pMap                   putStrLn $ concatMap (showKV hs) $ Map.toList pMap   where showKV :: Bool -> (PatId, PlayState) -> String-        showKV True  (k, (PlayState _  _ True _)) = k ++ " - solo\n"+        showKV True  (k, (PlayState {solo = True})) = k ++ " - solo\n"         showKV True  (k, _) = "(" ++ k ++ ")\n"-        showKV False (k, (PlayState _ False _ _)) = k ++ "\n"+        showKV False (k, (PlayState {solo = False})) = k ++ "\n"         showKV False (k, _) = "(" ++ k ++ ") - muted\n"  -- Evaluation of pat is forced so exceptions are picked up here, before replacing the existing pattern.+ streamReplace :: Show a => Stream -> a -> ControlPattern -> IO () streamReplace s k !pat   = E.catch (do let x = queryArc pat (Arc 0 0)@@ -305,7 +383,6 @@                 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) -> hPutStrLn stderr $ "Error in pattern: " ++ show e@@ -328,45 +405,6 @@ streamUnsolo :: Show a => Stream -> a -> IO () streamUnsolo s k = withPatId s (show k) (\x -> x {solo = False}) -streamOnce :: Stream -> ControlPattern -> IO ()-streamOnce st p = do i <- getStdRandom $ randomR (0, 8192)-                     streamFirst st $ rotL (toRational (i :: Int)) p--streamFirst :: Stream -> ControlPattern -> IO ()-streamFirst st p-  = do sMap <- readMVar (sInput st)-       tempo <- readMVar (sTempoMV st)-       now <- O.time-       let fakeTempo = T.Tempo {T.cps = T.cps tempo,-                                T.atCycle = 0,-                                T.atTime = now,-                                T.paused = False,-                                T.nudged = 0-                               }-           sMap' = Map.insert "_cps" (pure $ VF $ T.cps tempo) sMap-           es = filter eventHasOnset $ query p (State {arc = (Arc 0 1),-                                                       controls = sMap'-                                                      }-                                               )-           -- there should always be a whole (due to the eventHasOnset filter)-           at e = sched fakeTempo $ start $ wholeOrPart e-           -- there should always be a whole (due to the eventHasOnset filter)-           on e = sched tempo $ start $ wholeOrPart e-           cpsChanges = map (\e -> (on e - now, Map.lookup "cps" $ value e)) es-           config = sConfig st-           messages target =-             catMaybes $ map (\e -> do m <- toMessage config (at e + (oLatency target)) target fakeTempo e-                                       return $ (at e, m)-                             ) es-       mapM_ (\(Cx target udp) ->-                 E.catch (mapM_ (send target (oLatency target) udp) (messages target))-                 (\(e ::E.SomeException)-                   -> putStrLn $ "Failed to send. Is the '" ++ oName target ++ "' target running? " ++ show e-                 )-             ) (sCxs st)-       mapM_ (doCps $ sTempoMV st) cpsChanges-       return ()- withPatId :: Stream -> PatId -> (PlayState -> PlayState) -> IO () withPatId s k f = withPatIds s [k] f @@ -375,26 +413,21 @@   = do playMap <- takeMVar $ sPMapMV s        let pMap' = foldr (Map.update (\x -> Just $ f x)) playMap ks        putMVar (sPMapMV s) pMap'-       calcOutput s        return ()  -- TODO - is there a race condition here? streamMuteAll :: Stream -> IO ()-streamMuteAll s = do modifyMVar_ (sOutput s) $ return . const silence-                     modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {mute = True})+streamMuteAll s = modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {mute = True})  streamHush :: Stream -> IO ()-streamHush s = do modifyMVar_ (sOutput s) $ return . const silence-                  modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {pattern = silence, history = silence:history x})+streamHush s = 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})-                       calcOutput s-+streamUnmuteAll s = modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {mute = False})  streamAll :: Stream -> (ControlPattern -> ControlPattern) -> IO () streamAll s f = do _ <- swapMVar (sGlobalFMV s) f-                   calcOutput s+                   return ()  streamSet :: Valuable a => Stream -> String -> Pattern a -> IO () streamSet s k pat = do sMap <- takeMVar $ sInput s@@ -416,37 +449,6 @@  streamSetR :: Stream -> String -> Pattern Rational -> IO () streamSetR = streamSet--calcOutput :: Stream -> IO ()-calcOutput s = do pMap <- readMVar $ sPMapMV s-                  globalF <- (readMVar $ sGlobalFMV s)-                  _ <- swapMVar (sOutput s) $ globalF $ toPat $ pMap-                  return ()-  where toPat pMap =-          stack $ map pattern $ filter (\pState -> if hasSolo pMap-                                                   then solo pState-                                                   else not (mute pState)-                                       ) (Map.elems pMap)--startTidal :: OSCTarget -> Config -> IO Stream-startTidal target config = startMulti [target] config--startMulti :: [OSCTarget] -> Config -> IO Stream-startMulti targets config =-  do sMapMV <- newMVar (Map.empty :: StateMap)-     listenTid <- ctrlListen sMapMV config-     (pMV, tempoMV, cxs) <- startStream config sMapMV targets-     pMapMV <- newMVar Map.empty-     globalFMV <- newMVar id-     return $ Stream {sConfig = config,-                      sInput = sMapMV,-                      sListenTid = listenTid,-                      sOutput = pMV,-                      sPMapMV = pMapMV,-                      sTempoMV = tempoMV,-                      sGlobalFMV = globalFMV,-                      sCxs = cxs-                     }  ctrlListen :: MVar StateMap -> Config -> IO (Maybe ThreadId) ctrlListen sMapMV c
src/Sound/Tidal/Tempo.hs view
@@ -34,6 +34,7 @@                     nowArc  :: P.Arc,                     starting :: Bool                    }+  deriving Show  changeTempo :: MVar Tempo -> (O.Time -> Tempo -> Tempo) -> IO Tempo changeTempo tempoMV f = do t <- O.time@@ -60,7 +61,7 @@  defaultCps :: O.Time defaultCps = 0.5625-  + defaultTempo :: O.Time -> O.UDP -> N.SockAddr -> Tempo defaultTempo t local remote = Tempo {atTime   = t,                                      atCycle  = 0,@@ -89,23 +90,23 @@ getCurrentCycle t = (readMVar t) >>= (cyclesNow) >>= (return . toRational) -} -clocked :: Config -> (MVar Tempo -> State -> IO ()) -> IO (MVar Tempo, [ThreadId])-clocked config callback+clocked :: Config -> MVar Tempo -> (State -> IO ()) -> IO [ThreadId]+clocked config tempoMV callback   = do s <- O.time        -- TODO - do something with thread id        _ <- serverListen config-       (tempoMV, listenTid) <- clientListen config s+       listenTid <- clientListen config tempoMV s        let st = State {ticks = 0,                        start = s,                        nowTimespan = (s, s + frameTimespan),                        nowArc = P.Arc 0 0,                        starting = True                       }-       clockTid <- forkIO $ loop tempoMV st-       return (tempoMV, [listenTid, clockTid])+       clockTid <- forkIO $ loop st+       return [listenTid, clockTid]   where frameTimespan :: Double         frameTimespan = cFrameTimespan config-        loop tempoMV st =+        loop st =           do -- putStrLn $ show $ nowArc ts              tempo <- readMVar tempoMV                             t <- O.time@@ -130,15 +131,15 @@                            starting = not (synched tempo)                           }              when ahead $ writeError $ "skip: " ++ show (actualTick - ticks st)-             callback tempoMV st'+             callback st'              {-putStrLn ("actual tick: " ++ show actualTick                        ++ " old tick: " ++ show (ticks st)                        ++ " new tick: " ++ show newTick                       )-}-             loop tempoMV st'+             loop st' -clientListen :: Config -> O.Time -> IO (MVar Tempo, ThreadId)-clientListen config s =+clientListen :: Config -> MVar Tempo -> O.Time -> IO (ThreadId)+clientListen config tempoMV s =   do -- Listen on random port      let tempoClientPort = cTempoClientPort config          hostname = cTempoAddr config@@ -148,13 +149,13 @@      let (N.SockAddrInet _ a) = N.addrAddress remote_addr          remote = N.SockAddrInet (fromIntegral port) a          t = defaultTempo s local remote+     putMVar tempoMV t      -- Send to clock port from same port that's listened to      O.sendTo local (O.p_message "/hello" []) remote      -- Make tempo mvar-     tempoMV <- newMVar t      -- Listen to tempo changes      tempoChild <- forkIO $ listenTempo local tempoMV-     return (tempoMV, tempoChild)+     return tempoChild  sendTempo :: Tempo -> IO () sendTempo tempo = O.sendTo (localUDP tempo) (O.p_bundle (atTime tempo) [m]) (remoteAddr tempo)
src/Sound/Tidal/Transition.hs view
@@ -28,7 +28,6 @@      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     appendPat flag = if flag then (pat:) else id
src/Sound/Tidal/UI.hs view
@@ -5,14 +5,13 @@ import           Prelude hiding ((<*), (*>))  import           Data.Char (digitToInt, isDigit, ord)-import           Data.Bits (testBit, Bits)+import           Data.Bits (testBit, Bits, xor, shiftL, shiftR) -- import           System.Random (randoms, mkStdGen)-import           System.Random.MWC-import           Control.Monad.ST-import           Control.Monad.Primitive (PrimState, PrimMonad)-import qualified Data.Vector as V-import           Data.Word (Word32)-import           Data.Ratio ((%),numerator,denominator)+-- import           Control.Monad.ST+-- import           Control.Monad.Primitive (PrimState, PrimMonad)+-- import qualified Data.Vector as V+-- import           Data.Word (Word32)+import           Data.Ratio ((%)) import           Data.List (sort, sortOn, findIndices, elemIndex, groupBy, transpose, intercalate, findIndex) import           Data.Maybe (isJust, fromJust, fromMaybe, mapMaybe) import qualified Data.Text as T@@ -29,21 +28,33 @@ -- * UI  -- | Randomisation-timeToSeed :: (PrimMonad m, Real a) => a -> m (Gen (PrimState m))-timeToSeed x = do-  let x' = toRational (x*x) / 1000000-  let n' = fromIntegral $ numerator x'-  let d' = fromIntegral $ denominator x'-  initialize (V.fromList [n',d'] :: V.Vector Word32) -timeToRand :: RealFrac a => a -> Double-timeToRand x = runST $ do seed <- timeToSeed x-                          uniform seed+-- cf. George Marsaglia (2003). "Xorshift RNGs". Journal of Statistical Software 8:14.+-- https://www.jstatsoft.org/article/view/v008i14+xorwise :: Int -> Int+xorwise x =+  let a = xor (shiftL x 13) x+      b = xor (shiftR a 17) a+  in xor (shiftL b 5) b -timeToRands :: RealFrac a => a -> Int -> [Double]-timeToRands x n = V.toList $ runST $ do seed <- timeToSeed x-                                        uniformVector seed n+-- stretch 300 cycles over the range of [0,2**29 == 536870912) then apply the xorshift algorithm+timeToIntSeed :: RealFrac a => a -> Int+timeToIntSeed = xorwise . truncate . (* 536870912) . snd . (properFraction :: (RealFrac a => a -> (Int,a))) . (/ 300) +intSeedToRand :: Fractional a => Int -> a+intSeedToRand = (/ 536870912) . realToFrac . (`mod` 536870912)++timeToRand :: (RealFrac a, Fractional b) => a -> b+timeToRand = intSeedToRand . timeToIntSeed++timeToRands :: (RealFrac a, Fractional b) => a -> Int -> [b]+timeToRands t n = timeToRands' (timeToIntSeed t) n++timeToRands' :: Fractional a => Int -> Int -> [a]+timeToRands' seed n+  | n <= 0 = []+  | otherwise = (intSeedToRand seed) : (timeToRands' (xorwise seed) (n-1))+ {-|  `rand` generates a continuous pattern of (pseudo-)random numbers between `0` and `1`.@@ -80,7 +91,7 @@ @ -} rand :: Fractional a => Pattern a-rand = Pattern (\(State a@(Arc s e) _) -> [Event (Context []) Nothing a (realToFrac $ timeToRand $ (e + s)/2)])+rand = Pattern (\(State a@(Arc s e) _) -> [Event (Context []) Nothing a (realToFrac $ (timeToRand ((e + s)/2) :: Double))])  {- | Just like `rand` but for whole numbers, `irand n` generates a pattern of (pseudo-) random whole numbers between `0` to `n-1` inclusive. Notably used to pick a random samples from a folder:@@ -262,7 +273,7 @@ {- | @someCyclesBy@ is a cycle-by-cycle version of @sometimesBy@. It has a `someCycles = someCyclesBy 0.5` alias -} someCyclesBy :: Pattern Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a-someCyclesBy pd f p = do {d <- pd; _someCyclesBy d f p}+someCyclesBy pd f pat = innerJoin $ (\d -> _someCyclesBy d f pat) <$> pd  _someCyclesBy :: Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a _someCyclesBy x = when test@@ -1069,13 +1080,13 @@ {- | @runMarkov n tmat xi seed@ generates a Markov chain (as a list) of length @n@ using the transition matrix @tmat@ starting from initial state @xi@, starting with random numbers generated from @seed@-Each entry in the chain is the index of state (starting from zero). +Each entry in the chain is the index of state (starting from zero). Each row of the matrix will be automatically normalized. For example: @ runMarkov 8 [[2,3], [1,3]] 0 0 @ will produce a two-state chain 8 steps long, from initial state @0@, where the-transition probability from state 0->0 is 2/5, 0->1 is 3/5, 1->0 is 1/4, and +transition probability from state 0->0 is 2/5, 0->1 is 3/5, 1->0 is 1/4, and 1->1 is 3/4.  -} runMarkov :: Int -> [[Double]] -> Int -> Time -> [Int] runMarkov n tp xi seed = reverse $ (iterate (markovStep $ renorm) [xi])!! (n-1) where@@ -1102,7 +1113,7 @@ markovPat = tParam2 _markovPat  _markovPat :: Int -> Int -> [[Double]] -> Pattern Int-_markovPat n xi tp = splitQueries $ Pattern (\(State a@(Arc s _) _) -> +_markovPat n xi tp = splitQueries $ Pattern (\(State a@(Arc s _) _) ->   queryArc (listToPat $ runMarkov n tp xi (sam s)) a)  {-|@@ -1298,7 +1309,7 @@   splitQueries $ Pattern (\(State a@(Arc s _) _) -> events a $ sam s)   where events a seed = mapMaybe toEv $ zip arcs shuffled           where shuffled = map snd $ sortOn fst $ zip rs [0 .. (n'-1)]-                rs = timeToRands seed n'+                rs = timeToRands seed n' :: [Double]                 arcs = zipWith Arc fractions (tail fractions)                 fractions = map (+ (sam $ start a)) [0, 1 / fromIntegral n' .. 1]                 toEv (a',v) = do a'' <- subArc a a'@@ -1344,7 +1355,7 @@ -- | @arpeggiate@ finds events that share the same timespan, and spreads -- them out during that timespan, so for example @arpeggiate "[bd,sn]"@ -- gets turned into @"bd sn"@. Useful for creating arpeggios/broken chords.-arpeggiate :: Pattern a -> Pattern a +arpeggiate :: Pattern a -> Pattern a arpeggiate = arpWith id  -- | Shorthand alias for arpeggiate@@ -1813,10 +1824,10 @@ snowball :: Int -> (Pattern a -> Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a snowball depth combinationFunction f pattern = cat $ take depth $ scanl combinationFunction pattern $ iterate f pattern -{- @soak@ | +{- @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 +    this can be used to create a pattern that wanders away from     the original pattern by continually adding random numbers     d1 $ note (scale "hexDorian" mutateBy (+ (range -1 1 $ irand 2)) 8 $ "0 1 . 2 3 4") # s "gtr" -}@@ -1825,7 +1836,7 @@  deconstruct :: Int -> Pattern String -> String deconstruct n p = intercalate " " $ map showStep $ toList p-  where +  where     showStep :: [String] -> String     showStep [] = "~"     showStep [x] = x@@ -1866,10 +1877,14 @@              return (Event (combineContexts [ci,co]) (Just w') p' v)         munge _ _ _ _ = Nothing -chew :: Int -> Pattern Int -> ControlPattern  -> ControlPattern-chew n ipat pat = (squeezeJoinUp $ zoompat <$> ipat) |/ P.speed (pure $ fromIntegral n)+_chew :: Int -> Pattern Int -> ControlPattern  -> ControlPattern+_chew n ipat pat = (squeezeJoinUp $ zoompat <$> ipat) |/ P.speed (pure $ fromIntegral n)   where zoompat i = zoom (i'/(fromIntegral n), (i'+1)/(fromIntegral n)) (pat)            where i' = fromIntegral $ i `mod` n++-- TODO maybe _chew could pattern the first parameter directly..+chew :: Pattern Int -> Pattern Int -> ControlPattern  -> ControlPattern+chew npat ipat pat = innerJoin $ (\n -> _chew n ipat pat) <$> npat  __binary :: Data.Bits.Bits b => Int -> b -> [Bool] __binary n num = map (testBit num) $ reverse [0 .. n-1]
src/Sound/Tidal/Version.hs view
@@ -1,4 +1,4 @@ module Sound.Tidal.Version where  tidal_version :: String-tidal_version = "1.4.9"+tidal_version = "1.5.0"
test/Sound/Tidal/PatternTest.hs view
@@ -9,7 +9,6 @@  import           Data.Ratio -import           Sound.Tidal.Control import           Sound.Tidal.Core import           Sound.Tidal.Pattern import           Sound.Tidal.UI
test/Sound/Tidal/UITest.hs view
@@ -81,25 +81,25 @@     describe "rand" $ do       it "generates a (pseudo-)random number between zero & one" $ do         it "at the start of a cycle" $-          (queryArc rand (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (0.5000844 :: Float)]+          (queryArc rand (Arc 0 0)) `shouldBe` [Event (Context []) Nothing (Arc 0 0) (0 :: Float)]         it "at 1/4 of a cycle" $-          (queryArc rand (Arc 0.25 0.25)) `shouldBe` -            [Event (Context []) Nothing (Arc 0.25 0.25) (0.8587171 :: Float)]+          (queryArc rand (Arc 0.25 0.25)) `shouldBe`+            [Event (Context []) Nothing (Arc 0.25 0.25) (0.6295689214020967:: Float)]         it "at 3/4 of a cycle" $-          (queryArc rand (Arc 0.75 0.75)) `shouldBe` -          [Event (Context []) Nothing (Arc 0.75 0.75) (0.7277789 :: Float)]+          (queryArc rand (Arc 0.75 0.75)) `shouldBe`+          [Event (Context []) Nothing (Arc 0.75 0.75) (0.20052618719637394 :: Float)]      describe "range" $ do       describe "scales a pattern to the supplied range" $ do         describe "from 3 to 4" $ do           it "at the start of a cycle" $-            (queryArc (Sound.Tidal.UI.range 3 4 saw) (Arc 0 0)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 3 4 saw) (Arc 0 0)) `shouldBe`               [Event (Context []) Nothing (Arc 0 0) (3 :: Float)]           it "at 1/4 of a cycle" $             (queryArc (Sound.Tidal.UI.range 3 4 saw) (Arc 0.25  0.25)) `shouldBe`               [Event (Context []) Nothing (Arc 0.25 0.25) (3.25 :: Float)]           it "at 3/4 of a cycle" $-            (queryArc (Sound.Tidal.UI.range 3 4 saw) (Arc 0.75 0.75)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 3 4 saw) (Arc 0.75 0.75)) `shouldBe`               [Event (Context []) Nothing (Arc 0.75 0.75) (3.75 :: Float)]          describe "from -1 to 1" $ do@@ -109,21 +109,21 @@          describe "from 4 to 2" $ do           it "at the start of a cycle" $-            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0 0)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0 0)) `shouldBe`               [Event (Context []) Nothing (Arc 0 0) (4 :: Float)]           it "at 1/4 of a cycle" $-            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0.25 0.25)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0.25 0.25)) `shouldBe`               [Event (Context []) Nothing (Arc 0.25 0.25) (3.5 :: Float)]           it "at 3/4 of a cycle" $-            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0.75 0.75)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 4 2 saw) (Arc 0.75 0.75)) `shouldBe`               [Event (Context []) Nothing (Arc 0.75 0.75) (2.5 :: Float)]          describe "from 10 to 10" $ do           it "at 1/2 of a cycle" $-            (queryArc (Sound.Tidal.UI.range 10 10 saw) (Arc 0.5 0.5)) `shouldBe` +            (queryArc (Sound.Tidal.UI.range 10 10 saw) (Arc 0.5 0.5)) `shouldBe`               [Event (Context []) Nothing (Arc 0.5 0.5) (10 :: Float)] -    describe "rot" $ do +    describe "rot" $ do       it "rotates values in a pattern irrespective of structure" $         property $ comparePD (Arc 0 2)           (rot 1 "a ~ b c" :: Pattern String)@@ -182,7 +182,7 @@         compareP (Arc 0 1)           (euclidFull 3 8 "bd" silence)           ("bd(3,8)" :: Pattern String)-          +     describe "snowball" $ do       let testPattern = ("1 2 3 4"::Pattern Int)       it "acummulates a transform version of a pattern and appends the result - addition" $ do@@ -264,9 +264,9 @@           ("0" |+ chooseBy (sig fromRational) [0,1,2,3])           ("2"::Pattern Int) -    describe "arpeggiate" $ do -      it "can arpeggiate" $ do -         compareP (Arc 0 1) +    describe "arpeggiate" $ do+      it "can arpeggiate" $ do+         compareP (Arc 0 1)            (arpeggiate ("[bd, sn] [hh:1, cp]" :: Pattern String))            ("bd sn hh:1 cp" :: Pattern String)       it "can arpeggiate" $ do
tidal.cabal view
@@ -1,5 +1,5 @@ name:                tidal-version:             1.4.9+version:             1.5.0 synopsis:            Pattern language for improvised music -- description: homepage:            http://tidalcycles.org/@@ -56,7 +56,6 @@     , text < 1.3     , parsec >= 3.1.12 && < 3.2     , network < 3.2-    , mwc-random < 0.15     , vector < 0.13     , bifunctors < 5.6     , transformers >= 0.5 && < 0.5.7@@ -96,13 +95,13 @@  benchmark bench-speed   type:             exitcode-stdio-1.0-  main-is:          Main.hs +  main-is:          Main.hs   hs-source-dirs:   bench/Speed   other-modules: Tidal.PatternB                  Tidal.CoreB                  Tidal.UIB                  Tidal.Inputs-  build-depends:    +  build-depends:                 base == 4.*               , criterion               , tidal@@ -112,10 +111,10 @@  benchmark bench-memory   type:    exitcode-stdio-1.0-  main-is: Main.hs -  hs-source-dirs: bench/Memory +  main-is: Main.hs+  hs-source-dirs: bench/Memory   other-modules: Tidal.UIB-                 Tidal.Inputs +                 Tidal.Inputs   build-depends:                base == 4.*              , weigh
tidal.el view
@@ -66,7 +66,7 @@              ("separator" . "\\")              ))           ((or (string-equal system-type "darwin") (string-equal system-type "gnu/linux"))-           '(("path" . "ghc-pkg field -f ~/.cabal/store/ghc-$(ghc --numeric-version)/package.db tidal data-dir")+           '(("path" . "ghc-pkg field tidal data-dir")              ("separator" . "/")              ))           )