-- | -- Module : SoXBasics1 -- Copyright : (c) OleksandrZhabenko 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). -- This module differs from a SoXBasics that the resulting files -- in it have possibly just the same name as the input ones. The functions -- try to replace the initial file with the processed one. -- module SoXBasics1 ( -- * Produce sound -- ** Amplitude modification norm , normL , gainL , quarterSinFade -- ** Adding silence , silenceBoth -- ** Changing sample rate , resampleA -- ** Working with noise , noiseReduceB , noiseReduceE , noiseReduceBU , noiseReduceEU -- ** Filtering , sincA -- ** Volume amplification , volS , volS2 ) where import System.Directory import Data.Maybe (isJust, fromJust) import Numeric import System.Process import EndOfExe import System.Exit import qualified SoXBasics as SB (extremeS1,upperBnd,selMA,maxAbs,norm) import Control.Exception.FinalException -- | 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("8" ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("9" ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("9" ++ file) file else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled -- | 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. quarterSinFade :: FilePath -> IO () quarterSinFade file = if isJust (showE "sox") then do pos <- SB.extremeS1 file upp <- SB.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 do removeFile file renameFile ("4" ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. silenceBoth :: FilePath -> Int -> Int -> IO () silenceBoth file beginning end = if isJust (showE "sox") then do (code, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "3" ++ file, "delay", show beginning ++ "s", "reverse"] "" if code /= ExitSuccess then do e1 <- doesFileExist $ "3" ++ file if e1 then do removeFile $ "3" ++ file catchEnd (NotCreatedWithEffects "delay reverse") else catchEnd (NotCreatedWithEffects "delay reverse") else do e2 <- doesFileExist $ "3" ++ file if e2 then do (code1, _, _) <- readProcessWithExitCode (fromJust (showE "sox")) ["3" ++ file, "2" ++ file, "delay", show end ++ "s", "reverse"] "" if code1 /= ExitSuccess then do e2 <- doesFileExist $ "2" ++ file if e2 then do removeFile $ "3" ++ file removeFile $ "2" ++ file catchEnd (NotCreated file) else do removeFile $ "3" ++ file catchEnd (NotCreated file) else do e3 <- doesFileExist $ "2" ++ file if e3 then do removeFile $ "3" ++ file removeFile file renameFile ("2" ++ file) file else do removeFile $ "3" ++ file catchEnd (NotCreated file) else catchEnd (InitialFileNotChanged file) else 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("3" ++ file) file else catchEnd (InitialFileNotChanged 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("_" ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("_." ++ file) file 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. While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("_" ++ file) file 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 '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). 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. While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("_." ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. volS :: FilePath -> Double -> IO () volS file amplitude = if isJust (showE "sox") then do SB.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 do removeFile file removeFile $ "8" ++ file renameFile ("8." ++ file) file 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). While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. volS2 :: FilePath -> FilePath -> IO () volS2 fileA fileB = if isJust (showE "sox") then do upp <- SB.upperBnd fileB amplMax <- SB.selMA fileB (0, upp) True amplMin <- SB.selMA fileB (0, upp) False let ampl = read (fst . SB.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 do removeFile fileA renameFile ("8." ++ tail fileA) fileA 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. While being -- executed the function tries to replace the initial file with the resulting processed one and to clean the temporary files. If it is not -- successful the function exits with exception of the type 'FinalException' and leaves the initial file without modification. 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 do removeFile file renameFile ("4." ++ file) file else catchEnd (InitialFileNotChanged file) else catchEnd ExecutableNotProperlyInstalled