{-# LINE 2 "./Media/Streaming/GStreamer/Core/Clock.chs" #-}
-- GIMP Toolkit (GTK) Binding for Haskell: binding to gstreamer -*-haskell-*-
--
-- Author : Peter Gavin
-- Created: 1-Apr-2007
--
-- Copyright (c) 2007 Peter Gavin
--
-- This library is free software: you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public License
-- as published by the Free Software Foundation, either version 3 of
-- the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this program. If not, see
-- <http:
--
-- GStreamer, the C library which this Haskell library depends on, is
-- available under LGPL Version 2. The documentation included with
-- this library is based on the original GStreamer documentation.
--
-- |
-- Maintainer : gtk2hs-devel@lists.sourceforge.net
-- Stability : alpha
-- Portability : portable (depends on GHC)
--
-- Abstract class of global clocks.
module Media.Streaming.GStreamer.Core.Clock (

-- * Detail
  -- | GStreamer uses a global clock to synchronize the plugins in a
  -- pipeline. Different clock implementations are possible by
  -- implementing this abstract base class.
  --
  -- The 'Clock' returns a monotonically increasing time with the
  -- method 'clockGetTime'. Its accuracy and base time depend
  -- on the specific clock implementation but time is always
  -- expressed in nanoseconds. Since the baseline of the clock is
  -- undefined, the clock time returned is not meaningful in itself,
  -- what matters are the deltas between two clock times. The time
  -- returned by a clock is called the absolute time.
  --
  -- The pipeline uses the clock to calculate the stream
  -- time. Usually all renderers synchronize to the global clock
  -- using the buffer timestamps, the newsegment events and the
  -- element's base time, see GstPipeline.
  --
  -- A clock implementation can support periodic and single shot
  -- clock notifications both synchronous and asynchronous.
  --
  -- One first needs to create a 'ClockID' for the periodic or
  -- single shot notification using 'clockNewSingleShotID' or
  -- 'clockNewPeriodicID'.
  --
  -- To perform a blocking wait for the specific time of the
  -- 'ClockID' use 'clockIDWait'. This calls can be interrupted with
  -- the 'clockIDUnschedule' call. If the blocking wait is
  -- unscheduled a return value of 'ClockUnscheduled' is returned.
  --
  -- Periodic callbacks scheduled async will be repeadedly called
  -- automatically until it is unscheduled. To schedule a sync
  -- periodic callback, 'clockIDWait' should be called repeatedly.
  --
  -- The async callbacks can happen from any thread, either provided
  -- by the core or from a streaming thread. The application should
  -- be prepared for this.
  --
  -- A 'ClockID' that has been unscheduled cannot be used again for
  -- any wait operation; a new 'ClockID' should be created.
  --
  -- It is possible to perform a blocking wait on the same 'ClockID'
  -- from multiple threads. However, registering the same 'ClockID'
  -- for multiple async notifications is not possible, the callback
  -- will only be called for the thread registering the entry last.
  --
  -- These clock operations do not operate on the stream time, so
  -- the callbacks will also occur when not in the playing state as
  -- if the clock just keeps on running. Some clocks however do not
  -- progress when the element that provided the clock is not
  -- playing.
  --
  -- When a clock has the 'ClockFlagCanSetMaster' flag set, it can
  -- be slaved to another 'Clock' with 'clockSetMaster'. The clock
  -- will then automatically be synchronized to this master clock by
  -- repeatedly sampling the master clock and the slave clock and
  -- recalibrating the slave clock with 'clockSetCalibration'. This
  -- feature is mostly useful for plugins that have an internal
  -- clock but must operate with another clock selected by the
  -- GstPipeline. They can track the offset and rate difference of
  -- their internal clock relative to the master clock by using the
  -- 'clockGetCalibration' function.
  --
  -- The master\/slave synchronisation can be tuned with the
  -- the 'clockTimeout', 'clockWindowSize' and 'clockWindowThreshold' properties.
  -- The 'clockTimeout' property defines the interval to
  -- sample the master clock and run the calibration
  -- functions. 'clockWindowSize' defines the number of samples to
  -- use when calibrating and 'clockWindowThreshold' defines the
  -- minimum number of samples before the calibration is performed.

-- * Types
  Clock,
  ClockClass,
  castToClock,
  gTypeClock,

  -- | A time value measured in nanoseconds.
  ClockTime,

  -- | The 'ClockTime' value representing an invalid time.
  clockTimeNone,
  clockTimeIsValid,

  -- | The 'ClockTime' value representing 1 second, i.e. 1e9.
  second,
  -- | The 'ClockTime' value representing 1 millisecond, i.e. 1e6.
  msecond,
  -- | The 'ClockTime' value representing 1 microsecond, i.e. 1e3.
  usecond,
  -- | The 'ClockTime' value representing 1 nanosecond, i.e. 1.
  nsecond,
  -- | A value holding the difference between two 'ClockTime's.
  ClockTimeDiff,
  -- | An opaque identifier for a timer event.
  ClockID,
  -- | An enumeration type returned by 'clockIDWait'.
  ClockReturn(..),
  -- | The flags a 'Clock' may have.
  ClockFlags(..),
  clockGetFlags,
  clockSetFlags,
  clockUnsetFlags,

-- * Clock Operations
  clockAddObservation,
  clockSetMaster,
  clockGetMaster,
  clockSetResolution,
  clockGetResolution,
  clockGetTime,
  clockNewSingleShotID,
  clockNewPeriodicID,
  clockGetInternalTime,
  clockGetCalibration,
  clockSetCalibration,
  clockIDGetTime,
  clockIDWait,
  clockIDUnschedule,

-- * Clock Properties
  clockTimeout,
  clockWindowSize,
  clockWindowThreshold

  ) where

import Data.Ratio ( Ratio
                  , (%)
                  , numerator
                  , denominator )
import Control.Monad ( liftM
                     , liftM4)
import Media.Streaming.GStreamer.Core.Types
{-# LINE 169 "./Media/Streaming/GStreamer/Core/Clock.chs" #-}
import System.Glib.FFI
import System.Glib.Attributes ( Attr
                              , newAttr )
import System.Glib.Properties


{-# LINE 175 "./Media/Streaming/GStreamer/Core/Clock.chs" #-}

-- | Get the flags set on the clock.
clockGetFlags :: ClockClass clockT
              => clockT -- ^ @clock@
              -> IO [ClockFlags] -- ^ the flags currently set on the clock
clockGetFlags = mkObjectGetFlags

-- | Set the given flags on the clock.
clockSetFlags :: ClockClass clockT
              => clockT -- ^ @clock@
              -> [ClockFlags] -- ^ @flags@ - the flags to be set
              -> IO ()
clockSetFlags = mkObjectSetFlags

-- | Unset the given flags on the clock.
clockUnsetFlags :: ClockClass clockT
                => clockT -- ^ @clock@
                -> [ClockFlags] -- ^ @flags@ - the flags to be unset
                -> IO ()
clockUnsetFlags = mkObjectUnsetFlags

-- | Returns 'True' if the given 'ClockTime' is valid, and 'False'
-- otherwise.
clockTimeIsValid :: ClockTime -- ^ @clockTime@
                 -> Bool -- ^ 'True' if @clockTime@ is valid, 'False' otherwise
clockTimeIsValid = (/= clockTimeNone)

-- | The time master of the master clock and the time slave of the
-- slave clock are added to the list of observations. If enough
-- observations are available, a linear regression algorithm is run
-- on the observations and clock is recalibrated.
--
-- If a calibration is performed, the correlation coefficient of the
-- interpolation will be returned. A value of 1.0 means the clocks
-- are in perfect sync. This value can be used to control the
-- sampling frequency of the master and slave clocks.
clockAddObservation :: ClockClass clock
                    => clock
                    -> ClockTime
                    -> ClockTime
                    -> IO (Maybe Double)
clockAddObservation clock slave master =
    alloca $ \rSquaredPtr ->
        do success <- (\(Clock arg1) arg2 arg3 arg4 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_add_observation argPtr1 arg2 arg3 arg4) (toClock clock)
                                                       (fromIntegral slave)
                                                       (fromIntegral master)
                                                       rSquaredPtr
           if toBool success
               then liftM (Just . realToFrac) $ peek rSquaredPtr
               else return Nothing

-- | Set @master@ as the master clock for @clock@. The @clock@ will
-- automatically be calibrated so that 'clockGetTime' reports the
-- same time as the @master@ clock.
--
-- A clock provider that slaves its clock to a master can get the
-- current calibration values with 'clockGetCalibration'.
--
-- The @master@ clock can be 'Nothing' in which case @clock@ will
-- not be slaved any longer. It will, however, continue to report
-- its time adjusted using the last configured rate and time
-- offsets.
--
-- Note that if @clock@ does not have the 'ClockFlagCanSetMaster'
-- flag set, this function will not succeed and return 'False'.
clockSetMaster :: (ClockClass clock, ClockClass master)
               => clock -- ^ @clock@
               -> Maybe master -- ^ @master@
               -> IO Bool -- ^ 'True' if @clock@ is capable of
                               -- being slaved to the @master@ clock, otherwise 'False'
clockSetMaster clock master =
    withObject (toClock clock) $ \clockPtr ->
        maybeWith withObject (liftM toClock $ master) $ \masterPtr ->
            liftM toBool $ gst_clock_set_master clockPtr masterPtr
    where
      _ = (\(Clock arg1) (Clock arg2) -> withForeignPtr arg1 $ \argPtr1 ->withForeignPtr arg2 $ \argPtr2 ->gst_clock_set_master argPtr1 argPtr2)
{-# LINE 251 "./Media/Streaming/GStreamer/Core/Clock.chs" #-}

-- | Return the master that @clock@ is slaved to, or 'Nothing' if
-- @clock@ is not slaved.
clockGetMaster :: ClockClass clock
               => clock -- ^ @clock@
               -> IO (Maybe Clock) -- ^ the master that @clock@ is slaved to, or 'Nothing'
clockGetMaster clock =
    (\(Clock arg1) -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_get_master argPtr1) (toClock clock) >>= maybePeek takeObject

-- | Set the resolution of @clock@. Some clocks have the possibility
-- to operate with different resolution at the expense of more
-- resource usage. There is normally no need to change the default
-- resolution of a clock. The resolution of a clock can only be
-- changed if the clock has the 'ClockFlagCanSetResolution' flag
-- set.
clockSetResolution :: ClockClass clock
                   => clock
                   -> ClockTime
                   -> IO ClockTime
clockSetResolution clock resolution =
    liftM fromIntegral $
        (\(Clock arg1) arg2 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_set_resolution argPtr1 arg2) (toClock clock)
                                        (fromIntegral resolution)

-- | Get the resolution of the @clock@. The resolution of the clock is
-- the granularity of the values returned by 'clockGetTime'.
clockGetResolution :: ClockClass clock
                   => clock -- ^ @clock@ -
                   -> IO ClockTime -- ^ the resolution currently set in @clock@
clockGetResolution clock =
    liftM fromIntegral $
        (\(Clock arg1) -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_get_resolution argPtr1) (toClock clock)

-- | Get the current time stored in @clock@. The time is always
-- monotonically increasing and adjusted according to the current
-- offset and rate.
clockGetTime :: ClockClass clock
             => clock -- ^ @clock@
             -> IO ClockTime -- ^ the current time in @clock@
clockGetTime clock =
    liftM fromIntegral $
        (\(Clock arg1) -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_get_time argPtr1) (toClock clock)

-- | Get a 'ClockID' from @clock@ to trigger a single shot
-- notification at the requested time.
clockNewSingleShotID :: ClockClass clock
                     => clock -- ^ @clock@
                     -> ClockTime -- ^ @clockTime@
                     -> IO ClockID -- ^ a single shot notification id triggered at @clockTime@
clockNewSingleShotID clock time =
    (\(Clock arg1) arg2 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_new_single_shot_id argPtr1 arg2) (toClock clock)
                                        (fromIntegral time) >>=
        takeClockID . castPtr

-- | Get a 'ClockID' from @clock@ to trigger periodic
-- notifications. The notifications will start at time @startTime@
-- and then be fired at each @interval@ after.
clockNewPeriodicID :: ClockClass clock
                   => clock -- ^ @clock@
                   -> ClockTime -- ^ @startTime@
                   -> ClockTime -- ^ @interval@
                   -> IO ClockID -- ^ a periodic notification id
clockNewPeriodicID clock startTime interval =
    (\(Clock arg1) arg2 arg3 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_new_periodic_id argPtr1 arg2 arg3) (toClock clock)
                                     (fromIntegral startTime)
                                     (fromIntegral interval) >>=
        takeClockID . castPtr

-- | Gets the current internal time of @clock@. The time is
-- returned unadjusted in the offset and rate.
clockGetInternalTime :: ClockClass clock
                     => clock -- ^ @clock@
                     -> IO ClockTime -- ^ the clock's internal time value
clockGetInternalTime clock =
    liftM fromIntegral $
        (\(Clock arg1) -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_get_internal_time argPtr1) (toClock clock)

-- | Gets the internal rate and reference time of @clock@. See
-- 'clockSetCalibration' for more information.
clockGetCalibration :: ClockClass clock
                    => clock -- ^ @clock@
                    -> IO (ClockTime, ClockTime, Ratio ClockTime)
                       -- ^ the clock's internal time, external (adjusted) time, and skew rate
clockGetCalibration clock =
    alloca $ \internalPtr ->
        alloca $ \externalPtr ->
            alloca $ \rateNumPtr ->
                alloca $ \rateDenomPtr ->
                    do (\(Clock arg1) arg2 arg3 arg4 arg5 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_get_calibration argPtr1 arg2 arg3 arg4 arg5) (toClock clock)
                                                        internalPtr
                                                        externalPtr
                                                        rateNumPtr
                                                        rateDenomPtr
                       liftM4 (\a b c d ->
                               (fromIntegral a,
                                fromIntegral b,
                                fromIntegral c % fromIntegral d))
                              (peek internalPtr)
                              (peek externalPtr)
                              (peek rateNumPtr)
                              (peek rateDenomPtr)

-- | Adjusts the rate and time of clock. A rate of @1 % 1@ is the
-- normal speed of the clock. Larger values make the clock go
-- faster.
--
-- The parameters @internal@ and @external@ specifying that
-- 'clockGetTime' should have returned @external@ when the clock had
-- internal time @internal@. The parameter @internal@ should not be
-- in the future; that is, it should be less than the value returned
-- by 'clockGetInternalTime' when this function is called.
--
-- Subsequent calls to 'clockGetTime' will return clock times
-- computed as follows:
--
-- > (clock_internal - internal) * rate + external
--
-- Note that 'clockGetTime' always returns increasing values, so if
-- the clock is moved backwards, 'clockGetTime' will report the
-- previous value until the clock catches up.
clockSetCalibration :: ClockClass clock
                    => clock -- ^ @clock@
                    -> ClockTime -- ^ @internal@
                    -> ClockTime -- ^ @external@
                    -> Ratio ClockTime -- ^ @rate@
                    -> IO ()
clockSetCalibration clock internal external rate =
    (\(Clock arg1) arg2 arg3 arg4 arg5 -> withForeignPtr arg1 $ \argPtr1 ->gst_clock_set_calibration argPtr1 arg2 arg3 arg4 arg5) (toClock clock)
                                     (fromIntegral internal)
                                     (fromIntegral external)
                                     (fromIntegral $ numerator rate)
                                     (fromIntegral $ denominator rate)

-- | Get the time of @clockID@.
clockIDGetTime :: ClockID -- ^ @clockID@
               -> IO ClockTime
clockIDGetTime clockID =
    liftM fromIntegral $ withClockID clockID $
        gst_clock_id_get_time . castPtr

-- | Perform a blocking wait on @clockID@. The parameter @clockID@
-- should have been created with 'clockNewSingleShotID' or
-- 'clockNewPeriodicID', and should not been unscheduled with a call
-- to 'clockIDUnschedule'.
--
-- If second value in the returned pair is not 'Nothing', it will
-- contain the difference against the clock and the time of
-- @clockID@ when this method was called. Positive values indicate
-- how late @clockID@ was relative to the clock. Negative values
-- indicate how much time was spend waiting on the clock before the
-- function returned.
clockIDWait :: ClockID -- ^ @clockID@
            -> IO (ClockReturn, Maybe ClockTimeDiff)
clockIDWait clockID =
    alloca $ \jitterPtr ->
        do result <- liftM cToEnum $ withClockID clockID $ \clockIDPtr ->
                         gst_clock_id_wait (castPtr clockIDPtr) jitterPtr
           jitter <- let peekJitter = liftM (Just . fromIntegral) $ peek jitterPtr
                     in case result of
                          ClockOk -> peekJitter
                          ClockEarly -> peekJitter
                          _ -> return Nothing
           return (result, jitter)

-- | Cancel an outstanding request with @clockID@. After this call,
-- @clockID@ cannot be used anymore to recieve notifications; you
-- must create a new 'ClockID'.
clockIDUnschedule :: ClockID -- ^ @clockID@
                  -> IO ()
clockIDUnschedule clockID =
    withClockID clockID $ gst_clock_id_unschedule . castPtr

-- | The amount of time, in nanoseconds, between samples.
clockTimeout :: ClockClass clockT
             => Attr clockT ClockTime
clockTimeout = newAttr
    (objectGetPropertyUInt64 "timeout")
    (objectSetPropertyUInt64 "timeout")

-- | The size of the window used to calculate rate and offset.
clockWindowSize :: ClockClass clockT
                => Attr clockT Int
clockWindowSize =
    newAttrFromIntProperty "window-size"

-- | The threshold to start calculating rate and offset.
clockWindowThreshold :: ClockClass clockT
                     => Attr clockT Int
clockWindowThreshold =
    newAttrFromIntProperty "window-threshold"

foreign import ccall safe "gst_clock_add_observation"
  gst_clock_add_observation :: ((Ptr Clock) -> (CULLong -> (CULLong -> ((Ptr CDouble) -> (IO CInt)))))

foreign import ccall safe "gst_clock_set_master"
  gst_clock_set_master :: ((Ptr Clock) -> ((Ptr Clock) -> (IO CInt)))

foreign import ccall safe "gst_clock_get_master"
  gst_clock_get_master :: ((Ptr Clock) -> (IO (Ptr Clock)))

foreign import ccall safe "gst_clock_set_resolution"
  gst_clock_set_resolution :: ((Ptr Clock) -> (CULLong -> (IO CULLong)))

foreign import ccall safe "gst_clock_get_resolution"
  gst_clock_get_resolution :: ((Ptr Clock) -> (IO CULLong))

foreign import ccall safe "gst_clock_get_time"
  gst_clock_get_time :: ((Ptr Clock) -> (IO CULLong))

foreign import ccall safe "gst_clock_new_single_shot_id"
  gst_clock_new_single_shot_id :: ((Ptr Clock) -> (CULLong -> (IO (Ptr ()))))

foreign import ccall safe "gst_clock_new_periodic_id"
  gst_clock_new_periodic_id :: ((Ptr Clock) -> (CULLong -> (CULLong -> (IO (Ptr ())))))

foreign import ccall safe "gst_clock_get_internal_time"
  gst_clock_get_internal_time :: ((Ptr Clock) -> (IO CULLong))

foreign import ccall safe "gst_clock_get_calibration"
  gst_clock_get_calibration :: ((Ptr Clock) -> ((Ptr CULLong) -> ((Ptr CULLong) -> ((Ptr CULLong) -> ((Ptr CULLong) -> (IO ()))))))

foreign import ccall safe "gst_clock_set_calibration"
  gst_clock_set_calibration :: ((Ptr Clock) -> (CULLong -> (CULLong -> (CULLong -> (CULLong -> (IO ()))))))

foreign import ccall safe "gst_clock_id_get_time"
  gst_clock_id_get_time :: ((Ptr ()) -> (IO CULLong))

foreign import ccall safe "gst_clock_id_wait"
  gst_clock_id_wait :: ((Ptr ()) -> ((Ptr CLLong) -> (IO CInt)))

foreign import ccall safe "gst_clock_id_unschedule"
  gst_clock_id_unschedule :: ((Ptr ()) -> (IO ()))