{- |
Interpolation buffers.
-}

{-# LANGUAGE Arrows #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module FRP.Rhine.ResamplingBuffer.Interpolation where

-- containers
import Data.Sequence

-- dunai
import Data.VectorSpace

-- rhine
import FRP.Rhine.ClSF
import FRP.Rhine.ResamplingBuffer
import FRP.Rhine.ResamplingBuffer.Util
import FRP.Rhine.ResamplingBuffer.KeepLast

-- | A simple linear interpolation based on the last calculated position and velocity.
linear
  :: ( Monad m, Clock m cl1, Clock m cl2
     , VectorSpace v
     , Groundfield v ~ Diff (Time cl1)
     , Groundfield v ~ Diff (Time cl2)
     )
  => v -- ^ The initial velocity (derivative of the signal)
  -> v -- ^ The initial position
  -> ResamplingBuffer m cl1 cl2 v v
linear initVelocity initPosition
  =    (derivativeFrom initPosition &&& clId) &&& timeInfoOf sinceInit
  ^->> keepLast ((initVelocity, initPosition), 0)
  >>-^ proc ((velocity, lastPosition), sinceInit1) -> do
    sinceInit2 <- timeInfoOf sinceInit -< ()
    let diff = sinceInit2 - sinceInit1
    returnA -< lastPosition ^+^ velocity ^* diff

{- |
sinc-Interpolation, or Whittaker-Shannon-Interpolation.

The incoming signal is strictly bandlimited
by the frequency at which @cl1@ ticks.
Each incoming value is hulled in a sinc function,
these are added and sampled at @cl2@'s ticks.
In order not to produce a space leak,
the buffer only remembers the past values within a given window,
which should be chosen much larger than the average time between @cl1@'s ticks.
-}
sinc
  :: ( Monad m, Clock m cl1, Clock m cl2
     , VectorSpace v
     , Ord (Groundfield v)
     , Floating (Groundfield v)
     , Groundfield v ~ Diff (Time cl1)
     , Groundfield v ~ Diff (Time cl2)
     )
  => Groundfield v
  -- ^ The size of the interpolation window
  --   (for how long in the past to remember incoming values)
  -> ResamplingBuffer m cl1 cl2 v v
sinc windowSize = historySince windowSize ^->> keepLast empty >>-^ proc as -> do
  sinceInit2 <- sinceInitS -< ()
  returnA                  -< vectorSum $ mkSinc sinceInit2 <$> as
  where
    mkSinc sinceInit2 (TimeInfo {..}, as)
      = let t = pi * (sinceInit2 - sinceInit) / sinceLast
        in  as ^* (sin t / t)
    vectorSum = foldr (^+^) zeroVector

-- TODO Do we want to give initial values?
-- | Interpolates the signal with Hermite splines,
--   using 'threePointDerivative'.
--
--   Caution: In order to calculate the derivatives of the incoming signal,
--   it has to be delayed by two ticks of @cl1@.
--   In a non-realtime situation, a higher quality is achieved
--   if the ticks of @cl2@ are delayed by two ticks of @cl1@.
cubic
  :: ( Monad m
     , VectorSpace v
     , Groundfield v ~ Diff (Time cl1)
     , Groundfield v ~ Diff (Time cl2)
     )
  => ResamplingBuffer m cl1 cl2 v v
cubic = ((iPre zeroVector &&& threePointDerivative) &&& (sinceInitS >-> iPre 0))
    >-> (clId &&& iPre (zeroVector, 0))
   ^->> keepLast ((zeroVector, 0), (zeroVector, 0))
   >>-^ proc (((dv, v), t1), ((dv', v'), t1')) -> do
     t2 <- sinceInitS -< ()
     let
       t        = (t1 - t1') / (t2 - t1')
       tsquared = t ^ 2
       tcubed   = t ^ 3
       vInter   = ( 2 * tcubed - 3 * tsquared     + 1) *^  v'
              ^+^ (     tcubed - 2 * tsquared + t    ) *^ dv'
              ^+^ (-2 * tcubed + 3 * tsquared        ) *^  v
              ^+^ (     tcubed -     tsquared        ) *^ dv
     returnA -< vInter