{-# LANGUAGE MultiWayIf #-}
-- | Streams and stream manipulation API.
--
-- The evaluation of Yampa SFs, especially for testing purposes, needs the
-- generation of suitable input streams.
--
-- While some streams can be generated randomly using QuickCheck, it is
-- sometimes useful to be able to preprend or adapt an input stream. It is also
-- useful to debug programs when you have recorded input streams using Haskell
-- Titan.
--
-- This module defines types for input streams, as well as an API to create,
-- examine and combine streams. It also provides evaluation functions that are
-- needed to apply an SF to a stream and obtain an output stream and a
-- continuation SF.
module FRP.Yampa.Stream where

import FRP.Yampa (DTime, SF, FutureSF, evalAtZero, evalAt)

-- * Types

-- | A stream of samples, with their sampling times.
type SignalSampleStream a = (a, FutureSampleStream a)

-- | A stream of future samples, with their sampling times. The difference
-- between 'SignalSampleStream' and 'FutureSampleStream' is that all elements
-- in the latter have a non-zero time delta.
type FutureSampleStream a = [(DTime, a)]

-- * Creation

-- | Group a series of samples with a series of time deltas.
--
--   The first sample will have no delta. Unused samples and deltas will be
--   dropped.
groupDeltas :: [a] -> [DTime] -> SignalSampleStream a
groupDeltas :: [a] -> [DTime] -> SignalSampleStream a
groupDeltas (a
x:[a]
xs) [DTime]
ds = (a
x, [DTime] -> [a] -> [(DTime, a)]
forall a b. [a] -> [b] -> [(a, b)]
zip [DTime]
ds [a]
xs)
groupDeltas [a]
xs     [DTime]
ds = [Char] -> SignalSampleStream a
forall a. HasCallStack => [Char] -> a
error ([Char] -> SignalSampleStream a) -> [Char] -> SignalSampleStream a
forall a b. (a -> b) -> a -> b
$ [Char]
"groupDeltas: called me with lists with lengths" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show ([a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
xs) [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" and " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show ([DTime] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [DTime]
ds)

-- * Examination

-- | Turn a stream with sampling times into a list of values.
samples :: SignalSampleStream a -> [a]
samples :: SignalSampleStream a -> [a]
samples (a
a, FutureSampleStream a
as) = a
a a -> [a] -> [a]
forall a. a -> [a] -> [a]
: ((DTime, a) -> a) -> FutureSampleStream a -> [a]
forall a b. (a -> b) -> [a] -> [b]
map (DTime, a) -> a
forall a b. (a, b) -> b
snd FutureSampleStream a
as

-- | Return the first sample in a sample stream.
firstSample :: SignalSampleStream a -> a
firstSample :: SignalSampleStream a -> a
firstSample = [a] -> a
forall a. [a] -> a
head ([a] -> a)
-> (SignalSampleStream a -> [a]) -> SignalSampleStream a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SignalSampleStream a -> [a]
forall a. SignalSampleStream a -> [a]
samples

-- | Return the last sample in a sample stream.
lastSample :: SignalSampleStream a -> a
lastSample :: SignalSampleStream a -> a
lastSample = [a] -> a
forall a. [a] -> a
last ([a] -> a)
-> (SignalSampleStream a -> [a]) -> SignalSampleStream a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SignalSampleStream a -> [a]
forall a. SignalSampleStream a -> [a]
samples

-- * Manipulation

-- | Merge two streams, using an auxilary function to merge samples that fall
-- at the exact same sampling time.
sMerge :: (a -> a -> a) -> SignalSampleStream a -> SignalSampleStream a -> SignalSampleStream a
sMerge :: (a -> a -> a)
-> SignalSampleStream a
-> SignalSampleStream a
-> SignalSampleStream a
sMerge a -> a -> a
f (a
x1, FutureSampleStream a
xs1) (a
x2, FutureSampleStream a
xs2) = (a -> a -> a
f a
x1 a
x2, (a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
forall a.
(a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
sMergeTail a -> a -> a
f FutureSampleStream a
xs1 FutureSampleStream a
xs2)
  where
    sMergeTail :: (a -> a -> a) -> FutureSampleStream a -> FutureSampleStream a -> FutureSampleStream a
    sMergeTail :: (a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
sMergeTail a -> a -> a
f []              FutureSampleStream a
xs2             = FutureSampleStream a
xs2
    sMergeTail a -> a -> a
f FutureSampleStream a
xs1             []              = FutureSampleStream a
xs1
    sMergeTail a -> a -> a
f ((DTime
dt1, a
x1):FutureSampleStream a
xs1) ((DTime
dt2, a
x2):FutureSampleStream a
xs2)
      | DTime
dt1 DTime -> DTime -> Bool
forall a. Eq a => a -> a -> Bool
== DTime
dt2 = (DTime
dt1, a -> a -> a
f a
x1 a
x2) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: (a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
forall a.
(a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
sMergeTail a -> a -> a
f FutureSampleStream a
xs1 FutureSampleStream a
xs2
      | DTime
dt1 DTime -> DTime -> Bool
forall a. Ord a => a -> a -> Bool
<  DTime
dt2 = (DTime
dt1, a
x1) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: (a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
forall a.
(a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
sMergeTail a -> a -> a
f FutureSampleStream a
xs1 ((DTime
dt2DTime -> DTime -> DTime
forall a. Num a => a -> a -> a
-DTime
dt1, a
x2)(DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
:FutureSampleStream a
xs2)
      | Bool
otherwise  = (DTime
dt2, a
x2) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: (a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
forall a.
(a -> a -> a)
-> FutureSampleStream a
-> FutureSampleStream a
-> FutureSampleStream a
sMergeTail a -> a -> a
f ((DTime
dt1DTime -> DTime -> DTime
forall a. Num a => a -> a -> a
-DTime
dt2, a
x1)(DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
:FutureSampleStream a
xs1) FutureSampleStream a
xs2

-- | Concatenate two sample streams, separating them by a given time delta.
sConcat :: SignalSampleStream a -> DTime -> SignalSampleStream a -> SignalSampleStream a
sConcat :: SignalSampleStream a
-> DTime -> SignalSampleStream a -> SignalSampleStream a
sConcat (a
x1, FutureSampleStream a
xs1) DTime
dt (a
x2, FutureSampleStream a
xs2) = (a
x1 , FutureSampleStream a
xs1 FutureSampleStream a
-> FutureSampleStream a -> FutureSampleStream a
forall a. [a] -> [a] -> [a]
++ ((DTime
dt, a
x2)(DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
:FutureSampleStream a
xs2))

-- | Refine a stream by establishing the maximum time delta.
--
-- If two samples are separated by a time delta bigger than the given max DT,
-- the former is replicated as many times as necessary.
sRefine :: DTime -> SignalSampleStream a -> SignalSampleStream a
sRefine :: DTime -> SignalSampleStream a -> SignalSampleStream a
sRefine DTime
maxDT (a
a, FutureSampleStream a
as) = (a
a, DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
DTime -> a -> FutureSampleStream a -> FutureSampleStream a
sRefineFutureStream DTime
maxDT a
a FutureSampleStream a
as)
  where
    sRefineFutureStream :: DTime -> a -> FutureSampleStream a -> FutureSampleStream a
    sRefineFutureStream :: DTime -> a -> FutureSampleStream a -> FutureSampleStream a
sRefineFutureStream DTime
maxDT a
_ [] = []
    sRefineFutureStream DTime
maxDT a
a0 ((DTime
dt, a
a):FutureSampleStream a
as)
      | DTime
dt DTime -> DTime -> Bool
forall a. Ord a => a -> a -> Bool
> DTime
maxDT = (DTime
maxDT, a
a0) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
DTime -> a -> FutureSampleStream a -> FutureSampleStream a
sRefineFutureStream DTime
maxDT a
a0 ((DTime
dt DTime -> DTime -> DTime
forall a. Num a => a -> a -> a
- DTime
maxDT, a
a)(DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
:FutureSampleStream a
as)
      | Bool
otherwise  = (DTime
dt, a
a) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
DTime -> a -> FutureSampleStream a -> FutureSampleStream a
sRefineFutureStream DTime
maxDT a
a FutureSampleStream a
as

-- | Refine a stream by establishing the maximum time delta.
--
-- If two samples are separated by a time delta bigger than the given max DT,
-- the auxiliary interpolation function is used to determine the intermendiate
-- sample.
sRefineWith :: (a -> a -> a) -> DTime -> SignalSampleStream a -> SignalSampleStream a
sRefineWith :: (a -> a -> a)
-> DTime -> SignalSampleStream a -> SignalSampleStream a
sRefineWith a -> a -> a
interpolate DTime
maxDT (a
a, FutureSampleStream a
as) = (a
a, (a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
(a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
refineFutureStreamWith a -> a -> a
interpolate DTime
maxDT a
a FutureSampleStream a
as)
  where
    refineFutureStreamWith :: (a -> a -> a) -> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
    refineFutureStreamWith :: (a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
refineFutureStreamWith a -> a -> a
interpolate DTime
maxDT a
_  [] = []
    refineFutureStreamWith a -> a -> a
interpolate DTime
maxDT a
a0 ((DTime
dt, a
a):FutureSampleStream a
as)
      | DTime
dt DTime -> DTime -> Bool
forall a. Ord a => a -> a -> Bool
> DTime
maxDT = let a' :: a
a' = a -> a -> a
interpolate a
a0 a
a
                     in (DTime
maxDT, a -> a -> a
interpolate a
a0 a
a) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: (a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
(a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
refineFutureStreamWith a -> a -> a
interpolate DTime
maxDT a
a' ((DTime
dt DTime -> DTime -> DTime
forall a. Num a => a -> a -> a
- DTime
maxDT, a
a)(DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
:FutureSampleStream a
as)
      | Bool
otherwise  = (DTime
dt, a
a) (DTime, a) -> FutureSampleStream a -> FutureSampleStream a
forall a. a -> [a] -> [a]
: (a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
forall a.
(a -> a -> a)
-> DTime -> a -> FutureSampleStream a -> FutureSampleStream a
refineFutureStreamWith a -> a -> a
interpolate DTime
maxDT a
a FutureSampleStream a
as

-- | Clip a sample stream at a given number of samples.
sClipAfterFrame  :: Int -> SignalSampleStream a -> SignalSampleStream a
sClipAfterFrame :: Int -> SignalSampleStream a -> SignalSampleStream a
sClipAfterFrame  Int
0 (a
x,FutureSampleStream a
_)  = (a
x, [])
sClipAfterFrame  Int
n (a
x,FutureSampleStream a
xs) = (a
x, FutureSampleStream a
xs')
  where
    xs' :: FutureSampleStream a
xs' = Int -> FutureSampleStream a -> FutureSampleStream a
forall a. Int -> [a] -> [a]
take (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) FutureSampleStream a
xs

-- | Clip a sample stream after a certain (non-zero) time.
sClipAfterTime   :: DTime -> SignalSampleStream a -> SignalSampleStream a
sClipAfterTime :: DTime -> SignalSampleStream a -> SignalSampleStream a
sClipAfterTime DTime
dt (a
x,FutureSampleStream a
xs) = (a
x, DTime -> FutureSampleStream a -> FutureSampleStream a
forall t b. (Ord t, Num t) => t -> [(t, b)] -> [(t, b)]
sClipAfterTime' DTime
dt FutureSampleStream a
xs)
  where
    sClipAfterTime' :: t -> [(t, b)] -> [(t, b)]
sClipAfterTime' t
dt [] = []
    sClipAfterTime' t
dt ((t
dt',b
x):[(t, b)]
xs)
      | t
dt t -> t -> Bool
forall a. Ord a => a -> a -> Bool
< t
dt'  = []
      | Bool
otherwise = ((t
dt',b
x)(t, b) -> [(t, b)] -> [(t, b)]
forall a. a -> [a] -> [a]
:t -> [(t, b)] -> [(t, b)]
sClipAfterTime' (t
dt t -> t -> t
forall a. Num a => a -> a -> a
- t
dt') [(t, b)]
xs)

-- | Drop the first n samples of a signal stream. The time
-- deltas are not re-calculated.
sClipBeforeFrame :: Int -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeFrame :: Int -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeFrame Int
0 (a
x,FutureSampleStream a
xs) = (a
x,FutureSampleStream a
xs)
sClipBeforeFrame Int
n (a
x,[]) = (a
x,[])
sClipBeforeFrame Int
n (a
_,(DTime
dt,a
x):FutureSampleStream a
xs) = Int -> SignalSampleStream a -> SignalSampleStream a
forall a. Int -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeFrame (Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) (a
x, FutureSampleStream a
xs)

-- | Drop the first samples of a signal stream up to a given time. The time
-- deltas are not re-calculated to match the original stream.
sClipBeforeTime  :: DTime -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeTime :: DTime -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeTime DTime
dt SignalSampleStream a
xs
  | DTime
dt DTime -> DTime -> Bool
forall a. Ord a => a -> a -> Bool
<= DTime
0   = SignalSampleStream a
xs
  | Bool
otherwise = case SignalSampleStream a
xs of
                  (a
x,[])           -> (a
x,[])
                  (a
_,(DTime
dt',a
x'):[(DTime, a)]
xs') -> if | DTime
dt DTime -> DTime -> Bool
forall a. Ord a => a -> a -> Bool
< DTime
dt'  -> -- (dt' - dt, x'):xs'
                                                        (a
x',[(DTime, a)]
xs')
                                         | Bool
otherwise -> DTime -> SignalSampleStream a -> SignalSampleStream a
forall a. DTime -> SignalSampleStream a -> SignalSampleStream a
sClipBeforeTime (DTime
dt DTime -> DTime -> DTime
forall a. Num a => a -> a -> a
- DTime
dt') (a
x', [(DTime, a)]
xs')

-- ** Stream-based evaluation

-- | Evaluate an SF with a 'SignalSampleStream', obtaining an output
-- stream and a continuation.
--
-- You should never use this for actual execution in your applications,
-- only for testing.
evalSF :: SF a b
       -> SignalSampleStream a
       -> (SignalSampleStream b, FutureSF a b)
evalSF :: SF a b
-> SignalSampleStream a -> (SignalSampleStream b, FutureSF a b)
evalSF SF a b
sf (a
a, FutureSampleStream a
as) = (SignalSampleStream b
outputStrm, FutureSF a b
fsf')
  where (b
b,  FutureSF a b
fsf)  = SF a b -> a -> (b, FutureSF a b)
forall a b. SF a b -> a -> (b, FutureSF a b)
evalAtZero SF a b
sf a
a
        (FutureSampleStream b
bs, FutureSF a b
fsf') = FutureSF a b
-> FutureSampleStream a -> (FutureSampleStream b, FutureSF a b)
forall a b.
FutureSF a b
-> FutureSampleStream a -> (FutureSampleStream b, FutureSF a b)
evalFutureSF FutureSF a b
fsf FutureSampleStream a
as
        outputStrm :: SignalSampleStream b
outputStrm = (b
b, FutureSampleStream b
bs)

-- | Evaluate an initialised SF with a 'FutureSampleStream', obtaining
-- an output stream and a continuation.
--
-- You should never use this for actual execution in your applications,
-- only for testing.
evalFutureSF :: FutureSF a b
             -> FutureSampleStream a
             -> (FutureSampleStream b, FutureSF a b)
evalFutureSF :: FutureSF a b
-> FutureSampleStream a -> (FutureSampleStream b, FutureSF a b)
evalFutureSF FutureSF a b
fsf [] = ([], FutureSF a b
fsf)
evalFutureSF FutureSF a b
fsf ((DTime
dt, a
a):FutureSampleStream a
as) = (FutureSampleStream b
outputStrm, FutureSF a b
fsf'')
  where (b
b, FutureSF a b
fsf')   = FutureSF a b -> DTime -> a -> (b, FutureSF a b)
forall a b. FutureSF a b -> DTime -> a -> (b, FutureSF a b)
evalAt FutureSF a b
fsf DTime
dt a
a
        (FutureSampleStream b
bs, FutureSF a b
fsf'') = FutureSF a b
-> FutureSampleStream a -> (FutureSampleStream b, FutureSF a b)
forall a b.
FutureSF a b
-> FutureSampleStream a -> (FutureSampleStream b, FutureSF a b)
evalFutureSF FutureSF a b
fsf' FutureSampleStream a
as
        outputStrm :: FutureSampleStream b
outputStrm  = (DTime
dt, b
b) (DTime, b) -> FutureSampleStream b -> FutureSampleStream b
forall a. a -> [a] -> [a]
: FutureSampleStream b
bs