Mercurial > hg > svcore
comparison base/RealTimeSV.cpp @ 1365:3382d914e110
Merge from branch 3.0-integration
author | Chris Cannam |
---|---|
date | Fri, 13 Jan 2017 10:29:44 +0000 |
parents | a1af054d8f75 |
children | 622d193a00dc |
comparison
equal
deleted
inserted
replaced
1272:6a7ea3bd0e10 | 1365:3382d914e110 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 | |
8 This program is free software; you can redistribute it and/or | |
9 modify it under the terms of the GNU General Public License as | |
10 published by the Free Software Foundation; either version 2 of the | |
11 License, or (at your option) any later version. See the file | |
12 COPYING included with this distribution for more information. | |
13 */ | |
14 | |
15 /* | |
16 This is a modified version of a source file from the | |
17 Rosegarden MIDI and audio sequencer and notation editor. | |
18 This file copyright 2000-2006 Chris Cannam. | |
19 */ | |
20 | |
21 #include <iostream> | |
22 | |
23 #include <cstdlib> | |
24 #include <sstream> | |
25 | |
26 #include "RealTime.h" | |
27 | |
28 #include "Debug.h" | |
29 | |
30 #include "Preferences.h" | |
31 | |
32 // A RealTime consists of two ints that must be at least 32 bits each. | |
33 // A signed 32-bit int can store values exceeding +/- 2 billion. This | |
34 // means we can safely use our lower int for nanoseconds, as there are | |
35 // 1 billion nanoseconds in a second and we need to handle double that | |
36 // because of the implementations of addition etc that we use. | |
37 // | |
38 // The maximum valid RealTime on a 32-bit system is somewhere around | |
39 // 68 years: 999999999 nanoseconds longer than the classic Unix epoch. | |
40 | |
41 #define ONE_BILLION 1000000000 | |
42 | |
43 RealTime::RealTime(int s, int n) : | |
44 sec(s), nsec(n) | |
45 { | |
46 if (sec == 0) { | |
47 while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } | |
48 while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } | |
49 } else if (sec < 0) { | |
50 while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } | |
51 while (nsec > 0 && sec < 0) { nsec -= ONE_BILLION; ++sec; } | |
52 } else { | |
53 while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } | |
54 while (nsec < 0 && sec > 0) { nsec += ONE_BILLION; --sec; } | |
55 } | |
56 } | |
57 | |
58 RealTime | |
59 RealTime::fromSeconds(double sec) | |
60 { | |
61 if (sec >= 0) { | |
62 return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5)); | |
63 } else { | |
64 return -fromSeconds(-sec); | |
65 } | |
66 } | |
67 | |
68 RealTime | |
69 RealTime::fromMilliseconds(int msec) | |
70 { | |
71 return RealTime(msec / 1000, (msec % 1000) * 1000000); | |
72 } | |
73 | |
74 RealTime | |
75 RealTime::fromTimeval(const struct timeval &tv) | |
76 { | |
77 return RealTime(int(tv.tv_sec), int(tv.tv_usec * 1000)); | |
78 } | |
79 | |
80 RealTime | |
81 RealTime::fromXsdDuration(std::string xsdd) | |
82 { | |
83 RealTime t; | |
84 | |
85 int year = 0, month = 0, day = 0, hour = 0, minute = 0; | |
86 double second = 0.0; | |
87 | |
88 char *loc = setlocale(LC_NUMERIC, 0); | |
89 (void)setlocale(LC_NUMERIC, "C"); // avoid strtod expecting ,-separator in DE | |
90 | |
91 int i = 0; | |
92 | |
93 const char *s = xsdd.c_str(); | |
94 int len = int(xsdd.length()); | |
95 | |
96 bool negative = false, afterT = false; | |
97 | |
98 while (i < len) { | |
99 | |
100 if (s[i] == '-') { | |
101 if (i == 0) negative = true; | |
102 ++i; | |
103 continue; | |
104 } | |
105 | |
106 double value = 0.0; | |
107 char *eptr = 0; | |
108 | |
109 if (isdigit(s[i]) || s[i] == '.') { | |
110 value = strtod(&s[i], &eptr); | |
111 i = int(eptr - s); | |
112 } | |
113 | |
114 if (i == len) break; | |
115 | |
116 switch (s[i]) { | |
117 case 'Y': year = int(value + 0.1); break; | |
118 case 'D': day = int(value + 0.1); break; | |
119 case 'H': hour = int(value + 0.1); break; | |
120 case 'M': | |
121 if (afterT) minute = int(value + 0.1); | |
122 else month = int(value + 0.1); | |
123 break; | |
124 case 'S': | |
125 second = value; | |
126 break; | |
127 case 'T': afterT = true; break; | |
128 }; | |
129 | |
130 ++i; | |
131 } | |
132 | |
133 if (year > 0) { | |
134 cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero year.\nWith no origin and a limited data size, I will treat a year as exactly 31556952\nseconds and you should expect overflow and/or poor results." << endl; | |
135 t = t + RealTime(year * 31556952, 0); | |
136 } | |
137 | |
138 if (month > 0) { | |
139 cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero month.\nWith no origin and a limited data size, I will treat a month as exactly 2629746\nseconds and you should expect overflow and/or poor results." << endl; | |
140 t = t + RealTime(month * 2629746, 0); | |
141 } | |
142 | |
143 if (day > 0) { | |
144 t = t + RealTime(day * 86400, 0); | |
145 } | |
146 | |
147 if (hour > 0) { | |
148 t = t + RealTime(hour * 3600, 0); | |
149 } | |
150 | |
151 if (minute > 0) { | |
152 t = t + RealTime(minute * 60, 0); | |
153 } | |
154 | |
155 t = t + fromSeconds(second); | |
156 | |
157 setlocale(LC_NUMERIC, loc); | |
158 | |
159 if (negative) { | |
160 return -t; | |
161 } else { | |
162 return t; | |
163 } | |
164 } | |
165 | |
166 double | |
167 RealTime::toDouble() const | |
168 { | |
169 double d = sec; | |
170 d += double(nsec) / double(ONE_BILLION); | |
171 return d; | |
172 } | |
173 | |
174 std::ostream &operator<<(std::ostream &out, const RealTime &rt) | |
175 { | |
176 if (rt < RealTime::zeroTime) { | |
177 out << "-"; | |
178 } else { | |
179 out << " "; | |
180 } | |
181 | |
182 int s = (rt.sec < 0 ? -rt.sec : rt.sec); | |
183 int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec); | |
184 | |
185 out << s << "."; | |
186 | |
187 int nn(n); | |
188 if (nn == 0) out << "00000000"; | |
189 else while (nn < (ONE_BILLION / 10)) { | |
190 out << "0"; | |
191 nn *= 10; | |
192 } | |
193 | |
194 out << n << "R"; | |
195 return out; | |
196 } | |
197 | |
198 std::string | |
199 RealTime::toString(bool align) const | |
200 { | |
201 std::stringstream out; | |
202 out << *this; | |
203 | |
204 std::string s = out.str(); | |
205 | |
206 if (!align && *this >= RealTime::zeroTime) { | |
207 // remove leading " " | |
208 s = s.substr(1, s.length() - 1); | |
209 } | |
210 | |
211 // remove trailing R | |
212 return s.substr(0, s.length() - 1); | |
213 } | |
214 | |
215 RealTime | |
216 RealTime::fromString(std::string s) | |
217 { | |
218 bool negative = false; | |
219 int section = 0; | |
220 std::string ssec, snsec; | |
221 | |
222 for (size_t i = 0; i < s.length(); ++i) { | |
223 | |
224 char c = s[i]; | |
225 if (isspace(c)) continue; | |
226 | |
227 if (section == 0) { | |
228 | |
229 if (c == '-') negative = true; | |
230 else if (isdigit(c)) { section = 1; ssec += c; } | |
231 else if (c == '.') section = 2; | |
232 else break; | |
233 | |
234 } else if (section == 1) { | |
235 | |
236 if (c == '.') section = 2; | |
237 else if (isdigit(c)) ssec += c; | |
238 else break; | |
239 | |
240 } else if (section == 2) { | |
241 | |
242 if (isdigit(c)) snsec += c; | |
243 else break; | |
244 } | |
245 } | |
246 | |
247 while (snsec.length() < 8) snsec += '0'; | |
248 | |
249 int sec = atoi(ssec.c_str()); | |
250 int nsec = atoi(snsec.c_str()); | |
251 if (negative) sec = -sec; | |
252 | |
253 // SVDEBUG << "RealTime::fromString: string " << s << " -> " | |
254 // << sec << " sec, " << nsec << " nsec" << endl; | |
255 | |
256 return RealTime(sec, nsec); | |
257 } | |
258 | |
259 std::string | |
260 RealTime::toText(bool fixedDp) const | |
261 { | |
262 if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp); | |
263 | |
264 Preferences *p = Preferences::getInstance(); | |
265 bool hms = true; | |
266 | |
267 if (p) { | |
268 hms = p->getShowHMS(); | |
269 int fps = 0; | |
270 switch (p->getTimeToTextMode()) { | |
271 case Preferences::TimeToTextMs: break; | |
272 case Preferences::TimeToTextUs: fps = 1000000; break; | |
273 case Preferences::TimeToText24Frame: fps = 24; break; | |
274 case Preferences::TimeToText25Frame: fps = 25; break; | |
275 case Preferences::TimeToText30Frame: fps = 30; break; | |
276 case Preferences::TimeToText50Frame: fps = 50; break; | |
277 case Preferences::TimeToText60Frame: fps = 60; break; | |
278 } | |
279 if (fps != 0) return toFrameText(fps, hms); | |
280 } | |
281 | |
282 return toMSText(fixedDp, hms); | |
283 } | |
284 | |
285 static void | |
286 writeSecPart(std::stringstream &out, bool hms, int sec) | |
287 { | |
288 if (hms) { | |
289 if (sec >= 3600) { | |
290 out << (sec / 3600) << ":"; | |
291 } | |
292 | |
293 if (sec >= 60) { | |
294 int minutes = (sec % 3600) / 60; | |
295 if (sec >= 3600 && minutes < 10) out << "0"; | |
296 out << minutes << ":"; | |
297 } | |
298 | |
299 if (sec >= 10) { | |
300 out << ((sec % 60) / 10); | |
301 } | |
302 | |
303 out << (sec % 10); | |
304 | |
305 } else { | |
306 out << sec; | |
307 } | |
308 } | |
309 | |
310 std::string | |
311 RealTime::toMSText(bool fixedDp, bool hms) const | |
312 { | |
313 if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms); | |
314 | |
315 std::stringstream out; | |
316 | |
317 writeSecPart(out, hms, sec); | |
318 | |
319 int ms = msec(); | |
320 | |
321 if (ms != 0) { | |
322 out << "."; | |
323 out << (ms / 100); | |
324 ms = ms % 100; | |
325 if (ms != 0) { | |
326 out << (ms / 10); | |
327 ms = ms % 10; | |
328 } else if (fixedDp) { | |
329 out << "0"; | |
330 } | |
331 if (ms != 0) { | |
332 out << ms; | |
333 } else if (fixedDp) { | |
334 out << "0"; | |
335 } | |
336 } else if (fixedDp) { | |
337 out << ".000"; | |
338 } | |
339 | |
340 std::string s = out.str(); | |
341 | |
342 return s; | |
343 } | |
344 | |
345 std::string | |
346 RealTime::toFrameText(int fps, bool hms) const | |
347 { | |
348 if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms); | |
349 | |
350 std::stringstream out; | |
351 | |
352 writeSecPart(out, hms, sec); | |
353 | |
354 // avoid rounding error if fps does not divide into ONE_BILLION | |
355 int64_t fbig = nsec; | |
356 fbig *= fps; | |
357 int f = int(fbig / ONE_BILLION); | |
358 | |
359 int div = 1; | |
360 int n = fps - 1; | |
361 while ((n = n / 10)) { | |
362 div *= 10; | |
363 } | |
364 | |
365 out << ":"; | |
366 | |
367 // cerr << "div = " << div << ", f = "<< f << endl; | |
368 | |
369 while (div) { | |
370 int d = (f / div) % 10; | |
371 out << d; | |
372 div /= 10; | |
373 } | |
374 | |
375 std::string s = out.str(); | |
376 | |
377 // cerr << "converted " << toString() << " to " << s << endl; | |
378 | |
379 return s; | |
380 } | |
381 | |
382 std::string | |
383 RealTime::toSecText() const | |
384 { | |
385 if (*this < RealTime::zeroTime) return "-" + (-*this).toSecText(); | |
386 | |
387 std::stringstream out; | |
388 | |
389 writeSecPart(out, true, sec); | |
390 | |
391 if (sec < 60) { | |
392 out << "s"; | |
393 } | |
394 | |
395 std::string s = out.str(); | |
396 | |
397 return s; | |
398 } | |
399 | |
400 std::string | |
401 RealTime::toXsdDuration() const | |
402 { | |
403 std::string s = "PT" + toString(false) + "S"; | |
404 return s; | |
405 } | |
406 | |
407 RealTime | |
408 RealTime::operator*(int m) const | |
409 { | |
410 double t = (double(nsec) / ONE_BILLION) * m; | |
411 t += sec * m; | |
412 return fromSeconds(t); | |
413 } | |
414 | |
415 RealTime | |
416 RealTime::operator/(int d) const | |
417 { | |
418 int secdiv = sec / d; | |
419 int secrem = sec % d; | |
420 | |
421 double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d; | |
422 | |
423 return RealTime(secdiv, int(nsecdiv + 0.5)); | |
424 } | |
425 | |
426 RealTime | |
427 RealTime::operator*(double m) const | |
428 { | |
429 double t = (double(nsec) / ONE_BILLION) * m; | |
430 t += sec * m; | |
431 return fromSeconds(t); | |
432 } | |
433 | |
434 RealTime | |
435 RealTime::operator/(double d) const | |
436 { | |
437 double t = (double(nsec) / ONE_BILLION) / d; | |
438 t += sec / d; | |
439 return fromSeconds(t); | |
440 } | |
441 | |
442 double | |
443 RealTime::operator/(const RealTime &r) const | |
444 { | |
445 double lTotal = double(sec) * ONE_BILLION + double(nsec); | |
446 double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec); | |
447 | |
448 if (rTotal == 0) return 0.0; | |
449 else return lTotal/rTotal; | |
450 } | |
451 | |
452 static RealTime | |
453 frame2RealTime_i(sv_frame_t frame, sv_frame_t iSampleRate) | |
454 { | |
455 if (frame < 0) return -frame2RealTime_i(-frame, iSampleRate); | |
456 | |
457 int sec = int(frame / iSampleRate); | |
458 frame -= sec * iSampleRate; | |
459 int nsec = int((double(frame) / double(iSampleRate)) * ONE_BILLION + 0.5); | |
460 // Use ctor here instead of setting data members directly to | |
461 // ensure nsec > ONE_BILLION is handled properly. It's extremely | |
462 // unlikely, but not impossible. | |
463 return RealTime(sec, nsec); | |
464 } | |
465 | |
466 sv_frame_t | |
467 RealTime::realTime2Frame(const RealTime &time, sv_samplerate_t sampleRate) | |
468 { | |
469 if (time < zeroTime) return -realTime2Frame(-time, sampleRate); | |
470 double s = time.sec + double(time.nsec) / 1000000000.0; | |
471 return sv_frame_t(s * sampleRate + 0.5); | |
472 } | |
473 | |
474 RealTime | |
475 RealTime::frame2RealTime(sv_frame_t frame, sv_samplerate_t sampleRate) | |
476 { | |
477 if (sampleRate == double(int(sampleRate))) { | |
478 return frame2RealTime_i(frame, int(sampleRate)); | |
479 } | |
480 | |
481 double sec = double(frame) / sampleRate; | |
482 return fromSeconds(sec); | |
483 } | |
484 | |
485 const RealTime RealTime::zeroTime(0,0); | |
486 |