packages feed

hsc3-rec-0.9: mk/build.hs

import Data.Char
import Sound.SC3.UGen.Rate
import qualified Sound.SC3.UGen.DB.Data as D
import System.FilePath
import Text.Printf

-- Rename parameters that conflict with keywords or prelude functions,
-- or which have otherwise unwieldy names.
rename_input :: String -> String
rename_input "in" = "input"
rename_input "channelsArray" = "input"
rename_input "exp" = "exp_"
rename_input "id" = "id_"
rename_input "length" = "length_"
rename_input "init" = "init_"
rename_input "floor" = "floor_"
rename_input "div" = "div_"
rename_input "max" = "max_"
rename_input "min" = "min_"
rename_input "spring" = "spring_"
rename_input "rate" = "rate_"
rename_input "default" = "default_"
rename_input s = s

-- Rename unit generators that conflict with keywords or prelude
-- functions.
rename_ugen :: String -> String
rename_ugen "in" = "in'"
rename_ugen s = s

input_name :: D.I -> String
input_name = rename_input . D.input_name

-- True if the unit generator can operate at multiple rates, in which
-- case the record will require a rate field.
needs_rate :: D.U -> Bool
needs_rate u = length (D.ugen_operating_rates u) > 1

-- In the case of one input rate use that instead of default rate,
-- which can be innacurate (see pitch for example).
fixed_rate :: D.U -> Rate
fixed_rate (D.U _ [r] _ _ _) = r
fixed_rate (D.U _ [] r _ _) = r
fixed_rate _ = undefined

-- List of field names and types.
inputs_of :: D.U -> [(String, String)]
inputs_of u =
    let is = D.ugen_inputs u
        bs = zip (map input_name is) (repeat "S.UGen")
    in if needs_rate u
       then ("rate", "S.Rate") : bs
       else bs

-- Append a character to each but the last string.
with_char :: Char -> [String] -> [String]
with_char c (x:y:xs) = (x ++ [c]) : with_char c (y:xs)
with_char _ xs = xs

with_comma :: [String] -> [String]
with_comma = with_char ','

with_space :: [String] -> [String]
with_space = with_char ' '

downcase_first_char :: String -> String
downcase_first_char [] = []
downcase_first_char (x:xs) = toLower x : xs

-- Construct the parameter record.
gen_param :: D.U -> [String]
gen_param u =
    let n = D.ugen_name u
        pre = [printf "data %s = %s {" n n]
        post = ["  } deriving (Show)"]
        f (nm, ty) = printf "  %s :: %s" nm ty
    in pre ++ with_comma (map f (inputs_of u)) ++ post

-- Construct a default instance of the parameter structure.
gen_defaults :: D.U -> [String]
gen_defaults u@(D.U n _ r is _) =
    let nm = rename_ugen (downcase_first_char n)
        pre = [ printf "%s :: %s" nm n
              , printf "%s = %s {" nm n]
        post = [ "  }" ]
        opt = if needs_rate u then [printf "  rate = S.%s," (show r)] else []
        f i = printf "  %s = %f" (input_name i) (D.input_default i)
    in pre ++ opt ++ with_comma (map f is) ++ post

-- Generate a list of variable names (a,b..)
var_names :: Int -> [String]
var_names n = map (\x -> x : "'") (take n ['a'..])

-- Generate the constructor for the unit generator.
gen_cons :: D.U -> [String]
gen_cons u@(D.U n _ _ is o) = 
    let xs = var_names (length is)
        nr = needs_rate u
        l_opt = if nr then "r" else ""
        r_opt = if nr then "r" else printf "S.%s" (show (fixed_rate u))
    in [ printf "mk%s :: %s -> S.UGen" n n
       , printf "mk%s (%s %s %s) = S.mkOsc %s \"%s\" [%s] %d" 
                n
                n
                l_opt
                (concat (with_space xs))
                r_opt
                n 
                (concat (with_comma xs))
                o]

-- Generate instance of the Make class.
gen_make :: D.U -> [String]
gen_make u =
    let n = D.ugen_name u
    in [ printf "instance Make %s where" n
       , printf "  ugen = mk%s" n ]

-- Path to write files to.
sc3_ugen_dir :: FilePath
sc3_ugen_dir = "Sound" </> "SC3" </> "UGen"

-- Write module for a unit generator.
write_module :: D.U -> IO ()
write_module u =
    let n = D.ugen_name u
        m = [ printf "module Sound.SC3.UGen.Record.%s where" n
            , "import qualified Sound.SC3.UGen as S"
            , "import Sound.SC3.UGen.Record" ]
        p = gen_param u
        d = gen_defaults u
        c = gen_cons u
        i = gen_make u
        fn = sc3_ugen_dir </> "Record" </> n <.> ".hs"
    in writeFile fn (unlines (m ++ p ++ d ++ c ++ i))

main :: IO ()
main = do
  let us = filter (\u -> not (null (D.ugen_inputs u))) D.ugenDB
  mapM_ write_module us

{-
gen_module_clause :: [D.U] -> [String]
gen_module_clause us =
    let f u = printf "  module Sound.SC3.UGen.Record.%s" (D.ugen_name u)
        pre = ["{-# LANGUAGE DisambiguateRecordFields #-}"
              ,"module Sound.SC3.UGen.Record.All ("]
        post = ["  ) where"]
    in pre ++ with_comma (map f us) ++ post

gen_imports :: [D.U] -> [String]
gen_imports = 
    let f u = printf "import Sound.SC3.UGen.Record.%s" (D.ugen_name u)
    in map f

write_records :: [D.U] -> IO ()
write_records us = 
    let s = unlines (gen_module_clause us ++ gen_imports us)
    in writeFile (sc3_ugen_dir </> "Record" </> "All" <.> "hs") s
-}