synthesizer-dimensional-0.2: src/Synthesizer/Dimensional/Rate/Oscillator.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{- |
Copyright : (c) Henning Thielemann 2008, 2009
License : GPL
Maintainer : synthesizer@henning-thielemann.de
Stability : provisional
Portability : requires multi-parameter type classes
-}
module Synthesizer.Dimensional.Rate.Oscillator (
{- * Oscillators with constant waveforms -}
static,
staticAntiAlias,
freqMod,
freqModAntiAlias,
phaseMod,
phaseFreqMod,
shapeMod,
shapeFreqMod,
staticSample,
freqModSample,
-- shapeFreqModSample,
shapeFreqModFromSampledTone,
shapePhaseFreqModFromSampledTone,
) where
import qualified Synthesizer.Dimensional.Abstraction.HomogeneousGen as Hom
import qualified Synthesizer.Dimensional.Abstraction.Flat as Flat
import qualified Synthesizer.Dimensional.Amplitude as Amp
import qualified Synthesizer.Dimensional.RatePhantom as RP
import qualified Synthesizer.Dimensional.RateWrapper as SigP
import qualified Synthesizer.State.Oscillator as Osci
import qualified Synthesizer.State.Signal as Sig
import qualified Synthesizer.Dimensional.Causal.Process as CausalD
import qualified Synthesizer.Dimensional.Causal.Oscillator as OsciC
import qualified Synthesizer.Dimensional.Map as MapD
import qualified Synthesizer.Generic.Signal as SigG
import qualified Synthesizer.Basic.WaveSmoothed as WaveSmooth
import qualified Synthesizer.Basic.Wave as Wave
import qualified Synthesizer.Basic.Phase as Phase
import qualified Synthesizer.Dimensional.Straight.Signal as SigS
import qualified Synthesizer.Dimensional.Cyclic.Signal as SigC
import qualified Synthesizer.Dimensional.Amplitude.Signal as SigA
import qualified Synthesizer.Dimensional.Process as Proc
import Synthesizer.Dimensional.Process (toFrequencyScalar, )
import qualified Synthesizer.Interpolation as Interpolation
import qualified Number.DimensionTerm as DN
import qualified Algebra.DimensionTerm as Dim
-- import Number.DimensionTerm ((&*&))
import qualified Algebra.RealField as RealField
import qualified Algebra.Field as Field
-- import NumericPrelude
import PreludeBase as P
{- |
This class is similar to the Homogeneous class
in the implementation,
but it is even more strict semantically.
It requires that values from the waveform
go untouched to the output signal,
whereas Homogeneous class still allows homogeneous
(aka amplitude-unit-independent) operations.
We could use the Homogeneous constraints
immediately in the oscillator functions,
but with the functional dependencies
we get more from type inference.
This way, the compiler knows,
that when we apply an oscillator to a flat wave,
that we want a flat signal as output.
-}
class (Hom.C amp (Wave.T t) wave, Hom.C amp Sig.T signal) =>
Simple amp t wave signal
| wave -> t, signal t -> wave, wave -> signal,
signal -> amp, wave -> amp where
instance Simple CausalD.Flat t (Wave.T t) (SigS.T Sig.T) where
instance (Amp.C amp) =>
Simple amp t (SigA.T amp (Wave.T t)) (SigA.T amp (SigS.T Sig.T)) where
class (Hom.C amp (WaveSmooth.T t) wave, Hom.C amp Sig.T signal) =>
Smooth amp t wave signal
| wave -> t, signal t -> wave, wave -> signal,
signal -> amp, wave -> amp where
instance Smooth CausalD.Flat t (WaveSmooth.T t) (SigS.T Sig.T) where
instance (Amp.C amp) =>
Smooth amp t (SigA.T amp (WaveSmooth.T t)) (SigA.T amp (SigS.T Sig.T)) where
withWave ::
(Hom.C amp waveStore wave, Hom.C amp Sig.T sig) =>
wave y -> (waveStore y -> Sig.T y) -> RP.T s sig y
withWave w f =
RP.fromSignal $ Hom.plainProcessSamples f w
{- * Oscillators with constant waveforms -}
{- | oscillator with a functional waveform with constant frequency -}
{-# INLINE static #-}
static ::
(RealField.C t, Dim.C u,
Simple amp t wave sig) =>
wave y {- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> DN.T (Dim.Recip u) t
{- ^ frequency -}
-> Proc.T s u t (RP.T s sig y)
static wave phase =
staticAux (\freq -> withWave wave $ \w -> Osci.static w phase freq)
{- | oscillator with a functional waveform with constant frequency -}
{-# INLINE staticAntiAlias #-}
staticAntiAlias ::
(RealField.C t, Dim.C u,
Smooth amp t wave sig) =>
wave y
{- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> DN.T (Dim.Recip u) t
{- ^ frequency -}
-> Proc.T s u t (RP.T s sig y)
staticAntiAlias wave phase =
staticAux (\freq -> withWave wave $ \w -> Osci.staticAntiAlias w phase freq)
{- | oscillator with a functional waveform with modulated frequency -}
{-# INLINE freqMod #-}
freqMod ::
(RealField.C t, Dim.C u,
Simple amp t wave sig) =>
wave y {- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> Proc.T s u t (
SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> RP.T s sig y)
freqMod wave phase =
freqModAux (\t -> withWave wave $ \w -> Osci.freqMod w phase t)
{- | oscillator with a functional waveform with modulated frequency -}
{-# INLINE freqModAntiAlias #-}
freqModAntiAlias ::
(RealField.C t, Dim.C u,
Smooth amp t wave sig) =>
wave y
{- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> Proc.T s u t (
SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> RP.T s sig y)
freqModAntiAlias wave phase =
freqModAux (\t -> withWave wave $ \w -> Osci.freqModAntiAlias w phase t)
{- | oscillator with modulated phase -}
{-# INLINE phaseMod #-}
phaseMod ::
(Flat.C flat t, RealField.C t, Dim.C u,
Simple amp t wave sig) =>
wave y {- ^ waveform -}
-> DN.T (Dim.Recip u) t
{- ^ frequency -}
-> Proc.T s u t (
RP.T s flat t
{- v phase modulation, phases must have no unit -}
-> RP.T s sig y)
phaseMod wave =
staticAux (\freq sig ->
withWave wave $ \w -> Osci.phaseMod w freq . Flat.toSamples $ sig)
{- | oscillator with modulated shape -}
{-# INLINE shapeMod #-}
shapeMod ::
(Flat.C flat c, RealField.C t, Dim.C u) =>
(c -> Wave.T t y)
{- ^ waveform -}
-> Phase.T t {- ^ phase -}
-> DN.T (Dim.Recip u) t
{- ^ frequency -}
-> Proc.T s u t (
RP.T s flat c {- v shape control -}
-> SigS.R s y)
shapeMod wave phase =
staticAux (\freq -> SigS.fromSamples . Osci.shapeMod wave phase freq . Flat.toSamples)
{- | oscillator with a functional waveform with modulated phase and frequency -}
{-# INLINE phaseFreqMod #-}
phaseFreqMod ::
(Flat.C flat t, RealField.C t, Dim.C u,
Simple amp t wave sig) =>
wave y {- ^ waveform -}
-> Proc.T s u t (
RP.T s flat t
{- v phase control -}
-> SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> RP.T s sig y)
phaseFreqMod wave =
fmap flip $
freqModAux (\ freqs phases ->
withWave wave $ \w ->
Osci.phaseFreqMod w (Flat.toSamples phases) freqs)
{- | oscillator with both shape and frequency modulation -}
{-# INLINE shapeFreqMod #-}
shapeFreqMod :: (Flat.C flat c, RealField.C t, Dim.C u) =>
(c -> Wave.T t y)
{- ^ waveform -}
-> Phase.T t {- ^ phase -}
-> Proc.T s u t (
RP.T s flat c
{- v shape control -}
-> SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> SigS.R s y)
shapeFreqMod wave phase =
fmap flip $
freqModAux
(\ freqs parameters ->
SigS.fromSamples $ Osci.shapeFreqMod wave phase (Flat.toSamples parameters) freqs)
{- |
oscillator with a sampled waveform with constant frequency
This is essentially an interpolation with cyclic padding.
You can also achieve this with a waveform constructed by 'Wave.sample'.
-}
{-# INLINE staticSample #-}
staticSample :: (RealField.C t, Dim.C u) =>
Interpolation.T t y
-> SigC.R r y {- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> DN.T (Dim.Recip u) t
{- ^ frequency -}
-> Proc.T s u t (SigS.R s y)
staticSample ip wave phase =
staticAux (SigS.fromSamples . Osci.staticSample ip (SigC.toPeriod wave) phase)
{- |
oscillator with a sampled waveform with modulated frequency
Should behave homogenously for different types of interpolation.
-}
{-# INLINE freqModSample #-}
freqModSample :: (RealField.C t, Dim.C u) =>
Interpolation.T t y
-> SigC.R r y {- ^ waveform -}
-> Phase.T t {- ^ start phase -}
-> Proc.T s u t (
SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> SigS.R s y)
freqModSample ip wave phase =
freqModAux (SigS.fromSamples . Osci.freqModSample ip (SigC.toPeriod wave) phase)
{-
{-# INLINE shapeFreqModSample #-}
shapeFreqModSample :: (RealField.C c, RealField.C t) =>
Interpolation.T c (Wave.T t y)
-> sig (Wave.T t y)
-> c -> Phase.T t
-> Proc.T s u t (
RP.T s flat c
{- v shape control -}
-> SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> SigS.R s y)
shapeFreqModSample ip waves shape0 phase =
uncurry Wave.apply ^<<
(InterpolationC.relativeConstantPad ip shape0 waves ***
freqsToPhases phase)
-}
{-# INLINE shapeFreqModFromSampledTone #-}
shapeFreqModFromSampledTone ::
(RealField.C t, SigG.Transform storage yv, Dim.C u,
Hom.C amp storage input, Hom.C amp Sig.T output,
Flat.C flat t) =>
Interpolation.T t yv
-> Interpolation.T t yv
-> DN.T (Dim.Recip u) t
{- ^ source frequency -}
-> SigP.T u t input yv
-> t -> Phase.T t
-> Proc.T s u t (
RP.T s flat t
{- v shape control -}
-> SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> RP.T s output yv)
shapeFreqModFromSampledTone
ipLeap ipStep srcFreq sampledTone shape0 phase =
flip fmap
(OsciC.shapeFreqModFromSampledTone
ipLeap ipStep srcFreq sampledTone shape0 phase)
(\osci ->
\shapes freqs ->
osci
`CausalD.applyFlatFst`
shapes
`CausalD.apply`
freqs)
{-# INLINE shapePhaseFreqModFromSampledTone #-}
shapePhaseFreqModFromSampledTone ::
(RealField.C t, SigG.Transform storage yv, Dim.C u,
Hom.C amp storage input, Hom.C amp Sig.T output,
Flat.C flatS t, Flat.C flatP t) =>
Interpolation.T t yv
-> Interpolation.T t yv
-> DN.T (Dim.Recip u) t
{- ^ source frequency -}
-> SigP.T u t input yv
-> t -> Phase.T t
-> Proc.T s u t (
RP.T s flatS t
{- v shape control -}
-> RP.T s flatP t
{- v phase control -}
-> SigA.R s (Dim.Recip u) t t
{- v frequency control -}
-> RP.T s output yv)
shapePhaseFreqModFromSampledTone
ipLeap ipStep srcFreq sampledTone shape0 phase =
flip fmap
(OsciC.shapePhaseFreqModFromSampledTone
ipLeap ipStep srcFreq sampledTone shape0 phase)
(\osci ->
\shapes phaseDistort freqs ->
(osci CausalD.<<^ MapD.packTriple)
`CausalD.applyFlatFst`
shapes
`CausalD.applyFlatFst`
phaseDistort
`CausalD.apply`
freqs)
{-# INLINE freqModAux #-}
freqModAux :: (Field.C t, Dim.C u) =>
(Sig.T t -> c)
-> Proc.T s u t (
SigA.R s (Dim.Recip u) t t
-> c)
freqModAux f =
fmap
(\toFreq -> f . SigA.scalarSamples toFreq)
(Proc.withParam toFrequencyScalar)
{-# INLINE staticAux #-}
staticAux :: (Dim.C u, Field.C t) =>
(t -> c)
-> DN.T (Dim.Recip u) t
-> Proc.T s u t c
staticAux f freq =
fmap f (toFrequencyScalar freq)