Initial work on subtitle writing.
[libdcp.git] / src / dcp_time.cc
1 /*
2     Copyright (C) 2012 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/dcp_time.cc
21  *  @brief A representation of time within a DCP.
22  */
23
24 #include <iostream>
25 #include <vector>
26 #include <boost/algorithm/string.hpp>
27 #include <boost/lexical_cast.hpp>
28 #include <cmath>
29 #include "dcp_time.h"
30 #include "exceptions.h"
31
32 using namespace std;
33 using namespace boost;
34 using namespace libdcp;
35
36 Time::Time (int frame, int frames_per_second)
37         : h (0)
38         , m (0)
39         , s (0)
40         , t (0)
41 {
42         float sec_float = float (frame) / frames_per_second;
43         t = (int (floor (sec_float * 1000)) % 1000) / 4;
44         s = floor (sec_float);
45
46         if (s > 60) {
47                 m = s / 60;
48                 s -= m * 60;
49         }
50
51         if (m > 60) {
52                 h = m / 60;
53                 m -= h * 60;
54         }
55 }
56
57 Time::Time (string time)
58 {
59         vector<string> b;
60         split (b, time, is_any_of (":"));
61         if (b.size() != 4) {
62                 throw DCPReadError ("unrecognised time specification");
63         }
64         
65         h = lexical_cast<int> (b[0]);
66         m = lexical_cast<int> (b[1]);
67         s = lexical_cast<int> (b[2]);
68         t = lexical_cast<int> (b[3]);
69 }
70
71 bool
72 libdcp::operator== (Time const & a, Time const & b)
73 {
74         return (a.h == b.h && a.m == b.m && a.s == b.s && a.t == b.t);
75 }
76
77 bool
78 libdcp::operator!= (Time const & a, Time const & b)
79 {
80         return !(a == b);
81 }
82
83 bool
84 libdcp::operator<= (Time const & a, Time const & b)
85 {
86         if (a.h != b.h) {
87                 return a.h <= b.h;
88         }
89
90         if (a.m != b.m) {
91                 return a.m <= b.m;
92         }
93
94         if (a.s != b.s) {
95                 return a.s <= b.s;
96         }
97
98         if (a.t != b.t) {
99                 return a.t <= b.t;
100         }
101
102         return true;
103 }
104
105 bool
106 libdcp::operator< (Time const & a, Time const & b)
107 {
108         if (a.h != b.h) {
109                 return a.h < b.h;
110         }
111
112         if (a.m != b.m) {
113                 return a.m < b.m;
114         }
115
116         if (a.s != b.s) {
117                 return a.s < b.s;
118         }
119
120         if (a.t != b.t) {
121                 return a.t < b.t;
122         }
123
124         return true;
125 }
126
127 bool
128 libdcp::operator> (Time const & a, Time const & b)
129 {
130         if (a.h != b.h) {
131                 return a.h > b.h;
132         }
133
134         if (a.m != b.m) {
135                 return a.m > b.m;
136         }
137
138         if (a.s != b.s) {
139                 return a.s > b.s;
140         }
141
142         if (a.t != b.t) {
143                 return a.t > b.t;
144         }
145
146         return true;
147 }
148
149 ostream &
150 libdcp::operator<< (ostream& s, Time const & t)
151 {
152         s << t.h << ":" << t.m << ":" << t.s << "." << t.t;
153         return s;
154 }
155
156 libdcp::Time
157 libdcp::operator+ (Time a, Time const & b)
158 {
159         Time r;
160
161         r.t = a.t + b.t;
162         if (r.t >= 250) {
163                 r.t -= 250;
164                 r.s++;
165         }
166
167         r.s += a.s + b.s;
168         if (r.s >= 60) {
169                 r.s -= 60;
170                 r.m++;
171         }
172
173         r.m += a.m + b.m;
174         if (r.m >= 60) {
175                 r.m -= 60;
176                 r.h++;
177         }
178
179         r.h += a.h + b.h;
180
181         return r;
182 }
183
184 libdcp::Time
185 libdcp::operator- (Time a, Time const & b)
186 {
187         Time r;
188
189         r.t = a.t - b.t;
190         if (r.t < 0) {
191                 r.t += 250;
192                 r.s--;
193         }
194
195         r.s += (a.s - b.s);
196         if (r.s < 0) {
197                 r.s += 60;
198                 r.m--;
199         }
200
201         r.m += (a.m - b.m);
202         if (r.m < 0) {
203                 r.m += 60;
204                 r.h--;
205         }
206
207         r.h += (a.h - b.h);
208
209         return r;
210 }
211
212 float
213 libdcp::operator/ (Time a, Time const & b)
214 {
215         int64_t const at = a.h * 3600 * 250 + a.m * 60 * 250 + a.s * 250 + a.t;
216         int64_t const bt = b.h * 3600 * 250 + b.m * 60 * 250 + b.s * 250 + b.t;
217         return float (at) / bt;
218 }
219
220 string
221 Time::to_string () const
222 {
223         stringstream str;
224         str << h << ":" << m << ":" << s << ":" << t;
225         return str.str ();
226 }
227
228 int64_t
229 Time::to_ticks () const
230 {
231         return t + s * 25 + m * 60 * 25 + h * 60 * 60 * 25;
232 }
233