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 +8/−0
- DobutokO/Sound.hs +49/−488
- DobutokO/Sound/Functional.hs +356/−259
- README.markdown +6/−0
- dobutokO2.cabal +1/−1
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