Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@52: Sonic Visualiser Chris@52: An audio file viewer and annotation editor. Chris@52: Centre for Digital Music, Queen Mary, University of London. Chris@0: Chris@52: This program is free software; you can redistribute it and/or Chris@52: modify it under the terms of the GNU General Public License as Chris@52: published by the Free Software Foundation; either version 2 of the Chris@52: License, or (at your option) any later version. See the file Chris@52: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: /* Chris@0: This is a modified version of a source file from the Chris@0: Rosegarden MIDI and audio sequencer and notation editor. Chris@17: This file copyright 2000-2006 Chris Cannam. Chris@0: */ Chris@0: Chris@0: #include Chris@0: Chris@405: #include Chris@0: #include Chris@0: Chris@150: #include "RealTime.h" Chris@0: Chris@843: #include "Debug.h" Chris@843: Chris@612: #include "Preferences.h" Chris@612: Chris@0: // A RealTime consists of two ints that must be at least 32 bits each. Chris@0: // A signed 32-bit int can store values exceeding +/- 2 billion. This Chris@0: // means we can safely use our lower int for nanoseconds, as there are Chris@0: // 1 billion nanoseconds in a second and we need to handle double that Chris@0: // because of the implementations of addition etc that we use. Chris@0: // Chris@0: // The maximum valid RealTime on a 32-bit system is somewhere around Chris@0: // 68 years: 999999999 nanoseconds longer than the classic Unix epoch. Chris@0: Chris@0: #define ONE_BILLION 1000000000 Chris@0: Chris@0: RealTime::RealTime(int s, int n) : Chris@0: sec(s), nsec(n) Chris@0: { Chris@0: if (sec == 0) { Chris@0: while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } Chris@0: while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } Chris@0: } else if (sec < 0) { Chris@0: while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } Chris@1268: while (nsec > 0 && sec < 0) { nsec -= ONE_BILLION; ++sec; } Chris@0: } else { Chris@0: while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } Chris@1268: while (nsec < 0 && sec > 0) { nsec += ONE_BILLION; --sec; } Chris@0: } Chris@0: } Chris@0: Chris@26: RealTime Chris@26: RealTime::fromSeconds(double sec) Chris@26: { Chris@1012: if (sec >= 0) { Chris@1012: return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5)); Chris@1012: } else { Chris@1012: return -fromSeconds(-sec); Chris@1012: } Chris@26: } Chris@26: Chris@26: RealTime Chris@26: RealTime::fromMilliseconds(int msec) Chris@26: { Chris@26: return RealTime(msec / 1000, (msec % 1000) * 1000000); Chris@26: } Chris@26: Chris@26: RealTime Chris@26: RealTime::fromTimeval(const struct timeval &tv) Chris@26: { Chris@1038: return RealTime(int(tv.tv_sec), int(tv.tv_usec * 1000)); Chris@26: } Chris@0: Chris@439: RealTime Chris@439: RealTime::fromXsdDuration(std::string xsdd) Chris@439: { Chris@439: RealTime t; Chris@439: Chris@439: int year = 0, month = 0, day = 0, hour = 0, minute = 0; Chris@439: double second = 0.0; Chris@439: Chris@1298: char *loc = setlocale(LC_NUMERIC, 0); Chris@1298: (void)setlocale(LC_NUMERIC, "C"); // avoid strtod expecting ,-separator in DE Chris@1298: Chris@439: int i = 0; Chris@439: Chris@439: const char *s = xsdd.c_str(); Chris@1038: int len = int(xsdd.length()); Chris@439: Chris@439: bool negative = false, afterT = false; Chris@439: Chris@439: while (i < len) { Chris@439: Chris@439: if (s[i] == '-') { Chris@439: if (i == 0) negative = true; Chris@439: ++i; Chris@439: continue; Chris@439: } Chris@439: Chris@439: double value = 0.0; Chris@439: char *eptr = 0; Chris@439: Chris@439: if (isdigit(s[i]) || s[i] == '.') { Chris@439: value = strtod(&s[i], &eptr); Chris@1038: i = int(eptr - s); Chris@439: } Chris@439: Chris@439: if (i == len) break; Chris@439: Chris@439: switch (s[i]) { Chris@439: case 'Y': year = int(value + 0.1); break; Chris@439: case 'D': day = int(value + 0.1); break; Chris@439: case 'H': hour = int(value + 0.1); break; Chris@439: case 'M': Chris@439: if (afterT) minute = int(value + 0.1); Chris@439: else month = int(value + 0.1); Chris@439: break; Chris@439: case 'S': Chris@439: second = value; Chris@439: break; Chris@439: case 'T': afterT = true; break; Chris@439: }; Chris@439: Chris@439: ++i; Chris@439: } Chris@439: Chris@439: if (year > 0) { Chris@843: 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: t = t + RealTime(year * 31556952, 0); Chris@439: } Chris@439: Chris@439: if (month > 0) { Chris@843: 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: t = t + RealTime(month * 2629746, 0); Chris@439: } Chris@439: Chris@439: if (day > 0) { Chris@439: t = t + RealTime(day * 86400, 0); Chris@439: } Chris@439: Chris@439: if (hour > 0) { Chris@439: t = t + RealTime(hour * 3600, 0); Chris@439: } Chris@439: Chris@439: if (minute > 0) { Chris@439: t = t + RealTime(minute * 60, 0); Chris@439: } Chris@439: Chris@439: t = t + fromSeconds(second); Chris@439: Chris@1298: setlocale(LC_NUMERIC, loc); Chris@1298: Chris@928: if (negative) { Chris@928: return -t; Chris@928: } else { Chris@928: return t; Chris@928: } Chris@439: } Chris@439: Chris@439: double Chris@439: RealTime::toDouble() const Chris@439: { Chris@439: double d = sec; Chris@439: d += double(nsec) / double(ONE_BILLION); Chris@439: return d; Chris@439: } Chris@439: Chris@0: std::ostream &operator<<(std::ostream &out, const RealTime &rt) Chris@0: { Chris@0: if (rt < RealTime::zeroTime) { Chris@0: out << "-"; Chris@0: } else { Chris@0: out << " "; Chris@0: } Chris@0: Chris@0: int s = (rt.sec < 0 ? -rt.sec : rt.sec); Chris@0: int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec); Chris@0: Chris@0: out << s << "."; Chris@0: Chris@0: int nn(n); Chris@0: if (nn == 0) out << "00000000"; Chris@0: else while (nn < (ONE_BILLION / 10)) { Chris@0: out << "0"; Chris@0: nn *= 10; Chris@0: } Chris@0: Chris@0: out << n << "R"; Chris@0: return out; Chris@0: } Chris@0: Chris@0: std::string Chris@121: RealTime::toString(bool align) const Chris@0: { Chris@0: std::stringstream out; Chris@0: out << *this; Chris@0: Chris@0: std::string s = out.str(); Chris@0: Chris@121: if (!align && *this >= RealTime::zeroTime) { Chris@121: // remove leading " " Chris@121: s = s.substr(1, s.length() - 1); Chris@121: } Chris@121: Chris@0: // remove trailing R Chris@0: return s.substr(0, s.length() - 1); Chris@0: } Chris@0: Chris@350: RealTime Chris@350: RealTime::fromString(std::string s) Chris@350: { Chris@350: bool negative = false; Chris@957: int section = 0; Chris@350: std::string ssec, snsec; Chris@350: Chris@350: for (size_t i = 0; i < s.length(); ++i) { Chris@350: Chris@350: char c = s[i]; Chris@350: if (isspace(c)) continue; Chris@350: Chris@350: if (section == 0) { Chris@350: Chris@350: if (c == '-') negative = true; Chris@350: else if (isdigit(c)) { section = 1; ssec += c; } Chris@350: else if (c == '.') section = 2; Chris@350: else break; Chris@350: Chris@350: } else if (section == 1) { Chris@350: Chris@350: if (c == '.') section = 2; Chris@350: else if (isdigit(c)) ssec += c; Chris@350: else break; Chris@350: Chris@350: } else if (section == 2) { Chris@350: Chris@350: if (isdigit(c)) snsec += c; Chris@350: else break; Chris@350: } Chris@350: } Chris@350: Chris@350: while (snsec.length() < 8) snsec += '0'; Chris@350: Chris@350: int sec = atoi(ssec.c_str()); Chris@350: int nsec = atoi(snsec.c_str()); Chris@350: if (negative) sec = -sec; Chris@350: Chris@690: // SVDEBUG << "RealTime::fromString: string " << s << " -> " Chris@687: // << sec << " sec, " << nsec << " nsec" << endl; Chris@350: Chris@350: return RealTime(sec, nsec); Chris@350: } Chris@350: Chris@0: std::string Chris@0: RealTime::toText(bool fixedDp) const Chris@0: { Chris@247: if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp); Chris@0: Chris@612: Preferences *p = Preferences::getInstance(); Chris@1070: bool hms = true; Chris@1070: Chris@612: if (p) { Chris@1070: hms = p->getShowHMS(); Chris@612: int fps = 0; Chris@612: switch (p->getTimeToTextMode()) { Chris@612: case Preferences::TimeToTextMs: break; Chris@612: case Preferences::TimeToTextUs: fps = 1000000; break; Chris@612: case Preferences::TimeToText24Frame: fps = 24; break; Chris@612: case Preferences::TimeToText25Frame: fps = 25; break; Chris@612: case Preferences::TimeToText30Frame: fps = 30; break; Chris@612: case Preferences::TimeToText50Frame: fps = 50; break; Chris@612: case Preferences::TimeToText60Frame: fps = 60; break; Chris@612: } Chris@1070: if (fps != 0) return toFrameText(fps, hms); Chris@612: } Chris@612: Chris@1070: return toMSText(fixedDp, hms); Chris@1070: } Chris@0: Chris@1070: static void Chris@1070: writeSecPart(std::stringstream &out, bool hms, int sec) Chris@1070: { Chris@1070: if (hms) { Chris@1031: if (sec >= 3600) { Chris@1031: out << (sec / 3600) << ":"; Chris@1031: } Chris@1031: Chris@1031: if (sec >= 60) { Chris@1070: int minutes = (sec % 3600) / 60; Chris@1070: if (sec >= 3600 && minutes < 10) out << "0"; Chris@1070: out << minutes << ":"; Chris@1031: } Chris@1031: Chris@1031: if (sec >= 10) { Chris@1031: out << ((sec % 60) / 10); Chris@1031: } Chris@1031: Chris@1031: out << (sec % 10); Chris@1031: Chris@1031: } else { Chris@1031: out << sec; Chris@0: } Chris@1070: } Chris@1070: Chris@1070: std::string Chris@1070: RealTime::toMSText(bool fixedDp, bool hms) const Chris@1070: { Chris@1070: if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms); Chris@1070: Chris@1070: std::stringstream out; Chris@1070: Chris@1070: writeSecPart(out, hms, sec); Chris@0: Chris@0: int ms = msec(); Chris@0: Chris@0: if (ms != 0) { Chris@0: out << "."; Chris@0: out << (ms / 100); Chris@0: ms = ms % 100; Chris@0: if (ms != 0) { Chris@0: out << (ms / 10); Chris@0: ms = ms % 10; Chris@0: } else if (fixedDp) { Chris@0: out << "0"; Chris@0: } Chris@0: if (ms != 0) { Chris@0: out << ms; Chris@0: } else if (fixedDp) { Chris@0: out << "0"; Chris@0: } Chris@0: } else if (fixedDp) { Chris@0: out << ".000"; Chris@0: } Chris@0: Chris@612: std::string s = out.str(); Chris@0: Chris@612: return s; Chris@612: } Chris@612: Chris@612: std::string Chris@1070: RealTime::toFrameText(int fps, bool hms) const Chris@612: { Chris@1070: if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms); Chris@1031: Chris@612: std::stringstream out; Chris@612: Chris@1070: writeSecPart(out, hms, sec); Chris@1031: Chris@1070: // avoid rounding error if fps does not divide into ONE_BILLION Chris@1070: int64_t fbig = nsec; Chris@1070: fbig *= fps; Chris@1070: int f = int(fbig / ONE_BILLION); Chris@612: Chris@612: int div = 1; Chris@612: int n = fps - 1; Chris@612: while ((n = n / 10)) { Chris@612: div *= 10; Chris@612: } Chris@612: Chris@612: out << ":"; Chris@612: Chris@843: // cerr << "div = " << div << ", f = "<< f << endl; Chris@612: Chris@612: while (div) { Chris@612: int d = (f / div) % 10; Chris@612: out << d; Chris@612: div /= 10; Chris@612: } Chris@612: Chris@0: std::string s = out.str(); Chris@0: Chris@843: // cerr << "converted " << toString() << " to " << s << endl; Chris@612: Chris@0: return s; Chris@0: } Chris@0: Chris@247: std::string Chris@247: RealTime::toSecText() const Chris@247: { Chris@247: if (*this < RealTime::zeroTime) return "-" + (-*this).toSecText(); Chris@247: Chris@247: std::stringstream out; Chris@247: Chris@1070: writeSecPart(out, true, sec); Chris@247: Chris@247: if (sec < 60) { Chris@247: out << "s"; Chris@247: } Chris@247: Chris@247: std::string s = out.str(); Chris@247: Chris@247: return s; Chris@247: } Chris@247: Chris@494: std::string Chris@494: RealTime::toXsdDuration() const Chris@494: { Chris@494: std::string s = "PT" + toString(false) + "S"; Chris@494: return s; Chris@494: } Chris@494: Chris@183: RealTime Chris@183: RealTime::operator*(int m) const Chris@183: { Chris@183: double t = (double(nsec) / ONE_BILLION) * m; Chris@183: t += sec * m; Chris@183: return fromSeconds(t); Chris@183: } Chris@0: Chris@0: RealTime Chris@0: RealTime::operator/(int d) const Chris@0: { Chris@0: int secdiv = sec / d; Chris@0: int secrem = sec % d; Chris@0: Chris@0: double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d; Chris@0: Chris@0: return RealTime(secdiv, int(nsecdiv + 0.5)); Chris@0: } Chris@0: Chris@378: RealTime Chris@378: RealTime::operator*(double m) const Chris@378: { Chris@378: double t = (double(nsec) / ONE_BILLION) * m; Chris@378: t += sec * m; Chris@378: return fromSeconds(t); Chris@378: } Chris@378: Chris@378: RealTime Chris@378: RealTime::operator/(double d) const Chris@378: { Chris@378: double t = (double(nsec) / ONE_BILLION) / d; Chris@378: t += sec / d; Chris@378: return fromSeconds(t); Chris@378: } Chris@378: Chris@0: double Chris@0: RealTime::operator/(const RealTime &r) const Chris@0: { Chris@0: double lTotal = double(sec) * ONE_BILLION + double(nsec); Chris@0: double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec); Chris@0: Chris@0: if (rTotal == 0) return 0.0; Chris@0: else return lTotal/rTotal; Chris@0: } Chris@0: Chris@1040: static RealTime Chris@1040: frame2RealTime_i(sv_frame_t frame, sv_frame_t iSampleRate) Chris@1040: { Chris@1040: if (frame < 0) return -frame2RealTime_i(-frame, iSampleRate); Chris@1040: Chris@1262: int sec = int(frame / iSampleRate); Chris@1040: frame -= sec * iSampleRate; Chris@1262: int nsec = int((double(frame) / double(iSampleRate)) * ONE_BILLION + 0.5); Chris@1262: // Use ctor here instead of setting data members directly to Chris@1262: // ensure nsec > ONE_BILLION is handled properly. It's extremely Chris@1262: // unlikely, but not impossible. Chris@1262: return RealTime(sec, nsec); Chris@1040: } Chris@1040: Chris@1038: sv_frame_t Chris@1040: RealTime::realTime2Frame(const RealTime &time, sv_samplerate_t sampleRate) Chris@0: { Chris@0: if (time < zeroTime) return -realTime2Frame(-time, sampleRate); Chris@1262: double s = time.sec + double(time.nsec) / 1000000000.0; Chris@1262: return sv_frame_t(s * sampleRate + 0.5); Chris@0: } Chris@0: Chris@0: RealTime Chris@1040: RealTime::frame2RealTime(sv_frame_t frame, sv_samplerate_t sampleRate) Chris@0: { Chris@1040: if (sampleRate == double(int(sampleRate))) { Chris@1040: return frame2RealTime_i(frame, int(sampleRate)); Chris@1040: } Chris@0: Chris@1040: double sec = double(frame) / sampleRate; Chris@1040: return fromSeconds(sec); Chris@0: } Chris@0: Chris@0: const RealTime RealTime::zeroTime(0,0); Chris@0: