diff options
| -rw-r--r-- | src/Main.hs | 18 | ||||
| -rw-r--r-- | src/Trivstream/Options.hs | 37 | ||||
| -rw-r--r-- | src/Trivstream/Options/Utils.hs | 32 | ||||
| -rw-r--r-- | src/Trivstream/Types.hs | 92 | ||||
| -rw-r--r-- | trivstream.cabal | 24 | ||||
| -rw-r--r-- | trivstream.nix | 14 | 
6 files changed, 212 insertions, 5 deletions
diff --git a/src/Main.hs b/src/Main.hs index c2e4af9..70c3ad5 100644 --- a/src/Main.hs +++ b/src/Main.hs  | |||
| @@ -1,4 +1,22 @@ | |||
| 1 | module Main where | 1 | module Main where | 
| 2 | 2 | ||
| 3 | |||
| 4 | import Trivstream.Types | ||
| 5 | import Trivstream.Options | ||
| 6 | |||
| 7 | |||
| 8 | import Options.Applicative | ||
| 9 | |||
| 10 | import Control.Lens | ||
| 11 | import Data.Default.Class | ||
| 12 | import Data.Serialize | ||
| 13 | import Data.Conduit.Cereal | ||
| 14 | |||
| 15 | import Sound.Pulse.Simple | ||
| 16 | import Sound.JACK | ||
| 17 | |||
| 18 | import Network.Socket | ||
| 19 | |||
| 20 | |||
| 3 | main :: IO () | 21 | main :: IO () | 
| 4 | main = undefined | 22 | main = undefined | 
diff --git a/src/Trivstream/Options.hs b/src/Trivstream/Options.hs new file mode 100644 index 0000000..a777942 --- /dev/null +++ b/src/Trivstream/Options.hs  | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | {-# LANGUAGE TemplateHaskell, OverloadedStrings #-} | ||
| 2 | |||
| 3 | module Trivstream.Options | ||
| 4 | ( withOptions | ||
| 5 | ) where | ||
| 6 | |||
| 7 | |||
| 8 | import Trivstream.Types | ||
| 9 | import Trivstream.Options.Utils | ||
| 10 | import Paths_trivstream (version) | ||
| 11 | |||
| 12 | |||
| 13 | import Options.Applicative | ||
| 14 | |||
| 15 | import Control.Monad.Reader | ||
| 16 | import Control.Monad.IO.Class | ||
| 17 | |||
| 18 | |||
| 19 | withOptions :: MonadIO m => ReaderT Configuration a -> IO a | ||
| 20 | withOptions f = liftIO (execParser options) >>= runReaderT f | ||
| 21 | where | ||
| 22 | options = options' `info` mconcat [ header $ concat [ "trivstream " | ||
| 23 | , show version | ||
| 24 | , " - " | ||
| 25 | , "A trivial client & server for streaming audio between pulseaudio and jack over udp/tcp" | ||
| 26 | ] | ||
| 27 | , footer $ concat [ "trivstream " | ||
| 28 | , show version | ||
| 29 | , " (", $(gitBranch), "@", $(gitHash), (if $(gitDirty) then "*" else ""), ")" | ||
| 30 | ] | ||
| 31 | ] | ||
| 32 | options' = Configuration <$> argument rCI (help "Mode of operation" <> value def <> showDefault <> metavar "MODE") | ||
| 33 | <*> optional ( undefined | ||
| 34 | ) | ||
| 35 | <*> audioOptions | ||
| 36 | |||
| 37 | audioOptions = undefined | ||
diff --git a/src/Trivstream/Options/Utils.hs b/src/Trivstream/Options/Utils.hs new file mode 100644 index 0000000..30694d8 --- /dev/null +++ b/src/Trivstream/Options/Utils.hs  | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | {-# LANGUAGE StandaloneDeriving #-} | ||
| 2 | |||
| 3 | module Trivstream.Options.Utils | ||
| 4 | ( rCI | ||
| 5 | ) where | ||
| 6 | |||
| 7 | import Options.Applicative | ||
| 8 | |||
| 9 | import Data.Char | ||
| 10 | import Data.Maybe | ||
| 11 | |||
| 12 | import Network.Socket | ||
| 13 | |||
| 14 | |||
| 15 | deriving instance Enum Family | ||
| 16 | deriving instance Bounded Family | ||
| 17 | |||
| 18 | |||
| 19 | rCI :: (Show a, Read a) => ReadM a | ||
| 20 | rCI = eitherReader rRep' | ||
| 21 | where | ||
| 22 | rRep' str = case mapMaybe readMaybe $ cases str of | ||
| 23 | [] -> Left $ "Could not parse `" ++ str ++ "'" | ||
| 24 | [x] -> Right x | ||
| 25 | xs -> Left $ "Ambiguous parse for `" ++ str ++ "': " ++ show xs | ||
| 26 | cases [] = [[]] | ||
| 27 | cases (c:cs) = [(c':cs') | c' <- [toLower c, c, toUpper c], cs' <- cases cs] | ||
| 28 | |||
| 29 | rFamily :: ReadM Family | ||
| 30 | rFamily = undefined | ||
| 31 | where | ||
| 32 | families = filter isSupportedFamily [minBound..maxBound] | ||
diff --git a/src/Trivstream/Types.hs b/src/Trivstream/Types.hs new file mode 100644 index 0000000..8cdd592 --- /dev/null +++ b/src/Trivstream/Types.hs  | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | {-# LANGUAGE TemplateHaskell #-} | ||
| 2 | {-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving, DeriveGeneric #-} | ||
| 3 | |||
| 4 | module Trivstream.Types | ||
| 5 | ( AudioConfig(..) | ||
| 6 | , AudioBackend(..) | ||
| 7 | , SampleRate(..) | ||
| 8 | , Configuration(..) | ||
| 9 | , Mode (..) | ||
| 10 | ) where | ||
| 11 | |||
| 12 | |||
| 13 | import Control.Lens | ||
| 14 | import Control.Lens.TH | ||
| 15 | import Data.Default.Class (Default(..)) | ||
| 16 | import Data.Serialize (Serialize) | ||
| 17 | import GHC.Generics (Generic) | ||
| 18 | |||
| 19 | import Foreign.C.Types (CInt(..)) | ||
| 20 | |||
| 21 | import Sound.Pulse.Simple (ChannelPosition(..), ChannelPan(..)) | ||
| 22 | import Sound.JACK () | ||
| 23 | |||
| 24 | import Network.Socket (Family(..), SocketType(..), ProtocolNumber(..), SockAddr(..)) | ||
| 25 | |||
| 26 | |||
| 27 | deriving instance Generic ChannelPan | ||
| 28 | instance Serialize ChannelPan | ||
| 29 | |||
| 30 | deriving instance Generic ChannelPosition | ||
| 31 | instance Serialize ChannelPosition | ||
| 32 | |||
| 33 | |||
| 34 | data AudioBackend = Pulse | Jack | ||
| 35 | deriving (Enum, Eq, Generic) | ||
| 36 | |||
| 37 | instance Default AudioBackend where | ||
| 38 | def = Pulse | ||
| 39 | |||
| 40 | instance Serialize AudioBackend | ||
| 41 | |||
| 42 | |||
| 43 | -- | Numeric instances consider this value to be in `kHz` | ||
| 44 | newtype SampleRate = SampleRate Int | ||
| 45 | deriving (Eq, Ord, Enum, Num, Real, Integral, Generic) | ||
| 46 | makePrisms ''SampleRate | ||
| 47 | |||
| 48 | instance Default SampleRate where | ||
| 49 | def = 44100 | ||
| 50 | |||
| 51 | instance Serialize SampleRate | ||
| 52 | |||
| 53 | |||
| 54 | data AudioConfig = AudioConfig | ||
| 55 | { _aBackend :: AudioBackend | ||
| 56 | , _aChannels :: [Maybe ChannelPosition] | ||
| 57 | , _aRate :: SampleRate | ||
| 58 | } deriving (Generic) | ||
| 59 | makeLenses ''AudioConfig | ||
| 60 | |||
| 61 | instance Default AudioConfig where | ||
| 62 | def = AudioConfig | ||
| 63 | { _aBackend = def | ||
| 64 | , _aChannels = [Just $ ChannelNormal PanLeft, Just $ ChannelNormal PanRight] | ||
| 65 | , _aRate = def | ||
| 66 | } | ||
| 67 | |||
| 68 | instance Serialize AudioConfig | ||
| 69 | |||
| 70 | |||
| 71 | data Mode = Server | Client | ||
| 72 | deriving (Enum, Eq, Generic, Show) | ||
| 73 | |||
| 74 | instance Default Mode where | ||
| 75 | def = Server | ||
| 76 | |||
| 77 | instance Serialize Mode | ||
| 78 | |||
| 79 | |||
| 80 | data Configuration = Configuration | ||
| 81 | { _cMode :: Mode | ||
| 82 | , _cSocketDesc :: Maybe (Family, SocketType, ProtocolNumber, SockAddr) | ||
| 83 | , _cAudio :: AudioConfig | ||
| 84 | } deriving (Generic) | ||
| 85 | makeLenses ''Configuration | ||
| 86 | |||
| 87 | instance Default Configuration where | ||
| 88 | def = Configuration | ||
| 89 | { _cMode = def | ||
| 90 | , _cSocketDesc = Nothing | ||
| 91 | , _cAudio = def | ||
| 92 | } | ||
diff --git a/trivstream.cabal b/trivstream.cabal index 546fc1a..a8dc9ec 100644 --- a/trivstream.cabal +++ b/trivstream.cabal  | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | name: trivstream | 1 | name: trivstream | 
| 2 | version: 0.0.0 | 2 | version: 0.0.0 | 
| 3 | synopsis: A trivial client & server for streaming audio over udp between pulseaudio & jack | 3 | synopsis: A trivial client & server for streaming audio between pulseaudio and jack over udp/tcp | 
| 4 | -- description: | 4 | -- description: | 
| 5 | license: GPL-3 | 5 | license: GPL-3 | 
| 6 | author: Gregor Kleen | 6 | author: Gregor Kleen | 
| @@ -13,8 +13,28 @@ cabal-version: >=1.10 | |||
| 13 | 13 | ||
| 14 | executable trivstream | 14 | executable trivstream | 
| 15 | main-is: Main.hs | 15 | main-is: Main.hs | 
| 16 | -- other-modules: | 16 | extensions: TemplateHaskell | 
| 17 | , DeriveGeneric | ||
| 18 | , StandaloneDeriving | ||
| 19 | , GeneralizedNewtypeDeriving | ||
| 20 | , OverloadedStrings | ||
| 21 | other-modules: Trivstream.Types | ||
| 17 | -- other-extensions: | 22 | -- other-extensions: | 
| 18 | build-depends: base >=4.9 && <5 | 23 | build-depends: base >=4.9 && <5 | 
| 24 | , cereal >=0.5.3.0 && <1 | ||
| 25 | , cereal-conduit >=0.7.3 && <1 | ||
| 26 | , conduit >=1.2.6.6 && <2 | ||
| 27 | , conduit-extra >=1.1.13.2 && <2 | ||
| 28 | , data-default-class >=0.1.2.0 && <1 | ||
| 29 | , gitrev >=1.2.0 && <2 | ||
| 30 | , jack >=0.7.0.3 && <1 | ||
| 31 | , lens >=4.14 && <5 | ||
| 32 | , network >=2.6.2.1 && <3 | ||
| 33 | , mtl | ||
| 34 | , optparse-applicative >=0.12.1.0 && <1 | ||
| 35 | , pulse-simple >=0.1.14 && <1 | ||
| 36 | , socket-activation >=0.1.0.1 && <1 | ||
| 37 | , transformers | ||
| 19 | hs-source-dirs: src | 38 | hs-source-dirs: src | 
| 20 | default-language: Haskell2010 | 39 | default-language: Haskell2010 | 
| 40 | ghc-options: -threaded | ||
diff --git a/trivstream.nix b/trivstream.nix index 2521a88..7e8c872 100644 --- a/trivstream.nix +++ b/trivstream.nix  | |||
| @@ -1,11 +1,19 @@ | |||
| 1 | { mkDerivation, base, stdenv }: | 1 | { mkDerivation, base, cereal, cereal-conduit, conduit | 
| 2 | , conduit-extra, data-default-class, gitrev, jack, lens, mtl | ||
| 3 | , network, optparse-applicative, pulse-simple, socket-activation | ||
| 4 | , stdenv, transformers | ||
| 5 | }: | ||
| 2 | mkDerivation { | 6 | mkDerivation { | 
| 3 | pname = "trivstream"; | 7 | pname = "trivstream"; | 
| 4 | version = "0.0.0"; | 8 | version = "0.0.0"; | 
| 5 | src = ./.; | 9 | src = ./.; | 
| 6 | isLibrary = false; | 10 | isLibrary = false; | 
| 7 | isExecutable = true; | 11 | isExecutable = true; | 
| 8 | executableHaskellDepends = [ base ]; | 12 | executableHaskellDepends = [ | 
| 9 | description = "A trivial client & server for streaming audio over udp between pulseaudio & jack"; | 13 | base cereal cereal-conduit conduit conduit-extra data-default-class | 
| 14 | gitrev jack lens mtl network optparse-applicative pulse-simple | ||
| 15 | socket-activation transformers | ||
| 16 | ]; | ||
| 17 | description = "A trivial client & server for streaming audio between pulseaudio and jack over udp/tcp"; | ||
| 10 | license = stdenv.lib.licenses.gpl3; | 18 | license = stdenv.lib.licenses.gpl3; | 
| 11 | } | 19 | } | 
