synthesizer-llvm-0.3: src/Synthesizer/LLVM/Server/Option.hs
module Synthesizer.LLVM.Server.Option (
T(..),
get,
defaultChannel,
Play.defaultChunkSize,
defaultSampleRate,
defaultLatency,
SampleRate(SampleRate),
sampleRate,
) where
import qualified Synthesizer.Storable.ALSA.Play as Play
import qualified Synthesizer.Generic.Signal as SigG
import qualified Data.StorableVector.Lazy as SVL
import Synthesizer.EventList.ALSA.MIDI (ClientName(ClientName))
import qualified Sound.MIDI.Message.Channel as ChannelMsg
import System.Console.GetOpt
(getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )
import System.Environment (getArgs, getProgName, )
import System.Exit (exitSuccess, exitFailure, )
import qualified System.IO as IO
import Control.Monad (when, )
import qualified NumericPrelude.Numeric as NP
import Prelude hiding (Real, )
data T =
Cons {
device :: Play.Device,
clientName :: ClientName,
channel, extraChannel :: ChannelMsg.Channel,
sampleDirectory :: FilePath,
sampleRateInt :: SampleRate Integer,
chunkSize :: SVL.ChunkSize,
lazySize :: SigG.LazySize,
periodTime :: Double,
latency :: Int
}
deriving (Show)
deflt :: T
deflt =
Cons {
device = Play.defaultDevice,
clientName = defaultClientName,
channel = defaultChannel,
extraChannel = ChannelMsg.toChannel 1,
sampleDirectory = "speech",
sampleRateInt = SampleRate defaultSampleRate,
chunkSize = Play.defaultChunkSize,
lazySize = error "Option.lazySize must be initialized after option parsing",
periodTime = error "Option.periodTime must be initialized after option parsing",
latency = defaultLatency
}
defaultClientName :: ClientName
defaultClientName =
ClientName "Haskell-LLVM-Synthesizer"
defaultChannel :: ChannelMsg.Channel
defaultChannel = ChannelMsg.toChannel 0
defaultSampleRate :: Num a => a
-- defaultSampleRate = 24000
-- defaultSampleRate = 48000
defaultSampleRate = 44100
defaultLatency :: Int
defaultLatency =
-- 0
-- 256
1024
newtype SampleRate a = SampleRate a
deriving (Show)
instance Functor SampleRate where
fmap f (SampleRate sr) = SampleRate (f sr)
sampleRate :: Num a => T -> SampleRate a
sampleRate = fmap fromInteger . sampleRateInt
exitFailureMsg :: String -> IO a
exitFailureMsg msg =
IO.hPutStrLn IO.stderr msg >> exitFailure
parseChannel :: String -> IO ChannelMsg.Channel
parseChannel str =
case reads str of
[(chan, "")] ->
if 0<=chan && chan<16
then return $ ChannelMsg.toChannel chan
else exitFailureMsg "MIDI channel must a number from 0..15"
_ ->
exitFailureMsg $ "channel must be a number, but is '" ++ str ++ "'"
parseNumber ::
(Read a) =>
String -> (a -> Bool) -> String -> String -> IO a
parseNumber name constraint constraintName str =
case reads str of
[(n, "")] ->
if constraint n
then return n
else exitFailureMsg $ name ++ " must be a " ++ constraintName ++ " number"
_ ->
exitFailureMsg $ name ++ " must be a number, but is '" ++ str ++ "'"
maxInt :: Integer
maxInt =
fromIntegral (maxBound :: Int)
{-
Guide for common Linux/Unix command-line options:
http://www.faqs.org/docs/artu/ch10s05.html
-}
description :: [OptDescr (T -> IO T)]
description =
Option ['h'] ["help"]
(NoArg $ \ _flags -> do
programName <- getProgName
putStrLn
(usageInfo ("Usage: " ++ programName ++ " [OPTIONS]") description)
exitSuccess)
"show options" :
Option ['d'] ["device"]
(flip ReqArg "NAME" $ \str flags ->
return $ flags{device = str})
"select ALSA output device" :
Option [] ["clientname"]
(flip ReqArg "NAME" $ \str flags ->
return $ flags{clientName = ClientName str})
"name of the ALSA client" :
Option ['r'] ["samplerate"]
(flip ReqArg "RATE" $ \str flags ->
fmap (\rate -> flags{sampleRateInt = SampleRate rate}) $
parseNumber "sample-rate" (\n -> 0<n && n<=maxInt) "positive" str)
"sample-rate in samples per second" :
Option ['b'] ["blocksize"]
(flip ReqArg "SIZE" $ \str flags ->
fmap (\size -> flags{chunkSize = SVL.ChunkSize $ fromInteger size}) $
parseNumber "blocksize" (\n -> 0<n && n<=maxInt) "positive" str)
"block size as number of sample-frames" :
Option [] ["latency"]
(flip ReqArg "SIZE" $ \str flags ->
fmap (\size -> flags{latency = fromInteger size}) $
parseNumber "latency" (\n -> 0<=n && n<=maxInt) "non-negative" str)
"latency as number of sample-frames" :
Option ['c'] ["channel"]
(flip ReqArg "CHANNEL" $ \str flags ->
fmap (\chan -> flags{channel = chan}) $
parseChannel str)
"select MIDI input channel (0-based)" :
Option [] ["extra-channel"]
(flip ReqArg "CHANNEL" $ \str flags ->
fmap (\chan -> flags{extraChannel = chan}) $
parseChannel str)
"select MIDI channel with effects" :
Option ['I'] ["sample-directory"]
(flip ReqArg "DIR" $ \str flags ->
return $ flags{sampleDirectory = str})
"directory for sound samples" :
[]
get :: IO T
get = do
argv <- getArgs
let (opts, files, errors) = getOpt RequireOrder description argv
when (not $ null errors) $
exitFailureMsg (init (concat errors))
when (not $ null files) $
exitFailureMsg $
"Do not know what to do with arguments " ++ show files
fmap
(\actual ->
case chunkSize actual of
SVL.ChunkSize size ->
actual {
lazySize = SigG.LazySize size,
periodTime =
NP.fromIntegral size NP./
case sampleRate actual of SampleRate sr -> sr
}) $
foldl (>>=)
(return deflt)
opts