{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{- |
Copyright   :  (c) Henning Thielemann 2008-2009
License     :  GPL

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

A class that allows unified handling of
@Amplitude.Flat@ and @Amplitude.Dimensional Dim.Scalar@
which is often used for control curves.
However, I'm thinking about whether this is more abuse than use.
So this class may disappear in future.
Amplitude.Flat might become a synonym for @DN.scalar one@.
Sometimes, using Flat instead of DN.Scalar has the advantage
of internally saving a multiplication with one,
but I think the compiler should optimize that away.
The optimization however is more complicated
if a whole StorableVector is multiplied element-wise by one.
E.g. the concatenation of flat (storable) signals
can be done without copying the entire data.
-}
module Synthesizer.Dimensional.Amplitude.Flat
   (C, amplifySample, canonicalize, toSamples, ) where

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

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

-- import qualified Synthesizer.State.Signal as Sig

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.Numeric
import NumericPrelude.Base
import Prelude ()


{-
we could use OccasionallyScalar class,
but this would flood user code with OccScalar.C y y constraints
-}
class Amp.C amp => C y amp | amp -> y where
   toScalar :: amp -> y
   amplifySample :: amp -> y -> y
   amplify :: (SigG.Transform sig y) =>
      amp -> sig y -> sig y

instance Ring.C y => C y (Amp.Flat y) where
   toScalar :: Flat y -> y
toScalar = forall a b. a -> b -> a
const forall a. C a => a
Ring.one
   amplifySample :: Flat y -> y -> y
amplifySample Flat y
_ = forall a. a -> a
id
   amplify :: forall (sig :: * -> *). Transform sig y => Flat y -> sig y -> sig y
amplify Flat y
_ = forall a. a -> a
id

instance (Dim.IsScalar v, Ring.C y) => C y (Amp.Numeric (DN.T v y)) where
   toScalar :: Numeric (T v y) -> y
toScalar (Amp.Numeric T v y
amp) =
      forall a. Scalar a -> a
DN.toNumber forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      forall u v a. (C u, C v) => (u -> v) -> T u a -> T v a
DN.rewriteDimension forall dim. IsScalar dim => dim -> Scalar
Dim.toScalar forall a b. (a -> b) -> a -> b
$
      T v y
amp
   amplifySample :: Numeric (T v y) -> y -> y
amplifySample Numeric (T v y)
amp y
y = forall y amp. C y amp => amp -> y
toScalar Numeric (T v y)
amp forall a. C a => a -> a -> a
* y
y
   amplify :: forall (sig :: * -> *).
Transform sig y =>
Numeric (T v y) -> sig y -> sig y
amplify Numeric (T v y)
amp = forall a (sig :: * -> *).
(C a, Transform sig a) =>
a -> sig a -> sig a
FiltG.amplify (forall y amp. C y amp => amp -> y
toScalar Numeric (T v y)
amp)


{- DEPRECATED toSamples "this function drops the sample rate, better use canonicalize" -}
{-# INLINE toSamples #-}
toSamples ::
   (C y flat, SigG.Transform sig y) =>
   SigA.T rate flat (sig y) -> sig y
toSamples :: forall y flat (sig :: * -> *) rate.
(C y flat, Transform sig y) =>
T rate flat (sig y) -> sig y
toSamples T rate flat (sig y)
sig =
   forall y amp (sig :: * -> *).
(C y amp, Transform sig y) =>
amp -> sig y -> sig y
amplify (forall rate amplitude body. T rate amplitude body -> amplitude
SigA.amplitude T rate flat (sig y)
sig) (forall rate amplitude body. T rate amplitude body -> body
SigA.body T rate flat (sig y)
sig)

{-# INLINE canonicalize #-}
canonicalize ::
   (C y flat, SigG.Transform sig y) =>
   SigA.T rate flat (sig y) -> SigA.T rate (Amp.Flat y) (sig y)
canonicalize :: forall y flat (sig :: * -> *) rate.
(C y flat, Transform sig y) =>
T rate flat (sig y) -> T rate (Flat y) (sig y)
canonicalize T rate flat (sig y)
sig =
   forall rate amplitude body.
rate -> amplitude -> body -> T rate amplitude body
SigA.Cons (forall rate amplitude body. T rate amplitude body -> rate
SigA.sampleRate T rate flat (sig y)
sig) forall y. Flat y
Amp.Flat (forall y flat (sig :: * -> *) rate.
(C y flat, Transform sig y) =>
T rate flat (sig y) -> sig y
toSamples T rate flat (sig y)
sig)