/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
*/
#include "sub_time.h"
-#include "compose.hpp"
+#include "sub_assert.h"
+#include "exceptions.h"
+#include <cmath>
+#include <iomanip>
#include <iostream>
using std::ostream;
-using std::string;
+using std::cout;
+using std::setw;
+using std::setfill;
+using boost::optional;
using namespace sub;
bool
-sub::operator== (Time const & a, Time const & b)
+sub::operator< (sub::Time const & a, sub::Time const & b)
{
- return a._hours == b._hours && a._minutes == b._minutes && a._seconds == b._seconds && a._frames == b._frames;
+ if (a._seconds != b._seconds) {
+ return a._seconds < b._seconds;
+ }
+
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return a._frames < b._frames;
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) < (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator> (sub::Time const & a, sub::Time const & b)
+{
+ if (a._seconds != b._seconds) {
+ return a._seconds > b._seconds;
+ }
+
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return a._frames > b._frames;
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) > (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator== (sub::Time const & a, sub::Time const & b)
+{
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return (a._seconds == b._seconds && a._frames == b._frames);
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ if (a._seconds != b._seconds) {
+ return false;
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) == (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator!= (sub::Time const & a, sub::Time const & b)
+{
+ return !(a == b);
}
ostream&
sub::operator<< (ostream& s, Time const & t)
{
- s << t._hours << ":" << t._minutes << ":" << t._seconds << ":" << t._frames;
+ s << setw (2) << setfill('0') << t.hours() << ":"
+ << setw (2) << setfill('0') << t.minutes() << ":"
+ << setw (2) << setfill('0') << t.seconds() << ":"
+ << t._frames;
+
+ if (t._rate) {
+ s << " @ " << t._rate.get().numerator << "/" << t._rate.get().denominator;
+ }
+
return s;
}
-string
-Time::timecode () const
+int
+Time::hours () const
+{
+ return _seconds / 3600;
+}
+
+int
+Time::minutes () const
+{
+ return (_seconds - hours() * 3600) / 60;
+}
+
+int
+Time::seconds () const
+{
+ return (_seconds - hours() * 3600 - minutes() * 60);
+}
+
+int
+Time::frames_at (Rational rate) const
+{
+ if (!_rate) {
+ throw UnknownFrameRateError ();
+ }
+
+ return rint (double (_frames) * _rate.get().denominator * rate.numerator / (_rate.get().numerator * rate.denominator));
+}
+
+int
+Time::milliseconds () const
+{
+ return frames_at (Rational (1000, 1));
+}
+
+Time
+Time::from_hmsf (int h, int m, int s, int f, optional<Rational> rate)
+{
+ return Time (h * 3600 + m * 60 + s, f, rate);
+}
+
+Time
+Time::from_hms (int h, int m, int s, int ms)
{
- return String::compose ("%1:%2:%3:%4", _hours, _minutes, _seconds, _frames);
+ return Time (h * 3600 + m * 60 + s, ms, Rational (1000, 1));
+}
+
+/** Create a Time from a number of frames.
+ * rate must be integer.
+ */
+Time
+Time::from_frames (int f, Rational rate)
+{
+ SUB_ASSERT (rate.denominator != 0);
+ SUB_ASSERT (rate.integer ());
+ return Time (f / rate.integer_fraction(), f % rate.integer_fraction(), rate);
+}
+
+double
+Time::all_as_seconds () const
+{
+ return _seconds + double(milliseconds()) / 1000;
+}
+
+/** Add a time to this one. Both *this and t must have a specified _rate */
+void
+Time::add (Time t)
+{
+ SUB_ASSERT (_rate);
+ SUB_ASSERT (t._rate);
+
+ Rational result_rate = max (*_rate, *t._rate);
+ *this = Time::from_frames((all_as_seconds() + t.all_as_seconds()) * result_rate.fraction(), result_rate);
+}
+
+void
+Time::scale (float f)
+{
+ SUB_ASSERT (_rate);
+ SUB_ASSERT (_rate->denominator != 0);
+ SUB_ASSERT (_rate->integer ());
+
+ double const s = Time::all_as_seconds() * f;
+ _seconds = floor (s);
+ _frames = rint ((s - _seconds) * _rate->fraction());
}