annotate base/RealTime.cpp @ 1188:d9698ee93659 spectrogram-minor-refactor

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