/*
- Copyright (C) 2014-2018 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
+
/** @file src/lib/dcpomatic_time.h
* @brief Types to describe time.
*/
+
#ifndef DCPOMATIC_TIME_H
#define DCPOMATIC_TIME_H
+
#include "frame_rate_change.h"
#include "dcpomatic_assert.h"
#include <boost/optional.hpp>
-#include <boost/foreach.hpp>
#include <stdint.h>
#include <cmath>
#include <ostream>
#include <iomanip>
#include <cstdio>
+
+struct dcpomatic_time_ceil_test;
+struct dcpomatic_time_floor_test;
+
+
+namespace dcpomatic {
+
+
+class HMSF
+{
+public:
+ HMSF () {}
+
+ HMSF (int h_, int m_, int s_, int f_)
+ : h(h_)
+ , m(m_)
+ , s(s_)
+ , f(f_)
+ {}
+
+ int h = 0;
+ int m = 0;
+ int s = 0;
+ int f = 0;
+};
+
+
/** A time in seconds, expressed as a number scaled up by Time::HZ. We want two different
- * versions of this class, ContentTime and DCPTime, and we want it to be impossible to
+ * versions of this class, dcpomatic::ContentTime and dcpomatic::DCPTime, and we want it to be impossible to
* convert implicitly between the two. Hence there's this template hack. I'm not
* sure if it's the best way to do it.
*
/* Explicit conversion from type O */
Time (Time<O, S> d, FrameRateChange f);
+ /** @param hmsf Hours, minutes, seconds, frames.
+ * @param fps Frame rate
+ */
+ Time (HMSF const& hmsf, float fps) {
+ *this = from_seconds (hmsf.h * 3600)
+ + from_seconds (hmsf.m * 60)
+ + from_seconds (hmsf.s)
+ + from_frames (hmsf.f, fps);
+ }
+
Type get () const {
return _t;
}
return *this;
}
+ Time<S, O> operator/ (int o) const {
+ return Time<S, O> (_t / o);
+ }
+
/** Round up to the nearest sampling interval
* at some sampling rate.
* @param r Sampling rate.
*/
- Time<S, O> ceil (float r) const {
- return Time<S, O> (llrint (HZ * frames_ceil(r) / double(r)));
+ Time<S, O> ceil (double r) const {
+ return Time<S, O> (llrint(HZ * frames_ceil(r) / r));
}
- Time<S, O> floor (float r) const {
- return Time<S, O> (llrint (HZ * frames_floor(r) / double(r)));
+ Time<S, O> floor (double r) const {
+ return Time<S, O> (llrint(HZ * frames_floor(r) / r));
+ }
+
+ Time<S, O> round (double r) const {
+ return Time<S, O> (llrint(HZ * frames_round(r) / r));
}
double seconds () const {
}
Time<S, O> abs () const {
- return Time<S, O> (std::abs (_t));
+ return Time<S, O> (std::abs(_t));
}
template <typename T>
/** Split a time into hours, minutes, seconds and frames.
* @param r Frames per second.
- * @param h Returned hours.
- * @param m Returned minutes.
- * @param s Returned seconds.
- * @param f Returned frames.
+ * @return Split time.
*/
template <typename T>
- void split (T r, int& h, int& m, int& s, int& f) const
+ HMSF split (T r) const
{
/* Do this calculation with frames so that we can round
to a frame boundary at the start rather than the end.
*/
- int64_t ff = frames_round (r);
+ auto ff = frames_round (r);
+ HMSF hmsf;
- h = ff / (3600 * r);
- ff -= h * 3600 * r;
- m = ff / (60 * r);
- ff -= m * 60 * r;
- s = ff / r;
- ff -= s * r;
+ hmsf.h = ff / (3600 * r);
+ ff -= static_cast<int64_t>(hmsf.h) * 3600 * r;
+ hmsf.m = ff / (60 * r);
+ ff -= static_cast<int64_t>(hmsf.m) * 60 * r;
+ hmsf.s = ff / r;
+ ff -= static_cast<int64_t>(hmsf.s) * r;
- f = static_cast<int> (ff);
+ hmsf.f = static_cast<int> (ff);
+ return hmsf;
}
template <typename T>
std::string timecode (T r) const {
- int h;
- int m;
- int s;
- int f;
- split (r, h, m, s, f);
+ auto hmsf = split (r);
char buffer[128];
- snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d:%02d", h, m, s, f);
+ snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d:%02d", hmsf.h, hmsf.m, hmsf.s, hmsf.f);
return buffer;
}
-
static Time<S, O> from_seconds (double s) {
return Time<S, O> (llrint (s * HZ));
}
return Time<S, O> (INT64_MAX);
}
+ static const int HZ = 96000;
+
private:
- friend struct dcpomatic_time_ceil_test;
- friend struct dcpomatic_time_floor_test;
+ friend struct ::dcpomatic_time_ceil_test;
+ friend struct ::dcpomatic_time_floor_test;
Type _t;
- static const int HZ = 96000;
};
+
class ContentTimeDifferentiator {};
class DCPTimeDifferentiator {};
+
/* Specializations for the two allowed explicit conversions */
template<>
template<>
Time<DCPTimeDifferentiator, ContentTimeDifferentiator>::Time (Time<ContentTimeDifferentiator, DCPTimeDifferentiator> d, FrameRateChange f);
+
/** Time relative to the start or position of a piece of content in its native frame rate */
typedef Time<ContentTimeDifferentiator, DCPTimeDifferentiator> ContentTime;
/** Time relative to the start of the output DCP in its frame rate */
return TimePeriod<T> (from + o, to + o);
}
- boost::optional<TimePeriod<T> > overlap (TimePeriod<T> const & other) const {
+ boost::optional<TimePeriod<T>> overlap (TimePeriod<T> const & other) const {
T const max_from = std::max (from, other.from);
T const min_to = std::min (to, other.to);
if (max_from >= min_to) {
- return boost::optional<TimePeriod<T> > ();
+ return {};
}
return TimePeriod<T> (max_from, min_to);
}
};
+
/** @param A Period which is subtracted from.
* @param B Periods to subtract from `A', must be in ascending order of start time and must not overlap.
*/
template <class T>
-std::list<TimePeriod<T> > subtract (TimePeriod<T> A, std::list<TimePeriod<T> > const & B)
+std::list<TimePeriod<T>> subtract (TimePeriod<T> A, std::list<TimePeriod<T>> const & B)
{
- std::list<TimePeriod<T> > result;
+ std::list<TimePeriod<T>> result;
result.push_back (A);
- BOOST_FOREACH (TimePeriod<T> i, B) {
- std::list<TimePeriod<T> > new_result;
- BOOST_FOREACH (TimePeriod<T> j, result) {
- boost::optional<TimePeriod<T> > ov = i.overlap (j);
+ for (auto i: B) {
+ std::list<TimePeriod<T>> new_result;
+ for (auto j: result) {
+ auto ov = i.overlap (j);
if (ov) {
if (*ov == i) {
/* A contains all of B */
if (i.from != j.from) {
- new_result.push_back (TimePeriod<T> (j.from, i.from));
+ new_result.push_back (TimePeriod<T>(j.from, i.from));
}
if (i.to != j.to) {
- new_result.push_back (TimePeriod<T> (i.to, j.to));
+ new_result.push_back (TimePeriod<T>(i.to, j.to));
}
} else if (*ov == j) {
/* B contains all of A */
} else if (i.from < j.from) {
/* B overlaps start of A */
- new_result.push_back (TimePeriod<T> (i.to, j.to));
+ new_result.push_back (TimePeriod<T>(i.to, j.to));
} else if (i.to > j.to) {
/* B overlaps end of A */
- new_result.push_back (TimePeriod<T> (j.from, i.from));
+ new_result.push_back (TimePeriod<T>(j.from, i.from));
}
} else {
new_result.push_back (j);
return result;
}
+
typedef TimePeriod<ContentTime> ContentTimePeriod;
typedef TimePeriod<DCPTime> DCPTimePeriod;
+
DCPTime min (DCPTime a, DCPTime b);
DCPTime max (DCPTime a, DCPTime b);
ContentTime min (ContentTime a, ContentTime b);
std::string to_string (DCPTime t);
std::string to_string (DCPTimePeriod p);
+
+}
+
+
#endif