-- | Server notification processors. module Sound.SC3.Server.Notification ( Notification(..) , hasAddress , waitFor , waitForAll , Status(..) , status_reply , tr , synced , done , NodeNotification(nodeId, parentGroupId, previousNodeId, nextNodeId) , headNodeId, tailNodeId , n_go , n_end , n_off , n_on , n_move , n_info , n_go_, n_end_, n_off_, n_on_ , n_set, n_setn , BufferInfo(..) , b_info ) where import Control.Applicative (pure, (<*>)) import qualified Data.List.Zipper as Zipper import Sound.SC3.Server.State (BufferId, NodeId, SyncId) import Sound.OpenSoundControl (Datum(..), Message(..)) import Sound.OSC.Transport.Monad (RecvOSC(..), SendOSC(..), recvMessage) -- | A notification transformer, extracting a value from a matching OSC message. newtype Notification a = Notification { match :: Message -> Maybe a } instance Functor Notification where fmap f = Notification . (.) (fmap f) . match -- | Wait for an OSC message matching a specific address. -- -- Returns the matched OSC message. hasAddress :: String -> Notification Message hasAddress a = Notification f where f p@(Message a' _) | a == a' = Just p f _ = Nothing -- | Send an OSC packet and wait for a notification. -- -- Returns the transformed value. waitFor :: (RecvOSC m, SendOSC m) => Notification a -> m a waitFor n = go where go = do msg <- recvMessage case match n =<< msg of Nothing -> go Just a -> return a -- | Send an OSC packet and wait for a list of notifications. -- -- Returns the transformed values, in unspecified order. waitForAll :: (RecvOSC m, SendOSC m) => [Notification a] -> m [a] waitForAll = go [] where go as [] = return as go as ns = do msg <- recvMessage case msg of Nothing -> go as ns Just msg -> case findMatch msg ns of Nothing -> go as ns Just (a, ns') -> go (a:as) ns' findMatch msg = go . Zipper.fromList where go z | Zipper.endp z = Nothing | otherwise = let n = Zipper.cursor z in case match n msg of Nothing -> go (Zipper.right z) Just a -> Just (a, Zipper.toList (Zipper.delete z)) data Status = Status { numUGens :: Int , numSynths :: Int , numGroups :: Int , numSynthDefs :: Int , avgCPU :: Double , peakCPU :: Double , nominalSampleRate :: Double , actualSampleRate :: Double } deriving (Eq, Show) status_reply :: Notification Status status_reply = Notification f where f (Message "/status.reply" [Int _, Int u, Int s, Int g, Int d, Float a, Float p, Double sr, Double sr']) = Just $ Status u s g d a p sr sr' f _ = Nothing tr :: NodeId -> Maybe Int -> Notification Double tr n = Notification . f where f (Just i) (Message "/tr" [Int n', Int i', Float r]) | fromIntegral n == n' && i == i' = Just r f Nothing (Message "/tr" [Int n', Int _, Float r]) | fromIntegral n == n' = Just r f _ _ = Nothing synced :: SyncId -> Notification SyncId synced i = Notification f where f (Message "/synced" [Int j]) | fromIntegral j == i = Just i f _ = Nothing normalize :: String -> String normalize ('/':s) = s normalize s = s done :: String -> Notification [Datum] done c = Notification f where f (Message "/done" (String s:xs)) | normalize c == normalize s = Just xs f _ = Nothing data NodeNotification = SynthNotification { nodeId :: NodeId , parentGroupId :: NodeId , previousNodeId :: Maybe NodeId , nextNodeId :: Maybe NodeId } | GroupNotification { nodeId :: NodeId , parentGroupId :: NodeId , previousNodeId :: Maybe NodeId , nextNodeId :: Maybe NodeId , _headNodeId :: Maybe NodeId , _tailNodeId :: Maybe NodeId } deriving (Eq, Show) isSynthNotification :: NodeNotification -> Bool isSynthNotification (SynthNotification _ _ _ _) = True isSynthNotification _ = False headNodeId :: NodeNotification -> Maybe NodeId headNodeId n | isSynthNotification n = Nothing | otherwise = _headNodeId n tailNodeId :: NodeNotification -> Maybe NodeId tailNodeId n | isSynthNotification n = Nothing | otherwise = _tailNodeId n n_notification :: String -> NodeId -> Notification NodeNotification n_notification s nid = Notification f where nodeIdToMaybe (-1) = Nothing nodeIdToMaybe i = Just (fromIntegral i) f osc = case osc of Message s' (Int nid':xs) -> if s == s' && fromIntegral nid == nid' then case xs of (Int g:Int p:Int n:Int b:rest) -> let group = fromIntegral g prev = nodeIdToMaybe p next = nodeIdToMaybe n in case b of 1 -> case rest of [Int h, Int t] -> Just $ GroupNotification nid group prev next (nodeIdToMaybe h) (nodeIdToMaybe t) _ -> Nothing _ -> Just $ SynthNotification nid group prev next _ -> Nothing else Nothing _ -> Nothing n_go :: NodeId -> Notification NodeNotification n_go = n_notification "/n_go" n_end :: NodeId -> Notification NodeNotification n_end = n_notification "/n_end" n_off :: NodeId -> Notification NodeNotification n_off = n_notification "/n_off" n_on :: NodeId -> Notification NodeNotification n_on = n_notification "/n_on" n_move :: NodeId -> Notification NodeNotification n_move = n_notification "/n_move" n_info :: NodeId -> Notification NodeNotification n_info = n_notification "/n_info" n_notification_ :: String -> NodeId -> Notification () n_notification_ s nid = Notification f where f (Message s' (Int nid':_)) | s == s' && fromIntegral nid == nid' = Just () f _ = Nothing n_go_ :: NodeId -> Notification () n_go_ = n_notification_ "/n_go" n_end_ :: NodeId -> Notification () n_end_ = n_notification_ "/n_end" n_off_ :: NodeId -> Notification () n_off_ = n_notification_ "/n_off" n_on_ :: NodeId -> Notification () n_on_ = n_notification_ "/n_on" n_set :: NodeId -> Notification [(Either Int String, Double)] n_set nid = Notification f where f (Message "/n_set" (Int nid':cs)) | nid == fromIntegral nid' = mapM ctrl (pairs cs) f _ = Nothing pairs (a:a':as) = (a, a') : pairs as pairs _ = [] ctrl (Int k, Float v) = Just (Left k, v) ctrl (String k, Float v) = Just (Right k, v) ctrl _ = Nothing n_setn :: NodeId -> Notification [(Either Int String, [Double])] n_setn nid = Notification f where f (Message "/n_setn" (Int nid':cs)) | nid == fromIntegral nid' = sequence (conv cs) f _ = Nothing value (Float v) = Just v value _ = Nothing conv (Int k:Int n:xs) = (pure (,) <*> pure (Left k) <*> mapM value (take n xs)) : conv (drop n xs) conv (String k:Int n:xs) = (pure (,) <*> pure (Right k) <*> mapM value (take n xs)) : conv (drop n xs) conv _ = [] data BufferInfo = BufferInfo { numFrames :: Int , numChannels :: Int , sampleRate :: Double } deriving (Eq, Show) b_info :: BufferId -> Notification BufferInfo b_info bid = Notification f where f (Message "/b_info" [Int bid', Int f, Int c, Float r]) | fromIntegral bid == bid' = Just $ BufferInfo f c r f _ = Nothing