tidal 0.9.2 → 0.9.3
raw patch · 10 files changed
+269/−54 lines, 10 filesdep +tastydep +tasty-hunitdep +tidaldep −applicative-numbersdep ~base
Dependencies added: tasty, tasty-hunit, tidal
Dependencies removed: applicative-numbers
Dependency ranges changed: base
Files
- Sound/Tidal/Context.hs +1/−0
- Sound/Tidal/Dirt.hs +5/−0
- Sound/Tidal/Params.hs +1/−0
- Sound/Tidal/Parse.hs +75/−33
- Sound/Tidal/Pattern.hs +112/−16
- Sound/Tidal/PatternList.hs +12/−0
- Sound/Tidal/Stream.hs +1/−1
- Sound/Tidal/Version.hs +1/−1
- tests/test.hs +41/−0
- tidal.cabal +20/−3
Sound/Tidal/Context.hs view
@@ -7,6 +7,7 @@ import Data.Monoid as C import Sound.Tidal.Parse as C import Sound.Tidal.Pattern as C+import Sound.Tidal.PatternList as C import Sound.Tidal.Stream as C import Sound.Tidal.Dirt as C import Sound.Tidal.Strategies as C
Sound/Tidal/Dirt.hs view
@@ -13,6 +13,7 @@ import Data.Maybe import Data.Fixed import Data.Ratio+import Data.List (elemIndex) import Sound.Tidal.Stream import Sound.Tidal.OscStream@@ -337,3 +338,7 @@ {- | Copies the @n@ parameter to the @orbit@ parameter, so different sound variants or notes go to different orbits in SuperDirt. -} nToOrbit = copyParam n_p orbit_p++{- | Maps the sample or synth names to different @orbit@s, using indexes from the given list. E.g. @soundToOrbit ["bd", "sn", "cp"] $ sound "bd [cp sn]"@ would cause the bd, sn and cp smamples to be sent to orbit 0, 1, 2 respectively.-}+soundToOrbit :: [String] -> ParamPattern -> ParamPattern+soundToOrbit sounds p = follow s_p orbit_p ((\s -> fromMaybe 0 $ elemIndex s sounds) <$>) p
Sound/Tidal/Params.hs view
@@ -321,6 +321,7 @@ pit2 = pitch2 pit3 = pitch3 por = portamento+rel = release sag = sagogo scl = sclaves scp = sclap
Sound/Tidal/Parse.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE LambdaCase, GADTs #-} module Sound.Tidal.Parse where @@ -21,27 +21,30 @@ import Sound.Tidal.Time (Arc, Time) -- | AST representation of patterns-data TPat a- = TPat_Atom a- | TPat_Density Time (TPat a)- -- We keep this distinct from '_density because of divide-by-zero:- | TPat_Slow Time (TPat a)- | TPat_Zoom Arc (TPat a)- | TPat_DegradeBy Double (TPat a)- | TPat_Silence- | TPat_Foot- | TPat_Cat [TPat a]- | TPat_Overlay (TPat a) (TPat a)- | TPat_ShiftL Time (TPat a)- -- | TPat_E Int Int (TPat a)- | TPat_pE (TPat Int) (TPat Int) (TPat Integer) (TPat a)- deriving (Show)+data TPat a where+ TPat_Atom :: Parseable a => a -> TPat a+ TPat_Density :: Parseable a => Time -> (TPat a) -> (TPat a)+ -- We keep this distinct from '_density because of divide-by-zero:+ TPat_Slow :: Parseable a => Time -> TPat a -> TPat a+ TPat_Zoom :: Parseable a => Arc -> TPat a -> TPat a+ TPat_DegradeBy :: Parseable a => Double -> TPat a -> TPat a+ TPat_Silence :: Parseable a => TPat a+ TPat_Foot :: Parseable a => TPat a+ TPat_Enum :: Parseable a => TPat a+ TPat_EnumFromTo :: Parseable a => TPat a -> TPat a -> TPat a + TPat_Cat :: Parseable a => [TPat a] -> TPat a+ TPat_Overlay :: Parseable a => TPat a -> TPat a -> TPat a+ TPat_ShiftL :: Parseable a => Time -> TPat a -> TPat a+ -- TPat_E Int Int (TPat a)+ TPat_pE :: Parseable a => TPat Int -> TPat Int -> TPat Integer -> TPat a -> TPat a -instance Monoid (TPat a) where+-- deriving (Show)++instance Parseable a => Monoid (TPat a) where mempty = TPat_Silence mappend = TPat_Overlay -toPat :: TPat a -> Pattern a+toPat :: Parseable a => TPat a -> Pattern a toPat = \case TPat_Atom x -> atom x TPat_Density t x -> _density t $ toPat x@@ -55,35 +58,60 @@ TPat_pE n k s thing -> unwrap $ eoff <$> toPat n <*> toPat k <*> toPat s <*> pure (toPat thing) TPat_Foot -> error "Can't happen, feet (.'s) only used internally.."+ TPat_Enum -> error "Can't happen, enums (..'s) only used internally.."+ TPat_EnumFromTo a b -> unwrap $ fromTo <$> (toPat a) <*> (toPat b)+ -- TPat_EnumFromThenTo a b c -> unwrap $ fromThenTo <$> (toPat a) <*> (toPat b) <*> (toPat c) p :: Parseable a => String -> Pattern a p = toPat . parseTPat class Parseable a where parseTPat :: String -> TPat a+ fromTo :: a -> a -> Pattern a+ fromThenTo :: a -> a -> a -> Pattern a instance Parseable Double where parseTPat = parseRhythm pDouble+ fromTo a b = enumFromTo' a b+ fromThenTo a b c = enumFromThenTo' a b c instance Parseable String where parseTPat = parseRhythm pVocable+ fromTo a b = listToPat [a,b]+ fromThenTo a b c = listToPat [a,b,c] instance Parseable Bool where parseTPat = parseRhythm pBool+ fromTo a b = listToPat [a,b]+ fromThenTo a b c = listToPat [a,b,c] instance Parseable Int where parseTPat = parseRhythm pIntegral+ fromTo a b = enumFromTo' a b+ fromThenTo a b c = enumFromThenTo' a b c instance Parseable Integer where parseTPat s = parseRhythm pIntegral s+ fromTo a b = enumFromTo' a b+ fromThenTo a b c = enumFromThenTo' a b c instance Parseable Rational where parseTPat = parseRhythm pRational+ fromTo a b = enumFromTo' a b+ fromThenTo a b c = enumFromThenTo' a b c -type ColourD = Colour Double+enumFromTo' a b | a > b = listToPat $ reverse $ enumFromTo b a+ | otherwise = listToPat $ enumFromTo a b +enumFromThenTo' a b c | a > c = listToPat $ reverse $ enumFromThenTo c (c + (a-b)) a+ | otherwise = listToPat $ enumFromThenTo a b c++type ColourD = Colour Double + instance Parseable ColourD where parseTPat = parseRhythm pColour+ fromTo a b = listToPat [a,b]+ fromThenTo a b c = listToPat [a,b,c] instance (Parseable a) => IsString (Pattern a) where fromString = toPat . parseTPat@@ -140,21 +168,35 @@ ) (return $ p s) -parseRhythm :: Parser (TPat a) -> String -> TPat a+parseRhythm :: Parseable a => Parser (TPat a) -> String -> TPat a parseRhythm f input = either (const TPat_Silence) id $ parse (pSequence f') "" input where f' = f <|> do symbol "~" <?> "rest" return TPat_Silence -pSequenceN :: Parser (TPat a) -> GenParser Char () (Int, TPat a)+pSequenceN :: Parseable a => Parser (TPat a) -> GenParser Char () (Int, TPat a) pSequenceN f = do spaces -- d <- pDensity- ps <- many $ pPart f+ ps <- many $ do a <- pPart f+ do Text.ParserCombinators.Parsec.try $ symbol "-"+ b <- pPart f+ return [TPat_EnumFromTo (TPat_Cat a) (TPat_Cat b)]+ <|> return a <|> do symbol "." return [TPat_Foot] let ps' = TPat_Cat $ map TPat_Cat $ splitFeet $ concat ps return (length ps, ps') +{-+expandEnum :: Parseable t => Maybe (TPat t) -> [TPat t] -> [TPat t]+expandEnum a [] = [a]+expandEnum (Just a) (TPat_Enum:b:ps) = (TPat_EnumFromTo a b) : (expandEnum Nothing ps)+-- ignore ..s in other places+expandEnum a (TPat_Enum:ps) = expandEnum a ps+expandEnum (Just a) (b:ps) = a:(expandEnum b (Just c) ps)+expandEnum Nothing (c:ps) = expandEnum (Just c) ps+-}+ -- could use splitOn here but `TPat a` isn't a member of `EQ`.. splitFeet :: [TPat t] -> [[TPat t]] splitFeet [] = []@@ -164,14 +206,14 @@ takeFoot (TPat_Foot:ps) = ([], ps) takeFoot (p:ps) = (\(a,b) -> (p:a,b)) $ takeFoot ps -pSequence :: Parser (TPat a) -> GenParser Char () (TPat a)+pSequence :: Parseable a => Parser (TPat a) -> GenParser Char () (TPat a) pSequence f = do (_, p) <- pSequenceN f return p -pSingle :: Parser (TPat a) -> Parser (TPat a)+pSingle :: Parseable a => Parser (TPat a) -> Parser (TPat a) pSingle f = f >>= pRand >>= pMult -pPart :: Parser (TPat a) -> Parser [TPat a]+pPart :: Parseable a => Parser (TPat a) -> Parser [TPat a] pPart f = do part <- pSingle f <|> pPolyIn f <|> pPolyOut f part <- pE part part <- pRand part@@ -181,12 +223,12 @@ spaces return $ parts -pPolyIn :: Parser (TPat a) -> Parser (TPat a)+pPolyIn :: Parseable a => Parser (TPat a) -> Parser (TPat a) pPolyIn f = do ps <- brackets (pSequence f `sepBy` symbol ",") spaces pMult $ mconcat ps -pPolyOut :: Parser (TPat a) -> Parser (TPat a)+pPolyOut :: Parseable a => Parser (TPat a) -> Parser (TPat a) pPolyOut f = do ps <- braces (pSequenceN f `sepBy` symbol ",") spaces base <- do char '%'@@ -233,7 +275,7 @@ i <- integer return $ applySign s $ fromIntegral i -pIntegral :: Integral i => Parser (TPat i)+pIntegral :: Parseable a => Integral a => Parser (TPat a) pIntegral = TPat_Atom <$> parseIntNote parseNote :: Integral a => Parser a@@ -266,7 +308,7 @@ colour <- readColourName name <?> "known colour" return $ TPat_Atom colour -pMult :: TPat a -> Parser (TPat a)+pMult :: Parseable a => TPat a -> Parser (TPat a) pMult thing = do char '*' spaces r <- pRatio@@ -281,13 +323,13 @@ -pRand :: TPat a -> Parser (TPat a)+pRand :: Parseable a => TPat a -> Parser (TPat a) pRand thing = do char '?' spaces return $ TPat_DegradeBy 0.5 thing <|> return thing -pE :: TPat a -> Parser (TPat a)+pE :: Parseable a => TPat a -> Parser (TPat a) pE thing = do (n,k,s) <- parens (pair) pure $ TPat_pE n k s thing <|> return thing@@ -307,7 +349,7 @@ eoff n k s p = ((s%(fromIntegral k)) `rotL`) (e n k p) -- TPat_ShiftL (s%(fromIntegral k)) (TPat_E n k p) -pReplicate :: TPat a -> Parser [TPat a]+pReplicate :: Parseable a => TPat a -> Parser [TPat a] pReplicate thing = do extras <- many $ do char '!' -- if a number is given (without a space)slow 2 $ fast@@ -320,7 +362,7 @@ return (thing:concat extras) -pStretch :: TPat a -> Parser [TPat a]+pStretch :: Parseable a => TPat a -> Parser [TPat a] pStretch thing = do char '@' n <- ((read <$> many1 digit) <|> return 1)
Sound/Tidal/Pattern.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, CPP #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans -fno-warn-name-shadowing #-} module Sound.Tidal.Pattern where import Control.Applicative--- import Data.Monoid+import Data.Monoid import Data.Fixed import Data.List import Data.Maybe@@ -30,13 +30,89 @@ data Pattern a = Pattern {arc :: Arc -> [Event a]} deriving Typeable -#define INSTANCE_Eq-#define INSTANCE_Ord-#define INSTANCE_Enum+noOv :: String -> a+noOv meth = error $ meth ++ ": No overloading" -#define APPLICATIVE Pattern-#include "ApplicativeNumeric-inc.hs"+instance Eq (Pattern a) where+ (==) = noOv "(==)" +instance Ord a => Ord (Pattern a) where+ min = liftA2 min+ max = liftA2 max++instance Num a => Num (Pattern a) where+ negate = fmap negate+ (+) = liftA2 (+)+ (*) = liftA2 (*)+ fromInteger = pure . fromInteger+ abs = fmap abs+ signum = fmap signum++instance Enum a => Enum (Pattern a) where+ succ = fmap succ+ pred = fmap pred+ toEnum = pure . toEnum+ fromEnum = noOv "fromEnum"+ enumFrom = noOv "enumFrom"+ enumFromThen = noOv "enumFromThen"+ enumFromTo = noOv "enumFromTo"+ enumFromThenTo = noOv "enumFromThenTo"++instance (Num a, Ord a) => Real (Pattern a) where+ toRational = noOv "toRational"++instance (Integral a) => Integral (Pattern a) where+ quot = liftA2 quot+ rem = liftA2 rem+ div = liftA2 div+ mod = liftA2 mod+ toInteger = noOv "toInteger"+ x `quotRem` y = (x `quot` y, x `rem` y)+ x `divMod` y = (x `div` y, x `mod` y)++instance (Fractional a) => Fractional (Pattern a) where+ recip = fmap recip+ fromRational = pure . fromRational++instance (Floating a) => Floating (Pattern a) where+ pi = pure pi+ sqrt = fmap sqrt+ exp = fmap exp+ log = fmap log+ sin = fmap sin+ cos = fmap cos+ asin = fmap asin+ atan = fmap atan+ acos = fmap acos+ sinh = fmap sinh+ cosh = fmap cosh+ asinh = fmap asinh+ atanh = fmap atanh+ acosh = fmap acosh++instance (RealFrac a) => RealFrac (Pattern a) where+ properFraction = noOv "properFraction"+ truncate = noOv "truncate"+ round = noOv "round"+ ceiling = noOv "ceiling"+ floor = noOv "floor"++instance (RealFloat a) => RealFloat (Pattern a) where+ floatRadix = noOv "floatRadix"+ floatDigits = noOv "floatDigits"+ floatRange = noOv "floatRange"+ decodeFloat = noOv "decodeFloat"+ encodeFloat = ((.).(.)) pure encodeFloat+ exponent = noOv "exponent"+ significand = noOv "significand"+ scaleFloat n = fmap (scaleFloat n)+ isNaN = noOv "isNaN"+ isInfinite = noOv "isInfinite"+ isDenormalized = noOv "isDenormalized"+ isNegativeZero = noOv "isNegativeZero"+ isIEEE = noOv "isIEEE"+ atan2 = liftA2 atan2+ -- | @show (p :: Pattern)@ returns a text string representing the -- event values active during the first cycle of the given pattern. instance (Show a) => Show (Pattern a) where@@ -201,6 +277,9 @@ listToPat :: [a] -> Pattern a listToPat = fastcat . map atom +patToList :: Pattern a -> [a]+patToList p = map (thd') $ sortBy (\a b -> compare (snd' a) (snd' b)) $ filter ((\x -> x >= 0 && x < 1) . fst . snd' ) (arc p (0,1))+ -- | @maybeListToPat@ is similar to @listToPat@, but allows values to -- be optional using the @Maybe@ type, so that @Nothing@ results in -- gaps in the pattern.@@ -235,6 +314,8 @@ fast :: Pattern Time -> Pattern a -> Pattern a fast = temporalParam _density +_fast = _density+ fast' :: Pattern Time -> Pattern a -> Pattern a fast' = temporalParam' _density @@ -342,7 +423,9 @@ -- | @rev p@ returns @p@ with the event positions in each cycle -- reversed (or mirrored). rev :: Pattern a -> Pattern a-rev p = splitQueries $ Pattern $ \a -> mapArcs mirrorArc (arc p (mirrorArc a))+rev p = splitQueries $ Pattern $ \a -> map makeWholeAbsolute $ mapSnds' mirrorArc $ map makeWholeRelative (arc p (mirrorArc a))+ where makeWholeRelative ((s,e), part@(s',e'), v) = ((s'-s, e-e'), part, v)+ makeWholeAbsolute ((s,e), part@(s',e'), v) = ((s'-e,e'+s), part, v) -- | @palindrome p@ applies @rev@ to @p@ every other cycle, so that -- the pattern alternates between forwards and backwards.@@ -911,11 +994,11 @@ splitQueries :: Pattern a -> Pattern a splitQueries p = Pattern $ \a -> concatMap (arc p) $ arcCycles a -{- | Truncates a pattern so that only a fraction of the pattern is played.-The following example plays only the first three quarters of the pattern:+{- | @trunc@ truncates a pattern so that only a fraction of the pattern is played.+The following example plays only the first quarter of the pattern: @-d1 $ trunc 0.75 $ sound "bd sn*2 cp hh*4 arpy bd*2 cp bd*2"+d1 $ trunc 0.25 $ sound "bd sn*2 cp hh*4 arpy bd*2 cp bd*2" @ -} trunc :: Pattern Time -> Pattern a -> Pattern a@@ -924,6 +1007,18 @@ _trunc :: Time -> Pattern a -> Pattern a _trunc t = compress (0,t) . zoom (0,t) +{- | @linger@ is similar to `trunc` but the truncated part of the pattern loops until the end of the cycle++@+d1 $ linger 0.25 $ sound "bd sn*2 cp hh*4 arpy bd*2 cp bd*2"+@+-}+linger :: Pattern Time -> Pattern a -> Pattern a+linger = temporalParam _linger++_linger :: Time -> Pattern a -> Pattern a+_linger n p = _density (1/n) $ zoom (0,n) p+ {- | Plays a portion of a pattern, specified by a beginning and end arc of time. The new resulting pattern is played over the time period of the original pattern: @@ -1129,12 +1224,13 @@ -- | @discretise n p@: 'samples' the pattern @p@ at a rate of @n@ -- events per cycle. Useful for turning a continuous pattern into a -- discrete one.-discretise :: Pattern Time -> Pattern a -> Pattern a-discretise n p = (density n $ atom (id)) <*> p -discretise' :: Time -> Pattern a -> Pattern a-discretise' n p = (_density n $ atom (id)) <*> p+discretise :: Time -> Pattern a -> Pattern a+discretise n p = (_density n $ atom (id)) <*> p +discretise' = discretise+_discretise = discretise+ -- | @randcat ps@: does a @slowcat@ on the list of patterns @ps@ but -- randomises the order in which they are played. randcat :: [Pattern a] -> Pattern a@@ -1205,7 +1301,7 @@ -- | @substruct a b@: similar to @struct@, but each event in pattern @a@ gets replaced with pattern @b@, compressed to fit the timespan of the event. substruct :: Pattern String -> Pattern b -> Pattern b-substruct s p = filterStartInRange $ Pattern $ f+substruct s p = Pattern $ f where f a = concatMap (\a' -> arc (compressTo a' p) a') $ (map fst' $ arc s a) compressTo (s,e) p = compress (cyclePos s, e-(sam s)) p
+ Sound/Tidal/PatternList.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE OverloadedLists, TypeFamilies #-}++module Sound.Tidal.PatternList where++import Sound.Tidal.Pattern+import GHC.Exts( IsList(..) )++instance (Ord a) => IsList (Pattern a) where+ type Item (Pattern a) = a+ fromList = listToPat+ toList = patToList+
Sound/Tidal/Stream.hs view
@@ -295,7 +295,7 @@ withS :: Param -> (Pattern String -> Pattern String) -> ParamPattern -> ParamPattern withS = with -follow :: ParamType a => Param -> Param -> (Pattern a -> Pattern a) -> ParamPattern -> ParamPattern+follow :: (ParamType a, ParamType b) => Param -> Param -> (Pattern a -> Pattern b) -> ParamPattern -> ParamPattern follow source dest f p = p # (makeP dest $ f (get source p)) -- follow :: ParamType a => Param -> (Pattern a -> ParamPattern) -> ParamPattern -> ParamPattern
Sound/Tidal/Version.hs view
@@ -1,4 +1,4 @@ module Sound.Tidal.Version where -tidal_version = "0.9.1"+tidal_version = "0.9.3"
+ tests/test.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE OverloadedStrings #-}++import Test.Tasty+-- import Test.Tasty.SmallCheck as SC+-- import Test.Tasty.QuickCheck as QC+import Test.Tasty.HUnit++import Data.List+import Data.Ord++import Sound.Tidal.Context++main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [basic1,+ patternsOfPatterns+ ]++basic1 = testGroup "fast / slow"+ [+ testCase "silence" $ same16 (fast 1.1 silence) (silence :: Pattern Double),+ testCase "fast" $ same16 silence (silence :: Pattern Double),+ testCase "fast" $ same16 "bd*128" (rep 128 "bd")+ ]++patternsOfPatterns =+ testGroup "patterns of patterns"+ [+ testCase "decimal density" $ same16 (_discretise 0.25 saw) (discretise 0.25 saw)+ ]++rep :: Int -> String -> Pattern String+rep n v = p $ intercalate " " $ take n $ repeat v++sameN :: (Eq a, Show a) => String -> Time -> Pattern a -> Pattern a -> Assertion+sameN s n a b = assertEqual s (arc a (0,n)) (arc b (0,n))++same16 :: (Eq a, Show a) => Pattern a -> Pattern a -> Assertion+same16 = sameN "for 16 cycles," 16+
tidal.cabal view
@@ -1,5 +1,5 @@ name: tidal-version: 0.9.2+version: 0.9.3 synopsis: Pattern language for improvised music -- description: homepage: http://tidalcycles.org/@@ -11,7 +11,7 @@ Copyright: (c) Tidal contributors, 2017 category: Sound build-type: Simple-cabal-version: >=1.6+cabal-version: >=1.10 tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1 Extra-source-files: README.md tidal.el doc/tidal.md@@ -19,6 +19,8 @@ Description: Tidal is a domain specific language for live coding pattern. library+ default-language: Haskell2010+ Exposed-modules: Sound.Tidal.Bjorklund Sound.Tidal.Strategies Sound.Tidal.Dirt@@ -36,6 +38,7 @@ Sound.Tidal.Scales Sound.Tidal.Chords Sound.Tidal.Version+ Sound.Tidal.PatternList Build-depends: base < 5@@ -50,8 +53,22 @@ , safe , websockets > 0.8 , mtl >= 2.1- , applicative-numbers source-repository head type: git location: https://github.com/tidalcycles/Tidal++test-suite test+ default-language:+ Haskell2010+ type:+ exitcode-stdio-1.0+ hs-source-dirs:+ tests+ main-is:+ test.hs+ build-depends:+ base >= 4 && < 5+ , tasty >= 0.11+ , tasty-hunit+ , tidal