packages feed

hsc3-0.20: Sound/Sc3/Server/Graphdef/Text.hs

{- | .scsyndef file encoded in plain text.
There are reader and writer functions.
-}
module Sound.Sc3.Server.Graphdef.Text where

import Control.Monad {- base -}
import Data.Char {- base -}
import Data.Functor.Identity {- base -}

import qualified Numeric {- base -}

import qualified Control.Monad.State as S {- mtl -}

import qualified Sound.Osc.Datum as Datum {- hosc -}

import Sound.Sc3.Server.Graphdef {- hsc3 -}

-- | * Printer

-- | Print string.  Strings must not have internal whitespace or semi-colons.  A quoting system could allow these if required.
print_string :: Datum.Ascii -> String
print_string a =
  let s = Datum.ascii_to_string a
  in if any isSpace s || ';' `elem` s then error "print_string" else s

-- | 'Encode_Functions' for plain text output.
enc_text :: (String -> String) -> Encode_Functions String
enc_text com_f =
  (unwords . filter (not . null),print_string,show,show,show,\n -> Numeric.showFFloat Nothing n ""
  ,com_f)

{- | 'encode_graphdef_f' of 'enc_text' with optional semi-colon delimited comments.

> import Sound.Sc3.Server.Graphdef.Binary
> dir = "/home/rohan/sw/rsc3-disassembler/scsyndef/"
> pp nm = read_graphdef_file (dir ++ nm) >>= putStrLn . print_graphdef True
> pp "simple.scsyndef"
> pp "with-ctl.scsyndef"
> pp "mce.scsyndef"
> pp "mrg.scsyndef"
-}
print_graphdef :: Bool -> Graphdef -> String
print_graphdef with_com =
    let com_f = if with_com then \c -> concat ["\n; ",c,"\n"] else const ""
    in encode_graphdef_f (enc_text com_f)

-- * List Input

-- | Read the next value from a list.
list_read_f :: (t -> u) -> S.State [t] u
list_read_f f = do
  l <- S.get
  when (null l) (error "list_read_f")
  S.put (tail l)
  return (f (head l))

-- | Read function for floating point that admits inf and infinity.
read_float :: (Fractional p, Read p) => String -> p
read_float txt =
  case map toLower txt of
    "inf" -> 1 / 0
    "infinity" -> 1 / 0
    _ -> read txt

-- | Get_Functions for text representation of Graphdef.
text_get_f :: Get_Functions (S.StateT [String] Identity)
text_get_f = (list_read_f Datum.ascii,list_read_f read,list_read_f read,list_read_f read,list_read_f read_float)

-- | Read text representation of Graphdef, as written by 'print_graphdef'.
--
-- > read_graphdef "1396926310 0 1 simple 2 0.0 440.0 0 0 2 SinOsc 2 2 1 0 -1 1 -1 0 2 Out 2 2 0 0 -1 0 0 0"
read_graphdef :: String -> Graphdef
read_graphdef txt =
  let delete_comments = filter (\x -> not (null x) && (head x /= ';'))
  in S.evalState (get_graphdef text_get_f) (concatMap words (delete_comments (lines txt)))

read_graphdef_file :: FilePath -> IO Graphdef
read_graphdef_file = fmap read_graphdef . readFile