Mercurial > hg > svcore
comparison base/RealTimeSV.cpp @ 1228:a2091d148d7f project-file-rework
Cut down vastly on the number of config.pri files and places where their contents has to be effectively duplicated without them
author | Chris Cannam |
---|---|
date | Mon, 24 Oct 2016 17:53:33 +0100 |
parents | base/RealTime.cpp@c811991a5efa |
children | c4f873749ab5 |
comparison
equal
deleted
inserted
replaced
1224:ab050519c4ba | 1228:a2091d148d7f |
---|---|
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) { nsec -= ONE_BILLION; ++sec; } | |
52 } else { | |
53 while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } | |
54 while (nsec < 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 int i = 0; | |
89 | |
90 const char *s = xsdd.c_str(); | |
91 int len = int(xsdd.length()); | |
92 | |
93 bool negative = false, afterT = false; | |
94 | |
95 while (i < len) { | |
96 | |
97 if (s[i] == '-') { | |
98 if (i == 0) negative = true; | |
99 ++i; | |
100 continue; | |
101 } | |
102 | |
103 double value = 0.0; | |
104 char *eptr = 0; | |
105 | |
106 if (isdigit(s[i]) || s[i] == '.') { | |
107 value = strtod(&s[i], &eptr); | |
108 i = int(eptr - s); | |
109 } | |
110 | |
111 if (i == len) break; | |
112 | |
113 switch (s[i]) { | |
114 case 'Y': year = int(value + 0.1); break; | |
115 case 'D': day = int(value + 0.1); break; | |
116 case 'H': hour = int(value + 0.1); break; | |
117 case 'M': | |
118 if (afterT) minute = int(value + 0.1); | |
119 else month = int(value + 0.1); | |
120 break; | |
121 case 'S': | |
122 second = value; | |
123 break; | |
124 case 'T': afterT = true; break; | |
125 }; | |
126 | |
127 ++i; | |
128 } | |
129 | |
130 if (year > 0) { | |
131 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; | |
132 t = t + RealTime(year * 31556952, 0); | |
133 } | |
134 | |
135 if (month > 0) { | |
136 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; | |
137 t = t + RealTime(month * 2629746, 0); | |
138 } | |
139 | |
140 if (day > 0) { | |
141 t = t + RealTime(day * 86400, 0); | |
142 } | |
143 | |
144 if (hour > 0) { | |
145 t = t + RealTime(hour * 3600, 0); | |
146 } | |
147 | |
148 if (minute > 0) { | |
149 t = t + RealTime(minute * 60, 0); | |
150 } | |
151 | |
152 t = t + fromSeconds(second); | |
153 | |
154 if (negative) { | |
155 return -t; | |
156 } else { | |
157 return t; | |
158 } | |
159 } | |
160 | |
161 double | |
162 RealTime::toDouble() const | |
163 { | |
164 double d = sec; | |
165 d += double(nsec) / double(ONE_BILLION); | |
166 return d; | |
167 } | |
168 | |
169 std::ostream &operator<<(std::ostream &out, const RealTime &rt) | |
170 { | |
171 if (rt < RealTime::zeroTime) { | |
172 out << "-"; | |
173 } else { | |
174 out << " "; | |
175 } | |
176 | |
177 int s = (rt.sec < 0 ? -rt.sec : rt.sec); | |
178 int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec); | |
179 | |
180 out << s << "."; | |
181 | |
182 int nn(n); | |
183 if (nn == 0) out << "00000000"; | |
184 else while (nn < (ONE_BILLION / 10)) { | |
185 out << "0"; | |
186 nn *= 10; | |
187 } | |
188 | |
189 out << n << "R"; | |
190 return out; | |
191 } | |
192 | |
193 std::string | |
194 RealTime::toString(bool align) const | |
195 { | |
196 std::stringstream out; | |
197 out << *this; | |
198 | |
199 std::string s = out.str(); | |
200 | |
201 if (!align && *this >= RealTime::zeroTime) { | |
202 // remove leading " " | |
203 s = s.substr(1, s.length() - 1); | |
204 } | |
205 | |
206 // remove trailing R | |
207 return s.substr(0, s.length() - 1); | |
208 } | |
209 | |
210 RealTime | |
211 RealTime::fromString(std::string s) | |
212 { | |
213 bool negative = false; | |
214 int section = 0; | |
215 std::string ssec, snsec; | |
216 | |
217 for (size_t i = 0; i < s.length(); ++i) { | |
218 | |
219 char c = s[i]; | |
220 if (isspace(c)) continue; | |
221 | |
222 if (section == 0) { | |
223 | |
224 if (c == '-') negative = true; | |
225 else if (isdigit(c)) { section = 1; ssec += c; } | |
226 else if (c == '.') section = 2; | |
227 else break; | |
228 | |
229 } else if (section == 1) { | |
230 | |
231 if (c == '.') section = 2; | |
232 else if (isdigit(c)) ssec += c; | |
233 else break; | |
234 | |
235 } else if (section == 2) { | |
236 | |
237 if (isdigit(c)) snsec += c; | |
238 else break; | |
239 } | |
240 } | |
241 | |
242 while (snsec.length() < 8) snsec += '0'; | |
243 | |
244 int sec = atoi(ssec.c_str()); | |
245 int nsec = atoi(snsec.c_str()); | |
246 if (negative) sec = -sec; | |
247 | |
248 // SVDEBUG << "RealTime::fromString: string " << s << " -> " | |
249 // << sec << " sec, " << nsec << " nsec" << endl; | |
250 | |
251 return RealTime(sec, nsec); | |
252 } | |
253 | |
254 std::string | |
255 RealTime::toText(bool fixedDp) const | |
256 { | |
257 if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp); | |
258 | |
259 Preferences *p = Preferences::getInstance(); | |
260 bool hms = true; | |
261 | |
262 if (p) { | |
263 hms = p->getShowHMS(); | |
264 int fps = 0; | |
265 switch (p->getTimeToTextMode()) { | |
266 case Preferences::TimeToTextMs: break; | |
267 case Preferences::TimeToTextUs: fps = 1000000; break; | |
268 case Preferences::TimeToText24Frame: fps = 24; break; | |
269 case Preferences::TimeToText25Frame: fps = 25; break; | |
270 case Preferences::TimeToText30Frame: fps = 30; break; | |
271 case Preferences::TimeToText50Frame: fps = 50; break; | |
272 case Preferences::TimeToText60Frame: fps = 60; break; | |
273 } | |
274 if (fps != 0) return toFrameText(fps, hms); | |
275 } | |
276 | |
277 return toMSText(fixedDp, hms); | |
278 } | |
279 | |
280 static void | |
281 writeSecPart(std::stringstream &out, bool hms, int sec) | |
282 { | |
283 if (hms) { | |
284 if (sec >= 3600) { | |
285 out << (sec / 3600) << ":"; | |
286 } | |
287 | |
288 if (sec >= 60) { | |
289 int minutes = (sec % 3600) / 60; | |
290 if (sec >= 3600 && minutes < 10) out << "0"; | |
291 out << minutes << ":"; | |
292 } | |
293 | |
294 if (sec >= 10) { | |
295 out << ((sec % 60) / 10); | |
296 } | |
297 | |
298 out << (sec % 10); | |
299 | |
300 } else { | |
301 out << sec; | |
302 } | |
303 } | |
304 | |
305 std::string | |
306 RealTime::toMSText(bool fixedDp, bool hms) const | |
307 { | |
308 if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms); | |
309 | |
310 std::stringstream out; | |
311 | |
312 writeSecPart(out, hms, sec); | |
313 | |
314 int ms = msec(); | |
315 | |
316 if (ms != 0) { | |
317 out << "."; | |
318 out << (ms / 100); | |
319 ms = ms % 100; | |
320 if (ms != 0) { | |
321 out << (ms / 10); | |
322 ms = ms % 10; | |
323 } else if (fixedDp) { | |
324 out << "0"; | |
325 } | |
326 if (ms != 0) { | |
327 out << ms; | |
328 } else if (fixedDp) { | |
329 out << "0"; | |
330 } | |
331 } else if (fixedDp) { | |
332 out << ".000"; | |
333 } | |
334 | |
335 std::string s = out.str(); | |
336 | |
337 return s; | |
338 } | |
339 | |
340 std::string | |
341 RealTime::toFrameText(int fps, bool hms) const | |
342 { | |
343 if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms); | |
344 | |
345 std::stringstream out; | |
346 | |
347 writeSecPart(out, hms, sec); | |
348 | |
349 // avoid rounding error if fps does not divide into ONE_BILLION | |
350 int64_t fbig = nsec; | |
351 fbig *= fps; | |
352 int f = int(fbig / ONE_BILLION); | |
353 | |
354 int div = 1; | |
355 int n = fps - 1; | |
356 while ((n = n / 10)) { | |
357 div *= 10; | |
358 } | |
359 | |
360 out << ":"; | |
361 | |
362 // cerr << "div = " << div << ", f = "<< f << endl; | |
363 | |
364 while (div) { | |
365 int d = (f / div) % 10; | |
366 out << d; | |
367 div /= 10; | |
368 } | |
369 | |
370 std::string s = out.str(); | |
371 | |
372 // cerr << "converted " << toString() << " to " << s << endl; | |
373 | |
374 return s; | |
375 } | |
376 | |
377 std::string | |
378 RealTime::toSecText() const | |
379 { | |
380 if (*this < RealTime::zeroTime) return "-" + (-*this).toSecText(); | |
381 | |
382 std::stringstream out; | |
383 | |
384 writeSecPart(out, true, sec); | |
385 | |
386 if (sec < 60) { | |
387 out << "s"; | |
388 } | |
389 | |
390 std::string s = out.str(); | |
391 | |
392 return s; | |
393 } | |
394 | |
395 std::string | |
396 RealTime::toXsdDuration() const | |
397 { | |
398 std::string s = "PT" + toString(false) + "S"; | |
399 return s; | |
400 } | |
401 | |
402 RealTime | |
403 RealTime::operator*(int m) const | |
404 { | |
405 double t = (double(nsec) / ONE_BILLION) * m; | |
406 t += sec * m; | |
407 return fromSeconds(t); | |
408 } | |
409 | |
410 RealTime | |
411 RealTime::operator/(int d) const | |
412 { | |
413 int secdiv = sec / d; | |
414 int secrem = sec % d; | |
415 | |
416 double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d; | |
417 | |
418 return RealTime(secdiv, int(nsecdiv + 0.5)); | |
419 } | |
420 | |
421 RealTime | |
422 RealTime::operator*(double m) const | |
423 { | |
424 double t = (double(nsec) / ONE_BILLION) * m; | |
425 t += sec * m; | |
426 return fromSeconds(t); | |
427 } | |
428 | |
429 RealTime | |
430 RealTime::operator/(double d) const | |
431 { | |
432 double t = (double(nsec) / ONE_BILLION) / d; | |
433 t += sec / d; | |
434 return fromSeconds(t); | |
435 } | |
436 | |
437 double | |
438 RealTime::operator/(const RealTime &r) const | |
439 { | |
440 double lTotal = double(sec) * ONE_BILLION + double(nsec); | |
441 double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec); | |
442 | |
443 if (rTotal == 0) return 0.0; | |
444 else return lTotal/rTotal; | |
445 } | |
446 | |
447 static RealTime | |
448 frame2RealTime_i(sv_frame_t frame, sv_frame_t iSampleRate) | |
449 { | |
450 if (frame < 0) return -frame2RealTime_i(-frame, iSampleRate); | |
451 | |
452 RealTime rt; | |
453 sv_frame_t sec = frame / iSampleRate; | |
454 rt.sec = int(sec); | |
455 frame -= sec * iSampleRate; | |
456 rt.nsec = (int)(((double(frame) * 1000000.0) / double(iSampleRate)) * 1000.0); | |
457 return rt; | |
458 } | |
459 | |
460 sv_frame_t | |
461 RealTime::realTime2Frame(const RealTime &time, sv_samplerate_t sampleRate) | |
462 { | |
463 if (time < zeroTime) return -realTime2Frame(-time, sampleRate); | |
464 double s = time.sec + double(time.nsec + 1) / 1000000000.0; | |
465 return sv_frame_t(s * sampleRate); | |
466 } | |
467 | |
468 RealTime | |
469 RealTime::frame2RealTime(sv_frame_t frame, sv_samplerate_t sampleRate) | |
470 { | |
471 if (sampleRate == double(int(sampleRate))) { | |
472 return frame2RealTime_i(frame, int(sampleRate)); | |
473 } | |
474 | |
475 double sec = double(frame) / sampleRate; | |
476 return fromSeconds(sec); | |
477 } | |
478 | |
479 const RealTime RealTime::zeroTime(0,0); | |
480 |