net.sf.fmj.ejmf.toolkit.media.AbstractClock Maven / Gradle / Ivy
Show all versions of fmj Show documentation
package net.sf.fmj.ejmf.toolkit.media;
import javax.media.*;
/**
* The AbstractClock provides an abstract implementation of the
* javax.media.Clock interface. All methods are implemented except for
* setStopTime() and getStopTime(). These methods will invariably require
* implementation-specific functionality and therefore are left to the
* subclasses to implement.
*
* From the book: Essential JMF, Gordon, Talley (ISBN 0130801046). Used with
* permission.
*
* @see AbstractController
* @see StopTimeMonitor
*
* @author Steve Talley & Rob Gordon
*/
public class AbstractClock implements Clock
{
private TimeBase systemtimebase = Manager.getSystemTimeBase();
private TimeBase timebase = systemtimebase;
private Time mediaStartTime = new Time(0);
private Time mediaStopTime = Clock.RESET;
private Time timeBaseStartTime;
private float rate = 1.0F;
private boolean isStarted = false;
/**
* Constructs an AbstractClock.
*/
public AbstractClock()
{
super();
}
// //////////////////////////////////////////////////////
//
// javax.media.Clock methods
//
// //////////////////////////////////////////////////////
/**
* Calculates the media time on a started Clock based on the assumption that
* the Clock is started.
*/
private synchronized Time calculateMediaTime()
{
long tbCurrent = timebase.getNanoseconds();
long tbStart = timeBaseStartTime.getNanoseconds();
// If we are scheduled to start but haven't yet reached
// the scheduled start time, return the media start time
if (tbCurrent < tbStart)
{
return mediaStartTime;
}
long mStart = mediaStartTime.getNanoseconds();
long mCurrent = (long) ((tbCurrent - tbStart) * rate + mStart);
return new Time(mCurrent);
}
/**
* Get the media time in nanoseconds.
*/
public synchronized long getMediaNanoseconds()
{
return getMediaTime().getNanoseconds();
}
/**
* Get the media time the media is scheduled to start (if the Clock is
* stopped), or the media time at which the Clock started (if the Clock is
* started).
*/
protected Time getMediaStartTime()
{
return mediaStartTime;
}
/**
* Calculates the current media time based on the current time-base time,
* the time-base start time, the media start time, and the rate.
*
* @return The current media time
*/
public synchronized Time getMediaTime()
{
if (!isStarted)
{
// If the Clock is stopped return it's starting
// media-time
return mediaStartTime;
} else
{
// Calculate the media time
return calculateMediaTime();
}
}
/**
* Get the current temporal scale factor. The scale factor defines the
* relationship between the Clock's media time and its
* TimeBase.
*
* For example, a rate of 2.0 indicates that media time will pass
* twice as fast as the TimeBase time once the Clock
* starts. Similarly, a negative rate indicates that the Clock runs
* in the opposite direction of its TimeBase. All Clocks
* are guaranteed to support a rate of 1.0, the default rate.
* Clocks are not required to support any other rate.
*
*/
public synchronized float getRate()
{
return rate;
}
/**
* Get the last value successfully set by setStopTime. Returns the constant
* Clock.RESET if no stop time is set
*
* @return The current stop time.
*/
public synchronized Time getStopTime()
{
return mediaStopTime;
}
/**
* Gets the time until the Clock's media synchronizes with its time-base.
*
* @return The time remaining until the time-base start-time if this Clock
* is Started and the time-base start-time has not yet been reached,
* or the media time otherwise.
*/
public synchronized Time getSyncTime()
{
if (isStarted)
{
long startNano = timeBaseStartTime.getNanoseconds();
long nowNano = getTimeBase().getNanoseconds();
if (startNano >= nowNano)
{
return new Time((nowNano - startNano));
}
}
return getMediaTime();
}
/**
* Get the TimeBase that this Clock is using.
*/
public synchronized TimeBase getTimeBase()
{
return timebase;
}
/**
* Get the time-base time the media is scheduled to start (if the Clock is
* stopped), or the time-base time at which the Clock started (if the Clock
* is started).
*/
protected Time getTimeBaseStartTime()
{
return timeBaseStartTime;
}
/**
* Given a media time, returns the corresponding time-base time. Uses the
* current rate, the time-base start time, and the media start time to
* calculate.
*
* @param t
* A media time to be mapped to a time-base time.
*
* @return A time-base time.
*
* @exception ClockStoppedException
* If the clock has not started.
*/
public synchronized Time mapToTimeBase(Time t) throws ClockStoppedException
{
if (!isStarted)
{
throw new ClockStoppedException(
"Cannot map media time to time-base time on a Stopped Clock");
}
long mCurrent = t.getNanoseconds();
long mStart = mediaStartTime.getNanoseconds();
long tbStart = timeBaseStartTime.getNanoseconds();
return new Time((long) (((mCurrent - mStart) / rate) + tbStart));
}
/**
* Sets the media time.
*
* @param t
* The media time to set
*
* @exception ClockStartedError
* If the Clock is Started.
*/
public synchronized void setMediaTime(Time t)
{
if (isStarted)
{
throw new ClockStartedError(
"Cannot set media time on a Started Clock");
}
mediaStartTime = t;
}
/**
* Set the temporal scale factor. The argument suggests the scale
* factor to use.
*
* The setRate method returns the actual rate set by the
* Clock. Clocks should set their rate as close to the
* requested value as possible, but are not required to set the rate to the
* exact value of any argument other than 1.0. A Clock is only
* guaranteed to set its rate exactly to 1.0.
*
* @param rate
* The temporal scale factor (rate) to set.
*
* @exception ClockStartedError
* If the Clock is Started.
*
* @return The actual rate set.
*
*/
public synchronized float setRate(float rate)
{
if (isStarted)
{
throw new ClockStartedError("Cannot set rate on a Started Clock");
}
if (rate != 0.0F)
{
this.rate = rate;
}
return this.rate;
}
/**
* Set the media time at which you want the Clock to stop.
* The Clock will stop when its media time passes the
* stop-time. To clear the stop time, set it to: Clock.RESET.
*
* You can always call setStopTime on a Stopped
* Clock.
*
* On a Started Clock, the stop-time can only be set
* once. A StopTimeSetError is thrown if
* setStopTime is called and the media stop-time has already
* been set.
*
* @param mediaStopTime
* The time at which you want the Clock to stop, in
* media time.
*/
public synchronized void setStopTime(Time mediaStopTime)
{
if (isStarted && this.mediaStopTime != RESET)
{
throw new StopTimeSetError(
"Stop time may be set only once on a Started Clock");
}
this.mediaStopTime = mediaStopTime;
}
/**
* Set the TimeBase for this Clock. This method can only
* be called on a Stopped Clock. A
* ClockStartedError is thrown if setTimeBase is called on
* a Started Clock.
*
* A Clock has a default TimeBase that is determined by
* the implementation. To reset a Clock to its default
* TimeBase, call setTimeBase(null).
*
* @param timebase
* The new TimeBase or null to reset the
* Clock to its default TimeBase.
*
* @exception IncompatibleTimeBaseException
* Thrown if the Clock can't use the specified
* TimeBase.
*/
public synchronized void setTimeBase(TimeBase timebase)
throws IncompatibleTimeBaseException
{
if (isStarted)
{
throw new ClockStartedError(
"Cannot set time base on a Started Clock");
}
if (timebase == null)
{
this.timebase = systemtimebase;
} else
{
this.timebase = timebase;
}
}
/**
* Stop the Clock.
*/
public synchronized void stop()
{
if (isStarted)
{
mediaStartTime = calculateMediaTime(); // KAL: TODO: what is the
// reason for this?
// Commented out -- is this necessary?
// timeBaseStartTime = timebase.getTime();
isStarted = false;
}
}
/**
* syncStart the AbstractClock at the previously- specified time-base start
* time.
*
* Synchronous method -- return when transition complete
*/
public synchronized void syncStart(Time t)
{
// Enforce state prereqs
if (isStarted)
{
throw new ClockStartedError(
"syncStart() cannot be called on a started Clock");
}
long now = getTimeBase().getNanoseconds();
long start = t.getNanoseconds();
if (start - now > 0)
{
// Start time is in the future
// Set the time-base start time
this.timeBaseStartTime = new Time(start);
} else
{
// Start time is in the past
// Set the time-base start time to be now
this.timeBaseStartTime = new Time(now);
}
isStarted = true;
}
}