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