{- | Copyright : (c) Henning Thielemann 2008-2009 License : GPL Maintainer : synthesizer@henning-thielemann.de Stability : provisional Portability : requires multi-parameter type classes -} module Synthesizer.Dimensional.Causal.Displacement ( mix, mixVolume, fanoutAndMixMulti, fanoutAndMixMultiVolume, raise, distort, ) where import qualified Synthesizer.Dimensional.Process as Proc import qualified Synthesizer.Dimensional.Amplitude as Amp import qualified Synthesizer.Dimensional.Arrow as ArrowD import qualified Synthesizer.Dimensional.Causal.Process as CausalD import qualified Control.Arrow as Arrow import Control.Arrow ((^<<), (&&&), ) import qualified Number.DimensionTerm as DN import qualified Algebra.DimensionTerm as Dim import qualified Algebra.Module as Module import qualified Algebra.Field as Field import qualified Algebra.Real as Real -- import qualified Algebra.Ring as Ring -- import qualified Algebra.Additive as Additive -- import Algebra.Module ((*>)) import Control.Monad.Trans.Reader (Reader, runReader, asks, ) import Control.Applicative (liftA2, ) import PreludeBase import NumericPrelude import Prelude () type DN v y = Amp.Numeric (DN.T v y) type Context v y = Reader (DN.T v y) causalMap :: (yv0 -> yv1) -> CausalD.Core s yv0 yv1 causalMap = Arrow.arr {- * Mixing -} {- | Mix two signals. In contrast to 'zipWith' the result has the length of the longer signal. -} {-# INLINE mix #-} mix :: (Real.C y, Field.C y, Module.C y yv, Dim.C v) => Proc.T s u t (CausalD.T s (DN v y, DN v y) (DN v y) (yv,yv) yv) mix = Proc.pure $ fromAmplitudeReader $ \(Amp.Numeric amp0, Amp.Numeric amp1) -> (DN.abs amp0 + DN.abs amp1, mixCore amp0 amp1) {-# INLINE mixVolume #-} mixVolume :: (Field.C y, Module.C y yv, Dim.C v) => DN.T v y -> Proc.T s u t (CausalD.T s (DN v y, DN v y) (DN v y) (yv,yv) yv) mixVolume amp = Proc.pure $ fromAmplitudeReader $ \(Amp.Numeric amp0, Amp.Numeric amp1) -> (amp, mixCore amp0 amp1) {-# INLINE mixCore #-} mixCore :: (Field.C y, Module.C y yv, Dim.C v) => DN.T v y -> DN.T v y -> Context v y (CausalD.Core s (yv,yv) yv) mixCore amp0 amp1 = liftA2 (\toSamp0 toSamp1 -> causalMap (\(y0,y1) -> toSamp0 y0 + toSamp1 y1)) (toAmplitudeVector amp0) (toAmplitudeVector amp1) {- | Mix one or more signals. -} {-# INLINE fanoutAndMixMulti #-} fanoutAndMixMulti :: (Real.C y, Field.C y, Module.C y yv, Dim.C v) => [Proc.T s u t (CausalD.T s ampIn (DN v y) yvIn yv)] -> Proc.T s u t (CausalD.T s ampIn (DN v y) yvIn yv) fanoutAndMixMulti = fmap fanoutAndMixMultiPlain . sequence {-# INLINE fanoutAndMixMultiPlain #-} fanoutAndMixMultiPlain :: (Real.C y, Field.C y, Module.C y yv, Dim.C v) => [CausalD.T s ampIn (DN v y) yvIn yv] -> CausalD.T s ampIn (DN v y) yvIn yv fanoutAndMixMultiPlain cs = fromAmplitudeReader $ \ampIn -> let ampCs = map (\(ArrowD.Cons f) -> f ampIn) cs in (maximum (map (\(_, Amp.Numeric amp) -> amp) ampCs), fanoutAndMixMultiVolumeCore ampCs) {-# INLINE fanoutAndMixMultiVolume #-} fanoutAndMixMultiVolume :: (Field.C y, Module.C y yv, Dim.C v) => DN.T v y -> [Proc.T s u t (CausalD.T s ampIn (DN v y) yvIn yv)] -> Proc.T s u t (CausalD.T s ampIn (DN v y) yvIn yv) fanoutAndMixMultiVolume amp = fmap (fanoutAndMixMultiVolumePlain amp) . sequence {-# INLINE fanoutAndMixMultiVolumePlain #-} fanoutAndMixMultiVolumePlain :: (Field.C y, Module.C y yv, Dim.C v) => DN.T v y -> [CausalD.T s ampIn (DN v y) yvIn yv] -> CausalD.T s ampIn (DN v y) yvIn yv fanoutAndMixMultiVolumePlain amp cs = fromAmplitudeReader $ \ampIn -> (amp, fanoutAndMixMultiVolumeCore $ map (\(ArrowD.Cons f) -> f ampIn) cs) {-# INLINE fanoutAndMixMultiVolumeCore #-} fanoutAndMixMultiVolumeCore :: (Field.C y, Module.C y yv, Dim.C v) => [(CausalD.Core s yvIn yv, DN v y)] -> Context v y (CausalD.Core s yvIn yv) fanoutAndMixMultiVolumeCore cs = foldr (\(c, Amp.Numeric ampX) -> liftA2 (\toSamp rest -> uncurry (+) ^<< (toSamp ^<< c) &&& rest) (toAmplitudeVector ampX)) (return $ causalMap (const zero)) cs {- | Add a number to all of the signal values. This is useful for adjusting the center of a modulation. -} {-# INLINE raise #-} raise :: (Field.C y, Module.C y yv, Dim.C v) => DN.T v y -> yv -> Proc.T s u t (CausalD.T s (DN v y) (DN v y) yv yv) raise y' yv = Proc.pure $ fromAmplitudeReader $ \(Amp.Numeric amp) -> (amp, fmap (\toSamp -> causalMap (toSamp yv +)) (toAmplitudeVector y')) {- | Distort the signal using a flat function. The first signal gives the scaling of the function. If the scaling is c and the input sample is y, then @c * f(y/c)@ is output. This way we can use an (efficient) flat function and have a simple, yet dimension conform, way of controlling the distortion. E.g. if the distortion function is @tanh@ then the value @c@ controls the saturation level. -} {-# INLINE distort #-} distort :: (Field.C y, Module.C y yv, Dim.C v) => (yv -> yv) -> Proc.T s u t (CausalD.T s (DN v y, DN v y) (DN v y) (y,yv) yv) distort f = Proc.pure $ fromAmplitudeReader $ \(Amp.Numeric ampCtrl, Amp.Numeric ampIn) -> (ampIn, fmap (\toSamp -> causalMap (\(c,y) -> let c' = toSamp c in c' *> f (recip c' *> y))) (toAmplitudeScalar ampCtrl)) {-# INLINE toAmplitudeScalar #-} toAmplitudeScalar :: (Field.C y, Dim.C u) => DN.T u y -> Reader (DN.T u y) (y -> y) toAmplitudeScalar ampIn = asks (\ampOut -> (DN.divToScalar ampIn ampOut *)) {-# INLINE toAmplitudeVector #-} toAmplitudeVector :: (Module.C y yv, Field.C y, Dim.C u) => DN.T u y -> Reader (DN.T u y) (yv -> yv) toAmplitudeVector ampIn = asks (\ampOut -> (DN.divToScalar ampIn ampOut *> )) {-# INLINE fromAmplitudeReader #-} fromAmplitudeReader :: (ampIn -> (ampOut, Reader ampOut (CausalD.Core s yv0 yv1))) -> CausalD.T s ampIn (Amp.Numeric ampOut) yv0 yv1 fromAmplitudeReader f = ArrowD.Cons $ \ampIn -> let (ampOut, rd) = f ampIn in (runReader rd ampOut, Amp.Numeric ampOut)