annotate base/RealTimeSV.cpp @ 1290:fa574c909c3d 3.0-integration

Add MAD_BUFFER_GUARD padding at end of mp3 buffer, in order to ensure last frame is decoded successfully (otherwise the decoded audio is truncated). Another thing learned from madplay.
author Chris Cannam
date Thu, 24 Nov 2016 17:06:31 +0000
parents b7b84ae5f0a7
children a1af054d8f75
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@439 88 int i = 0;
Chris@439 89
Chris@439 90 const char *s = xsdd.c_str();
Chris@1038 91 int len = int(xsdd.length());
Chris@439 92
Chris@439 93 bool negative = false, afterT = false;
Chris@439 94
Chris@439 95 while (i < len) {
Chris@439 96
Chris@439 97 if (s[i] == '-') {
Chris@439 98 if (i == 0) negative = true;
Chris@439 99 ++i;
Chris@439 100 continue;
Chris@439 101 }
Chris@439 102
Chris@439 103 double value = 0.0;
Chris@439 104 char *eptr = 0;
Chris@439 105
Chris@439 106 if (isdigit(s[i]) || s[i] == '.') {
Chris@439 107 value = strtod(&s[i], &eptr);
Chris@1038 108 i = int(eptr - s);
Chris@439 109 }
Chris@439 110
Chris@439 111 if (i == len) break;
Chris@439 112
Chris@439 113 switch (s[i]) {
Chris@439 114 case 'Y': year = int(value + 0.1); break;
Chris@439 115 case 'D': day = int(value + 0.1); break;
Chris@439 116 case 'H': hour = int(value + 0.1); break;
Chris@439 117 case 'M':
Chris@439 118 if (afterT) minute = int(value + 0.1);
Chris@439 119 else month = int(value + 0.1);
Chris@439 120 break;
Chris@439 121 case 'S':
Chris@439 122 second = value;
Chris@439 123 break;
Chris@439 124 case 'T': afterT = true; break;
Chris@439 125 };
Chris@439 126
Chris@439 127 ++i;
Chris@439 128 }
Chris@439 129
Chris@439 130 if (year > 0) {
Chris@843 131 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 132 t = t + RealTime(year * 31556952, 0);
Chris@439 133 }
Chris@439 134
Chris@439 135 if (month > 0) {
Chris@843 136 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 137 t = t + RealTime(month * 2629746, 0);
Chris@439 138 }
Chris@439 139
Chris@439 140 if (day > 0) {
Chris@439 141 t = t + RealTime(day * 86400, 0);
Chris@439 142 }
Chris@439 143
Chris@439 144 if (hour > 0) {
Chris@439 145 t = t + RealTime(hour * 3600, 0);
Chris@439 146 }
Chris@439 147
Chris@439 148 if (minute > 0) {
Chris@439 149 t = t + RealTime(minute * 60, 0);
Chris@439 150 }
Chris@439 151
Chris@439 152 t = t + fromSeconds(second);
Chris@439 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@0 172 out << "-";
Chris@0 173 } else {
Chris@0 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@0 185 out << "0";
Chris@0 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@0 317 out << ".";
Chris@0 318 out << (ms / 100);
Chris@0 319 ms = ms % 100;
Chris@0 320 if (ms != 0) {
Chris@0 321 out << (ms / 10);
Chris@0 322 ms = ms % 10;
Chris@0 323 } else if (fixedDp) {
Chris@0 324 out << "0";
Chris@0 325 }
Chris@0 326 if (ms != 0) {
Chris@0 327 out << ms;
Chris@0 328 } else if (fixedDp) {
Chris@0 329 out << "0";
Chris@0 330 }
Chris@0 331 } else if (fixedDp) {
Chris@0 332 out << ".000";
Chris@0 333 }
Chris@0 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@612 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