/*
* @(#)BasicClock.java 1.7 02/08/21
*
* Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.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();
}
/**
* 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;
}
/**
* Start the clock with the given time-base-time
* @param 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();
}
/**
* 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;
}
/**
* 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();
}
/**
* Return the preset stop time.
* @return the preset stop time.
**/
public Time getStopTime() {
return new Time(stopTime);
}
/**
* Set the media time. This will be the media presented at the
* clock's start time.
* @param 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;
}
/**
* Return the current media time.
* @return the current media time.
**/
public Time getMediaTime() {
return new Time(getMediaNanoseconds());
}
/**
* 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;
}
}
/**
* 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 upper bound of the media time.
* @param t the upper bound of the media time.
*/
protected void setMediaLength(long t) {
mediaLength = t;
}
/**
* 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 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 rate of presentation: 1.0: normal, 2.0: twice the speed.
* -2.0: twice the speed in reverse.
* @param 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;
}
/**
* Get the current presentation speed.
* @return the current presentation speed.
**/
public float getRate() {
return rate;
}
protected void throwError(Error e) {
Log.dumpStack(e);
throw e;
}
}
|