annotate base/RealTimeSV.cpp @ 1412:b7a9edee85e0 scale-ticks

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