/* This file is part of Raul. Copyright 2007-2014 David Robillard Raul is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Raul is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Raul. If not, see . */ #ifndef RAUL_TIMESTAMP_HPP #define RAUL_TIMESTAMP_HPP #include #include #include #include #include #include namespace Raul { /** A type of time stamp * \ingroup raul */ class TimeUnit { public: enum Type { FRAMES, BEATS, SECONDS }; /** `ppt` (parts per tick) is the sample rate for FRAMES, PPQN for BEATS, * and ignored for SECONDS. */ inline TimeUnit(Type type, uint32_t ppt) : _type(type) , _ppt(ppt) { assert(type == SECONDS || ppt != 0); _type = type; _ppt = ppt; } static inline TimeUnit frames(uint32_t srate) { return {FRAMES, srate}; } static inline TimeUnit beats(uint32_t ppqn) { return {BEATS, ppqn}; } static inline TimeUnit seconds() { return {BEATS, std::numeric_limits::max()}; } inline Type type() const { return _type; } inline uint32_t ppt() const { return _ppt; } inline bool operator==(const TimeUnit& rhs) const { return (_type == rhs._type && _ppt == rhs._ppt); } inline bool operator!=(const TimeUnit& rhs) const { return (_type != rhs._type || _ppt != rhs._ppt); } private: Type _type; uint32_t _ppt; }; /** A real-time time stamp (possible units: frame, absolute (s), or beat). * * This is a uint32_t:uint32_t fixed point representation, capable of * sub-sample accurate frame time, beat time (at any remotely sane * tempo and sample rate), and absolute time. The absolute time (seconds) * is compatible with standard OSC/NTP time stamps. * * \ingroup raul */ class TimeStamp { public: explicit TimeStamp(TimeUnit unit, uint32_t ticks = 0, uint32_t subticks = 0) : _ticks(ticks) , _subticks(subticks) , _unit(unit) {} inline TimeStamp(TimeUnit unit, double dec) : _ticks(0u) , _subticks(0u) , _unit(unit) { dec = std::max(0.0, dec); dec = std::min(double(std::numeric_limits::max()), dec); double integral; const double fractional = modf(dec, &integral); _ticks = static_cast(integral); _subticks = static_cast(fractional * unit.ppt()); } inline TimeStamp(const TimeStamp&) = default; TimeStamp& operator=(const TimeStamp&) = default; inline TimeUnit unit() const { return _unit; } inline uint32_t ticks() const { return _ticks; } inline uint32_t subticks() const { return _subticks; } inline double to_double() const { return _ticks + (_subticks / static_cast(_unit.ppt())); } inline bool is_zero() const { return _ticks == 0 && _subticks == 0; } inline TimeStamp& operator=(uint32_t ticks) { _ticks = ticks; _subticks = 0; return *this; } inline bool operator==(const TimeStamp& rhs) const { return _ticks == rhs._ticks && _subticks == rhs._subticks && _unit == rhs._unit; } inline bool operator!=(const TimeStamp& rhs) const { return !operator==(rhs); } inline bool operator<(const TimeStamp& rhs) const { assert(_unit == rhs._unit); return (_ticks < rhs._ticks || (_ticks == rhs._ticks && _subticks < rhs._subticks)); } inline bool operator>(const TimeStamp& rhs) const { assert(_unit == rhs._unit); return (_ticks > rhs._ticks || (_ticks == rhs._ticks && _subticks > rhs._subticks)); } inline bool operator<=(const TimeStamp& rhs) const { return (*this == rhs) || ((*this) < rhs); } inline bool operator>=(const TimeStamp& rhs) const { return (*this == rhs) || ((*this) > rhs); } inline TimeStamp& operator+=(const TimeStamp& rhs) { assert(_unit == rhs._unit); _ticks += rhs._ticks; if (_subticks + rhs._subticks <= _unit.ppt()) { _subticks += rhs._subticks; } else if (rhs._subticks > 0) { ++_ticks; _subticks = rhs._subticks + _subticks - _unit.ppt(); } return *this; } inline TimeStamp& operator-=(const TimeStamp& rhs) { assert(_unit == rhs._unit); assert(rhs <= *this); _ticks -= rhs._ticks; if (_subticks >= rhs._subticks) { _subticks -= rhs._subticks; } else if (rhs._subticks > 0) { --_ticks; _subticks = _unit.ppt() - (rhs._subticks - _subticks); } return *this; } inline TimeStamp operator+(const TimeStamp& rhs) const { assert(_unit == rhs._unit); TimeStamp result = *this; result += rhs; return result; } inline TimeStamp operator-(const TimeStamp& rhs) const { assert(_unit == rhs._unit); TimeStamp result = *this; result -= rhs; return result; } private: uint32_t _ticks; uint32_t _subticks; TimeUnit _unit; }; static inline std::ostream& operator<<(std::ostream& os, const TimeStamp& t) { os << t.ticks() << ":" << t.subticks(); switch (t.unit().type()) { case TimeUnit::FRAMES: os << " frames"; break; case TimeUnit::BEATS: os << " beats"; break; case TimeUnit::SECONDS: os << " seconds"; break; } return os; } class FrameStamp : public TimeStamp { public: explicit FrameStamp(uint32_t rate, uint32_t ticks = 0, uint32_t subticks = 0) : TimeStamp(TimeUnit(TimeUnit::FRAMES, rate), ticks, subticks) {} }; class BeatStamp : public TimeStamp { public: explicit BeatStamp(uint32_t ppqn, uint32_t ticks = 0, uint32_t subticks = 0) : TimeStamp(TimeUnit(TimeUnit::BEATS, ppqn), ticks, subticks) {} }; /** Same thing as TimeStamp really, but makes code clearer and enforces * correct semantics via type safety */ using TimeDuration = TimeStamp; } // namespace Raul #endif // RAUL_TIMESTAMP_HPP