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

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

Signals equipped with a volume information that may carry a unit.
Is the approach with separated volume information still appropriate?
Actually it simplifies reusing code from "Synthesizer.State.Signal"
because we do not have to replace @(*)@ by @(&*&)@.
-}
module Synthesizer.Dimensional.Amplitude.Signal where

import qualified Synthesizer.Dimensional.Amplitude as Amp
import qualified Synthesizer.Format as Format
import qualified Synthesizer.Dimensional.Abstraction.RateIndependent as Ind

import qualified Synthesizer.Dimensional.RatePhantom as RP
import qualified Synthesizer.Dimensional.Straight.Signal as SigS

import qualified Synthesizer.State.Filter.NonRecursive as Filt
import qualified Synthesizer.State.Signal as Sig

import qualified Synthesizer.Generic.Filter.NonRecursive as FiltG
import qualified Synthesizer.Generic.Signal as SigG

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

import qualified Algebra.Module         as Module
import qualified Algebra.Field          as Field
import qualified Algebra.Ring           as Ring

-- import Number.DimensionTerm ((&/&))

import NumericPrelude
import PreludeBase as P
import Prelude ()

data T amp sig yv =
Cons {
privateAmplitude :: amp     {-^ scaling of the values -}
, signal           :: sig yv  {-^ the embedded signal -}
}
--   deriving (Eq, Show)

instance (Show amp, Format.C sig) => Format.C (T amp sig) where
format p (Cons amp sig) =
showParen (p >= 10)
(showString "amplitudeSignal " . showsPrec 11 amp .
showString " " . Format.format 11 sig)

instance (Show amp, Show yv, Format.C sig) => Show (T amp sig yv) where
showsPrec = Format.format

type R s v y yv = RP.T s (S v y) yv
type S v y = D v y SigS.S  -- kind * -> *
type D v y = T (DN.T v y)

{-
We removed that instance because 'fmap' is too dangerous for application code.
You may write functions that depend on the particular amplitude scaling.

instance Dim.C v => Functor (D v y s) where
fmap f (Cons amp ss) = Cons amp (map f ss)
-}

{-# INLINE amplitude #-}
amplitude :: (Ind.C w, Dim.C v) =>
w (D v y sig) yv -> DN.T v y
amplitude = privateAmplitude . Ind.toSignal

{-# INLINE samples #-}
samples :: (Ind.C w, Dim.C v) =>
w (D v y (SigS.T sig)) yv -> sig yv
samples = privateSamples . Ind.toSignal

{-# INLINE privateSamples #-}
privateSamples :: (Amp.C amp) =>
T amp (SigS.T sig) yv -> sig yv
privateSamples = SigS.samples . signal

{-# INLINE phantomSignal #-}
phantomSignal ::
RP.T s (D v y sig) yv -> RP.T s sig yv
phantomSignal =
RP.fromSignal . signal . RP.toSignal

{-# INLINE toAmplitudeScalar #-}
toAmplitudeScalar :: (Ind.C w, Field.C y, Dim.C v) =>
w (D v y sig) yv -> DN.T v y -> y
toAmplitudeScalar sig y =
DN.divToScalar y (amplitude sig)

{-# INLINE scalarSamples #-}
{-
scalarSamples :: (Ind.C w, Ring.C y, Dim.C v) =>
(DN.T v y -> y) -> w (S v y) y -> Sig.T y
-}
scalarSamples :: (Ind.C w, Ring.C y, Amp.C amp) =>
(amp -> y) -> w (T amp SigS.S) y -> Sig.T y
scalarSamples toAmpScalar =
scalarSamplesPrivate toAmpScalar . Ind.toSignal

{-# INLINE scalarSamplesGeneric #-}
scalarSamplesGeneric ::
(Ind.C w, Ring.C y, Dim.C v, SigG.Transform sig y) =>
(DN.T v y -> y) -> w (D v y (SigS.T sig)) y -> sig y
scalarSamplesGeneric toAmpScalar =
scalarSamplesPrivateGeneric toAmpScalar . Ind.toSignal

{-# INLINE vectorSamples #-}
vectorSamples :: (Ind.C w, Module.C y yv, Dim.C v) =>
(DN.T v y -> y) -> w (S v y) yv -> Sig.T yv
vectorSamples toAmpScalar =
vectorSamplesPrivate toAmpScalar . Ind.toSignal

{-# INLINE rewriteDimension #-}
rewriteDimension :: (Dim.C v0, Dim.C v1) =>
(v0 -> v1) -> D v0 y sig yv -> D v1 y sig yv
rewriteDimension f (Cons amp ss) =
Cons (DN.rewriteDimension f amp) ss

{-# INLINE fromSignal #-}
-- fromSignal :: DN.T v y -> SigS.R s yv -> R s v y yv
fromSignal :: amp -> SigS.R s yv -> RP.T s (T amp SigS.S) yv
fromSignal amp  =  RP.fromSignal . Cons amp . RP.toSignal

{-# INLINE toScalarSignal #-}
toScalarSignal :: (Ind.C w, Field.C y, Dim.C v) =>
DN.T v y -> w (S v y) y -> w SigS.S y
toScalarSignal amp  =
Ind.processSignal
(SigS.Cons . scalarSamplesPrivate (flip DN.divToScalar amp))

{-# INLINE toVectorSignal #-}
toVectorSignal :: (Ind.C w, Field.C y, Module.C y yv, Dim.C v) =>
DN.T v y -> w (S v y) yv -> w SigS.S yv
toVectorSignal amp  =
Ind.processSignal
(SigS.Cons . vectorSamplesPrivate (flip DN.divToScalar amp))

{-# INLINE scalarSamplesPrivate #-}
{-
scalarSamplesPrivate :: (Ring.C y, Dim.C v) =>
(DN.T v y -> y) -> S v y y -> Sig.T y
-}
scalarSamplesPrivate :: (Ring.C y, Amp.C amp) =>
(amp -> y) -> T amp SigS.S y -> Sig.T y
scalarSamplesPrivate toAmpScalar sig =
let y = toAmpScalar (privateAmplitude sig)
in  Filt.amplify y (privateSamples sig)

{-# INLINE scalarSamplesPrivateGeneric #-}
scalarSamplesPrivateGeneric ::
(Ring.C y, Dim.C v, SigG.Transform sig y) =>
(DN.T v y -> y) -> D v y (SigS.T sig) y -> sig y
scalarSamplesPrivateGeneric toAmpScalar sig =
let y = toAmpScalar (privateAmplitude sig)
in  FiltG.amplify y (privateSamples sig)

{-# INLINE vectorSamplesPrivate #-}
vectorSamplesPrivate :: (Module.C y yv, Dim.C v) =>
(DN.T v y -> y) -> S v y yv -> Sig.T yv
vectorSamplesPrivate toAmpScalar sig =
let y = toAmpScalar (privateAmplitude sig)
in  y *> privateSamples sig

{-# INLINE fromSamples #-}
-- fromSamples :: (Dim.C v) => DN.T v y -> Sig.T yv -> R s v y yv
fromSamples :: {- (Amp.C amp) => -} amp -> Sig.T yv -> RP.T s (T amp SigS.S) yv
fromSamples amp  =  fromSignal amp . SigS.fromSamples

{-# INLINE fromScalarSamples #-}
fromScalarSamples :: {- (Amp.C amp) => -}
amp -> Sig.T y -> RP.T s (T amp SigS.S) y
fromScalarSamples  =  fromSamples

{-# INLINE fromVectorSamples #-}
fromVectorSamples :: {- (Amp.C amp) => -}
amp -> Sig.T yv -> RP.T s (T amp SigS.S) yv
fromVectorSamples  =  fromSamples

{-# INLINE replaceAmplitude #-}
replaceAmplitude :: (Ind.C w, Dim.C v0, Dim.C v1) =>
DN.T v1 y -> w (D v0 y sig) yv -> w (D v1 y sig) yv
replaceAmplitude amp  =  Ind.processSignal (replaceAmplitudePrivate amp)

{-# INLINE replaceSamples #-}
replaceSamples :: (Ind.C w, Dim.C v) =>
sig1 yv1 -> w (D v y sig0) yv0 -> w (D v y (SigS.T sig1)) yv1
replaceSamples ss  =  Ind.processSignal (replaceSamplesPrivate ss)

{-# INLINE replaceAmplitudePrivate #-}
replaceAmplitudePrivate :: (Dim.C v0, Dim.C v1) =>
DN.T v1 y -> D v0 y sig yv -> D v1 y sig yv
replaceAmplitudePrivate amp  =  Cons amp . signal

{-# INLINE replaceSamplesPrivate #-}
replaceSamplesPrivate :: (Dim.C v) =>
sig1 yv1 -> D v y sig0 yv0 -> D v y (SigS.T sig1) yv1
replaceSamplesPrivate ss x  =  Cons (privateAmplitude x) (SigS.Cons ss)

{-# INLINE processSamples #-}
processSamples :: (Ind.C w, Dim.C v) =>
(sig0 yv0 -> sig1 yv1) ->
w (D v y (SigS.T sig0)) yv0 -> w (D v y (SigS.T sig1)) yv1
processSamples f =
Ind.processSignal (processSamplesPrivate f)

{-# INLINE processSamplesPrivate #-}
processSamplesPrivate :: (Dim.C v) =>
(sig0 yv0 -> sig1 yv1) ->
D v y (SigS.T sig0) yv0 -> D v y (SigS.T sig1) yv1
processSamplesPrivate f (Cons amp sig) =
Cons amp (SigS.processSamplesPrivate f sig)

{-# INLINE asTypeOfAmplitude #-}
asTypeOfAmplitude :: y -> w (D v y sig) yv -> y
asTypeOfAmplitude = const
```