synthesizer-core-0.7: src/Synthesizer/PiecewiseConstant/Signal.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Synthesizer.PiecewiseConstant.Signal (
T,
StrictTime,
ShortStrictTime,
LazyTime,
subdivideLazy,
subdivideLazyToShort,
subdivideLongStrict,
chopLongTime,
longFromShortTime,
zipWith,
) where
import qualified Data.EventList.Relative.TimeTime as EventListTT
-- import qualified Data.EventList.Relative.TimeMixed as EventListTM
import qualified Data.EventList.Relative.MixedTime as EventListMT
import qualified Data.EventList.Relative.BodyTime as EventListBT
-- import qualified Data.EventList.Relative.TimeBody as EventList
import qualified Numeric.NonNegative.Class as NonNeg
import qualified Numeric.NonNegative.Wrapper as NonNegW
import qualified Numeric.NonNegative.Chunky as NonNegChunky
import Numeric.NonNegative.Class ((-|), )
import Control.Monad.Trans.State (evalState, get, put, )
import Data.Traversable (traverse, )
import qualified Data.List as List
import Data.Maybe.HT (toMaybe, )
import NumericPrelude.Numeric
import NumericPrelude.Base hiding (zipWith, )
import qualified Prelude as P
type StrictTime = NonNegW.Integer
type ShortStrictTime = NonNegW.Int
type LazyTime = NonNegChunky.T StrictTime
type T = EventListBT.T StrictTime
{-# INLINE subdivideLazy #-}
subdivideLazy ::
(NonNeg.C time) =>
EventListBT.T (NonNegChunky.T time) body ->
EventListBT.T time body
subdivideLazy =
EventListBT.foldrPair
(\y lt r ->
List.foldr
(\dt ->
EventListMT.consBody y .
EventListMT.consTime dt) r $
NonNegChunky.toChunks (NonNegChunky.normalize lt))
EventListBT.empty
{- |
Subdivide lazy times into chunks that fit into the number range
representable by @Int@.
-}
{-# INLINE subdivideLazyToShort #-}
subdivideLazyToShort ::
EventListBT.T LazyTime y -> EventListBT.T ShortStrictTime y
subdivideLazyToShort =
subdivideLazy .
EventListBT.mapTime
(NonNegChunky.fromChunks .
List.concatMap chopLongTime .
NonNegChunky.toChunks)
{- |
Returns a list of non-zero times.
-}
{-# INLINE chopLongTime #-}
chopLongTime :: StrictTime -> [ShortStrictTime]
chopLongTime n =
let d = fromIntegral (maxBound :: Int)
(q,r) = P.divMod (NonNegW.toNumber n) d
in map (NonNegW.fromNumberMsg "chopLongTime" . fromInteger) $
List.genericReplicate q d ++
if not $ isZero r then [r] else []
{-# INLINE longFromShortTime #-}
longFromShortTime :: ShortStrictTime -> StrictTime
longFromShortTime =
NonNegW.fromNumberMsg "longFromShortTime" .
fromIntegral .
NonNegW.toNumber
{-# INLINE subdivideLongStrict #-}
subdivideLongStrict ::
EventListBT.T StrictTime y -> EventListBT.T ShortStrictTime y
subdivideLongStrict =
subdivideLazy .
EventListBT.mapTime
(NonNegChunky.fromChunks . chopLongTime)
_subdivideMaybe ::
EventListBT.T LazyTime y -> EventListBT.T StrictTime (Maybe y)
_subdivideMaybe =
EventListBT.foldrPair
(\y lt r ->
case NonNegChunky.toChunks (NonNegChunky.normalize lt) of
[] -> r
(t:ts) ->
EventListBT.cons (Just y) t $
List.foldr (EventListBT.cons Nothing) r ts)
EventListBT.empty
{- |
When a lazy time value is split into chunks
then do not just replicate the sample for the whole time,
but insert 'Nothing's.
-}
{-# INLINE subdivideMaybe #-}
subdivideMaybe ::
EventListTT.T LazyTime y ->
EventListTT.T StrictTime (Maybe y)
subdivideMaybe =
EventListTT.foldr
(\lt r ->
uncurry EventListMT.consTime $
case NonNegChunky.toChunks (NonNegChunky.normalize lt) of
[] ->
(NonNegW.fromNumber zero, r)
(t:ts) ->
(t, List.foldr (EventListBT.cons Nothing) r ts))
(\y r -> EventListMT.consBody (Just y) r)
EventListBT.empty
{-# INLINE unionMaybe #-}
unionMaybe ::
EventListTT.T StrictTime (Maybe y) ->
EventListTT.T LazyTime y
unionMaybe =
EventListTT.foldr
(\t ->
EventListMT.mapTimeHead
(NonNegChunky.fromChunks . (t:) . NonNegChunky.toChunks))
(\my ->
case my of
Nothing -> id
Just y ->
EventListMT.consTime NonNegChunky.zero .
EventListMT.consBody y)
(EventListTT.pause NonNegChunky.zero)
zipWithCore ::
(NonNeg.C time) =>
(a -> b -> c) ->
a -> b ->
EventListTT.T time (Maybe a) ->
EventListTT.T time (Maybe b) ->
EventListTT.T time (Maybe c)
zipWithCore f =
let switch ac ar g =
flip (EventListMT.switchBodyL EventListBT.empty) ar $ \am ar1 ->
g (maybe (False,ac) ((,) True) am) ar1
cont j ac bc as bs =
EventListMT.consBody (toMaybe j $ f ac bc) $
recourse ac bc as bs
recourse ac bc as bs =
flip EventListMT.switchTimeL as $ \at ar ->
flip EventListMT.switchTimeL bs $ \bt br ->
let ct = min at bt
in -- ToDo: redundant comparison of 'at' and 'bt'
EventListMT.consTime ct $
case compare at bt of
LT ->
switch ac ar $ \(ab,a) ar1 ->
cont ab a bc ar1 (EventListMT.consTime (bt-|ct) br)
GT ->
switch bc br $ \(bb,b) br1 ->
cont bb ac b (EventListMT.consTime (at-|ct) ar) br1
EQ ->
switch ac ar $ \(ab,a) ar1 ->
switch bc br $ \(bb,b) br1 ->
cont (ab||bb) a b ar1 br1
in recourse
zipWith ::
(NonNeg.C time) =>
(a -> b -> c) ->
EventListBT.T time a ->
EventListBT.T time b ->
EventListBT.T time c
zipWith f as0 bs0 =
flip (EventListMT.switchBodyL EventListBT.empty) as0 $ \a0 as1 ->
flip (EventListMT.switchBodyL EventListBT.empty) bs0 $ \b0 bs1 ->
let c0 = f a0 b0
in EventListMT.consBody c0 $
flip evalState c0 $
traverse (\mc -> maybe (return ()) put mc >> get) $
zipWithCore f a0 b0 (fmap Just as1) (fmap Just bs1)
_zipWithLazy ::
(a -> b -> c) ->
EventListBT.T LazyTime a ->
EventListBT.T LazyTime b ->
EventListBT.T LazyTime c
_zipWithLazy f as0 bs0 =
flip (EventListMT.switchBodyL EventListBT.empty) as0 $ \a0 as1 ->
flip (EventListMT.switchBodyL EventListBT.empty) bs0 $ \b0 bs1 ->
EventListMT.consBody (f a0 b0) $ unionMaybe $
zipWithCore f a0 b0 (subdivideMaybe as1) (subdivideMaybe bs1)
{-
*Synthesizer.PiecewiseConstant.ALSA.MIDI Data.EventList.Relative.MixedTime> zipWithLazy (,) ('a' ./ 2 /. 'b' ./ 7 /. EventListBT.empty) ('c' ./ (1 P.+ 1) /. 'd' ./ 1 /. EventListBT.empty)
-}