packages feed

hCsound-0.3.0: src/Sound/Csound.hs

-- |Sound.Csound provides a Haskell interface to the Csound API (defined in
-- csound.h). (almost) all functions from the API are wrapped and marshalled
-- in Sound.CSound.Interface.
-- In addition to the raw functions provided there, Sound.CSound provides
-- many more convenient to use when calling csound, as well as marshalling
-- for some functions that return vectors.
--
-- C++ class files, such as CSoundFile.hpp, are not yet wrapped.
-- Also support for cscore is currently very limited.

module Sound.Csound (
  module Sound.Csound.Interface,
  -- defined in this module
  performKsmpsIO,
  performBufferIO,
  withChannelList,
  runSimple
)

where

import           Sound.Csound.Foreign
import           Sound.Csound.Interface
import           Sound.Csound.Vector

import           Foreign.Marshal.Array (peekArray, pokeArray)
import           Control.Exception (bracket)
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Error
import           Control.Monad.State

-- |Basic run with csound-implemented I/O
runSimple :: String -> IO (Maybe CsoundError)
runSimple args = fmap rf $ runCsound csnd
 where
  csnd = compile args >> perform >> destroy
  rf = either Just (const Nothing)

-- Functions to perform a score with input and output from the host.

-- |Perform an entire score using performKsmps, with handling of
-- input and output buffers spin and spout.  Note that
-- setHostImplementedIO must be called before compile.
performKsmpsIO ::
  (Int -> Int -> IO CsVector) -- ^Function that takes buffer size
                              -- and starting position (in samples)
                              -- and returns a list of length
                              -- buffersize of input to use.
  -> (Int -> Int -> CsVector -> IO ()) -- ^Function that takes
                                       -- buffer size, starting
                                       -- position (in samples),
                                       -- and list of CsndFlt
                                       -- and performs an IO action
                                       -- with the [CsndFlt]
  -> Csound ()
performKsmpsIO inBuf outBuf = do
  bufSize <- getKsmps
  let ifn = inBuf bufSize
  let ofn = outBuf bufSize
  let performKsmpsIO' :: Int -> Csound ()
      performKsmpsIO' iposAcc = do
        liftIO (ifn iposAcc) >>= unsafeVec2Spin
        res <- performKsmps
        readSpout >>= liftIO . ofn iposAcc
        case res of
          PerformStopped -> let newPosAcc = iposAcc + bufSize in
            performKsmpsIO' newPosAcc
          PerformFinished -> return ()
          PerformError stat -> throwError $ CsStatus stat 
  performKsmpsIO' 0

-- |Perform an entire score using performBuffer, with handling of
-- input and output buffers.
performBufferIO ::
  (Int -> Int -> IO CsVector) -- ^Function that takes buffer size and
                              -- starting position (in samples) and
                              -- returns a list of length buffersize
                              -- of input to use.
  -> (Int -> Int -> CsVector -> IO ()) -- ^Function that takes buffer
                                       -- size, starting position
                                       -- (in samples), and list of CsndFlt
                                       -- and performs an IO action
                                       -- with the [CsndFlt]
  -> Csound ()
performBufferIO inBuf outBuf = do
  inBufSize <- getInputBufferSize
  outBufSize <- getOutputBufferSize
  let ifn = inBuf inBufSize
  let ofn = outBuf outBufSize
  let performBufferIO' :: Int -> Csound ()
      performBufferIO' iposAcc = do
        ibufSz <- getInputBufferSize
        liftIO (ifn iposAcc) >>= vec2Ibuf
        res <- performBuffer
        readObuf >>= liftIO . ofn iposAcc
        case res of
          PerformStopped -> performBufferIO' $! iposAcc+inBufSize
          PerformFinished -> return ()
          PerformError stat -> throwError $ CsStatus stat :: Csound ()
  performBufferIO' 0

-- |Perform an action within the IO monad, using the list of channels.
withChannelList :: ([CsoundChannelListEntry] -> IO a) -> Csound a
withChannelList func = do
  openRes <- listChannels
  x <- liftIO . func . snd $ openRes
  deleteChannelList . fst $ openRes
  return x