Add OpenSSL licence exception.
[libdcp.git] / src / dcp_time.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp 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     libdcp 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 libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 /** @file  src/dcp_time.cc
35  *  @brief Time class.
36  */
37
38 #include "raw_convert.h"
39 #include "dcp_time.h"
40 #include "exceptions.h"
41 #include <boost/algorithm/string.hpp>
42 #include <iostream>
43 #include <vector>
44 #include <cmath>
45
46 using namespace std;
47 using namespace boost;
48 using namespace dcp;
49
50 Time::Time (int frame, double frames_per_second, int tcr_)
51 {
52         set (double (frame) / frames_per_second, tcr_);
53 }
54
55 /** Construct a Time from a number of seconds and a timecode rate.
56  *
57  *  @param seconds A number of seconds.
58  *  @param tcr_ Timecode rate.
59  */
60 Time::Time (double seconds, int tcr_)
61 {
62         set (seconds, tcr_);
63 }
64
65 /** Construct a Time with specified timecode rate and using the supplied
66  *  number of seconds.
67  *
68  *  @param seconds A number of seconds.
69  *  @param tcr_ Timecode rate to use.
70  */
71 void
72 Time::set (double seconds, int tcr_)
73 {
74         s = floor (seconds);
75         tcr = tcr_;
76
77         e = int (round ((seconds - s) * tcr));
78
79         if (s >= 60) {
80                 m = s / 60;
81                 s -= m * 60;
82         } else {
83                 m = 0;
84         }
85
86         if (m >= 60) {
87                 h = m / 60;
88                 m -= h * 60;
89         } else {
90                 h = 0;
91         }
92 }
93
94 /** @param time String of the form HH:MM:SS:EE[E] */
95 Time::Time (string time, int tcr_)
96         : tcr (tcr_)
97 {
98         vector<string> b;
99         split (b, time, is_any_of (":"));
100         if (b.size() != 4) {
101                 boost::throw_exception (DCPReadError ("unrecognised time specification"));
102         }
103
104         h = raw_convert<int> (b[0]);
105         m = raw_convert<int> (b[1]);
106         s = raw_convert<int> (b[2]);
107         e = raw_convert<int> (b[3]);
108 }
109
110 bool
111 dcp::operator== (Time const & a, Time const & b)
112 {
113         return (a.h == b.h && a.m == b.m && a.s == b.s && (a.e * b.tcr) == (b.e * a.tcr));
114 }
115
116 bool
117 dcp::operator!= (Time const & a, Time const & b)
118 {
119         return !(a == b);
120 }
121
122 bool
123 dcp::operator<= (Time const & a, Time const & b)
124 {
125         return a < b || a == b;
126 }
127
128 bool
129 dcp::operator>= (Time const & a, Time const & b)
130 {
131         return a > b || a == b;
132 }
133
134 bool
135 dcp::operator< (Time const & a, Time const & b)
136 {
137         if (a.h != b.h) {
138                 return a.h < b.h;
139         }
140
141         if (a.m != b.m) {
142                 return a.m < b.m;
143         }
144
145         if (a.s != b.s) {
146                 return a.s < b.s;
147         }
148
149         if ((a.e * b.tcr) != (b.e * a.tcr)) {
150                 return (a.e * b.tcr) < (b.e * a.tcr);
151         }
152
153         return true;
154 }
155
156 bool
157 dcp::operator> (Time const & a, Time const & b)
158 {
159         if (a.h != b.h) {
160                 return a.h > b.h;
161         }
162
163         if (a.m != b.m) {
164                 return a.m > b.m;
165         }
166
167         if (a.s != b.s) {
168                 return a.s > b.s;
169         }
170
171         if ((a.e * b.tcr) != (b.e * a.tcr)) {
172                 return (a.e * b.tcr) > (b.e * a.tcr);
173         }
174
175         return true;
176 }
177
178 ostream &
179 dcp::operator<< (ostream& s, Time const & t)
180 {
181         s << t.h << ":" << t.m << ":" << t.s << "." << t.e;
182         return s;
183 }
184
185 dcp::Time
186 dcp::operator+ (Time a, Time b)
187 {
188         Time r;
189
190         /* Make sure we have a common tcr */
191         if (a.tcr != b.tcr) {
192                 a.e *= b.tcr;
193                 b.e *= a.tcr;
194                 r.tcr = a.tcr * b.tcr;
195         } else {
196                 r.tcr = a.tcr;
197         }
198
199         r.e = a.e + b.e;
200         if (r.e >= r.tcr) {
201                 r.e -= r.tcr;
202                 r.s++;
203         }
204
205         r.s += a.s + b.s;
206         if (r.s >= 60) {
207                 r.s -= 60;
208                 r.m++;
209         }
210
211         r.m += a.m + b.m;
212         if (r.m >= 60) {
213                 r.m -= 60;
214                 r.h++;
215         }
216
217         r.h += a.h + b.h;
218
219         return r;
220 }
221
222 dcp::Time
223 dcp::operator- (Time a, Time b)
224 {
225         Time r;
226
227         /* Make sure we have a common tcr */
228         if (a.tcr != b.tcr) {
229                 a.e *= b.tcr;
230                 b.e *= a.tcr;
231                 r.tcr = a.tcr * b.tcr;
232         } else {
233                 r.tcr = a.tcr;
234         }
235
236         r.e = a.e - b.e;
237         if (r.e < 0) {
238                 r.e += r.tcr;
239                 r.s--;
240         }
241
242         r.s += (a.s - b.s);
243         if (r.s < 0) {
244                 r.s += 60;
245                 r.m--;
246         }
247
248         r.m += (a.m - b.m);
249         if (r.m < 0) {
250                 r.m += 60;
251                 r.h--;
252         }
253
254         r.h += (a.h - b.h);
255
256         return r;
257 }
258
259 float
260 dcp::operator/ (Time a, Time const & b)
261 {
262         int64_t const at = a.h * 3600 + a.m * 60 + a.s * float (a.e) / a.tcr;
263         int64_t const bt = b.h * 3600 + b.m * 60 + b.s * float (b.e) / b.tcr;
264         return float (at) / bt;
265 }
266
267 /** @return A string of the form h:m:s:e padded as in 00:00:00:000 (for Interop) or 00:00:00:00 (for SMPTE) */
268 string
269 Time::as_string (Standard standard) const
270 {
271         stringstream str;
272         str << setw(2) << setfill('0') << h << ":"
273             << setw(2) << setfill('0') << m << ":"
274             << setw(2) << setfill('0') << s << ":";
275
276         if (standard == SMPTE) {
277                 str << setw(2) << setfill('0') << e;
278         } else {
279                 str << setw(3) << setfill('0') << e;
280         }
281
282         return str.str ();
283 }
284
285 /** @param tcr_ Timecode rate with which the return value should be counted.
286  *  @return the total number of editable units that this time consists of at the specified timecode rate, rounded up
287  *  to the nearest editable unit. For example, as_editable_units (24) returns the total time in frames at 24fps.
288  */
289 int64_t
290 Time::as_editable_units (int tcr_) const
291 {
292         return ceil (int64_t(e) * double (tcr_) / tcr) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_;
293 }
294
295 /** @return the total number of seconds that this time consists of */
296 double
297 Time::as_seconds () const
298 {
299         return h * 3600 + m * 60 + s + double(e) / tcr;
300 }
301
302 /** @param tcr_ New timecode rate.
303  *  @return A new Time which is this time at the spcified new timecode rate.
304  */
305 Time
306 Time::rebase (int tcr_) const
307 {
308         return Time (h, m, s, floor (float (e) * tcr_ / tcr), tcr_);
309 }