packages feed

synthesizer-core-0.9: src/Synthesizer/Generic/Control.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
module Synthesizer.Generic.Control (
   constant,
   linear,
   linearMultiscale,
   linearMultiscaleNeutral,
   line,
   exponential, exponentialMultiscale,
   exponentialMultiscaleNeutral,
   exponential2, exponential2Multiscale,
   exponential2MultiscaleNeutral,
   vectorExponential,
   vectorExponential2,
   cosine, cosineMultiscaleLinear,
   cosineMultiscale,
   Ctrl.cosineWithSlope,
   cubicHermite,
   ) where

import qualified Synthesizer.Plain.Control as Ctrl

import qualified Synthesizer.Generic.Signal as SigG

import qualified Algebra.Module                as Module
import qualified Algebra.Transcendental        as Trans
import qualified Algebra.Field                 as Field
import qualified Algebra.Additive              as Additive

import qualified Number.Complex as Complex
import Number.Complex (cis,real)

import NumericPrelude.Numeric
import NumericPrelude.Base


{- * Control curve generation -}

constant :: (SigG.Produce sig y) => y -> sig y
constant = SigG.repeat


linear :: (Additive.C y, SigG.Produce sig y) =>
      y   {-^ steepness -}
   -> y   {-^ initial value -}
   -> sig y
          {-^ linear progression -}
linear d y0 = SigG.iterate (d+) y0

{- |
Minimize rounding errors by reducing number of operations per element
to a logarithmuc number.
-}
linearMultiscale ::
   (Additive.C y, SigG.Produce sig y) =>
      y
   -> y
   -> sig y
linearMultiscale =
   curveMultiscale (+)

{- |
Linear curve starting at zero.
-}
linearMultiscaleNeutral :: (Additive.C y, SigG.Produce sig y) =>
      y
   -> sig y
linearMultiscaleNeutral slope =
   curveMultiscaleNeutral (+) slope zero

{- |
Linear curve of a fixed length.
The final value is not actually reached,
instead we stop one step before.
This way we can concatenate several lines
without duplicate adjacent values.
-}
line :: (Field.C y, SigG.Produce sig y) =>
      Int   {-^ length -}
   -> (y,y) {-^ initial and final value -}
   -> sig y
            {-^ linear progression -}
line n (y0,y1) =
   SigG.take n $ linear ((y1-y0) / fromIntegral n) y0


exponential, exponentialMultiscale ::
   (Trans.C y, SigG.Produce sig y) =>
      y   {-^ time where the function reaches 1\/e of the initial value -}
   -> y   {-^ initial value -}
   -> sig y
          {-^ exponential decay -}
exponential time =
   SigG.iterate (* exp (- recip time))
exponentialMultiscale time =
   curveMultiscale (*) (exp (- recip time))

exponentialMultiscaleNeutral :: (Trans.C y, SigG.Produce sig y) =>
      y   {-^ time where the function reaches 1\/e of the initial value -}
   -> sig y
          {-^ exponential decay -}
exponentialMultiscaleNeutral time =
   curveMultiscaleNeutral (*) (exp (- recip time)) one

exponential2, exponential2Multiscale :: (Trans.C y, SigG.Produce sig y) =>
      y   {-^ half life -}
   -> y   {-^ initial value -}
   -> sig y
          {-^ exponential decay -}
exponential2 halfLife =
   SigG.iterate (*  0.5 ** recip halfLife)
exponential2Multiscale halfLife =
   curveMultiscale (*) (0.5 ** recip halfLife)

exponential2MultiscaleNeutral :: (Trans.C y, SigG.Produce sig y) =>
      y   {-^ half life -}
   -> sig y
          {-^ exponential decay -}
exponential2MultiscaleNeutral halfLife =
   curveMultiscaleNeutral (*) (0.5 ** recip halfLife) one




{-| This is an extension of 'exponential' to vectors
    which is straight-forward but requires more explicit signatures.
    But since it is needed rarely I setup a separate function. -}
vectorExponential ::
   (Trans.C y, Module.C y v, SigG.Produce sig v) =>
       y  {-^ time where the function reaches 1\/e of the initial value -}
   ->  v  {-^ initial value -}
   -> sig v
          {-^ exponential decay -}
vectorExponential time y0 =
   SigG.iterate (exp (-1/time) *>) y0

vectorExponential2 ::
   (Trans.C y, Module.C y v, SigG.Produce sig v) =>
       y  {-^ half life -}
   ->  v  {-^ initial value -}
   -> sig v
          {-^ exponential decay -}
vectorExponential2 halfLife y0 =
   SigG.iterate (0.5**(1/halfLife) *>) y0



cosine, cosineMultiscaleLinear :: (Trans.C y, SigG.Produce sig y) =>
       y  {-^ time t0 where  1 is approached -}
   ->  y  {-^ time t1 where -1 is approached -}
   -> sig y
          {-^ a cosine wave where one half wave is between t0 and t1 -}
cosine = Ctrl.cosineWithSlope $
   \d x -> SigG.map cos (linear d x)

cosineMultiscaleLinear = Ctrl.cosineWithSlope $
   \d x -> SigG.map cos (linearMultiscale d x)

cosineMultiscale ::
   (Trans.C y, SigG.Produce sig (Complex.T y),
    SigG.Transform sig (Complex.T y), SigG.Transform sig y) =>
       y  {-^ time t0 where  1 is approached -}
   ->  y  {-^ time t1 where -1 is approached -}
   -> sig y
          {-^ a cosine wave where one half wave is between t0 and t1 -}
cosineMultiscale = Ctrl.cosineWithSlope $
   \d x -> SigG.map real (curveMultiscale (*) (cis d) (cis x))


cubicHermite :: (Field.C y, SigG.Produce sig y) =>
      (y, (y,y)) -> (y, (y,y)) -> sig y
cubicHermite node0 node1 =
   SigG.map (Ctrl.cubicFunc node0 node1) $ linear 1 0


{- * Auxiliary functions -}


curveMultiscale :: (SigG.Produce sig y) => (y -> y -> y) -> y -> y -> sig y
curveMultiscale op d y0 =
   SigG.cons y0 . SigG.map (op y0) $ SigG.iterateAssociative op d


curveMultiscaleNeutral :: (SigG.Produce sig y) =>
   (y -> y -> y) -> y -> y -> sig y
curveMultiscaleNeutral op d neutral =
   SigG.cons neutral $ SigG.iterateAssociative op d