summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2015-06-07 19:33:45 +0200
committerGregor Kleen <gkleen@yggdrasil.li>2015-06-07 19:33:45 +0200
commitbd7874ef606ae78bb8b626bd01906481feb784d6 (patch)
tree5990e8498393e9344be47f722543668e54081b18 /src
parent9921cfd56ceca0cff91c9df018538a04f5776123 (diff)
downloadtrivmix-bd7874ef606ae78bb8b626bd01906481feb784d6.tar
trivmix-bd7874ef606ae78bb8b626bd01906481feb784d6.tar.gz
trivmix-bd7874ef606ae78bb8b626bd01906481feb784d6.tar.bz2
trivmix-bd7874ef606ae78bb8b626bd01906481feb784d6.tar.xz
trivmix-bd7874ef606ae78bb8b626bd01906481feb784d6.zip
Rewrite in haskell
Diffstat (limited to 'src')
-rw-r--r--src/Trivmix.hs103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/Trivmix.hs b/src/Trivmix.hs
new file mode 100644
index 0000000..019ee32
--- /dev/null
+++ b/src/Trivmix.hs
@@ -0,0 +1,103 @@
1{-# LANGUAGE RecordWildCards #-}
2
3import Foreign.C.Types (CFloat(..))
4import qualified Sound.JACK as Jack
5import qualified Sound.JACK.Audio as Audio
6
7import Options.Applicative
8
9import Data.Maybe
10
11import System.Directory
12import System.FilePath
13import System.Posix.Files
14import System.Posix.IO
15import System.Environment
16
17import Control.Concurrent
18import Control.Concurrent.MVar
19
20import qualified Control.Monad.Trans.Class as Trans
21
22import Control.Exception
23import System.IO.Error
24
25import System.INotify
26
27data Options = Options
28 { input :: String
29 , output :: String
30 , initialLevel :: Float
31 , stateDir :: FilePath
32 }
33
34optionParser :: Parser Options
35optionParser = Options <$>
36 strOption ( long "input"
37 <> metavar "JACK"
38 )
39 <*> strOption ( long "output"
40 <> metavar "JACK"
41 )
42 <*> (fromMaybe 0 <$> optional (option auto ( long "level"
43 <> metavar "FLOAT"
44 )
45 )
46 )
47 <*> strOption ( long "dir"
48 <> metavar "DIRECTORY"
49 )
50
51main :: IO ()
52main = execParser opts >>= trivmix
53 where
54 opts = info (helper <*> optionParser)
55 ( fullDesc
56 <> progDesc "Setup a JACK mixing input/output pair controlled by fifos in a state directory"
57 <> header "Trivmix - A trivial mixer"
58 )
59
60trivmix :: Options -> IO ()
61trivmix Options{..} = do
62 name <- getProgName
63 createDirectoryIfMissing True stateDir
64 level <- newMVar initialLevel
65 let levelFile = stateDir </> "level"
66 onLevelFile levelFile initialLevel $ withINotify $ \n -> do
67 addWatch n [Modify] levelFile (const $ handleLevel level levelFile)
68 Jack.handleExceptions $
69 Jack.withClientDefault name $ \client ->
70 Jack.withPort client input $ \input' ->
71 Jack.withPort client output $ \output' ->
72 Audio.withProcessMono client input' (mix level) output' $
73 Jack.withActivation client $ Trans.lift $ do
74 Jack.waitForBreak
75
76mix :: MVar Float -> CFloat -> IO CFloat
77mix level input = do
78 level' <- readMVar level
79 return $ (CFloat level') * input
80
81onLevelFile :: FilePath -> Float -> IO a -> IO a
82onLevelFile file initial action = do
83 exists <- doesFileExist file
84 let acquire = case exists of
85 True -> return ()
86 False -> createFile file mode >>= closeFd
87 mode = foldl unionFileModes nullFileMode [ ownerReadMode
88 , ownerWriteMode
89 , groupReadMode
90 , groupWriteMode
91 ]
92 release = case exists of
93 True -> return ()
94 False -> removeFile file
95 bracket_ acquire release action
96
97handleLevel :: MVar Float -> FilePath -> IO ()
98handleLevel level file = catch action handler
99 where
100 action = readFile file >>= readIO >>= swapMVar level >>= const (return ())
101 handler e = if isUserError e
102 then readMVar level >>= \l -> writeFile file (show l)
103 else throw e