packages feed

synthesizer-llvm-0.5: alsa/Synthesizer/LLVM/Server/Option.hs

module Synthesizer.LLVM.Server.Option (
   T(..),
   get,
   defaultChannel,
   Play.defaultChunkSize,
   defaultSampleRate,
   defaultLatency,
   SampleRate(SampleRate),
   ) where

import Synthesizer.LLVM.Server.Common (SampleRate(SampleRate), )
import qualified Synthesizer.ALSA.Storable.Play as Play
import qualified Data.StorableVector.Lazy       as SVL
import Synthesizer.ALSA.EventList (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 Prelude hiding (Real, )


data T =
   Cons {
      device :: Play.Device,
      clientName :: ClientName,
      channel, extraChannel :: ChannelMsg.Channel,
      sampleDirectory :: FilePath,
      sampleRate :: Maybe (SampleRate Int),
      chunkSize :: SVL.ChunkSize,
      latency :: Int
   }
   deriving (Show)


deflt :: T
deflt =
   Cons {
      device = Play.defaultDevice,
      clientName = defaultClientName,
      channel = defaultChannel,
      extraChannel = ChannelMsg.toChannel 1,
      sampleDirectory = "speech",
      sampleRate = Nothing,
      chunkSize = Play.defaultChunkSize,
      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



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{sampleRate = Just $ SampleRate $ fromInteger 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
   foldl (>>=) (return deflt) opts