-- | Binary 'Graph Definition' as understood by @scsynth@. module Sound.SC3.Server.Graphdef where import Control.Monad {- base -} import qualified Data.ByteString.Lazy as L {- bytestring -} import qualified Data.ByteString.Char8 as C {- bytestring -} import Data.List import System.IO {- base -} import Sound.OSC.Coding.Byte {- hosc -} import Sound.OSC.Coding.Cast {- hosc -} import Sound.OSC.Type {- hosc -} -- * Type type Name = ASCII type Control = (Name,Int) type Sample = Double data Input = Input Int Int deriving (Eq,Show) input_ugen_ix :: Input -> Maybe Int input_ugen_ix (Input u p) = if p == -1 then Nothing else Just u type Output = Int type Rate = Int type Special = Int type UGen = (Name,Rate,[Input],[Output],Special) ugen_inputs :: UGen -> [Input] ugen_inputs (_,_,i,_,_) = i ugen_outputs :: UGen -> [Output] ugen_outputs (_,_,_,o,_) = o ugen_is_control :: UGen -> Bool ugen_is_control (nm,_,_,_,_) = ascii_to_string nm `elem` ["Control","LagControl","TrigControl"] ugen_rate :: UGen -> Rate ugen_rate (_,r,_,_,_) = r input_is_control :: Graphdef -> Input -> Bool input_is_control g (Input u _) = if u == -1 then False else ugen_is_control (graphdef_ugen g u) data Graphdef = Graphdef {graphdef_name :: Name ,graphdef_constants :: [Sample] ,graphdef_controls :: [(Control,Sample)] ,graphdef_ugens :: [UGen]} deriving (Eq,Show) graphdef_ugen :: Graphdef -> Int -> UGen graphdef_ugen g = (graphdef_ugens g !!) graphdef_control :: Graphdef -> Int -> (Control,Sample) graphdef_control g = (graphdef_controls g !!) graphdef_constant_nid :: Graphdef -> Int -> Int graphdef_constant_nid _ = id graphdef_control_nid :: Graphdef -> Int -> Int graphdef_control_nid g = (+) (length (graphdef_constants g)) graphdef_ugen_nid :: Graphdef -> Int -> Int graphdef_ugen_nid g n = graphdef_control_nid g 0 + length (graphdef_controls g) + n -- * Read read_i8 :: Handle -> IO Int read_i8 h = fmap decode_i8 (L.hGet h 1) read_i16 :: Handle -> IO Int read_i16 h = fmap decode_i16 (L.hGet h 2) read_i32 :: Handle -> IO Int read_i32 h = fmap decode_i32 (L.hGet h 4) read_sample :: Handle -> IO Sample read_sample h = fmap (realToFrac . decode_f32) (L.hGet h 4) read_pstr :: Handle -> IO ASCII read_pstr h = do n <- fmap decode_u8 (L.hGet h 1) fmap decode_str (L.hGet h n) read_control :: Handle -> IO Control read_control h = do nm <- read_pstr h ix <- read_i16 h return (nm,ix) read_input :: Handle -> IO Input read_input h = do u <- read_i16 h p <- read_i16 h return (Input u p) read_output :: Handle -> IO Int read_output = read_i8 read_ugen :: Handle -> IO UGen read_ugen h = do name <- read_pstr h rate <- read_i8 h number_of_inputs <- read_i16 h number_of_outputs <- read_i16 h special <- read_i16 h inputs <- replicateM number_of_inputs (read_input h) outputs <- replicateM number_of_outputs (read_output h) return (name ,rate ,inputs ,outputs ,special) read_graphdef :: Handle -> IO Graphdef read_graphdef h = do magic <- L.hGet h 4 version <- read_i32 h number_of_definitions <- read_i16 h when (magic /= L.pack (map (fromIntegral . fromEnum) "SCgf")) (error "read_graphdef: illegal magic string") when (version /= 0) (error "read_graphdef: version not at zero") when (number_of_definitions /= 1) (error "read_graphdef: non unary graphdef file") name <- read_pstr h number_of_constants <- read_i16 h constants <- replicateM number_of_constants (read_sample h) number_of_control_defaults <- read_i16 h control_defaults <- replicateM number_of_control_defaults (read_sample h) number_of_controls <- read_i16 h controls <- replicateM number_of_controls (read_control h) number_of_ugens <- read_i16 h ugens <- replicateM number_of_ugens (read_ugen h) return (Graphdef name constants (zip controls control_defaults) ugens) -- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/simple.scsyndef" -- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/with-ctl.scsyndef" -- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/mce.scsyndef" -- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/mrg.scsyndef" read_graphdef_file :: FilePath -> IO Graphdef read_graphdef_file nm = do h <- openFile nm ReadMode g <- read_graphdef h hClose h return g -- * Encode -- | Pascal (length prefixed) encoding of string. encode_pstr :: ASCII -> L.ByteString encode_pstr = L.pack . str_pstr . ascii_to_string -- | Byte-encode 'Input' value. encode_input :: Input -> L.ByteString encode_input (Input u p) = L.append (encode_i16 u) (encode_i16 p) encode_control :: Control -> L.ByteString encode_control (nm,k) = L.concat [encode_pstr nm,encode_i16 k] -- | Byte-encode 'UGen'. encode_ugen :: UGen -> L.ByteString encode_ugen (nm,r,i,o,s) = L.concat [encode_pstr nm ,encode_i8 r ,encode_i16 (length i) ,encode_i16 (length o) ,encode_i16 s ,L.concat (map encode_input i) ,L.concat (map encode_i8 o)] encode_sample :: Sample -> L.ByteString encode_sample = encode_f32 . realToFrac encode_graphdef :: Graphdef -> L.ByteString encode_graphdef (Graphdef nm cs ks us) = let (ks_ctl,ks_def) = unzip ks in L.concat [encode_str (C.pack "SCgf") ,encode_i32 0 -- version ,encode_i16 1 -- number of graphs ,encode_pstr nm ,encode_i16 (length cs) ,L.concat (map encode_sample cs) ,encode_i16 (length ks_def) ,L.concat (map encode_sample ks_def) ,encode_i16 (length ks_ctl) ,L.concat (map encode_control ks_ctl) ,encode_i16 (length us) ,L.concat (map encode_ugen us)] -- * Stat graphdef_stat :: Graphdef -> String graphdef_stat (Graphdef _ cs ks us) = let u_nm (sc3_nm,_,_,_,_) = ascii_to_string sc3_nm f g = let h (x:xs) = (x,length (x:xs)) h [] = error "graphdef_stat" in show . map h . group . sort . map g sq = intercalate "," (map u_nm us) in unlines ["number of constants : " ++ show (length cs) ,"number of controls : " ++ show (length ks) ,"number of unit generators : " ++ show (length us) ,"unit generator rates : " ++ f ugen_rate us ,"unit generator sequence : " ++ sq]