synthesizer-core-0.8.4: src/Synthesizer/Plain/Oscillator/BandLimited.hs
{-# LANGUAGE NoImplicitPrelude #-}
{- |
Tone generators with measures for band-limitation.
They are not exactly band-limiting because this would cause infinite lag.
Instead we use only cubic interpolation polynomials.
This still incurs a small lag.
<https://youtu.be/lpM4Tawq-XU>
-}
module Synthesizer.Plain.Oscillator.BandLimited where
import qualified Synthesizer.Plain.Signal as Sig
import qualified Algebra.RealField as RealField
import NumericPrelude.Numeric
import NumericPrelude.Base
{-
sinc approximation, that could be used for band-limited oscillators:
GP.plotFuncs [] (GP.linearScale 1000 (-2,2::Double)) [\x -> if x<0 then (if x< -1 then (x+1)*(x+2)*(x+2) else 1-x*x*2-x*x*x) else (if x<1 then 1-x*x*2+x*x*x else -(x-1)*(x-2)*(x-2)), \x -> if x==0 then 1 else sin (pi*x)/(pi*x)]
Has the same tangent as sinc-pi at point 1.
Cf.
DSP.Filter.FIR.PolyInterp
Integral Sine: gsl_sf_Si
-}
{- | impulse train with static frequency -}
staticImpulses :: (RealField.C a) => a -> a -> Sig.T a
staticImpulses phase = freqModImpulses phase . repeat
{- | impulse train with modulated frequency -}
freqModImpulses :: (RealField.C a) => a -> Sig.T a -> Sig.T a
freqModImpulses phase =
(\ ~(~(_,remaining),xs) -> xs ++ remaining) .
Sig.mapAccumL
(\freq (p0,xs0) ->
let p1 = p0+freq
(p2, xs1) =
if p1>=1
then
let p1frac=fraction p1
t=p1frac/freq
t_2 = t*t; y0 = t_2*(t-1)
t1_2 = (t-1)*(t-1); y3 = -t1_2*t
in (p1frac, xs0 + [y0, 1-t1_2+y3, 1-t_2+y0, y3])
else (p1, xs0)
(x3,xs3) =
case xs1 of
[] -> (0,[])
x2:xs2 -> (x2,xs2)
in Just $ (x3, (p2,xs3)))
(phase,[])