packages feed

hCsound-0.2: 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
	(
                CsoundMonad, --exported from Interface
                csoundCreate,
                csoundCompile,
                csoundPerform,
                CsoundPerformStatus,
                csoundPreCompile,
                csoundSetHostImplementedAudioIO,

-- defined in this module
                csoundPerformKsmpsIO,
                csoundPerformBufferIO,
                withOpcodeList,
                withUtilityList,
                listUtilityNames,
                withChannelList
	)
where

import Sound.Csound.Interface
import Foreign.Marshal.Array (peekArray, pokeArray)
import Control.Monad.Error (liftIO, throwError)
import Control.Exception (bracket)

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

-- |Perform an entire score using csoundPerformKsmps, with handling of
-- input and output buffers spin and spout.  Note that
-- csoundSetHostImplementedIO must be called before csoundCompile.
csoundPerformKsmpsIO ::
        CsoundPtr -- ^Pointer to csound instance
        -> (Int -> Int -> IO [CsndFlt]) -- ^Function that takes buffer size and starting
                                        -- position (in samples) and returns a list of length
                                        -- buffersize of input to use.
        -> (Int -> Int -> [CsndFlt] -> IO ()) -- ^Function that takes buffer size, starting
                                              -- position (in samples), and list of CsndFlt
                                              -- and performs an IO action with the [CsndFlt]
        -> CsoundMonad ()
csoundPerformKsmpsIO csPtr inBuf outBuf = csoundPerformKsmpsIO' 0
        where
        bufSize = csoundGetKsmps csPtr
        ifn = inBuf bufSize
        ofn = outBuf bufSize
        peekFn = peekArray bufSize
        csoundPerformKsmpsIO' :: Int -> CsoundMonad ()
        csoundPerformKsmpsIO' iposAcc = do
                liftIO $ csoundGetSpin' csPtr >>= (\iptr -> ifn iposAcc >>= pokeArray iptr)
                res <- csoundPerformKsmps csPtr
                liftIO $ csoundGetSpout' csPtr >>= peekFn >>= (ofn iposAcc)
                case res of
                        PerformStopped -> let newPosAcc = iposAcc + bufSize in
                                csoundPerformKsmpsIO' newPosAcc
                        PerformFinished -> return ()
                        _ -> throwError $ show res --I don't think this will ever happen,
                            -- because if it does csoundPerformKsmps should handle the error

-- |Perform an entire score using csoundPerformBuffer, with handling of
-- input and output buffers.
csoundPerformBufferIO ::
        CsoundPtr -- ^Pointer to Csound struct
        -> (Int -> Int -> IO [CsndFlt]) -- ^Function that takes buffer size and starting
                                -- position (in samples) and returns a list of length
                                -- buffersize of input to use.
        -> (Int -> Int -> [CsndFlt] -> IO ()) -- ^Function that takes buffer size, starting
                                              -- position (in samples), and list of CsndFlt
                                              -- and performs an IO action with the [CsndFlt]
        -> CsoundMonad ()
csoundPerformBufferIO csp inBuf outBuf = csoundPerformBufferIO' 0
        where
        inBufSize = csoundGetInputBufferSize csp
        ifn = inBuf inBufSize
        outBufSize = csoundGetOutputBufferSize csp
        ofn = outBuf outBufSize
        peekFn = peekArray $ csoundGetInputBufferSize csp
        csoundPerformBufferIO' :: Int -> CsoundMonad ()
        csoundPerformBufferIO' iposAcc = do
                liftIO $ csoundGetInputBuffer' csp >>= (\iptr -> ifn iposAcc >>= pokeArray iptr)
                res <- csoundPerformBuffer csp
                liftIO $ csoundGetOutputBuffer' csp >>= peekFn >>= (ofn iposAcc)
                case res of
                        PerformStopped -> let newPosAcc = iposAcc+inBufSize in
                                csoundPerformBufferIO' newPosAcc
                        PerformFinished -> return ()
                        _ -> throwError $ show res --Again, this should never happen

-- |Perform an IO action with the current csound opcode list, properly disposing of the list after completing
-- the action
withOpcodeList :: CsoundPtr -> ([OpcodeListEntry] -> IO b) -> CsoundMonad b
withOpcodeList csPtr func = do
        openRes <- csoundNewOpcodeList csPtr
        liftIO $ bracket (return openRes) disposeFunc procFunc
        where
        disposeFunc = (\(ptr, _) -> csoundDisposeOpcodeList csPtr ptr)
        procFunc = (\(_, list) -> func list)

-- |Perform an action within the IO monad, using the list of named utilities.
withUtilityList :: CsoundPtr -> ([String] -> IO b) -> CsoundMonad b
withUtilityList csPtr func = do
        openRes <- csoundListUtilities csPtr
        case openRes of
                (_, []) -> throwError "No utilities found."
                _ -> liftIO $ bracket (return openRes) disposeFunc procFunc
        where
        disposeFunc = (\(ptr, _) -> csoundDeleteUtilityList csPtr ptr)
        procFunc = (\(_, list) -> func list)

-- |Get a list of all the registered csound utilities.  This should be used instead of csoundListUtilities
-- because this function calls csoundDeleteUtilityList after use.
listUtilityNames :: CsoundPtr -> CsoundMonad [String]
listUtilityNames csPtr = withUtilityList csPtr return

-- |Perform an action within the IO monad, using the list of channels.
withChannelList :: CsoundPtr -> ([CsoundChannelListEntry] -> IO a) -> CsoundMonad a
withChannelList csPtr func = do
        openRes <- csoundListChannels csPtr
        case openRes of
                (_, []) -> throwError "No open channels"
                _ -> liftIO $ bracket (return openRes) disposeFunc procFunc
        where
        disposeFunc = (\(ptr, _) -> csoundDeleteChannelList csPtr ptr)
        procFunc = (\(_, list) -> func list)

-- |Perform a simple run of csound, with no special handling.
{-
runCsoundSimple :: String -> CsoundMonad CsoundPerformStatus
runCsoundSimple argList = do
        perfStatus <- liftIO $ bracket (csoundCreate nullPtr) csoundDestroy perform
	where
        perform csPtr = do
                res <- csoundCompile csPtr argv argc
                case res of
                        CsoundSuccess -> csoundPerform csPtr
                        otherwise -> return $ PerformError res
        argc = "csound" : words argList
	argv = length argc
-}