packages feed

synthesizer-inference-0.2: src/Synthesizer/Inference/Monad/Signal/Displacement.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{- |
Copyright   :  (c) Henning Thielemann 2006
License     :  GPL

Maintainer  :  synthesizer@henning-thielemann.de
Stability   :  provisional
Portability :  requires multi-parameter type classes

-}

module Synthesizer.Inference.Monad.Signal.Displacement (
   {- * Non-linearities -}
   mapScalar,
   mapVector,

   {- * Mixing -}
   mix,
   mixMulti,
) where


import qualified UniqueLogicNP.Explicit.Process    as Process
import qualified UniqueLogicNP.Explicit.Expression as Expr
import qualified Synthesizer.Inference.Monad.Signal     as SigI
import qualified UniqueLogicNP.Explicit.System     as IS

import UniqueLogicNP.Explicit.Expression ((=!=))
import Synthesizer.Inference.Monad.Signal
   (toAmplitudeScalar,
    sampleRateExpr, amplitudeExpr)

import qualified Synthesizer.Physical.Signal as SigP
import qualified Synthesizer.Plain.Displacement as Syn

import qualified Algebra.OccasionallyScalar as OccScalar
import qualified Algebra.Field          as Field
import qualified Algebra.Ring           as Ring
import qualified Algebra.Additive       as Additive
import qualified Algebra.Module         as Module

import Control.Monad.Fix (mfix)

import NumericPrelude
import PreludeBase
import qualified Data.List as List


{- * Non-linearities -}

{- | Apply a function to the signal values.
     If input and output signal shall have the same global amplitude,
     then it must hold @rateX * ampY = 1@. -}
mapVector :: (Module.C a v0, Field.C q, OccScalar.C a q) =>
      q  {- ^ rateX: If @v@ is the physical value
                     which shall appear as 1 to @f@,
                     then choose @rateX * v == 1@. -}
   -> q  {- ^ ampY: The physical value of the output signal
                    which is associated with the value 1 of @f@. -}
   -> (v0 -> v1)
         {- ^ f, the mapping -}
   -> SigI.T a q v0
   -> SigI.Process a q v1
mapVector rateX ampY f x =
   do samples <- SigI.vectorSamples
         (Process.exprToScalar . (Expr.constant rateX *)) x
      SigI.returnCons (SigP.sampleRate x) (IS.constant ampY)
         (List.map f samples)

mapScalar :: (Ring.C a, Field.C q, OccScalar.C a q) =>
      q  {- ^ rateX: If @v@ is the physical value
                     which shall appear as 1 to @f@,
                     then choose @rateX * v == 1@. -}
   -> q  {- ^ ampY: The physical value of the output signal
                    which is associated with the value 1 of @f@. -}
   -> (a -> a)
         {- ^ f, the mapping -}
   -> SigI.T a q a
   -> SigI.Process a q a
mapScalar rateX ampY f x =
   do samples <- SigI.scalarSamples
         (Process.exprToScalar . (Expr.constant rateX *)) x
      SigI.returnCons (SigP.sampleRate x) (IS.constant ampY)
         (List.map f samples)


{- * Mixing -}

{- | Mix two signals.
     In opposition to 'zipWith' the result has the length of the longer signal. -}
mix :: (Field.C q, Eq q, Module.C a v, OccScalar.C a q) =>
      SigI.T a q v
   -> SigI.T a q v
   -> SigI.Process a q v
mix x y =
   do sampleRate <- Process.fromExpr (sampleRateExpr x =!= sampleRateExpr y)
      amplitude  <- Process.fromExpr (amplitudeExpr  x  +  amplitudeExpr  y)
      mfix (\z ->
         do sampX <- SigI.vectorSamples (toAmplitudeScalar z) x
            sampY <- SigI.vectorSamples (toAmplitudeScalar z) y
            SigI.returnCons sampleRate amplitude
               (sampX + sampY))

{- | Mix one or more signals. -}
mixMulti :: (Field.C q, Eq q, Module.C a v, OccScalar.C a q) =>
      [SigI.T a q v]
   ->  SigI.Process a q v
mixMulti xs =
   do sampleRate <- Process.equalValues (List.map SigP.sampleRate xs)
      let ampExprs = List.map amplitudeExpr xs
      amplitude  <- Process.fromExpr (sum1 ampExprs)
         {- 'sum1' must be used, because 'sum' introduces a zero,
            which will probably have an incompatible unit. -}
      mfix (\z ->
         do samps <- mapM (SigI.vectorSamples (toAmplitudeScalar z)) xs
            SigI.returnCons sampleRate amplitude
               (Syn.mixMulti samps))