{-# OPTIONS_GHC -O2 -fno-implicit-prelude #-}
{- |
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.RealField             as RealField
-- import qualified Algebra.Field                 as Field
-- import qualified Algebra.Ring                  as Ring
import qualified Algebra.Additive              as Additive

import NumericPrelude

import qualified Prelude as P
import PreludeBase



{- * 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 :: (RealField.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 #-}
{-# SPECULATE static :: Storable b => ChunkSize -> (Double -> b) -> (Double -> Double -> Signal.T b) #-}
{- | oscillator with constant frequency -}
static :: (RealField.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 :: (RealField.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

{-# ONLINE shapeMod #-}
{- | oscillator with modulated shape -}
shapeMod :: (RealField.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 :: (RealField.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 :: (RealField.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 :: (RealField.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 :: RealField.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 :: RealField.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 #-}
{-# SPECULATE staticSine :: ChunkSize -> Double -> Double -> Signal.T Double #-}
{- | sine oscillator with static frequency -}
staticSine :: (Trans.C a, RealField.C a, Storable a) =>
   ChunkSize -> Phase.T a -> a -> Signal.T a
staticSine size = static size Wave.sine

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

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

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

{-# INLINE freqModSaw #-}
{-# SPECULATE freqModSaw :: ChunkSize -> Double -> Signal.T Double -> Signal.T Double #-}
{- | saw tooth oscillator with modulated frequency -}
freqModSaw :: (RealField.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]))
-}