dobutokO4-0.2.0.0: DobutokO/Sound/Faded.hs
-- |
-- Module : DobutokO.Sound.Faded
-- Copyright : (c) OleksandrZhabenko 2020
-- License : MIT
-- Stability : Experimental
-- Maintainer : olexandr543@yahoo.com
--
-- Helps to create experimental music.
-- Uses SoX fade (in a special 2D way) effect and frequency modulation.
{-# OPTIONS_GHC -threaded #-}
{-# LANGUAGE CPP #-}
module DobutokO.Sound.Faded (
-- * Provide special faded effects and frequency modulation
overChangeVolGN
, overChangeVolG
, overChangeVolGC
, overChangeVolGF
, overChangeVol
, overChangeVolC
, overChangeVolF
, overChangeVolGCN
, overChangeVolGFN
, overChangeVolN
, overChangeVolCN
, overChangeVolFN
-- * Mixing function
, mixGTest
, mixGTestN
-- * Generate several files
, basicFN
, basicF
, basicFC
, basicF2
, basicF2C
, basicFCN
, basicF2N
, basicF2CN
-- * Auxiliary functions
, endingWF
, charFadeType
, argString
, freqChange
) where
#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__>=710
/* code that applies only to GHC 7.10.* and higher versions */
import GHC.Base (mconcat)
#endif
#endif
import System.Exit (ExitCode (ExitSuccess))
import Data.List (isPrefixOf,isSuffixOf)
import Data.Maybe (fromJust)
import System.Process
import EndOfExe (showE)
import MMSyn7l (fadeEndsTMB,fadeEndsTMN)
import Numeric (showFFloat)
import qualified Data.Vector as V
import System.Directory
import DobutokO.Sound.Functional.Basics
import DobutokO.Sound.IntermediateF (soxBasicParams)
import Data.Vector.DoubleZip (evalSndFV)
#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__==708
/* code that applies only to GHC 7.8.* */
mconcat = concat
#endif
#endif
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points at the time-volume scale with possibly
-- changing frequency (they are specified by the first and the second 'Double' arguments). Uses SoX inside especially the \"fade\" and \"synth\" effects.
-- For the equal frequencies generates specifically faded output without frequency modulation.
overChangeVolG :: String -> String -> Int -> Double -> Double -> Double -> Double -> ((Double,Double), (Double,Double)) -> IO ()
overChangeVolG = overChangeVolGN "test"
-- | A generalized version of the 'overChangeVolGN' with a possibility to specify the name of the resulting file (by default it is \"test\" based).
overChangeVolGN :: FilePath -> String -> String -> Int -> Double -> Double -> Double -> Double -> ((Double,Double), (Double,Double)) -> IO ()
overChangeVolGN filestart ys cs j freq1 freq2 x0 xdelta ((t0,v0), (t1,v1))
| x0 /= 0 && compare (abs x0) 1.0 /= GT && compare freq1 16 == GT && compare freq1 20000 == LT =
case compare (v1 * v0) 0 of
GT -> do
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (soxBasicParams ys ["","-n","test1.wav","synth",
showFFloat Nothing (if t1 == t0 then abs x0 else abs (t1 - t0)) "", "sine", showFFloat (Just 4) freq1 (freqChange (drop 1 cs) freq2 freq1), "fade",
charFadeType (if null cs then 'l' else head cs)] ++
if compare ((v1 - v0) * (t1 - t0)) 0 /= LT then [showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) ""]
else ["0", "-0.0", showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "vol",
showFFloat (Just 4) (signum v1 * abs (v1 - v0)) ""]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr1
else do
(code2,_,herr2) <- readProcessWithExitCode (fromJust (showE "sox")) (soxBasicParams ys ["","-n","test0.wav","synth",
showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "sine",
showFFloat (Just 4) freq1 (freqChange (drop 1 cs) freq2 freq1), "vol", showFFloat (Just 4) (min v0 v1) ""]) ""
if code2 == ExitSuccess
then do
(code3,_,herr3) <- readProcessWithExitCode (fromJust (showE "sox")) ["-m","test0" ++ endingWF ys,"test1" ++ endingWF ys, (filestart ++ "G") ++
prependZeroes 6 (show j) ++ endingWF ys, "vol", "2"] ""
if code3 == ExitSuccess
then removeFile ("test0" ++ endingWF ys) >> removeFile ("test1" ++ endingWF ys)
else error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr3
else print herr2 >> error "DobutokO.Sound.Faded.overChangeVolGN: Operation not successful. "
LT -> do
overChangeVolGN filestart ys cs j freq1 freq2 x0 xdelta ((t0,v0), ((v0 * t1 - v1 * t0) / (v0 - v1),0)) >> renameFile ((filestart ++ "G") ++
prependZeroes 6 (show j) ++ endingWF ys) ("temp0" ++ endingWF ys)
overChangeVolGN filestart ys cs j freq1 freq2 x0 xdelta (((v0 * t1 - v1 * t0) / (v0 - v1),0), (t1,v1)) >> renameFile ((filestart ++ "G") ++
prependZeroes 6 (show j) ++ endingWF ys) ("temp1" ++ endingWF ys)
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) ["temp0" ++ endingWF ys,"temp1" ++ endingWF ys, (filestart ++ "G") ++
prependZeroes 6 (show j) ++ endingWF ys] ""
if code1 == ExitSuccess then removeFile ("temp0" ++ endingWF ys) >> removeFile ("temp1" ++ endingWF ys)
else error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr1
_ ->
case v1 of
0 ->
if v0 == 0
then do
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (soxBasicParams ys ["","-n",(filestart ++ "G") ++ prependZeroes 6 (show j) ++
".wav","delay", showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "trim", showFFloat Nothing
(if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) ""]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr1
else return ()
else do
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (soxBasicParams ys ["","-n",(filestart ++ "G") ++ prependZeroes 6 (show j) ++
".wav","synth", showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "sine",
showFFloat (Just 4) freq1 (freqChange (drop 1 cs) freq2 freq1), "fade", charFadeType (if null cs then 'l' else head cs)] ++
if compare t0 t1 == GT then [showFFloat Nothing
(if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) ""] else ["0", "-0.0", showFFloat Nothing
(if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "vol", showFFloat (Just 4) v0 ""]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr1
else return ()
_ -> do
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (soxBasicParams ys ["","-n",(filestart ++ "G") ++ prependZeroes 6 (show j) ++ ".wav",
"synth", showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "", "sine",
showFFloat (Just 4) freq1 (freqChange (drop 1 cs) freq2 freq1), "fade", charFadeType (if null cs then 'l' else head cs)] ++
if compare t1 t0 == GT
then [showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0
else abs (t1 - t0)) ""] else ["0", "-0.0", showFFloat Nothing (if compare (abs (t1 - t0)) xdelta /= GT then abs x0 else abs (t1 - t0)) "",
"vol", showFFloat (Just 4) v1 ""]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Faded.overChangeVolGN: " ++ herr1
else return ()
| otherwise = error "DobutokO.Sound.Faded.overChangeVolGN: sound for these conditions is not defined. "
freqChange :: String -> Double -> Double -> String
freqChange xs freq freq1
| compare freq 16 /= LT && compare freq 20000 /= GT = if freq /= freq1 then
case xs of
"l" -> ':':showFFloat (Just 4) freq ""
"s" -> '+':showFFloat (Just 4) freq ""
"e" -> '/':showFFloat (Just 4) freq ""
_ -> '-':showFFloat (Just 4) freq ""
else ""
| otherwise = error "DobutokO.Sound.Faded.freqChange: undefined for this value of the frequency (the first Double argument). "
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points at the time-volume scale. Uses SoX inside
-- especially the \"fade\" and \"synth\" effects. A frequency does not change and is specified (in Hz) by the first 'Double' argument.
overChangeVol :: String -> Char -> Int -> Double -> Double -> Double -> ((Double,Double), (Double,Double)) -> IO ()
overChangeVol ys c j freq1 = overChangeVolGN "test" ys [c] j freq1 freq1
-- | A generalized version of the 'overChangeVol' with a possibility to specify the name for the mixed files (by default is \"test\" based).
overChangeVolN :: FilePath -> String -> Char -> Int -> Double -> Double -> Double -> ((Double,Double), (Double,Double)) -> IO ()
overChangeVolN filestart ys c j freq1 = overChangeVolGN filestart ys [c] j freq1 freq1
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points in the time-volume scale. Uses SoX inside especially
-- the \"fade\" and \"synth\" effects. A frequency does not change and is specified (in Hz) by the first 'Double' argument. Is a curried variant of the
-- 'overChangeVol' in its two last arguments.
overChangeVolC :: String -> Char -> Int -> Double -> Double -> Double -> (Double,Double) -> (Double,Double) -> IO ()
overChangeVolC ys c j freq x0 xdelta w1 = overChangeVol ys c j freq x0 xdelta . (,) w1
-- | A generalized version of the 'overChangeVolC' with a possibility to specify the name for the mixed files (by default is \"test\" based).
overChangeVolCN :: FilePath -> String -> Char -> Int -> Double -> Double -> Double -> (Double,Double) -> (Double,Double) -> IO ()
overChangeVolCN filestart ys c j freq x0 xdelta w1 = overChangeVolN filestart ys c j freq x0 xdelta . (,) w1
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points in the time-volume scale with possibly changing frequency (they are specified by
-- the first and the second 'Double' arguments). Uses SoX inside especially the \"fade\" and \"synth\" effects. For the equal frequencies generates specifically
-- faded output without frequency modulation. Is a curried variant of the 'overChangeVolG' in its two last arguments.
overChangeVolGC :: String -> String -> Int -> Double -> Double -> Double -> Double -> (Double,Double) -> (Double,Double) -> IO ()
overChangeVolGC ys cs j freq1 freq2 x0 xdelta w1 = overChangeVolG ys cs j freq1 freq2 x0 xdelta . (,) w1
-- | A generalized version of the 'overChangeVolGC' with a possibility to specify the name for the mixed files (by default is \"test\" based).
overChangeVolGCN :: FilePath -> String -> String -> Int -> Double -> Double -> Double -> Double -> (Double,Double) -> (Double,Double) -> IO ()
overChangeVolGCN filestart ys cs j freq1 freq2 x0 xdelta w1 = overChangeVolGN filestart ys cs j freq1 freq2 x0 xdelta . (,) w1
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points in the time-volume scale. Uses SoX inside especially
-- the \"fade\" and \"synth\" effects. Is a somewhat flipped variant of the 'overChangeVol' with changed order of the arguments (is provided here
-- for convenience).
overChangeVolF :: String -> Char -> Int -> Double -> Double -> (Double,Double) -> (Double,Double) -> Double -> IO ()
overChangeVolF ys c j x0 xdelta w1 w2 freq = overChangeVol ys c j freq x0 xdelta (w1,w2)
-- | A generalized version of the 'overChangeVolF' with a possibility to specify the name for the mixed files (by default is \"test\" based).
overChangeVolFN :: FilePath -> String -> Char -> Int -> Double -> Double -> (Double,Double) -> (Double,Double) -> Double -> IO ()
overChangeVolFN filestart ys c j x0 xdelta w1 w2 freq = overChangeVolN filestart ys c j freq x0 xdelta (w1,w2)
-- | Generates a sound, the volume of which (being plotted graphically) comes through the given 2D points in the time-volume scale with possibly
-- changing frequency (they are specified by the first and the second 'Double' arguments). Uses SoX inside especially the \"fade\" and \"synth\" effects.
-- For the equal frequencies generates specifically faded output without frequency modulation. Is a somewhat flipped variant of the 'overChangeVolGC'
-- with changed order of the arguments (is provided here for convenience).
overChangeVolGF :: String -> String -> Int -> Double -> Double -> (Double,Double) -> (Double,Double) -> Double -> Double -> IO ()
overChangeVolGF ys cs j x0 xdelta w1 w2 freq1 freq2 = overChangeVolG ys cs j freq1 freq2 x0 xdelta (w1,w2)
-- | A generalized version of the 'overChangeVolGF' with a possibility to specify the name for the mixed files (by default is \"test\" based).
overChangeVolGFN :: FilePath -> String -> String -> Int -> Double -> Double -> (Double,Double) -> (Double,Double) -> Double -> Double -> IO ()
overChangeVolGFN filestart ys cs j x0 xdelta w1 w2 freq1 freq2 = overChangeVolGN filestart ys cs j freq1 freq2 x0 xdelta (w1,w2)
-- | A simplified variant of the 'soxBasicParameters' function with defining only a file extension.
endingWF :: String -> String
endingWF ys
| not (null ys) = if last ys == 'f' then ".flac" else ".wav"
| otherwise = ".wav"
-- | Converts a character into a corresponding string using \"l\" (a logarithmic one) as the default one. An output can specify then the fade type for SoX.
charFadeType :: Char -> String
charFadeType c =
case c of
'h' -> "h"
'p' -> "p"
't' -> "t"
_ -> "l"
-- | Using SoX mixes all the \"testG*\" (of the WAV or FLAC extension specified by the 'String' argument -- see 'endingWF') in the current directory.
-- If there are \"resultG.*" (wav or flac respectively) file in the directory, it is overwritten. Also the "testG*" files are deleted afterwards if the
-- mixing is successful.
mixGTest :: String -> IO ()
mixGTest ys = do
dir <- listDirectory "."
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (mconcat [["-m"], filter (\xs -> "testG" `isPrefixOf` xs &&
endingWF ys `isSuffixOf` xs) dir, ["resultG" ++ endingWF ys]]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Times.mixGTest: " ++ herr1
else mapM_ removeFile . filter (\xs -> "testG" `isPrefixOf` xs && endingWF ys `isSuffixOf` xs) $ dir
-- | A generalized version of the 'mixGTest' with a possibility to specify the name for the mixed files (by default is \"test\" based).
mixGTestN :: FilePath -> String -> IO ()
mixGTestN filestart ys = do
dir <- listDirectory "."
(code1,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (mconcat [["-m"], filter (\xs -> (filestart ++ "G") `isPrefixOf` xs &&
endingWF ys `isSuffixOf` xs) dir, ["resultG" ++ endingWF ys]]) ""
if code1 /= ExitSuccess then error $ "DobutokO.Sound.Times.mixGTestN: " ++ herr1
else mapM_ removeFile . filter (\xs -> (filestart ++ "G") `isPrefixOf` xs && endingWF ys `isSuffixOf` xs) $ dir
-- | Generates a sequence of sounds using 'overChangeVol' so that their time-volume characteristic is going through the 2D points obtained
-- with the last two arguments.
-- Uses 'fadeEndsTMB', the arguments for which are specified by the second symbol in the second 'String' and by the third 'Double' argument.
basicF :: String -> String -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicF ys x2s freq x0 xdelta per f v = do
let (xs1,xs2) = splitAt 1 x2s
c1 = if null xs1 then 'l' else head xs1
c2 = if null xs2 then 'l' else head xs2
v1 <- evalSndFV f v
V.imapM_ (\i x -> do
overChangeVol ys c1 i freq x0 xdelta x
fadeEndsTMB c2 per $ "testG" ++ prependZeroes 6 (show i) ++ endingWF ys) v1
-- | A generalized version of the 'basicF' with a possibility to specify the name for the generated files (by default is \"test\" based).
basicFN :: FilePath -> String -> String -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicFN filestart ys x2s freq x0 xdelta per f v = do
let (xs1,xs2) = splitAt 1 x2s
c1 = if null xs1 then 'l' else head xs1
c2 = if null xs2 then 'l' else head xs2
v1 <- evalSndFV f v
V.imapM_ (\i x -> do
overChangeVolGN filestart ys [c1] i freq freq x0 xdelta x
fadeEndsTMB c2 per $ (filestart ++ "G") ++ prependZeroes 6 (show i) ++ endingWF ys) v1
-- | Splits its argument (the first six symbols if present) (like 'splitAt') into two 'String' with the length of (if possible) 4 and 2 characters.
-- The rest of the argument is not used.
argString :: String -> (String,String)
argString xs = (take 4 xs,take 2 . drop 4 $ xs)
-- | Generates a sequence of sounds using 'overChangeVol' so that their time-volume characteristic (if being plotted graphically) is going through
-- the 2D points obtained with the last two arguments.
-- The 'String' should consist of 6 alphanumeric characters. The first four as for the 'soxBasicParams', and the fifth one -- a letter from the \"hlpqt\". The
-- sixth one is one of the \"els\" or some other symbol.
-- Otherwise, the default values are used (\"221w\" for the first and \"ll\" for the second one).
basicFC :: String -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicFC = basicFCN "test"
-- | A generalized version of the 'basicFC' with a possibility to specify the name for the mixed files (by default is \"test\" based).
basicFCN :: FilePath -> String -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicFCN filestart xs freq x0 xdelta per f v = let (ys,x2s) = argString xs in basicFN filestart ys x2s freq x0 xdelta per f v
-- | Generates a sequence of sounds using 'overChangeVol' so that their time-volume characteristic (if being plotted graphically) is going through the 2D points obtained
-- with the last two arguments.
-- Uses 'fadeEndsTMN', the arguments for which are specified by the second symbol in the second 'String' and by the third and fourth 'Double' arguments.
basicF2 :: String -> String -> Double -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicF2 = basicF2N "test"
-- | A generalized version of the 'basicF2' with a possibility to specify the name for the mixed files (by default is \"test\" based).
basicF2N :: FilePath -> String -> String -> Double -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicF2N filestart ys x2s freq x0 xdelta per1 per2 f v = do
let (xs1,xs2) = splitAt 1 x2s
c1 = if null xs1 then 'l' else head xs1
c2 = if null xs2 then 'l' else head xs2
v1 <- evalSndFV f v
V.imapM_ (\i x -> do
overChangeVolN filestart ys c1 i freq x0 xdelta x
fadeEndsTMN c2 per1 per2 $ (filestart ++ "G") ++ prependZeroes 6 (show i) ++ endingWF ys) v1
-- | Generates a sequence of sounds using 'overChangeVol' so that their time-volume characteristic (if being plotted graphically) is going through
-- the 2D points obtained with the last two arguments.
-- The 'String' should consist of 6 alphanumeric characters. The first four as for the 'soxBasicParams' and the the fifth one -- a letter
-- from the \"hlpqt\". The sixth one is one of the \"els\" or some other symbol. Otherwise, the default values are used (\"221w\" for the first
-- and \"ll\" for the second one).
basicF2C :: String -> Double -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicF2C = basicF2CN "test"
-- | A generalized version of the 'basicF2C' with a possibility to specify the name for the mixed files (by default is \"test\" based).
basicF2CN :: FilePath -> String -> Double -> Double -> Double -> Double -> Double -> (Double -> Double) -> V.Vector Double -> IO ()
basicF2CN filestart xs freq x0 xdelta per1 per2 f v = let (ys,x2s) = argString xs in basicF2N filestart ys x2s freq x0 xdelta per1 per2 f v