hosc-0.10: Sound/OpenSoundControl/Time.hs
-- | Temporal representations and clock operations (read current time
-- and pause thread).
module Sound.OpenSoundControl.Time where
import Control.Concurrent
import Control.Monad
import Data.Word
import qualified Data.Time as T
-- | Type for integer representation of NTP time.
type NTPi = Word64
-- | Time is represented in either UTC or NTP form. The NTP form may
-- be either integral or real.
data Time = UTCr Double | NTPr Double | NTPi NTPi
deriving (Read, Show)
instance Eq Time where
a == b = as_ntpi a == as_ntpi b
a /= b = as_ntpi a /= as_ntpi b
-- | Coerce to NTPi form.
as_ntpi :: Time -> NTPi
as_ntpi x =
case x of
UTCr t -> utcr_ntpi t
NTPr t -> ntpr_ntpi t
NTPi t -> t
-- | Coerce to UTCr form.
as_utcr :: Time -> Double
as_utcr x =
case x of
UTCr t -> t
NTPr t -> ntpr_utcr t
NTPi t -> ntpi_utcr t
-- | Times can be ordered, avoid coercion if not required.
instance Ord Time where
compare p q =
case (p,q) of
(UTCr p',UTCr q') -> compare p' q'
(NTPr p',NTPr q') -> compare p' q'
(NTPi p',NTPi q') -> compare p' q'
_ -> compare (as_ntpi p) (as_ntpi q)
-- | Convert a real-valued NTP timestamp to an NTP timestamp.
ntpr_ntpi :: Double -> NTPi
ntpr_ntpi t = round (t * 2^(32::Int))
-- | Convert an NTP timestamp to a real-valued NTP timestamp.
ntpi_ntpr :: NTPi -> Double
ntpi_ntpr t = fromIntegral t / 2^(32::Int)
-- | Convert UTC timestamp to NTP timestamp.
utcr_ntpi :: Double -> NTPi
utcr_ntpi t =
let secdif = (70 * 365 + 17) * 24 * 60 * 60
in ntpr_ntpi (t + secdif)
-- | Convert NTP timestamp to UTC timestamp.
ntpr_utcr :: Double -> Double
ntpr_utcr t =
let secdif = (70 * 365 + 17) * 24 * 60 * 60
in t - secdif
-- | Convert NTP timestamp to UTC timestamp.
ntpi_utcr :: NTPi -> Double
ntpi_utcr = ntpr_utcr . ntpi_ntpr
-- | The time at 1970-01-01:00:00:00.
utc_base :: T.UTCTime
utc_base =
let d = T.fromGregorian 1970 1 1
s = T.secondsToDiffTime 0
in T.UTCTime d s
-- | Read current UTCr timestamp.
utcr :: IO Double
utcr = do
t <- T.getCurrentTime
return (realToFrac (T.diffUTCTime t utc_base))
-- | Read current NTP timestamp.
ntpi :: IO NTPi
ntpi = liftM utcr_ntpi utcr
-- | The pauseThread limit (in seconds). Values larger than this
-- require a different thread delay mechanism, see sleepThread. The
-- value is the number of microseconds in maxBound::Int.
pauseThreadLimit :: Double
pauseThreadLimit = fromIntegral (maxBound::Int) / 1e6
-- | Pause current thread for the indicated duration (in seconds), see
-- pauseThreadLimit. Note also that this function does not attempt
-- pauses less than 1e-4.
pauseThread :: Double -> IO ()
pauseThread n = when (n > 1e-4) (threadDelay (floor (n * 1e6)))
-- | Pause current thread until the given utcr time, see
-- pauseThreadLimit.
pauseThreadUntil :: Double -> IO ()
pauseThreadUntil t = pauseThread . (t -) =<< utcr
-- | Sleep current thread for the indicated duration (in seconds).
-- Divides long sleeps into parts smaller than pauseThreadLimit.
sleepThread :: Double -> IO ()
sleepThread n =
if n >= pauseThreadLimit
then let n' = pauseThreadLimit - 1
in pauseThread n >> sleepThread (n - n')
else pauseThread n
-- | Sleep current thread until the given utcr time. Divides long
-- sleeps into parts smaller than pauseThreadLimit.
sleepThreadUntil :: Double -> IO ()
sleepThreadUntil t = sleepThread . (t -) =<< utcr
-- | Execute the bundle immediately.
immediately :: Time
immediately = NTPi 1