LUMIERA.clone/src/lib/time/quantiser.hpp
Ichthyostega 27089550d2 clean-up: relocate frame quantisation operations
The `FixedFrameQuantiser` relied on three functions from the raw-time handling library.
Since this (and NTSC drop-frame) are the only usages, these functions
can be relocated into the implemntation translation unit `lib/time/quantiser.cpp`

On closer inspection, this reveals some room for improvements:
Instead of relying on raw-computation functions written in C,
we could rather revert the dependency and express these computations
in terms of our Time-entities, which are written in C++, are much more
systematic and provide consistency checks and protection against numeric
overflow, all integrated with linear arithmetic and concise notation.

After performing these rearrangements,
most of the functions can be collapsed into ''almost nothing''.

This was taken as opportunity to re-check and improve the remaining
implementation core of the `FixedFrameQuantiser` -- the handling of
extreme corner cases can be much improved, now representing the
"grid-local time" as `Offset`, which doubles the possible value range.

The reworked unit test shows that, with this change, now the limitation
happens prior to quantisation, meaning that we always get a grid-aligned
result, even in the most extreme corner cases.
2025-05-28 23:49:01 +02:00

157 lines
5.1 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
QUANTISER.hpp - aligning time values to a time grid
Copyright (C)
2010, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** 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 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file quantiser.hpp
** Library functions to support the formation of grid-aligned time values.
** This is a crucial part of Lumiera's time and time handling framework; it
** serves as link between the precise internal time representation and various
** grid-aligned external standard time representations. The underlying theme is,
** by forcing all conversions to go through this central set of library functions,
** the notorious act of frame-aligning / grid-aligning time values, which can not
** be avoided, will at least be performed in a reproducible and predictable way.
** Moreover, the time handling library was built in a way such as to encourage
** use of the internal, abstracted yet precise time entities, so that the
** inevitable conversion to the imprecise but well-established external
** entities (frame numbers, SMPTE, drop frame, HMS) happens as late as possible.
*/
#ifndef LIB_TIME_QUANTISER_H
#define LIB_TIME_QUANTISER_H
#include "lib/error.hpp"
#include "lib/time/grid.hpp"
#include "lib/time/formats.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/iter-adapter.hpp"
#include <vector>
#include <string>
#include <cmath>
namespace lib {
namespace time {
LUMIERA_ERROR_DECLARE (UNKNOWN_GRID); ///< referring to an undefined grid or scale in value quantisation
namespace { // stashed here for later
template<typename NUM>
struct ValTrait;
template<>
struct ValTrait<int>
{
static int asInt (int val) { return val; }
static double asDouble (int val) { return val; }
};
template<>
struct ValTrait<double>
{
static int asInt (double val) { return std::floor(0.5+val); } ///< in accordance with Lumiera's time handling RfC
static double asDouble (double val) { return val; }
};
}
/** @todo placeholder for accessing
* a current or default session grid.
* To be implemented later.
*/
PQuant getDefaultGridFallback(); ///////////////////////TICKET #810
/**
* Facility to create grid-aligned time values.
* Effectively, a quantiser exposes the value Grid API, but
* additionally also manages a set of supported (display) formats or
* "time code" formats. Plus there is an static API to fetch a suitable
* quantiser instance by-name; actually this utilises a hidden link to
* the Lumiera session. Time quantisation and timecode handling explicitly
* relies on this Quantiser interface.
*
*/
class Quantiser
: public virtual Grid
{
protected:
format::Supported supportedFormats_;
Quantiser()
: supportedFormats_(format::SupportStandardTimecode())
{ }
public:
template<class FMT>
bool
supports() const
{
return supportedFormats_.check<FMT>();
}
static PQuant retrieve (Symbol gridID); ///< @note defined in common-services.cpp
TimeValue materialise (TimeValue const& raw) const;
//------Grid-API----------------------------------------------
virtual FrameCnt gridPoint (TimeValue const& raw) const =0;
virtual Offset gridLocal (TimeValue const& raw) const =0;
virtual TimeValue timeOf (FrameCnt gridPoint) const =0;
virtual TimeValue timeOf (FSecs, int =0) const =0;
};
/**
* Simple stand-alone Quantiser implementation based on a constant sized gird.
* This is a self-contained quantiser implementation without any implicit referral
* to the Lumiera session. As such it is suited for simplified unit testing.
* @warning real Stage and Steam-Layer code should always fetch a quantiser from the
* Session, referring to a pre defined TimeGrid. Basically, the overall purpose of
* the time-quantisation framework is to enforce such a link to a distinct time scale
* and quantisation, so to prevent "wild and uncoordinated" rounding attempts.
*/
class FixedFrameQuantiser
: public Quantiser
{
Time origin_;
Duration raster_;
public:
FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0));
FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0));
FrameCnt gridPoint (TimeValue const&) const override;
Offset gridLocal (TimeValue const&) const override;
TimeValue timeOf (FrameCnt gridPoint) const override;
TimeValue timeOf (FSecs, int =0) const override;
private:
static int64_t grid_aligned (TimeValue const&, TimeValue const&);
};
}} // lib::time
#endif