annotate base/RealTimeSV.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

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