{- | 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