aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Horn2010-01-04 22:48:28 +0000
committerMax Horn2010-01-04 22:48:28 +0000
commit4ae9412a3aea75990008c3a32080c54533fdb389 (patch)
tree5f7910434efd0e25c9278fa75cc86628dab7a4ce
parenta6aaeb70e6dfb3a9727e8861041601cf658aa5cd (diff)
downloadscummvm-rg350-4ae9412a3aea75990008c3a32080c54533fdb389.tar.gz
scummvm-rg350-4ae9412a3aea75990008c3a32080c54533fdb389.tar.bz2
scummvm-rg350-4ae9412a3aea75990008c3a32080c54533fdb389.zip
Make some improvements for Audio::Timestamp.
* Add convertToFramerate() method * Add framerate() method * Add operator == and != * Improve frameDiff() to work for two timestamps with distinct framerates * Improve Doxygen comments svn-id: r46994
-rw-r--r--sound/timestamp.cpp107
-rw-r--r--sound/timestamp.h72
-rw-r--r--test/sound/timestamp.h30
3 files changed, 170 insertions, 39 deletions
diff --git a/sound/timestamp.cpp b/sound/timestamp.cpp
index c85ec47ae8..a517462bb4 100644
--- a/sound/timestamp.cpp
+++ b/sound/timestamp.cpp
@@ -27,51 +27,110 @@
namespace Audio {
-Timestamp::Timestamp(uint32 m, int frameRate) :
- _msecs(m), _frameRate(frameRate), _frameOffset(0) {
- assert(_frameRate > 0);
+static uint gcd(uint a, uint b) {
+ while (a > 0) {
+ int tmp = a;
+ a = b % a;
+ b = tmp;
+ }
+ return b;
+}
+
+Timestamp::Timestamp(uint32 m, int framerate) :
+ _msecs(m), _framerate(framerate), _numberOfFrames(0) {
+ assert(_framerate > 0);
+}
+
+
+Timestamp Timestamp::convertToFramerate(int newFramerate) const {
+ Timestamp ts(*this);
+
+ if (ts._framerate != newFramerate) {
+ ts._framerate = newFramerate;
+
+ const uint g = gcd(_framerate, ts._framerate);
+ const uint p = _framerate / g;
+ const uint q = ts._framerate / g;
+
+ // Convert the frame offset to the new framerate.
+ // We round to the nearest (as opposed to always
+ // rounding down), to minimize rounding errors during
+ // round trip conversions.
+ ts._numberOfFrames = (ts._numberOfFrames * q + p/2) / p;
+
+ ts._msecs += (ts._numberOfFrames / ts._framerate) * 1000;
+ ts._numberOfFrames %= ts._framerate;
+ }
+
+ return ts;
+}
+
+bool Timestamp::operator==(const Timestamp &ts) const {
+ // TODO: Alternatively, we could define equality to mean that
+ // two timestamps describe the exacts same moment in time.
+ return (_msecs == ts._msecs) &&
+ (_numberOfFrames == ts._numberOfFrames) &&
+ (_framerate == ts._framerate);
+}
+
+bool Timestamp::operator!=(const Timestamp &ts) const {
+ return !(*this == ts);
}
Timestamp Timestamp::addFrames(int frames) const {
- Timestamp timestamp(*this);
- timestamp._frameOffset += frames;
+ Timestamp ts(*this);
+ ts._numberOfFrames += frames;
- if (timestamp._frameOffset < 0) {
- int secsub = 1 + (-timestamp._frameOffset / timestamp._frameRate);
+ if (ts._numberOfFrames < 0) {
+ int secsub = 1 + (-ts._numberOfFrames / ts._framerate);
- timestamp._frameOffset += timestamp._frameRate * secsub;
- timestamp._msecs -= secsub * 1000;
+ ts._numberOfFrames += ts._framerate * secsub;
+ ts._msecs -= secsub * 1000;
}
- timestamp._msecs += (timestamp._frameOffset / timestamp._frameRate) * 1000;
- timestamp._frameOffset %= timestamp._frameRate;
+ ts._msecs += (ts._numberOfFrames / ts._framerate) * 1000;
+ ts._numberOfFrames %= ts._framerate;
- return timestamp;
+ return ts;
}
Timestamp Timestamp::addMsecs(int ms) const {
- Timestamp timestamp(*this);
- timestamp._msecs += ms;
- return timestamp;
+ Timestamp ts(*this);
+ ts._msecs += ms;
+ return ts;
}
-int Timestamp::frameDiff(const Timestamp &b) const {
- assert(_frameRate == b._frameRate);
+int Timestamp::frameDiff(const Timestamp &ts) const {
+
+ int delta = 0;
+ if (_msecs != ts._msecs)
+ delta = (long(_msecs) - long(ts._msecs)) * _framerate / 1000;
+
+ delta += _numberOfFrames;
- int msecdelta = 0;
- if (_msecs != b._msecs)
- msecdelta = (long(_msecs) - long(b._msecs)) * _frameRate / 1000;
+ if (_framerate == ts._framerate) {
+ delta -= ts._numberOfFrames;
+ } else {
+ // We need to multiply by the quotient of the two framerates.
+ // We cancel the GCD in this fraction to reduce the risk of
+ // overflows.
+ const uint g = gcd(_framerate, ts._framerate);
+ const uint p = _framerate / g;
+ const uint q = ts._framerate / g;
+
+ delta -= (ts._numberOfFrames * p + q/2) / q;
+ }
- return msecdelta + _frameOffset - b._frameOffset;
+ return delta;
}
-int Timestamp::msecsDiff(const Timestamp &b) const {
- return long(msecs()) - long(b.msecs());
+int Timestamp::msecsDiff(const Timestamp &ts) const {
+ return long(msecs()) - long(ts.msecs());
}
uint32 Timestamp::msecs() const {
- return _msecs + _frameOffset * 1000L / _frameRate;
+ return _msecs + _numberOfFrames * 1000L / _framerate;
}
diff --git a/sound/timestamp.h b/sound/timestamp.h
index 1a30b4353e..218198dcf7 100644
--- a/sound/timestamp.h
+++ b/sound/timestamp.h
@@ -38,33 +38,77 @@ namespace Audio {
*/
class Timestamp {
protected:
- uint32 _msecs;
- int _frameRate;
- int _frameOffset;
- /* Total time: msecs + frame_offset/frame_rate */
+ /**
+ * The millisecond part of this timestamp.
+ * The total time represented by this timestamp is
+ * computed as follows:
+ * _msecs + 1000 * _numberOfFrames / _framerate
+ */
+ uint _msecs;
+
+ /**
+ * The number of frames which together with _msecs encodes
+ * the timestamp. The total number of frames represented
+ * by this timestamp is computed as follows:
+ * _numberOfFrames + _msecs * _framerate / 1000
+ */
+ int _numberOfFrames;
+
+ /** The framerate, i.e. the number of frames per second. */
+ int _framerate;
public:
/**
* Set up a timestamp with a given time and framerate.
- * @param msecs staring time in milliseconds
- * @param frameRate number of frames per second (must be > 0)
+ * @param msecs starting time in milliseconds
+ * @param framerate number of frames per second (must be > 0)
*/
- Timestamp(uint32 msecs, int frameRate);
+ Timestamp(uint32 msecs, int framerate);
+
+ /**
+ * Return a timestamp which represents as closely as possible
+ * the point in time describes by this timestamp, but with
+ * a different framerate.
+ */
+ Timestamp convertToFramerate(int newFramerate) const;
+
+ bool operator==(const Timestamp &ts) const;
+ bool operator!=(const Timestamp &ts) const;
+// bool operator<(const Timestamp &ts) const;
+// bool operator<=(const Timestamp &ts) const;
+// bool operator>(const Timestamp &ts) const;
+// bool operator>=(const Timestamp &ts) const;
- /** Adds a number of frames to a timestamp. */
+ /**
+ * Returns a new timestamp, which corresponds to the time encoded
+ * by this timestamp with the given number of frames added.
+ */
Timestamp addFrames(int frames) const;
- /** Adds a number of milliseconds to a timestamp. */
+ /**
+ * Returns a new timestamp, which corresponds to the time encoded
+ * by this timestamp with the given number of milliseconds added.
+ */
Timestamp addMsecs(int ms) const;
- /** Computes the difference (# of frames) between this timestamp and b. */
- int frameDiff(const Timestamp &b) const;
+ /**
+ * Computes the number of frames between this timestamp and ts.
+ * The frames are with respect to the framerate used by this
+ * Timestamp (which may differ from the framerate used by ts).
+ */
+ int frameDiff(const Timestamp &ts) const;
- /** Computes the difference (# of milliseconds) between this timestamp and b. */
- int msecsDiff(const Timestamp &b) const;
+ /** Computes the number off milliseconds between this timestamp and ts. */
+ int msecsDiff(const Timestamp &ts) const;
- /** Determines the time in milliseconds described by this timestamp. */
+ /**
+ * Determines the time in milliseconds described by this timestamp,
+ * rounded down.
+ */
uint32 msecs() const;
+
+ /** Return the framerate used by this timestamp. */
+ int getFramerate() const { return _framerate; }
};
diff --git a/test/sound/timestamp.h b/test/sound/timestamp.h
index 89fc851a80..dccf67399c 100644
--- a/test/sound/timestamp.h
+++ b/test/sound/timestamp.h
@@ -47,7 +47,6 @@ class TimestampTestSuite : public CxxTest::TestSuite
TS_ASSERT_EQUALS(a.addFrames(-60).msecs(), (uint32)(1234 - 1000 * 60/60));
}
-
void test_more_add_diff() {
const Audio::Timestamp c(10002, 1000);
@@ -56,4 +55,33 @@ class TimestampTestSuite : public CxxTest::TestSuite
TS_ASSERT_EQUALS(v, i);
}
}
+
+
+ void test_diff_with_conversion() {
+ const Audio::Timestamp a = Audio::Timestamp(10, 1000).addFrames(20);
+ const Audio::Timestamp b = Audio::Timestamp(10, 1000/5).addFrames(20/5);
+ const Audio::Timestamp c = Audio::Timestamp(10, 1000*2).addFrames(20*2);
+
+ TS_ASSERT_EQUALS(a.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(a.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(a.frameDiff(c), 0);
+
+ TS_ASSERT_EQUALS(b.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(b.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(b.frameDiff(c), 0);
+
+ TS_ASSERT_EQUALS(c.frameDiff(a), 0);
+ TS_ASSERT_EQUALS(c.frameDiff(b), 0);
+ TS_ASSERT_EQUALS(c.frameDiff(c), 0);
+ }
+
+
+ void test_convert() {
+ const Audio::Timestamp a = Audio::Timestamp(10, 1000).addFrames(20);
+ const Audio::Timestamp b = Audio::Timestamp(10, 1000/5).addFrames(20/5);
+ const Audio::Timestamp c = Audio::Timestamp(10, 1000*2).addFrames(20*2);
+
+ TS_ASSERT_EQUALS(a.convertToFramerate(1000/5), b);
+ TS_ASSERT_EQUALS(a.convertToFramerate(1000*2), c);
+ }
};