
This module uses shell-haskell and a pipe
in order to play CSound music without a temporary score file.
A temporary orchestra file is necessary, though.

In my tests on a SuSE Linux only CSound5 (but not CSound4)
could play via '-odac'.

> module Haskore.Interface.CSound.Play where

> import System.IO (IO)
> import Haskore.General.ShellPipe (launch)
> import qualified System.IO as IO
> import qualified System.Posix.Signals as Signals
> import System.Cmd(system)
> import System.Directory(doesFileExist,removeFile)

> import           Haskore.Interface.CSound(Name)
> import qualified Haskore.Interface.CSound.Orchestra as Orchestra
> import qualified Haskore.Interface.CSound.Score     as Score

> play :: Orchestra.Output out => Orchestra.T out -> Score.T -> IO ()
> play orc sco = playV4 "/tmp" ("tmp", orc, sco)

> scorePipe, tmpWave :: FilePath
> scorePipe = "/tmp/pipe.sco"
> tmpWave   = "/tmp/csound.wav"


> {-
>   This works:
>   cat tut01.sco >plug & csound32 -d -o stdout tut01.orc plug | play -t sw -r 44100 -c 2 -
>   But piping into 'play' will only work within system call.
> -}
> playV4, playV5 :: Orchestra.Output out => 
>    FilePath -> (Name, Orchestra.T out, Score.T) -> IO ()

> playV4 dir (name, o@(Orchestra.Cons (rate, _) _), s) =
>    playNamedPipe ("csound32 -d -o stdout -s ", " 2>/dev/null" ++
>                   " | play -t sw -r " ++ show rate ++
>                          " -c " ++ show (Orchestra.channelCount o) ++
>                          " - &")
>                  dir name o s

> playV5 dir (name, o, s) =
>    playNamedPipe ("csound5 -d -o dac -s ", " >/dev/null &") dir name o s

> playNamedPipe :: Orchestra.Output out =>
>    (String, String) -> FilePath -> Name -> Orchestra.T out -> Score.T -> IO ()
> playNamedPipe cmd dir name o s =
>    let orchName  = dir ++ "/" ++ name ++ ".orc"
>    in  do
>           exists <- doesFileExist scorePipe
>           if exists
>             then putStrLn (scorePipe ++ " already exists")
>             else system ("mkfifo " ++ scorePipe) >> return ()
>           writeFile orchName  (Orchestra.toString o)
>           system (fst cmd ++ orchName ++ " " ++ scorePipe ++ snd cmd)
>           -- how can I reliably wait for CSound to open the pipe?
>           writeFile scorePipe (Score.toString s)
>           removeFile scorePipe



Trials with shell-haskell that failed for reasons I don't know.

> playV5NamedPipe :: Orchestra.Output out =>
>    FilePath -> (Name, Score.T, Orchestra.T out) -> IO ()
> playV5NamedPipe dir (name, s, o) =
>    let orchName  = dir ++ "/" ++ name ++ ".orc"
>        -- ((rate, _, channels), _) = o
>    in  do
>           -- Disable sigPIPE.  This means that the whole program
>           -- won't crash when the tool exits.  Unfortunately there
>           -- doesn't seem to be another way of doing this.
>           Signals.installHandler Signals.sigPIPE
>                       Signals.Ignore Nothing
> --          system ("mkfifo "++scorePipe)
>           writeFile orchName  (Orchestra.toString o)
> --           system ("csound -d -odac "++orchName++" "++scorePipe++" &")
>           (input,_,_) <- launch "csound"
>              ["csound", "-d", "-odac", orchName, scorePipe]
> --          IO.hPutStr input (Score.toString s)
>           IO.hClose input
>           writeFile scorePipe (Score.toString s)
> --           removeFile scorePipe
> --           outStr <- IO.hGetContents output
> --           putStr outStr
> --           IO.hClose output
>           return ()

> playV4NamedPipe :: Orchestra.Output out =>
>    FilePath -> (Name, Score.T, Orchestra.T out) -> IO ()
> playV4NamedPipe dir (name, s, o) =
>    let orchName  = dir ++ "/" ++ name ++ ".orc"
>        Orchestra.Cons (rate, _) _ = o
>    in  do
>           -- Disable sigPIPE.  This means that the whole program
>           -- won't crash when the tool exits.  Unfortunately there
>           -- doesn't seem to be another way of doing this.
>           Signals.installHandler Signals.sigPIPE
>                       Signals.Ignore Nothing
>           writeFile orchName  (Orchestra.toString o)
>           (input,_,_) <- launch "play"
>               ["play", "-r", show rate,
>                        "-c", show (Orchestra.channelCount o),
>                        "-"]
>           (_,_,output) <- launch "csound32"
>               ["csound32", "-d", "-o", tmpWave, "-s",
>                            orchName, scorePipe]
>           writeFile scorePipe (Score.toString s)
>           IO.hGetContents output >>= IO.hPutStr input
>           IO.hClose input
>           IO.hClose output

> playV4AnonymousPipe :: Orchestra.Output out =>
>    FilePath -> (Name, Score.T, Orchestra.T out) -> IO ()
> playV4AnonymousPipe dir (name, s, o) =
>    let orchName  = dir ++ "/" ++ name ++ ".orc"
>        -- ((rate, _, channels), _) = o
>    in  do
>           -- Disable sigPIPE.  This means that the whole program
>           -- won't crash when the tool exits.  Unfortunately there
>           -- doesn't seem to be another way of doing this.
>           Signals.installHandler Signals.sigPIPE
>                       Signals.Ignore Nothing
>           writeFile orchName  (Orchestra.toString o)
>           (input,_,_) <- launch "csound32"
>               ["csound32", "-d", "-o", tmpWave, "-W",
>                            "-L", "stdin", orchName]
>           IO.hPutStr input (Score.toString s)
>           IO.hClose input
