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 +3/−1
- CHANGELOG.md +14/−0
- src/Sound/Tidal/Control.hs +6/−322
- src/Sound/Tidal/Core.hs +314/−2
- src/Sound/Tidal/Params.hs +8/−1
- src/Sound/Tidal/ParseBP.hs +21/−2
- src/Sound/Tidal/Pattern.hs +2/−2
- src/Sound/Tidal/Show.hs +1/−1
- src/Sound/Tidal/Stream.hs +262/−260
- src/Sound/Tidal/Tempo.hs +14/−13
- src/Sound/Tidal/Transition.hs +0/−1
- src/Sound/Tidal/UI.hs +46/−31
- src/Sound/Tidal/Version.hs +1/−1
- test/Sound/Tidal/PatternTest.hs +0/−1
- test/Sound/Tidal/UITest.hs +16/−16
- tidal.cabal +6/−7
- tidal.el +1/−1
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" . "/") )) )