vivid-0.5.0.0: Vivid/SCServer/State.hs
{-# LANGUAGE
BangPatterns
, DataKinds
, KindSignatures
, LambdaCase
#-}
{-# LANGUAGE NoIncoherentInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE NoUndecidableInstances #-}
module Vivid.SCServer.State (
BufferId(..)
, NodeId(..)
, SyncId(..)
, SCServerState(..)
, ConnProtocol(..)
, setServerClientId
, setServerMaxBufferIds
, numberOfSyncIdsToDrop
, makeEmptySCServerState
-- We might not need to export these (or the global equivalents) at all:
, getNextAvailable'
, getNextAvailables'
) where
import Vivid.OSC (OSC)
import Vivid.SC.Server.Types
import Vivid.SynthDef.Types
import Network.Socket (Socket)
import Control.Concurrent (ThreadId)
import Control.Concurrent.MVar
import Control.Concurrent.STM -- (readTVar, atomically, writeTVar, newTVar, TVar, TMVar)
import Control.Monad (when)
import Data.Bits
import Data.Int (Int32)
-- import Data.IORef
import qualified Data.Map as Map
import Data.Map (Map)
import qualified Data.Set as Set
import Data.Set (Set)
import Prelude
data SCServerState
= SCServerState
-- We use 'IORef Maybe's instead of MVars so we can use weak pointer
-- finalizers with older versions of GHC:
{ _scServerState_socketConnectStarted :: TVar Bool
, _scServerState_socket :: !(TMVar Socket) -- !(TVar (Maybe Socket))
, _scServerState_listener :: !(TMVar ThreadId) -- !(TVar (Maybe ThreadId))
, _scServerState_availableBufferIds :: !(TVar [BufferId])
, _scServerState_maxBufIds :: !(TVar Int32)
, _scServerState_availableNodeIds :: !(TVar [NodeId])
, _scServerState_availableSyncIds :: !(TVar [SyncId])
, _scServerState_syncIdMailboxes :: !(TVar (Map SyncId (MVar ())))
, _scServerState_serverMessageFunction :: !(TVar (OSC -> IO ()))
, _scServerState_definedSDs :: !(TVar (Set (SDName, Int))) -- Int is the hash
, _scServerState_connProtocol :: TVar ConnProtocol -- This doesn't change after boot, but we could e.g. disconnect and reconnect after boot
}
data ConnProtocol
= ConnProtocol_UDP
| ConnProtocol_TCP
deriving (Show, Read, Eq, Ord)
setServerClientId :: SCServerState -> Int32 -> IO ()
setServerClientId serverState clientId = do
when (clientId < 0 || clientId > 31) $
error "client id must be betw 0 and 31"
atomically $ writeTVar (_scServerState_availableNodeIds serverState) $
-- The client id is the first 5 bits of a positive int:
-- Note the incrementing gets weird once we hit the (.&.) -- should
-- fix if anyone plans to use more than 33 million nodes
map f [1000..]
where
f :: Int32 -> NodeId
f nodeNum = NodeId $
((clientId `shiftL` ((finiteBitSize nodeNum-5)-1)) .|.) $
((maxBound `shiftR` 5) .&. nodeNum)
numberOfSyncIdsToDrop :: Int
numberOfSyncIdsToDrop = 10000
makeEmptySCServerState :: IO SCServerState
-- We don't do this with 'atomically' because you can't put 'atomically' in
-- 'unsafePerformIO' (or, apparently you can with the "!_ =" hack I was
-- doing, but let's do the recommended way):
makeEmptySCServerState = do -- atomically $ do
sockConnectStarted <- newTVarIO False
sockIORef <- newEmptyTMVarIO -- newTVar Nothing -- newIORef Nothing
listenerIORef <- newEmptyTMVarIO -- newTVar Nothing -- newIORef Nothing
availBufIds <- newTVarIO $ drop 512 $ map BufferId [0..]
-- these'll be allocated when we connect (and get a clientId):
availNodeIds <- newTVarIO $ map (NodeId . ((1 `shiftL` 26) .|.)) [1000..]
maxBufIds <- newTVarIO 1024
syncIds <- newTVarIO $ drop numberOfSyncIdsToDrop $ map SyncId [0..]
syncMailboxes <- newTVarIO $ Map.empty
serverMessageFunction <- newTVarIO $ \_ -> return ()
definedSDs <- newTVarIO $ Set.empty
connProtocolVar <- newTVarIO ConnProtocol_UDP
return $ SCServerState
{ _scServerState_socketConnectStarted = sockConnectStarted
, _scServerState_socket = sockIORef
, _scServerState_listener = listenerIORef
, _scServerState_availableBufferIds = availBufIds
, _scServerState_maxBufIds = maxBufIds
, _scServerState_availableNodeIds = availNodeIds
, _scServerState_availableSyncIds = syncIds
, _scServerState_syncIdMailboxes = syncMailboxes
, _scServerState_serverMessageFunction = serverMessageFunction
, _scServerState_definedSDs = definedSDs
, _scServerState_connProtocol = connProtocolVar
}
-- | If you've started the SC server with a non-default number of buffer ids,
-- (e.g. with the \"-b\" argument), you can reflect that here
--
-- Note that the buffer ids start at 512, to not clash with any that
-- another client (e.g. sclang) has allocated
setServerMaxBufferIds :: SCServerState -> Int32 -> IO ()
setServerMaxBufferIds serverState newMax =
atomically $
writeTVar (_scServerState_maxBufIds serverState) newMax
getNextAvailable' :: SCServerState -> (SCServerState -> TVar [a]) -> IO a
getNextAvailable' serverState getter =
getNextAvailables' serverState 1 getter >>= \case
[x] -> return x
_ -> error "i don't even - 938"
getNextAvailables' :: SCServerState -> Int -> (SCServerState -> TVar [a]) -> IO [a]
getNextAvailables' serverState numToGet getter = do
let !_ = serverState
atomically $ do
let avail = getter serverState
(ns, rest) <- splitAt numToGet <$> readTVar avail
writeTVar avail rest
return ns