a94b605d2b94f61d7a1eebb68d0835d7856174ac
[dcpomatic.git] / src / lib / dcpomatic_time.h
1 /*
2     Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 /** @file  src/lib/dcpomatic_time.h
22  *  @brief Types to describe time.
23  */
24
25 #ifndef DCPOMATIC_TIME_H
26 #define DCPOMATIC_TIME_H
27
28 #include "frame_rate_change.h"
29 #include "dcpomatic_assert.h"
30 #include <locked_sstream.h>
31 #include <boost/optional.hpp>
32 #include <stdint.h>
33 #include <cmath>
34 #include <ostream>
35 #include <iomanip>
36
37 class dcpomatic_round_up_test;
38
39 /** A time in seconds, expressed as a number scaled up by Time::HZ.  We want two different
40  *  versions of this class, ContentTime and DCPTime, and we want it to be impossible to
41  *  convert implicitly between the two.  Hence there's this template hack.  I'm not
42  *  sure if it's the best way to do it.
43  *
44  *  S is the name of `this' class and O is its opposite (see the typedefs below).
45  */
46 template <class S, class O>
47 class Time
48 {
49 public:
50         Time ()
51                 : _t (0)
52         {}
53
54         typedef int64_t Type;
55
56         explicit Time (Type t)
57                 : _t (t)
58         {}
59
60         explicit Time (Type n, Type d)
61                 : _t (n * HZ / d)
62         {}
63
64         /* Explicit conversion from type O */
65         Time (Time<O, S> d, FrameRateChange f);
66
67         Type get () const {
68                 return _t;
69         }
70
71         bool operator< (Time<S, O> const & o) const {
72                 return _t < o._t;
73         }
74
75         bool operator<= (Time<S, O> const & o) const {
76                 return _t <= o._t;
77         }
78
79         bool operator== (Time<S, O> const & o) const {
80                 return _t == o._t;
81         }
82
83         bool operator!= (Time<S, O> const & o) const {
84                 return _t != o._t;
85         }
86
87         bool operator>= (Time<S, O> const & o) const {
88                 return _t >= o._t;
89         }
90
91         bool operator> (Time<S, O> const & o) const {
92                 return _t > o._t;
93         }
94
95         Time<S, O> operator+ (Time<S, O> const & o) const {
96                 return Time<S, O> (_t + o._t);
97         }
98
99         Time<S, O> & operator+= (Time<S, O> const & o) {
100                 _t += o._t;
101                 return *this;
102         }
103
104         Time<S, O> operator- () const {
105                 return Time<S, O> (-_t);
106         }
107
108         Time<S, O> operator- (Time<S, O> const & o) const {
109                 return Time<S, O> (_t - o._t);
110         }
111
112         Time<S, O> & operator-= (Time<S, O> const & o) {
113                 _t -= o._t;
114                 return *this;
115         }
116
117         /** Round up to the nearest sampling interval
118          *  at some sampling rate.
119          *  @param r Sampling rate.
120          */
121         Time<S, O> round_up (float r) const {
122                 Type const n = llrintf (HZ / r);
123                 Type const a = _t + n - 1;
124                 return Time<S, O> (a - (a % n));
125         }
126
127         double seconds () const {
128                 return double (_t) / HZ;
129         }
130
131         Time<S, O> abs () const {
132                 return Time<S, O> (std::abs (_t));
133         }
134
135         template <typename T>
136         int64_t frames_round (T r) const {
137                 /* We must cast to double here otherwise if T is integer
138                    the calculation will round down before we get the chance
139                    to llrint().
140                 */
141                 return llrint (_t * double(r) / HZ);
142         }
143
144         template <typename T>
145         int64_t frames_floor (T r) const {
146                 return floor (_t * r / HZ);
147         }
148
149         template <typename T>
150         int64_t frames_ceil (T r) const {
151                 /* We must cast to double here otherwise if T is integer
152                    the calculation will round down before we get the chance
153                    to ceil().
154                 */
155                 return ceil (_t * double(r) / HZ);
156         }
157
158         /** @param r Frames per second */
159         template <typename T>
160         void split (T r, int& h, int& m, int& s, int& f) const
161         {
162                 /* Do this calculation with frames so that we can round
163                    to a frame boundary at the start rather than the end.
164                 */
165                 int64_t ff = frames_round (r);
166
167                 h = ff / (3600 * r);
168                 ff -= h * 3600 * r;
169                 m = ff / (60 * r);
170                 ff -= m * 60 * r;
171                 s = ff / r;
172                 ff -= s * r;
173
174                 f = static_cast<int> (ff);
175         }
176
177         template <typename T>
178         std::string timecode (T r) const {
179                 int h;
180                 int m;
181                 int s;
182                 int f;
183                 split (r, h, m, s, f);
184
185                 locked_stringstream o;
186                 o.width (2);
187                 o.fill ('0');
188                 o << std::setw(2) << std::setfill('0') << h << ":"
189                   << std::setw(2) << std::setfill('0') << m << ":"
190                   << std::setw(2) << std::setfill('0') << s << ":"
191                   << std::setw(2) << std::setfill('0') << f;
192                 return o.str ();
193         }
194
195
196         static Time<S, O> from_seconds (double s) {
197                 return Time<S, O> (llrint (s * HZ));
198         }
199
200         template <class T>
201         static Time<S, O> from_frames (int64_t f, T r) {
202                 DCPOMATIC_ASSERT (r > 0);
203                 return Time<S, O> (f * HZ / r);
204         }
205
206         static Time<S, O> delta () {
207                 return Time<S, O> (1);
208         }
209
210         static Time<S, O> min () {
211                 return Time<S, O> (-INT64_MAX);
212         }
213
214         static Time<S, O> max () {
215                 return Time<S, O> (INT64_MAX);
216         }
217
218 private:
219         friend struct dcptime_round_up_test;
220
221         Type _t;
222         static const int HZ = 96000;
223 };
224
225 class ContentTimeDifferentiator {};
226 class DCPTimeDifferentiator {};
227
228 /* Specializations for the two allowed explicit conversions */
229
230 template<>
231 Time<ContentTimeDifferentiator, DCPTimeDifferentiator>::Time (Time<DCPTimeDifferentiator, ContentTimeDifferentiator> d, FrameRateChange f);
232
233 template<>
234 Time<DCPTimeDifferentiator, ContentTimeDifferentiator>::Time (Time<ContentTimeDifferentiator, DCPTimeDifferentiator> d, FrameRateChange f);
235
236 /** Time relative to the start or position of a piece of content in its native frame rate */
237 typedef Time<ContentTimeDifferentiator, DCPTimeDifferentiator> ContentTime;
238 /** Time relative to the start of the output DCP in its frame rate */
239 typedef Time<DCPTimeDifferentiator, ContentTimeDifferentiator> DCPTime;
240
241 template <class T>
242 class TimePeriod
243 {
244 public:
245         TimePeriod () {}
246
247         TimePeriod (T f, T t)
248                 : from (f)
249                 , to (t)
250         {}
251
252         /** start time of sampling interval that the period is from */
253         T from;
254         /** start time of next sampling interval after the period */
255         T to;
256
257         T duration () const {
258                 return to - from;
259         }
260
261         TimePeriod<T> operator+ (T const & o) const {
262                 return TimePeriod<T> (from + o, to + o);
263         }
264
265         boost::optional<TimePeriod<T> > overlap (TimePeriod<T> const & other) {
266                 T const max_from = std::max (from, other.from);
267                 T const min_to = std::min (to, other.to);
268
269                 if (max_from >= min_to) {
270                         return boost::optional<TimePeriod<T> > ();
271                 }
272
273                 return TimePeriod<T> (max_from, min_to);
274         }
275
276         bool contains (T const & other) const {
277                 return (from <= other && other < to);
278         }
279
280         bool operator== (TimePeriod<T> const & other) const {
281                 return from == other.from && to == other.to;
282         }
283 };
284
285 typedef TimePeriod<ContentTime> ContentTimePeriod;
286 typedef TimePeriod<DCPTime> DCPTimePeriod;
287
288 DCPTime min (DCPTime a, DCPTime b);
289 DCPTime max (DCPTime a, DCPTime b);
290 ContentTime min (ContentTime a, ContentTime b);
291 ContentTime max (ContentTime a, ContentTime b);
292 std::ostream& operator<< (std::ostream& s, ContentTime t);
293 std::ostream& operator<< (std::ostream& s, DCPTime t);
294 std::ostream& operator<< (std::ostream& s, DCPTimePeriod p);
295
296 #endif