packages feed

bearriver-0.14.5: src/FRP/BearRiver/Delays.hs

-- |
-- Module      : FRP.BearRiver.Delays
-- Copyright   : (c) Ivan Perez, 2014-2023
--               (c) George Giorgidze, 2007-2012
--               (c) Henrik Nilsson, 2005-2006
--               (c) Antony Courtney and Henrik Nilsson, Yale University, 2003-2004
-- 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.BearRiver.Delays
    (
      -- * Basic delays
      pre
    , iPre
    , fby

      -- * Timed delays
    , delay
    )
  where

-- External imports
import Control.Arrow ((>>>))

-- Internal imports (dunai)
import Control.Monad.Trans.MSF                 (ask)
import Data.MonadicStreamFunction.InternalCore (MSF (..))

-- Internal imports
import FRP.BearRiver.Basic        (identity, (-->))
import FRP.BearRiver.InternalCore (SF (..), Time)
import FRP.BearRiver.Scan         (sscanPrim)

infixr 0 `fby`

-- * Delays

-- | Uninitialized delay operator.
--
-- The output has an infinitesimal delay (1 sample), and the value at time zero
-- is undefined.
pre :: Monad m => SF m a a
pre = sscanPrim f uninit uninit
  where
    f c a = Just (a, c)
    uninit = error "bearriver: 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 :: Monad m => a -> SF m 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 :: Monad m => b -> SF m a b -> SF m 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.
delay :: Monad m => Time -> a -> SF m a a
delay q aInit | q < 0     = error "bearriver: delay: Negative delay."
              | q == 0    = identity
              | otherwise = MSF tf0
  where
    tf0 a0 = return (aInit, delayAux [] [(q, a0)] 0 aInit)

    -- Invariants:
    -- tDiff 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 <= tDiff < 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.
    delayAux _ [] _ _ = undefined
    delayAux rbuf buf@((bdt, ba) : buf') tDiff aPrev = MSF tf -- True
      where
        tf a = do
          dt <- ask
          let tDiff' = tDiff + dt
              rbuf'  = (dt, a) : rbuf
          if (tDiff' < bdt)
            then return (aPrev, delayAux rbuf' buf tDiff' aPrev)
            else nextSmpl rbuf' buf' (tDiff' - bdt) ba
          where

            nextSmpl rbuf [] tDiff a =
              nextSmpl [] (reverse rbuf) tDiff a
            nextSmpl rbuf buf@((bdt, ba) : buf') tDiff a
              | tDiff < bdt = return (a, delayAux rbuf buf tDiff a)
              | otherwise   = nextSmpl rbuf buf' (tDiff - bdt) ba