diff options
-rw-r--r-- | adjmix/Adjmix.hs | 71 | ||||
-rw-r--r-- | src/Trivmix/Types.hs | 90 | ||||
-rw-r--r-- | trivmix.cabal | 23 | ||||
-rw-r--r-- | trivmix.nix | 12 | ||||
-rw-r--r-- | trivmix/Trivmix.hs (renamed from src/Trivmix.hs) | 46 |
5 files changed, 189 insertions, 53 deletions
diff --git a/adjmix/Adjmix.hs b/adjmix/Adjmix.hs new file mode 100644 index 0000000..49b820f --- /dev/null +++ b/adjmix/Adjmix.hs | |||
@@ -0,0 +1,71 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | |||
3 | import Options.Applicative | ||
4 | |||
5 | import System.FilePath | ||
6 | |||
7 | import Data.Char | ||
8 | |||
9 | import Trivmix.Types | ||
10 | |||
11 | data Options = Options | ||
12 | { baseDirectory :: FilePath | ||
13 | , targetDirectory :: FilePath | ||
14 | , levelFile :: FilePath | ||
15 | , adjustment :: Adjustment Level | ||
16 | } | ||
17 | optionParser :: Parser Options | ||
18 | optionParser = Options | ||
19 | <$> strOption ( long "base" | ||
20 | <> metavar "DIRECTORY" | ||
21 | <> value "/dev/shm/mix" | ||
22 | ) | ||
23 | <*> strOption ( long "target" | ||
24 | <> short 't' | ||
25 | <> metavar "DIRECTORY" | ||
26 | <> help "Directory relative to ‘--base’ containing the level file" | ||
27 | ) | ||
28 | <*> strOption ( long "level" | ||
29 | <> metavar "FILENAME" | ||
30 | <> value "level" | ||
31 | <> help "Filename of the level file" | ||
32 | ) | ||
33 | <*> ( ( Set <$> option auto ( long "set" | ||
34 | <> short 'o' | ||
35 | <> metavar "LEVEL" | ||
36 | ) | ||
37 | ) | ||
38 | <|> ( Add <$> option auto ( long "add" | ||
39 | <> short 'a' | ||
40 | <> metavar "LEVEL" | ||
41 | ) | ||
42 | ) | ||
43 | <|> ( Sub <$> option auto ( long "sub" | ||
44 | <> short 's' | ||
45 | <> metavar "LEVEL" | ||
46 | ) | ||
47 | ) | ||
48 | ) | ||
49 | |||
50 | main :: IO () | ||
51 | main = execParser opts >>= adjmix | ||
52 | where | ||
53 | opts = info (helper <*> optionParser) | ||
54 | ( fullDesc | ||
55 | <> progDesc "Adjust the level file of a trivmix" | ||
56 | <> header "Adjmix — A trivial interface to a trivial mixer" | ||
57 | ) | ||
58 | |||
59 | adjmix :: Options -> IO () | ||
60 | adjmix Options{..} = do | ||
61 | oldLevel <- readFile levelFile >>= readIO . stripSpace | ||
62 | let | ||
63 | newLevel = oldLevel `doAdjustment` adjustment | ||
64 | writeFile levelFile (show newLevel ++ "\n") | ||
65 | where | ||
66 | levelFile = baseDirectory </> targetDirectory </> levelFile | ||
67 | stripSpace = reverse . stripSpace' . reverse . stripSpace' | ||
68 | stripSpace' [] = [] | ||
69 | stripSpace' l@(x:xs) = if isSpace x | ||
70 | then stripSpace' xs | ||
71 | else l | ||
diff --git a/src/Trivmix/Types.hs b/src/Trivmix/Types.hs new file mode 100644 index 0000000..66accdf --- /dev/null +++ b/src/Trivmix/Types.hs | |||
@@ -0,0 +1,90 @@ | |||
1 | {-# LANGUAGE FlexibleInstances, UndecidableInstances #-} | ||
2 | |||
3 | module Trivmix.Types | ||
4 | ( Level | ||
5 | , toFloat | ||
6 | , Adjustment(..) | ||
7 | , doAdjustment | ||
8 | , module Data.Default | ||
9 | ) where | ||
10 | |||
11 | import Data.Fixed | ||
12 | import Data.CaseInsensitive ( CI ) | ||
13 | import qualified Data.CaseInsensitive as CI | ||
14 | |||
15 | import Data.Default | ||
16 | |||
17 | import Data.Function (on) | ||
18 | |||
19 | data Level = Lin Float | DB Float | ||
20 | |||
21 | instance Num Level where | ||
22 | (+) = asFloat (+) | ||
23 | (-) = asFloat (-) | ||
24 | (*) = asFloat (*) | ||
25 | abs = Lin . abs . toFloat | ||
26 | signum = Lin . signum . toFloat | ||
27 | fromInteger = Lin . fromInteger | ||
28 | |||
29 | asFloat :: (Float -> Float -> Float) -> Level -> Level -> Level | ||
30 | asFloat f (Lin x) (Lin y) = Lin $ f x y | ||
31 | asFloat f x y = DB $ (f `on` toFloat) x y | ||
32 | |||
33 | toFloat :: Level -> Float | ||
34 | toFloat (Lin x) = x | ||
35 | toFloat (DB x) = x | ||
36 | |||
37 | withPrec :: (Num a, RealFrac a, HasResolution b) => a -> Fixed b | ||
38 | withPrec x = withResolution $ \p' -> MkFixed $ toInteger $ round $ x * (fromInteger p') | ||
39 | |||
40 | withType :: (p a -> f a) -> f a | ||
41 | withType f = f undefined | ||
42 | |||
43 | withResolution :: (HasResolution a) => (Integer -> f a) -> f a | ||
44 | withResolution f = withType (f . resolution) | ||
45 | |||
46 | instance Show Level where | ||
47 | show (Lin x) = show x | ||
48 | show (DB x) = (show $ (withPrec x' :: Milli)) ++ "dB" | ||
49 | where | ||
50 | x' = 20 * (logBase 10 x) | ||
51 | |||
52 | instance Read Level where | ||
53 | readsPrec i = map toL . readsPrec i | ||
54 | where | ||
55 | toL :: (Float, String) -> (Level, String) | ||
56 | toL (f, str) | ||
57 | | ((==) `on` CI.mk) prec unit = (DB $ 10 ** (0.05 * f), rest) | ||
58 | | otherwise = (Lin f, str) | ||
59 | where | ||
60 | prec = take lU str | ||
61 | rest = drop lU str | ||
62 | unit = "dB" | ||
63 | lU = length unit | ||
64 | |||
65 | instance Eq Level where | ||
66 | (Lin a) == (Lin b) = a == b | ||
67 | (Lin a) == (DB b) = a == b | ||
68 | (DB a) == (Lin b) = a == b | ||
69 | (DB a) == (DB b) = a == b | ||
70 | |||
71 | instance Default Level where | ||
72 | def = Lin 0 | ||
73 | |||
74 | data Adjustment a = Set a | ||
75 | | Add a | ||
76 | | Sub a | ||
77 | deriving (Show, Eq) | ||
78 | |||
79 | class Adjustable a where | ||
80 | add :: a -> a -> a | ||
81 | sub :: a -> a -> a | ||
82 | |||
83 | instance Num a => Adjustable a where | ||
84 | add = (+) | ||
85 | sub = (-) | ||
86 | |||
87 | doAdjustment :: Adjustable a => a -> Adjustment a -> a | ||
88 | doAdjustment _ (Set y) = y | ||
89 | doAdjustment x (Add y) = add x y | ||
90 | doAdjustment x (Sub y) = sub x y | ||
diff --git a/trivmix.cabal b/trivmix.cabal index 40aa9a8..0e02a7d 100644 --- a/trivmix.cabal +++ b/trivmix.cabal | |||
@@ -2,7 +2,7 @@ | |||
2 | -- documentation, see http://haskell.org/cabal/users-guide/ | 2 | -- documentation, see http://haskell.org/cabal/users-guide/ |
3 | 3 | ||
4 | name: trivmix | 4 | name: trivmix |
5 | version: 2.4.6 | 5 | version: 2.5.0 |
6 | -- synopsis: | 6 | -- synopsis: |
7 | -- description: | 7 | -- description: |
8 | license: PublicDomain | 8 | license: PublicDomain |
@@ -15,6 +15,14 @@ build-type: Simple | |||
15 | -- extra-source-files: | 15 | -- extra-source-files: |
16 | cabal-version: >=1.10 | 16 | cabal-version: >=1.10 |
17 | 17 | ||
18 | library | ||
19 | hs-source-dirs: src | ||
20 | default-language: Haskell2010 | ||
21 | exposed-modules: Trivmix.Types | ||
22 | build-depends: base >=4.7 && <4.8 | ||
23 | , data-default >=0.5 && <1 | ||
24 | , case-insensitive >=1.2 && <2 | ||
25 | |||
18 | executable trivmix | 26 | executable trivmix |
19 | main-is: Trivmix.hs | 27 | main-is: Trivmix.hs |
20 | -- other-modules: | 28 | -- other-modules: |
@@ -29,12 +37,21 @@ executable trivmix | |||
29 | , transformers >=0.3 && <1 | 37 | , transformers >=0.3 && <1 |
30 | , explicit-exception >=0.1 && <1 | 38 | , explicit-exception >=0.1 && <1 |
31 | , process >=1.2 && <2 | 39 | , process >=1.2 && <2 |
32 | , case-insensitive >=1.2 && <2 | ||
33 | , filelock >=0.1 && <1 | 40 | , filelock >=0.1 && <1 |
34 | hs-source-dirs: src | 41 | , trivmix |
42 | hs-source-dirs: trivmix | ||
35 | default-language: Haskell2010 | 43 | default-language: Haskell2010 |
36 | ghc-options: -threaded | 44 | ghc-options: -threaded |
37 | 45 | ||
46 | executable adjmix | ||
47 | main-is: Adjmix.hs | ||
48 | build-depends: base >=4.7 && <4.8 | ||
49 | , optparse-applicative >=0.11 && <1 | ||
50 | , filepath >=1.3 && <2 | ||
51 | , trivmix | ||
52 | hs-source-dirs: adjmix | ||
53 | default-language: Haskell2010 | ||
54 | |||
38 | -- Local Variables: | 55 | -- Local Variables: |
39 | -- firestarter: "nix-shell -p haskellPackages.cabal2nix --command 'cabal2nix ./.' | tee trivmix.nix" | 56 | -- firestarter: "nix-shell -p haskellPackages.cabal2nix --command 'cabal2nix ./.' | tee trivmix.nix" |
40 | -- End: | 57 | -- End: |
diff --git a/trivmix.nix b/trivmix.nix index fae88fc..6edcff0 100644 --- a/trivmix.nix +++ b/trivmix.nix | |||
@@ -1,18 +1,18 @@ | |||
1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! | 1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! |
2 | 2 | ||
3 | { cabal, caseInsensitive, explicitException, filelock, filepath | 3 | { cabal, caseInsensitive, dataDefault, explicitException, filelock |
4 | , hinotify, jack, optparseApplicative, transformers | 4 | , filepath, hinotify, jack, optparseApplicative, transformers |
5 | }: | 5 | }: |
6 | 6 | ||
7 | cabal.mkDerivation (self: { | 7 | cabal.mkDerivation (self: { |
8 | pname = "trivmix"; | 8 | pname = "trivmix"; |
9 | version = "2.4.6"; | 9 | version = "2.5.0"; |
10 | src = ./.; | 10 | src = ./.; |
11 | isLibrary = false; | 11 | isLibrary = true; |
12 | isExecutable = true; | 12 | isExecutable = true; |
13 | buildDepends = [ | 13 | buildDepends = [ |
14 | caseInsensitive explicitException filelock filepath hinotify jack | 14 | caseInsensitive dataDefault explicitException filelock filepath |
15 | optparseApplicative transformers | 15 | hinotify jack optparseApplicative transformers |
16 | ]; | 16 | ]; |
17 | meta = { | 17 | meta = { |
18 | license = self.stdenv.lib.licenses.publicDomain; | 18 | license = self.stdenv.lib.licenses.publicDomain; |
diff --git a/src/Trivmix.hs b/trivmix/Trivmix.hs index 37ecec6..9f0cf22 100644 --- a/src/Trivmix.hs +++ b/trivmix/Trivmix.hs | |||
@@ -34,11 +34,8 @@ import Data.Char | |||
34 | import Data.Function | 34 | import Data.Function |
35 | 35 | ||
36 | import Control.Monad | 36 | import Control.Monad |
37 | |||
38 | import Data.Fixed | ||
39 | 37 | ||
40 | import Data.CaseInsensitive ( CI ) | 38 | import Trivmix.Types |
41 | import qualified Data.CaseInsensitive as CI | ||
42 | 39 | ||
43 | data Options = Options | 40 | data Options = Options |
44 | { input :: String | 41 | { input :: String |
@@ -48,42 +45,6 @@ data Options = Options | |||
48 | , levelFiles :: [FilePath] | 45 | , levelFiles :: [FilePath] |
49 | } | 46 | } |
50 | 47 | ||
51 | data Level = Lin Float | DB Float | ||
52 | |||
53 | withPrec :: (Num a, RealFrac a, HasResolution b) => a -> Fixed b | ||
54 | withPrec x = withResolution $ \p' -> MkFixed $ toInteger $ round $ x * (fromInteger p') | ||
55 | |||
56 | withType :: (p a -> f a) -> f a | ||
57 | withType f = f undefined | ||
58 | |||
59 | withResolution :: (HasResolution a) => (Integer -> f a) -> f a | ||
60 | withResolution f = withType (f . resolution) | ||
61 | |||
62 | instance Show Level where | ||
63 | show (Lin x) = show x | ||
64 | show (DB x) = (show $ (withPrec x' :: Milli)) ++ "dB" | ||
65 | where | ||
66 | x' = 20 * (logBase 10 x) | ||
67 | |||
68 | instance Read Level where | ||
69 | readsPrec i = map toL . readsPrec i | ||
70 | where | ||
71 | toL :: (Float, String) -> (Level, String) | ||
72 | toL (f, str) | ||
73 | | ((==) `on` CI.mk) prec unit = (DB $ 10 ** (0.05 * f), rest) | ||
74 | | otherwise = (Lin f, str) | ||
75 | where | ||
76 | prec = take lU str | ||
77 | rest = drop lU str | ||
78 | unit = "dB" | ||
79 | lU = length unit | ||
80 | |||
81 | instance Eq Level where | ||
82 | (Lin a) == (Lin b) = a == b | ||
83 | (Lin a) == (DB b) = a == b | ||
84 | (DB a) == (Lin b) = a == b | ||
85 | (DB a) == (DB b) = a == b | ||
86 | |||
87 | optionParser :: Parser Options | 48 | optionParser :: Parser Options |
88 | optionParser = Options <$> | 49 | optionParser = Options <$> |
89 | (fromMaybe "in" <$> optional (strOption ( long "input" | 50 | (fromMaybe "in" <$> optional (strOption ( long "input" |
@@ -120,7 +81,7 @@ watchedAttrs = [ Modify | |||
120 | ] | 81 | ] |
121 | 82 | ||
122 | initialLevel :: Level | 83 | initialLevel :: Level |
123 | initialLevel = Lin 0 | 84 | initialLevel = def |
124 | 85 | ||
125 | defFileMode :: FileMode | 86 | defFileMode :: FileMode |
126 | defFileMode = foldl unionFileModes nullFileMode [ ownerReadMode | 87 | defFileMode = foldl unionFileModes nullFileMode [ ownerReadMode |
@@ -168,9 +129,6 @@ mix :: MVar Level -> CFloat -> IO CFloat | |||
168 | mix level input = do | 129 | mix level input = do |
169 | level' <- readMVar level | 130 | level' <- readMVar level |
170 | return $ (CFloat $ toFloat level') * input | 131 | return $ (CFloat $ toFloat level') * input |
171 | where | ||
172 | toFloat (Lin x) = x | ||
173 | toFloat (DB x) = x | ||
174 | 132 | ||
175 | handleFiles :: INotify -> MVar Level -> [FilePath] -> IO () | 133 | handleFiles :: INotify -> MVar Level -> [FilePath] -> IO () |
176 | handleFiles inotify level files = do | 134 | handleFiles inotify level files = do |