packages feed

vivid-0.5.2.1: Vivid/GlobalState.hs

{-# LANGUAGE
     BangPatterns
   , InstanceSigs
   #-}

module Vivid.GlobalState (
     withGlobalSCServerState
   , setClientId
   , setMaxBufferIds
   , waitForSync_io
   , waitForSync_io_noGC
   , startMailbox
   , getMailboxForSyncId
   , connectToSCServer
   , createSCServerConnection
   , closeSCServerConnection
   , getSCServerSocket
   , doScheduledIn
   , doScheduledAt
   , doScheduledNow
   , quitSCServer
   , defineSDFromFile

   -- Not sure we need to export these:
   , getNextAvailables
   , getNextAvailable
   ) where

import Control.Monad (void)
import Control.Concurrent (ThreadId, forkIO)
import Control.Concurrent.MVar (MVar)
import Control.Concurrent.STM
import Control.Monad.Reader
import Data.ByteString (ByteString)
import Data.Int
import Network.Socket (Socket)

-- We use this only for "the unsafePerformIO hack"
-- (https://wiki.haskell.org/Top_level_mutable_state) so that functions can
-- refer to the state without being passed the state explicitly. This should
-- still be safe:
import System.IO.Unsafe (unsafePerformIO)

import Vivid.Actions (quitSCServerWith)
import Vivid.Actions.Class (VividAction(..))
import Vivid.Actions.IO (defineSDFromFileWith)
import Vivid.Actions.Scheduled
import Vivid.OSC (Timestamp)
import Vivid.SCServer.Connection
import Vivid.SCServer.State
import Vivid.SynthDef.Types (SynthDef)

{-# NOINLINE globalSCServerState #-}
globalSCServerState :: SCServerState
-- See the above note about this use of unsafePerformIO:
globalSCServerState = unsafePerformIO makeEmptySCServerState

withGlobalSCServerState :: (SCServerState -> x) -> x
withGlobalSCServerState action =
   -- This line is meant to prevent 'nested STM' errors.
   -- Would like a more systematic look at the causes.:
   let !_ = globalSCServerState

   in action globalSCServerState


setClientId :: Int32 -> IO ()
setClientId = withGlobalSCServerState setServerClientId

setMaxBufferIds :: Int32 -> IO ()
setMaxBufferIds = withGlobalSCServerState setServerMaxBufferIds

getNextAvailable :: (SCServerState -> TVar [a]) -> IO a
getNextAvailable = withGlobalSCServerState getNextAvailable'

getNextAvailables :: Int -> (SCServerState -> TVar [a]) -> IO [a]
getNextAvailables = withGlobalSCServerState getNextAvailables'


closeSCServerConnection :: IO ()
closeSCServerConnection = withGlobalSCServerState closeSCServerConnection'

connectToSCServer :: SCConnectConfig -> IO (Socket, ThreadId)
connectToSCServer = withGlobalSCServerState connectToSCServer'

waitForSync_io :: SyncId -> IO ()
waitForSync_io = withGlobalSCServerState waitForSync_io'

waitForSync_io_noGC :: SyncId -> IO ()
waitForSync_io_noGC = withGlobalSCServerState waitForSync_io_noGC'

startMailbox :: Socket -> IO ()
startMailbox = withGlobalSCServerState startMailbox'

getMailboxForSyncId :: SyncId -> IO (MVar ())
getMailboxForSyncId = withGlobalSCServerState getMailboxForSyncId'

createSCServerConnection :: SCConnectConfig -> IO (Either String Socket)
createSCServerConnection = withGlobalSCServerState createSCServerConnection'

getSCServerSocket :: IO Socket
getSCServerSocket = withGlobalSCServerState getSCServerSocket'

defineSDFromFile :: SynthDef a -> IO ()
defineSDFromFile = withGlobalSCServerState defineSDFromFileWith

doScheduledAt :: Timestamp -> Scheduled a -> IO a
doScheduledAt =
   withGlobalSCServerState doScheduledAtWith

doScheduledIn :: Double -> Scheduled a -> IO a
doScheduledIn =
   withGlobalSCServerState doScheduledInWith

doScheduledNow :: Scheduled a -> IO a
doScheduledNow =
   withGlobalSCServerState doScheduledNowWith

quitSCServer :: IO ()
quitSCServer = withGlobalSCServerState quitSCServerWith

instance VividAction IO where
   callBS :: ByteString -> IO ()
   callBS bs = globalIO $ callBS bs

   sync :: IO ()
   sync = globalIO sync

   waitForSync :: SyncId -> IO ()
   waitForSync syncId = globalIO $ waitForSync syncId

   wait :: Real n => n -> IO ()
   wait n = globalIO $ wait n

   getTime :: IO Timestamp
   getTime = globalIO getTime

   newBufferId :: IO BufferId
   newBufferId = globalIO newBufferId

   newNodeId :: IO NodeId
   newNodeId = globalIO newNodeId

   newSyncId :: IO SyncId
   newSyncId = globalIO newSyncId

   fork :: IO () -> IO ()
   fork = void . forkIO

   defineSD :: SynthDef a -> IO ()
   defineSD synthDef = globalIO $ defineSD synthDef

globalIO :: (ReaderT SCServerState IO x) -> IO x
globalIO action =
   withGlobalSCServerState (runReaderT action)