-- | -- Module : Composition.Sound.FunctionF -- Copyright : (c) OleksandrZhabenko 2020-2021 -- License : MIT -- Stability : Experimental -- Maintainer : olexandr543@yahoo.com -- -- Helps to create experimental music from a file (or its part) and a Ukrainian text. -- It can also generate a timbre for the notes. Uses SoX inside. {-# OPTIONS_GHC -threaded #-} module Composition.Sound.FunctionF ( -- * Working with OvertonesO and function f maybeFFromStrVec , fVecCoefs , showFFromStrVec ) where import Text.Read (readMaybe) import Data.Maybe (isNothing,fromJust,fromMaybe) import Numeric import GHC.Arr import qualified Data.Foldable as F import Composition.Sound.Functional.Basics -- | Gets a function @f::Float -> OvertonesO@ that can be used further. Has two variants with usage of 'closestNote' ('Int' argument is greater than 0)v -- and without it ('Int' argument is less than 0). For both cases 'String' must be in a form list of tuples of pairs of 'Float' to get somewhat -- reasonable result. The function @f@ can be shown using a special printing function 'showFFromStrVec'. It is a simplest multiplicative (somewhat -- acoustically and musically reasonable) form for the function that can provide such a result that fits into the given data. -- -- > let (y,f1) = fromJust (maybeFFromStrVec 1 3583.9783 "[(25.368,0.2486356),(37.259,0.6464867),(486.153,0.374618646),(789.563,0.463486461)]") in (y,f1 3583.9783) -- > -- > (3520.0,[(25.829079975681818,0.2486356),(37.936206670369316,0.6464867),(494.9891484317899,0.374618646),(803.9138234326421,0.463486461)]) -- > -- > let (y,f1) = fromJust (maybeFFromStrVec (-1) 3583.9783 "[(25.368,0.2486356),(37.259,0.6464867),(486.153,0.374618646),(789.563,0.463486461)]") in (y,f1 3583.9783) -- > -- > (3583.9783,[(25.368,0.2486356),(37.259,0.6464867),(486.153,0.374618646),(789.563,0.463486461)]) -- maybeFFromStrVec :: Int -> Float -> String -> Maybe (Float,(Float -> [(Float,Float)])) maybeFFromStrVec n x ys | n == 0 || null ys = Nothing | n > 0 = let y = closestNote (if x /= 0.0 then abs x else unsafeAt notes 0) v = readMaybe ys::Maybe ([(Float,Float)]) v2 = fromMaybe [] v v30 = map (\(t,_) -> t / y) $ v2 in if F.null v30 then Nothing else let v3 = (\rs -> listArray (0,length rs -1) rs) v30 in Just (y,(\t1 -> map (\(i, (_,ampl2)) -> ((unsafeAt v3 i) * t1,ampl2)) . zip [0..] $ v2)) | otherwise = let y = (if x /= 0.0 then abs x else unsafeAt notes 0) v = readMaybe ys::Maybe ([(Float,Float)]) v2 = fromMaybe [] v v30 = map (\(t,_) -> t / y) v2 in if F.null v30 then Nothing else let v3 = (\rs -> listArray (0,length rs -1) rs) v30 in Just (y,(\t1 -> map (\(i, (_,ampl2)) -> ((unsafeAt v3 i) * t1,ampl2)) . zip [0..] $ v2)) -- | Gets multiplication coefficients for @f::Float -> [(Float,Float)]@ from the 'maybeFFromStrVec' with the same arguments. fVecCoefs :: Int -> Float -> String -> [Float] fVecCoefs n x ys = let rs = maybeFFromStrVec n x ys in case rs of Nothing -> [] _ -> let (_,f1) = fromJust rs in map fst (f1 1) -- | Experimental 'show' for @f::Float -> [(Float,Float)]@ that is used only for visualisation. It is correct only with 'maybeFFromStrVec' or -- equivalent function. Because the shape of the @f@ is known the function can be defined. -- -- > showFFromStrVec (-1) 440 "[(25.358,0.3598),(489.35,0.4588962),(795.35,0.6853)]" -- > -- > "(440.00,(\t -> [(0.05763181818181818 * t, 0.3598),(1.112159090909091 * t, 0.4588962),(1.8076136363636364 * t, 0.6853)]))" -- showFFromStrVec :: Int -> Float -> String -> String showFFromStrVec n x ys | isNothing . maybeFFromStrVec n x $ ys = "" | otherwise = let (y,f) = fromJust . maybeFFromStrVec n x $ ys l = length ("(" ++ (showFFloat Nothing y "") ++ ",(\t -> [(" ++ concatMap (\z -> (showFFloat Nothing (fst z) $ " * t, " ++ (showFFloat Nothing (snd z) "),("))) (f 1)) in take (l - 2) ("(" ++ (showFFloat Nothing y "") ++ ",(\t -> [(" ++ concatMap (\z -> (showFFloat Nothing (fst z) " * t, " ++ (showFFloat Nothing (snd z) "),("))) (f 1)) ++ "]))"