tidal-0.1: Rhythm.lhs
> {-# OPTIONS_GHC -XTypeSynonymInstances -XOverlappingInstances -XIncoherentInstances -XOverloadedStrings -XFlexibleInstances #-}
> module Rhythm where
> import GHC.Exts( IsString(..) )
> import Text.ParserCombinators.Parsec
> import qualified Text.ParserCombinators.Parsec.Token as P
> import Text.ParserCombinators.Parsec.Language ( haskellDef )
> import Data.List
> import Data.Maybe
> --import Text.Regex
> import Pattern
> import Data.Colour
> import Data.Colour.Names
> import Data.Colour.SRGB
> class Parseable a where
> p :: String -> Pattern a
> instance Parseable Double where
> p = parseRhythm pDouble
> instance Parseable String where
> p = parseRhythm pVocable
> instance Parseable Bool where
> p = parseRhythm pBool
> instance Parseable Int where
> p = parseRhythm pInt
> type ColourD = Colour Double
> instance Parseable ColourD where
> p = parseRhythm pColour
> instance (Parseable a) => IsString (Pattern a) where
> fromString = p
> lexer = P.makeTokenParser haskellDef
> braces = P.braces lexer
> brackets = P.brackets lexer
> parens = P.parens lexer
> symbol = P.symbol lexer
> natural = P.natural lexer
> float = P.float lexer
> naturalOrFloat = P.naturalOrFloat lexer
> data Sign = Positive | Negative
> applySign :: Num a => Sign -> a -> a
> applySign Positive = id
> applySign Negative = negate
> sign :: Parser Sign
> sign = do { char '-'
> ; return Negative
> }
> <|> do { char '+'
> ; return Positive
> }
> <|> return Positive
> intOrFloat :: Parser (Either Integer Double)
> intOrFloat = do { s <- sign
> ; num <- naturalOrFloat
> ; return (case num of
> Right x -> Right (applySign s x)
> Left x -> Left (applySign s x)
> )
> }
r :: String -> Pattern a -> IO (Pattern a)
r s orig = do catch (return $ p s)
(\err -> do putStrLn (show err)
return orig
)
playRhythm :: (Monad m, Show a) => Parser (Pattern a) -> String -> m [Char]
playRhythm f s = do let parsed = parseRhythm f s
return $ either (\e -> "Error" ++ show e) show parsed
> parseRhythm :: Parser (Pattern a) -> String -> (Pattern a)
> parseRhythm f input = either (const silence) id $ parse (pRhythm f') "" input
> where f' = f
> <|> do symbol "~" <?> "rest"
> return silence
> pRhythm :: Parser (Pattern a) -> GenParser Char () (Pattern a)
> pRhythm f = do spaces
> pSequence f
> pSequence :: Parser (Pattern a) -> GenParser Char () (Pattern a)
> pSequence f = do ps <- many $ pPart f
> return $ cat ps
> pPart :: Parser (Pattern a) -> Parser (Pattern a)
> pPart f = do part <- parens (pSequence f) <|> f <|> pPoly f <|> pPolyPad f
> spaces
> return part
> pPoly :: Parser (Pattern a) -> Parser (Pattern a)
> pPoly f = do ps <- brackets (pRhythm f `sepBy` symbol ",")
> return $ combine ps
> pPolyPad :: Parser (Pattern a) -> Parser (Pattern a)
> pPolyPad f = do ps <- braces (pRhythm f `sepBy` symbol ",")
> return $ combinePad ps
> pString :: Parser (String)
> pString = many1 (letter <|> oneOf "0123456789" <|> char '/') <?> "string"
> pVocable :: Parser (Pattern String)
> pVocable = do v <- pString
> return $ atom v
> pDouble :: Parser (Pattern Double)
> pDouble = do nf <- intOrFloat <?> "float"
> let f = either fromIntegral id nf
> return $ atom f
> pBool :: Parser (Pattern Bool)
> pBool = do oneOf "t1"
> return $ atom True
> <|>
> do oneOf "f0"
> return $ atom False
> pColour :: Parser (Pattern ColourD)
> pColour = do name <- many1 letter <?> "colour name"
> colour <- readColourName name <?> "known colour"
> return $ atom colour
> pInt :: Parser (Pattern Int)
> pInt = do i <- natural <?> "integer"
> return $ atom (fromIntegral i)
> doubleToGray :: Double -> ColourD
> doubleToGray n = let shade = n in
> sRGB shade shade shade