-- | -- Module : SoXBasics -- Copyright : (c) OleksandrZhabenko 2019-2020 -- License : MIT -- Stability : Experimental -- Maintainer : olexandr543@yahoo.com -- -- A program and a library that can be used as a simple -- basic interface to some SoX functionality or for producing -- the approximately Ukrainian speech with your own recorded -- voice (actually it produces the needed sound representations). -- module SoXBasics ( -- * Get Information maxAbs , getMaxA , getMinA , selMaxAbs , selMA , extremeS , extremeS1 , soxStat , upperBnd , durationA , sampleAn -- * Produce sound -- ** Trimming the silence , alterVadB , alterVadE , alterVadHelp , opFile -- ** Amplitude modification , norm , normL , gainL , quarterSinFade -- ** Adding silence , silenceBoth -- ** Recording , recA -- ** Changing sample rate , resampleA -- ** Working with noise , noiseProfB , noiseProfE , noiseReduceB , noiseReduceE , noiseReduceBU , noiseReduceEU -- ** Filtering , sincA -- ** Volume amplification , volS , volS2 -- * Playing sound , playA ) where import System.Directory import Data.Maybe (isJust, fromJust) import Numeric import Data.Char import System.Process import System.IO import EndOfExe import System.Exit import Control.Concurrent (threadDelay) import Control.Exception (onException) import System.Info (os) import Control.Exception.FinalException -- | Function 'maxAbs' allows to choose a maximum by absolute value if the values are written as @String@. Bool @True@ corresponds to maximum value, @False@ - to minimum value maxAbs :: (String, String) -> (String, Bool) maxAbs (xs, ys) | null xs || null ys = ([], False) | head xs == '-' && head ys == '-' = if compare xs ys /= LT then (xs, False) else (ys, False) | head xs /= '-' && head ys /= '-' = if compare xs ys == GT then (xs, True) else (ys, True) | head xs == '-' && head ys /= '-' = if compare (tail xs) ys /= LT then (xs, False) else (ys, True) | otherwise = if compare xs (tail ys) == GT then (xs, True) else (ys, False) -- | Function 'getMaxA' returns a maximum amplitude of the sound in the file in the given lower and upper bounds represented as a tuple of @Int@ values. getMaxA :: FilePath -> (Int, Int) -> IO String getMaxA file (lowerbound, upperbound) = if isJust (showE "sox") then do (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", show lowerbound ++ "s", "=" ++ show upperbound ++ "s", "stat"] "" let zs = lines herr in return (let u = (words $ zs !! 3) !! 2 in if head u == '-' then take 9 u else take 8 u) else do catchEnd ExecutableNotProperlyInstalled return [] -- | Function 'getMinA' returns a minimum amplitude of the sound in the file in the given lower and upper bounds represented as a tuple of @Int@ values. getMinA :: FilePath -> (Int, Int) -> IO String getMinA file (lowerbound, upperbound) = if isJust (showE "sox") then do (_, _, herr1) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", show lowerbound ++ "s", "=" ++ show upperbound ++ "s", "stat"] "" let zs = lines herr1 in return (let u = (words $ zs !! 4) !! 2 in if head u == '-' then take 9 u else take 8 u) else do catchEnd ExecutableNotProperlyInstalled return [] -- | Function 'selMaxAbs' returns a maximum by absolute value amplitude of the sound and allows by its second value in the tuple determine whether it is a maximum or minimum. -- Bool @True@ corresponds to maximum value, @False@ - to minimum value. selMaxAbs :: FilePath -> (Int, Int) -> IO (String, Bool) selMaxAbs file (lowerbnd, upperbnd) = do tX <- getMaxA file (lowerbnd, upperbnd) tN <- getMinA file (lowerbnd, upperbnd) return (maxAbs (tX, tN)) -- | Function 'selMA' returns a maximum or a minimum of the sound amplitude of the file depending on the @Bool@ value given. -- Bool @True@ corresponds to maximum value, @False@ - to minimum value. selMA :: FilePath -> (Int, Int) -> Bool -> IO String selMA file (lowerbnd, upperbnd) x = if x then getMaxA file (lowerbnd, upperbnd) else getMinA file (lowerbnd, upperbnd) -- | Function 'extremeS' returns an approximate sample number of the extremum, which will be used further for fade effect. extremeS :: FilePath -> (Int, Int) -> Int -> IO (String, Bool) -> IO Int extremeS file (lowerbnd, upperbnd) eps x = if compare (upperbnd - lowerbnd) (eps + 33) == LT then return $ (upperbnd + lowerbnd) `quot` 2 else do (ys, z) <- x let t = (lowerbnd + upperbnd) `quot` 2 rs <- selMA file (lowerbnd, t) z if (ys == rs) then extremeS file (lowerbnd, t) eps x else extremeS file (t, upperbnd) eps x -- | Function 'alterVadB' removes an approximate silence measured by the absolute value of the sound amplitude from the beginning of the file. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). The file must have maximum amplitude absolute value close to 1 before call to the 'alterVadB'. -- The second @Double@ parameter is used to exit the iteration cycle. The @Int@ parameter from the range [0..3] specifies a maximum amplitude, starting from -- which the sound will not be trimmed. alterVadB :: FilePath -> Double -> Int -> Double -> IO () alterVadB file lim noiseMax exit | compare lim exit /= GT = putStrLn $ "File " ++ file ++ " is ready for further processing." | otherwise = if isJust (showE "sox") then do lim1 <- durationA file alterVadHelp file lim1 lim noiseMax exit else catchEnd ExecutableNotProperlyInstalled -- | Function 'alterVadHelp' is used internally in the 'alterVadB' and 'alterVadE' functions. alterVadHelp :: FilePath -> Double -> Double -> Int -> Double -> IO () alterVadHelp file lim1 lim noiseMax exit | compare lim1 lim == LT = alterVadB file lim1 noiseMax exit | compare lim1 lim == EQ = let noiseM = (case noiseMax of 0 -> "0.01" 1 -> "0.02" 2 -> "0.04" 3 -> "0.08" _ -> "0.04") in do (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", "0", showFFloat Nothing (lim1 / 2.0) $ show 0, "stat"] "" let zs = lines herr in let z = concatMap (dropWhile (not . isDigit)) . take 1 . drop 3 $ zs in if z < noiseM then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "7" ++ file, "trim", showFFloat Nothing (lim1 / 2.0) $ show 0, "-0.000000"] "" if code == ExitSuccess then do threadDelay 100000 opFile file exit noiseMax else do e0 <- doesFileExist $ "7" ++ file if e0 then do removeFile $ "7" ++ file catchEnd MaybePartiallyTrimmed else catchEnd MaybePartiallyTrimmed else alterVadB file (lim1 / 4.0) noiseMax exit | otherwise = let noiseM = (case noiseMax of 0 -> "0.01" 1 -> "0.02" 2 -> "0.04" 3 -> "0.08" _ -> "0.04") in do (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", "0", showFFloat Nothing (lim / 2.0) $ show 0, "stat"] "" let zs = lines herr in let z = concatMap (dropWhile (not . isDigit)) . take 1 . drop 3 $ zs in if z < noiseM then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "7" ++ file, "trim", showFFloat Nothing (lim / 2.0) $ show 0, "-0.000000"] "" if code == ExitSuccess then do threadDelay 100000 opFile file exit noiseMax else do e0 <- doesFileExist $ "7" ++ file if e0 then do removeFile $ "7" ++ file catchEnd MaybePartiallyTrimmed else catchEnd MaybePartiallyTrimmed else alterVadB file (lim / 4.0) noiseMax exit -- | Function 'opFile' is used internally in 'alterVadB' to check whether @FilePath@ exist and if so to do some processing to allow the 'alterVadB' function iterate further. opFile :: FilePath -> Double -> Int -> IO () opFile file exit noiseMax = do removeFile file exist0 <- doesFileExist file if not exist0 then do renameFile ("7" ++ file) file lim2 <- durationA file alterVadB file lim2 noiseMax exit else opFile file exit noiseMax -- | Function 'norm' applies a SoX normalization effect on the audio file. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). norm :: FilePath -> IO () norm file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "8" ++ file, "norm"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "8" ++ file if e1 then do removeFile $ "8" ++ file catchEnd (NotCreatedWithEffect "norm") else catchEnd (NotCreatedWithEffect "norm") else do e2 <- doesFileExist $ "8" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'normL' applies a SoX gain effect on the audio file with the maximum absolute dB value given by the @Int@ argument. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). normL :: FilePath -> Int -> IO () normL file level = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "9" ++ file, "gain", "-n", show level] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "9" ++ file if e1 then do removeFile $ "9" ++ file catchEnd (NotCreatedWithEffect "gain -n") else catchEnd (NotCreatedWithEffect "gain -n") else do e2 <- doesFileExist $ "9" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'normL' applies a SoX \"gain -b [db-Value]\" effect on the audio file with dB value given by the @Double@ argument. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). gainL :: FilePath -> Double -> IO () gainL file level = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "9" ++ file, "gain", "-b", showFFloat (Just 6) level $ show 0] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "9" ++ file if e1 then do removeFile $ "9" ++ file catchEnd (NotCreatedWithEffect "gain -b") else catchEnd (NotCreatedWithEffect "gain -b") else do e2 <- doesFileExist $ "9" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'soxStat' prints a SoX statistics for the audio file. soxStat :: FilePath -> IO () soxStat file = if isJust (showE "sox") then do (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "stat"] "" putStrLn herr else catchEnd ExecutableNotProperlyInstalled -- | Function 'alterVadE' removes an approximate silence measured by the absolute value of the sound amplitude from the end of the file. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). The second @Double@ parameter is used to exit the iteration cycle. The @Int@ parameter -- from the range [0..3] specifies a maximum amplitude, starting from which the sound will not be trimmed. alterVadE :: FilePath -> Double -> Int -> Double -> IO () alterVadE file lim noiseMax exit | compare lim exit /= GT = putStrLn $ "File " ++ file ++ " is ready for further processing" | otherwise = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "6" ++ file, "reverse"] "" if code /= ExitSuccess then do e0 <- doesFileExist $ "6" ++ file if e0 then do removeFile $ "6" ++ file catchEnd (NotCreated file) else do catchEnd (NotCreated file) else do alterVadB ("6" ++ file) lim noiseMax exit (code1, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) ["6" ++ file, "76" ++ file, "reverse"] "" if code1 /= ExitSuccess then do e1 <- doesFileExist $ "76" ++ file if e1 then do removeFile $ "76" ++ file removeFile $ "6" ++ file catchEnd (NotCreated file) else do removeFile $ "6" ++ file catchEnd (NotCreated file) else do e2 <- doesFileExist $ "76" ++ file if e2 then do removeFile $ "6" ++ file removeFile file renameFile ("76" ++ file) file else do removeFile $ "6" ++ file catchEnd (NotCreated file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'upperBnd' returns a maximum number of samples for use in other functions. upperBnd :: FilePath -> IO Int upperBnd file = if isJust (showE "soxi") then do (_, Just hout, _, _) <- createProcess (proc (fromJust (showE "soxi")) ["-s",file]){ std_out = CreatePipe } x0 <- hGetContents hout let z = read x0::Int in return z else catchEnd ExecutableNotProperlyInstalled >> return (0::Int) -- | Variant of the function 'extremeS' with all the additional information included. extremeS1 :: FilePath -> IO Int extremeS1 file = do upp <- upperBnd file extremeS file (0::Int, upp) (if upp `quot` 32 > 2 then upp `quot` 32 else 2::Int) (selMaxAbs file (0::Int, upp)) -- | Function 'quarterSinFade' applies a fade effect by SoX to the audio file with \"q\" type. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). quarterSinFade :: FilePath -> IO () quarterSinFade file = if isJust (showE "sox") then do pos <- extremeS1 file upp <- upperBnd file (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "4" ++ file, "fade", "q", show pos ++ "s", "=" ++ show upp ++ "s", show (upp - pos) ++ "s"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "4" ++ file if e1 then do removeFile $ "4" ++ file catchEnd (NotCreatedWithEffect "fade q") else catchEnd (NotCreatedWithEffect "fade q") else do e2 <- doesFileExist $ "4" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'silenceBoth' adds some silence to both ends of the audio. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). silenceBoth :: FilePath -> Int -> Int -> IO () silenceBoth file beginning end = if isJust (showE "sox") then do _ <- readProcessWithExitCode (fromJust (showE "sox")) [file, "3" ++ file, "delay", show beginning ++ "s", "reverse"] "" _ <- readProcessWithExitCode (fromJust (showE "sox")) ["3" ++ file, "2" ++ file, "delay", show end ++ "s", "reverse"] "" removeFile $ "3" ++ file else catchEnd ExecutableNotProperlyInstalled -- | Function 'recA' records audio file with the given name and duration in seconds. For Windows it uses a default audio device and \"-t waveaudio -d\" option to the SoX. recA :: FilePath -> Double -> IO () recA file x | isJust (showE "sox") && take 5 os == "mingw" = do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) ["-t","waveaudio","-d","-b16", "-c1", "-esigned-integer", "-L", file, "trim", "0.5", showFFloat Nothing x $ show 0] "" if code /= ExitSuccess then do e0 <- doesFileExist file if e0 then do removeFile file catchEnd (NotRecorded file) else catchEnd (NotRecorded file) else do e1 <- doesFileExist file if e1 then return () else catchEnd (NotRecorded file) | isJust (showE "rec") = do (code, _, _) <- readProcessWithExitCode (fromJust (showE "rec")) ["-b16", "-c1", "-esigned-integer", "-L", file, "trim", "0.5", showFFloat Nothing x $ show 0] "" if code /= ExitSuccess then do e0 <- doesFileExist file if e0 then do removeFile file catchEnd (NotRecorded file) else catchEnd (NotRecorded file) else do e1 <- doesFileExist file if e1 then return () else catchEnd (NotRecorded file) | otherwise = catchEnd ExecutableNotProperlyInstalled -- | Function 'resampleA' changes the sample rate for the recorded audio for further processing. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). resampleA :: FilePath -> Int -> IO () resampleA file frequency = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "3" ++ file, "rate", "-s", "-I", show frequency] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "3" ++ file if e1 then do removeFile $ "3" ++ file catchEnd (NotCreatedWithEffect "rate") else catchEnd (NotCreatedWithEffect "rate") else do e2 <- doesFileExist $ "3" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'durationA' returns a duration of the audio file in seconds. durationA :: FilePath -> IO Double durationA file = if isJust (showE "soxi") then do (_, Just hout, _, _) <- createProcess (proc (fromJust (showE "soxi")) ["-D",file]){ std_out = CreatePipe } x0 <- hGetContents hout let z = read x0::Double in return z else catchEnd ExecutableNotProperlyInstalled >> return 0.0 -- | Function 'playA' plays the given file with SoX. For Windows it uses \"-t waveaudio -d\" options for SoX. playA :: FilePath -> IO () playA file | take 5 os == "mingw" = if isJust (showE "sox") then readProcessWithExitCode (fromJust (showE "sox")) [file, "-t", "waveaudio", "-d"] "" >> return () else catchEnd ExecutableNotProperlyInstalled | otherwise = if isJust (showE "play") then readProcessWithExitCode (fromJust (showE "play")) [file] "" >> return () else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseProfB' creates with SoX a file containing a noise profile for the first 0.05 s of the audio file given. noiseProfB :: FilePath -> IO () noiseProfB file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", "0", "0.05", "noiseprof",file ++ ".b.prof"] "" if code /= ExitSuccess then do e0 <- doesFileExist $ file ++ ".b.prof" if e0 then do removeFile $ file ++ ".b.prof" catchEnd (NoiseProfileNotCreatedB file) else catchEnd (NoiseProfileNotCreatedB file) else do e1 <- doesFileExist $ file ++ ".b.prof" if e1 then return () else catchEnd (NoiseProfileNotCreatedB file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseProfE' creates with SoX a file containing a noise profile for the last 0.05 s of the audio file given. noiseProfE :: FilePath -> IO () noiseProfE file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", "-0.05", "0.05", "noiseprof",file ++ ".e.prof"] "" if code /= ExitSuccess then do e0 <- doesFileExist $ file ++ ".e.prof" if e0 then do removeFile $ file ++ ".e.prof" catchEnd (NoiseProfileNotCreatedE file) else catchEnd (NoiseProfileNotCreatedE file) else do e1 <- doesFileExist $ file ++ ".e.prof" if e1 then return () else catchEnd (NoiseProfileNotCreatedE file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseReduceB' reduces with SoX a noise in the file given with the corresponding noise profile created with 'noiseProfB' function. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). noiseReduceB :: FilePath -> IO () noiseReduceB file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "_" ++ file, "noisered", file ++ ".b.prof"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "_" ++ file if e1 then do removeFile $ "_" ++ file catchEnd (NotCreatedWithEffect "noisered") else catchEnd (NotCreatedWithEffect "noisered") else do e2 <- doesFileExist $ "_" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseReduceE' reduces with SoX a noise in the file given with the corresponding noise profile created with 'noiseProfE' function. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). noiseReduceE :: FilePath -> IO () noiseReduceE file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "_." ++ file, "noisered", file ++ ".e.prof"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "_." ++ file if e1 then do removeFile $ "_." ++ file catchEnd (NotCreatedWithEffect "noisered") else catchEnd (NotCreatedWithEffect "noisered") else do e2 <- doesFileExist $ "_." ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseReduceBU' reduces with SoX a noise in the file given with the corresponding noise profile created with 'noiseProfBU' function. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). The @Double@ parameter is a number between 0 and 1 showing the level of -- reducing the noise (the greater number means that the function will reduce more intensively may be even aggressively so that for greater -- numbers it can remove some sensitive and important sound data as a noise). Internally this parameter is passed unchanged to the \"sox\" -- so that it uses it as an amount parameter for the \"noisered\" effect. Therefore, please, (as being stated in the SoX manual) experiment -- with the amount to get suitable results. noiseReduceBU :: FilePath -> Double -> IO () noiseReduceBU file amount = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "_" ++ file, "noisered", file ++ ".b.prof", showFFloat (Just 4) amount $ show 0] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "_" ++ file if e1 then do removeFile $ "_" ++ file catchEnd (NotCreatedWithEffect "noisered") else catchEnd (NotCreatedWithEffect "noisered") else do e2 <- doesFileExist $ "_" ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'noiseReduceEU' reduces with SoX a noise in the file given with the corresponding noise profile created with 'noiseProfEU' function. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). The @Double@ parameter is a number between 0 and 1 showing the level of -- reducing the noise (the greater number means that the function will reduce more intensively may be even aggressively so that for greater -- numbers it can remove some sensitive and important sound data as a noise). Internally this parameter is passed unchanged to the \"sox\" -- so that it uses it as an amount parameter for the \"noisered\" effect. Therefore, please, (as being stated in the SoX manual) experiment -- with the amount to get suitable results. noiseReduceEU :: FilePath -> Double -> IO () noiseReduceEU file amount = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "_." ++ file, "noisered", file ++ ".e.prof", showFFloat (Just 4) amount $ show 0] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "_." ++ file if e1 then do removeFile $ "_." ++ file catchEnd (NotCreatedWithEffect "noisered") else catchEnd (NotCreatedWithEffect "noisered") else do e2 <- doesFileExist $ "_." ++ file if e2 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'volS' changes the given audio with the linear ratio for the amplitude so that the resulting amlitude is equal to the given @Double@ parameter. -- The function must be used with the @FilePath@ parameter containing no directories in its name (that means the file of the @FilePath@ parameter must be -- in the same directory where the function is called from). volS :: FilePath -> Double -> IO () volS file amplitude = if isJust (showE "sox") then do norm file e0 <- doesFileExist $ "8" ++ file if e0 then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) ["8" ++ file, "8." ++ file, "vol", showFFloat Nothing amplitude $ show 0, "amplitude"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "8." ++ file if e1 then do removeFile $ "8." ++ file removeFile $ "8" ++ file catchEnd (NotCreatedWithEffect "vol") else do removeFile $ "8" ++ file catchEnd (NotCreatedWithEffect "vol") else do e2 <- doesFileExist $ "8." ++ file if e2 then return () else do removeFile $ "8" ++ file catchEnd (InitialFileNotChanged file) else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'volS2' changes the given audio (the first @FilePath@ parameter, which must be normalized e. g. by the 'norm' function before) with -- the linear ratio for the amplitude so that the resulting amlitude is equal to the maximum by absolute value amplitude for the file given -- by the second @FilePath@ parameter. The function must be used with the first @FilePath@ parameter containing no directories in its name -- (that means the file of the first @FilePath@ parameter must be in the same directory where the function is called from). volS2 :: FilePath -> FilePath -> IO () volS2 fileA fileB = if isJust (showE "sox") then do upp <- upperBnd fileB amplMax <- selMA fileB (0, upp) True amplMin <- selMA fileB (0, upp) False let ampl = read (fst . maxAbs $ (amplMax, amplMin))::Double (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [fileA, "8." ++ tail fileA, "vol", showFFloat Nothing ampl $ show 0, "amplitude"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "8." ++ tail fileA if e1 then do removeFile $ "8." ++ tail fileA catchEnd (NotCreatedWithEffect "vol") else catchEnd (NotCreatedWithEffect "vol") else do file8e <- doesFileExist $ "8." ++ tail fileA if file8e then return () else catchEnd (InitialFileNotChanged fileA) else catchEnd ExecutableNotProperlyInstalled -- | Function 'sincA' uses a \"sinc\" effect with @-a 50 -I 0.07k-11k@ band-pass filter for the audio file given. sincA :: FilePath -> IO () sincA file = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "4." ++ file, "sinc", "-a", "50", "-I", "0.07k-11k"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "4." ++ file if e1 then do removeFile $ "4." ++ file catchEnd (NotCreatedWithEffect "sinc") else catchEnd (NotCreatedWithEffect "sinc") else do e0 <- doesFileExist $ "4." ++ file if e0 then return () else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | Function 'sampleAn' analyzes the one samle of the 1-channel sound file (or k samples for the k-channel file) and returns a tuple pair of -- the maximum and minimum amplitudes of the sound given as @String@s. For the 1-channel sound file they are the same. -- The @Integer@ parameter is the number of the sample, starting from which SoX analyzes the sound. If it is less than number of the samples available, -- then the function returns the value for the last one sample for the 1-channel file (or the last k samples for the k-channel sound file). -- The file must not be in a RAW format for the function to work properly. sampleAn :: FilePath -> Integer -> IO (String, String) sampleAn file pos = if isJust (showE "sox") && isJust (showE "soxi") then onException (do (_, hout, _) <- readProcessWithExitCode (fromJust (showE "soxi")) ["-s", file] "" let length0 = read hout::Integer f param = do (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", show param ++ "s", "1s", "stat"] "" let lns = map (last . words) . drop 3 . take 5 . lines $ herr in return (head lns, last lns) if compare length0 (fromIntegral pos) == GT then f pos else f (length0 - 1)) (catchEnd (NotEnoughData file)) else catchEnd ExecutableNotProperlyInstalled >> return ("","")