{-# LANGUAGE FlexibleContexts #-} -- | Read server options from configuraton file. module Sound.SC3.Server.Process.ConfigFile ( fromAssocs, toAssocs ) where import Control.Monad.Error (MonadError) import Control.Monad.Trans.State (StateT, evalStateT, execStateT) import Data.Accessor import Sound.OpenSoundControl (TCP, UDP) import Sound.SC3.Server.Process.Options import Data.ConfigFile (CPError, OptionSpec) import Data.Map (Map) import qualified Data.Map as Map readMaybe :: (Read a) => String -> Maybe a readMaybe s = case reads s of [(a, "")] -> return a _ -> fail ("Could not read " ++ (show s)) -- TODO: Add parse error handling set :: (Read b, Monad m) => Map OptionSpec String -> OptionSpec -> Accessor a b -> StateT a m () set opts name accessor = do case Map.lookup name opts of Nothing -> return () Just str -> case readMaybe str of Nothing -> return () Just value -> putA accessor value get :: (Show b, Monad m) => OptionSpec -> Accessor a b -> StateT a m (OptionSpec, String) get name accessor = do value <- getA accessor return (name, show value) -- | Get 'ServerOptions' from an option 'Map'. -- Uninitialized fields are taken from 'defaultServerOptions'. getServerOptions :: (Monad m) => Map OptionSpec String -> m ServerOptions getServerOptions m = flip execStateT defaultServerOptions $ do set m "serverProgram" _serverProgram set m "numberOfControlBusChannels" _numberOfControlBusChannels set m "numberOfAudioBusChannels" _numberOfAudioBusChannels set m "numberOfInputBusChannels" _numberOfInputBusChannels set m "numberOfOutputBusChannels" _numberOfOutputBusChannels set m "blockSize" _blockSize set m "numberOfSampleBuffers" _numberOfSampleBuffers set m "maxNumberOfNodes" _maxNumberOfNodes set m "maxNumberOfSynthDefs" _maxNumberOfSynthDefs set m "realtimeMemorySize" _realtimeMemorySize set m "numberOfWireBuffers" _numberOfWireBuffers set m "numberOfRandomSeeds" _numberOfRandomSeeds set m "loadSynthDefs" _loadSynthDefs set m "verbosity" _verbosity -- | Get 'RTOptions' from an option 'Map'. -- Uninitialized fields are taken from 'defaultRTOptions'. getRTOptions :: (Monad m) => Map OptionSpec String -> m RTOptions getRTOptions m = flip execStateT defaultRTOptions $ do set m "udpPortNumber" _udpPortNumber set m "tcpPortNumber" _tcpPortNumber set m "useZeroconf" _useZeroconf set m "maxNumberOfLogins" _maxNumberOfLogins set m "sessionPassword" _sessionPassword set m "hardwareDeviceName" _hardwareDeviceName set m "hardwareBufferSize" _hardwareBufferSize set m "hardwareSampleRate" _hardwareSampleRate set m "inputStreamsEnabled" _inputStreamsEnabled set m "outputStreamsEnabled" _outputStreamsEnabled -- | Get 'NRTOptions' from an option 'Map'. -- Uninitialized fields are taken from 'defaultNRTOptions'. getNRTOptions :: Monad m => Map OptionSpec String -> m NRTOptions getNRTOptions m = flip execStateT defaultNRTOptions $ do set m "commandFilePath" _commandFilePath set m "inputFilePath" _inputFilePath set m "outputFilePath" _outputFilePath set m "outputSampleRate" _outputSampleRate set m "outputHeaderFormat" _outputHeaderFormat set m "outputSampleFormat" _outputSampleFormat -- | Read server options, realtime options and non-relatime options from an -- association list. -- -- TODO: Add error handling. fromAssocs :: MonadError CPError m => [(OptionSpec, String)] -> m (ServerOptions, RTOptions, NRTOptions) fromAssocs opts = do srvo <- getServerOptions m rto <- getRTOptions m nrto <- getNRTOptions m return (srvo, rto, nrto) where m = Map.fromList opts -- | Convert 'ServerOptions' to association list. assocsServerOptions :: Monad m => ServerOptions -> m [(OptionSpec, String)] assocsServerOptions options = flip evalStateT options $ sequence [ get "serverProgram" _serverProgram , get "numberOfControlBusChannels" _numberOfControlBusChannels , get "numberOfAudioBusChannels" _numberOfAudioBusChannels , get "numberOfInputBusChannels" _numberOfInputBusChannels , get "numberOfOutputBusChannels" _numberOfOutputBusChannels , get "blockSize" _blockSize , get "numberOfSampleBuffers" _numberOfSampleBuffers , get "maxNumberOfNodes" _maxNumberOfNodes , get "maxNumberOfSynthDefs" _maxNumberOfSynthDefs , get "realtimeMemorySize" _realtimeMemorySize , get "numberOfWireBuffers" _numberOfWireBuffers , get "numberOfRandomSeeds" _numberOfRandomSeeds , get "loadSynthDefs" _loadSynthDefs , get "verbosity" _verbosity ] -- | Convert 'RTOptions' to association list. assocsRTOptions :: Monad m => RTOptions -> m [(OptionSpec, String)] assocsRTOptions options = flip evalStateT options $ sequence [ get "udpPortNumber" _udpPortNumber , get "tcpPortNumber" _tcpPortNumber , get "useZeroconf" _useZeroconf , get "maxNumberOfLogins" _maxNumberOfLogins , get "sessionPassword" _sessionPassword , get "hardwareDeviceName" _hardwareDeviceName , get "hardwareBufferSize" _hardwareBufferSize , get "hardwareSampleRate" _hardwareSampleRate , get "inputStreamsEnabled" _inputStreamsEnabled , get "outputStreamsEnabled" _outputStreamsEnabled ] -- | Convert 'NRTOptions' to association list. assocsNRTOptions :: Monad m => NRTOptions -> m [(OptionSpec, String)] assocsNRTOptions options = flip evalStateT options $ sequence [ get "commandFilePath" _commandFilePath , get "inputFilePath" _inputFilePath , get "outputFilePath" _outputFilePath , get "outputSampleRate" _outputSampleRate , get "outputHeaderFormat" _outputHeaderFormat , get "outputSampleFormat" _outputSampleFormat ] -- | Convert server options and optionally realtime options and non-realtime -- options to an association list. toAssocs :: Monad m => ServerOptions -> Maybe RTOptions -> Maybe NRTOptions -> m [(OptionSpec, String)] toAssocs serverOptions rtOptions nrtOptions = do srvo <- assocsServerOptions serverOptions rto <- maybe (return []) assocsRTOptions rtOptions nrto <- maybe (return []) assocsNRTOptions nrtOptions return $ srvo++rto++nrto