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

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

Tone generators
-}
module Synthesizer.Storable.Oscillator where

import qualified Synthesizer.Basic.Wave       as Wave
import qualified Synthesizer.Basic.Phase      as Phase

import qualified Synthesizer.Storable.Signal as Signal
import Synthesizer.Storable.Signal (ChunkSize)
import Foreign.Storable (Storable)

-- import qualified Synthesizer.Plain.Interpolation as Interpolation

{-
import qualified Algebra.RealTranscendental    as RealTrans
import qualified Algebra.Module                as Module
import qualified Algebra.VectorSpace           as VectorSpace

import Algebra.Module((*>))
-}
import qualified Algebra.Transcendental        as Trans
-- import qualified Algebra.Field                 as Field
import qualified Algebra.RealRing              as RealRing
import qualified Algebra.Additive              as Additive

import NumericPrelude.Numeric

import qualified Prelude as P
import NumericPrelude.Base



{- * Oscillators with arbitrary but constant waveforms -}

{-# INLINE freqToPhase #-}
{- | Convert a list of phase steps into a list of momentum phases
     phase is a number in the interval [0,1)
     freq contains the phase steps -}
freqToPhase :: (RealRing.C a, Storable a) =>
   Phase.T a -> Signal.T a -> Signal.T (Phase.T a)
freqToPhase phase freq = Signal.scanL (flip Phase.increment) phase freq


{-# INLINE static #-}
{- disabled SPECIALISE static :: Storable b => ChunkSize -> (Double -> b) -> (Double -> Double -> Signal.T b) -}
{- | oscillator with constant frequency -}
static :: (RealRing.C a, Storable a, Storable b) =>
    ChunkSize -> Wave.T a b -> (Phase.T a -> a -> Signal.T b)
static size wave phase freq =
    Signal.map (Wave.apply wave) (Signal.iterate size (Phase.increment freq) phase)

{- | oscillator with modulated phase -}
phaseMod :: (RealRing.C a, Storable a, Storable b) =>
    ChunkSize -> Wave.T a b -> a -> Signal.T a -> Signal.T b
phaseMod size wave = shapeMod size (Wave.phaseOffset wave) zero

{- disabled INLINE shapeMod -}
{- | oscillator with modulated shape -}
shapeMod :: (RealRing.C a, Storable a, Storable b, Storable c) =>
    ChunkSize -> (c -> Wave.T a b) -> Phase.T a -> a -> Signal.T c -> Signal.T b
shapeMod size wave phase freq parameters =
    Signal.zipWith (Wave.apply . wave) parameters
       (Signal.iterate size (Phase.increment freq) phase)

{- | oscillator with modulated frequency -}
freqMod :: (RealRing.C a, Storable a, Storable b) =>
    ChunkSize -> Wave.T a b -> Phase.T a -> Signal.T a -> Signal.T b
freqMod _size wave phase freqs =
    Signal.map (Wave.apply wave) (freqToPhase phase freqs)

{- | oscillator with both phase and frequency modulation -}
phaseFreqMod :: (RealRing.C a, Storable a, Storable b) =>
    ChunkSize -> Wave.T a b -> Signal.T a -> Signal.T a -> Signal.T b
phaseFreqMod size wave =
    shapeFreqMod size (Wave.phaseOffset wave) zero

{- | oscillator with both shape and frequency modulation -}
shapeFreqMod :: (RealRing.C a, Storable a, Storable b, Storable c) =>
    ChunkSize -> (c -> Wave.T a b) ->
    Phase.T a -> Signal.T c -> Signal.T a -> Signal.T b
shapeFreqMod _size wave phase parameters freqs =
    Signal.zipWith (Wave.apply . wave) parameters (freqToPhase phase freqs)


{-
{- | oscillator with a sampled waveform with constant frequency
     This essentially an interpolation with cyclic padding. -}
staticSample :: RealRing.C a => Interpolation.T a b -> Signal.T b -> a -> a -> Signal.T b
staticSample ip wave phase freq =
    freqModSample ip wave phase (repeat freq)

{- | oscillator with a sampled waveform with modulated frequency
     Should behave homogenously for different types of interpolation. -}
freqModSample :: RealRing.C a => Interpolation.T a b -> Signal.T b -> a -> Signal.T a -> Signal.T b
freqModSample ip wave phase freqs =
    let len = fromIntegral (length wave)
    in  Interpolation.multiRelativeCyclicPad
           ip (phase*len) (Signal.map (*len) freqs) wave
-}



{- * Oscillators with specific waveforms -}

{-# INLINE staticSine #-}
{- disabled SPECIALISE staticSine :: ChunkSize -> Double -> Double -> Signal.T Double -}
{- | sine oscillator with static frequency -}
staticSine :: (Trans.C a, RealRing.C a, Storable a) =>
   ChunkSize -> Phase.T a -> a -> Signal.T a
staticSine size = static size Wave.sine

{-# INLINE freqModSine #-}
{- disabled SPECIALISE freqModSine :: ChunkSize -> Double -> Signal.T Double -> Signal.T Double -}
{- | sine oscillator with modulated frequency -}
freqModSine :: (Trans.C a, RealRing.C a, Storable a) =>
   ChunkSize -> Phase.T a -> Signal.T a -> Signal.T a
freqModSine size = freqMod size Wave.sine

{-# INLINE phaseModSine #-}
{- disabled SPECIALISE phaseModSine :: ChunkSize -> Double -> Signal.T Double -> Signal.T Double -}
{- | sine oscillator with modulated phase, useful for FM synthesis -}
phaseModSine :: (Trans.C a, RealRing.C a, Storable a) =>
   ChunkSize -> a -> Signal.T a -> Signal.T a
phaseModSine size = phaseMod size Wave.sine

{-# INLINE staticSaw #-}
{- disabled SPECIALISE staticSaw :: ChunkSize -> Double -> Double -> Signal.T Double -}
{- | saw tooth oscillator with modulated frequency -}
staticSaw :: (RealRing.C a, Storable a) =>
   ChunkSize -> Phase.T a -> a -> Signal.T a
staticSaw size = static size Wave.saw

{-# INLINE freqModSaw #-}
{- disabled SPECIALISE freqModSaw :: ChunkSize -> Double -> Signal.T Double -> Signal.T Double -}
{- | saw tooth oscillator with modulated frequency -}
freqModSaw :: (RealRing.C a, Storable a) =>
   ChunkSize -> Phase.T a -> Signal.T a -> Signal.T a
freqModSaw size = freqMod size Wave.saw


{- Test whether Fusion takes place.
For the following code the simplifier can't resist!

testLength :: (Storable a, Enum a) => a -> Int
testLength x =
   Signal.length (Signal.map succ (Signal.fromList (Signal.ChunkSize 100) [x,x,x]))
-}