packages feed

hsc3-db-0.15: Sound/SC3/UGen/DB/Bindings.hs

-- | Generate (approximate) UGen binding functions from DB.
module Sound.SC3.UGen.DB.Bindings where

import Data.List {- base -}
import Data.Maybe {- base -}
import Text.Printf {- base -}

import Sound.SC3.UGen.Rate {- hsc3 -}
import Sound.SC3.UGen.UGen (sep_last) {- hsc3 -}

import Sound.SC3.UGen.DB
import Sound.SC3.UGen.DB.Rename
import Sound.SC3.UGen.DB.Record

bindings_mce :: U -> [String] -> ([String],Maybe String)
bindings_mce u i =
    if ugen_std_mce u
    then case sep_last i of
           Just (lhs,rhs) -> (lhs,Just rhs)
           Nothing -> error "bindings_mce: halt mce transform?"
    else (i,Nothing)

-- | Give name of 'Enum' give name of function to map to UGen input.
--
-- > unenumerator "Warp" == "from_warp"
-- > import Sound.SC3.UGen.Enum {- hsc3 -}
-- > from_warp Linear == 0
unenumerator :: String -> String
unenumerator en =
    case en of
      "Envelope UGen" -> "envelope_to_ugen"
      "Loop" -> "from_loop"
      "Interpolation" -> "from_interpolation"
      "DoneAction" -> "from_done_action"
      "Warp" -> "from_warp"
      _ -> error "unenumerator"

-- | If input is an enumeration add 'unenumerator' as prefix.
input_name_proc :: U -> (String,Int) -> String
input_name_proc u (nm,ix) =
    case input_enumeration u ix of
      Just en -> printf "(%s %s)" (unenumerator en) nm
      Nothing -> nm

-- | 'input_name_proc' of 'ugen_inputs'
--
-- > let r = ["start","end","dur","from_done_action doneAction"]
-- > in fmap u_input_names_proc (uLookup "Line") == Just r
u_input_names_proc :: U -> [String] -> [String]
u_input_names_proc u nms = map (input_name_proc u) (zip nms [0..])

-- | Bracket list.
--
-- > about ('[',']') "a,b" == "[a,b]"
about :: (a, a) -> [a] -> [a]
about (p,q) s = p : s ++ [q]

-- | 'about' of @[]@.
--
-- > brckt "a,b" == "[a,b]"
brckt :: String -> String
brckt = about ('[',']')

-- | 'about' of @""@.
quote :: [Char] -> [Char]
quote = about ('"','"')

-- | Variant that 'delete's empty inputs, useful for pretty printing.
--
-- > unwords ["a","","b"] == "a  b"
-- > unwords' ["a","","b"] == "a b"
unwords' :: [String] -> String
unwords' = unwords . filter (not . null)

-- | Alias for 'unwords''.
--
-- > ppl_space ["freq","phase"] == "freq phase"
ppl_space :: [String] -> String
ppl_space = unwords'

-- | Haskel list PP.
--
-- > ppl_list ["freq","phase"] == "[freq,phase]"
ppl_list :: [String] -> String
ppl_list = brckt . intercalate ","

-- | Generate haskell type signature for UGen constructor.
--
-- > fmap u_gen_type_sig (uLookup "Blip") -- oscillator
-- > fmap u_gen_type_sig (uLookup "A2K") -- fixed rate
-- > fmap u_gen_type_sig (uLookup "BufRd") -- variable channel oscillator
-- > fmap u_gen_type_sig (uLookup "Resonz") -- filter
-- > fmap u_gen_type_sig (uLookup "BrownNoise") -- non-deterministic
-- > fmap u_gen_type_sig (uLookup "Dseq") -- fixed rate / non-det
-- > fmap u_gen_type_sig (uLookup "EnvGen") -- enum
u_gen_type_sig :: U -> [String]
u_gen_type_sig u =
    let i = ugen_inputs u
        i_sig = map (fromMaybe "UGen" . (input_enumeration u)) [0 .. length i - 1]
        nm = [hs_rename_ugen (ugen_name u),"::"]
        nd = if ugen_nondet u then ["ID","a","=>","a","->"] else []
        o = if ugen_nc_input u then ["Int","->"] else []
        r = case ugen_filter u of
              Nothing -> case ugen_fixed_rate u of
                           Just _ -> []
                           Nothing -> ["Rate","->"]
              Just _ -> []
        i_sig' = intersperse "->" i_sig
        arr = if null i then [] else ["->"]
    in concat [nm,nd,o,r,i_sig',arr,["UGen"]]

-- | The @outputs@ field may be fixed (ie. @SinOsc@) or variable
-- (ie. @In@ or @Demand@).  The output is a @(lhs,rhs)@ pair, either
-- @("nc","nc")@ or @("","k")@.
--
-- > fmap u_outputs (uLookup "BufRd") == Just ("numChannels","numChannels")
-- > fmap u_outputs (uLookup "SinOsc") == Just ("","1")
-- > fmap u_outputs (uLookup "Demand") == Just ("","(length (mceChannels demandUGens) + 0)")
u_outputs :: U -> (String,String)
u_outputs u =
    case u_fixed_outputs u of
      Just d -> ("",show d)
      Nothing ->
          if ugen_nc_input u
          then ("numChannels","numChannels")
          else case ugen_nc_mce u of
                 Just j -> let i = input_name (last (ugen_inputs u))
                               j' = show j
                           in ("",concat ["(length (mceChannels ",i,") + ",j',")"])
                 Nothing -> error "u_outputs?" -- rhs...

-- | Generate oscillator UGen constructor.
--
-- > let f = fmap (u_gen_fun . u_rename) . uLookup
-- > let g = mapM_ (putStrLn . unwords') . mapMaybe f
-- > g (words "Blip BufRd Dseq Rand WhiteNoise")
-- > g (words "CoinGate HPZ1 Out Resonz")
u_gen_fun :: U -> [String]
u_gen_fun u =
    let nm_sc3 = ugen_name u
        nm_hs = hs_rename_ugen nm_sc3
        i_lhs = u_renamed_inputs u
        (i_rhs,mc) = case bindings_mce u (u_input_names_proc u i_lhs) of
                       (r,Nothing) -> (ppl_list r,"Nothing")
                       (r,Just mc') -> (ppl_list r,concat ["(Just ",mc',")"])
        (nd_lhs,nd_rhs) = if ugen_nondet u then ("z","(toUId z)") else ("","NoId")
        r_set = let rr = case ugen_operating_rates u of
                           [] -> all_rates
                           r' -> r'
                in map show rr
        (o_lhs,o_rhs) = u_outputs u
        (rt_lhs,rt_rhs) = case ugen_filter u of
                            Just ix' -> ("",concat ["(Right ",ppl_list (map show ix'),")"])
                            Nothing -> case ugen_fixed_rate u of
                                         Just rt' -> ("","(Left " ++ show rt' ++ ")")
                                         Nothing -> ("rate","(Left rate)")
    in concat [[nm_hs,nd_lhs,o_lhs,rt_lhs],i_lhs
              ,["=","mkUGen","Nothing"]
              ,[ppl_list r_set,rt_rhs,quote nm_sc3,i_rhs,mc,o_rhs,"(Special 0)",nd_rhs]]

-- | Binding as @[haddock-comment,type-signature,function-definition]@ triple.
--
-- > fmap (u_gen_binding . u_rename) (uLookup "LFGauss")
-- > fmap (u_gen_binding . u_rename) (uLookup "In")
-- > fmap (u_gen_binding . u_rename) (uLookup "EnvGen")
u_gen_binding :: U -> [String]
u_gen_binding u =
    let c = ["-- |",ugen_summary u]
        s = u_gen_type_sig u
        b = u_gen_fun u
    in map unwords' [c,s,b]

-- | 'u_gen_binding' of 'ugenDB'.
u_gen_bindings :: [String]
u_gen_bindings =
    let b = map (u_gen_binding . u_rename_db ugenDB) ugenDB
    in intercalate [""] b

u_bindings_preamble :: [String]
u_bindings_preamble =
    ["module Sound.SC3.UGen.Bindings.DB where"
    ,""
    ,"import Sound.SC3.UGen.Envelope"
    ,"import Sound.SC3.UGen.Enum"
    ,"import Sound.SC3.UGen.Identifier"
    ,"import Sound.SC3.UGen.Rate"
    ,"import Sound.SC3.UGen.Type"
    ,"import Sound.SC3.UGen.UGen"
    ,""]

-- | 'writeFile' of 'u_gen_bindings'.
--
-- > let fn = "/tmp/hsc3-bindings.hs"
-- > let fn = "/home/rohan/sw/hsc3/Sound/SC3/UGen/Bindings/DB.hs"
-- > u_gen_bindings_write fn
u_gen_bindings_write :: FilePath -> IO ()
u_gen_bindings_write fn = writeFile fn (unlines (u_bindings_preamble ++ u_gen_bindings))