module Sound.DF.Uniform.Faust where
import qualified Data.Graph.Inductive as G
import Data.Maybe
import Data.List
import qualified Data.List.Split as S
import Data.Typeable
import Sound.OSC
import System.Process
import Text.Printf
import qualified Sound.DF.Uniform.LL.Audition as L
import Sound.DF.Uniform.LL.CGen
import Sound.DF.Uniform.LL.Dot
import Sound.DF.Uniform.LL.K
import Sound.DF.Uniform.LL.UId
type Rec_Id = (Id,Id,TypeRep)
data BD = Constant (Maybe Id) K
| Prim (Maybe Id) String [TypeRep] (Maybe TypeRep)
| Par BD BD
| Seq BD BD
| Split BD BD
| Rec (Maybe [Rec_Id]) BD BD
deriving (Eq,Show)
instance Num BD where
p + q = (p ~. q) ~:> (ty_add p q)
p q = (p ~. q) ~:> (ty_sub p q)
p * q = (p ~. q) ~:> (ty_mul p q)
abs = ty_uop bd_ty_uniform i_abs r_abs
negate = ty_uop bd_ty_uniform i_negate r_negate
signum = undefined
fromInteger = i_constant . fromInteger
instance Fractional BD where
p / q = (p ~. q) ~:> (ty_div p q)
fromRational = r_constant . fromRational
bd_id :: BD -> Maybe Id
bd_id bd =
case bd of
Constant k _ -> k
Prim k _ _ _ -> k
_ -> Nothing
bd_req_id :: BD -> Id
bd_req_id = fromMaybe (error "bd_req_id") . bd_id
bd_pp :: BD -> String
bd_pp bd =
case bd of
Constant _ n -> show n
Prim _ nm _ _ -> nm
Par _ _ -> ","
Seq _ _ -> ":"
Split _ _ -> "<:"
Rec _ _ _ -> "~"
bd_signature :: BD -> ([TypeRep],[TypeRep])
bd_signature = let f = map port_ty in bimap f f . ports
bd_ty :: BD -> [TypeRep]
bd_ty = map port_ty . snd . ports
bd_ty_uniform :: BD -> Maybe TypeRep
bd_ty_uniform bd =
case nub (bd_ty bd) of
[t] -> Just t
_ -> Nothing
bd_ty1 :: BD -> Maybe TypeRep
bd_ty1 bd =
let (_,op) = ports bd
in case op of
[Output_Port (Constant _ n)] -> Just (typeOf n)
[Output_Port (Prim _ _ _ (Just o))] -> Just o
_ -> Nothing
infixl 4 `rec`, ~~
infixl 3 `Par`, ~.
infixl 2 `Seq`, ~:
infixl 1 `split`, ~<:
infixl 1 `merge`, ~:>
(~~) :: BD -> BD -> BD
(~~) = rec
(~.) :: BD -> BD -> BD
(~.) = Par
(~:) :: BD -> BD -> BD
(~:) = Seq
(~<:) :: BD -> BD -> BD
(~<:) = split
(~:>) :: BD -> BD -> BD
(~:>) = merge
bd_foldl :: (t -> BD -> t) -> t -> BD -> t
bd_foldl f st bd =
let g p q = f (bd_foldl f (bd_foldl f st p) q) bd
in case bd of
Constant _ _ -> f st bd
Prim _ _ _ _ -> f st bd
Par p q -> g p q
Seq p q -> g p q
Split p q -> g p q
Rec _ p q -> g p q
bd_traverse :: (st -> BD -> (st,BD)) -> st -> BD -> (st,BD)
bd_traverse f st bd =
let g j t p q = let (t',p') = bd_traverse f t p
(t'',q') = bd_traverse f t' q
in f t'' (j p' q')
in case bd of
Constant _ _ -> f st bd
Prim _ _ _ _ -> f st bd
Par p q -> g Par st p q
Seq p q -> g Seq st p q
Split p q -> g Split st p q
Rec k p q -> g (Rec k) st p q
rec_ids :: Id -> Int -> [TypeRep] -> [Rec_Id]
rec_ids k n ty =
let k' = k + (n * 2) 1
(p,q) = unzip (adjacent [k .. k'])
in zip3 p q ty
bd_set_id :: BD -> (Id,BD)
bd_set_id =
let f k bd =
case bd of
Constant _ n -> (k + 1,Constant (Just k) n)
Prim _ nm i o -> (k + 1,Prim (Just k) nm i o)
Rec _ p q -> let n = out_degree q
k' = rec_ids k n (bd_ty q)
in (k + (n * 2),Rec (Just k') p q)
_ -> (k,bd)
in bd_traverse f 0
type Degree = (Int,Int)
degree :: BD -> Degree
degree bd =
case bd of
Constant _ _ -> (0,1)
Prim _ _ i o -> (length i,maybe 0 (const 1) o)
Par p q -> (in_degree p + in_degree q,out_degree p + out_degree q)
Seq p q ->
let (ip,op) = degree p
(iq,oq) = degree q
in case op `compare` iq of
EQ -> (ip,oq)
GT -> (ip,oq + op iq)
LT -> (ip + iq op,oq)
Split p q -> (in_degree p,out_degree q)
Rec _ p q -> (in_degree p out_degree q,out_degree p)
in_degree :: BD -> Int
in_degree = fst . degree
out_degree :: BD -> Int
out_degree = snd . degree
type Port_Index = Int
data Port = Input_Port {port_bd :: BD, port_index :: Port_Index}
| Output_Port {port_bd :: BD}
deriving (Eq,Show)
ports :: BD -> ([Port],[Port])
ports bd =
case bd of
Constant _ _ -> ([],[Output_Port bd])
Prim _ _ i o -> (map (Input_Port bd) [0 .. length i 1]
,maybe [] (const [Output_Port bd]) o)
Par p q -> let (ip,op) = ports p
(iq,oq) = ports q
in (ip ++ iq,op ++ oq)
Seq p q ->
let (_,opk) = degree p
(iqk,_) = degree q
(ip,op) = ports p
(iq,oq) = ports q
in case opk `compare` iqk of
EQ -> (ip,oq)
GT -> (ip,oq ++ (drop iqk op))
LT -> (ip ++ (drop opk iq),oq)
Split p q -> let (ip,_) = ports p
(_,oq) = ports q
in (ip,oq)
Rec _ p q -> let (ip,op) = ports p
k = out_degree q
in (drop k ip,op)
port_ty :: Port -> TypeRep
port_ty p =
case p of
Output_Port (Constant _ n) -> typeOf n
Input_Port (Prim _ _ i _) k -> i !! k
Output_Port (Prim _ _ _ (Just o)) -> o
_ -> undefined
data Wire_Ty = Normal
| Backward Rec_Id
| Implicit_Normal
| Implicit_Rec
| Implicit_Backward
deriving (Eq,Show)
type Wire = (Port,Port,Wire_Ty)
normal_wires :: [Port] -> [Port] -> [Wire]
normal_wires = let f p q = (p,q,Normal) in zipWith f
rec_back_wires :: [Rec_Id] -> [Port] -> [Port] -> [Wire]
rec_back_wires = let f k p q = (p,q,Backward k) in zipWith3 f
wires_immed :: BD -> [Wire]
wires_immed bd =
case bd of
Constant _ _ -> []
Prim _ _ _ _ -> []
Par _ _ -> []
Seq p q -> let (_,op) = ports p
(iq,_) = ports q
in normal_wires op iq
Split p q -> let (_,op) = ports p
(iq,_) = ports q
in normal_wires (cycle op) iq
Rec (Just k) p q ->
let (ip,op) = ports p
(iq,oq) = ports q
in rec_back_wires k oq ip ++ normal_wires op iq
Rec Nothing _ _ -> error "wires_immed"
wires :: BD -> [Wire]
wires = bd_foldl (\st r -> st ++ wires_immed r) []
wire_coheres :: Wire -> Bool
wire_coheres (p,q,_) = port_ty p == port_ty q
bd_non_coherent :: BD -> [Wire]
bd_non_coherent = filter (not . wire_coheres) . wires
bd_is_coherent :: BD -> Bool
bd_is_coherent = null . bd_non_coherent
data Node = N_Constant {n_constant_id :: Id
,n_constant_k :: K}
| N_Prim {n_prim_id :: Either Id (Id,Id)
,n_prim_name :: String
,n_prim_in_degree :: Int
,n_prim_ty :: Maybe TypeRep}
deriving (Eq)
actual_id :: Either Id (Id,Id) -> Id
actual_id = either id fst
node_ty :: Node -> Maybe TypeRep
node_ty n =
case n of
N_Constant _ k -> Just (k_typeOf k)
N_Prim _ _ _ ty -> ty
node_id :: Node -> Id
node_id n =
case n of
N_Constant k _ -> k
N_Prim k _ _ _ -> actual_id k
node_lift_id :: Node -> (Id,Node)
node_lift_id n = (node_id n,n)
node_pp :: Node -> String
node_pp n =
case n of
N_Constant _ k -> k_concise k
N_Prim _ nm _ _ -> nm
instance Show Node where show = node_pp
type Edge = (Id,Id,(Port_Index,Wire_Ty))
type Graph = ([Node],[Edge])
edge_is_implicit_backward :: Edge -> Bool
edge_is_implicit_backward (_,_,(_,ty)) = ty == Implicit_Backward
rec_nodes :: [Rec_Id] -> [Node]
rec_nodes =
let f (i,j,ty) = [N_Prim (Right (i,i)) "df_rec_w" 1 (Just ty)
,N_Prim (Right (j,i)) "df_rec_r" 1 (Just ty)]
in concatMap f
nodes :: Bool -> BD -> [Node]
nodes impl bd =
let f = nodes impl
in case bd of
Constant (Just k) n -> [N_Constant k n]
Constant _ _ -> error "nodes"
Prim (Just k) nm i o -> [N_Prim (Left k) nm (length i) o]
Prim _ _ _ _ -> error "nodes"
Par p q -> f p ++ f q
Seq p q -> f p ++ f q
Split p q -> f p ++ f q
Rec (Just k) p q -> f p ++ f q ++ if impl then rec_nodes k else []
Rec _ _ _ -> error "nodes"
wire_to_edges :: Bool -> Wire -> [Edge]
wire_to_edges impl w =
case w of
(Output_Port p,Input_Port q qn,Backward (k0,k1,ty)) ->
if impl
then [(bd_req_id p,k0,(0,Implicit_Rec))
,(k1,bd_req_id q,(qn,Implicit_Normal))
,(k0,k1,(0,Implicit_Backward))]
else [(bd_req_id p,bd_req_id q,(qn,Backward (k0,k1,ty)))]
(Output_Port p,Input_Port q qn,Normal) ->
[(bd_req_id p,bd_req_id q,(qn,Normal))]
_ -> error (show ("wire_to_edges",w))
wires_to_edges :: Bool -> [Wire] -> [Edge]
wires_to_edges impl = concatMap (wire_to_edges impl)
edges :: Bool -> BD -> [Edge]
edges impl = wires_to_edges impl . wires
graph' :: Bool -> BD -> Graph
graph' impl bd =
let (_,bd') = bd_set_id bd
n = nub (nodes impl bd')
w = wires bd'
e = wires_to_edges impl w
in case filter (not . wire_coheres) w of
[] -> (n,e)
w' -> error (show ("graph': incoherent",w'))
graph :: BD -> Graph
graph = graph' False
type Gr = G.Gr Node (Port_Index,Wire_Ty)
gr :: BD -> Gr
gr bd =
let (n,e) = graph' True bd
n' = map node_lift_id n
e' = filter (not . edge_is_implicit_backward) e
in G.mkGraph n' e'
tsort :: BD -> Graph
tsort bd =
let g = gr bd
in (map (fromMaybe (error "tsort") . G.lab g) (G.topsort g)
,G.labEdges g)
gr_dot :: BD -> String
gr_dot = G.graphviz' . gr
gr_draw :: BD -> IO ()
gr_draw = draw_dot . gr_dot
dot_node :: Node -> String
dot_node nd =
case nd of
N_Constant k c -> dot_rec' k (k_concise c) [] (k_typeOf c)
N_Prim k nm i o -> dot_rec (actual_id k) nm (dot_rec_ar i) o
wire_colour :: Wire_Ty -> String
wire_colour w =
case w of
Normal -> "black"
Backward _ -> "red"
Implicit_Normal -> "grey"
Implicit_Rec -> "blue"
Implicit_Backward -> "red"
dot_edge :: Edge -> String
dot_edge (p,q,(k,d)) =
let c = wire_colour d
in printf "%d:o_0 -> %d:i_%d %s;" p q k (dot_attr [("color",c)])
dot_graph :: Graph -> [String]
dot_graph (n,e) =
concat [["digraph Anonymous {"
,"graph [splines=false];"]
,map dot_node n
,map dot_edge e
,["}"]]
draw_dot :: String -> IO ()
draw_dot d = do
writeFile "/tmp/faust.dot" d
_ <- system "dotty /tmp/faust.dot"
return ()
draw :: Graph -> IO ()
draw = draw_dot . unlines . dot_graph
par_l :: [BD] -> BD
par_l = foldr1 Par
bd_sum :: [BD] -> BD
bd_sum l =
case l of
[] -> error "bd_sum"
[d] -> d
p:q:l' -> bd_sum (((p ~. q) ~: (ty_add p q)) : l')
split_r :: BD -> BD -> Bool
split_r p q =
let (i,j) = in_degree q `divMod` out_degree p
in i >= 1 && j == 0
split_m :: BD -> BD -> Maybe BD
split_m p q =
if split_r p q
then Just (Split p q)
else Nothing
split :: BD -> BD -> BD
split p = fromMaybe (error "split") . split_m p
merge_degree :: BD -> BD -> Maybe Int
merge_degree p q =
let (i,j) = out_degree p `divMod` in_degree q
in if i > 1 && j == 0 then Just i else Nothing
merge_m :: BD -> BD -> Maybe BD
merge_m p q =
case merge_degree p q of
Just n ->
let (_,op) = ports p
(iq,_) = degree q
op' = map port_bd op
p' = if iq == 1
then [op']
else transpose (S.chunksOf n op')
in Just (Seq (par_l (map bd_sum p')) q)
_ -> Nothing
merge :: BD -> BD -> BD
merge p = fromMaybe (error "merge") . merge_m p
rec_r :: BD -> BD -> Bool
rec_r p q = out_degree p >= in_degree q && in_degree p >= out_degree q
rec_m :: BD -> BD -> Maybe BD
rec_m p q =
if rec_r p q
then Just (Rec Nothing p q)
else Nothing
rec :: BD -> BD -> BD
rec p = fromMaybe (error "rec") . rec_m p
i_constant :: Int -> BD
i_constant = Constant Nothing . I . fromIntegral
r_constant :: Float -> BD
r_constant = Constant Nothing . F
u_prim :: TypeRep -> String -> Int -> BD
u_prim ty nm i = Prim Nothing nm (replicate i ty) (Just ty)
i_prim :: String -> Int -> BD
i_prim = u_prim int32_t
r_prim :: String -> Int -> BD
r_prim = u_prim float_t
i_add,r_add :: BD
i_add = i_prim "df_add" 2
r_add = r_prim "df_add" 2
i_sub,r_sub :: BD
i_sub = i_prim "df_sub" 2
r_sub = r_prim "df_sub" 2
i_mul,r_mul :: BD
i_mul = i_prim "df_mul" 2
r_mul = r_prim "df_mul" 2
i_div :: BD
i_div = i_prim "df_div" 2
r_div :: BD
r_div = r_prim "df_div" 2
i_abs,r_abs :: BD
i_abs = i_prim "df_abs" 1
r_abs = r_prim "df_abs" 1
i_negate,r_negate :: BD
i_negate = i_prim "df_negate" 1
r_negate = r_prim "df_negate" 1
i_identity, r_identity :: BD
i_identity = u_prim int32_t "df_identity" 1
r_identity = u_prim float_t "df_identity" 1
float_to_int32 :: BD
float_to_int32 = Prim Nothing "df_float_to_int32" [float_t] (Just int32_t)
int32_to_float :: BD
int32_to_float = Prim Nothing "df_int32_to_float" [int32_t] (Just float_t)
i32_to_normal_f32 :: BD
i32_to_normal_f32 = (int32_to_float ~. 2147483647.0) ~: r_div
out1 :: BD
out1 = Prim Nothing "df_out1" [float_t] Nothing
ty_uop :: (BD -> Maybe TypeRep) -> t -> t -> BD -> t
ty_uop ty f g p =
let p' = ty p
in if p' == Just int32_t
then f
else if p' == Just float_t
then g
else error "ty_uop"
ty_binop :: (BD -> Maybe TypeRep) -> t -> t -> BD -> BD -> t
ty_binop ty f g p q =
let p' = ty p
q' = ty q
in if p' == Just int32_t && q' == Just int32_t
then f
else if p' == Just float_t && q' == Just float_t
then g
else error "ty_binop"
ty_add,ty_sub,ty_mul,ty_div :: BD -> BD -> BD
ty_add = ty_binop bd_ty_uniform i_add r_add
ty_sub = ty_binop bd_ty_uniform i_sub r_sub
ty_mul = ty_binop bd_ty_uniform i_mul r_mul
ty_div = ty_binop bd_ty_uniform i_div r_div
ty_add1,ty_mul1,ty_div1 :: BD -> BD -> BD
ty_add1 = ty_binop bd_ty1 i_add r_add
ty_mul1 = ty_binop bd_ty1 i_mul r_mul
ty_div1 = ty_binop bd_ty1 i_div r_div
cg_k :: [Node] -> [(Id,K)]
cg_k =
let f n = case n of
N_Constant k c -> Just (k,c)
_ -> Nothing
in mapMaybe f
cg_node_var :: Node -> Maybe Var
cg_node_var n =
case n of
N_Constant k c -> Just (k_var k Std_Var c)
N_Prim k _ _ (Just ty) ->
case k of
Left k' -> Just (Std_Var,ty,k',Nothing)
Right (k',k'') ->
if k' == k''
then Just (Rec_Var,ty,k',Just (Left 0))
else Just (Std_Var,ty,k',Nothing)
N_Prim _ _ _ Nothing -> Nothing
node_output :: Node -> Maybe (Var_Ty,Id)
node_output n =
case n of
N_Prim _ _ _ Nothing -> Nothing
N_Constant k _ -> Just (Std_Var,k)
N_Prim (Right (k,_)) "df_rec_r" _ _ -> Just (Std_Var,k)
N_Prim (Right (k,_)) "df_rec_w" _ _ -> Just (Rec_Var,k)
N_Prim (Right _) _ _ _ -> error "node_output: Right"
N_Prim (Left k) _ _ _ -> Just (Std_Var,k)
node_inputs :: [Edge] -> Node -> [(Var_Ty,Id)]
node_inputs e n =
let f k (_,k',_) = k == k'
g (k,_,(p,_)) = (p,k)
i = sort (map g (filter (f (node_id n)) e))
in case n of
N_Prim (Right (_,k)) "df_rec_r" _ _ -> [(Rec_Var,k)]
_ -> zip (repeat Std_Var) (map snd i)
cg_node_c_call :: [Edge] -> Node -> Maybe C_Call
cg_node_c_call e n =
case n of
N_Constant _ _ -> Nothing
N_Prim _ nm _ _ -> let i = node_inputs e n
i' = case node_output n of
Just o -> o : i
Nothing -> i
in Just (Nothing,nm,i')
bd_instructions :: BD -> Instructions
bd_instructions bd =
let (n,e) = tsort bd
in (cg_k n
,mapMaybe cg_node_var n
,mapMaybe (cg_node_c_call e) n)
audition :: [Message] -> BD -> IO ()
audition is bd = L.audition is (bd_instructions bd)
fig_3_2 :: BD
fig_3_2 = 10.0 ~. r_mul
fig_3_3 :: BD
fig_3_3 = (r_mul ~. r_div) ~: r_add
fig_3_4 :: BD
fig_3_4 = (10.0 ~. 20.0) ~<: (par_l [r_add,r_mul,r_div])
fig_3_5 :: BD
fig_3_5 = par_l [10,20,30,40] ~:> i_mul
fig_3_6 :: BD
fig_3_6 =
let p = 12345 ~: i_add
q = 1103515245 ~: i_mul
in p ~~ q
fig_3_6' :: BD
fig_3_6' = fig_3_6 ~: i32_to_normal_f32 ~: (0.1 ~: r_mul) ~: out1
i_counter :: BD
i_counter = (1 ~: i_add) ~~ i_identity
adjacent :: [t] -> [(t,t)]
adjacent l =
case l of
[] -> []
p:q:l' -> (p,q) : adjacent l'
_ -> error "adjacent"
bimap :: (a -> b) -> (c -> d) -> (a,c) -> (b,d)
bimap f g (p,q) = (f p,g q)