All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.fmj.media.BasicClock Maven / Gradle / Ivy

package net.sf.fmj.media;

import javax.media.*;

/**
 * BasicClock implements javax.media.Clock. This is not a running thread that
 * implements the clock ticks. It just implements the math and maintains the
 * correct states to perform the computations from media-time to time-base-time.
 *
 * @version 1.7, 02/08/21
 */
public class BasicClock implements Clock
{
    private TimeBase master;
    private long startTime = Long.MAX_VALUE; // in time base time
    private long stopTime = Long.MAX_VALUE; // in media time
    private long mediaTime = 0; // The media time when the state of
    // the clock last changes.
    private long mediaStart = 0; // lower bound of the mediaTime.
    private long mediaLength = -1; // upper bound of the mediaTime.
    private float rate = (float) 1.0;

    public final static int STOPPED = 0;
    public final static int STARTED = 1;

    public BasicClock()
    {
        master = new SystemTimeBase();
    }

    /**
     * Get the current media time in nanoseconds.
     *
     * @return the current media time in nanoseconds.
     */
    public long getMediaNanoseconds()
    {
        if (getState() == STOPPED)
        {
            return mediaTime;
        }

        long now = master.getNanoseconds();
        if (now > startTime)
        {
            // The media has been playing for a while.
            long t = (long) ((double) (now - startTime) * rate) + mediaTime;
            if (mediaLength != -1 && t > mediaStart + mediaLength)
                return mediaStart + mediaLength;
            else
                return t;
        } else
        {
            // We haven't reached the scheduled start time yet.
            return mediaTime;
        }
    }

    /**
     * Return the current media time.
     *
     * @return the current media time.
     */
    public Time getMediaTime()
    {
        return new Time(getMediaNanoseconds());
    }

    /**
     * Get the current presentation speed.
     *
     * @return the current presentation speed.
     */
    public float getRate()
    {
        return rate;
    }

    /**
     * Return the current state of the clock in either started or stopped state.
     *
     * @return the current clock state.
     */
    public int getState()
    {
        // A start time has not been set.
        if (startTime == Long.MAX_VALUE)
            return STOPPED;

        // A stop time has not been set.
        if (stopTime == Long.MAX_VALUE)
            return STARTED;

        // The tricky case is when the clocked has started and
        // the media has reached the scheduled stop time. In that
        // case, the clock is considered stopped.
        // COMMENTED OUT BY BABU
        // long now = master.getNanoseconds();
        // if (now > startTime) {
        // // The media has already been playing for some time.

        // long curMediaTime = (long)((now - startTime) * rate) + mediaTime;
        // if ((rate > 0 && curMediaTime >= stopTime) ||
        // (rate < 0 && curMediaTime <= stopTime)) {
        // // We have gone past the scheduled stop time.
        // // We are in the stop state.
        // System.out.println(this + ": BasicClock getState() SIDEEFFECT");
        // mediaTime = validateTime(stopTime);
        // startTime = Long.MAX_VALUE;
        // return STOPPED;
        // }
        // }

        // In all other cases, the clock has already been started.
        return STARTED;
    }

    /**
     * Return the preset stop time.
     *
     * @return the preset stop time.
     */
    public Time getStopTime()
    {
        return new Time(stopTime);
    }

    /**
     * Return the Sync Time. Not yet implementated.
     */
    public Time getSyncTime()
    {
        return new Time(0);
    }

    /**
     * Get the Time Base that the clock is currently using.
     *
     * @return the Time Base that the clock is currently using.
     */
    public TimeBase getTimeBase()
    {
        return master;
    }

    /**
     * Map the the given media-time to time-base-time.
     *
     * @param t
     *            media time
     * @return time base time.
     * @exception ClockStoppedException
     *                is thrown if this method is invoked on a stopped clock.
     */
    public Time mapToTimeBase(Time t) throws ClockStoppedException
    {
        if (getState() == STOPPED)
        {
            ClockStoppedException e = new ClockStoppedException();
            Log.dumpStack(e);
            throw e;
        }
        return new Time((long) ((t.getNanoseconds() - mediaTime) / rate)
                + startTime);
    }

    /**
     * Set the upper bound of the media time.
     *
     * @param t
     *            the upper bound of the media time.
     */
    protected void setMediaLength(long t)
    {
        mediaLength = t;
    }

    /**
     * Set the lower bound of the media time.
     *
     * @param t
     *            the lower bound of the media time.
     */
    protected void setMediaStart(long t)
    {
        mediaStart = t;
    }

    /**
     * Set the media time. This will be the media presented at the clock's start
     * time.
     *
     * @param now the media time to set to.
     */
    public void setMediaTime(Time now)
    {
        if (getState() == STARTED)
        {
            throwError(new ClockStartedError(
                    "setMediaTime() cannot be used on a started clock."));
        }
        long t = now.getNanoseconds();
        if (t < mediaStart)
            mediaTime = mediaStart;
        else if (mediaLength != -1 && t > mediaStart + mediaLength)
            mediaTime = mediaStart + mediaLength;
        else
            mediaTime = t;
    }

    /**
     * Set the rate of presentation: 1.0: normal, 2.0: twice the speed. -2.0:
     * twice the speed in reverse.
     *
     * @param factor the speed factor.
     * @return the actual rate the clock is set to.
     */
    public float setRate(float factor)
    {
        if (getState() == STARTED)
        {
            throwError(new ClockStartedError(
                    "setRate() cannot be used on a started clock."));
        }
        rate = factor;
        return rate;
    }

    /**
     * Preset a stop time in media time.
     *
     * @param t
     *            the preset stop time.
     */
    public void setStopTime(Time t)
    {
        if (getState() == STARTED && stopTime != Long.MAX_VALUE)
        {
            throwError(new StopTimeSetError(
                    "setStopTime() may be set only once on a Started Clock"));
        }
        stopTime = t.getNanoseconds();
    }

    /**
     * Set a time base on the clock. All media-time to time-base-time will be
     * computed with the given time base.
     *
     * @param master
     *            the new master time base.
     * @exception IncompatibleTimeBaseException
     *                thrown if clock cannot accept the given time base.
     */
    public void setTimeBase(TimeBase master)
            throws IncompatibleTimeBaseException
    {
        if (getState() == STARTED)
        {
            throwError(new ClockStartedError(
                    "setTimeBase cannot be used on a started clock."));
        }
        if (master == null)
        {
            if (!(this.master instanceof SystemTimeBase))
                this.master = new SystemTimeBase();
        } else
            this.master = master;
    }

    /**
     * Stop the clock immediately.
     */
    public void stop()
    {
        if (getState() == STOPPED)
        {
            // It's already stopped. No-op.
            return;
        }

        // It's a change of state, so we'll mark the mediaTime.
        mediaTime = getMediaNanoseconds();

        // Reset the start time.
        startTime = Long.MAX_VALUE;
    }

    /**
     * Start the clock with the given time-base-time
     *
     * @param tbt the time base time to start the clock.
     */
    public void syncStart(Time tbt)
    {
        if (getState() == STARTED)
        {
            throwError(new ClockStartedError(
                    "syncStart() cannot be used on an already started clock."));
        }
        // If the given start time is already later than now. We'll reset
        // the clock start time to now.
        if (master.getNanoseconds() > tbt.getNanoseconds())
            startTime = master.getNanoseconds();
        else
            startTime = tbt.getNanoseconds();
    }

    protected void throwError(Error e)
    {
        Log.dumpStack(e);
        throw e;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy