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 +2/−0
- CHANGELOG.md +34/−0
- README.md +2/−2
- bench/Memory/Tidal/Inputs.hs +33/−23
- bench/Speed/Tidal/Inputs.hs +27/−21
- src/Sound/Tidal/Chords.hs +28/−11
- src/Sound/Tidal/Core.hs +12/−13
- src/Sound/Tidal/ID.hs +40/−0
- src/Sound/Tidal/Params.hs +3/−0
- src/Sound/Tidal/ParseBP.hs +41/−49
- src/Sound/Tidal/Pattern.hs +78/−5
- src/Sound/Tidal/Show.hs +3/−3
- src/Sound/Tidal/Stream.hs +38/−42
- src/Sound/Tidal/Time.hs +23/−6
- src/Sound/Tidal/Transition.hs +7/−6
- src/Sound/Tidal/UI.hs +29/−9
- src/Sound/Tidal/Utils.hs +0/−21
- src/Sound/Tidal/Version.hs +1/−1
- test/Sound/Tidal/ParseTest.hs +47/−0
- test/Sound/Tidal/UITest.hs +26/−0
- tidal.cabal +2/−1
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