-- | Open sound control {-# Language ScopedTypeVariables #-} module Csound.Typed.Control.Osc( OscRef, OscHost, OscPort, OscAddress, OscType, initOsc, listenOsc, sendOsc, OscVal, listenOscVal ) where import Data.Boolean ((==*)) import Csound.Dynamic(Rate(..)) import Csound.Typed.Types import Csound.Typed.GlobalState hiding (oscInit, oscListen, oscSend) import qualified Csound.Typed.GlobalState as C(oscListen, oscSend) import Csound.Typed.Control.Ref 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 -> OscRef initOsc port = OscRef $ fromGE $ getOscPortHandle port -- | Listens for the OSC-messages. The first argument is OSC-reference. -- We can create it with the function @initOsc@. 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 resRef <- initOscRef oscType cond <- listen resRef when1 cond $ bam =<< readRef resRef where listen :: Tuple a => Ref a -> SE BoolSig listen ref = fmap (==* 1) $ csdOscListen ref oscRef oscAddr oscType csdOscListen :: Tuple a => Ref a -> OscRef -> OscAddress -> OscType -> SE Sig csdOscListen (Ref refVars) oscHandle addr ty = fmap fromGE $ fromDep $ hideGEinDep $ do expOscHandle <- toGE $ unOscRef oscHandle expAddr <- toGE $ text addr expOscType <- toGE $ text ty return $ C.oscListen expOscHandle expAddr expOscType refVars initOscRef :: OscType -> SE (Ref a) initOscRef typeStr = fmap Ref $ newLocalVars (fmap getOscRate typeStr) (fromTuple $ (defTuple :: a)) getOscRate :: Char -> Rate getOscRate x = case x of 'a' -> Ar 's' -> Sr 'i' -> Kr 'f' -> Kr _ -> Kr -- | 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 = do flagRef <- newRef (0 :: Sig) valRef <- newRef defTuple runEvt evts $ \a -> do flag <- readRef flagRef writeRef flagRef (flag + 1) writeRef valRef a flag <- readRef flagRef value <- readRef valRef send flag value where send :: Tuple a => Sig -> a -> SE () send trig as = SE $ hideGEinDep $ do args <- fromTuple as expTrig <- toGE trig expHost <- toGE $ text $ host expPort <- toGE $ int $ port expAddr <- toGE $ text $ addr expTy <- toGE $ text $ ty return $ C.oscSend $ expTrig : expHost : expPort : expAddr : expTy : args class Tuple a => OscVal a where getOscTypes :: a -> String getOscRef :: a -> SE (Ref a) instance OscVal Sig where getOscTypes = const "f" getOscRef = newCtrlRef instance OscVal Str where getOscTypes = const "s" getOscRef = newRef instance (OscVal a, OscVal b) => OscVal (a, b) where getOscTypes (a, b) = getOscTypes a ++ getOscTypes b getOscRef (a, b) = do refA <- getOscRef a refB <- getOscRef b return $ concatRef refA refB instance (OscVal a, OscVal b, OscVal c) => OscVal (a, b, c) where getOscTypes (a, b, c) = getOscTypes a ++ getOscTypes b ++ getOscTypes c getOscRef (a, b, c) = do refA <- getOscRef a refB <- getOscRef b refC <- getOscRef c return $ concatRef3 refA refB refC instance (OscVal a, OscVal b, OscVal c, OscVal d) => OscVal (a, b, c, d) where getOscTypes (a, b, c, d) = getOscTypes a ++ getOscTypes b ++ getOscTypes c ++ getOscTypes d getOscRef (a, b, c, d) = do refA <- getOscRef a refB <- getOscRef b refC <- getOscRef c refD <- getOscRef d return $ concatRef4 refA refB refC refD instance (OscVal a, OscVal b, OscVal c, OscVal d, OscVal e) => OscVal (a, b, c, d, e) where getOscTypes (a, b, c, d, e) = getOscTypes a ++ getOscTypes b ++ getOscTypes c ++ getOscTypes d ++ getOscTypes e getOscRef (a, b, c, d, e) = do refA <- getOscRef a refB <- getOscRef b refC <- getOscRef c refD <- getOscRef d refE <- getOscRef e return $ concatRef5 refA refB refC refD refE -- | Listens for tuples of continuous signals read from OSC-channel. -- -- > listenOscVal ref address initValue listenOscVal :: (Tuple a, OscVal a) => OscRef -> String -> a -> SE a listenOscVal port path initVal = do ref <- getOscRef initVal runEvt (listenOsc port path (getOscTypes initVal)) $ \a -> writeRef ref a readRef ref