-- |
-- Module      :  DobutokO.Sound.Executable
-- Copyright   :  (c) OleksandrZhabenko 2020
-- License     :  MIT
-- Stability   :  Experimental
-- Maintainer  :  olexandr543@yahoo.com
--
-- A program and a library to create experimental music
-- from a mono audio and a Ukrainian text.

{-# LANGUAGE LambdaCase #-}
{-# OPTIONS_GHC -threaded #-}

module DobutokO.Sound.Executable (
  -- * Basic functions for the executable
  dobutokO2
  , recAndProcess
  , dobutokO2H7
  , dobutokO2H9
  -- * Library functions
  , testSoundGen2G
  , soundGen3G
  , soundGen3G_O
  , soundGen3G_O2
  , h1
  , h2
) where

import Control.Monad (void)
import System.Exit (ExitCode (ExitSuccess))
import qualified Data.List as L (groupBy,sort)
import CaseBi (getBFst')
import Numeric (showFFloat)
import Control.Exception (onException)
import System.Environment (getArgs)
import Data.List (isPrefixOf)
import Data.Maybe (fromJust,isNothing,fromMaybe)
import Data.Char (isDigit,isSpace)
import System.Process
import EndOfExe (showE)
import qualified Data.Vector as V (Vector (..),generate,fromList,length,imapM_,snoc,toList,unsafeSlice,mapM_,imap,unsafeIndex,map,filter,singleton)
import System.Directory
import SoXBasics
import Processing_mmsyn7ukr
import DobutokO.Sound hiding (dobutokO2, recAndProcess)
import DobutokO.Sound.IntermediateF 
import DobutokO.Sound.ParseList (parseStoLInts)
import DobutokO.Sound.Functional
import DobutokO.Sound.Keyboard

-- | Function that actually makes processing in the @dobutokO2@ executable. Please, check before executing
-- whether there is no \"x.wav\", \"test*\", \"result*\" and \"end*\" files in the current directory, because they can be overwritten.
dobutokO2 :: IO ()
dobutokO2 = do
  arggs <- getArgs
  let arg1 = concat . take 1 $ arggs
      file = concat . drop 1 . take 2 $ arggs
      args = unwords . drop 2 $ arggs
      argss = drop 1 arggs
  exist2 <- doesFileExist file
  getBFst' (dobutokO2H exist2 args file, V.fromList . fmap (\(xs, f) -> (xs,f exist2 args file)) $ [("0",o2help),("00",dobutokO2H00),("1",dobutokO2H1),
    ("11",dobutokO2H11),("2",dobutokO2H2),("21",dobutokO2H21),("3",dobutokO2H3),("31",dobutokO2H31),("4",dobutokO2H4),("41",dobutokO2H41),
      ("5",dobutokO2H5),("51",dobutokO2H51),("61",dobutokO2H61),("7",dobutokO2H7),("8",dobutokO2H8),("80",dobutokO2H80),("82",dobutokO2H82),
        ("820",dobutokO2H820),("9",dobutokO2H9),("92",dobutokO2H92),("99",dobutokO2H99 argss),("992",dobutokO2H992G argss),("999",dobutokO2H999 argss),
          ("9992",dobutokO2H9992G argss)]) arg1

dobutokO2H1 :: Bool -> String -> FilePath -> IO ()
dobutokO2H1 exist2 args file = do
  [_,_,octave,ampLS,time2] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5] else [1..5])
  let (octave1,ampL,time3) = fromJust . threeStr2Val $ [octave,ampLS,time2] in overSoXSynthNGen file octave1 ampL time3 args
{-# INLINE dobutokO2H1 #-}

threeStr2Val :: [String] -> Maybe (Int,Double,Double)
threeStr2Val [xs,ys,zs] = Just (read xs::Int,read ys::Double,read zs::Double)
threeStr2Val _ = Nothing
{-# INLINE threeStr2Val #-}

fourStr2Val :: [String] -> Maybe (Int,Double,Double,Double)
fourStr2Val [xs,ys,zs,tws] = Just (read xs::Int,read ys::Double,read zs::Double,read tws::Double)
fourStr2Val _ = Nothing
{-# INLINE fourStr2Val #-}

fiveStr2Val :: [String] -> Maybe ([Int],Double,Double,Double)
fiveStr2Val [xs,ys,zs,tws] = Just (map (\z -> read z::Int) . words $ xs,read ys::Double,read zs::Double,read tws::Double)
fiveStr2Val _ = Nothing
{-# INLINE fiveStr2Val #-}

dobutokO2H2 :: Bool -> String -> FilePath -> IO ()
dobutokO2H2 exist2 args file = do
  [_,_,octave,ampLS,time2,wws] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5,6] else [1..6])
  let (octave1,ampL,time3) = fromJust . threeStr2Val $ [octave,ampLS,time2] in uniqOverSoXSynthNGen file octave1 ampL time3 args wws
{-# INLINE dobutokO2H2 #-}

dobutokO2H3 :: Bool -> String -> FilePath -> IO ()
dobutokO2H3 exist2 args file = do
  [_,_,octave,ampLS,time2,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5,7] else [1,2,3,4,5,7])
  let (octave1,ampL,time3) = fromJust . threeStr2Val $ [octave,ampLS,time2] in overSoXSynthNGen2 file octave1 ampL time3 args tts
{-# INLINE dobutokO2H3 #-}

dobutokO2H4 :: Bool -> String -> FilePath -> IO ()
dobutokO2H4 exist2 args file = do
  [_,_,octave,ampLS,time2,wws,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5,6,7] else [1..7])
  let (octave1,ampL,time3) = fromJust . threeStr2Val $ [octave,ampLS,time2] in uniqOverSoXSynthNGen3 file octave1 ampL time3 args wws tts
{-# INLINE dobutokO2H4 #-}

dobutokO2H5 :: Bool -> String -> FilePath -> IO ()
dobutokO2H5 exist2 args file = do
  [_,_,octave,ampLS,time2,tts,dAmpl0,vs] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5,7,8,9] else [1,2,3,4,5,7,8,9])
  let (octave1,ampL,time3,dAmpl) = fromJust . fourStr2Val $ [octave,ampLS,time2,dAmpl0] in overSoXSynthNGen3 file octave1 ampL time3 dAmpl args tts vs
{-# INLINE dobutokO2H5 #-}

dobutokO2H :: Bool -> String -> FilePath -> IO ()
dobutokO2H exist2 args file = do
  [_,_,octave,ampLS,time2,wws,tts,dAmpl0,vs] <- mapM (recAndProcess file) (if exist2 then [0,2,3,4,5,6,7,8,9] else [1..9])
  let (octave1,ampL,time3,dAmpl) = fromJust . fourStr2Val $ [octave,ampLS,time2,dAmpl0] in
    uniqOverSoXSynthNGen4 file octave1 ampL time3 dAmpl args wws tts vs
{-# INLINE dobutokO2H #-}

dobutokO2H61 :: Bool -> String -> FilePath -> IO ()
dobutokO2H61 exist2 args file = do
  [_,_,complexNky,ampLS,time2,wws,tts,dAmpl0,vs] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,6,7,8,9] else [1,2,11,4,5,6,7,8,9])
  let ([enkA,nTh],ampL,time3,dAmpl) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,dAmpl0] in
    uniqOverSoXSynthNGen4E file nTh enkA ampL time3 dAmpl args wws tts vs
{-# INLINE dobutokO2H61 #-}

dobutokO2H8 :: Bool -> String -> FilePath -> IO ()
dobutokO2H8 exist2 args file = void (dobutokO2H8G exist2 args file)
{-# INLINE dobutokO2H8 #-}

dobutokO2H80 :: Bool -> String -> FilePath -> IO ()
dobutokO2H80 exist2 args file = dobutokO2H8G exist2 args file  >>= \case
    (ExitSuccess, path8v) -> V.mapM_ removeFile path8v
    _           -> return ()
{-# INLINE dobutokO2H80 #-}

dobutokO2H8G :: Bool -> String -> FilePath -> IO (ExitCode, V.Vector FilePath)
dobutokO2H8G _ _ _ = do
  path8s0 <- listDirectory "."
  let path8v = V.fromList . L.sort . filter (isPrefixOf "result") $ path8s0
      path8v1 = V.generate (V.length path8v `quot` 800) (\i0 -> V.unsafeSlice (i0 * 800) 800 path8v ) `V.snoc` V.unsafeSlice (800 *
         (V.length path8v `quot` 800)) (V.length path8v `rem` 800) path8v 
  V.imapM_ dO2H8 path8v1
  epath0s <- listDirectory "."
  let epaths = L.sort . filter (isPrefixOf "end0") $ epath0s
  (code1,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (epaths ++ ["end.wav"]) ""
  case code1 of
    ExitSuccess -> mapM_ removeFile epaths
    _           -> do
      exi1 <- doesFileExist "end.wav"
      if exi1
        then do
          removeFile "end.wav"
          error "The end file \"end.wav\" was not created. "
        else error "The end file \"end.wav\" was not created. "
  return (code1, path8v)
{-# INLINE dobutokO2H8G #-}

dobutokO2H82G :: Bool -> String -> FilePath -> IO (ExitCode, V.Vector FilePath)
dobutokO2H82G _ ys _ = do
  path8s0 <- listDirectory "."
  let path8v = V.fromList . L.sort . filter (isPrefixOf "result") $ path8s0
      path8v1 = V.generate (V.length path8v `quot` 800) (\i0 -> V.unsafeSlice (i0 * 800) 800 path8v ) `V.snoc` V.unsafeSlice (800 *
         (V.length path8v `quot` 800)) (V.length path8v `rem` 800) path8v 
  if drop 3 ys == "f" then V.imapM_ dO2H8f path8v1 else V.imapM_ dO2H8 path8v1
  epath0s <- listDirectory "."
  let epaths = L.sort . filter (isPrefixOf "end0") $ epath0s
  (code1,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (epaths ++ soxBasicParams ys ("":["end.wav"])) ""
  case code1 of
    ExitSuccess -> mapM_ removeFile epaths
    _           -> do
      exiW <- doesFileExist "end.wav"
      exiF <- doesFileExist "end.flac"
      if exiW && not (exiF)
        then do
          removeFile "end.wav"
          error "The end file \"end.wav\" was not created. "
        else
          if exiF && not (exiW) then do
            removeFile "end.flac"
            error "The end file \"end.flac\" was not created. "
          else if exiF && exiW then do
                 removeFile "end.flac"
                 removeFile "end.wav"
                 error "The end file \"end.*\" was not created. "
               else error "The end file \"end.*\" was not created. "
  return (code1, path8v)
{-# INLINE dobutokO2H82G #-}

dobutokO2H82 :: Bool -> String -> FilePath -> IO ()
dobutokO2H82 exist2 ys file = void (dobutokO2H82G exist2 ys file)
{-# INLINE dobutokO2H82 #-}

dobutokO2H820 :: Bool -> String -> FilePath -> IO ()
dobutokO2H820 exist2 ys file = dobutokO2H82G exist2 ys file  >>= \case
    (ExitSuccess, path8v) -> V.mapM_ removeFile path8v
    _           -> return ()
{-# INLINE dobutokO2H820 #-}

dobutokO2H11 :: Bool -> String -> FilePath -> IO ()
dobutokO2H11 exist2 args file = do
  [_,_,complexNky,ampLS,time2] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5] else [1,2,11,4,5])
  let ([enkA,nTh],ampL,time3,_) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,""] in overSoXSynthNGenE file nTh enkA ampL time3 args
{-# INLINE dobutokO2H11 #-}

dobutokO2H21 :: Bool -> String -> FilePath -> IO ()
dobutokO2H21 exist2 args file = do
  [_,_,complexNky,ampLS,time2,wws] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,6] else [1,2,11,4,5,6])
  let ([enkA,nTh],ampL,time3,_) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,""] in uniqOverSoXSynthNGenE file nTh enkA ampL time3 args wws
{-# INLINE dobutokO2H21 #-}

dobutokO2H31 :: Bool -> String -> FilePath -> IO ()
dobutokO2H31 exist2 args file = do
  [_,_,complexNky,ampLS,time2,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,7] else [1,2,11,4,5,7])
  let ([enkA,nTh],ampL,time3,_) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,""] in overSoXSynthNGen2E file nTh enkA ampL time3 args tts
{-# INLINE dobutokO2H31 #-}

dobutokO2H41 :: Bool -> String -> FilePath -> IO ()
dobutokO2H41 exist2 args file = do
  [_,_,complexNky,ampLS,time2,wws,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,6,7] else [1,2,11,4,5,6,7])
  let ([enkA,nTh],ampL,time3,_) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,""] in uniqOverSoXSynthNGen3E file nTh enkA ampL time3 args wws tts
{-# INLINE dobutokO2H41 #-}

dobutokO2H51 :: Bool -> String -> FilePath -> IO ()
dobutokO2H51 exist2 args file = do
  [_,_,complexNky,ampLS,time2,tts,dAmpl0,vs] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,7,8,9] else [1,2,11,4,5,7,8,9])
  let ([enkA,nTh],ampL,time3,dAmpl) = fromJust . fiveStr2Val $ [complexNky,ampLS,time2,dAmpl0] in
    overSoXSynthNGen3E file nTh enkA ampL time3 dAmpl args tts vs
{-# INLINE dobutokO2H51 #-}

dO2H8 :: Int -> V.Vector String -> IO ()
dO2H8 i v = do
  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (V.toList v ++ ["end0" ++ show i ++ ".wav"]) ""
  case code of
    ExitSuccess -> putStr ""
    _           -> do
      exi0 <- doesFileExist $ "end0" ++ show i ++ ".wav"
      if exi0
        then do
          removeFile $ "end0" ++ show i ++ ".wav"
          error $ "The intermediate file " ++ "\"end0" ++ show i ++ ".wav\" was not created. "
        else error $ "The intermediate file " ++ "\"end0" ++ show i ++ ".wav\" was not created. "
{-# INLINE dO2H8 #-}

dO2H8f :: Int -> V.Vector String -> IO ()
dO2H8f i v = do
  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (V.toList v ++ ["end0" ++ show i ++ ".flac"]) ""
  case code of
    ExitSuccess -> putStr ""
    _           -> do
      exi0 <- doesFileExist $ "end0" ++ show i ++ ".flac"
      if exi0
        then do
          removeFile $ "end0" ++ show i ++ ".flac"
          error $ "The intermediate file " ++ "\"end0" ++ show i ++ ".flac\" was not created. "
        else error $ "The intermediate file " ++ "\"end0" ++ show i ++ ".flac\" was not created. "
{-# INLINE dO2H8f #-}

-- | Actually works as 'pAnR_' function.
dobutokO2H9 :: Bool -> String -> FilePath -> IO ()
dobutokO2H9 _ _ _ = pAnR_
{-# INLINE dobutokO2H9 #-}

-- | Actually works as 'pAnR_2G' function.
dobutokO2H92 :: Bool -> String -> FilePath -> IO ()
dobutokO2H92 _ ys _ = pAnR_2G ys
{-# INLINE dobutokO2H92 #-}

dobutokO2H99 :: [String] -> Bool -> String -> FilePath -> IO ()
dobutokO2H99 argss _ _ file = do
  (v1,dir0V) <- dO2H99 "221w" file 
  V.mapM_ (\idx -> playE (V.unsafeIndex dir0V idx) argss) v1
{-# INLINE dobutokO2H99 #-}

dO2H99 :: String -> FilePath -> IO (V.Vector Int,V.Vector FilePath)
dO2H99 ys file = do
  dir0V <- listVDirectory2G ys
  let l0 = V.length dir0V
  putStrLn $ "You have available " ++ show l0 ++ " files that can be played. The minimum index further is 0, the maximum is " ++ show (l0 - 1)
  list1 <- recAndProcess file (99::Int)
  let yss = divideToStr l0 list1
      v01 = V.fromList yss
      mxE = fromMaybe (l0 - 1) (maxLinV v01)
      mnE = fromMaybe 0 (minLinV v01)
      zss = map (filterToBnds mnE mxE) yss
      v1 = doubleLtoV zss
  return (v1,dir0V)
{-# INLINE dO2H99 #-}

dobutokO2H992G :: [String] -> Bool -> String -> FilePath -> IO ()
dobutokO2H992G argss _ ys file = do
  (v1,dir0V) <- dO2H99 ys file 
  V.mapM_ (\idx -> playE (V.unsafeIndex dir0V idx) argss) v1
{-# INLINE dobutokO2H992G #-}

dobutokO2H999 :: [String] -> Bool -> String -> FilePath -> IO ()
dobutokO2H999 argss _ _ file = do
  (v1,dir0V) <- dO2H99 "221w" file 
  V.mapM_ (\idx -> soxE1 (V.unsafeIndex dir0V idx) argss) v1
{-# INLINE dobutokO2H999 #-}

dobutokO2H9992G :: [String] -> Bool -> String -> FilePath -> IO ()
dobutokO2H9992G argss _ ys file = do
  (v1,dir0V) <- dO2H99 ys file 
  V.mapM_ (\idx -> soxE1 (V.unsafeIndex dir0V idx) argss) v1
{-# INLINE dobutokO2H9992G #-}

divideToStr :: Int -> String -> [[Int]]
divideToStr n = map (parseStoLInts n). lines

isDataStr :: String -> Bool
isDataStr = notElem '@'

isTextPair :: String -> String -> Bool
isTextPair xs ys = isDataStr xs && isDataStr ys

-- | Used to obtain one multiline specially formatted textual input and do the full processment for the sound. 
-- The function generates overtones using additional 'String' and allows maximum control over the parameters.
-- Besides, all the needed information it obtains from the singular formatted input, which can be ended
-- with a keyboard keys combination that means an end of input (e. g. for Unices, that is probably Ctrl + D).
-- \'@\' are separators for the input parts for their respective parts. For more information about the
-- format of the single input, see:
--
-- 'https://drive.google.com/open?id=10Z_GRZR4TKoL5KXfqPm-t-4humuHN0O4'
--
-- The file is also provided with the package as text.dat.txt. 
-- The last two or three inputs (an input just here means a textual input between two \'@\') can be omitted,
-- the program will work also but with less control for the user possible. 
-- 
dobutokO2H7 :: Bool -> String -> FilePath -> IO ()
dobutokO2H7 True args file = do
  putStrLn "Please, specify a prepared textual input. To end the input press a keyboard keys combination that means an end of the input (e. g. for Unices, possibly Ctrl + D). "
  input <- getContents
  let text0   = lines input
      listTxt = filter isDataStr . map (unwords . words . unlines) . L.groupBy isTextPair $ text0
      l       = length listTxt
  case l of
    4 -> onException (do
      let [octave0,ampLS0,time20,wws] = listTxt
          octave1 = read (d3H octave0)::Int
          ampL = read (d4H ampLS0)::Double
          time3 = read (d5H time20)::Double
      uniqOverSoXSynthNGen file octave1 ampL time3 args wws) (do
        putStrLn "--------------------------------------------------------------------------------------------------------------------"
        putStrLn ""
        putStrLn "The operation was not successful because of the not valid textual input. Please, specify a valid textual input. "
        dobutokO2H7 True args file)
    5 -> onException (do
      let [octave0,ampLS0,time20,wws,tts0] = listTxt
          octave1 = read (d3H octave0)::Int
          ampL = read (d4H ampLS0)::Double
          time3 = read (d5H time20)::Double
      uniqOverSoXSynthNGen3 file octave1 ampL time3 args wws (d7H tts0)) (do
        putStrLn "--------------------------------------------------------------------------------------------------------------------"
        putStrLn ""
        putStrLn "The operation was not successful because of the not valid textual input. Please, specify a valid textual input. "
        dobutokO2H7 True args file)
    7 -> onException (do
      let [octave0,ampLS0,time20,wws,tts0,dAmpl0,vs0] = listTxt
          octave1 = read (d3H octave0)::Int
          ampL = read (d4H ampLS0)::Double
          time3 = read (d5H time20)::Double
          dAmpl = read (d8H dAmpl0)::Double
      uniqOverSoXSynthNGen4 file octave1 ampL time3 dAmpl args wws (d7H tts0) (d9H vs0)) (do
        putStrLn "--------------------------------------------------------------------------------------------------------------------"
        putStrLn ""
        putStrLn "The operation was not successful because of the not valid textual input. Please, specify a valid textual input. "
        dobutokO2H7 True args file)
    _ -> do
        putStrLn "--------------------------------------------------------------------------------------------------------------------"
        putStrLn ""
        putStrLn "The operation was not successful because of the not valid textual input. Please, specify a valid textual input. "
        dobutokO2H7 True args file
dobutokO2H7 _ args file = onException (do
  _ <- processD1
  _ <- processD2 file
  dobutokO2H7 True args file) (do
    putStrLn "--------------------------------------------------------------------------------------------------------------------"
    putStrLn ""
    putStr "The operation was not successful because the file with such a name does not exist or was not created by a program. "
    putStrLn "Please, interrupt a program and start again with a better data. "
    dobutokO2H7 False args file)
{-# INLINE dobutokO2H7 #-}

-- | Takes textual input from the stdin and prints it as one 'String' to the stdout.
o2help :: Bool -> String -> FilePath -> IO ()
o2help _ _ _ = do
  xs <- getContents
  let ys = unwords . lines $ xs in do
    putStrLn ""
    putStrLn "-------------------------------------------------------------------------------------------------------------"
    putStrLn ys

dobutokO2H00 :: Bool -> String -> FilePath -> IO ()
dobutokO2H00 exist2 args beginningSymbols = fadeAllE args beginningSymbols
{-# INLINE dobutokO2H00 #-}

----------------------------------------------------------------------------------------------------------------------------------------------------

-- | Function records and processes the sound data needed to generate the \"end.wav\" file in the 'dobutokO2' function. Please, check before executing
-- whether there is no \"x.wav\" file in the current directory, because it can be overwritten.
recAndProcess :: FilePath -> Int -> IO String
recAndProcess file =
  getBFst' (processD, V.fromList [(0,processD0 file),(1,processD1),(2,processD2 file),(3,processD3),(4,processD4),(5,processD5),(7,processD7),
    (8,processD8),(9,processD9),(11,processD_1),(99,processD99),(999,processD99)])

processD_1 :: IO String
processD_1 = onException (do
  putStr "Please, specify two \'Int\' numbers (with intermediate space character between them): the first one is a number of different notes there will be "
  putStr "in the result, and the second one is a number of enky, to which you would like all the main components (not taking into account their "
  putStr "respective lower pure quints) should belong. "
  putStrLn "If you specify as the first one 2 (possibly the simplest case), then to the second one you can define a number in the range [3..53]. "
  putStrLn "If you specify as the first one 3, then to the second one you can define a number in the range [2..35]. "
  putStrLn "If you specify as the first one 4, then to the second one you can define a number in the range [2..26]. "
  putStrLn "If you specify as the first one 6, then to the second one you can define a number in the range [1..17]. "
  putStrLn "If you specify as the first one 9, then to the second one you can define a number in the range [1..11]. "
  enka0 <- getLine
  let enka1 = take 2 . words . filter (\x -> isDigit x || isSpace x) $ enka0
      enka2 = read (head . take 1 $ enka1)::Int
      enka3
        | enka2 == 2 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 53) 3 == LT then 28 else (read (take 2 . head . tail $ enka1)::Int)
           `rem` 53
        | enka2 == 3 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 35) 2 == LT then 19 else (read (take 2 . head . tail $ enka1)::Int)
           `rem` 35
        | enka2 == 4 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 26) 2 == LT then 14 else (read (take 2 . head . tail $ enka1)::Int)
           `rem` 26
        | enka2 == 6 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 17) 1 == LT then 9 else (read (take 2 . head . tail $ enka1)::Int)
           `rem` 17
        | enka2 == 9 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 11) 1 == LT then 6 else (read (take 2 . head . tail $ enka1)::Int)
           `rem` 11
        | otherwise  = error "Not valid number in the second place. "
  return $ show enka2 ++ " " ++ show enka3 ) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD3)
{-# INLINE processD_1 #-}

processD0 :: FilePath -> IO String
processD0 file = onException (readProcessWithExitCode (fromJust (showE "sox")) [file, "x.wav", "-r22050", "channels", "1"] "" >> putStrLn "" >> return "") (do
  exist <- doesFileExist "x.wav"
  if exist then removeFile "x.wav"
  else putStr ""
  putStrLn ""
  putStr "The process was not successful may be because of the not valid data OR SoX cannot convert the given file to the .wav format. "
  putStrLn "Interrupt the program and start again with the valid file. "
  putStrLn "_______________________________________________________________________"
  processD0 file)
{-# INLINE processD0 #-}
  
processD1 :: IO String
processD1 = onException (do
  tempeRa 0
  putStrLn "Please, specify, how many seconds long sound data you would like to record."
  time <- getLine
  let time0 = read (filter (\t -> isDigit t || t == '.') time)::Double
  putStrLn "Please, wait for 0.5 second and produce the needed sound now."
  recA "x.wav" time0
  putStrLn ""
  return "") (do
    dir0 <- listDirectory "."
    let paths5 = filter (isPrefixOf "nx.") dir0
        paths6 = filter (== "x.wav") dir0
        paths  = paths5 ++ paths6
    mapM_ removeFile paths
    putStrLn ""
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD1)
{-# INLINE processD1 #-}

processD2 :: FilePath -> IO String
processD2 file = onException (do     
  exist3 <- doesFileExist file
  if exist3 then return ""
  else do
    putStr "Please, specify the control parameter for the SoX \"noisered\" effect in the range from 0.0 to 1.0. "
    putStrLn "The greater value causes more reduction with possibly removing some important sound data. The default value is 0.5 "
    putStrLn "To use the default value, you can simply press Enter."
    ctrlN <- getLine
    let addit = dropWhile (/= '.') . filter (\t -> isDigit t || t == '.') $ ctrlN
        noiseP = if null ctrlN then ""
                 else tail addit
    controlNoiseReduction $ '0':noiseP 
    norm "_x.wav" 
    if "nx." `isPrefixOf` file 
      then putStr "" 
      else renameFile "8_x.wav" file 
    removeFile "x.wav" 
    removeFile "_x.wav" 
    dir <- listDirectory "." 
    let paths4 = filter (isPrefixOf "nx.") dir
    mapM_ removeFile paths4 
    putStrLn "" 
    return "") (do
      putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
      putStrLn "_______________________________________________________________________"
      processD2 file)
{-# INLINE processD2 #-}

processD3 :: IO String
processD3 = onException (do
  putStr "Please, specify the octave number, to which you would like all the main components (not taking into account their respective lower pure quints) "
  putStrLn "should belong. The number should be better in the range [1..8]"
  fmap d3H getLine) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD3)
{-# INLINE processD3 #-}

d3H :: String -> String
d3H xs = show $ (read (take 1 xs)::Int) `rem` 9
{-# INLINE d3H #-}

processD4 :: IO String
processD4 = onException (do
  putStr "Please, specify the amplitude for the generated overtones as an Int number in the range [0..99]. "
  putStrLn "The default one is 99"
  putStrLn "To use the default value, you can simply press Enter."
  fmap d4H getLine) (do
             putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
             putStrLn "_______________________________________________________________________"
             processD4)
{-# INLINE processD4 #-}

d4H :: String -> String
d4H xs
 | null xs = "1.0"
 | otherwise = let amplOb = (read (take 2 . filter isDigit $ xs)::Int) `rem` 100 in
    case amplOb of
      99 -> "1.0"
      _ -> if compare (amplOb `quot` 9) 1 == LT then "0.0" ++ show (amplOb + 1)
           else "0." ++ show (amplOb + 1)
{-# INLINE d4H #-}

processD5 :: IO String
processD5 = onException (do
  putStr "Please, specify the basic duration for the generated sounds as a Double number in the range [0.1..4.0]. "
  putStrLn "The default one is 0.5"
  putStrLn "To use the default value, you can simply press Enter."
  fmap d5H getLine) (do
             putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
             putStrLn "_______________________________________________________________________"
             processD5)
{-# INLINE processD5 #-}

d5H :: String -> String
d5H xs
  | null xs = "0.5"
  | otherwise = let time1 = (read (filter (\z -> isDigit z || z == '.') xs)::Double) in
      if compare time1 0.1 /= LT && compare time1 4.0 /= GT then showFFloat (Just 4) time1 $ show 0
      else let mantissa = time1 - (fromIntegral . truncate $ time1)
               ceilP    = (truncate time1::Int) `rem` 4 in
             if ceilP == 0 then "0." ++ showFFloat (Just 4) mantissa (show 0)
             else show ceilP ++ "." ++ showFFloat (Just 4) mantissa (show 0)
{-# INLINE d5H #-}

processD7 :: IO String
processD7 = onException (do
  putStrLn "Please, input the Ukrainian text that will be used to define signs for the harmonics coefficients to produce a special timbre for the notes: "
  fmap d7H getLine) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD7)
{-# INLINE processD7 #-}

d7H :: String -> String
d7H xs
  | null xs = "або"
  | otherwise = xs
{-# INLINE d7H #-}

processD8 :: IO String
processD8 = onException (do
  putStr "Please, specify in how many times the amplitude for the second lower note (if any) is greater than the amplitude for the main note. "
  putStrLn "The number is in the range [0.1..2.0]. The default one is 1.0"
  putStrLn "To use the default value, you can simply press Enter."
  fmap d8H getLine) (do
             putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
             putStrLn "_______________________________________________________________________"
             processD8)
{-# INLINE processD8 #-}

d8H :: String -> String
d8H xs
  | null xs = "1.0"
  | otherwise = let dAmpl1 = (read (filter (\z -> isDigit z || z == '.') xs)::Double) in
    if compare dAmpl1 0.1 /= LT && compare dAmpl1 2.0 /= GT then showFFloat (Just 4) dAmpl1 $ show 0
    else let mantissa = dAmpl1 - (fromIntegral . truncate $ dAmpl1)
             ceilP    = (truncate dAmpl1::Int) `rem` 2 in
           if ceilP == 0 then "0." ++ showFFloat (Just 4) mantissa (show 0)
           else show ceilP ++ "." ++ showFFloat (Just 4) mantissa (show 0)
{-# INLINE d8H #-}

processD9 :: IO String
processD9 = onException (do
  putStrLn "Please, input the Ukrainian text that will be used to define intervals to be used to produce the lower note for the given main one. "
  putStrLn "The default one is \"й\". "
  putStrLn "To use the default value, you can simply press Enter."
  fmap d9H getLine) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD9)
{-# INLINE processD9 #-}

d9H :: String -> String
d9H xs
  | null xs = "й"
  | otherwise = xs
{-# INLINE d9H #-}

processD :: IO String
processD = onException (do
  putStrLn "Please, input the Ukrainian text that will be used to create a special timbre for the notes: "
  getLine) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD)
{-# INLINE processD #-}

processD99 :: IO String
processD99 = onException (do
  putStr "Please, input the lists of Int in Haskell syntaxis (e. g. [1,3..56], or [3..45], or [2..]) of the indices for the files to be played "
  putStr "with SoX effects applied to. The lists must be separated with newline (just press \"Enter\"), empty lists are ignored. If index is "
  putStrLn "element of several input lists then if its number of occurrences in all the lists is odd, then it is played, otherwise it is not. "
  putStrLn "To end the input, just press the combination that means end of input (e. g. for Unices, it's probably Ctrl + D). "
  getContents) (do
    putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."
    putStrLn "_______________________________________________________________________"
    processD)
{-# INLINE processD99 #-}

----------------------------------------------------------------------------------------------   

-- | Tesing variant of the 'soundGen3G' with predefined three last functional arguments.
testSoundGen2G :: FilePath -> (Double -> OvertonesO) -> Double -> String -> IO ()
                              ----------------------                        
  --                                    f
testSoundGen2G file f y zs = do
  vecA0 <- fmap (V.map (\t -> quotRem t 108)) (readFileDoubles file) -- >>= print
  let n = V.length vecA0
      freq0 j = V.unsafeIndex notes (snd . V.unsafeIndex vecA0 $ j `rem` n)
      f0 t = V.fromList [(0.05763181818181818 * t, 0.3598),(1.112159090909091 * t, 0.4588962),(2 * t, 0.6853),(3 * t, 0.268),(4 * t, 0.6823),(5 * t, 0.53)]
      fA1 j = fAddFElem (freq0 (j + 1),0.5) (freq0 j) f0 gAdd04 
      fR1 j = fRemoveFElem (freq0 (j + 1),0.5) (freq0 j) f0 gRem03
      vecB = V.imap (\j r -> (V.unsafeIndex notes (snd r),
       case fst r of
         0 -> (\vv -> f0 vv)
         1 -> fA1 j
         2 -> fA1 j
         3 -> fA1 j
         4 -> fA1 j
         _ -> fR1 j)) vecA0
      (t, ws) = splitAt 1 . syllableStr n $ zs
      m0    = length ws
      zeroN = numVZeroesPre vecB
      v2    = V.map (\yy -> y * fromIntegral (yy * m0) / fromIntegral (head t)) . V.fromList $ ws in V.imapM_ (\j (x,k) -> do
        h1 (\u -> k (1.1 * freq0 j)) (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j 
        renameFile ("result.wav") $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB
  endFromResult

-- | Generates a sequence of sounds with changing timbre. Uses several functions as parameters. 
soundGen3G :: FilePath -> (Double -> OvertonesO) -> Double -> String -> ((Double,Double) -> Double -> (Double -> OvertonesO) -> OvertonesO) 
  -> ((Double,Double) -> Double -> (Double -> OvertonesO) -> OvertonesO) -> (Double -> OvertonesO) -> IO ()
soundGen3G file f y zs gAdd gRem f0 = do
  vecA0 <- fmap (V.map (\t -> quotRem t 108)) (readFileDoubles file) -- >>= print
  let n = V.length vecA0
      freq0 j = V.unsafeIndex notes (snd . V.unsafeIndex vecA0 $ j `rem` n)
      fA1 j = fAddFElem (freq0 (j + 1),0.5) (freq0 j) f0 gAdd
      fR1 j = fRemoveFElem (freq0 (j + 1),0.5) (freq0 j) f0 gRem
      vecB = V.imap (\j r -> (V.unsafeIndex notes (snd r),
       case fst r of
         0 -> (\vv -> f0 vv)
         1 -> fA1 j
         2 -> fA1 j
         3 -> fA1 j
         4 -> fA1 j
         _ -> fR1 j)) vecA0
      (t, ws) = splitAt 1 . syllableStr n $ zs
      m0    = length ws
      zeroN = numVZeroesPre vecB
      v2    = V.map (\yy -> y * fromIntegral (yy * m0) / fromIntegral (head t)) . V.fromList $ ws in V.imapM_ (\j (x,k) -> do
        h1 (\u -> k (1.1 * freq0 j)) (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j 
        renameFile ("result.wav") $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB
  endFromResult

-- | Generates a sequence of sounds with changing timbre. Uses several functions as parameters. Unlike the 'soundGen3G', the last two 
-- functions as arguments their first argument have not ('Double','Double'), but 'V.Vector' of them so areapplied to 'OvertonesO'. To 
-- provide a generalized functionality it uses two additional functions @freq0 :: Int -> OvertonesO@ and @proj :: OvertonesO -> (Double,Double)@ 
-- to define the first element to which are applied @gAdds@ and @gRems@ and the way to obtain a one tuple from the 'V.Vector' of them (possibly, each 
-- element of which differs from the result). Besides, it lifts notes into specified with the first two 'Int' arguments enku (see 'liftInEnku'). 
-- The 'Double' argument is expected to be not used, though it is specified.
soundGen3G_O :: Int -> Int -> Double -> FilePath -> (Double -> OvertonesO) -> Double -> String -> (OvertonesO -> Double -> (Double -> OvertonesO) -> OvertonesO) 
  -> (OvertonesO -> Double -> (Double -> OvertonesO) -> OvertonesO) -> (Int -> OvertonesO) -> (OvertonesO -> (Double,Double)) -> 
    (Double -> OvertonesO) -> IO ()
soundGen3G_O m ku freq1 file f y zs gAdds gRems freq0 proj f0 = do
  vecA0 <- fmap (V.map (\t -> quotRem t 108)) (readFileDoubles file) -- >>= print
  let n = V.length vecA0
      fA1 j = fAddFElems (freq0 (j + 1)) (fst . proj . freq0 $ j) f0 gAdds
-- It can be: proj . freq0 $ j = if V.null (freq0 j) then (V.unsafeIndex notes (snd . V.unsafeIndex vecA0 $ j `rem` n),0.5) else V.unsafeIndex (freq0 j) 0
      fR1 j = fRemoveFElems (freq0 (j + 1)) (fst . proj . freq0 $ j) f0 gRems
      vecB = V.imap (\j r -> (V.unsafeIndex notes (snd r),
       case fst r of
         0 -> (\vv -> f0 vv)
         1 -> fA1 j
         2 -> fA1 j
         3 -> fA1 j
         4 -> fA1 j
         _ -> fR1 j)) vecA0
      (t, ws) = splitAt 1 . syllableStr n $ zs
      m0    = length ws
      zeroN = numVZeroesPre vecB
      v2    = V.map (\yy -> y * fromIntegral (yy * m0) / fromIntegral (head t)) . V.fromList $ ws in V.imapM_ (\j (x,k) -> do
        h2 (k x) (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) m ku freq1
        renameFile ("result.wav") $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB
  endFromResult  

-- | Generates a sequence of sounds with changing timbre. Is a generalized version of the 'soundGen3G_O', instead of predefined conversion function 
-- inside, it uses a user-defined one. 
-- 
-- > soundGen3G_O = soundGen3G_O2 
-- with the first argument 
-- > conversionFII (f0,fA1,fR1) = V.imap (\j r -> (V.unsafeIndex notes (snd r),
-- >      case fst r of
-- >        0 -> (\vv -> f0 vv)
-- >        1 -> fA1 j
-- >        2 -> fA1 j
-- >        3 -> fA1 j
-- >        4 -> fA1 j
-- >        _ -> fR1 j))
-- 
soundGen3G_O2 :: ((Double -> OvertonesO,Int -> Double -> OvertonesO,Int -> Double -> OvertonesO) -> V.Vector (Int,Int) -> 
  V.Vector (Double,Double -> OvertonesO)) -> Int -> Int -> Double -> FilePath -> (Double -> OvertonesO) -> Double -> String -> 
    (OvertonesO -> Double -> (Double -> OvertonesO) -> OvertonesO) -> (OvertonesO -> Double -> (Double -> OvertonesO) -> OvertonesO) -> 
      (Int -> OvertonesO) -> (OvertonesO -> (Double,Double)) -> (Double -> OvertonesO) -> IO ()
soundGen3G_O2 conversionFII m ku freq1 file f y zs gAdds gRems freq0 proj f0 = do
  vecA0 <- fmap (V.map (\t -> quotRem t 108)) (readFileDoubles file) -- >>= print
  let n = V.length vecA0
      fA1 j = fAddFElems (freq0 (j + 1)) (fst . proj . freq0 $ j) f0 gAdds
-- It can be: proj . freq0 $ j = if V.null (freq0 j) then (V.unsafeIndex notes (snd . V.unsafeIndex vecA0 $ j `rem` n),0.5) else V.unsafeIndex (freq0 j) 0
      fR1 j = fRemoveFElems (freq0 (j + 1)) (fst . proj . freq0 $ j) f0 gRems
      vecB = conversionFII (f0,fA1,fR1) vecA0
      (t, ws) = splitAt 1 . syllableStr n $ zs
      m0    = length ws
      zeroN = numVZeroesPre vecB
      v2    = V.map (\yy -> y * fromIntegral (yy * m0) / fromIntegral (head t)) . V.fromList $ ws in V.imapM_ (\j (x,k) -> do
        h2 (k x) (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) m ku freq1
        renameFile ("result.wav") $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB
  endFromResult    

-- | For the given parameters generates a single sound with overtones.
h1 :: (Double -> OvertonesO) -> (Double, Double) -> Int -> IO ()
h1 f (x, y) j = do
    let note0 = closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0)
        v0    = f note0
        ts = showFFloat (Just 4) (abs y) ""
    (code,_,herr) <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA.wav", "synth", ts,"sine",showFFloat Nothing note0 ""] ""
    print herr
    partialTest_k v0 0 ts
    mixTest

-- | For the given parameters generates a single sound with overtones. Unlike the 'h1' function, it lifts into specified by 'Int' arguments enku 
-- (see 'liftInEnku') the frequency.
h2 :: OvertonesO -> (Double, Double) -> Int -> Int -> Double -> IO ()
h2 v (x, y) m ku freq1 = do
    let note0 = fromMaybe freq1 . liftInEnku m ku . closestNote $ (if x /= 0.0 then abs x else V.unsafeIndex notes 0)
        ts = showFFloat (Just 4) (abs y) ""
    (code,_,herr) <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA.wav", "synth", ts,"sine",showFFloat Nothing note0 ""] ""
    print herr
    partialTest_k v 0 ts
    mixTest
