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 | 
