{-# LANGUAGE Rank2Types #-} {- | Signals equipped with volume and sample rate information that may carry a unit. Kind of volume and sample rate is configurable by types. -} module Synthesizer.Dimensional.Signal.Private where import qualified Synthesizer.Dimensional.Amplitude as Amp import qualified Synthesizer.Dimensional.Rate as Rate import qualified Synthesizer.Dimensional.Process as Proc import qualified Synthesizer.Generic.Filter.NonRecursive as FiltG import qualified Synthesizer.Generic.Signal as SigG -- import qualified Data.StorableVector.Lazy.Pattern as SVP import qualified Synthesizer.Storable.Signal as SigSt import qualified Synthesizer.Frame.Stereo as Stereo import qualified Synthesizer.Basic.Binary as BinSmp import Data.Int (Int16, ) import Foreign.Storable (Storable, ) import qualified Synthesizer.State.Signal as Sig import qualified Algebra.Module as Module import qualified Algebra.RealField as RealField import qualified Algebra.Field as Field import qualified Algebra.Ring as Ring import qualified Number.DimensionTerm as DN import qualified Algebra.DimensionTerm as Dim -- import NumericPrelude import PreludeBase as P import Prelude () {- | A signal value 0.5 at global amplitude 1 and signal value 1 at global amplitude 0.5 shall represent the same signal. Thus it is unsafe to observe the amplitude. ToDo: Maybe we should support zipped signals with mixed amplitudes, e.g. @T rate (amp0, amp1) (Sig.T (y0,y1))@ in order to be compliant with the way @Causal@ and @Wave.Controlled@ handle multiple sources. However, this is dangerous, since @T rate amp (Sig.T (y0,y1))@ might be used for stereo signals. Of course, for stereo signals @Stereo.T@ should be prefered. Cyclic nature such as needed for Fourier transform must be expressend in the body. It would be nice to use the data type for waveforms, too, but for waveforms the @rate@ parameter makes no sense. -} data T rate amplitude body = Cons { sampleRate :: rate, amplitude :: amplitude, body :: body } type R s v y yv = T (Rate.Phantom s) (Amp.Dimensional v y) (Sig.T yv) {-# INLINE actualSampleRate #-} actualSampleRate :: T (Rate.Actual rate) amp sig -> rate actualSampleRate sig = let (Rate.Actual amp) = sampleRate sig in amp {-# INLINE actualAmplitude #-} actualAmplitude :: T rate (Amp.Numeric amp) sig -> amp actualAmplitude sig = let (Amp.Numeric amp) = amplitude sig in amp {-# INLINE toAmplitudeScalar #-} toAmplitudeScalar :: (Field.C y, Dim.C v) => T rate (Amp.Dimensional v y) sig -> DN.T v y -> y toAmplitudeScalar sig y = DN.divToScalar y (actualAmplitude sig) {-# INLINE rewriteAmplitudeDimension #-} rewriteAmplitudeDimension :: (Dim.C v0, Dim.C v1) => (v0 -> v1) -> T rate (Amp.Dimensional v0 y) sig -> T rate (Amp.Dimensional v1 y) sig rewriteAmplitudeDimension f (Cons rate (Amp.Numeric amp) ss) = Cons rate (Amp.Numeric $ DN.rewriteDimension f amp) ss {-# INLINE asTypeOfAmplitude #-} asTypeOfAmplitude :: y -> T rate (Amp.Dimensional v y) sig -> y asTypeOfAmplitude = const {-# INLINE scalarSamples #-} scalarSamples :: (Ring.C y, SigG.Transform sig y) => (amp -> y) -> T rate (Amp.Numeric amp) (sig y) -> sig y scalarSamples toAmpScalar sig = let y = toAmpScalar (actualAmplitude sig) in FiltG.amplify y (body sig) {-# INLINE vectorSamples #-} vectorSamples :: (Module.C y yv, SigG.Transform sig yv) => (amp -> y) -> T rate (Amp.Numeric amp) (sig yv) -> sig yv vectorSamples toAmpScalar sig = let y = toAmpScalar (actualAmplitude sig) in FiltG.amplifyVector y (body sig) {-# INLINE embedSampleRate #-} embedSampleRate :: (Dim.C u) => Proc.T s u t (T (Rate.Phantom s) amp sig -> T (Rate.Dimensional u t) amp sig) embedSampleRate = fmap (\rate (Cons _ amp sig) -> Cons (Rate.Actual rate) amp sig) Proc.getSampleRate {-# INLINE render #-} render :: (Dim.C u) => DN.T (Dim.Recip u) t -> (forall s. Proc.T s u t (T (Rate.Phantom s) amp sig)) -> T (Rate.Dimensional u t) amp sig render rate signal = Proc.run rate (embedSampleRate Proc.$: signal) {-# INLINE processBody #-} processBody :: (sig0 -> sig1) -> T rate amp sig0 -> T rate amp sig1 processBody f (Cons rate amp sig) = Cons rate amp (f sig) {-# INLINE replaceBody #-} replaceBody :: sig1 -> T rate amp sig0 -> T rate amp sig1 replaceBody sig = processBody (const sig) {-# INLINE fromBody #-} fromBody :: amp -> sig -> T (Rate.Phantom s) (Amp.Numeric amp) sig fromBody amp = Cons Rate.Phantom (Amp.Numeric amp) {-# INLINE flatFromBody #-} flatFromBody :: sig -> T (Rate.Phantom s) (Amp.Flat y) sig flatFromBody = Cons Rate.Phantom Amp.Flat {-# INLINE abstractFromBody #-} abstractFromBody :: sig -> T (Rate.Phantom s) Amp.Abstract sig abstractFromBody = Cons Rate.Phantom Amp.Abstract {-# INLINE primitiveFromBody #-} primitiveFromBody :: (Amp.Primitive amp) => sig -> T (Rate.Phantom s) amp sig primitiveFromBody = Cons Rate.Phantom Amp.primitive -- * caching {-# INLINE cache #-} cache :: (Storable yv) => T rate amp (Sig.T yv) -> T rate amp (Sig.T yv) cache = processBody (Sig.fromStorableSignal . Sig.toStorableSignal SigSt.defaultChunkSize) {-# INLINE bindCached #-} bindCached :: (Storable yv) => Proc.T s u t (T rate amp (Sig.T yv)) -> (T rate amp (Sig.T yv) -> Proc.T s u t b) -> Proc.T s u t b bindCached x y = y . cache =<< x {-# INLINE share #-} share :: (Storable yv) => Proc.T s u t (T rate amp (Sig.T yv)) -> (Proc.T s u t (T rate amp (Sig.T yv)) -> Proc.T s u t b) -> Proc.T s u t b share x y = bindCached x (y . return) {-# INLINE store #-} store :: (RealField.C t, Dim.C u, Storable yv) => DN.T u t -> Proc.T s u t ( {- Rate.Phantom required, because chunk size is dicretized with respect to the process' sample rate -} T (Rate.Phantom s) amp (Sig.T yv) -> T (Rate.Phantom s) amp (SigSt.T yv)) store chunkSize = fmap (\cs -> processBody (Sig.toStorableSignal (SigSt.chunkSize cs))) (Proc.intFromTime "Dimensional.Signal.store" chunkSize) {- better use ChunkySize.Signal.store we do not need Proc context {-# INLINE storeTake #-} storeTake :: (RealField.C t, Dim.C u, Storable yv) => Proc.T s u t ( T (Rate.Phantom s) Amp.Abstract SVP.LazySize -> T (Rate.Phantom s) amp (Sig.T yv) -> T (Rate.Phantom s) amp (SigSt.T yv)) storeTake = return (\cs -> processBody (Sig.toStorableSignalVary (body cs))) -} {-# INLINE restore #-} restore :: (SigG.Read sig yv) => T rate amp (sig yv) -> T rate amp (Sig.T yv) restore = processBody SigG.toState {- {-# INLINE restore #-} restore :: (Storable yv) => T rate amp (SigSt.T yv) -> T rate amp (Sig.T yv) restore = processBody Sig.fromStorableSignal -} {-# INLINE toStorableInt16Mono #-} toStorableInt16Mono :: (RealField.C a) => T rate (Amp.Dimensional Dim.Voltage a) (Sig.T a) -> SigSt.T Int16 toStorableInt16Mono = Sig.toStorableSignal SigSt.defaultChunkSize . Sig.map BinSmp.int16FromCanonical . scalarSamples (DN.toNumberWithDimension Dim.voltage) {-# INLINE toStorableInt16Stereo #-} toStorableInt16Stereo :: (Module.C a a, RealField.C a) => T rate (Amp.Dimensional Dim.Voltage a) (Sig.T (Stereo.T a)) -> SigSt.T (Stereo.T Int16) toStorableInt16Stereo = Sig.toStorableSignal SigSt.defaultChunkSize . Sig.map (Stereo.map BinSmp.int16FromCanonical) . vectorSamples (DN.toNumberWithDimension Dim.voltage)