{-# LANGUAGE Arrows #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeFamilies #-} module FRP.Rhine.Clock.Realtime.Millisecond where -- base import Data.Time.Clock import Control.Concurrent (threadDelay) import GHC.TypeLits (Nat, KnownNat) -- rhine import FRP.Rhine import FRP.Rhine.Clock.Step {- | A clock ticking every 'n' milliseconds, in real time. Since 'n' is in the type signature, it is ensured that when composing two signals on a 'Millisecond' clock, they will be driven at the same rate. The tag of this clock is 'Bool', where 'True' represents successful realtime, and 'False' a lag. -} type Millisecond (n :: Nat) = RescaledClockS IO (Step n) UTCTime Bool -- TODO Consider changing the tag to Maybe Double -- | This clock simply sleeps 'n' milliseconds after each tick. -- The current time is measured, but no adjustment is made. -- Consequently, the tag is constantly 'False', -- since the clock will accumulate the computation time as lag. sleepClock :: KnownNat n => Millisecond n sleepClock = sleepClock_ Step where sleepClock_ :: Step n -> Millisecond n sleepClock_ cl = RescaledClockS cl $ const $ do now <- getCurrentTime return ( arrM_ (threadDelay (fromInteger $ stepsize cl * 1000) >> getCurrentTime) *** arr (const False) , now ) -- TODO Test whether realtime detection really works here, -- e.g. with a getLine signal -- | A more sophisticated implementation that measures the time after each tick, -- and waits for the remaining time until the next tick. -- If the next tick should already have occurred, -- the tag is set to 'False', representing a failed real time attempt. waitClock :: KnownNat n => Millisecond n waitClock = RescaledClockS Step $ \_ -> do initTime <- getCurrentTime let runningClock = proc (n, ()) -> do beforeSleep <- arrM_ getCurrentTime -< () let diff :: Double diff = realToFrac $ beforeSleep `diffUTCTime` initTime remaining = fromInteger $ n * 1000 - round (diff * 1000000) _ <- arrM threadDelay -< remaining now <- arrM_ getCurrentTime -< () -- TODO Test whether this is a performance penalty returnA -< (now, diff > 0) return (runningClock, initTime)