module Main where import qualified SpectralDistribution as SD import qualified SignalProcessing as SP import qualified Synthesizer.Plain.Filter.Recursive.Universal as UniFilt import qualified Synthesizer.Plain.Filter.Recursive.FirstOrder as Filt1 import qualified Synthesizer.Plain.Filter.Recursive as FiltRec import qualified Synthesizer.Generic.Signal as SigG import qualified Synthesizer.State.Filter.NonRecursive as FiltNR import qualified Synthesizer.State.Signal as SigS import qualified Synthesizer.State.Oscillator as Osci import qualified Synthesizer.State.Displacement as Disp import qualified Synthesizer.State.Control as Ctrl import qualified Synthesizer.State.Noise as Noise import qualified Synthesizer.Causal.Process as Causal import Synthesizer.Causal.Class (($<), ($*), ) import qualified Data.StorableVector.Lazy as SVL import Control.Arrow ((&&&), (<<<), (^<<), (<<^), ) import Control.Monad (liftM2, ) import Data.Foldable (forM_, ) import NumericPrelude.Numeric import NumericPrelude.Base import Prelude () frequency :: Float frequency = 0.01 duration :: Int duration = 50000 toneEnvelope :: SigS.T Float toneEnvelope = FiltNR.envelope (Ctrl.exponential 20000 1) $ Osci.staticSine zero frequency toneChirp :: SigS.T Float toneChirp = Osci.freqModSine zero $ Ctrl.exponential 10000 (0.1::Float) {- | @sqrt@ asserts that the spectral centroid stays constant. This works, because our time-domain definition of spectral centroid computes the quadratic mean of frequencies. -} toneBroaden :: SigS.T Float toneBroaden = FiltNR.amplify 0.5 $ Disp.mix (Osci.freqModSine zero $ fmap sqrt $ Ctrl.line duration (0.01, 0.02::Float)) (Osci.freqModSine zero $ fmap sqrt $ Ctrl.line duration (0.01, 0::Float)) toneMix :: Float -> Float -> SigS.T Float toneMix freq0 freq1 = FiltNR.amplify 0.5 $ Disp.mix (Osci.staticSine zero freq0) (Osci.staticSine zero freq1) noiseEnvelope :: SigS.T Float noiseEnvelope = FiltNR.envelope (Ctrl.exponential 20000 1) Noise.white noiseChirp :: SigS.T Float noiseChirp = (UniFilt.bandpass ^<< UniFilt.causal) $< SigS.map (UniFilt.parameter . FiltRec.Pole 10) (Ctrl.exponential 10000 (0.1::Float)) $* Noise.white noiseBroaden :: SigS.T Float noiseBroaden = (UniFilt.bandpass ^<< UniFilt.causal) $< SigS.map (UniFilt.parameter . flip FiltRec.Pole frequency) (Ctrl.exponential 10000 (100::Float)) $* FiltNR.amplify 0.5 Noise.white smooth :: Causal.T Float Float smooth = Filt1.lowpass_ ^<< Filt1.causal <<< Causal.feedConstFst (Filt1.parameter (0.0002::Float)) volume :: SigS.T Float -> Float volume = SigS.sum . SigS.map abs followEnvelope :: Causal.T Float Float followEnvelope = smooth <<^ abs spectralDistribution1 :: Causal.T Float (SD.T Float) spectralDistribution1 = (\(d0,(d1,d2)) -> SD.mapSpread SD.signedSqrt $ SD.spectralDistribution1 d0 d1 d2) ^<< (followEnvelope <<< SP.zerothMoment) &&& (followEnvelope <<< SP.firstMoment) &&& (followEnvelope <<< SP.secondMoment) volumeSquare :: SigS.T Float -> Float volumeSquare = SigS.sum . SigS.map (^2) meanSquare :: Causal.T Float Float meanSquare = smooth <<^ (^2) spectralDistribution2 :: Causal.T Float (SD.T Float) spectralDistribution2 = (\(d0,(d1,d2)) -> SD.mapSpread SD.signedSqrt $ SD.spectralDistribution2 d0 d1 d2) ^<< (meanSquare <<< SP.zerothMoment) &&& (meanSquare <<< SP.firstMoment) &&& (meanSquare <<< SP.secondMoment) main :: IO () main = do forM_ [(0.01, 0.01), (0.01, 0.01*sqrt 2), (0.01, 0.02), (0.01, 0.04)] $ \(freq0,freq1) -> let sig = SigS.take 100000 $ toneMix freq0 freq1 d0 = volume $ Causal.apply SP.zerothMoment sig d1 = volume $ Causal.apply SP.firstMoment sig d2 = volume $ Causal.apply SP.secondMoment sig (SD.Cons centroid1 spread1) = SD.spectralDistribution1 d0 d1 d2 s0 = volumeSquare $ Causal.apply SP.zerothMoment sig s1 = volumeSquare $ Causal.apply SP.firstMoment sig s2 = volumeSquare $ Causal.apply SP.secondMoment sig (SD.Cons centroid2 spread2) = SD.spectralDistribution2 s0 s1 s2 r1 = s1/s0 in do putStrLn $ "\nfreqs: " ++ show (freq0,freq1) print (s0, s1, s2, s1/s0, s2/s1) print (s2/s0, r1^2, s2/s0-r1^2) print (sqrt ((freq0^2+freq1^2)/2), (centroid1 / (2*pi), sqrt spread1 / (2*pi)), (centroid2 / (2*pi), sqrt spread2 / (2*pi))) let signals = ("tone-env", toneEnvelope) : ("tone-chirp", toneChirp) : ("tone-broaden", toneBroaden) : ("noise-env", noiseEnvelope) : ("noise-chirp", noiseChirp) : ("noise-broaden", noiseBroaden) : [] write process filename = SVL.writeFile filename . SigG.fromState SigG.defaultLazySize . SigS.take duration . process renderers = ("", write id) : ("-distribution1", write $ Causal.apply spectralDistribution1) : ("-distribution2", write $ Causal.apply spectralDistribution2) : [] sequence_ $ liftM2 (\(featureName,render) (signalName,signal) -> render ("/tmp/" ++ signalName ++ featureName ++ ".f32") signal) renderers signals