annotate base/RealTime.cpp @ 985:f073d924a7c3

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