packages feed

algorithmic-composition-basic-0.1.0.0: Composition/Sound/FunctionF.hs

-- |
-- 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)) ++ "]))"