module Synthesizer.Dimensional.Wave where

import qualified Synthesizer.Dimensional.Sample as Sample
import qualified Synthesizer.Dimensional.Map as MapD

import qualified Synthesizer.Basic.Phase as Phase
import qualified Synthesizer.Basic.Wave as Wave
import qualified Synthesizer.Generic.Wave as WaveG
import qualified Synthesizer.Generic.Signal as SigG

import qualified Synthesizer.Interpolation as Interpolation

import qualified Synthesizer.Dimensional.Signal.Private as SigA
import qualified Synthesizer.Dimensional.Amplitude as Amp

import qualified Algebra.Transcendental as Trans
import qualified Algebra.RealRing      as RealRing
import qualified Algebra.Ring           as Ring

import qualified Number.DimensionTerm        as DN
import qualified Algebra.DimensionTerm       as Dim

import NumericPrelude.Numeric
import NumericPrelude.Base
import Prelude ()


type SamplePhase t = Sample.Abstract (Phase.T t)

{- |
We define a dimensional waveform in terms of a Map.
This allows any kind and number of result samples
and distortion of waveforms using @(distortion <<<)@
-}
type T t y = MapD.T (SamplePhase t) y

{-# INLINE simple #-}
simple ::
   amp ->
   Wave.T t y ->
   T t (Sample.T amp y)
simple amp wave =
   MapD.independent
      (const $ amp)
      (Wave.apply wave)


infix 7 &*~

{-# INLINE (&*~) #-}
(&*~) ::
   amp ->
   Wave.T t y ->
   T t (Sample.Numeric amp y)
(&*~) = amplified


{-# INLINE sample #-}
sample ::
   (RealRing.C t, SigG.Transform sig y) =>
   Interpolation.T t y ->
   SigA.T rate amp (sig y) ->
   T t (Sample.T amp y)
sample ip wave =
   simple (SigA.amplitude wave) $
   WaveG.sample ip (SigA.body wave)


{-# INLINE flat #-}
flat :: (Ring.C y) =>
   Wave.T t y ->
   T t (Sample.Flat y)
flat = simple Amp.Flat


{-# INLINE abstract #-}
abstract ::
   Wave.T t y ->
   T t (Sample.Abstract y)
abstract = simple Amp.Abstract


{-# INLINE amplified #-}
amplified ::
   amp ->
   Wave.T t y ->
   T t (Sample.Numeric amp y)
{-
 (Ring.C y, Dim.C u) =>
   DN.T u y ->
   Wave.T t y ->
   T t (Sample.Dimensional u y y)
-}
{-
   amp ->
   Wave.T t y ->
   T amp t y
-}
amplified = simple . Amp.Numeric


{-# INLINE mapLinear #-}
mapLinear :: (Ring.C y, Dim.C u) =>
   y ->
   DN.T u y ->
   Wave.T t y ->
   T t (Sample.Dimensional u y y)
mapLinear depth center =
   amplified center . Wave.distort (\x -> one+x*depth)

{-# INLINE mapExponential #-}
mapExponential :: (Trans.C y, Dim.C u) =>
   y ->
   DN.T u y ->
   Wave.T t y ->
   T t (Sample.Dimensional u y y)
mapExponential depth center =
   -- amplified center . Wave.distort (depth**)
   -- should be faster
   amplified center .
      let logDepth = log depth
      in  Wave.distort (exp . (logDepth*))