Yampa-0.11: src/FRP/Yampa/Delays.hs
-----------------------------------------------------------------------------------------
-- |
-- Module : FRP.Yampa.Delays
-- Copyright : (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer : ivan.perez@keera.co.uk
-- Stability : provisional
-- Portability : non-portable (GHC extensions)
--
-- SF primitives and combinators to delay signals, introducing new values in
-- them.
--
-----------------------------------------------------------------------------------------
module FRP.Yampa.Delays (
-- * Basic delays
pre, -- :: SF a a
iPre, -- :: a -> SF a a
fby, -- :: b -> SF a b -> SF a b, infixr 0
-- * Timed delays
delay, -- :: Time -> a -> SF a a
) where
import Control.Arrow
import FRP.Yampa.Diagnostics
import FRP.Yampa.InternalCore (SF(..), SF'(..), Time)
import FRP.Yampa.Basic
import FRP.Yampa.Scan
infixr 0 `fby`
------------------------------------------------------------------------------
-- Delays
------------------------------------------------------------------------------
-- | Uninitialized delay operator.
--
-- The output has an infinitesimal delay (1 sample), and the value at time
-- zero is undefined.
-- !!! Redefined using SFSScan
-- !!! About 20% slower than old_pre on its own.
pre :: SF a a
pre = sscanPrim f uninit uninit
where
f c a = Just (a, c)
uninit = usrErr "AFRP" "pre" "Uninitialized pre operator."
-- | Initialized delay operator.
--
-- Creates an SF that delays the input signal, introducing an infinitesimal
-- delay (one sample), using the given argument to fill in the initial output
-- at time zero.
iPre :: a -> SF a a
iPre = (--> pre)
-- | Lucid-Synchrone-like initialized delay (read "followed by").
--
-- Initialized delay combinator, introducing an infinitesimal delay (one
-- sample) in given 'SF', using the given argument to fill in the initial
-- output at time zero.
--
-- The difference with 'iPre' is that 'fby' takes an 'SF' as argument.
fby :: b -> SF a b -> SF a b
b0 `fby` sf = b0 --> sf >>> pre
------------------------------------------------------------------------------
-- Timed delays
------------------------------------------------------------------------------
-- | Delay a signal by a fixed time 't', using the second parameter
-- to fill in the initial 't' seconds.
-- Invariants:
-- t_diff measure the time since the latest output sample ideally
-- should have been output. Whenever that equals or exceeds the
-- time delta for the next buffered sample, it is time to output a
-- new sample (although not necessarily the one first in the queue:
-- it might be necessary to "catch up" by discarding samples.
-- 0 <= t_diff < bdt, where bdt is the buffered time delta for the
-- sample on the front of the buffer queue.
--
-- Sum of time deltas in the queue >= q.
-- !!! PROBLEM!
-- Since input samples sometimes need to be duplicated, it is not a
-- good idea use a delay on things like events since we then could
-- end up with duplication of event occurrences.
-- (Thus, we actually NEED delayEvent.)
delay :: Time -> a -> SF a a
delay q a_init | q < 0 = usrErr "AFRP" "delay" "Negative delay."
| q == 0 = identity
| otherwise = SF {sfTF = tf0}
where
tf0 a0 = (delayAux [] [(q, a0)] 0 a_init, a_init)
delayAux _ [] _ _ = undefined
delayAux rbuf buf@((bdt, ba) : buf') t_diff a_prev = SF' tf -- True
where
tf dt a | t_diff' < bdt =
(delayAux rbuf' buf t_diff' a_prev, a_prev)
| otherwise = nextSmpl rbuf' buf' (t_diff' - bdt) ba
where
t_diff' = t_diff + dt
rbuf' = (dt, a) : rbuf
nextSmpl rbuf [] t_diff a =
nextSmpl [] (reverse rbuf) t_diff a
nextSmpl rbuf buf@((bdt, ba) : buf') t_diff a
| t_diff < bdt = (delayAux rbuf buf t_diff a, a)
| otherwise = nextSmpl rbuf buf' (t_diff-bdt) ba
-- !!! Hmm. Not so easy to do efficiently, it seems ...
-- varDelay :: Time -> a -> SF (a, Time) a
-- varDelay = undefined
-- Vim modeline
-- vim:set tabstop=8 expandtab: