Skip to content
Permalink
d3b7409527
Go to file
 
 
Cannot retrieve contributors at this time
executable file 84 lines (67 sloc) 2.7 KB
#!/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