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