{- | Copyright : (c) Henning Thielemann 2008 License : GPL Maintainer : synthesizer@henning-thielemann.de Stability : provisional Portability : requires multi-parameter type classes -} module Synthesizer.SampleRateContext.Cut ( {- * dissection -} splitAt, take, drop, takeUntilPause, unzip, unzip3, {- * glueing -} concat, concatVolume, append, appendVolume, zip, zipVolume, zip3, zip3Volume, arrange, arrangeVolume, ) where import qualified Synthesizer.Amplitude.Cut as CutV import qualified Synthesizer.Plain.Cut as CutS import qualified Synthesizer.SampleRateContext.Signal as SigC import qualified Synthesizer.SampleRateContext.Rate as Rate -- import Synthesizer.SampleRateContext.Rate (($#)) import Synthesizer.SampleRateContext.Signal (toTimeScalar, toAmplitudeScalar) import qualified Data.EventList.Relative.TimeBody as EventList import qualified Numeric.NonNegative.Class as NonNeg import qualified Algebra.NormedSpace.Maximum as NormedMax import qualified Algebra.OccasionallyScalar as OccScalar 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 Data.List as List import PreludeBase ((.), ($), Ord, (<=), map, fst, snd) -- import NumericPrelude import Prelude (RealFrac) {- * dissection -} splitAt :: (RealField.C t, Ring.C t', OccScalar.C t t') => t' -> Rate.T t t' -> SigC.T y y' yv -> (SigC.T y y' yv, SigC.T y y' yv) splitAt t' sr x = let (ss0,ss1) = List.splitAt (RealField.round (toTimeScalar sr t')) (SigC.samples x) in (SigC.replaceSamples ss0 x, SigC.replaceSamples ss1 x) take :: (RealField.C t, Ring.C t', OccScalar.C t t') => t' -> Rate.T t t' -> SigC.T y y' yv -> SigC.T y y' yv take t sr = fst . splitAt t sr drop :: (RealField.C t, Ring.C t', OccScalar.C t t') => t' -> Rate.T t t' -> SigC.T y y' yv -> SigC.T y y' yv drop t sr = snd . splitAt t sr takeUntilPause :: (RealField.C t, Ring.C t', OccScalar.C t t', Field.C y', NormedMax.C y yv, OccScalar.C y y') => y' -> t' -> Rate.T t t' -> SigC.T y y' yv -> SigC.T y y' yv takeUntilPause y' t' sr x = let t = toTimeScalar sr t' y = toAmplitudeScalar x y' in SigC.replaceSamples (CutS.takeUntilInterval ((<=y) . NormedMax.norm) (RealField.ceiling t) (SigC.samples x)) x unzip :: Rate.T t t' -> SigC.T y y' (yv0, yv1) -> (SigC.T y y' yv0, SigC.T y y' yv1) unzip = Rate.pure CutV.unzip unzip3 :: Rate.T t t' -> SigC.T y y' (yv0, yv1, yv2) -> (SigC.T y y' yv0, SigC.T y y' yv1, SigC.T y y' yv2) unzip3 = Rate.pure CutV.unzip3 {- * glueing -} {- | Similar to @foldr1 append@ but more efficient and accurate, because it reduces the number of amplifications. Does not work for infinite lists, because no maximum amplitude can be computed. -} concat :: (Ord y', Field.C y', OccScalar.C y y', Module.C y yv) => Rate.T t t' -> [SigC.T y y' yv] -> SigC.T y y' yv concat = Rate.pure $ CutV.concat {- | Give the output volume explicitly. Does also work for infinite lists. -} concatVolume :: (Field.C y', OccScalar.C y y', Module.C y yv) => y' -> Rate.T t t' -> [SigC.T y y' yv] -> SigC.T y y' yv concatVolume amp = Rate.pure $ CutV.concatVolume amp append :: (Ord y', Field.C y', OccScalar.C y y', Module.C y yv) => Rate.T t t' -> SigC.T y y' yv -> SigC.T y y' yv -> SigC.T y y' yv append = Rate.pure $ CutV.append appendVolume :: (Field.C y', OccScalar.C y y', Module.C y yv) => y' -> Rate.T t t' -> SigC.T y y' yv -> SigC.T y y' yv -> SigC.T y y' yv appendVolume amp = Rate.pure $ CutV.appendVolume amp zip :: (Ord y', Field.C y', OccScalar.C y y', Module.C y yv0, Module.C y yv1) => Rate.T t t' -> SigC.T y y' yv0 -> SigC.T y y' yv1 -> SigC.T y y' (yv0,yv1) zip = Rate.pure $ CutV.zip zipVolume :: (Field.C y', OccScalar.C y y', Module.C y yv0, Module.C y yv1) => y' -> Rate.T t t' -> SigC.T y y' yv0 -> SigC.T y y' yv1 -> SigC.T y y' (yv0,yv1) zipVolume amp = Rate.pure $ CutV.zipVolume amp zip3 :: (Ord y', Field.C y', OccScalar.C y y', Module.C y yv0, Module.C y yv1, Module.C y yv2) => Rate.T t t' -> SigC.T y y' yv0 -> SigC.T y y' yv1 -> SigC.T y y' yv2 -> SigC.T y y' (yv0,yv1,yv2) zip3 = Rate.pure $ CutV.zip3 zip3Volume :: (Field.C y', OccScalar.C y y', Module.C y yv0, Module.C y yv1, Module.C y yv2) => y' -> Rate.T t t' -> SigC.T y y' yv0 -> SigC.T y y' yv1 -> SigC.T y y' yv2 -> SigC.T y y' (yv0,yv1,yv2) zip3Volume amp = Rate.pure $ CutV.zip3Volume amp {- | Uses maximum input volume as output volume. -} arrange :: (Ring.C t', OccScalar.C t t', RealFrac t, NonNeg.C t, Ord y', Field.C y', OccScalar.C y y', Module.C y yv) => t' {-^ Unit of the time values in the time ordered list. -} -> Rate.T t t' -> EventList.T t (SigC.T y y' yv) {- ^ A list of pairs: (relative start time, signal part), The start time is relative to the start time of the previous event. -} -> SigC.T y y' yv {- ^ The mixed signal. -} arrange unit' sr sched = let amp = List.maximum (map SigC.amplitude (EventList.getBodies sched)) in arrangeVolume amp unit' sr sched {- | Given a list of signals with time stamps, mix them into one signal as they occur in time. Ideally for composing music. Infinite schedules are not supported. Does not work for infinite lists, because no maximum amplitude can be computed. -} arrangeVolume :: (Ring.C t', OccScalar.C t t', RealFrac t, NonNeg.C t, Field.C y', OccScalar.C y y', Module.C y yv) => y' {-^ Output volume. -} -> t' {-^ Unit of the time values in the time ordered list. -} -> Rate.T t t' -> EventList.T t (SigC.T y y' yv) {- ^ A list of pairs: (relative start time, signal part), The start time is relative to the start time of the previous event. -} -> SigC.T y y' yv {- ^ The mixed signal. -} arrangeVolume amp unit' sr sched' = let unit = toTimeScalar sr unit' sched = EventList.mapBody (SigC.vectorSamples (toAmplitudeScalar z)) sched' z = SigC.Cons amp (CutS.arrange (EventList.resample unit sched)) in z