haskore-realtime-0.0.2: src/Haskore/Interface/CSound/Play.hs
{- |
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