annotate base/RealTimeSV.cpp @ 1520:954d0cf29ca7 import-audio-data

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