-- | Open sound control
{-# Language ScopedTypeVariables #-}
module Csound.Typed.Control.Osc(
OscRef, OscHost, OscPort, OscAddress, OscType,
initOsc, listenOsc, sendOsc
) where
import Data.Boolean ((==*))
import Csound.Typed.Types
import Csound.Typed.GlobalState hiding (oscInit, oscListen, oscSend)
import qualified Csound.Typed.GlobalState as C(oscInit, oscListen, oscSend)
import Csound.Typed.Control.SERef
newtype OscRef = OscRef { unOscRef :: D }
-- | Port to listen OSC-messages.
type OscPort = Int
-- | Path-like string ("/foo/bar/baz")
type OscAddress = String
-- | The string specifies the type of expected arguments.
-- The string can contain the characters "bcdfilmst" which stand for
-- Boolean, character, double, float, 32-bit integer, 64-bit integer, MIDI,
-- string and timestamp.
type OscType = String
-- | The hostname of the computer. An empty string is for local machine.
type OscHost = String
-- | Initializes host client. The process starts to run in the background.
initOsc :: OscPort -> SE OscRef
initOsc port = do
oscRef <- fmap fromGE $ fromDep $ C.oscInit (fromIntegral port)
varRef <- newGlobalSERef (0 :: D)
writeSERef varRef oscRef
ihandle <- readSERef varRef
return $ OscRef ihandle
-- | Listens for the OSC-messages. The first argument is OSC-reference.
-- We can create it with the function @oscInit@. The next two arguments are strings.
-- The former specifies the path-like address to listen the messages. It can be:
--
-- > /foo/bar/baz
--
-- The latter specifies the type of expected arguments.
-- The string can contain the characters "bcdfilmst" which stand for
-- Boolean, character, double, float, 32-bit integer, 64-bit integer, MIDI,
-- string and timestamp.
--
-- The result is an event of messages. We can run a callback on it
-- with standard function @runEvt@:
--
-- > runEvt :: Evt a -> (a -> SE ()) -> SE ()
listenOsc :: forall a . Tuple a => OscRef -> OscAddress -> OscType -> Evt a
listenOsc oscRef oscAddr oscType = Evt $ \bam -> do
(readCond, writeCond) <- sensorsSE (0 :: Sig)
resRef <- newSERef (defTuple :: a)
writeCond =<< listen resRef
readCond >>= (\cond -> whileDo (cond ==* 1) $ do
bam =<< readSERef resRef
writeCond =<< listen resRef)
where
listen :: Tuple a => SERef a -> SE Sig
listen ref = csdOscListen ref oscRef oscAddr oscType
csdOscListen :: Tuple a => SERef a -> OscRef -> OscAddress -> OscType -> SE Sig
csdOscListen resRef oscHandle addr ty = do
args <- readSERef resRef
res <- fmap fromGE $ fromDep $ hideGEinDep $ do
expArgs <- fromTuple args
expOscHandle <- toGE $ unOscRef oscHandle
expAddr <- toGE $ text addr
expOscType <- toGE $ text ty
return $ C.oscListen $ expOscHandle : expAddr : expOscType : expArgs
writeSERef resRef args
return res
-- | Sends OSC-messages. It takes in a name of the host computer
-- (empty string is alocal machine), port on which the target
-- machine is listening, OSC-addres and type. The last argument
-- produces the values for OSC-messages.
sendOsc :: forall a . Tuple a => OscHost -> OscPort -> OscAddress -> OscType -> Evt a -> SE ()
sendOsc host port addr ty evts = runEvt evts send
where
send :: Tuple a => a -> SE ()
send as = SE $ hideGEinDep $ do
args <- fromTuple as
expHost <- toGE $ text $ host
expPort <- toGE $ int $ port
expAddr <- toGE $ text $ addr
expTy <- toGE $ text $ ty
return $ C.oscSend $ 1 : expHost : expPort : expAddr : expTy : args