packages feed

tidal 1.7.8 → 1.7.9

raw patch · 21 files changed

+476/−213 lines, 21 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Sound.Tidal.Chords: m11sharp :: Num a => [a]
- Sound.Tidal.Chords: m7sharp5flat9 :: Num a => [a]
- Sound.Tidal.Chords: m9sharp5 :: Num a => [a]
- Sound.Tidal.Core: (<>) :: Pattern a -> Pattern a -> Pattern a
- Sound.Tidal.ParseBP: parseRhythm :: Parseable a => MyParser (TPat a) -> String -> Either ParseError (TPat a)
- Sound.Tidal.Stream: withPatId :: Stream -> PatId -> (PlayState -> PlayState) -> IO ()
- Sound.Tidal.Utils: deltaMini :: String -> String
+ Sound.Tidal.Chords: minor11sharp :: Num a => [a]
+ Sound.Tidal.Chords: minor7sharp5flat9 :: Num a => [a]
+ Sound.Tidal.Chords: minor9sharp5 :: Num a => [a]
+ Sound.Tidal.Core: timecat :: [(Time, Pattern a)] -> Pattern a
+ Sound.Tidal.ID: ID :: String -> ID
+ Sound.Tidal.ID: [fromID] :: ID -> String
+ Sound.Tidal.ID: instance Data.String.IsString Sound.Tidal.ID.ID
+ Sound.Tidal.ID: instance GHC.Num.Num Sound.Tidal.ID.ID
+ Sound.Tidal.ID: newtype ID
+ Sound.Tidal.Params: number :: Pattern Note -> ControlPattern
+ Sound.Tidal.ParseBP: isInt :: RealFrac a => a -> Bool
+ Sound.Tidal.ParseBP: pFraction :: RealFrac a => a -> MyParser Rational
+ Sound.Tidal.ParseBP: pRatioSingleChar :: Fractional a => Char -> a -> MyParser a
+ Sound.Tidal.Pattern: class Moddable a
+ Sound.Tidal.Pattern: class Stringy a
+ Sound.Tidal.Pattern: deltaMini :: String -> String
+ Sound.Tidal.Pattern: gmod :: Moddable a => a -> a -> a
+ Sound.Tidal.Pattern: instance GHC.Base.Monoid (Sound.Tidal.Pattern.Pattern a)
+ Sound.Tidal.Pattern: instance GHC.Base.Semigroup (Sound.Tidal.Pattern.Pattern a)
+ Sound.Tidal.Pattern: instance GHC.Float.Floating Sound.Tidal.Pattern.ValueMap
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Moddable GHC.Real.Rational
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Moddable GHC.Types.Double
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Moddable GHC.Types.Int
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Moddable Sound.Tidal.Pattern.Note
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Moddable Sound.Tidal.Pattern.ValueMap
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Stringy (Sound.Tidal.Pattern.Pattern a)
+ Sound.Tidal.Pattern: instance Sound.Tidal.Pattern.Stringy GHC.Base.String
+ Sound.Tidal.Stream: streamSilence :: Stream -> ID -> IO ()
+ Sound.Tidal.UI: _inside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
+ Sound.Tidal.UI: _outside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
+ Sound.Tidal.UI: _wedge :: Time -> Pattern a -> Pattern a -> Pattern a
+ Sound.Tidal.UI: necklace :: Rational -> [Int] -> Pattern Bool
- Sound.Tidal.Core: (%|) :: Real a => Pattern a -> Pattern a -> Pattern a
+ Sound.Tidal.Core: (%|) :: Moddable a => Pattern a -> Pattern a -> Pattern a
- Sound.Tidal.Core: (|%) :: Real a => Pattern a -> Pattern a -> Pattern a
+ Sound.Tidal.Core: (|%) :: Moddable a => Pattern a -> Pattern a -> Pattern a
- Sound.Tidal.Core: (|%|) :: (Applicative a, Real b) => a b -> a b -> a b
+ Sound.Tidal.Core: (|%|) :: (Applicative a, Moddable b) => a b -> a b -> a b
- Sound.Tidal.ParseBP: pSequence :: Parseable a => MyParser (TPat a) -> GenParser Char Int (TPat a)
+ Sound.Tidal.ParseBP: pSequence :: Parseable a => MyParser (TPat a) -> MyParser (TPat a)
- Sound.Tidal.Pattern: deltaContext :: Int -> Int -> Pattern a -> Pattern a
+ Sound.Tidal.Pattern: deltaContext :: Stringy a => Int -> Int -> a -> a
- Sound.Tidal.Stream: streamMute :: Show a => Stream -> a -> IO ()
+ Sound.Tidal.Stream: streamMute :: Stream -> ID -> IO ()
- Sound.Tidal.Stream: streamMutes :: Show a => Stream -> [a] -> IO ()
+ Sound.Tidal.Stream: streamMutes :: Stream -> [ID] -> IO ()
- Sound.Tidal.Stream: streamReplace :: Show a => Stream -> a -> ControlPattern -> IO ()
+ Sound.Tidal.Stream: streamReplace :: Stream -> ID -> ControlPattern -> IO ()
- Sound.Tidal.Stream: streamSolo :: Show a => Stream -> a -> IO ()
+ Sound.Tidal.Stream: streamSolo :: Stream -> ID -> IO ()
- Sound.Tidal.Stream: streamUnmute :: Show a => Stream -> a -> IO ()
+ Sound.Tidal.Stream: streamUnmute :: Stream -> ID -> IO ()
- Sound.Tidal.Stream: streamUnsolo :: Show a => Stream -> a -> IO ()
+ Sound.Tidal.Stream: streamUnsolo :: Stream -> ID -> IO ()
- Sound.Tidal.Stream: withPatIds :: Stream -> [PatId] -> (PlayState -> PlayState) -> IO ()
+ Sound.Tidal.Stream: withPatIds :: Stream -> [ID] -> (PlayState -> PlayState) -> IO ()
- Sound.Tidal.Transition: transition :: Show a => Stream -> Bool -> (Time -> [ControlPattern] -> ControlPattern) -> a -> ControlPattern -> IO ()
+ Sound.Tidal.Transition: transition :: Stream -> Bool -> (Time -> [ControlPattern] -> ControlPattern) -> ID -> ControlPattern -> IO ()
- Sound.Tidal.UI: perlin :: Pattern Double
+ Sound.Tidal.UI: perlin :: Fractional a => Pattern a
- Sound.Tidal.UI: perlinWith :: Pattern Double -> Pattern Double
+ Sound.Tidal.UI: perlinWith :: Fractional a => Pattern Double -> Pattern a
- Sound.Tidal.UI: wedge :: Time -> Pattern a -> Pattern a -> Pattern a
+ Sound.Tidal.UI: wedge :: Pattern Time -> Pattern a -> Pattern a -> Pattern a

Files

BootTidal.hs view
@@ -77,3 +77,5 @@  :set prompt "tidal> " :set prompt-cont ""++default (Pattern String, Integer, Double)
CHANGELOG.md view
@@ -1,5 +1,39 @@ # TidalCycles log of changes +## 1.7.9 - Tama++### What's Changed+* Show lists of values by @polymorphicengine in https://github.com/tidalcycles/Tidal/pull/838+* Port listener wiki content as README by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/851+* Chords - consistent naming cont'd by @cleary in https://github.com/tidalcycles/Tidal/pull/840+* Formalise pattern IDs by @mindofmatthew in https://github.com/tidalcycles/Tidal/pull/807+* Switch to non-blocking pMap updates for transitions by @bgold-cosmos in https://github.com/tidalcycles/Tidal/pull/858+* Add listener build workflows by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/852+* Fix tidal-listener install process by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/861+* Fix mod by @bgold-cosmos in https://github.com/tidalcycles/Tidal/pull/860+* Fix stack ci using last lts resolver by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/862+* Make Pattern instance Monoid by @fbous in https://github.com/tidalcycles/Tidal/pull/865+* Added OSC playback control for silencing individual patterns by @njanssen in https://github.com/tidalcycles/Tidal/pull/863+* Use better show instances for notes & rationals by @mindofmatthew in https://github.com/tidalcycles/Tidal/pull/857+* tidal-listener: Separate build from release by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/866+* Exponential double by @ndr-brt in https://github.com/tidalcycles/Tidal/pull/871+* Comments by @JeffreyBenjaminBrown in https://github.com/tidalcycles/Tidal/pull/877+* Fix for classic dirt support by @yaxu https://github.com/tidalcycles/Tidal/commit/15b5b8b91af08ebad39efe2a7e0712b21f606ca4+* New alias `number` for `n` by @yaxu https://github.com/tidalcycles/Tidal/commit/ac0be63d686ab37f7b2dcd440d4bd8f3898453e8+* Allow "0..8" to be parsed as a range of doubles in mininotation by @yaxu https://github.com/tidalcycles/Tidal/commit/55f8ad9b0091b43fdd364eced25bc9c655d157cc+* Bugfix for `timeLoop` by @yaxu in https://github.com/tidalcycles/Tidal/commit/29f28ed637a7c17ad2b22558d097a694da604e2d+* Retire pre ghc 8.4 support by @yaxu +* Pattern first parameter of `wedge` by @yaxu+* Allow `|**|` et al to work on control patterns by @yaxu in https://github.com/tidalcycles/Tidal/commit/7142775c2039cae4cde9bdd6f68b3e0cbae2de9d+* Add alias `timecat` for timeCat` by @yaxu in https://github.com/tidalcycles/Tidal/commit/363889bdc963d9357daf1893d18ab9dfc33ca5ac++### New Contributors+* @fbous made their first contribution in https://github.com/tidalcycles/Tidal/pull/865+* @njanssen made their first contribution in https://github.com/tidalcycles/Tidal/pull/863+* @JeffreyBenjaminBrown made their first contribution in https://github.com/tidalcycles/Tidal/pull/877++**Full Changelog**: https://github.com/tidalcycles/Tidal/compare/1.7.8...1.7.9+ ## 1.7.8 - Ayatakedai    * Add pattern id to patterns as _id_, and send it with /code/highlights 
README.md view
@@ -7,8 +7,8 @@ For documentation, mailing list and more info see here:     https://tidalcycles.org/ -You can help speed up Tidal development by sending coffee here:  -  https://ko-fi.com/yaxulive#+You can help speed up Tidal development by contributing to the collective fund here:  +  https://opencollective.com/tidalcycles  (c) Alex McLean and contributors, 2021 
bench/Memory/Tidal/Inputs.hs view
@@ -1,38 +1,45 @@ {-# LANGUAGE OverloadedStrings #-} -module Tidal.Inputs where +module Tidal.Inputs where  import Sound.Tidal.Pattern import Sound.Tidal.Core-import Sound.Tidal.ParseBP-import Sound.Tidal.Core+import Sound.Tidal.ParseBP() import Sound.Tidal.Params import Sound.Tidal.Control import Sound.Tidal.UI-import Weigh +import Weigh -columns :: Weigh () -columns = setColumns [Case, Allocated, Max, Live, GCs] +columns :: Weigh ()+columns = setColumns [Case, Allocated, Max, Live, GCs] -{- Pattern inputs -} -xs3 = [1..10^3]-xs4 = [1..10^4]-xs5 = [1..10^5]-xs6 = [1..10^6]+{- Pattern inputs -}+xs3 :: [Time]+xs3 = [1..10000] +xs4 :: [Time]+xs4 = [1..100000]++xs5 :: [Time]+xs5 = [1..1000000]++xs6 :: [Time]+xs6 = [1..10000000]++xsA :: [Time] xsA = [500000..1500000]  catPattSmall :: [Pattern Time] catPattSmall = pure <$> xs3  catPattMed :: [Pattern Time]-catPattMed = pure <$> xs4 +catPattMed = pure <$> xs4  catPattMedB :: [Pattern Time] catPattMedB = pure <$> xs5  catPattBig :: [Pattern Time]-catPattBig = pure <$> xs6 +catPattBig = pure <$> xs6  timeCatMed :: [(Time, Pattern Time)] timeCatMed = zip xs5 catPattMed@@ -41,23 +48,26 @@ timeCatBig = zip xs6 catPattBig  appendBig :: [Pattern Time]-appendBig = pure <$> xsA +appendBig = pure <$> xsA  pattApp1 :: Pattern [Time]-pattApp1 = sequence catPattBig +pattApp1 = sequence catPattBig  pattApp2 :: Pattern [Time] pattApp2 = sequence appendBig  {- Arc Inputs -}-arcFunc :: Arc -> Arc -arcFunc (Arc s e) = Arc (s * 2) (e * 4) +arcFunc :: Arc -> Arc+arcFunc (Arc st en) = Arc (st * 2) (en * 4) -wqaMed = fromList xs5 +wqaMed :: Pattern Time+wqaMed = fromList xs5++wqaBig :: Pattern Time wqaBig = fromList xs6 -{- fix inputs -} -fixArg1 :: ControlPattern +{- fix inputs -}+fixArg1 :: ControlPattern fixArg1 = pF "cc64" 1  fixArg2 :: ControlPattern@@ -76,8 +86,8 @@     #  pF "cc69" (cF 0 "69")  {- Euclid inputs -}-ecA1 :: [Pattern Int] -ecA1 = [1, 10^5]+ecA1 :: [Pattern Int]+ecA1 = [1, 1000000] -ecA2 :: Pattern String +ecA2 :: Pattern String ecA2 = "x"
bench/Speed/Tidal/Inputs.hs view
@@ -1,34 +1,40 @@ {-# LANGUAGE OverloadedStrings #-} -module Tidal.Inputs where +module Tidal.Inputs where  import Sound.Tidal.Pattern import Sound.Tidal.Core-import Sound.Tidal.ParseBP-import Sound.Tidal.Core+import Sound.Tidal.ParseBP() import Sound.Tidal.Params-import Sound.Tidal.Control import Sound.Tidal.UI -{- Pattern inputs -} -xs3 = [1..10^3]-xs4 = [1..10^4]-xs5 = [1..10^5]-xs6 = [1..10^6]+{- Pattern inputs -}+xs3 :: [Time]+xs3 = [1..10000] +xs4 :: [Time]+xs4 = [1..100000]++xs5 :: [Time]+xs5 = [1..1000000]++xs6 :: [Time]+xs6 = [1..10000000]++xsA :: [Time] xsA = [500000..1500000]  catPattSmall :: [Pattern Time] catPattSmall = pure <$> xs3  catPattMed :: [Pattern Time]-catPattMed = pure <$> xs4 +catPattMed = pure <$> xs4  catPattMedB :: [Pattern Time] catPattMedB = pure <$> xs5  catPattBig :: [Pattern Time]-catPattBig = pure <$> xs6 +catPattBig = pure <$> xs6  timeCatMed :: [(Time, Pattern Time)] timeCatMed = zip xs5 catPattMed@@ -37,32 +43,32 @@ timeCatBig = zip xs6 catPattBig  appendBig :: [Pattern Time]-appendBig = pure <$> xsA +appendBig = pure <$> xsA  pattApp1 :: Pattern [Time]-pattApp1 = sequence catPattBig +pattApp1 = sequence catPattBig  pattApp2 :: Pattern [Time] pattApp2 = sequence appendBig  {- Arc Inputs -}-arcFunc :: Arc -> Arc -arcFunc (Arc s e) = Arc (s * 2) (e * 4) +arcFunc :: Arc -> Arc+arcFunc (Arc st en) = Arc (st * 2) (en * 4)  wqaMed :: Pattern Time-wqaMed = fromList xs5 +wqaMed = fromList xs5  wqaBig :: Pattern Time wqaBig = fromList xs6 -{- fix inputs -} -fixArg1 :: ControlPattern +{- fix inputs -}+fixArg1 :: ControlPattern fixArg1 = pF "cc64" 1  fixArg2 :: ControlPattern fixArg2 =       fix ( # crush 4 ) (pF "cc65" 1)-    $ fix ( stut' 4 (0.125/4) ( + up "1" )) (pF "cc66" 1)+    -- $ fix ( stut' 4 (0.125/4) ( + up "1" )) (pF "cc66" 1)     $ fix ( |*| speed "-1" ) (pF "cc67" 1)     $ fix ( (# delaytime 0.125).(# delay 0.5)) (pF "cc68" 1)     $ fix ( # coarse 12) (pF "cc69" 1)@@ -75,8 +81,8 @@     #  pF "cc69" (cF 0 "69")  {- Euclid inputs -}-ecA1 :: [Pattern Int] +ecA1 :: [Pattern Int] ecA1 = [1, 100] -ecA2 :: Pattern String +ecA2 :: Pattern String ecA2 = "x"
src/Sound/Tidal/Chords.hs view
@@ -115,16 +115,16 @@ sevenFlat10 = [0,4,7,10,15] nineSharp5 :: Num a => [a] nineSharp5 = [0,1,13]-m9sharp5 :: Num a => [a]-m9sharp5 = [0,1,14]+minor9sharp5 :: Num a => [a]+minor9sharp5 = [0,1,14] sevenSharp5flat9 :: Num a => [a] sevenSharp5flat9 = [0,4,8,10,13]-m7sharp5flat9 :: Num a => [a]-m7sharp5flat9 = [0,3,8,10,13]+minor7sharp5flat9 :: Num a => [a]+minor7sharp5flat9 = [0,3,8,10,13] elevenSharp :: Num a => [a] elevenSharp = [0,4,7,10,14,18]-m11sharp :: Num a => [a]-m11sharp = [0,3,7,10,14,18]+minor11sharp :: Num a => [a]+minor11sharp = [0,3,7,10,14,18]  -- | @chordate cs m n@ selects the @n@th "chord" (a chord is a list of Ints) -- from a list of chords @cs@ and transposes it by @m@@@ -141,6 +141,7 @@ chordTable :: Num a => [(String, [a])] chordTable = [("major", major),               ("maj", major),+              ("M", major),               ("aug", aug),               ("plus", aug),               ("sharp5", aug),@@ -178,6 +179,7 @@               ("13", thirteen),               ("minor", minor),               ("min", minor),+              ("m", minor),               ("diminished", diminished),               ("dim", diminished),               ("minorSharp5", minorSharp5),@@ -194,22 +196,30 @@               ("mSixNine", minorSixNine),               ("m6by9", minorSixNine),               ("minor7flat5", minor7flat5),+              ("minor7f5", minor7flat5),               ("min7flat5", minor7flat5),+              ("min7f5", minor7flat5),               ("m7flat5", minor7flat5),               ("m7f5", minor7flat5),               ("minor7", minor7),               ("min7", minor7),               ("m7", minor7),               ("minor7sharp5", minor7sharp5),+              ("minor7s5", minor7sharp5),               ("min7sharp5", minor7sharp5),+              ("min7s5", minor7sharp5),               ("m7sharp5", minor7sharp5),               ("m7s5", minor7sharp5),               ("minor7flat9", minor7flat9),+              ("minor7f9", minor7flat9),               ("min7flat9", minor7flat9),+              ("min7f9", minor7flat9),               ("m7flat9", minor7flat9),               ("m7f9", minor7flat9),               ("minor7sharp9", minor7sharp9),+              ("minor7s9", minor7sharp9),               ("min7sharp9", minor7sharp9),+              ("min7s9", minor7sharp9),               ("m7sharp9", minor7sharp9),               ("m7s9", minor7sharp9),               ("diminished7", diminished7),@@ -239,16 +249,23 @@               ("sevenFlat10", sevenFlat10),               ("7f10", sevenFlat10),               ("nineSharp5", nineSharp5),+              ("9sharp5", nineSharp5),               ("9s5", nineSharp5),-              ("m9sharp5", m9sharp5),-              ("m9s5", m9sharp5),+              ("minor9sharp5", minor9sharp5),+              ("minor9s5", minor9sharp5),+              ("min9sharp5", minor9sharp5),+              ("min9s5", minor9sharp5),+              ("m9sharp5", minor9sharp5),+              ("m9s5", minor9sharp5),               ("sevenSharp5flat9", sevenSharp5flat9),               ("7s5f9", sevenSharp5flat9),-              ("m7sharp5flat9", m7sharp5flat9),+              ("minor7sharp5flat9", minor7sharp5flat9),+              ("m7sharp5flat9", minor7sharp5flat9),               ("elevenSharp", elevenSharp),               ("11s", elevenSharp),-              ("m11sharp", m11sharp),-              ("m11s", m11sharp)+              ("minor11sharp", minor11sharp),+              ("m11sharp", minor11sharp),+              ("m11s", minor11sharp)              ]  chordL :: Num a => Pattern String -> Pattern [a]
src/Sound/Tidal/Core.hs view
@@ -179,12 +179,12 @@ ( -|) :: Num a => Pattern a -> Pattern a -> Pattern a a  -| b = (-) <$> a *> b -(|%|) :: (Applicative a, Real b) => a b -> a b -> a b-a |%| b = mod' <$> a <*> b-(|% ) :: Real a => Pattern a -> Pattern a -> Pattern a-a |%  b = mod' <$> a <* b-( %|) :: Real a => Pattern a -> Pattern a -> Pattern a-a  %| b = mod' <$> a *> b+(|%|) :: (Applicative a, Moddable b) => a b -> a b -> a b+a |%| b = gmod <$> a <*> b+(|% ) :: Moddable a => Pattern a -> Pattern a -> Pattern a+a |%  b = gmod <$> a <* b+( %|) :: Moddable a => Pattern a -> Pattern a -> Pattern a+a  %| b = gmod <$> a *> b  (|**|) :: (Applicative a, Floating b) => a b -> a b -> a b a |**| b = (**) <$> a <*> b@@ -292,6 +292,7 @@ fastCat :: [Pattern a] -> Pattern a fastCat ps = _fast (toTime $ length ps) $ cat ps +-- | Alias for @fastCat@ fastcat :: [Pattern a] -> Pattern a fastcat = fastCat @@ -303,21 +304,19 @@           arrange _ [] = []           arrange t ((t',p):tps') = (t,t+t',p) : arrange (t+t') tps' +-- | Alias for @timeCat@+timecat :: [(Time, Pattern a)] -> Pattern a+timecat = timeCat+ -- | 'overlay' combines two 'Pattern's into a new pattern, so that -- their events are combined over time.  overlay :: Pattern a -> Pattern a -> Pattern a-overlay !p !p' = Pattern $ \st -> query p st ++ query p' st---- | An infix alias of @overlay@-(<>) :: Pattern a -> Pattern a -> Pattern a-(<>) = overlay+overlay = (<>)  -- | 'stack' combines a list of 'Pattern's into a new pattern, so that -- their events are combined over time. stack :: [Pattern a] -> Pattern a stack = foldr overlay silence--  -- ** Manipulating time 
+ src/Sound/Tidal/ID.hs view
@@ -0,0 +1,40 @@+module Sound.Tidal.ID (ID(..)) where++{-+    ID.hs - Polymorphic pattern identifiers+    Copyright (C) 2020, Alex McLean and contributors++    This library is free software: you can redistribute it and/or modify+    it under the terms of the GNU General Public License as published by+    the Free Software Foundation, either version 3 of the License, or+    (at your option) any later version.++    This library is distributed in the hope that it will be useful,+    but WITHOUT ANY WARRANTY; without even the implied warranty of+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+    GNU General Public License for more details.++    You should have received a copy of the GNU General Public License+    along with this library.  If not, see <http://www.gnu.org/licenses/>.+-}++import GHC.Exts ( IsString(..) )++-- | Wrapper for literals that can be coerced to a string and used as an identifier.+-- | Similar to Show typeclass, but constrained to strings and integers and designed+-- | so that similar cases (such as 1 and "1") convert to the same value.+newtype ID = ID { fromID :: String }++noOv :: String -> a+noOv meth = error $ meth ++ ": not supported for ids"++instance Num ID where+  fromInteger = ID . show+  (+) = noOv "+"+  (*) = noOv "*"+  abs = noOv "abs"+  signum = noOv "signum"+  (-) = noOv "-"++instance IsString ID where+  fromString = ID
src/Sound/Tidal/Params.hs view
@@ -3128,6 +3128,9 @@ ohdecayrecv :: Pattern Int -> ControlPattern ohdecayrecv = ophatdecayrecv +number :: Pattern Note -> ControlPattern+number = n+ lsn :: Pattern Double -> ControlPattern lsn = lsnare lsnbus :: Pattern Int -> Pattern Double -> ControlPattern
src/Sound/Tidal/ParseBP.hs view
@@ -177,7 +177,10 @@     toE (Right tp) = toPat tp  parseTPat :: Parseable a => String -> Either ParseError (TPat a)-parseTPat = parseRhythm tPatParser+parseTPat = runParser (pSequence f' Prelude.<* eof) (0 :: Int) ""+  where f' = do tPatParser+             <|> do symbol "~" <?> "rest"+                    return TPat_Silence  cP :: (Enumerable a, Parseable a) => String -> Pattern a cP s = innerJoin $ parseBP_E <$> _cX_ getS s@@ -321,19 +324,11 @@          <|> return Positive  intOrFloat :: MyParser Double-intOrFloat = do num <- naturalOrFloat-                return (case num of-                  Right x -> x-                  Left  x -> fromIntegral x-                  )--parseRhythm :: Parseable a => MyParser (TPat a) -> String -> Either ParseError (TPat a)-parseRhythm f = runParser (pSequence f' Prelude.<* eof) (0 :: Int) ""-  where f' = do f-                <|> do symbol "~" <?> "rest"-                       return TPat_Silence+intOrFloat = do -- use 'try' to avoid consuming the first '.' in a '..' range.+                try float+                <|> fromIntegral <$> integer -pSequence :: Parseable a => MyParser (TPat a) -> GenParser Char Int (TPat a)+pSequence :: Parseable a => MyParser (TPat a) -> MyParser (TPat a) pSequence f = do   spaces   s <- many $ do@@ -439,11 +434,11 @@  pDouble :: MyParser (TPat Double) pDouble = wrapPos $ do s <- sign-                       f <- choice [fromRational <$> pRatio, intOrFloat, parseNote] <?> "float"+                       f <- choice [fromRational <$> pRatio, parseNote] <?> "float"                        let v = applySign s f                        do TPat_Stack . map (TPat_Atom Nothing . (+ v)) <$> parseChord                          <|> return (TPat_Atom Nothing v)-                      <|>+                       <|>                          do TPat_Stack . map (TPat_Atom Nothing) <$> parseChord  pNote :: MyParser (TPat Note)@@ -468,8 +463,6 @@                   if isInt d                     then return $ applySign s $ round d                     else fail "not an integer"-                where-                  isInt x = x == fromInteger (round x)  pIntegral :: Integral a => MyParser (TPat a) pIntegral = wrapPos $ do i <- parseIntNote@@ -569,38 +562,37 @@ pRatio :: MyParser Rational pRatio = do    s <- sign-  r <- do n <- read <$> many1 digit-          result <- do char '%'-                       d <- decimal-                       return (n%d)-                    <|>-                    do char '.'-                       frac <- many1 digit-                       -- A hack, but not sure if doing this-                       -- numerically would be any faster..-                       return (toRational ((read $ show n ++ "." ++ frac) :: Double))-                    <|>-                    return (n%1)-          c <- pRatioChar <|> return 1-          return (result * c)-        <|> -        pRatioChar+  r <- do n <- try intOrFloat+          v <- pFraction n <|> return (toRational n)+          r <- pRatioChar <|> return 1+          return (v * r)+       <|>+       pRatioChar   return $ applySign s r +pFraction :: RealFrac a => a -> MyParser Rational+pFraction n = do+  char '%'+  d <- integer+  if (isInt n)+    then return ((round n) % d)+    else fail "fractions need int numerator and denominator"+ pRatioChar :: Fractional a => MyParser a-pRatioChar = do char 'w';-                return 1-             <|> do char 'h'-                    return 0.5-             <|> do char 'q'-                    return 0.25-             <|> do char 'e'-                    return 0.125-             <|> do char 's'-                    return 0.0625-             <|> do char 't'-                    return (1/3)-             <|> do char 'f'-                    return 0.2-             <|> do char 'x'-                    return (1/6)+pRatioChar = pRatioSingleChar 'w' 1+             <|> pRatioSingleChar 'h' 0.5+             <|> pRatioSingleChar 'q' 0.25+             <|> pRatioSingleChar 'e' 0.125+             <|> pRatioSingleChar 's' 0.0625+             <|> pRatioSingleChar 't' (1/3)+             <|> pRatioSingleChar 'f' 0.2+             <|> pRatioSingleChar 'x' (1/6)++pRatioSingleChar :: Fractional a => Char -> a -> MyParser a+pRatioSingleChar c v = try $ do+  char c+  notFollowedBy (letter)+  return v++isInt :: RealFrac a => a -> Bool+isInt x = x == fromInteger (round x)
src/Sound/Tidal/Pattern.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveDataTypeable #-}@@ -40,6 +41,7 @@ import           Data.Word (Word8) import           Data.Data (Data) -- toConstr import           Data.Typeable (Typeable)+import           Data.Fixed (mod')  import           Sound.Tidal.Time @@ -230,6 +232,12 @@   enumFromTo     = noOv "enumFromTo"   enumFromThenTo = noOv "enumFromThenTo" +instance Monoid (Pattern a) where+  mempty = empty++instance Semigroup (Pattern a) where+  (<>) !p !p' = Pattern $ \st -> query p st ++ query p' st+ instance (Num a, Ord a) => Real (Pattern a) where   toRational = noOv "toRational" @@ -297,6 +305,35 @@   recip        = fmap (applyFIS recip id id)   fromRational r = Map.singleton "speed" $ VF (fromRational r) +class Moddable a where+  gmod :: a -> a -> a++instance Moddable Double where+  gmod = mod'+instance Moddable Rational where+  gmod = mod'+instance Moddable Note where+  gmod (Note a) (Note b) = Note (mod' a b)+instance Moddable Int where+  gmod = mod+instance Moddable ValueMap where+  gmod = Map.unionWith (fNum2 mod mod')++instance Floating ValueMap+  where pi = noOv "pi"+        exp _ = noOv "exp"+        log _ = noOv "log"+        sin _ = noOv "sin"+        cos _ = noOv "cos"+        asin _ = noOv "asin"+        acos _ = noOv "acos"+        atan _ = noOv "atan"+        sinh _ = noOv "sinh"+        cosh _ = noOv "cosh"+        asinh _ = noOv "asinh"+        acosh _ = noOv "acosh"+        atanh _ = noOv "atanh"+ ------------------------------------------------------------------------ -- * Internal functions @@ -471,11 +508,39 @@ withContext :: (Context -> Context) -> Pattern a -> Pattern a withContext f pat = withEvents (map (\e -> e {context = f $ context e})) pat -deltaContext :: Int -> Int -> Pattern a -> Pattern a-deltaContext column line pat = withEvents (map (\e -> e {context = f $ context e})) pat-  where f :: Context -> Context-        f (Context xs) = Context $ map (\((bx,by), (ex,ey)) -> ((bx+column,by+line), (ex+column,ey+line))) xs+-- A hack to add to manipulate source code to add calls to+-- 'deltaContext' around strings, so events from mininotation know+-- where they are within a whole tidal pattern+deltaMini :: String -> String+deltaMini = outside 0 0+  where outside :: Int -> Int -> String -> String+        outside _ _ [] = []+        outside column line ('"':xs) = "(deltaContext "+                                         ++ show column+                                         ++ " "+                                         ++ show line+                                         ++ " \""+                                         ++ inside (column+1) line xs+        outside _ line ('\n':xs) = '\n':outside 0 (line+1) xs+        outside column line (x:xs) = x:outside (column+1) line xs+        inside :: Int -> Int -> String -> String+        inside _ _ [] = []+        inside column line ('"':xs) = '"':')':outside (column+1) line xs+        inside _ line ('\n':xs) = '\n':inside 0 (line+1) xs+        inside column line (x:xs) = x:inside (column+1) line xs +class Stringy a where+  deltaContext :: Int -> Int -> a -> a++instance Stringy (Pattern a) where+  deltaContext column line pat = withEvents (map (\e -> e {context = f $ context e})) pat+    where f :: Context -> Context+          f (Context xs) = Context $ map (\((bx,by), (ex,ey)) -> ((bx+column,by+line), (ex+column,ey+line))) xs++-- deltaContext on an actual (non overloaded) string is a no-op+instance Stringy String where+  deltaContext _ _ = id+ -- ** Events  -- | Some context for an event, currently just position within sourcecode@@ -604,8 +669,16 @@ type ValueMap = Map.Map String Value  -- | Note is Double, but with a different parser-newtype Note = Note { unNote :: Double } deriving (Typeable, Data, Generic, Eq, Ord, Show, Enum, Num, Fractional, Floating, Real)+newtype Note = Note { unNote :: Double } deriving (Typeable, Data, Generic, Eq, Ord, Enum, Num, Fractional, Floating, Real) instance NFData Note++instance Show Note where+  show n = (show . unNote $ n) ++ "n (" ++ pitchClass ++ octave ++ ")"+    where+      pitchClass = pcs !! mod noteInt 12+      octave = show $ div noteInt 12 + 5+      noteInt = round . unNote $ n+      pcs = ["c", "cs", "d", "ds", "e", "f", "fs", "g", "gs", "a", "as", "b"]  instance Valuable String where   toValue a = VS a
src/Sound/Tidal/Show.hs view
@@ -78,13 +78,13 @@   show (VS s)  = ('"':s) ++ "\""   show (VI i)  = show i   show (VF f)  = show f ++ "f"-  show (VN n)  = show n ++ "n"-  show (VR r)  = show r ++ "r"+  show (VN n)  = show n+  show (VR r)  = prettyRat r ++ "r"   show (VB b)  = show b   show (VX xs) = show xs   show (VPattern pat) = "(" ++ show pat ++ ")"   show (VState f) = show $ f Map.empty-  show (VList _) =  "<list>"+  show (VList vs) = show $ map show vs  instance {-# OVERLAPPING #-} Show ValueMap where   show m = intercalate ", " $ map (\(name, v) -> name ++ ": " ++ show v) $ Map.toList m
src/Sound/Tidal/Stream.hs view
@@ -36,6 +36,7 @@  import           Sound.Tidal.Config import           Sound.Tidal.Core (stack, silence, (#))+import           Sound.Tidal.ID import           Sound.Tidal.Params (pS) import           Sound.Tidal.Pattern import qualified Sound.Tidal.Tempo as T@@ -150,9 +151,7 @@                     }  dirtShape :: OSC-dirtShape = OSC "/play" $ ArgList [("sec", iDefault 0),-                                   ("usec", iDefault 0),-                                   ("cps", fDefault 0),+dirtShape = OSC "/play" $ ArgList [("cps", fDefault 0),                                    ("s", required),                                    ("offset", fDefault 0),                                    ("begin", fDefault 0),@@ -183,8 +182,8 @@                                    ("attack", fDefault (-1)),                                    ("hold", fDefault 0),                                    ("release", fDefault (-1)),-                                   ("orbit", iDefault 0),-                                   ("id", iDefault 0)+                                   ("orbit", iDefault 0) -- ,+                                   -- ("id", iDefault 0)                                   ]  startStream :: Config -> [(Target, [OSC])] -> IO Stream@@ -535,7 +534,7 @@  -- Evaluation of pat is forced so exceptions are picked up here, before replacing the existing pattern. -streamReplace :: Show a => Stream -> a -> ControlPattern -> IO ()+streamReplace :: Stream -> ID -> ControlPattern -> IO () streamReplace s k !pat   = E.catch (do let x = queryArc pat (Arc 0 0)                 tempo <- readMVar $ sTempoMV s@@ -544,42 +543,38 @@                 now <- O.time                 let cyc = T.timeToCycles tempo now                 putMVar (sStateMV s) $-                  Map.insert ("_t_all") (VR cyc) $ Map.insert ("_t_" ++ show k) (VR cyc) input+                  Map.insert ("_t_all") (VR cyc) $ Map.insert ("_t_" ++ fromID k) (VR cyc) input                 -- update the pattern itself                 pMap <- seq x $ takeMVar $ sPMapMV s-                let playState = updatePS $ Map.lookup (show k) pMap-                putMVar (sPMapMV s) $ Map.insert (show k) playState pMap+                let playState = updatePS $ Map.lookup (fromID k) pMap+                putMVar (sPMapMV s) $ Map.insert (fromID k) playState pMap                 return ()           )     (\(e :: E.SomeException) -> hPutStrLn stderr $ "Error in pattern: " ++ show e     )   where updatePS (Just playState) = do playState {pattern = pat', history = pat:(history playState)}         updatePS Nothing = PlayState pat' False False [pat']-        pat' = pat # pS "_id_" (pure $ show k)---streamMute :: Show a => Stream -> a -> IO ()-streamMute s k = withPatId s (show k) (\x -> x {mute = True})+        pat' = pat # pS "_id_" (pure $ fromID k) -streamMutes :: Show a => Stream -> [a] -> IO ()-streamMutes s ks = withPatIds s (map show ks) (\x -> x {mute = True})+streamMute :: Stream -> ID -> IO ()+streamMute s k = withPatIds s [k] (\x -> x {mute = True}) -streamUnmute :: Show a => Stream -> a -> IO ()-streamUnmute s k = withPatId s (show k) (\x -> x {mute = False})+streamMutes :: Stream -> [ID] -> IO ()+streamMutes s ks = withPatIds s ks (\x -> x {mute = True}) -streamSolo :: Show a => Stream -> a -> IO ()-streamSolo s k = withPatId s (show k) (\x -> x {solo = True})+streamUnmute :: Stream -> ID -> IO ()+streamUnmute s k = withPatIds s [k] (\x -> x {mute = False}) -streamUnsolo :: Show a => Stream -> a -> IO ()-streamUnsolo s k = withPatId s (show k) (\x -> x {solo = False})+streamSolo :: Stream -> ID -> IO ()+streamSolo s k = withPatIds s [k] (\x -> x {solo = True}) -withPatId :: Stream -> PatId -> (PlayState -> PlayState) -> IO ()-withPatId s k f = withPatIds s [k] f+streamUnsolo :: Stream -> ID -> IO ()+streamUnsolo s k = withPatIds s [k] (\x -> x {solo = False}) -withPatIds :: Stream -> [PatId] -> (PlayState -> PlayState) -> IO ()+withPatIds :: Stream -> [ID] -> (PlayState -> PlayState) -> IO () withPatIds s ks f   = do playMap <- takeMVar $ sPMapMV s-       let pMap' = foldr (Map.update (\x -> Just $ f x)) playMap ks+       let pMap' = foldr (Map.update (\x -> Just $ f x)) playMap (map fromID ks)        putMVar (sPMapMV s) pMap'        return () @@ -596,6 +591,9 @@ streamUnsoloAll :: Stream -> IO () streamUnsoloAll s = modifyMVar_ (sPMapMV s) $ return . fmap (\x -> x {solo = False}) +streamSilence :: Stream -> ID -> IO ()+streamSilence s k = withPatIds s [k] (\x -> x {pattern = silence, history = silence:history x})+ streamAll :: Stream -> (ControlPattern -> ControlPattern) -> IO () streamAll s f = do _ <- swapMVar (sGlobalFMV s) f                    return ()@@ -668,22 +666,14 @@         act (O.Message "/ctrl" (O.ASCII_String k:O.Int32 v:[]))           = add (O.ascii_to_string k) (VI (fromIntegral v))         -- Stream playback commands-        act (O.Message "/mute" (O.Int32 k:[]))-          = streamMute stream k-        act (O.Message "/mute" (O.ASCII_String k:[]))-          = streamMute stream (O.ascii_to_string k)-        act (O.Message "/unmute" (O.Int32 k:[]))-          = streamUnmute stream k-        act (O.Message "/unmute" (O.ASCII_String k:[]))-          = streamUnmute stream (O.ascii_to_string k)-        act (O.Message "/solo" (O.Int32 k:[]))-          = streamSolo stream k-        act (O.Message "/solo" (O.ASCII_String k:[]))-          = streamSolo stream (O.ascii_to_string k)-        act (O.Message "/unsolo" (O.Int32 k:[]))-          = streamUnsolo stream k-        act (O.Message "/unsolo" (O.ASCII_String k:[]))-          = streamUnsolo stream (O.ascii_to_string k)+        act (O.Message "/mute" (k:[]))+          = withID k $ streamMute stream+        act (O.Message "/unmute" (k:[]))+          = withID k $ streamUnmute stream+        act (O.Message "/solo" (k:[]))+          = withID k $ streamSolo stream+        act (O.Message "/unsolo" (k:[]))+          = withID k $ streamUnsolo stream         act (O.Message "/muteAll" [])           = streamMuteAll stream         act (O.Message "/unmuteAll" [])@@ -692,11 +682,17 @@           = streamUnsoloAll stream         act (O.Message "/hush" [])           = streamHush stream+        act (O.Message "/silence" (k:[]))+          = withID k $ streamSilence stream         act m = hPutStrLn stderr $ "Unhandled OSC: " ++ show m         add :: String -> Value -> IO ()         add k v = do sMap <- takeMVar (sStateMV stream)                      putMVar (sStateMV stream) $ Map.insert k v sMap                      return ()+        withID :: O.Datum -> (ID -> IO ()) -> IO ()+        withID (O.ASCII_String k) func = func $ (ID . O.ascii_to_string) k+        withID (O.Int32 k) func = func $ (ID . show) k+        withID _ _ = return () ctrlResponder _ _ _ = return ()  
src/Sound/Tidal/Time.hs view
@@ -38,7 +38,8 @@  -- Utility functions - Time --- | The 'sam' (start of cycle) for the given time value+-- | The 'sam' (start of cycle) for the given time value.+-- Cycles have duration 1, so every integer Time value divides two cycles. sam :: Time -> Time sam = fromIntegral . (floor :: Time -> Int) @@ -87,23 +88,39 @@ sect :: Arc -> Arc -> Arc sect (Arc s e) (Arc s' e') = Arc (max s s') (min e e') ---- | The arc of the whole cycle that the given time value falls within+-- | The Arc returned is the cycle that the Time falls within.+--+-- Edge case: If the Time is an integer,+-- the Arc claiming it is the one starting at that Time,+-- not the previous one ending at that Time. timeToCycleArc :: Time -> Arc timeToCycleArc t = Arc (sam t) (sam t + 1) --- | Shifts an arc to the equivalent one that starts during cycle zero+-- | Shifts an Arc to one of equal duration that starts within cycle zero.+-- (Note that the output Arc probably does not start *at* Time 0 --+-- that only happens when the input Arc starts at an integral Time.) cycleArc :: Arc -> Arc cycleArc (Arc s e) = Arc (cyclePos s) (cyclePos s + (e-s)) --- | A list of cycle numbers which are included in the given arc+-- | Returns the numbers of the cycles that the input Arc overlaps+-- (excluding the input Arc's endpoint, unless it has duration 0 --+-- see "Edge cases" below).+-- The "cycle number" of an Arc is equal to its start value.+-- Thus, for instance, `cyclesInArc (Arc 0 1.5) == [0,1]`.+-- * Edge cases:+--     `cyclesInArc $ Arc 0 1.0001 == [0,1]`+--     `cyclesInArc $ Arc 0 1      == [0]` -- the endpoint is excluded+--     `cyclesInArc $ Arc 1 1      == [1]` -- unless the Arc has duration 0 cyclesInArc :: Integral a => Arc -> [a] cyclesInArc (Arc s e)   | s > e = []   | s == e = [floor s]   | otherwise = [floor s .. ceiling e-1] --- | A list of arcs of the whole cycles which are included in the given arc+-- | The whole cycles that overlap the input Arc,+-- (excluding its endpoint, unless it has duration 0,+-- similarly to `cyclesInArc` --+-- see that function's description for details). cycleArcsInArc :: Arc -> [Arc] cycleArcsInArc = map (timeToCycleArc . (toTime :: Int -> Time)) . cyclesInArc 
src/Sound/Tidal/Transition.hs view
@@ -4,7 +4,7 @@  import Prelude hiding ((<*), (*>)) -import Control.Concurrent.MVar (readMVar, takeMVar, putMVar)+import Control.Concurrent.MVar (readMVar, swapMVar)  import qualified Sound.OSC.FD as O import qualified Data.Map.Strict as Map@@ -12,6 +12,7 @@  import Sound.Tidal.Control import Sound.Tidal.Core+import Sound.Tidal.ID import Sound.Tidal.Params (gain, pan) import Sound.Tidal.Pattern import Sound.Tidal.Stream@@ -39,13 +40,13 @@  -- Evaluation of pat is forced so exceptions are picked up here, before replacing the existing pattern. -- the "historyFlag" determines if the new pattern should be placed on the history stack or not-transition :: Show a => Stream -> Bool -> (Time -> [ControlPattern] -> ControlPattern) -> a -> ControlPattern -> IO ()+transition :: Stream -> Bool -> (Time -> [ControlPattern] -> ControlPattern) -> ID -> ControlPattern -> IO () transition stream historyFlag f patId !pat =-  do pMap <- takeMVar (sPMapMV stream)-     let playState = updatePS $ Map.lookup (show patId) pMap+  do pMap <- readMVar (sPMapMV stream)+     let playState = updatePS $ Map.lookup (fromID patId) pMap      pat' <- transition' $ appendPat (not historyFlag) (history playState)-     let pMap' = Map.insert (show patId) (playState {pattern = pat'}) pMap-     putMVar (sPMapMV stream) pMap'+     let pMap' = Map.insert (fromID patId) (playState {pattern = pat'}) pMap+     _ <- swapMVar (sPMapMV stream) pMap'      return ()   where     appendPat flag = if flag then (pat:) else id
src/Sound/Tidal/UI.hs view
@@ -142,14 +142,14 @@ repeat every cycle (because the saw does) The `perlin` function uses the cycle count as input and can be used much like @rand@. -}-perlinWith :: Pattern Double -> Pattern Double-perlinWith p = interp <$> (p-pa) <*> (timeToRand <$> pa) <*> (timeToRand <$> pb) where+perlinWith :: Fractional a => Pattern Double -> Pattern a+perlinWith p = fmap realToFrac $ (interp) <$> (p-pa) <*> (timeToRand <$> pa) <*> (timeToRand <$> pb) where   pa = (fromIntegral :: Int -> Double) . floor <$> p   pb = (fromIntegral :: Int -> Double) . (+1) . floor <$> p   interp x a b = a + smootherStep x * (b-a)   smootherStep x = 6.0 * x**5 - 15.0 * x**4 + 10.0 * x**3 -perlin :: Pattern Double+perlin :: Fractional a => Pattern a perlin = perlinWith (sig fromRational)  {- `perlin2With` is Perlin noise with a 2-dimensional input. This can be@@ -564,11 +564,15 @@ -- | @wedge t p p'@ combines patterns @p@ and @p'@ by squashing the -- @p@ into the portion of each cycle given by @t@, and @p'@ into the -- remainer of each cycle.-wedge :: Time -> Pattern a -> Pattern a -> Pattern a-wedge 0 _ p' = p'-wedge 1 p _ = p-wedge t p p' = overlay (_fastGap (1/t) p) (t `rotR` _fastGap (1/(1-t)) p')+wedge :: Pattern Time -> Pattern a -> Pattern a -> Pattern a+wedge pt pa pb = innerJoin $ (\t -> _wedge t pa pb) <$> pt +_wedge :: Time -> Pattern a -> Pattern a -> Pattern a+_wedge 0 _ p' = p'+_wedge 1 p _ = p+_wedge t p p' = overlay (_fastGap (1/t) p) (t `rotR` _fastGap (1/(1-t)) p')++ {- | @whenmod@ has a similar form and behavior to `every`, but requires an additional number. Applies the function to the pattern, when the remainder of the current loop number divided by the first parameter,@@ -1250,11 +1254,17 @@ chunk' :: Integral a1 => Pattern a1 -> (Pattern a2 -> Pattern a2) -> Pattern a2 -> Pattern a2 chunk' npat f p = innerJoin $ (\n -> _chunk' n f p) <$> npat +_inside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a+_inside n f p = _fast n $ f (_slow n p)+ inside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a-inside n f p = density n $ f (slow n p)+inside np f p = innerJoin $ (\n -> _inside n f p) <$> np +_outside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a+_outside n = _inside (1/n)+ outside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a-outside n = inside (1/n)+outside np f p = innerJoin $ (\n -> _outside n f p) <$> np  loopFirst :: Pattern a -> Pattern a loopFirst p = splitQueries $ p {query = f}@@ -1961,3 +1971,13 @@ grain s w = P.begin b # P.end e   where b = s         e = s + w++-- | For specifying a boolean pattern according to a list of offsets+-- (aka inter-onset intervals).  For example `necklace 12 [4,2]` is+-- the same as "t f f f t f t f f f t f". That is, 12 steps per cycle,+-- with true values alternating between every 4 and every 2 steps.+necklace :: Rational -> [Int] -> Pattern Bool+necklace perCycle xs = _slow ((toRational $ sum xs) / perCycle) $ listToPat $ list xs+  where list :: [Int] -> [Bool]+        list [] = []+        list (x:xs') = (True:(replicate (x-1) False)) ++ list xs'
src/Sound/Tidal/Utils.hs view
@@ -93,27 +93,6 @@    s':rest -> (s':w) : wordsBy p (drop 1 s'')           where (w, s'') = break p rest --- A hack to add to the source code context for mini-notation, so--- events know where they are within a whole tidal pattern-deltaMini :: String -> String-deltaMini = outside 0 0-  where outside :: Int -> Int -> String -> String-        outside _ _ [] = []-        outside column line ('"':xs) = "(deltaContext "-                                         ++ show column-                                         ++ " "-                                         ++ show line-                                         ++ " \""-                                         ++ inside (column+1) line xs-        outside _ line ('\n':xs) = '\n':outside 0 (line+1) xs-        outside column line (x:xs) = x:outside (column+1) line xs-        inside :: Int -> Int -> String -> String-        inside _ _ [] = []-        inside column line ('"':xs) = '"':')':outside (column+1) line xs-        inside _ line ('\n':xs) = '\n':inside 0 (line+1) xs-        inside column line (x:xs) = x:inside (column+1) line xs-- matchMaybe :: Maybe a -> Maybe a -> Maybe a matchMaybe Nothing y = y matchMaybe x       _ = x
src/Sound/Tidal/Version.hs view
@@ -21,7 +21,7 @@ -}  tidal_version :: String-tidal_version = "1.7.8"+tidal_version = "1.7.9"  tidal_status :: IO () tidal_status = tidal_status_string >>= putStrLn 
test/Sound/Tidal/ParseTest.hs view
@@ -97,6 +97,22 @@         compareP (Arc 0 2)           ("0 .. 8" :: Pattern Rational)           ("0 1 2 3 4 5 6 7 8")+      it "can parse .. with doubles" $ do+        compareP (Arc 0 2)+          ("0.0 .. 8.0" :: Pattern Double)+          ("0 1 2 3 4 5 6 7 8")+      it "can parse .. with doubles, without spaces" $ do+        compareP (Arc 0 2)+          ("0.0..8.0" :: Pattern Double)+          ("0 1 2 3 4 5 6 7 8")+      it "can parse .. with notes" $ do+        compareP (Arc 0 2)+          ("0.0 .. 8.0" :: Pattern Note)+          ("0 1 2 3 4 5 6 7 8")+      it "can parse .. with notes, without spaces" $ do+        compareP (Arc 0 2)+          ("0..8" :: Pattern Note)+          ("0 1 2 3 4 5 6 7 8")       it "can handle repeats (!) and durations (@) with <>" $ do         compareP (Arc 0 31)           ("<a!3 b ! c@5>" :: Pattern String)@@ -113,6 +129,17 @@         compareP (Arc 0 2)           ("1%3@0.5 3%4@1%6 3%4@1%6 3%4@1%6" :: Pattern Rational)           ("1%3 0.75*3")+      it "can handle ratio shortands on a fraction" $ do+        compareP (Arc 0 1)+          ("1%3t" :: Pattern Rational)+          ("1%9" :: Pattern Rational)+      it "can handle ratio shortands on a floating point number" $ do+        compareP (Arc 0 1)+          ("3.33t" :: Pattern Double)+          ("1.11" :: Pattern Double)+      it "cannot handle fractional with floating point numerator or denominator" $ do+        evaluate ("1.2%5.3" :: Pattern Time)+          `shouldThrow` anyException       it "can parse a chord" $ do         compareP (Arc 0 2)           ("'major" :: Pattern Int)@@ -145,6 +172,26 @@         compareP (Arc 0 1)           ("3h -2q 1.5q" :: Pattern Double)           ("1.5 -0.5 0.375" :: Pattern Double)+      it "can parse exponential notation value for pattern double" $ do+        compareP (Arc 0 1)+          ("1e3" :: Pattern Double)+          ("1000" :: Pattern Double)+      it "can parse negative exponential notation value for pattern double" $ do+        compareP (Arc 0 1)+          ("400e-3" :: Pattern Double)+          ("0.4" :: Pattern Double)+      it "can parse ratio shortand on exponential notation value" $ do+        compareP (Arc 0 1)+          ("4e2q" :: Pattern Double)+          ("100" :: Pattern Double)+      it "can parse euclid pattern" $ do+        compareP (Arc 0 1)+          ("bd(3,8,1)" :: Pattern String)+          ("~ ~ bd ~ ~ bd ~ bd")+      it "can parse euclid bool pattern" $ do+        compareP (Arc 0 1)+          ("t(3,8,1)" :: Pattern Bool)+          ("f f t f f t f t")       it "doesn't crash on zeroes (1)" $ do         compareP (Arc 0 2)           ("cp/0" :: Pattern String)
test/Sound/Tidal/UITest.hs view
@@ -344,3 +344,29 @@         compareP (Arc 0 1)           (off "-e" id $ s "0")           (superimpose ("e" <~) $ s "0")+    describe "loopFirst" $ do+      it "plays the first cycle" $ do+        compareP (Arc 0 1)+          (loopFirst $ rotL 3 $ slow 8 $ "0 .. 7" :: Pattern Int)+          ("3")+    describe "loopCycles" $ do+      it "plays the first n cycles" $ do+        compareP (Arc 0 1)+          (loopFirst $ rotL 3 $ slow 8 $ "0 .. 7" :: Pattern Int)+          ("3")+    describe "timeLoop" $ do+      it "can loop time" $ do+        compareP (Arc 0 1)+          ((3 <~) $ (timeLoop 3 $ sound "<a b c d>"))+          (sound "a")+    describe "timeLoop" $ do+      it "can pattern time" $ do+        compareP (Arc 0 1)+          ((1 <~) $ timeLoop "<2 1>" $ sound "b")+          (sound "b")+    describe "necklace" $ do+      it "can specify rhythm by IOI" $ do+        compareP (Arc 0 1)+          (necklace 12 [4,2])+          ("t f f f t f t f f f t f")+      
tidal.cabal view
@@ -1,5 +1,5 @@ name:                tidal-version:             1.7.8+version:             1.7.9 synopsis:            Pattern language for improvised music description:         Tidal is a domain specific language for live coding patterns. homepage:            http://tidalcycles.org/@@ -33,6 +33,7 @@                        Sound.Tidal.Control                        Sound.Tidal.Context                        Sound.Tidal.Core+                       Sound.Tidal.ID                        Sound.Tidal.Params                        Sound.Tidal.ParseBP                        Sound.Tidal.Pattern