mediabus-0.4.0.0: src/Data/MediaBus/Conduit/SyncStream.hs
-- | 'Conduit's that remove or set the sequence numbers and time stamps in
-- 'Stream's. The functions in this module lift the functions in 'SyncStream's
-- to conduits.
module Data.MediaBus.Conduit.SyncStream
( assumeSynchronizedC
, setSequenceNumberAndTimestampC
, convertTimestampC
, setTimestampFromDurationsC
, removeTimestampC
) where
import Control.Monad.State.Strict
import Control.DeepSeq (NFData)
import Data.Conduit
import Data.Conduit.Lift
import Data.MediaBus.Basics.Ticks
import Data.MediaBus.Conduit.Stream
import Data.MediaBus.Media.Stream
import Data.MediaBus.Media.SyncStream
-- * Combined Sequence number and timestamp calculation
-- | Assign sequence numbers and timestamps to the 'Frame's in a 'SyncStream',
-- starting both the sequence number and timestamp at @0@.
-- This functions is /strict/ and uses 'setSequenceNumberAndTimestamp'
-- under the hood.
-- Inorder to calculate only the 'timestamp' of a stream use
-- 'setTimestampFromDurationsC'.
setSequenceNumberAndTimestampC
:: (Monad m, KnownRate r, HasDuration d, Num s, CanBeTicks r t)
=> ConduitM (SyncStream i p d) (Stream i s (Ticks r t) p d) m ()
setSequenceNumberAndTimestampC =
evalStateC
(0, 0)
(awaitForever
(\sIn -> do
sOut <- lift (state (setSequenceNumberAndTimestamp sIn))
yield sOut))
-- | Remove the sequence numbers and time stamps from a 'Stream'.
-- It's much more explicit to use a 'SyncStream' instead of a 'Stream'.
-- For example, when a library function aggregates 'Frame's but doesn't regard
-- the sequence numbers and time stamps, using 'SyncStream' indicates to users
-- of that library, that the function does not handle any gaps and/or out of order
-- packages or discrepancies in the time stamps and e.g. frame durations.
--
-- The user then knows, that she has to add functions to that conduit to accomodate
-- for that.
--
-- This functions is /strict/ and uses 'assumeSynchronized'
-- under the hood.
assumeSynchronizedC
:: (Monad m, KnownRate r, HasDuration d, Num s, CanBeTicks r t)
=> ConduitM (SyncStream i p d) (Stream i s (Ticks r t) p d) m ()
assumeSynchronizedC =
evalStateC
(0, 0)
(awaitForever
(\sIn -> do
sOut <- lift (state (setSequenceNumberAndTimestamp sIn))
yield sOut))
-- * Timestamp calculation and conversion
-- | Set the timestamp of each element in the conduit.
-- The timestamp of each element is calculated from the sum of the durations of
-- the previous elements and the start time stamp @t0@.
--
-- The input elements must be instances of 'HasTimestamp' but with the important
-- condition, that the input timestamp is always /unit/ i.e. @()@.
-- This prevents /meaningful/ timestamps from being overwritten.
--
-- Use 'removeTimestampC' to explicitly remove a timestamp.
setTimestampFromDurationsC
:: forall m r t a.
( Monad m
, CanBeTicks r t
, HasDuration a
, HasTimestamp a
, GetTimestamp a ~ ()
)
=> Ticks r t -> Conduit a m (SetTimestamp a (Ticks r t))
setTimestampFromDurationsC t0 = evalStateC t0 (awaitForever go)
where
go !sb = lift (state (setTimestampFromDurations sb)) >>= yield
-- | Explicitly remove a timestamp, by setting the timestamp to @()@.
removeTimestampC
:: (Monad m, HasTimestamp a)
=> Conduit a m (SetTimestamp a ())
removeTimestampC = awaitForever (yield . removeTimestamp)
-- | Recalculate all timestamps in a 'Stream'. This function is strict in its arguments.
convertTimestampC
:: forall proxy0 proxy1 m r t r' t' i s c p.
(NFData t, NFData t', CanBeTicks r t, CanBeTicks r' t', Monad m, NFData t')
=> proxy0 '( r, t)
-> proxy1 '( r', t')
-> Conduit (Stream i s (Ticks r t) p c) m (Stream i s (Ticks r' t') p c)
convertTimestampC _ _ = mapTimestampC' convertTicks