{- | This module uses 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 Haskore.RealTime.ShellPipe (launch) import System.IO (IO) 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