Permalink
Cannot retrieve contributors at this time
| #!/usr/bin/env runhaskell | |
| -- Generate data.h and data.c | |
| import Data.List (intercalate, partition, unfoldr) | |
| -- http://unthingable.eat-up.org/tag/music.html | |
| -- | Euclidean rhythm generator | |
| euclidean :: Int -- ^ fill steps | |
| -> Int -- ^ total steps | |
| -> [Bool] | |
| euclidean k n = concat . efold $ | |
| replicate k [True] ++ replicate (n - k) [False] | |
| -- | A hybrid of zip and outer join, takes two lists of different lengths and | |
| -- concatenates their elements pairwise | |
| ezip :: [[a]] -> [[a]] -> [[a]] | |
| ezip x [] = x | |
| ezip [] x = x | |
| ezip (x:xs) (y:ys) = (x ++ y) : ezip xs ys | |
| -- | Repeatedly applies the tail end of the sequence under construction to the | |
| -- head, using ezip, until either there are 3 or fewer subpatterns or the | |
| -- pattern is cyclic. | |
| efold :: Eq a => [[a]] -> [[a]] | |
| efold xs | |
| | length xs <= 3 = xs | |
| | null a = xs | |
| | otherwise = efold $ ezip a b | |
| where (a, b) = partition (/= last xs) xs | |
| -- | All the Euclidean rhythms of a given length | |
| allEuclideans :: Int -> [[Bool]] | |
| allEuclideans n = fmap (`euclidean` n) [0..n] | |
| -- | Pad a list of `Bool` with False out to length `n` | |
| pad :: Int -> [Bool] -> [Bool] | |
| pad n xs | |
| | length xs >= n = xs | |
| | otherwise = pad n $ xs ++ [False] | |
| -- | Chunking algorithm | |
| chunk :: Int -> [a] -> [[a]] | |
| chunk n = takeWhile (not.null) . unfoldr (Just . splitAt n) | |
| -- | Split a list of bools into a list of list of bools, each padded to 8 bits | |
| -- (e.g char) | |
| bitArray :: [Bool] -> [[Bool]] | |
| bitArray xs = pad 8 <$> chunk 8 xs | |
| bitArrayCLiteral :: [[[Bool]]] -> String | |
| bitArrayCLiteral xs = multiLineCArray $ oneLineCArray <$> (fmap . fmap) bitCLiteral xs | |
| where oneLineCArray :: [String] -> String | |
| oneLineCArray ys = "{" ++ intercalate ", " ys ++ "}" | |
| multiLineCArray :: [String] -> String | |
| multiLineCArray ys = "{\n" ++ intercalate ",\n" (fmap ("\t"++) ys) ++ "\n}" | |
| bitCLiteral :: [Bool] -> String | |
| bitCLiteral ys = "0b" ++ fmap lit ys | |
| where lit True = '1' | |
| lit False = '0' | |
| bytes :: Int -> Int | |
| bytes x = x `div` 8 + p | |
| where p = if x `rem` 8 > 0 then 1 else 0 | |
| arrayPrototype :: Int -> String | |
| arrayPrototype x = "const char table_euclidean_" ++ show x ++ "[" ++ show (x + 1) ++ "][" ++ show (bytes x) ++ "]" | |
| array :: Int -> String | |
| array x = arrayPrototype x ++ " = " ++ bitArrayCLiteral (bitArray <$> allEuclideans x) ++ ";" | |
| dataC :: String | |
| dataC = intercalate "\n\n" (fmap array [1..32]) ++ "\n" | |
| headerC :: String | |
| headerC = "#ifndef _EUCLIDEAN_DATA_H_\n" ++ | |
| "#define _EUCLIDEAN_DATA_H_\n" ++ | |
| concatMap (\i -> "extern " ++ arrayPrototype i ++ ";\n") [1..32] ++ | |
| "#endif\n" | |
| main :: IO () | |
| main = do writeFile "data.h" headerC | |
| writeFile "data.c" dataC |