CENTRE_OF_SCREEN -> VERTICAL_CENTRE_OF_SCREEN.
[libsub.git] / src / sub_time.cc
index a8e3a31b57a620de237ebaa080c80a99947b5d84..763b4e44aba4e1389f118ba401fd3a0670bf3031 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    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());
 }