packages feed

dobutokO2 0.20.0.0 → 0.21.0.0

raw patch · 5 files changed

+420/−748 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- DobutokO.Sound: closestNote :: Double -> Double
- DobutokO.Sound: dNote :: Int -> Double -> Maybe Double
- DobutokO.Sound: enkuDown :: Int -> Double -> Double
- DobutokO.Sound: enkuUp :: Int -> Double -> Double
- DobutokO.Sound: intervalsFromString :: String -> Vector Int
- DobutokO.Sound: liftInEnku :: Int -> Int -> Double -> Maybe Double
- DobutokO.Sound: liftInEnkuV :: Int -> Int -> Vector Double -> Vector Double
- DobutokO.Sound: neighbourNotes :: Double -> Vector Double -> (Double, Double)
- DobutokO.Sound: nkyT :: Int -> Vector (Double, Double)
- DobutokO.Sound: notes :: Vector Double
- DobutokO.Sound: numVZeroesPre :: Vector a -> Int
- DobutokO.Sound: oberSoXSynth :: Double -> IO ()
- DobutokO.Sound: oberTones :: Double -> Vector (Double, Double)
- DobutokO.Sound: octavesT :: Vector (Double, Double)
- DobutokO.Sound: prependZeroes :: Int -> String -> String
- DobutokO.Sound: pureQuintNote :: Double -> Double
- DobutokO.Sound: syllableStr :: Int -> String -> [Int]
- DobutokO.Sound: whichEnka :: Int -> Double -> Maybe Int
+ DobutokO.Sound.Functional: closestNote :: Double -> Double
+ DobutokO.Sound.Functional: dNote :: Int -> Double -> Maybe Double
+ DobutokO.Sound.Functional: doubleVecFromVecOfDouble :: (Double -> ObertonesO) -> Double -> Vector (Maybe Double) -> Vector ObertonesO
+ DobutokO.Sound.Functional: endFromResult :: IO ()
+ DobutokO.Sound.Functional: enkuDown :: Int -> Double -> Double
+ DobutokO.Sound.Functional: enkuUp :: Int -> Double -> Double
+ DobutokO.Sound.Functional: freqsFromFile :: FilePath -> Int -> IO (Vector Int)
+ DobutokO.Sound.Functional: helpF0 :: Int -> String
+ DobutokO.Sound.Functional: helpF1 :: Vector (Double -> Double) -> Vector Double -> Vector Int -> Vector (Maybe Double)
+ DobutokO.Sound.Functional: intervalsFromString :: String -> Vector Int
+ DobutokO.Sound.Functional: liftInEnku :: Int -> Int -> Double -> Maybe Double
+ DobutokO.Sound.Functional: liftInEnkuV :: Int -> Int -> Vector Double -> Vector Double
+ DobutokO.Sound.Functional: mixTest :: IO ()
+ DobutokO.Sound.Functional: nOfZeroesLog :: Int -> Maybe Int
+ DobutokO.Sound.Functional: neighbourNotes :: Double -> Vector Double -> (Double, Double)
+ DobutokO.Sound.Functional: nkyT :: Int -> NotePairs
+ DobutokO.Sound.Functional: notes :: Vector Double
+ DobutokO.Sound.Functional: numVZeroesPre :: Vector a -> Int
+ DobutokO.Sound.Functional: oberSoXSynth :: Double -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_SG :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> ((Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()) -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_Sf3G :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String -> ((Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO ()) -> IO ()
+ DobutokO.Sound.Functional: oberTones :: Double -> ObertonesO
+ DobutokO.Sound.Functional: octavesT :: NotePairs
+ DobutokO.Sound.Functional: partialTest_k :: ObertonesO -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: prependZeroes :: Int -> String -> String
+ DobutokO.Sound.Functional: pureQuintNote :: Double -> Double
+ DobutokO.Sound.Functional: soundGenF3 :: Vector (Double -> Double) -> Vector Double -> Vector Int -> (Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: strToInt :: String -> Int
+ DobutokO.Sound.Functional: syllableStr :: Int -> String -> [Int]
+ DobutokO.Sound.Functional: type NotePairs = Vector (Double, Double)
+ DobutokO.Sound.Functional: type ObertonesO = Vector (Double, Double)
+ DobutokO.Sound.Functional: type SoundsO = Vector (Double, Double)
+ DobutokO.Sound.Functional: vStrToVInt :: Vector String -> Vector Int
+ DobutokO.Sound.Functional: whichEnka :: Int -> Double -> Maybe Int
- DobutokO.Sound: chetvirkyTA :: Vector (Double, Double)
+ DobutokO.Sound: chetvirkyTA :: NotePairs
- DobutokO.Sound: desyatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: desyatkyTA :: NotePairs
- DobutokO.Sound: dev'yatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: dev'yatkyTA :: NotePairs
- DobutokO.Sound: dviykyTA :: Vector (Double, Double)
+ DobutokO.Sound: dviykyTA :: NotePairs
- DobutokO.Sound: oberTones2 :: Double -> String -> Vector (Double, Double)
+ DobutokO.Sound: oberTones2 :: Double -> String -> ObertonesO
- DobutokO.Sound: octavesTA :: Vector (Double, Double)
+ DobutokO.Sound: octavesTA :: NotePairs
- DobutokO.Sound: odynadtsyatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: odynadtsyatkyTA :: NotePairs
- DobutokO.Sound: p'yatirkyTA :: Vector (Double, Double)
+ DobutokO.Sound: p'yatirkyTA :: NotePairs
- DobutokO.Sound: shistkyTA :: Vector (Double, Double)
+ DobutokO.Sound: shistkyTA :: NotePairs
- DobutokO.Sound: simkyTA :: Vector (Double, Double)
+ DobutokO.Sound: simkyTA :: NotePairs
- DobutokO.Sound: triykyTA :: Vector (Double, Double)
+ DobutokO.Sound: triykyTA :: NotePairs
- DobutokO.Sound: uniqOberTonesV :: Double -> String -> Vector (Double, Double)
+ DobutokO.Sound: uniqOberTonesV :: Double -> String -> ObertonesO
- DobutokO.Sound: uniqOberTonesV2 :: Double -> String -> String -> Vector (Double, Double)
+ DobutokO.Sound: uniqOberTonesV2 :: Double -> String -> String -> ObertonesO
- DobutokO.Sound: visimkyTA :: Vector (Double, Double)
+ DobutokO.Sound: visimkyTA :: NotePairs
- DobutokO.Sound.Functional: oberSoXSynth2FDN :: (Double -> Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynth2FDN :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynth2FDN_B :: (Double -> Vector (Double, Double)) -> (Double, Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynth2FDN_B :: (Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynth2FDN_S :: (Double -> Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynth2FDN_S :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynth2FDN_Sf :: (Double -> Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynth2FDN_Sf :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynth2FDN_Sf3 :: (Double -> Vector (Double, Double)) -> (Double, Double, Double) -> Int -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynth2FDN_Sf3 :: (Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynthGen2FDN :: FilePath -> Int -> Int -> (Double -> Vector (Double, Double)) -> Double -> String -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynthGen2FDN_B :: FilePath -> Int -> Int -> (Double -> Vector (Double, Double)) -> Double -> Double -> String -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_B :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynthGen2FDN_S :: FilePath -> Int -> Int -> (Double -> Vector (Double, Double)) -> Double -> String -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_S :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynthGen2FDN_Sf :: FilePath -> Int -> Int -> (Double -> Vector (Double, Double)) -> Double -> String -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_Sf :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO ()
- DobutokO.Sound.Functional: oberSoXSynthGen2FDN_Sf3 :: FilePath -> Int -> Int -> (Double -> Vector (Double, Double)) -> Double -> Double -> String -> String -> IO ()
+ DobutokO.Sound.Functional: oberSoXSynthGen2FDN_Sf3 :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String -> IO ()

Files

CHANGELOG.md view
@@ -188,3 +188,11 @@  * Twentieth version. Improved behaviour of the functions in the DobutokO.Sound.Functional module. Added new functions to produce melodies. Some minor documentation improvements.++## 0.21.0.0 -- 2020-03-30++* Thirteenth version. Starting from the version 0.21.0.0 the package extends its library functions with the possibility to create not only+single notes or intervals of sounds playing simultaneously but also sets of three, four, five, six, seven or more sounds played simultanously+with their obertones. For more information, please, refer to the documentation for the DobutokO.Sound.Functional module. Added the needed functionality+for this. Rearrange modules DobutokO.Sound and DobutokO.Sound.Functional to provide more flexibility and shorten the code. Added new generalized functions+to the DobutokO.Sound.Functional module. Some documentation improvements.
DobutokO/Sound.hs view
@@ -14,9 +14,7 @@ module DobutokO.Sound (   -- * Library and executable functions   -- ** For the fixed timbre-  oberTones-  , oberSoXSynth-  , oberSoXSynthN+  oberSoXSynthN   -- *** For the fixed timbre with different signs for harmonics coefficients   , oberTones2   , oberSoXSynth2@@ -43,19 +41,11 @@   , uniqOberSoXSynthNGen3   , uniqOberSoXSynthNGen4   -- ** Work with octaves-  , octavesT   , octaveUp   , octaveDown   , whichOctave   , liftInOctave   , liftInOctaveV-  -- * Work with enky (extension to octaves functionality)-  , nkyT-  , enkuUp-  , enkuDown-  , whichEnka-  , liftInEnku-  , liftInEnkuV   -- ** Even more extended   , dviykyTA   , triykyTA@@ -78,16 +68,7 @@   , uniqOberSoXSynthNGen3E   , uniqOberSoXSynthNGen4E   -- * Auxiliary functions-  , notes-  , neighbourNotes-  , closestNote-  , pureQuintNote-  , syllableStr   , signsFromString-  , prependZeroes-  , intervalsFromString-  , dNote-  , numVZeroesPre ) where  import CaseBi (getBFst')@@ -107,57 +88,39 @@ import SoXBasics import Processing_mmsyn7ukr import Melodics.Ukrainian (convertToProperUkrainian)---- | 'V.Vector' of musical notes in Hz.-notes :: V.Vector Double--- notes V.! 57 = 440.0   -- A4 in Hz-notes = V.generate 108 (\t ->  fromIntegral 440 * 2 ** (fromIntegral (t - 57) / fromIntegral 12))---- | Returns a 'V.Vector' of tuples with the lowest and highest frequencies for the notes in the octaves.-octavesT :: V.Vector (Double, Double)-octavesT = V.generate 9 (\i -> (V.unsafeIndex notes (i * 12), V.unsafeIndex notes (i * 12 + 11)))---- | Returns a 'V.Vector' of tuples with the lowest and highest frequencies for the notes in the sets consisting of @n@ consequential notes--- (including semi-tones). An 'Int' parameter defines this @n@. It can be 2, 3, 4, 6, 9, or 12 (the last one is for default octaves, see 'octavesT').--- So for different valid @n@ you obtain doubles, triples and so on. The function being applied returns a 'V.Vector' of such sets with--- their respective lowest and highest frequencies.-nkyT :: Int -> V.Vector (Double, Double)-nkyT n-  | getBFst' (False,V.fromList . zip [2,3,4,6,9,12] $ repeat True) n = V.generate (108 `quot` n) (\i -> (V.unsafeIndex notes (i * n),-       V.unsafeIndex notes (i * n + (n - 1))))-  | otherwise = octavesT+import DobutokO.Sound.Functional -dviykyTA :: V.Vector (Double, Double)+dviykyTA :: NotePairs dviykyTA = V.generate 107 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 1))) -triykyTA :: V.Vector (Double, Double)+triykyTA :: NotePairs triykyTA = V.generate 106 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 2))) -chetvirkyTA :: V.Vector (Double, Double)+chetvirkyTA :: NotePairs chetvirkyTA = V.generate 105 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 3))) -p'yatirkyTA :: V.Vector (Double, Double)+p'yatirkyTA :: NotePairs p'yatirkyTA = V.generate 104 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 4))) -shistkyTA :: V.Vector (Double, Double)+shistkyTA :: NotePairs shistkyTA = V.generate 103 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 5))) -simkyTA :: V.Vector (Double, Double)+simkyTA :: NotePairs simkyTA = V.generate 102 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 6))) -visimkyTA :: V.Vector (Double, Double)+visimkyTA :: NotePairs visimkyTA = V.generate 101 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 7))) -dev'yatkyTA :: V.Vector (Double, Double)+dev'yatkyTA :: NotePairs dev'yatkyTA = V.generate 100 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 8))) -desyatkyTA :: V.Vector (Double, Double)+desyatkyTA :: NotePairs desyatkyTA = V.generate 99 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 9))) -odynadtsyatkyTA  :: V.Vector (Double, Double)+odynadtsyatkyTA  :: NotePairs odynadtsyatkyTA = V.generate 98 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 10))) -octavesTA :: V.Vector (Double, Double)+octavesTA :: NotePairs octavesTA = V.generate 97 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 11)))  --------------------------------------------------------------------------------------------------------------------------@@ -167,25 +130,12 @@ octaveUp x = 2 * x {-# INLINE octaveUp #-} --- | Returns an analogous note in the higher n-th elements set (its frequency in Hz) (see 'nkyT'). An 'Int' parameter defines this @n@.-enkuUp  :: Int -> Double -> Double-enkuUp n x-  | getBFst' (False, V.fromList . zip [2..11] $ repeat True) n = 2 ** (fromIntegral n / fromIntegral 12) * x-  | otherwise = octaveUp x-{-# INLINE enkuUp #-}    -- | Returns an analogous note in the lower octave (its frequency in Hz). octaveDown :: Double -> Double octaveDown x = x / fromIntegral 2 {-# INLINE octaveDown #-} --- | Returns an analogous note in the lower n-th elements set (its frequency in Hz) (see 'nkyT'). An 'Int' parameter defines this @n@.-enkuDown  :: Int -> Double -> Double-enkuDown n x-  | getBFst' (False, V.fromList . zip [2..11] $ repeat True) n = 2 ** (fromIntegral (-n) / fromIntegral 12) * x-  | otherwise = octaveDown x-{-# INLINE enkuDown #-}- -----------------------------------------------------------------------------------------------------------------------------  -- | Function can be used to determine to which octave (in the American notation for the notes, this is a number in the note written form,@@ -201,19 +151,6 @@            _ -> z - 1) t        _    -> Just 8) . V.findIndex (\(t1, t2) -> compare (closestNote x) t1 == LT) $ octavesT   | otherwise = Nothing---- | Similarly to 'whichOctave' returns a 'Maybe' number (actually frequency) for the n-th elements set of notes (see 'nkyT').--- An 'Int' parameter defines that @n@.-whichEnka :: Int -> Double -> Maybe Int-whichEnka n x-  | getBFst' (False,V.fromList . zip [2,3,4,6,9,12] $ repeat True) n && compare (closestNote x) 24.4996 == GT = (\t ->-     case isJust t of -       True -> fmap (\z ->-         case z of-           0 -> z-           _ -> z - 1) t-       _    -> Just ((108 `quot` n) - 1)) . V.findIndex (\(t1, t2) -> compare (closestNote x) t1 == LT) $ nkyT n-  | otherwise = Nothing        -- | Function lifts the given frequency to the given number of the octave (in American notation, from 0 to 8). This number is an 'Int' parameter. -- The function also takes into account the lower pure quint for the closest note.@@ -236,85 +173,17 @@                      else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) octaveDown $ closestNote x)   | otherwise = Nothing --- | Similarly to 'liftInOctave' returns a 'Maybe' number (actually frequency) for the n-th elements set of notes (see 'nkyT').--- A second 'Int' parameter defines that @n@.-liftInEnku :: Int -> Int -> Double -> Maybe Double-liftInEnku n ku x-  | compare n 0 == LT || compare n ((108 `quot` ku) - 1) == GT = Nothing-  | getBFst' (False, V.fromList . zip [2,3,4,6,9,12] $ repeat True) ku && compare (closestNote x) 24.4996 == GT =-      case compare (fromJust . whichEnka ku $ x) n of-        EQ -> Just (closestNote x)-        LT -> let z  = log (V.unsafeIndex notes (n * ku) / closestNote x) / log 2.0-                  z1 = truncate z in-                   if abs (z - fromIntegral z1) > 0.999 || abs (z - fromIntegral z1) < 0.001-                     then Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) (enkuUp ku) $ closestNote x)-                     else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 2) (enkuUp ku) $ closestNote x)-        _  -> let z  = log (closestNote x / V.unsafeIndex notes (n * ku)) / log 2.0-                  z1 = truncate z in-                   if abs (z - fromIntegral z1) > 0.999 || abs (z - fromIntegral z1) < 0.001-                     then Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 2) (enkuDown ku) $ closestNote x)-                     else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) (enkuDown ku) $ closestNote x)-  | otherwise = Nothing  - -- | Function lifts the 'V.Vector' of 'Double' representing frequencies to the given octave with the 'Int' number. Better to use numbers in the range [1..8]. -- The function also takes into account the lower pure quint for the obtained note behaviour. If it is not practical to determine the octave, the resulting -- frequency is omitted from the resulting 'V.Vector'. liftInOctaveV :: Int -> V.Vector Double -> V.Vector Double liftInOctaveV n = V.mapMaybe (\z -> liftInOctave n z) --- | Similarly to 'liftInOctaveV' returns a 'V.Vector' 'Double' (actually frequencies) for the n-th elements set of notes (see 'nkyT') instead of octaves.--- A second 'Int' parameter defines that @n@. -liftInEnkuV :: Int -> Int -> V.Vector Double -> V.Vector Double-liftInEnkuV n ku = V.mapMaybe (\z -> liftInEnku n ku z)- -------------------------------------------------------------------------------------------------------------------------------- --- | Function returns either the nearest two musical notes if frequency is higher than one for C0 and lower than one for B8--- or the nearest note duplicated in a tuple.-neighbourNotes :: Double -> V.Vector Double -> (Double, Double)-neighbourNotes x v-  | compare x (V.unsafeIndex v 0) /= GT = (V.unsafeIndex v 0, V.unsafeIndex v 0)-  | compare x (V.unsafeIndex v (V.length v - 1)) /= LT = (V.unsafeIndex v (V.length v - 1), V.unsafeIndex v (V.length v - 1))-  | compare (V.length v) 2 == GT = if compare x (V.unsafeIndex  v (V.length v `quot` 2)) /= GT-      then neighbourNotes x (V.unsafeSlice 0 (V.length v `quot` 2 + 1) v)-      else neighbourNotes x (V.unsafeSlice (V.length v `quot` 2) (V.length v - (V.length v `quot` 2)) v)-  | otherwise = (V.unsafeIndex v 0, V.unsafeIndex v (V.length v - 1))---- | Returns the closest note to the given frequency in Hz.  -closestNote :: Double -> Double-closestNote x- | compare x 0.0 == GT =-    let (x0, x2) = neighbourNotes x notes-        r0       = x / x0-        r2       = x2 / x in -     if compare r2 r0 == GT-       then x0-       else x2- | otherwise = 0.0---- | Returns a pure quint lower than the given note.-pureQuintNote :: Double -> Double-pureQuintNote x = x / 2 ** (fromIntegral 7 / fromIntegral 12)-{-# INLINE pureQuintNote #-}---- | Function is used to generate a rhythm of the resulting file \'end.wav\' from the Ukrainian text and a number of sounds either in the syllables or in the words without vowels.-syllableStr :: Int -> String -> [Int]-syllableStr n xs =-  let ps = take n . cycle . concat . sylLengthsP2 . syllablesUkrP $ xs-      y  = sum ps in-       case y of-         0 -> [0]-         _ -> y:ps---- | For the given frequency of the note it generates a 'V.Vector' of the tuples, each one of which contains the harmonics' frequency and amplitude.-oberTones :: Double -> V.Vector (Double, Double)-oberTones note =-  V.takeWhile (\(!w,!z) -> compare w (V.unsafeIndex notes 107) /= GT && compare (abs z) 0.001 == GT) . V.zip (V.generate 1024 (\i ->-    note * fromIntegral (i + 2))) $ (V.generate 1024 (\i -> fromIntegral 1 / fromIntegral ((i + 1) * (i + 1))))- -- | For the given frequency of the note it generates a 'V.Vector' of the tuples, each one of which contains the harmonics' frequency and amplitude. For every given -- 'String' structure of the uniqueness (see the documentation for @mmsyn7s@ package and its 'MMSyn7.Syllable' module) it produces the unique timbre.-uniqOberTonesV :: Double -> String -> V.Vector (Double, Double)+uniqOberTonesV :: Double -> String -> ObertonesO uniqOberTonesV note xs =   let ys = uniquenessPeriods xs       z  = sum ys@@ -341,7 +210,7 @@  -- | For the given frequency of the note and a Ukrainian text it generates a 'V.Vector' of the tuples, each one of which contains -- the harmonics' frequency and amplitude. The 'String' is used to produce the signs for harmonics coefficients.-oberTones2 :: Double -> String -> V.Vector (Double, Double)+oberTones2 :: Double -> String -> ObertonesO oberTones2 note ts =   V.takeWhile (\(!w,!z) -> compare w (V.unsafeIndex notes 107) /= GT && compare (abs z) 0.001 == GT) . V.filter (\(_, t4) -> t4 /= 0.0) .     V.zip (V.generate 1024 (\i -> note * fromIntegral (i + 2))) $ (V.generate 1024 (\i -> fromIntegral (V.unsafeIndex (signsFromString 1024 ts)@@ -350,7 +219,7 @@ -- | For the given frequency of the note it generates a 'V.Vector' of the tuples, each one of which contains the harmonics' frequency and amplitude. For every given -- first 'String' argument structure of the uniqueness (see the documentation for @mmsyn7s@ package and its 'MMSyn7.Syllable' module) it produces the unique timbre. -- The second 'String' is used to produce the signs for harmonics coefficients.-uniqOberTonesV2 :: Double -> String -> String -> V.Vector (Double, Double)+uniqOberTonesV2 :: Double -> String -> String -> ObertonesO uniqOberTonesV2 note xs ts =    let ys = uniquenessPeriods xs       z  = sum ys@@ -360,29 +229,6 @@         V.takeWhile (\(!u,!z) -> compare u (V.unsafeIndex notes 107) /= GT && compare (abs z) 0.001 == GT) . V.filter (\(_, t4) -> t4 /= 0.0) .           V.unsafeSlice 1 (z2 - 1) . V.zip (V.generate z2 (\i -> note * fromIntegral (i + 1))) $ v2 --- | For the given frequency it generates a musical sound with a timbre. The main component of the sound includes the lower pure quint,--- which can be in the same octave or in the one with the number lower by one. Please, check before executing--- whether there is no \"x.wav\", \"test*\", \"result*\" and \"end.wav\" files in the current directory, because they can be overwritten.-oberSoXSynth :: Double -> IO ()-oberSoXSynth x = do-  let note0 = if x /= 0.0 then closestNote (abs x) else V.unsafeIndex notes 0-      note1 = pureQuintNote note0-      v0    = oberTones note0-      v1    = oberTones note1-      oberSoXSynthHelp vec = let l = V.length vec in V.imapM_ (\i (noteN, !amplN) -> readProcessWithExitCode (fromJust (showE "sox"))-        ["-r22050", "-n", "test0" ++ show (i + 2) ++ ".wav", "synth", "0.5","sine", showFFloat (Just 4) noteN $ show 0, "vol", showFFloat (Just 4) (amplN /-          fromIntegral l) $ show 0] "") vec-      oberSoXSynthHelp2 vec = let l = V.length vec in V.imapM_ (\i (noteN, !amplN) -> readProcessWithExitCode (fromJust (showE "sox"))-        ["-r22050", "-n", "test1" ++ show (i + 2) ++ ".wav", "synth", "0.5","sine", showFFloat (Just 4) noteN $ show 0, "vol", showFFloat (Just 4) (amplN /-          fromIntegral l) $ show 0] "") vec-  _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test01.wav", "synth", "0.5","sine", showFFloat (Just 4) note0 $ show 0, "synth", "0.5","sine", "mix", showFFloat (Just 4) note1 $ show 0, "vol","0.5"] ""-  oberSoXSynthHelp v0-  oberSoXSynthHelp2 v1-  paths0 <- listDirectory "."-  let paths = sort . filter (isPrefixOf "test") $ paths0-  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-  mapM_ removeFile paths- -- | Similar to 'oberSoXSynth' except that takes not necessarily pure lower quint note as the second one, but the one specified by the 'String' parameter -- as an argument to 'dNote'. If you begin the 'String' with space characters, or \"сь\", or \"ць\", or dash, or apostrophe, or soft sign, than there will -- be no interval and the sound will be solely one with its obertones.@@ -407,10 +253,7 @@     _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testB.wav", "synth", "0.5","sine", showFFloat (Just 4) (fromJust note1) $ show 0,   "vol","0.5"]   ""     oberSoXSynthHelp v0     oberSoXSynthHelp2 v1-  paths0 <- listDirectory "."-  let paths = sort . filter (isPrefixOf "test") $ paths0-  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-  mapM_ removeFile paths+  mixTest  -- | Similar to 'oberSoXSynthDN' except that the resulting duration is specified by the second 'Double' parameter in seconds. For 'oberSoXSynthDN' -- it is equal to 0.5.@@ -437,10 +280,7 @@          showFFloat (Just 4) (fromJust note1) $ show 0, "vol","0.5"] ""       oberSoXSynthHelp v0       oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+    mixTest  -- | For the given frequency it generates a musical sound with a timbre. The main component of the sound includes the lower pure quint, -- which can be in the same octave or in the one with the number lower by one. Please, check before executing@@ -461,10 +301,7 @@   _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test01.wav", "synth", "0.5","sine", showFFloat (Just 4) note0 $ show 0, "synth", "0.5","sine", "mix", showFFloat (Just 4) note1 $ show 0, "vol","0.5"] ""   oberSoXSynthHelp v0   oberSoXSynthHelp2 v1-  paths0 <- listDirectory "."-  let paths = sort . filter (isPrefixOf "test") $ paths0-  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-  mapM_ removeFile paths  +  mixTest   -- | Function to create a melody for the given arguments. 'String' is used to provide a rhythm. The main component of the sound includes the lower pure quint, which -- can be in the same octave or in the one with the number lower by one. The first 'Double' argument from the range [0.01..1.0] is used as a maximum amplitude@@ -494,10 +331,7 @@           soxSynthHelpMain note0 note1           oberSoXSynthHelpN v0           oberSoXSynthHelpN2 v1-          paths0 <- listDirectory "."-          let paths = sort . filter (isPrefixOf "test") $ paths0-          _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result0" ++ prependZeroes zeroN (show j) ++ ".wav","vol","0.3"]) ""-          mapM_ removeFile paths) vec0+          mixTest) vec0  | otherwise = let ampL1 = ampL - (fromIntegral . truncate $ ampL) in     if abs ampL1 < 0.01 then oberSoXSynthN n 0.01 time3 zs vec0     else oberSoXSynthN n ampL1 time3 zs vec0@@ -530,11 +364,7 @@           soxSynthHelpMain note0 note1           oberSoXSynthHelpN v0           oberSoXSynthHelpN2 v1-          paths0 <- listDirectory "."-          let paths = sort . filter (isPrefixOf "test") $ paths0-          _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result0" ++ prependZeroes zeroN (show j) ++ ".wav","vol",-              "0.3"]) ""-          mapM_ removeFile paths) vec0+          mixTest) vec0  | otherwise = let ampL1 = ampL - (fromIntegral . truncate $ ampL) in     if abs ampL1 < 0.01 then oberSoXSynthN2 n 0.01 time3 zs tts vec0     else oberSoXSynthN2 n ampL1 time3 zs tts vec0    @@ -597,28 +427,8 @@ -- -- For better usage the 'FilePath' should be a filepath for the .wav file. oberSoXSynthNGen :: FilePath -> Int -> Double -> Double -> String -> IO ()-oberSoXSynthNGen file m ampL time3 zs = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-    "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  oberSoXSynthN n ampL time3 zs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."-+oberSoXSynthNGen file m ampL time3 zs = oberSoXSynthNGenE file m 12 ampL time3 zs+   -- | Similar to 'oberSoXSynthNGen', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'oberSoXSynthNGen'. To obtain -- its modifications, please, use 2, 3, 4, 6, or 9.@@ -626,24 +436,10 @@ oberSoXSynthNGenE file m ku ampL time3 zs = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-    "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   oberSoXSynthN n ampL time3 zs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."        +  endFromResult         -- | Similar to 'oberSoXSynthN2', but uses a sound file to obtain the information analogous to 'V.Vector' in the latter one. Besides, the function lifts -- the frequencies to the octave with the given by 'Int' parameter number (better to use from the range [1..8]). The first 'Double' argument from@@ -655,28 +451,8 @@ -- For better usage the 'FilePath' should be a filepath for the .wav file. -- The second 'String' argument is used to define signs of the harmonics coefficients in the generated sounds. oberSoXSynthNGen2 :: FilePath -> Int -> Double -> Double -> String -> String -> IO ()-oberSoXSynthNGen2 file m ampL time3 zs tts = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4)-    (fromIntegral k * 0.001) $ show 0, "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  oberSoXSynthN2 n ampL time3 zs tts vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."-+oberSoXSynthNGen2 file m ampL time3 zs tts = oberSoXSynthNGen2E file m 12 ampL time3 zs tts+   -- | Similar to 'oberSoXSynthNGen2', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'oberSoXSynthNGen2'. To obtain -- its modifications, please, use 2, 3, 4, 6, or 9.@@ -684,24 +460,10 @@ oberSoXSynthNGen2E file m ku ampL time3 zs tts = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4)-    (fromIntegral k * 0.001) $ show 0, "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku. V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   oberSoXSynthN2 n ampL time3 zs tts vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."        +  endFromResult        -- | Similar to 'oberSoXSynthN2', but uses a sound file to obtain the information analogous to 'V.Vector' in the latter one. Besides, the function lifts -- the frequencies to the octave with the given by 'Int' parameter number (better to use from the range [1..8]). The first 'Double' argument from@@ -717,28 +479,8 @@ -- the main note. If it is rather great, it can signal that the volume for the second note obertones are greater than for the main note obetones. -- The last one is experimental feature. oberSoXSynthNGen3 :: FilePath -> Int -> Double -> Double -> Double -> String -> String -> String -> IO ()-oberSoXSynthNGen3 file m ampL time3 dAmpl zs tts vs = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4)-    (fromIntegral k * 0.001) $ show 0, "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  oberSoXSynthN3 n ampL time3 dAmpl zs tts vs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."-+oberSoXSynthNGen3 file m ampL time3 dAmpl zs tts vs = oberSoXSynthNGen3E file m 12 ampL time3 dAmpl zs tts vs+   -- | Similar to 'oberSoXSynthNGen3', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'oberSoXSynthNGen3'. To obtain -- its modifications, please, use 2, 3, 4, 6, or 9.@@ -746,47 +488,11 @@ oberSoXSynthNGen3E file m ku ampL time3 dAmpl zs tts vs = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do { (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4)-    (fromIntegral k * 0.001) $ show 0, "0.001", "stat"] ""-  ; let line0s = lines herr-        noteN1  = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-  ; if null noteN1 then return (11440::Int)-      else let noteN2  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN2 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   oberSoXSynthN3 n ampL time3 dAmpl zs tts vs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."        -  --- | Additional function to prepend zeroes to the given 'String'. The number of them are just that one to fulfill the length to the given 'Int' parameter.-prependZeroes :: Int -> String -> String -prependZeroes n xs -  | if compare n 0 /= GT || null xs then True else compare n (length xs) /= GT = xs-  | otherwise = replicate (n - length xs) '0' ++ xs-{-# INLINE prependZeroes #-}  --nOfZeroesLog :: Int -> Maybe Int-nOfZeroesLog x-  | compare x 0 /= GT = Nothing-  | otherwise = Just (truncate (log (fromIntegral x) / log 10) + 1)-{-# INLINE nOfZeroesLog #-}  ---- | Is a minimal number of decimal places that are just enough to represent a length of the 'V.Vector' given. For an 'V.empty' returns 0.-numVZeroesPre :: V.Vector a -> Int-numVZeroesPre v =-  let xx = nOfZeroesLog . V.length $ v in-    if isJust xx-      then fromJust xx-      else 0::Int-{-# INLINE numVZeroesPre #-}      -  +  endFromResult+     -- | For the given frequency and a Ukrainian text it generates a musical sound with the timbre obtained from the Ukrainian text (see the -- documentation for @mmsyn7s@ package). The timbre for another given text usually differs, but can be the same. The last one is only -- if the uniqueness structure and length are the same for both 'String'. Otherwise, they differs. This gives an opportunity to practically@@ -808,10 +514,7 @@   _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test-.wav", "synth", "0.5","sine", showFFloat (Just 4) note0 $ show 0, "synth", "0.5","sine", "mix", showFFloat (Just 4) note1 $ show 0, "vol","0.5"] ""   uniqOberSoXSynthHelp v0   uniqOberSoXSynthHelp2 v1-  paths0 <- listDirectory "."-  let paths = sort . filter (isPrefixOf "test") $ paths0-  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-  mapM_ removeFile paths+  mixTest  -- | For the given frequency and a Ukrainian text it generates a musical sound with the timbre obtained from the Ukrainian text (see the -- documentation for @mmsyn7s@ package). The timbre for another given text usually differs, but can be the same. The last one is only@@ -836,10 +539,7 @@      "0.5","sine", "mix", showFFloat (Just 4) note1 $ show 0, "vol","0.5"] ""   uniqOberSoXSynthHelp v0   uniqOberSoXSynthHelp2 v1-  paths0 <- listDirectory "."-  let paths = sort . filter (isPrefixOf "test") $ paths0-  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-  mapM_ removeFile paths  +  mixTest  -- | Function to create a melody for the given arguments. The first 'String' is used to provide a rhythm. The second one -- to provide a timbre. -- The timbre for another given text usually differs, but can be the same. This gives an opportunity to practically and quickly@@ -868,15 +568,11 @@                    "sine", showFFloat (Just 4) noteN $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l * ampL) $ show 0] "") vec                soxSynthHelpMain note01 note02 = readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA" ++                  prependZeroes zeroN "1" ++  ".wav", "synth", showFFloat (Just 4) (V.unsafeIndex v2 (j `rem` m)) $ show 0,"sine", showFFloat (Just 4) note01 $-                  show 0, "synth", showFFloat (Just 4) (V.unsafeIndex v2 (j `rem` m)) $ show 0,"sine", "mix", showFFloat (Just 4) note02 $ show 0, "vol","0.5"]-                    ""+                  show 0, "synth", showFFloat (Just 4) (V.unsafeIndex v2 (j `rem` m)) $ show 0,"sine", "mix", showFFloat (Just 4) note02 $ show 0, "vol","0.5"]  ""           soxSynthHelpMain note0 note1           uniqOberSoXSynthHelpN v0           uniqOberSoXSynthHelpN2 v1-          paths0 <- listDirectory "."-          let paths = sort . filter (isPrefixOf "test") $ paths0-          _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result0" ++ prependZeroes zeroN (show j) ++ ".wav","vol","0.3"]) ""-          mapM_ removeFile paths) vec0+          mixTest) vec0  | otherwise = let ampL1 = ampL - (fromIntegral . truncate $ ampL) in     if abs ampL1 < 0.01 then uniqOberSoXSynthN n 0.01 time3 zs wws vec0     else uniqOberSoXSynthN n ampL1 time3 zs wws vec0@@ -913,10 +609,7 @@           soxSynthHelpMain note0 note1           uniqOberSoXSynthHelpN v0           uniqOberSoXSynthHelpN2 v1-          paths0 <- listDirectory "."-          let paths = sort . filter (isPrefixOf "test") $ paths0-          _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result0" ++ prependZeroes zeroN (show j) ++ ".wav","vol","0.3"]) ""-          mapM_ removeFile paths) vec0+          mixTest) vec0  | otherwise = let ampL1 = ampL - (fromIntegral . truncate $ ampL) in     if abs ampL1 < 0.01 then uniqOberSoXSynthN3 n 0.01 time3 zs wws tts vec0     else uniqOberSoXSynthN3 n ampL1 time3 zs wws tts vec0    @@ -967,35 +660,11 @@                   ; soxSynthHelpMain1 (fromJust note1)                   ; uniqOberSoXSynthHelpN v0                   ; uniqOberSoXSynthHelpN2 v1}-          paths0 <- listDirectory "."-          let paths = sort . filter (isPrefixOf "test") $ paths0-          _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result0" ++ prependZeroes zeroN (show j) ++ ".wav","vol","0.3"]) ""-          mapM_ removeFile paths) vec0+          mixTest) vec0  | otherwise = let ampL1 = ampL - (fromIntegral . truncate $ ampL) in     if abs ampL1 < 0.01 then uniqOberSoXSynthN4 n 0.01 time3 dAmpl zs wws tts vs vec0     else uniqOberSoXSynthN4 n ampL1 time3 dAmpl zs wws tts vs vec0     --- | Function is used to get numbers of intervals from a Ukrainian 'String'. It is used internally in the 'uniqOberSoXSynthN4' function.-intervalsFromString :: String -> V.Vector Int-intervalsFromString vs = vStrToVInt . convertToProperUkrainian $ vs--vStrToVInt :: V.Vector String -> V.Vector Int-vStrToVInt = V.map strToInt--strToInt :: String -> Int-strToInt =-  getBFst' (0, V.fromList [("а", 12), ("б", 4), ("в", 7), ("г", 3), ("д", 4), ("дж", 5), ("дз", 5), ("е", 12), ("ж", 3), ("з", 8), ("и", 12), -    ("й", 7), ("к", 10), ("л", 7), ("м", 7), ("н", 7), ("о", 12), ("п", 10), ("р", 7), ("с", 10), ("т", 2), ("у", 12), ("ф", 2), ("х", 2),-      ("ц", 11), ("ч", 11), ("ш", 1), ("і", 12), ("ґ", 9)])-{-# INLINE strToInt #-}      ---- | Function to get from the number of semi-tones and a note a 'Maybe' note for the second lower note in the interval if any. If there is--- no need to obtain such a note, then the result is 'Nothing'.-dNote :: Int -> Double -> Maybe Double-dNote n note-  | n == 0 || compare note (V.unsafeIndex notes 0) == LT || compare note (V.unsafeIndex notes 107) == GT = Nothing-  | otherwise = Just (note / 2 ** (fromIntegral n / 12))- -- | Similar to 'uniqOberSoXSynthN', but uses a sound file to obtain the information analogous to 'V.Vector' in the latter one.  -- Besides, the function lifts the frequencies to the octave with the given by 'Int' parameter number (better to use from the range [1..8]). -- The first 'Double' argument from the range [0.01..1.0] is used as a maximum amplitude for obertones. If it is set to 1.0 the@@ -1005,28 +674,7 @@ -- -- For better usage the 'FilePath' should be a filepath for the .wav file. uniqOberSoXSynthNGen :: FilePath -> Int -> Double -> Double -> String -> String -> IO ()-uniqOberSoXSynthNGen file m ampL time3 zs wws = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  uniqOberSoXSynthN n ampL time3 zs wws vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+uniqOberSoXSynthNGen file m ampL time3 zs wws = uniqOberSoXSynthNGenE file m 12 ampL time3 zs wws  -- | Similar to 'uniqOberSoXSynthNGen', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'uniqOberSoXSynthNGen'. To obtain@@ -1035,25 +683,10 @@ uniqOberSoXSynthNGenE file m ku ampL time3 zs wws = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   uniqOberSoXSynthN n ampL time3 zs wws vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+  endFromResult  -- | Similar to 'uniqOberSoXSynthN', but uses a sound file to obtain the information analogous to 'V.Vector' in the latter one.  -- Besides, the function lifts the frequencies to the octave with the given by 'Int' parameter number (better to use from the range [1..8]).@@ -1065,28 +698,7 @@ -- For better usage the 'FilePath' should be a filepath for the .wav file. -- The third 'String' argument is used to define signs of the harmonics coefficients in the generated sounds. uniqOberSoXSynthNGen3 :: FilePath -> Int -> Double -> Double -> String -> String -> String -> IO ()-uniqOberSoXSynthNGen3 file m ampL time3 zs wws tts = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  uniqOberSoXSynthN3 n ampL time3 zs wws tts vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+uniqOberSoXSynthNGen3 file m ampL time3 zs wws tts = uniqOberSoXSynthNGen3E file m 12 ampL time3 zs wws tts  -- | Similar to 'uniqOberSoXSynthNGen3', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'uniqOberSoXSynthNGen3'. To obtain@@ -1095,25 +707,10 @@ uniqOberSoXSynthNGen3E file m ku ampL time3 zs wws tts = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   uniqOberSoXSynthN3 n ampL time3 zs wws tts vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."        +  endFromResult  -- | Similar to 'uniqOberSoXSynthN', but uses a sound file to obtain the information analogous to 'V.Vector' in the latter one.  -- Besides, the function lifts the frequencies to the octave with the given by 'Int' parameter number (better to use from the range [1..8]).@@ -1129,28 +726,7 @@ -- the main note. If it is rather great, it can signal that the volume for the second note obertones are greater than for the main note obetones. -- The last one is an experimental feature. uniqOberSoXSynthNGen4 :: FilePath -> Int -> Double -> Double -> Double -> String -> String -> String -> String -> IO ()-uniqOberSoXSynthNGen4 file m ampL time3 dAmpl zs wws tts vs = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })-  let vecB = liftInOctaveV m . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-  uniqOberSoXSynthN4 n ampL time3 dAmpl zs wws tts vs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+uniqOberSoXSynthNGen4 file m ampL time3 dAmpl zs wws tts vs = uniqOberSoXSynthNGen4E file m 12 ampL time3 dAmpl zs wws tts vs  -- | Similar to 'uniqOberSoXSynthNGen4', but uses additional second 'Int' parameter. It defines, to which n-th elements set (see 'nkyT') belongs the obtained -- higher notes in the intervals. If that parameter equals to 12, then the function is practically equivalent to 'uniqOberSoXSynthNGen4'. To obtain@@ -1159,22 +735,7 @@ uniqOberSoXSynthNGen4E file m ku ampL time3 dAmpl zs wws tts vs = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA   uniqOberSoXSynthN4 n ampL time3 dAmpl zs wws tts vs vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+  endFromResult
DobutokO/Sound/Functional.hs view
@@ -12,8 +12,34 @@ {-# OPTIONS_GHC -threaded #-}  module DobutokO.Sound.Functional (+  -- * Type synonyms with different semantics+  SoundsO+  , ObertonesO+  , NotePairs+  -- * Work with notes (general)+  , notes+  , neighbourNotes+  , closestNote+  , pureQuintNote +  , oberTones +  -- * Work with enky (extension to octaves functionality)+  , nkyT+  , whichEnka +  , enkuUp +  , enkuDown +  , liftInEnkuV+  , liftInEnku+  -- ** Work with octaves+  , octavesT+  -- * Combining intermediate files+  , mixTest+  -- * Working with files+  , freqsFromFile+  , endFromResult+  -- * Work with obertones+  , oberSoXSynth    -- * Use additional function as a parameter-  oberSoXSynth2FDN+  , oberSoXSynth2FDN   , oberSoXSynth2FDN_B   -- ** Just simple function application   , oberSoXSynth2FDN_S@@ -26,21 +52,49 @@   , oberSoXSynthGen2FDN_S   , oberSoXSynthGen2FDN_Sf   , oberSoXSynthGen2FDN_Sf3+  , dNote+  -- * Generalized functions with several functional parameters+  , soundGenF3+  , oberSoXSynthGen2FDN_SG+  , oberSoXSynthGen2FDN_Sf3G+  -- ** Auxiliary functions+  , partialTest_k+  , prependZeroes +  , nOfZeroesLog +  , numVZeroesPre+  , syllableStr +  , intervalsFromString +  , vStrToVInt +  , strToInt+  , doubleVecFromVecOfDouble+  , helpF1+  , helpF0 ) where +import CaseBi (getBFst') import Data.Char (isDigit) import System.Exit (ExitCode( ExitSuccess )) import Numeric import Data.List (isPrefixOf,sort,sortBy,nubBy)-import Data.Maybe (isNothing,fromJust)+import Data.Maybe (isNothing,fromJust,isJust) import qualified Data.Vector as V import System.Process import EndOfExe import System.Directory-import Melodics.Ukrainian+import Melodics.Ukrainian (convertToProperUkrainian) import SoXBasics (durationA)-import DobutokO.Sound hiding (oberSoXSynth2FDN)+import MMSyn7.Syllable  +-- | Is used to represent a sequence of intervals, each note being a 'Double' value (its frequency in Hz).+type SoundsO = V.Vector (Double, Double)++-- | Is used to represent a set of obertones for the single sound, the first 'Double' value is a frequency and the second one -- an amplitude.+type ObertonesO = V.Vector (Double, Double)++-- | Is used to represent a set of pairs of notes for each element of which the 'Double' values (notes frequencies in Hz) are somewhat+-- musically connected one with another..+type NotePairs = V.Vector (Double, Double)+ -- | Similar to 'oberSoXSynth2DN' but instead of 'oberTones' function, it uses volatile function @f::Double -> Vector (Double, Double)@ with -- somewhat sophisticated mechanism to normalize the resulting 'V.Vector' elements @(Double, Double)@. The last one is an experimental feature, so -- it is your responsibility to provide a function so that it does not lead to clipping. In such a case, the result of application of the@@ -51,7 +105,7 @@ -- But for a lot of functions this works well. --  -- It is recommended to fully simplify the computation for \"f\" function before using it in the 'oberSoXSynth2FDN'.-oberSoXSynth2FDN :: (Double -> V.Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()+oberSoXSynth2FDN :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO () oberSoXSynth2FDN f (x, y) j zs  | V.null . convertToProperUkrainian $ zs = oberSoXSynth x  | otherwise = do@@ -84,19 +138,20 @@          showFFloat (Just 4) (fromJust note1) $ show 0] ""       oberSoXSynthHelp v0       oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+    mixTest  -- | Uses additional 'Int' parameters. The first one is a number of enka (see 'nkyT'). The second one defines, to which n-th elements set -- (see 'nkyT') belongs the obtained higher notes in the intervals. To obtain reasonable results, please, use for the first one 2, 3, 4, 6, 9, or 12. -- The first 'String' parameter is used to produce durations of the notes. The second one is used to define intervals. A 'Double' parameter is a -- basic sound duration, it defines tempo of the melody in general.-oberSoXSynthGen2FDN :: FilePath -> Int -> Int -> (Double -> V.Vector (Double, Double)) -> Double -> String -> String -> IO ()-oberSoXSynthGen2FDN file m ku f y zs wws = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)+oberSoXSynthGen2FDN :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO ()+oberSoXSynthGen2FDN file m ku f y zs wws = oberSoXSynthGen2FDN_SG file m ku f y zs wws oberSoXSynth2FDN++-- | Gets 'V.Vector' of 'Int' frequencies from the given 'FilePath' using SoX. The frequencies are \"rough\" according to the SoX documentation and+-- the duration is too small so they can be definitely other than expected ones. Is used as a source of variable numbers (somewhat close each to another+-- in their order but not neccessarily). .+freqsFromFile :: FilePath -> Int -> IO (V.Vector Int)+freqsFromFile file n = do   vecA <- V.generateM n (\k -> do {     (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,       "0.001", "stat"] ""@@ -104,13 +159,22 @@           noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s     ; if null noteN0 then return (11440::Int)       else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })-  let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-      (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 -> do-        oberSoXSynth2FDN f (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j wws-        renameFile "result.wav" $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB+  return vecA++-- | Combines (mixes) all \"test\*" files in the given directory. The files should be similar in parameters and must be sound files for SoX to work+-- on them properly. Afterwards, the function deletes these combined files.+mixTest :: IO ()+mixTest = do+  paths0 <- listDirectory "."+  let paths = sort . filter (isPrefixOf "test") $ paths0+  _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""+  mapM_ removeFile paths++-- | Gets an \"end.wav\" file from the intermediate \"result\*.wav\" files in the current directory. If it is not successful, produces the notification+-- message and exits without error. If you would like to create the file if there are too many intermediate ones, please, run+-- \"dobutokO2 8\" or \"dobutokO2 80\" in the current directory.+endFromResult :: IO ()+endFromResult = do   path2s <- listDirectory "."   let paths3 = sort . filter (isPrefixOf "result") $ path2s   (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""@@ -120,8 +184,33 @@       exi <- doesFileExist "end.wav"       if exi then removeFile "end.wav"       else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+        putStrLn "Use them manually as needed."     +-- | Creates part of the needed \"test\*\.wav" files in the current directory. +partialTest_k :: ObertonesO -> Int -> String -> IO ()+partialTest_k vec k ts =+  let l     = V.length vec+      zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 50 == 0+    then do+      path1s <- listDirectory "."+      let path2s = sort . filter (isPrefixOf $ "test" ++ show k) $ path1s+      (code,_,herr0) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path2s ++ ["test-" ++ show k ++ prependZeroes zeroN+        (show (i `quot` 50)) ++ ".wav"]) ""+      case code of+        ExitSuccess -> mapM_ removeFile path2s+        _           -> do+          exi <- doesFileExist $ "test-" ++ show k ++ prependZeroes zeroN (show (i `quot` 50)) ++ ".wav"+          if exi then putStrLn ("DobutokO.Sound.Functional.partialTest_k: " ++ herr0) >> (removeFile $ "test-" ++ show k ++ prependZeroes zeroN (show (i `quot` 50)) ++ ".wav")+          else putStrLn $ "DobutokO.Sound.Functional.partialTest_k: " ++ herr0+    else readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test" ++ show k ++ show (i + 2) ++ ".wav", "synth", ts,"sine",+      showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] "" >> putStr "") vec++-- | Generates a 'V.Vector' of 'ObertonesO' that represents the melody. The order of elements of the vector is the order of the melody.+doubleVecFromVecOfDouble :: (Double -> ObertonesO) -> Double -> V.Vector (Maybe Double) -> V.Vector ObertonesO+doubleVecFromVecOfDouble f t0 vec =+  V.map (\note1 -> if isNothing note1 then V.empty else V.filter (\(_,!z) -> compare (abs z) t0 == GT) . f . fromJust $ note1) vec+   + -- | Similar to 'oberSoXSynth2DN' but instead of 'oberTones' function, it uses volatile function @f::Double -> Vector (Double, Double)@ with -- somewhat sophisticated mechanism to normalize the resulting 'V.Vector' elements @(Double, Double)@. The last one is experimental feature, so -- it is your responsibility to provide a function so that it does not lead to clipping. In such a case, the result of application of the@@ -134,7 +223,7 @@ -- But for a lot of functions this works well. --  -- It is recommended to fully simplify the computation for \"f\" function before using it in the 'oberSoXSynth2FDN_B'.-oberSoXSynth2FDN_B :: (Double -> V.Vector (Double, Double)) -> (Double, Double, Double) -> Int -> String -> IO ()+oberSoXSynth2FDN_B :: (Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO () oberSoXSynth2FDN_B f (x, y, limB) j zs  | V.null . convertToProperUkrainian $ zs = oberSoXSynth x  | otherwise = do@@ -169,44 +258,15 @@          showFFloat (Just 4) (fromJust note1) $ show 0] ""       oberSoXSynthHelp v0       oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+    mixTest  -- | Uses additional 'Int' parameters. The first one is a number of enka (see 'nkyT'). The second one defines, to which n-th elements set -- (see 'nkyT') belongs the obtained higher notes in the intervals. To obtain reasonable results, please, use for the first one 2, 3, 4, 6, 9, or 12. -- The first 'String' parameter is used to produce durations of the notes. The second one is used to define intervals. The first 'Double' parameter is a -- basic sound duration, it defines tempo of the melody in general. The second one is a limit for frequencies difference in Hz to be filtered out from the -- resulting sound. It is considered to be from the range @[0.1..10.0]@.-oberSoXSynthGen2FDN_B :: FilePath -> Int -> Int -> (Double -> V.Vector (Double, Double)) -> Double -> Double -> String -> String -> IO ()-oberSoXSynthGen2FDN_B file m ku f y limB zs wws = do-  duration0 <- durationA file-  let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })-  let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA-      (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 -> do-        oberSoXSynth2FDN_B f (x, (V.unsafeIndex v2 (j `rem` (V.length v2))), limB) j wws-        renameFile "result.wav" $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+oberSoXSynthGen2FDN_B :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String -> IO ()+oberSoXSynthGen2FDN_B file m ku f y limB zs wws = oberSoXSynthGen2FDN_Sf3G file m ku f y limB zs wws oberSoXSynth2FDN_B  -- | Similar to 'oberSoXSynth2FDN' but it does not make any normalizing transformations with the 'V.Vector' argument. To be used properly, it is needed -- that every second element in the tuple in the 'V.Vector' argument must be in the range [-1.0..1.0] and every first element must be in between@@ -217,7 +277,7 @@ -- But for a lot of functions this works well. --  -- It is recommended to fully simplify the computation for \"f\" function before using it in the 'oberSoXSynth2FDN_S'.-oberSoXSynth2FDN_S :: (Double -> V.Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()+oberSoXSynth2FDN_S :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO () oberSoXSynth2FDN_S f (x, y) j zs  | V.null . convertToProperUkrainian $ zs = oberSoXSynth x  | otherwise = do@@ -228,84 +288,36 @@         v1    = if isNothing note1 then V.empty                 else f . fromJust $ note1         ts = showFFloat (Just 4) (abs y) $ show 0-        oberSoXSynthHelp vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path1s <- listDirectory "."-              let path2s = sort . filter (isPrefixOf "test0") $ path1s-              (code,_,herr0) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path2s ++ ["test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code of-                ExitSuccess -> mapM_ removeFile path2s-                _           -> do-                   exi <- doesFileExist $ "test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 161: " ++ herr0) >> removeFile ("test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStrLn $ "Line 162: " ++ herr0 -            else do-            _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test0" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] ""-            return ()) vec-        oberSoXSynthHelp2 vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path3s <- listDirectory "."-              let path4s = sort . filter (isPrefixOf "test1") $ path3s-              (code2,_,herr2) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path4s ++ ["test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code2 of-                ExitSuccess -> mapM_ removeFile path4s-                _           -> do-                   exi <- doesFileExist $ "test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 177: " ++ herr2) >> removeFile ("test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStrLn $ "Line 178: " ++ herr2-            else do-            _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test1" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] ""-            return ()) vec     _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",        showFFloat (Just 4) note0 $ show 0] ""     if isNothing note1 then do-      oberSoXSynthHelp v0+      partialTest_k v0 0 ts     else do        _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testB.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",          showFFloat (Just 4) (fromJust note1) $ show 0] ""-      oberSoXSynthHelp v0-      oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+      partialTest_k v0 0 ts+      partialTest_k v1 1 ts+    mixTest  -- | Similar to 'oberSoXSynthGen2FDN', but instead of 'oberSoXSynth2FDN' uses 'oberSoXSynth2FDN_S' function. -oberSoXSynthGen2FDN_S :: FilePath -> Int -> Int -> (Double -> V.Vector (Double, Double)) -> Double -> String -> String -> IO ()-oberSoXSynthGen2FDN_S file m ku f y zs wws = do+oberSoXSynthGen2FDN_SG :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> ((Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()) -> IO ()+oberSoXSynthGen2FDN_SG file m ku f y zs wws h = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA       (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 -> do-        oberSoXSynth2FDN_S f (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j wws+        h f (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j wws         renameFile "result.wav" $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."+  endFromResult +-- | Similar to 'oberSoXSynthGen2FDN', but instead of 'oberSoXSynth2FDN' uses 'oberSoXSynth2FDN_S' function. +oberSoXSynthGen2FDN_S :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO ()+oberSoXSynthGen2FDN_S file m ku f y zs wws = oberSoXSynthGen2FDN_SG file m ku f y zs wws oberSoXSynth2FDN_S+ -- | Similar to 'oberSoXSynth2FDN_S' but additionally the program filters out from the resulting 'V.Vector' after \"f\" application values that are smaller -- by absolute value than 0.001. An 'Int' parameter is used to define an interval. To obtain compatible with versions prior to -- 0.20.0.0 behaviour, use for the 'Int' 0.@@ -314,73 +326,15 @@ -- But for a lot of functions this works well. --  -- It is recommended to fully simplify the computation for \"f\" function before using it in the 'oberSoXSynth2FDN_Sf'.-oberSoXSynth2FDN_Sf :: (Double -> V.Vector (Double, Double)) -> (Double, Double) -> Int -> String -> IO ()-oberSoXSynth2FDN_Sf f (x, y) j zs- | V.null . convertToProperUkrainian $ zs = oberSoXSynth x- | otherwise = do-    let note0 = closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0)-        l0    = length zs-        note1 = dNote (V.unsafeIndex (intervalsFromString zs) (abs (j `rem` l0))) note0-        v0    = V.filter (\(_,!z) -> compare (abs z) 0.001 == GT) . f $ note0-        v1    = if isNothing note1 then V.empty-                else V.filter (\(_,!z) -> compare z 0.001 == GT) . f . fromJust $ note1-        ts = showFFloat (Just 4) (abs y) $ show 0 -        oberSoXSynthHelp vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path1s <- listDirectory "."-              let path2s = sort . filter (isPrefixOf "test0") $ path1s-              (code,_,herr0) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path2s ++ ["test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code of-                ExitSuccess -> mapM_ removeFile path2s-                _           -> do-                   exi <- doesFileExist $ "test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 224: " ++ herr0) >> (removeFile $ "test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStrLn $ "Line 225: " ++ herr0-            else readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test0" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] "" >> putStr "") vec-        oberSoXSynthHelp2 vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path3s <- listDirectory "."-              let path4s = sort . filter (isPrefixOf "test1") $ path3s-              (code,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path4s ++ ["test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code of-                ExitSuccess -> mapM_ removeFile path4s-                _           -> do-                   exi <- doesFileExist $ "test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 239: " ++ herr1) >> (removeFile $ "test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStr $ "Line 240: " ++ herr1-            else readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test1" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] "" >> putStr "") vec-    _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",-       showFFloat (Just 4) note0 $ show 0] ""-    if isNothing note1 then do-      oberSoXSynthHelp v0-    else do -      _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testB.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",-         showFFloat (Just 4) (fromJust note1) $ show 0] ""-      oberSoXSynthHelp v0-      oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+oberSoXSynth2FDN_Sf :: (Double -> ObertonesO) -> (Double, Double) -> Int -> String -> IO ()+oberSoXSynth2FDN_Sf f (x, y) j zs = oberSoXSynth2FDN_Sf3 f (x, y, 0.001) j zs  -- | Similar to 'oberSoXSynthGen2FDN_S', but instead of 'oberSoXSynth2FDN_S' uses 'oberSoXSynth2FDN_Sf' function. -oberSoXSynthGen2FDN_Sf :: FilePath -> Int -> Int -> (Double -> V.Vector (Double, Double)) -> Double -> String -> String -> IO ()+oberSoXSynthGen2FDN_Sf :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> String -> String -> IO () oberSoXSynthGen2FDN_Sf file m ku f y zs wws = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA       (t, ws) = splitAt 1 . syllableStr n $ zs       m0    = length ws@@ -388,16 +342,7 @@       v2    = V.map (\yy -> y * fromIntegral (yy * m0) / fromIntegral (head t)) . V.fromList $ ws in V.imapM_ (\j x -> do         oberSoXSynth2FDN_Sf f (x, (V.unsafeIndex v2 (j `rem` (V.length v2)))) j wws         renameFile "result.wav" $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."    +  endFromResult     -- | Similar to 'oberSoXSynth2FDN_S' but additionally the program filters out from the resulting 'V.Vector' after \"f\" application values that are smaller -- than the third 'Double' parameter by an absolute value in the triple of @Double@'s. An 'Int' parameter is used to define an interval. To obtain compatible@@ -407,87 +352,239 @@ -- But for a lot of functions this works well. --  -- It is recommended to fully simplify the computation for \"f\" function before using it in the 'oberSoXSynth2FDN_Sf3'.-oberSoXSynth2FDN_Sf3 :: (Double -> V.Vector (Double, Double)) -> (Double, Double, Double) -> Int -> String -> IO ()+oberSoXSynth2FDN_Sf3 :: (Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO () oberSoXSynth2FDN_Sf3 f (x, y, t0) j zs  | V.null . convertToProperUkrainian $ zs = oberSoXSynth x  | otherwise = do-    let note0 = closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0)-        l0    = length zs-        note1 = dNote (V.unsafeIndex (intervalsFromString zs) (abs (j `rem` l0))) note0-        v0    = V.filter (\(_,!z) -> compare (abs z) t0 == GT) . f $ note0-        v1    = if isNothing note1 then V.empty-                else V.filter (\(_,!z) -> compare (abs z) t0 == GT) . f . fromJust $ note1-        ts = showFFloat (Just 4) (abs y) $ show 0 -        oberSoXSynthHelp vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path1s <- listDirectory "."-              let path2s = sort . filter (isPrefixOf "test0") $ path1s-              (code,_,herr0) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path2s ++ ["test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code of-                ExitSuccess -> mapM_ removeFile path2s-                _           -> do-                   exi <- doesFileExist $ "test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 285: " ++ herr0) >> (removeFile $ "test-0" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStrLn $ "Line 286: " ++ herr0-            else readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test0" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] "" >> putStr "") vec-        oberSoXSynthHelp2 vec =-          let l     = V.length vec-              zeroN = numVZeroesPre vec in V.imapM_ (\i (noteN, !amplN) -> if i /= 0 && i `rem` 250 == 0-            then do-              path3s <- listDirectory "."-              let path4s = sort . filter (isPrefixOf "test1") $ path3s-              (code,_,herr1) <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ path4s ++ ["test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"]) ""-              case code of-                ExitSuccess -> mapM_ removeFile path4s-                _           -> do-                   exi <- doesFileExist $ "test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav"-                   if exi then putStrLn ("Line 300: " ++ herr1) >> (removeFile $ "test-1" ++ prependZeroes zeroN (show (i `quot` 250)) ++ ".wav")-                   else putStrLn $ "Line 301: " ++ herr1-            else readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test1" ++ show (i + 2) ++ ".wav", "synth", ts,"sine",-              showFFloat (Just 4) (abs noteN) $ show 0, "vol", showFFloat (Just 4) (amplN / fromIntegral l) $ show 0] "" >> putStr "") vec-    _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testA.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",-       showFFloat (Just 4) note0 $ show 0] ""-    if isNothing note1 then do-      oberSoXSynthHelp v0-    else do -      _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "testB.wav", "synth", showFFloat (Just 4) (abs y) $ show 0,"sine",-         showFFloat (Just 4) (fromJust note1) $ show 0] ""-      oberSoXSynthHelp v0-      oberSoXSynthHelp2 v1-    paths0 <- listDirectory "."-    let paths = sort . filter (isPrefixOf "test") $ paths0-    _ <- readProcessWithExitCode (fromJust (showE "sox")) (["--combine", "mix"] ++ paths ++ ["result.wav","vol","0.3"]) ""-    mapM_ removeFile paths+    let l0    = length zs+    soundGenF3 (V.fromList [\x -> closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0),\x -> if isNothing $ dNote+     (V.unsafeIndex (intervalsFromString zs) (abs (j `rem` l0))) (closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0)) then V.unsafeIndex notes 0+       else fromJust $ dNote (V.unsafeIndex (intervalsFromString zs) (abs (j `rem` l0))) (closestNote (if x /= 0.0 then abs x else V.unsafeIndex notes 0))])+         (V.replicate 2 x) (V.fromList [1,V.unsafeIndex (intervalsFromString zs) (abs (j `rem` l0))]) f (x, y, t0) j zs+    mixTest +helpF1 :: V.Vector (Double -> Double) -> V.Vector Double -> V.Vector Int -> V.Vector (Maybe Double)+helpF1 vf vd =+  V.map (\(f1,x,i2) ->+    case i2 of+      0 -> Nothing+      _ -> Just $ f1 x) . V.zip3 vf vd++helpF0 :: Int -> String+helpF0 =+  getBFst' ("ZZ0",V.fromList . zip [0..] $ (map (:[]) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ concatMap (\z -> map (z:) . map (:[]) $ "ABCDEFGHIJKLMNOPQRSTUVWXYZ")+     "ABCDEFGHIJKLMNOPQRSTUVWXYZ")) ++-- | Can generates multiple notes with their respective obertones that are played simultaneously (e. g. it can be just one note with obertones,+-- an interval with obertones, an accord with obertones etc.). This allows to get a rather complex or even comlicated behaviour to obtain expressive+-- and rich sound.+soundGenF3 :: V.Vector (Double -> Double) -> V.Vector Double -> V.Vector Int -> (Double -> ObertonesO) -> (Double, Double, Double) -> Int ->+  String -> IO ()+soundGenF3 vf vd vi f (x, y, t0) j zs = do+  let vD = helpF1 vf vd vi   -- Vector of notes played simultaneously (e. g. just one, interval, accord etc.)+      vDz = V.mapMaybe id $ vD -- The previous one without Nothings and Justs+      ilDz = V.length vDz - 1+      vNotes = doubleVecFromVecOfDouble f t0 (V.map (Just) vDz) -- Vector of vectors of pairs (freq,ampl) -- notes and their absence (V.empty) with obertones+      l0 = length zs+      ts = showFFloat (Just 4) (abs y) $ show 0 -- duration of the sound to be generated+  V.imapM_ (\i note1 -> do+    _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test" ++ helpF0 i ++ ".wav", "synth", showFFloat (Just 4) (abs y) $ show 0,+        "sine", showFFloat (Just 4) (V.unsafeIndex vDz i) $ show 0] ""+    partialTest_k (V.unsafeIndex vNotes i) i ts) vDz+       -- | Similar to 'oberSoXSynthGen2FDN_S', but instead of 'oberSoXSynth2FDN_S' uses 'oberSoXSynth2FDN_Sf3' function. -oberSoXSynthGen2FDN_Sf3 :: FilePath -> Int -> Int -> (Double -> V.Vector (Double, Double)) -> Double -> Double -> String -> String -> IO ()-oberSoXSynthGen2FDN_Sf3 file m ku f y t0 zs wws = do+oberSoXSynthGen2FDN_Sf3 :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String -> IO ()+oberSoXSynthGen2FDN_Sf3 file m ku f y t0 zs wws = oberSoXSynthGen2FDN_Sf3G file m ku f y t0 zs wws oberSoXSynth2FDN_Sf3++-- | Similar to 'oberSoXSynthGen2FDN_S', but instead of 'oberSoXSynth2FDN_S' uses 'oberSoXSynth2FDN_Sf3' function. +oberSoXSynthGen2FDN_Sf3G :: FilePath -> Int -> Int -> (Double -> ObertonesO) -> Double -> Double -> String -> String ->+ ((Double -> ObertonesO) -> (Double, Double, Double) -> Int -> String -> IO ()) -> IO ()+oberSoXSynthGen2FDN_Sf3G file m ku f y t0 zs wws h = do   duration0 <- durationA file   let n = truncate (duration0 / 0.001)-  vecA <- V.generateM n (\k -> do {-    (_, _, herr) <- readProcessWithExitCode (fromJust (showE "sox")) [file, "-n", "trim", showFFloat (Just 4) (fromIntegral k * 0.001) $ show 0,-      "0.001", "stat"] ""-    ; let line0s = lines herr-          noteN0 = takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s-    ; if null noteN0 then return (11440::Int)-      else let noteN1  = read (takeWhile isDigit . dropWhile (not . isDigit) . concat . drop 13 . take 14 $ line0s)::Int in return noteN1 })+  vecA <- freqsFromFile file n   let vecB = liftInEnkuV m ku . V.map fromIntegral . V.filter (/= (11440::Int)) $ vecA       (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 -> do-        oberSoXSynth2FDN_Sf3 f (x, (V.unsafeIndex v2 (j `rem` (V.length v2))), t0) j wws+        h f (x, (V.unsafeIndex v2 (j `rem` (V.length v2))), t0) j wws         renameFile "result.wav" $ "result0" ++ prependZeroes zeroN (show (j + 1)) ++ ".wav") vecB-  path2s <- listDirectory "."-  let paths3 = sort . filter (isPrefixOf "result") $ path2s-  (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (paths3 ++ ["end.wav"]) ""-  case code of-    ExitSuccess -> putStrLn "The final file \"end.wav\" was successfully created. You can now manually change or delete \"result*\" files in the directory. "-    _           -> do-      exi <- doesFileExist "end.wav"-      if exi then removeFile "end.wav"-      else putStr "Your final file \"end.wav\" was not created by some reason, but the intermediate files in the respective order are present. " >>-        putStrLn "Use them manually as needed."    +  endFromResult++-- | Function to get from the number of semi-tones and a note a 'Maybe' note for the second lower note in the interval if any. If there is+-- no need to obtain such a note, then the result is 'Nothing'.+dNote :: Int -> Double -> Maybe Double+dNote n note+  | n == 0 || compare note (V.unsafeIndex notes 0) == LT || compare note (V.unsafeIndex notes 107) == GT = Nothing+  | otherwise = Just (note / 2 ** (fromIntegral n / 12))++-- | 'V.Vector' of musical notes in Hz.+notes :: V.Vector Double+-- notes V.! 57 = 440.0   -- A4 in Hz+notes = V.generate 108 (\t ->  fromIntegral 440 * 2 ** (fromIntegral (t - 57) / fromIntegral 12))++-- | Function returns either the nearest two musical notes if frequency is higher than one for C0 and lower than one for B8+-- or the nearest note duplicated in a tuple.+neighbourNotes :: Double -> V.Vector Double -> (Double, Double)+neighbourNotes x v+  | compare x (V.unsafeIndex v 0) /= GT = (V.unsafeIndex v 0, V.unsafeIndex v 0)+  | compare x (V.unsafeIndex v (V.length v - 1)) /= LT = (V.unsafeIndex v (V.length v - 1), V.unsafeIndex v (V.length v - 1))+  | compare (V.length v) 2 == GT = if compare x (V.unsafeIndex  v (V.length v `quot` 2)) /= GT+      then neighbourNotes x (V.unsafeSlice 0 (V.length v `quot` 2 + 1) v)+      else neighbourNotes x (V.unsafeSlice (V.length v `quot` 2) (V.length v - (V.length v `quot` 2)) v)+  | otherwise = (V.unsafeIndex v 0, V.unsafeIndex v (V.length v - 1))++-- | Returns the closest note to the given frequency in Hz.  +closestNote :: Double -> Double+closestNote x+ | compare x 0.0 == GT =+    let (x0, x2) = neighbourNotes x notes+        r0       = x / x0+        r2       = x2 / x in +     if compare r2 r0 == GT+       then x0+       else x2+ | otherwise = 0.0++-- | Additional function to prepend zeroes to the given 'String'. The number of them are just that one to fulfill the length to the given 'Int' parameter.+prependZeroes :: Int -> String -> String +prependZeroes n xs +  | if compare n 0 /= GT || null xs then True else compare n (length xs) /= GT = xs+  | otherwise = replicate (n - length xs) '0' ++ xs+{-# INLINE prependZeroes #-}  ++nOfZeroesLog :: Int -> Maybe Int+nOfZeroesLog x+  | compare x 0 /= GT = Nothing+  | otherwise = Just (truncate (log (fromIntegral x) / log 10) + 1)+{-# INLINE nOfZeroesLog #-}  ++-- | Is a minimal number of decimal places that are just enough to represent a length of the 'V.Vector' given. For an 'V.empty' returns 0.+numVZeroesPre :: V.Vector a -> Int+numVZeroesPre v =+  let xx = nOfZeroesLog . V.length $ v in+    if isJust xx+      then fromJust xx+      else 0::Int+{-# INLINE numVZeroesPre #-}      ++-- | Function is used to generate a rhythm of the resulting file \'end.wav\' from the Ukrainian text and a number of sounds either in the syllables or in the words without vowels.+syllableStr :: Int -> String -> [Int]+syllableStr n xs =+  let ps = take n . cycle . concat . sylLengthsP2 . syllablesUkrP $ xs+      y  = sum ps in+       case y of+         0 -> [0]+         _ -> y:ps   ++-- | Similarly to 'liftInOctaveV' returns a 'V.Vector' 'Double' (actually frequencies) for the n-th elements set of notes (see 'nkyT') instead of octaves.+-- A second 'Int' parameter defines that @n@. +liftInEnkuV :: Int -> Int -> V.Vector Double -> V.Vector Double+liftInEnkuV n ku = V.mapMaybe (\z -> liftInEnku n ku z)++-- | Similarly to 'liftInOctave' returns a 'Maybe' number (actually frequency) for the n-th elements set of notes (see 'nkyT').+-- A second 'Int' parameter defines that @n@.+liftInEnku :: Int -> Int -> Double -> Maybe Double+liftInEnku n ku x+  | compare n 0 == LT || compare n ((108 `quot` ku) - 1) == GT = Nothing+  | getBFst' (False, V.fromList . zip [2,3,4,6,9,12] $ repeat True) ku && compare (closestNote x) 24.4996 == GT =+      case compare (fromJust . whichEnka ku $ x) n of+        EQ -> Just (closestNote x)+        LT -> let z  = log (V.unsafeIndex notes (n * ku) / closestNote x) / log 2.0+                  z1 = truncate z in+                   if abs (z - fromIntegral z1) > 0.999 || abs (z - fromIntegral z1) < 0.001+                     then Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) (enkuUp ku) $ closestNote x)+                     else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 2) (enkuUp ku) $ closestNote x)+        _  -> let z  = log (closestNote x / V.unsafeIndex notes (n * ku)) / log 2.0+                  z1 = truncate z in+                   if abs (z - fromIntegral z1) > 0.999 || abs (z - fromIntegral z1) < 0.001+                     then Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 2) (enkuDown ku) $ closestNote x)+                     else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) (enkuDown ku) $ closestNote x)+  | otherwise = Nothing++-- | Similarly to 'whichOctave' returns a 'Maybe' number (actually frequency) for the n-th elements set of notes (see 'nkyT').+-- An 'Int' parameter defines that @n@.+whichEnka :: Int -> Double -> Maybe Int+whichEnka n x+  | getBFst' (False,V.fromList . zip [2,3,4,6,9,12] $ repeat True) n && compare (closestNote x) 24.4996 == GT = (\t ->+     case isJust t of +       True -> fmap (\z ->+         case z of+           0 -> z+           _ -> z - 1) t+       _    -> Just ((108 `quot` n) - 1)) . V.findIndex (\(t1, t2) -> compare (closestNote x) t1 == LT) $ nkyT n+  | otherwise = Nothing++-- | Returns an analogous note in the higher n-th elements set (its frequency in Hz) (see 'nkyT'). An 'Int' parameter defines this @n@.+enkuUp  :: Int -> Double -> Double+enkuUp n x+  | getBFst' (False, V.fromList . zip [2..11] $ repeat True) n = 2 ** (fromIntegral n / fromIntegral 12) * x+  | otherwise = 2 * x+{-# INLINE enkuUp #-}  ++-- | Returns an analogous note in the lower n-th elements set (its frequency in Hz) (see 'nkyT'). An 'Int' parameter defines this @n@.+enkuDown  :: Int -> Double -> Double+enkuDown n x+  | getBFst' (False, V.fromList . zip [2..11] $ repeat True) n = 2 ** (fromIntegral (-n) / fromIntegral 12) * x+  | otherwise = x / fromIntegral 2+{-# INLINE enkuDown #-}++-- | Function is used to get numbers of intervals from a Ukrainian 'String'. It is used internally in the 'uniqOberSoXSynthN4' function.+intervalsFromString :: String -> V.Vector Int+intervalsFromString vs = vStrToVInt . convertToProperUkrainian $ vs++vStrToVInt :: V.Vector String -> V.Vector Int+vStrToVInt = V.map strToInt++strToInt :: String -> Int+strToInt =+  getBFst' (0, V.fromList [("а", 12), ("б", 4), ("в", 7), ("г", 3), ("д", 4), ("дж", 5), ("дз", 5), ("е", 12), ("ж", 3), ("з", 8), ("и", 12), +    ("й", 7), ("к", 10), ("л", 7), ("м", 7), ("н", 7), ("о", 12), ("п", 10), ("р", 7), ("с", 10), ("т", 2), ("у", 12), ("ф", 2), ("х", 2),+      ("ц", 11), ("ч", 11), ("ш", 1), ("і", 12), ("ґ", 9)])+{-# INLINE strToInt #-}       ++-- | Returns a 'V.Vector' of tuples with the lowest and highest frequencies for the notes in the sets consisting of @n@ consequential notes+-- (including semi-tones). An 'Int' parameter defines this @n@. It can be 2, 3, 4, 6, 9, or 12 (the last one is for default octaves, see 'octavesT').+-- So for different valid @n@ you obtain doubles, triples and so on. The function being applied returns a 'V.Vector' of such sets with+-- their respective lowest and highest frequencies.+nkyT :: Int -> NotePairs+nkyT n+  | getBFst' (False,V.fromList . zip [2,3,4,6,9,12] $ repeat True) n = V.generate (108 `quot` n) (\i -> (V.unsafeIndex notes (i * n),+       V.unsafeIndex notes (i * n + (n - 1))))+  | otherwise = octavesT++-- | Returns a 'V.Vector' of tuples with the lowest and highest frequencies for the notes in the octaves.+octavesT :: NotePairs+octavesT = V.generate 9 (\i -> (V.unsafeIndex notes (i * 12), V.unsafeIndex notes (i * 12 + 11)))++-- | For the given frequency it generates a musical sound with a timbre. The main component of the sound includes the lower pure quint,+-- which can be in the same octave or in the one with the number lower by one. Please, check before executing+-- whether there is no \"x.wav\", \"test*\", \"result*\" and \"end.wav\" files in the current directory, because they can be overwritten.+oberSoXSynth :: Double -> IO ()+oberSoXSynth x = do+  let note0 = if x /= 0.0 then closestNote (abs x) else V.unsafeIndex notes 0+      note1 = pureQuintNote note0+      v0    = oberTones note0+      v1    = oberTones note1+      oberSoXSynthHelp vec = let l = V.length vec in V.imapM_ (\i (noteN, !amplN) -> readProcessWithExitCode (fromJust (showE "sox"))+        ["-r22050", "-n", "test0" ++ show (i + 2) ++ ".wav", "synth", "0.5","sine", showFFloat (Just 4) noteN $ show 0, "vol", showFFloat (Just 4) (amplN /+          fromIntegral l) $ show 0] "") vec+      oberSoXSynthHelp2 vec = let l = V.length vec in V.imapM_ (\i (noteN, !amplN) -> readProcessWithExitCode (fromJust (showE "sox"))+        ["-r22050", "-n", "test1" ++ show (i + 2) ++ ".wav", "synth", "0.5","sine", showFFloat (Just 4) noteN $ show 0, "vol", showFFloat (Just 4) (amplN /+          fromIntegral l) $ show 0] "") vec+  _ <- readProcessWithExitCode (fromJust (showE "sox")) ["-r22050", "-n", "test01.wav", "synth", "0.5","sine", showFFloat (Just 4) note0 $ show 0, "synth", "0.5","sine", "mix", showFFloat (Just 4) note1 $ show 0, "vol","0.5"] ""+  oberSoXSynthHelp v0+  oberSoXSynthHelp2 v1+  mixTest ++-- | Returns a pure quint lower than the given note.+pureQuintNote :: Double -> Double+pureQuintNote x = x / 2 ** (fromIntegral 7 / fromIntegral 12)+{-# INLINE pureQuintNote #-}++-- | For the given frequency of the note it generates a 'V.Vector' of the tuples, each one of which contains the harmonics' frequency and amplitude.+oberTones :: Double -> ObertonesO+oberTones note =+  V.takeWhile (\(!w,!z) -> compare w (V.unsafeIndex notes 107) /= GT && compare (abs z) 0.001 == GT) . V.zip (V.generate 1024 (\i ->+    note * fromIntegral (i + 2))) $ (V.generate 1024 (\i -> fromIntegral 1 / fromIntegral ((i + 1) * (i + 1))))
README.markdown view
@@ -158,6 +158,12 @@ is not a '0' or '1') by simply pressing 'Enter' while being prompted and the informational message contains the line about the default value. +Starting from the version 0.21.0.0 the package extends its library functions with+the possibility to create not only single notes or intervals of sounds playing+simultaneously but also sets of three, four, five, six, seven or more sounds+played simultanously with their obertones. For more information, please, refer+to the documentation for the DobutokO.Sound.Functional module.+ ** Note:  * Better to execute in the RAM. Need rather a lot of space on the disk for
dobutokO2.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                dobutokO2-version:             0.20.0.0+version:             0.21.0.0 synopsis:            A program and a library to create experimental music from a mono audio and a Ukrainian text description:         It can also create a timbre for the notes. Uses SoX inside. homepage:            https://hackage.haskell.org/package/dobutokO2