{- |
Copyright   :  (c) Henning Thielemann 2008-2009
License     :  GPL

Maintainer  :  synthesizer@henning-thielemann.de
Stability   :  provisional
Portability :  requires multi-parameter type classes
-}
module Synthesizer.Dimensional.Cyclic.Analysis (
    toFrequencySpectrum, fromFrequencySpectrum,
  ) where

import qualified Synthesizer.Generic.Cut as CutG

import qualified Synthesizer.State.Analysis as Ana
import qualified Synthesizer.State.Signal   as Sig

import qualified Synthesizer.Dimensional.Rate                 as Rate
import qualified Synthesizer.Dimensional.Amplitude            as Amp
import qualified Synthesizer.Dimensional.Signal.Private       as SigA
import qualified Synthesizer.Dimensional.Cyclic.Signal        as SigC

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

import Number.DimensionTerm ((&*&), (*&), )

import qualified Number.Complex as Complex

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


import NumericPrelude.Base ((.), )
import NumericPrelude.Numeric ((+), negate, (/), fromIntegral, pi, )
import Prelude (Int, )


{- * Positions -}

{-# INLINE period #-}
period :: (Field.C t, Dim.C u, CutG.Read body) =>
   SigA.T (Rate.Dimensional u t) amp (SigC.T body) ->
   DN.T u t
period = makePhysicalPeriod (fromIntegral . CutG.length)

{-# INLINE makePhysicalPeriod #-}
makePhysicalPeriod :: (Field.C t, Dim.C u) =>
   (body -> t) ->
   SigA.T (Rate.Dimensional u t) amp (SigC.T body) ->
   DN.T u t
makePhysicalPeriod f x =
   f (SigC.toPeriod (SigA.body x))
       *&  DN.unrecip (SigA.actualSampleRate x)


{- |
Fourier analysis
-}
{-# INLINE toFrequencySpectrum #-}
toFrequencySpectrum :: (Trans.C q, Dim.C u, Dim.C v) =>
   SigA.T (Rate.Dimensional u q) (Amp.Dimensional v q) (SigC.T (Sig.T (Complex.T q))) ->
   SigA.T (Rate.Dimensional (Dim.Recip u) q) (Amp.Dimensional (Dim.Mul u v) q) (SigC.T (Sig.T (Complex.T q)))
toFrequencySpectrum x =
   let len = DN.rewriteDimension Dim.doubleRecip (period x)
       amp = SigA.actualAmplitude x
       ss  = SigC.toPeriod (SigA.body x)
       n   = Sig.length ss
       z = Complex.cis (negate (pi+pi) / fromIntegral n)
       newAmp = DN.unrecip (SigA.actualSampleRate x) &*& amp
   in  SigA.Cons
          (Rate.Actual len)
          (Amp.Numeric newAmp)
          (SigC.Cons (Sig.take n (Ana.chirpTransform z ss)))
{-
toFrequencySpectrum $ SigP.Cons (DN.frequency (4::Prelude.Double)) (SigA.Cons (DN.voltage (1::Prelude.Double)) (SigC.Cons [1, 0 Number.Complex.+: (1::Prelude.Double), -1, 0 Number.Complex.+: (-1)]))
toFrequencySpectrum $ SigP.Cons (DN.frequency (4::Prelude.Double)) (SigA.Cons (DN.voltage (1::Prelude.Double)) (SigC.Cons [0 Number.Complex.+: (1::Prelude.Double), -1, 0 Number.Complex.+: (-1), 1]))
toFrequencySpectrum $ SigP.Cons (DN.frequency (4::Prelude.Double)) (SigA.Cons (DN.voltage (1::Prelude.Double)) (SigC.Cons [1, -1,1, (-1) Number.Complex.+: (0::Prelude.Double)]))
-}


{- |
Fourier synthesis
-}
{-# INLINE fromFrequencySpectrum #-}
fromFrequencySpectrum :: (Trans.C q, Dim.C u, Dim.C v) =>
   SigA.T (Rate.Dimensional (Dim.Recip u) q) (Amp.Dimensional (Dim.Mul u v) q) (SigC.T (Sig.T (Complex.T q))) ->
   SigA.T (Rate.Dimensional u q) (Amp.Dimensional v q) (SigC.T (Sig.T (Complex.T q)))
fromFrequencySpectrum x =
   let len = period x
       amp = SigA.actualAmplitude x
       ss  = SigC.toPeriod (SigA.body x)
       n   = Sig.length ss
       z = Complex.cis ((pi+pi) / fromIntegral n)
       newAmp =
          DN.rewriteDimension
             (Dim.identityLeft . Dim.applyLeftMul Dim.cancelLeft . Dim.associateLeft)
             (DN.unrecip (SigA.actualSampleRate x) &*& amp)
   in  SigA.Cons
          (Rate.Actual len)
          (Amp.Numeric newAmp)
          (SigC.Cons (Sig.take n (Ana.chirpTransform z ss)))