dobutokO2 0.14.0.0 → 0.15.0.0
raw patch · 5 files changed
+442/−56 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ DobutokO.Sound: chetvirkyTA :: Vector (Double, Double)
+ DobutokO.Sound: desyatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: dev'yatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: dviykyTA :: Vector (Double, Double)
+ DobutokO.Sound: enkuDown :: Int -> Double -> Double
+ DobutokO.Sound: enkuUp :: Int -> Double -> Double
+ DobutokO.Sound: nkyT :: Int -> Vector (Double, Double)
+ DobutokO.Sound: oberSoXSynthNGen2E :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> IO ()
+ DobutokO.Sound: oberSoXSynthNGen3E :: FilePath -> Int -> Int -> Double -> Double -> Double -> String -> String -> String -> IO ()
+ DobutokO.Sound: oberSoXSynthNGenE :: FilePath -> Int -> Int -> Double -> Double -> String -> IO ()
+ DobutokO.Sound: octavesTA :: Vector (Double, Double)
+ DobutokO.Sound: odynadtsyatkyTA :: Vector (Double, Double)
+ DobutokO.Sound: p'yatirkyTA :: Vector (Double, Double)
+ DobutokO.Sound: putInEnku :: Int -> Int -> Double -> Maybe Double
+ DobutokO.Sound: putInEnkuV :: Int -> Int -> Vector Double -> Vector Double
+ DobutokO.Sound: shistkyTA :: Vector (Double, Double)
+ DobutokO.Sound: simkyTA :: Vector (Double, Double)
+ DobutokO.Sound: triykyTA :: Vector (Double, Double)
+ DobutokO.Sound: uniqOberSoXSynthNGen3E :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> String -> IO ()
+ DobutokO.Sound: uniqOberSoXSynthNGen4E :: FilePath -> Int -> Int -> Double -> Double -> Double -> String -> String -> String -> String -> IO ()
+ DobutokO.Sound: uniqOberSoXSynthNGenE :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> IO ()
+ DobutokO.Sound: visimkyTA :: Vector (Double, Double)
+ DobutokO.Sound: whichEnka :: Int -> Double -> Maybe Int
Files
- CHANGELOG.md +5/−0
- DobutokO/Sound.hs +322/−34
- DobutokO/Sound/Executable.hs +81/−3
- README.markdown +33/−18
- dobutokO2.cabal +1/−1
CHANGELOG.md view
@@ -142,3 +142,8 @@ the resulting "end.wav" file and to clean the disk space from "result*" files by adding two new first command line options "8" and "80". Please, use the last one with a special attention. See README.markdown for more information. Fixed issues with negative frequencies and durations possible in the DobutokO.Sound.Functional functions. Added some more error messages to make debugging easier.++## 0.15.0.0 -- 2020-03-20++* Fifteenth version. Added an opportunity to work not only with octaves but also with n-th elements sets of consequential notes with the variants+of 2, 3, 4, 6, or 9 notes. Octave from such a perspective is a set of 12 notes. Some documentation improvements for the README.markdown file.
DobutokO/Sound.hs view
@@ -49,7 +49,35 @@ , whichOctave , putInOctave , putInOctaveV- -- ** Auxiliary functions+ -- * Work with enky (extension to octaves functionality)+ , nkyT+ , enkuUp+ , enkuDown+ , whichEnka+ , putInEnku+ , putInEnkuV+ -- ** Even more extended+ , dviykyTA+ , triykyTA+ , chetvirkyTA+ , p'yatirkyTA+ , shistkyTA+ , simkyTA+ , visimkyTA+ , dev'yatkyTA+ , desyatkyTA+ , odynadtsyatkyTA+ , octavesTA+ -- * Extended generation using enky functionality+ -- ** With somewhat fixed timbre+ , oberSoXSynthNGenE+ , oberSoXSynthNGen2E+ , oberSoXSynthNGen3E+ -- ** With usage of additional information in the Ukrainian text+ , uniqOberSoXSynthNGenE+ , uniqOberSoXSynthNGen3E+ , uniqOberSoXSynthNGen4E+ -- * Auxiliary functions , notes , neighbourNotes , closestNote@@ -62,9 +90,9 @@ , numVZeroesPre ) where -import CaseBi+import CaseBi (getBFst') import System.Exit (ExitCode(ExitSuccess))-import Numeric+import Numeric (showFFloat) import Control.Exception (onException) import System.Environment (getArgs) import Data.List (isPrefixOf,sort,sortBy,nubBy)@@ -72,61 +100,94 @@ import Data.Char (isDigit) import qualified Data.Vector as V import System.Process-import EndOfExe+import EndOfExe (showE) import MMSyn7.Syllable import MMSyn7s import System.Directory import SoXBasics import Processing_mmsyn7ukr-import Melodics.Ukrainian+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)) --- | 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 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 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 '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 --- | Returns a pure quint lower than the given note.-pureQuintNote :: Double -> Double-pureQuintNote x = x / 2 ** (fromIntegral 7 / fromIntegral 12)-{-# INLINE pureQuintNote #-}+dviykyTA :: V.Vector (Double, Double)+dviykyTA = V.generate 107 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 1))) +triykyTA :: V.Vector (Double, Double)+triykyTA = V.generate 106 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 2)))++chetvirkyTA :: V.Vector (Double, Double)+chetvirkyTA = V.generate 105 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 3)))++p'yatirkyTA :: V.Vector (Double, Double)+p'yatirkyTA = V.generate 104 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 4)))++shistkyTA :: V.Vector (Double, Double)+shistkyTA = V.generate 103 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 5)))++simkyTA :: V.Vector (Double, Double)+simkyTA = V.generate 102 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 6)))++visimkyTA :: V.Vector (Double, Double)+visimkyTA = V.generate 101 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 7)))++dev'yatkyTA :: V.Vector (Double, Double)+dev'yatkyTA = V.generate 100 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 8)))++desyatkyTA :: V.Vector (Double, Double)+desyatkyTA = V.generate 99 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 9)))++odynadtsyatkyTA :: V.Vector (Double, Double)+odynadtsyatkyTA = V.generate 98 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 10)))++octavesTA :: V.Vector (Double, Double)+octavesTA = V.generate 97 (\i -> (V.unsafeIndex notes i, V.unsafeIndex notes (i + 11)))++--------------------------------------------------------------------------------------------------------------------------+ -- | Returns an analogous note in the higher octave (its frequency in Hz). octaveUp :: Double -> Double 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 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 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, -- e. g. for C4 this is 4) the frequency belongs (to be more exact, the closest note for the given frequency -- see 'closestNote' taking into account -- its lower pure quint, which can lay in the lower by 1 octave). If it is not practical to determine the number, then the function returns 'Nothing'.@@ -140,6 +201,19 @@ _ -> 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.@@ -162,12 +236,67 @@ else Just (V.unsafeLast . V.iterateN (fromIntegral z1 + 1) octaveDown $ closestNote x) | otherwise = Nothing +-- | Similarly to 'putInOctave' returns a 'Maybe' number (actually frequency) for the n-th elements set of notes (see 'nkyT').+-- A second 'Int' parameter defines that @n@.+putInEnku :: Int -> Int -> Double -> Maybe Double+putInEnku 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'. putInOctaveV :: Int -> V.Vector Double -> V.Vector Double-putInOctaveV n = V.mapMaybe (\z -> putInOctave n z) +putInOctaveV n = V.mapMaybe (\z -> putInOctave n z) +-- | Similarly to 'putInOctaveV' 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@. +putInEnkuV :: Int -> Int -> V.Vector Double -> V.Vector Double+putInEnkuV n ku = V.mapMaybe (\z -> putInEnku 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 =@@ -490,6 +619,32 @@ 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." +-- | 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.+oberSoXSynthNGenE :: FilePath -> Int -> Int -> Double -> Double -> String -> IO ()+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 })+ let vecB = putInEnkuV 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." + -- | 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 -- the range [0.01..1.0] is used as a maximum amplitude for obertones. If it is set to 1.0 the obertones amplitudes are just maximum ones,@@ -522,6 +677,32 @@ 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." +-- | 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.+oberSoXSynthNGen2E :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> IO ()+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 })+ let vecB = putInEnkuV 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." + -- | 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 -- the range [0.01..1.0] is used as a maximum amplitude for obertones. If it is set to 1.0 the obertones amplitudes are just maximum ones,@@ -557,6 +738,32 @@ 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."++-- | 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.+oberSoXSynthNGen3E :: FilePath -> Int -> Int -> Double -> Double -> Double -> String -> String -> String -> IO ()+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 })+ let vecB = putInEnkuV 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 @@ -821,6 +1028,33 @@ 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." +-- | 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+-- its modifications, please, use 2, 3, 4, 6, or 9.+uniqOberSoXSynthNGenE :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> IO ()+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 })+ let vecB = putInEnkuV 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."+ -- | 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@@ -854,6 +1088,33 @@ 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." +-- | 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+-- its modifications, please, use 2, 3, 4, 6, or 9.+uniqOberSoXSynthNGen3E :: FilePath -> Int -> Int -> Double -> Double -> String -> String -> String -> IO ()+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 })+ let vecB = putInEnkuV 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." + -- | 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@@ -879,6 +1140,33 @@ ; 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 = putInOctaveV 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."++-- | 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+-- its modifications, please, use 2, 3, 4, 6, or 9.+uniqOberSoXSynthNGen4E :: FilePath -> Int -> Int -> Double -> Double -> Double -> String -> String -> String -> String -> IO ()+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 })+ let vecB = putInEnkuV 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
DobutokO/Sound/Executable.hs view
@@ -25,7 +25,7 @@ import System.Environment (getArgs) import Data.List (isPrefixOf) import Data.Maybe (fromJust)-import Data.Char (isDigit)+import Data.Char (isDigit,isSpace) import System.Process import EndOfExe (showE) import qualified Data.Vector as V (Vector (..),generate,fromList,length,imapM_,snoc,toList,unsafeSlice)@@ -44,7 +44,8 @@ args = unwords . drop 2 $ arggs exist2 <- doesFileExist file getBFst' (dobutokO2H exist2 args file, V.fromList . fmap (\(xs, f) -> (xs,f exist2 args file)) $ [("0",o2help),("1",dobutokO2H1),("2",dobutokO2H2),- ("3",dobutokO2H3),("4",dobutokO2H4),("5",dobutokO2H5),("7",dobutokO2H7),("8",dobutokO2H8),("80",dobutokO2H80)]) arg1+ ("3",dobutokO2H3),("4",dobutokO2H4),("5",dobutokO2H5),("7",dobutokO2H7),("8",dobutokO2H8),("11",dobutokO2H11),("21",dobutokO2H21),("31",dobutokO2H31),+ ("41",dobutokO2H41),("51",dobutokO2H51),("8",dobutokO2H8),("80",dobutokO2H80)]) arg1 dobutokO2H1 :: Bool -> String -> FilePath -> IO () dobutokO2H1 exist2 args file = do@@ -123,6 +124,52 @@ else error "The end file \"end.wav\" was not created. " {-# INLINE dobutokO2H8 #-} +dobutokO2H11 :: Bool -> String -> FilePath -> IO ()+dobutokO2H11 exist2 args file = do+ [_,_,complexNky,ampLS,time2] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5] else [1,2,11,4,5])+ let [enkA,nTh] = map (\z -> read z::Int) . words $ complexNky+ ampL = read ampLS::Double+ time3 = read time2::Double+ oberSoXSynthNGenE file nTh enkA ampL time3 args+{-# INLINE dobutokO2H11 #-}++dobutokO2H21 :: Bool -> String -> FilePath -> IO ()+dobutokO2H21 exist2 args file = do+ [_,_,complexNky,ampLS,time2,wws] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,6] else [1,2,11,4,5,6])+ let [enkA,nTh] = map (\z -> read z::Int) . words $ complexNky+ ampL = read ampLS::Double+ time3 = read time2::Double+ uniqOberSoXSynthNGenE file nTh enkA ampL time3 args wws+{-# INLINE dobutokO2H21 #-}++dobutokO2H31 :: Bool -> String -> FilePath -> IO ()+dobutokO2H31 exist2 args file = do+ [_,_,complexNky,ampLS,time2,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,7] else [1,2,11,4,5,7])+ let [enkA,nTh] = map (\z -> read z::Int) . words $ complexNky+ ampL = read ampLS::Double+ time3 = read time2::Double+ oberSoXSynthNGen2E file nTh enkA ampL time3 args tts+{-# INLINE dobutokO2H31 #-}++dobutokO2H41 :: Bool -> String -> FilePath -> IO ()+dobutokO2H41 exist2 args file = do+ [_,_,complexNky,ampLS,time2,wws,tts] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,6,7] else [1,2,11,4,5,6,7])+ let [enkA,nTh] = map (\z -> read z::Int) . words $ complexNky+ ampL = read ampLS::Double+ time3 = read time2::Double+ uniqOberSoXSynthNGen3E file nTh enkA ampL time3 args wws tts+{-# INLINE dobutokO2H41 #-}++dobutokO2H51 :: Bool -> String -> FilePath -> IO ()+dobutokO2H51 exist2 args file = do+ [_,_,complexNky,ampLS,time2,tts,dAmpl0,vs] <- mapM (recAndProcess file) (if exist2 then [0,2,11,4,5,7,8,9] else [1,2,11,4,5,7,8,9])+ let [enkA,nTh] = map (\z -> read z::Int) . words $ complexNky+ ampL = read ampLS::Double+ time3 = read time2::Double+ dAmpl = read dAmpl0::Double+ oberSoXSynthNGen3E file nTh enkA ampL time3 dAmpl args tts vs+{-# INLINE dobutokO2H51 #-}+ dO2H8 :: Int -> V.Vector String -> IO () dO2H8 i v = do (code,_,_) <- readProcessWithExitCode (fromJust (showE "sox")) (V.toList v ++ ["end0" ++ show i ++ ".wav"]) ""@@ -250,7 +297,38 @@ recAndProcess :: FilePath -> Int -> IO String recAndProcess file x = getBFst' (processD, V.fromList [(0,processD0 file),(1,processD1),(2,processD2 file),(3,processD3),(4,processD4),(5,processD5),(7,processD7),- (8,processD8),(9,processD9)]) x+ (8,processD8),(9,processD9),(11,processD_1)]) x++processD_1 :: IO String+processD_1 = onException (do+ putStr "Please, specify two \'Int\' numbers (with intermediate space character between them): the first one is a number of different notes there will be "+ putStr "in the result, and the second one is a number of enky, to which you would like all the main components (not taking into account their "+ putStr "respective lower pure quints) should belong. "+ putStrLn "If you specify as the first one 2 (possibly the simplest case), then to the second one you can define a number in the range [3..53]. "+ putStrLn "If you specify as the first one 3, then to the second one you can define a number in the range [2..35]. "+ putStrLn "If you specify as the first one 4, then to the second one you can define a number in the range [2..26]. "+ putStrLn "If you specify as the first one 6, then to the second one you can define a number in the range [1..17]. "+ putStrLn "If you specify as the first one 9, then to the second one you can define a number in the range [1..11]. "+ enka0 <- getLine+ let enka1 = take 2 . words . filter (\x -> isDigit x || isSpace x) $ enka0+ enka2 = read (head . take 1 $ enka1)::Int+ enka3+ | enka2 == 2 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 53) 3 == LT then 28 else (read (take 2 . head . tail $ enka1)::Int)+ `rem` 53+ | enka2 == 3 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 35) 2 == LT then 19 else (read (take 2 . head . tail $ enka1)::Int)+ `rem` 35+ | enka2 == 4 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 26) 2 == LT then 14 else (read (take 2 . head . tail $ enka1)::Int)+ `rem` 26+ | enka2 == 6 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 17) 1 == LT then 9 else (read (take 2 . head . tail $ enka1)::Int)+ `rem` 17+ | enka2 == 9 = if compare ((read (take 2 . head . tail $ enka1)::Int) `rem` 11) 1 == LT then 6 else (read (take 2 . head . tail $ enka1)::Int)+ `rem` 11+ | otherwise = error "Not valid number in the second place. "+ return $ show enka2 ++ " " ++ show enka3 ) (do+ putStrLn "The process was not successful may be because of the not valid data. Please, specify the valid data as requested."+ putStrLn "_______________________________________________________________________"+ processD3)+{-# INLINE processD_1 #-} processD0 :: FilePath -> IO String processD0 file = onException (readProcessWithExitCode (fromJust (showE "sox")) [file, "x.wav", "-r22050", "channels", "1"] "" >> putStrLn "" >> return "") (do
README.markdown view
@@ -5,15 +5,15 @@ ***** Usage ***** ----------------- -You can use it as a library or an executable.+You can use it as a library or as an executable. -Please, check before executing whether there is no "x.wav", "test*.wav",-"result*.wav" and "end.wav" files in the current directory, because they-can be overwritten. The same can be said about "nx*.wav" files in the directory.+Please, check before executing whether there is no "x.wav", "test\*.wav",+"result\*.wav" and "end.wav" files in the current directory, because they+can be overwritten. The same can be said about "nx\*.wav" files in the directory. For the executable you enter in the terminal: -dobutokO2 { 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 80 } {fileName} {Ukrainian text}+dobutokO2 { 0 | 1 | 11 | 2 | 21 | 3 | 31 | 4 | 41 | 5 | 51 | 6 | 7 | 8 | 80 } {fileName} {Ukrainian text} where filename is: the full name of the file to be recorded in the current directory or@@ -30,30 +30,48 @@ "3", or "4", or "5", or "6" -> the program uses additional String to define signs for the harmonics coefficients for obertones.- - In more detail: + "11", or "21", or "31", or "41", or "51" -> the program works as for the respective+ inputs with only the first character in the option (e. g. for "51", this is "5"),+ but uses not octaves, but n-th elements sets of consequential notes consisting+ of 2, 3, 4, 6, or 9 elements (called 'enky'). The usual octave is from such+ point of view a 12th elements set of consequential notes. This allows+ to create more 'condensed' and 'narrower' compositions that being+ more defined can be at the same time more precise.+ + In more details:+ "0" -> the program just converts multiline Ukrainian text from stdin into a String and prints it to stdout. No other arguments are used. "1" -> basic functionality without the possibility to define individual obertones.++ "11" -> the same as "1", but works with enky. See general information above. "2" -> basic functionality with the possibility to define individual obertones. In such a case, another text gives the other obertones. + "21" -> the same as "2", but works with enky. See general information above.+ "3" -> adittionally to basic functionality gives an opportunity to specify the signs for the harmonics coefficients for obertones by additional String. + "31" -> the same as "3", but works with enky. See general information above. + "4" -> similarly to "2" gives an opportunity to specify the signs for the harmonics coefficients for obertones by additional String. + "41" -> the same as "4", but works with enky. See general information above. + "5" -> additionally to that one functionality provided by "3" gives an opportunity to specify in how many times the amplitude for the second lower note (if any) is greater, than the amplitude for the main note and specify the intervals to be used for every note. + "51" -> the same as "5", but works with enky. See general information above. + "7" -> the program behaves like for the "5" option, but generates obertones using additional String and allows maximum control over the parameters. Besides, all the needed information it obtains from@@ -69,32 +87,29 @@ program will work also but with less control for the user possible. - "8" -> the program just creates from input "result*" files the "end.wav" by+ "8" -> the program just creates from input "result\*" files the "end.wav" by concatenating them into one. It is mostly useful after some processment- on the "result*" files after previous execution with other lesser first+ on the "result\*" files after previous execution with other lesser first command line arguments to get the test final sound file. It can be then- listened to and probably remade again by editing the "result*" files+ listened to and probably remade again by editing the "result\*" files and running the program with this option again. In such a case, noone from the other command line arguments is important for the program running, so they all can be simply omitted. "80" -> the same as "8" but with one important difference that the program if- succeeded in creation of the "end.wav" file, then removes all other "result*"+ succeeded in creation of the "end.wav" file, then removes all other "result\*" files from the current directory, so you cannot reverse the successful action back and try again with just the same files. In such a case, you need to- repeat all the process of creation of "result*" files. Be aware and use+ repeat all the process of creation of "result\*" files. Be aware and use with care! _ -> the program behaves like for the "5" option, but generates obertones using additional String and allows maximum control over the parameters. -If you have prepared multiline Ukrainian textual input than to use it you-can deal with O2help executable. It is simple converter of the multiline text-into a String.--After the program executing (it takes some time) there is a file "end.wav" in the directory.-This is the resulting melody generated.+After the program executing (it takes some time) with the first command line+options except "80" there are files "result\*.wav" in the directory.+These are the resulting melody generated in their order preserved. The program now lifts the frequencies to the octave with the number, which you can specify during its execution.
dobutokO2.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: dobutokO2-version: 0.14.0.0+version: 0.15.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 homepage: https://hackage.haskell.org/package/dobutokO2