Browse Source

Animation: implemented Player:seekBy() / Player::seekTo().

inverted-ranges
Vladimír Vondruš 8 years ago
parent
commit
adaeb21a4d
  1. 56
      src/Magnum/Animation/Player.h
  2. 38
      src/Magnum/Animation/Player.hpp
  3. 240
      src/Magnum/Animation/Test/PlayerTest.cpp

56
src/Magnum/Animation/Player.h

@ -160,7 +160,14 @@ Calling @ref pause() while the animation is running immediately transfers the
animation state to @ref State::Paused and the next @ref advance() iteration
will give out interpolated values corresponding to a time that was passed to
the @ref pause() function. After that, no more updates are done until the
animation is resumed again with @ref play() or stopped with @ref stop().
animation is resumed again with @ref play(), stopped with @ref stop() or seeked
using @ref seekBy() / @ref seekTo().
Calling @ref seekBy() / @ref seekTo() while the animation is either playing or
pause will cause it to jump to specified time -- the next call to @ref advance()
will update the destination locations and/or fire user-defined callbacks with
new values, behaving as if the animation was played / paused with the seek
time.
The callbacks are only ever fired from within the @ref advance() function,
never from @ref pause(), @ref stop() or any other API.
@ -642,6 +649,42 @@ template<class T, class K
*/
Player<T, K>& pause(T pauseTime);
/**
* @brief Seek by given time delta
*
* Causes the animation to jump forward (if @p timeDelta is positive)
* or backward (if @p timeDelta is negative). If @ref state() is
* @ref State::Paused, seeking too far backward will make the animation
* paused at the beginning, while seeking too far forward will cause
* it paused at the end (i.e., not stopped). If @ref state() is already
* @ref State::Stopped, the function does nothing. See @ref advance()
* for a detailed description of seeking behavior.
*
* @note This function doesn't clamp the seek in any way --- so for
* example seeking too far back will make the animation wait for
* being played from the beginning in the future.
*/
Player<T, K>& seekBy(T timeDelta);
/**
* @brief Seek to given absolute animation time
*
* Causes the animation to jump to @p animationTime at given
* @p seekTime. If @ref state() is @ref State::Playing, seeking too far
* backward will make the animation start from the beginning, while
* seeking too far forward will cause the animation to be stopped. If
* @ref state() is @ref State::Paused, seeking too far backward will
* make the animation paused at the beginning, while seeking too far
* forward will cause it paused at the end (i.e., not stopped). If
* @ref state() is @ref State::Stopped, the function does nothing. See
* @ref advance() for a detailed description of seeking behavior.
*
* @note This function doesn't clamp the seek in any way --- so for
* example seeking too far back will make the animation wait for
* being played from the beginning in the future.
*/
Player<T, K>& seekTo(T seekTime, T animationTime);
/**
* @brief Stop
*
@ -689,7 +732,8 @@ template<class T, class K
* fire user-defined callbacks with key and result values corresponding
* to the time passed to the @ref pause() call before in order to
* correctly "park" the animation. After that, no more updates are done
* until the animation is started again.
* until the animation is started again or @ref seekBy() / @ref seekTo()
* is called.
*
* If @ref stop() was called right before a particular @ref advance()
* iteration, the function will update destination locations and/or
@ -697,6 +741,14 @@ template<class T, class K
* to the begin time of @ref duration() in order to correctly "park"
* the animation back to its initial state. After that, no more updates
* are done until the animation is started again.
*
* If @ref seekBy() or @ref seekTo() was called right before a
* particular @ref advance() iteration and @ref state() is
* @ref State::Paused, the function will update destination locations
* and/or fire user-defined callbacks with key and result values
* corresponding to the new pause time in order to correctly "park" the
* animation. After that, no more updates are done until the animation
* is started again or @ref seekBy() / @ref seekTo() is called.
* @see @ref elapsed()
*/
Player<T, K>& advance(T time);

38
src/Magnum/Animation/Player.hpp

@ -127,6 +127,44 @@ template<class T, class K> Player<T, K>& Player<T, K>::pause(T pauseTime) {
return *this;
}
template<class T, class K> Player<T, K>& Player<T, K>::seekBy(T timeDelta) {
/* Animation is stopped, nothing to do */
if(_state == State::Stopped) return *this;
/* If the animation is paused and parked already, trigger a "park" again in
order to have the values updated on the next call to advance(). The
value is simply the new elapsed animation time. */
if(_state == State::Paused && _stopPauseTime == T{}) {
_stopPauseTime = _startTime + timeDelta;
_startTime = {};
return *this;
}
/* Otherwise, the animation is either playing or not yet parked, simply
patch the start time to make the seek */
_startTime -= timeDelta;
return *this;
}
template<class T, class K> Player<T, K>& Player<T, K>::seekTo(T seekTime, T animationTime) {
/* Animation is stopped, nothing to do */
if(_state == State::Stopped) return *this;
/* If the animation is paused and parked already, trigger a "park" again in
order to have the values updated on the next call to advance(). The
value is simply the new elapsed animation time. */
if(_state == State::Paused && _stopPauseTime == T{}) {
_stopPauseTime = animationTime;
_startTime = {};
return *this;
}
/* Otherwise, the animation is either playing or not yet parked, simply
patch the start time to make the seek */
_startTime = seekTime - animationTime;
return *this;
}
template<class T, class K> Player<T, K>& Player<T, K>::stop() {
_state = State::Stopped;
/* Anything, just not a default-constructed value */

240
src/Magnum/Animation/Test/PlayerTest.cpp

@ -61,6 +61,16 @@ struct PlayerTest: TestSuite::Tester {
void advanceZeroDurationInfinitePlayCount();
void advanceZeroDurationInfinitePlayCountChrono();
void seekByStopped();
void seekByPlaying();
void seekByPaused();
void seekByPausedParked();
void seekToStopped();
void seekToPlaying();
void seekToPaused();
void seekToPausedParked();
void setState();
void add();
@ -132,6 +142,16 @@ PlayerTest::PlayerTest() {
&PlayerTest::advanceZeroDurationInfinitePlayCount,
&PlayerTest::advanceZeroDurationInfinitePlayCountChrono,
&PlayerTest::seekByStopped,
&PlayerTest::seekByPlaying,
&PlayerTest::seekByPaused,
&PlayerTest::seekByPausedParked,
&PlayerTest::seekToStopped,
&PlayerTest::seekToPlaying,
&PlayerTest::seekToPaused,
&PlayerTest::seekToPausedParked,
&PlayerTest::setState,
&PlayerTest::add,
@ -800,6 +820,226 @@ void PlayerTest::advanceZeroDurationInfinitePlayCountChrono() {
CORRADE_COMPARE(value, 4.0f);
}
void PlayerTest::seekByStopped() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
player.seekBy(1.5f);
player.advance(1.75f);
/* Nothing should change */
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::seekByPlaying() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* 1.75 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
value = -1.0f;
player.seekBy(-1.25f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
}
void PlayerTest::seekByPaused() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* Pause at 1.75 secs in, no advance() yet so not parked yet */
player.pause(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, -1.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
player.seekBy(-1.25f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
/* Updating again should do nothing */
value = -1.0f;
player.advance(25.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(25.0f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::seekByPausedParked() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* Pause at 1.75 secs in */
player.pause(23.75f)
.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
value = -1.0f;
player.seekBy(-1.25f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated (re-parked) at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
/* Updating again should do nothing */
value = -1.0f;
player.advance(25.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(25.0f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::seekToStopped() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value);
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
player.seekTo(1.75f, -0.5f);
player.advance(1.75f);
/* Nothing should change */
CORRADE_COMPARE(player.state(), State::Stopped);
CORRADE_COMPARE(player.elapsed(0.0f), std::make_pair(0, 0.0f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::seekToPlaying() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* 1.75 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
value = -1.0f;
player.seekTo(23.75f, 0.5f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Playing);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
}
void PlayerTest::seekToPaused() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* Pause at 1.75 secs in, no advance() yet so not parked yet */
player.pause(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, -1.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
player.seekTo(23.75f, 0.5f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
/* Updating again should do nothing */
value = -1.0f;
player.advance(25.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(25.0f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::seekToPausedParked() {
Float value = -1.0f;
Player<Float> player;
player.add(Track, value)
.play(22.0f);
/* Pause at 1.75 secs in */
player.pause(23.75f)
.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 1.75f));
CORRADE_COMPARE(value, 4.0f);
/* Seek to 0.5 secs in, the value should not change after just a seek */
value = -1.0f;
player.seekTo(23.75f, 0.5f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
/* Now it should be updated (re-parked) at 0.5 secs in */
player.advance(23.75f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(23.75f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, 2.0f);
/* Updating again should do nothing */
value = -1.0f;
player.advance(25.0f);
CORRADE_COMPARE(player.state(), State::Paused);
CORRADE_COMPARE(player.elapsed(25.0f), std::make_pair(0, 0.5f));
CORRADE_COMPARE(value, -1.0f);
}
void PlayerTest::setState() {
Player<Float> player;
CORRADE_COMPARE(player.state(), State::Stopped);

Loading…
Cancel
Save