2008-06-19 00:57:47 +02:00
|
|
|
/*
|
2013-01-07 05:43:01 +01:00
|
|
|
Time - Lumiera time handling foundation
|
2008-06-19 00:57:47 +02:00
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
2011-02-07 00:54:16 +01:00
|
|
|
2008, Christian Thaeter <ct@pipapo.org>
|
2013-01-07 05:43:01 +01:00
|
|
|
2010, Stefan Kangas <skangas@skangas.se>
|
2011-02-07 00:54:16 +01:00
|
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
2008-06-19 00:57:47 +02:00
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
2010-12-17 23:28:49 +01:00
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
2008-06-19 00:57:47 +02:00
|
|
|
|
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file time.cpp
|
|
|
|
|
** Lumiera time handling core implementation unit.
|
|
|
|
|
** This translation unit generates code for the Lumiera internal time wrapper,
|
|
|
|
|
** based on gavl_time_t, associated constants, marker classes for the derived
|
|
|
|
|
** time entities (TimeVar, Offset, Duration, TimeSpan, FrameRate) and for the
|
|
|
|
|
** basic time and frame rate conversion functions.
|
|
|
|
|
**
|
|
|
|
|
** Client code includes either time.h (for basics and conversion functions)
|
|
|
|
|
** or timevalue.hpp (for the time entities), timequant.hpp for grid aligned
|
|
|
|
|
** time values or timecode.hpp
|
|
|
|
|
**
|
|
|
|
|
** @see Time
|
|
|
|
|
** @see TimeValue
|
|
|
|
|
** @see Grid
|
|
|
|
|
** @see TimeValue_test
|
|
|
|
|
** @see QuantiserBasics_test
|
|
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2011-01-09 02:13:58 +01:00
|
|
|
#include "lib/error.hpp"
|
2013-01-07 05:43:01 +01:00
|
|
|
#include "lib/time.h"
|
|
|
|
|
#include "lib/time/timevalue.hpp"
|
2012-03-04 01:34:18 +01:00
|
|
|
#include "lib/util-quant.hpp"
|
2011-01-09 04:56:46 +01:00
|
|
|
|
2011-01-09 02:13:58 +01:00
|
|
|
extern "C" {
|
2010-07-21 04:20:03 +02:00
|
|
|
#include "lib/tmpbuf.h"
|
2011-01-09 02:13:58 +01:00
|
|
|
}
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2011-01-09 02:13:58 +01:00
|
|
|
#include <math.h>
|
2013-01-07 05:43:01 +01:00
|
|
|
#include <limits>
|
|
|
|
|
#include <string>
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
#include <sstream>
|
2013-01-07 05:43:01 +01:00
|
|
|
#include <boost/rational.hpp>
|
2013-09-01 17:36:05 +02:00
|
|
|
#include <boost/lexical_cast.hpp>
|
2011-01-06 13:30:27 +01:00
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
using std::string;
|
2011-01-09 04:56:46 +01:00
|
|
|
using util::floordiv;
|
|
|
|
|
using lib::time::FSecs;
|
|
|
|
|
using lib::time::FrameRate;
|
|
|
|
|
using boost::rational_cast;
|
2013-09-01 17:36:05 +02:00
|
|
|
using boost::lexical_cast;
|
2011-01-09 04:56:46 +01:00
|
|
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-06 13:30:27 +01:00
|
|
|
|
2010-12-06 16:18:54 +01:00
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
namespace lib {
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
namespace meta {
|
|
|
|
|
extern const std::string FAILURE_INDICATOR;
|
|
|
|
|
}
|
2013-01-07 05:43:01 +01:00
|
|
|
namespace time {
|
|
|
|
|
|
|
|
|
|
|
2018-11-17 18:00:39 +01:00
|
|
|
const gavl_time_t TimeValue::SCALE = GAVL_TIME_SCALE;
|
|
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
/** @note the allowed time range is explicitly limited to help overflow protection */
|
|
|
|
|
const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<gavl_time_t>::max() / 30) );
|
|
|
|
|
const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
|
|
|
|
|
const Time Time::ZERO;
|
|
|
|
|
|
2018-11-17 17:25:10 +01:00
|
|
|
const Time Time::ANYTIME(Time::MIN);
|
|
|
|
|
const Time Time::NEVER (Time::MAX);
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
const Offset Offset::ZERO (Time::ZERO);
|
2018-11-17 18:00:39 +01:00
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
|
2018-11-17 18:00:39 +01:00
|
|
|
/** scale factor _used locally within this implementation header_.
|
|
|
|
|
* GAVL_TIME_SCALE rsp. TimeValue::SCALE is the correct factor or dividend when using gavl_time_t
|
|
|
|
|
* for units of whole seconds from gavl_time_t. Since we want to use milliseconds,
|
|
|
|
|
* we need to multiply or divide by 1000 to get correct results. */
|
|
|
|
|
#define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000)
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/** convenience constructor to build an
|
|
|
|
|
* internal Lumiera Time value from the usual parts
|
|
|
|
|
* of an sexagesimal time specification. Arbitrary integral
|
|
|
|
|
* values are acceptable and will be summed up accordingly.
|
|
|
|
|
* The minute and hour part can be omitted.
|
|
|
|
|
* @warning internal Lumiera time values refer to an
|
|
|
|
|
* implementation dependent time origin/scale.
|
|
|
|
|
* The given value will be used as-is, without
|
|
|
|
|
* any further adjustments.
|
|
|
|
|
*/
|
|
|
|
|
Time::Time ( long millis
|
|
|
|
|
, uint secs
|
|
|
|
|
, uint mins
|
|
|
|
|
, uint hours
|
|
|
|
|
)
|
|
|
|
|
: TimeValue(lumiera_build_time (millis,secs,mins,hours))
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** convenience constructor to build an Time value
|
|
|
|
|
* from a fraction of seconds, given as rational number.
|
|
|
|
|
* An example would be to the time unit of a framerate.
|
|
|
|
|
*/
|
|
|
|
|
Time::Time (FSecs const& fractionalSeconds)
|
|
|
|
|
: TimeValue(lumiera_rational_to_time (fractionalSeconds))
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
2015-09-25 02:03:12 +02:00
|
|
|
/** display an internal Lumiera Time value
|
2013-01-07 05:43:01 +01:00
|
|
|
* for diagnostic purposes or internal reporting.
|
|
|
|
|
* @warning internal Lumiera time values refer to an
|
|
|
|
|
* implementation dependent time origin/scale.
|
|
|
|
|
* @return string rendering of the actual, underlying
|
2016-01-08 00:13:59 +01:00
|
|
|
* implementation value, as `h:m:s:ms`
|
2013-01-07 05:43:01 +01:00
|
|
|
*/
|
|
|
|
|
Time::operator string() const
|
|
|
|
|
{
|
|
|
|
|
return string (lumiera_tmpbuf_print_time (t_));
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 00:13:59 +01:00
|
|
|
/** @note recommendation is to use TCode for external representation
|
|
|
|
|
* @remarks this is the most prevalent internal diagnostics display
|
|
|
|
|
* of any "time-like" value, it is meant to be compact. */
|
2015-09-25 02:03:12 +02:00
|
|
|
TimeValue::operator string() const
|
2013-01-07 05:43:01 +01:00
|
|
|
{
|
2015-09-25 02:03:12 +02:00
|
|
|
gavl_time_t time = t_;
|
2016-01-08 00:13:59 +01:00
|
|
|
int64_t millis, seconds;
|
2015-09-25 02:03:12 +02:00
|
|
|
bool negative = (time < 0);
|
|
|
|
|
|
|
|
|
|
if (negative) time = -time;
|
2018-11-17 18:00:39 +01:00
|
|
|
time /= TIME_SCALE_MS;
|
2015-09-25 02:03:12 +02:00
|
|
|
millis = time % 1000;
|
|
|
|
|
seconds = time / 1000;
|
|
|
|
|
|
|
|
|
|
return string (negative ? "-" : "")
|
|
|
|
|
+ (seconds>0 or time==0? lexical_cast<string> (seconds)+"s" : "")
|
|
|
|
|
+ (millis>0? lexical_cast<string> (millis)+"ms" : "")
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 00:13:59 +01:00
|
|
|
Offset::operator string() const
|
|
|
|
|
{
|
|
|
|
|
return (t_< 0? "" : "∆")
|
|
|
|
|
+ TimeValue::operator string();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Duration::operator string() const
|
|
|
|
|
{
|
|
|
|
|
return "≺"+TimeValue::operator string()+"≻";
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-25 02:03:12 +02:00
|
|
|
TimeSpan::operator string() const
|
|
|
|
|
{
|
|
|
|
|
return string (lumiera_tmpbuf_print_time (t_))
|
2016-01-08 00:13:59 +01:00
|
|
|
+ string (dur_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
namespace {
|
|
|
|
|
template<typename INT>
|
|
|
|
|
string
|
|
|
|
|
renderFraction (INT const& frac, Literal postfx) noexcept
|
|
|
|
|
try {
|
|
|
|
|
std::ostringstream buffer;
|
|
|
|
|
if (1 == frac.denominator() or 0 == frac.numerator())
|
|
|
|
|
buffer << frac.numerator() << postfx;
|
|
|
|
|
else
|
|
|
|
|
buffer << frac <<postfx;
|
|
|
|
|
return buffer.str();
|
|
|
|
|
}
|
|
|
|
|
catch(...)
|
|
|
|
|
{ return meta::FAILURE_INDICATOR; }
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-08 00:13:59 +01:00
|
|
|
/** visual framerate representation (for diagnostics) */
|
|
|
|
|
FrameRate::operator string() const
|
|
|
|
|
{
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
return renderFraction (*this, "FPS");
|
2013-01-07 05:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
/** @internal backdoor to sneak in a raw time value
|
2016-01-08 00:13:59 +01:00
|
|
|
* bypassing any normalisation and limiting */
|
2013-01-07 05:43:01 +01:00
|
|
|
TimeValue
|
|
|
|
|
TimeValue::buildRaw_ (gavl_time_t raw)
|
|
|
|
|
{
|
|
|
|
|
return reinterpret_cast<TimeValue const&> (raw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** predefined constant for PAL framerate */
|
|
|
|
|
const FrameRate FrameRate::PAL (25);
|
|
|
|
|
const FrameRate FrameRate::NTSC (30000,1001);
|
|
|
|
|
|
2013-06-15 04:02:48 +02:00
|
|
|
const FrameRate FrameRate::HALTED (1,std::numeric_limits<int>::max());
|
|
|
|
|
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
/** @return time span of one frame of this rate,
|
|
|
|
|
* cast into internal Lumiera time scale */
|
|
|
|
|
Duration
|
|
|
|
|
FrameRate::duration() const
|
|
|
|
|
{
|
|
|
|
|
if (0 == *this)
|
|
|
|
|
throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
|
|
|
|
|
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
|
|
|
|
|
|
|
|
|
return Duration (1, *this);
|
|
|
|
|
}
|
2013-09-01 17:36:05 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-04-28 03:02:02 +02:00
|
|
|
/** @internal stretch offset by a possibly fractional factor, and quantise into raw (micro tick) grid */
|
2013-01-07 05:43:01 +01:00
|
|
|
Offset
|
2018-04-28 03:02:02 +02:00
|
|
|
Offset::stretchedByRationalFactor (boost::rational<int64_t> factor) const
|
2013-01-07 05:43:01 +01:00
|
|
|
{
|
2018-04-28 03:02:02 +02:00
|
|
|
boost::rational<int64_t> distance (this->t_);
|
2013-01-07 05:43:01 +01:00
|
|
|
distance *= factor;
|
|
|
|
|
gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
|
|
|
|
|
return Offset(TimeValue(microTicks));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** offset by the given number of frames. */
|
2013-11-18 00:01:43 +01:00
|
|
|
Offset::Offset (FrameCnt count, FrameRate const& fps)
|
2013-01-07 05:43:01 +01:00
|
|
|
: TimeValue (count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
|
|
|
|
|
: _raw(Duration::NIL))
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
/** duration of the given number of frames.
|
|
|
|
|
* @note always positive; count used absolute */
|
2013-11-18 00:01:43 +01:00
|
|
|
Duration::Duration (FrameCnt count, FrameRate const& fps)
|
2013-01-07 05:43:01 +01:00
|
|
|
: TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** constant to indicate "no duration" */
|
|
|
|
|
const Duration Duration::NIL (Time::ZERO);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::Time
|
|
|
|
|
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
namespace util {
|
|
|
|
|
string
|
|
|
|
|
StringConv<lib::time::FSecs, void>::invoke (lib::time::FSecs val) noexcept
|
|
|
|
|
{
|
|
|
|
|
return lib::time::renderFraction (val, "sec");
|
|
|
|
|
}
|
|
|
|
|
} // namespace util
|
2013-01-07 05:43:01 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ===== implementation of the C API functions ===== */
|
|
|
|
|
|
|
|
|
|
|
2008-06-19 00:57:47 +02:00
|
|
|
char*
|
|
|
|
|
lumiera_tmpbuf_print_time (gavl_time_t time)
|
|
|
|
|
{
|
|
|
|
|
int milliseconds, seconds, minutes, hours;
|
2015-09-25 02:03:12 +02:00
|
|
|
bool negative = (time < 0);
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2015-09-25 02:03:12 +02:00
|
|
|
if (negative)
|
2008-06-19 00:57:47 +02:00
|
|
|
time = -time;
|
|
|
|
|
|
2018-11-17 18:00:39 +01:00
|
|
|
time /= TIME_SCALE_MS;
|
2008-06-19 00:57:47 +02:00
|
|
|
milliseconds = time % 1000;
|
|
|
|
|
time /= 1000;
|
|
|
|
|
seconds = time % 60;
|
|
|
|
|
time /= 60;
|
|
|
|
|
minutes = time % 60;
|
|
|
|
|
time /= 60;
|
|
|
|
|
hours = time;
|
|
|
|
|
|
2011-01-07 16:28:13 +01:00
|
|
|
char *buffer = lumiera_tmpbuf_snprintf(64, "%s%01d:%02d:%02d.%03d",
|
2008-06-19 00:57:47 +02:00
|
|
|
negative ? "-" : "", hours, minutes, seconds, milliseconds);
|
|
|
|
|
|
|
|
|
|
ENSURE(buffer != NULL);
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-09 04:56:46 +01:00
|
|
|
gavl_time_t
|
|
|
|
|
lumiera_rational_to_time (FSecs const& fractionalSeconds)
|
2011-01-06 13:30:27 +01:00
|
|
|
{
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
return rational_cast<gavl_time_t> (fractionalSeconds * int{lib::time::TimeValue::SCALE});
|
2011-01-06 13:30:27 +01:00
|
|
|
}
|
|
|
|
|
|
2011-05-13 04:04:02 +02:00
|
|
|
gavl_time_t
|
|
|
|
|
lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps)
|
|
|
|
|
{
|
|
|
|
|
// convert to 64bit
|
|
|
|
|
boost::rational<uint64_t> framerate (fps.numerator(), fps.denominator());
|
|
|
|
|
|
2018-11-17 18:00:39 +01:00
|
|
|
return rational_cast<gavl_time_t> (lib::time::TimeValue::SCALE * frameCount / framerate);
|
2011-05-13 04:04:02 +02:00
|
|
|
}
|
2011-01-09 04:56:46 +01:00
|
|
|
|
|
|
|
|
gavl_time_t
|
|
|
|
|
lumiera_frame_duration (FrameRate const& fps)
|
2011-01-06 13:30:27 +01:00
|
|
|
{
|
2011-01-09 04:56:46 +01:00
|
|
|
if (!fps)
|
|
|
|
|
throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
|
|
|
|
|
, error::LUMIERA_ERROR_BOTTOM_VALUE);
|
|
|
|
|
|
Timehandling: choose safer representation for fractional seconds (closes #939)
When drafting the time handling framework some years ago,
I foresaw the possible danger of mixing up numbers relating
to fractional seconds, with other plain numbers intended as
frame counts or as micro ticks. Thus I deliberately picked
an incompatible integer type for FSecs = boost::rational<long>
However, using long is problematic in itself, since its actual
bit length is not fixed, and especially on 32bit platforms long
is quite surprisingly defined to be the same as int.
However, meanwhile, using the new C++ features, I have blocked
pretty much any possible implicit conversion path, requiring
explicit conversions in the relevant ctor invocations. So,
after weighting in the alternatives, FSecs is now defined
as boost::rational<int64_t>.
2020-02-17 02:36:54 +01:00
|
|
|
FSecs duration = 1 / fps;
|
2011-01-09 04:56:46 +01:00
|
|
|
return lumiera_rational_to_time (duration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
namespace { // implementation: basic frame quantisation....
|
2011-01-06 13:30:27 +01:00
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
inline int64_t
|
2011-01-09 04:56:46 +01:00
|
|
|
calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
|
|
|
|
{
|
|
|
|
|
time -= origin;
|
|
|
|
|
return floordiv (time,grid);
|
|
|
|
|
}
|
2011-05-21 06:58:15 +02:00
|
|
|
|
|
|
|
|
inline int64_t
|
|
|
|
|
calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1)
|
|
|
|
|
{
|
|
|
|
|
REQUIRE (framerate);
|
|
|
|
|
REQUIRE (framerate_divisor);
|
|
|
|
|
|
|
|
|
|
const int64_t limit_num = std::numeric_limits<gavl_time_t>::max() / framerate;
|
|
|
|
|
const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor;
|
2018-11-17 18:00:39 +01:00
|
|
|
const int64_t microScale {lib::time::TimeValue::SCALE};
|
2011-05-21 06:58:15 +02:00
|
|
|
|
|
|
|
|
// protect against numeric overflow
|
|
|
|
|
if (abs(time) < limit_num && microScale < limit_den)
|
|
|
|
|
{
|
|
|
|
|
// safe to calculate "time * framerate"
|
|
|
|
|
time -= origin;
|
|
|
|
|
return floordiv (time*framerate, microScale*framerate_divisor);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// direct calculation will overflow.
|
|
|
|
|
// use the less precise method instead...
|
|
|
|
|
gavl_time_t frameDuration = microScale / framerate; // truncated to µs
|
|
|
|
|
return calculate_quantisation (time,origin, frameDuration);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-06 13:30:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-03-31 18:43:50 +02:00
|
|
|
int64_t
|
2011-01-09 07:04:20 +01:00
|
|
|
lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
2011-01-06 13:30:27 +01:00
|
|
|
{
|
2011-01-09 04:56:46 +01:00
|
|
|
return calculate_quantisation (time, origin, grid);
|
2011-01-06 13:30:27 +01:00
|
|
|
}
|
|
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
int64_t
|
|
|
|
|
lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate)
|
|
|
|
|
{
|
|
|
|
|
return calculate_quantisation (time,origin,framerate);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-06 13:30:27 +01:00
|
|
|
gavl_time_t
|
2011-01-09 07:04:20 +01:00
|
|
|
lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
2011-01-06 13:30:27 +01:00
|
|
|
{
|
2011-01-09 04:56:46 +01:00
|
|
|
int64_t count = calculate_quantisation (time, origin, grid);
|
|
|
|
|
gavl_time_t alignedTime = count * grid;
|
|
|
|
|
return alignedTime;
|
2011-01-06 13:30:27 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-14 05:33:50 +01:00
|
|
|
gavl_time_t
|
2011-05-13 04:04:02 +02:00
|
|
|
lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid)
|
2011-01-14 05:33:50 +01:00
|
|
|
{
|
|
|
|
|
gavl_time_t offset = nr * grid;
|
|
|
|
|
return origin + offset;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-06 13:30:27 +01:00
|
|
|
|
2009-07-12 22:51:04 +02:00
|
|
|
gavl_time_t
|
2010-12-06 16:18:54 +01:00
|
|
|
lumiera_build_time(long millis, uint secs, uint mins, uint hours)
|
2009-07-12 22:51:04 +02:00
|
|
|
{
|
|
|
|
|
gavl_time_t time = millis
|
|
|
|
|
+ 1000 * secs
|
|
|
|
|
+ 1000 * 60 * mins
|
|
|
|
|
+ 1000 * 60 * 60 * hours;
|
2018-11-17 18:00:39 +01:00
|
|
|
time *= TIME_SCALE_MS;
|
2009-07-12 22:51:04 +02:00
|
|
|
return time;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-13 18:16:15 +01:00
|
|
|
gavl_time_t
|
2011-05-21 06:58:15 +02:00
|
|
|
lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
|
2010-12-13 18:16:15 +01:00
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
gavl_time_t time = 1000LL * frames/fps
|
2010-12-13 18:16:15 +01:00
|
|
|
+ 1000 * secs
|
|
|
|
|
+ 1000 * 60 * mins
|
|
|
|
|
+ 1000 * 60 * 60 * hours;
|
2018-11-17 18:00:39 +01:00
|
|
|
time *= TIME_SCALE_MS;
|
2010-12-13 18:16:15 +01:00
|
|
|
return time;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 16:18:54 +01:00
|
|
|
int
|
2010-12-13 18:16:15 +01:00
|
|
|
lumiera_time_hours (gavl_time_t time)
|
2010-12-06 16:18:54 +01:00
|
|
|
{
|
2018-11-17 18:00:39 +01:00
|
|
|
return time / TIME_SCALE_MS / 1000 / 60 / 60;
|
2010-12-06 16:18:54 +01:00
|
|
|
}
|
2009-07-12 22:51:04 +02:00
|
|
|
|
2010-12-06 16:18:54 +01:00
|
|
|
int
|
2010-12-13 18:16:15 +01:00
|
|
|
lumiera_time_minutes (gavl_time_t time)
|
2010-12-06 16:18:54 +01:00
|
|
|
{
|
2018-11-17 18:00:39 +01:00
|
|
|
return (time / TIME_SCALE_MS / 1000 / 60) % 60;
|
2010-12-06 16:18:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2010-12-13 18:16:15 +01:00
|
|
|
lumiera_time_seconds (gavl_time_t time)
|
2010-12-06 16:18:54 +01:00
|
|
|
{
|
2018-11-17 18:00:39 +01:00
|
|
|
return (time / TIME_SCALE_MS / 1000) % 60;
|
2010-12-06 16:18:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2010-12-13 18:16:15 +01:00
|
|
|
lumiera_time_millis (gavl_time_t time)
|
2010-12-06 16:18:54 +01:00
|
|
|
{
|
2018-11-17 18:00:39 +01:00
|
|
|
return (time / TIME_SCALE_MS) % 1000;
|
2010-12-06 16:18:54 +01:00
|
|
|
}
|
2010-12-10 12:53:39 +01:00
|
|
|
|
|
|
|
|
int
|
2011-05-21 06:58:15 +02:00
|
|
|
lumiera_time_frames (gavl_time_t time, uint fps)
|
2010-12-10 12:53:39 +01:00
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
REQUIRE (fps < uint(std::numeric_limits<int>::max()));
|
2018-11-17 18:00:39 +01:00
|
|
|
return floordiv<int> (lumiera_time_millis(time) * int(fps), TIME_SCALE_MS);
|
2010-12-10 12:53:39 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-13 18:22:12 +01:00
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ===== NTSC drop-frame conversions ===== */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // implementation helper
|
2011-01-19 11:32:03 +01:00
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
const uint FRAMES_PER_10min = 10*60 * 30000/1001;
|
|
|
|
|
const uint FRAMES_PER_1min = 1*60 * 30000/1001;
|
|
|
|
|
const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
|
|
|
|
|
|
2011-01-19 11:32:03 +01:00
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
/** reverse the drop-frame calculation
|
|
|
|
|
* @param time absolute time value in micro ticks
|
|
|
|
|
* @return the absolute frame number using NTSC drop-frame encoding
|
|
|
|
|
* @todo I doubt this works correct for negative times!!
|
|
|
|
|
*/
|
|
|
|
|
inline int64_t
|
|
|
|
|
calculate_drop_frame_number (gavl_time_t time)
|
|
|
|
|
{
|
|
|
|
|
int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
|
|
|
|
|
|
|
|
|
|
// partition into 10 minute segments
|
|
|
|
|
lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min);
|
|
|
|
|
|
|
|
|
|
// ensure the drop-frame incidents happen at full minutes;
|
|
|
|
|
// at start of each 10-minute segment *no* drop incident happens,
|
|
|
|
|
// thus we need to correct discrepancy between nominal/real framerate once:
|
|
|
|
|
int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min;
|
|
|
|
|
|
|
|
|
|
int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
|
|
|
|
|
return frameNr + 2*dropIncidents;
|
|
|
|
|
}
|
2010-12-13 18:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lumiera_time_ntsc_drop_frames (gavl_time_t time)
|
|
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
return calculate_drop_frame_number(time) % 30;
|
2010-12-13 18:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lumiera_time_ntsc_drop_seconds (gavl_time_t time)
|
|
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
return calculate_drop_frame_number(time) / 30 % 60;
|
2010-12-13 18:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lumiera_time_ntsc_drop_minutes (gavl_time_t time)
|
|
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
return calculate_drop_frame_number(time) / 30 / 60 % 60;
|
2010-12-13 18:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
lumiera_time_ntsc_drop_hours (gavl_time_t time)
|
|
|
|
|
{
|
2011-05-21 06:58:15 +02:00
|
|
|
return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24;
|
2010-12-13 18:22:12 +01:00
|
|
|
}
|
|
|
|
|
|
2011-05-21 06:58:15 +02:00
|
|
|
gavl_time_t
|
|
|
|
|
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
|
|
|
|
|
{
|
|
|
|
|
uint64_t total_mins = 60 * hours + mins;
|
|
|
|
|
uint64_t total_frames = 30*60*60 * hours
|
|
|
|
|
+ 30*60 * mins
|
|
|
|
|
+ 30 * secs
|
|
|
|
|
+ frames
|
|
|
|
|
- 2 * (total_mins - total_mins / 10);
|
|
|
|
|
gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
|
|
|
|
|
|
|
|
|
|
if (0 != result) // compensate for truncating down on conversion
|
|
|
|
|
result += 1; // without this adjustment the frame number
|
|
|
|
|
return result; // would turn out off by -1 on back conversion
|
|
|
|
|
}
|