lumiera_/src/lib/time/timequant.hpp
Ichthyostega f1b3f4e666 Timeline: reconsider time handling and Stage/Steam integration
This ZoomWindow_test highlights again the question about the intended usage
of the Lumiera time entities. In which way do we want to perform time calculations,
and under which circumstances is it adequate to perform arithmetic on
raw time values?

These questions made me think about rather far reaching concerns regarding
subsidiarity and implicit or explicit usage context. Basically I could
reconfirm the design choices taken some years ago -- while I must admit
that the project is headed towards a way larger scale and more loose
coupling of the parts, than I could imagine several years ago, at the
time when the design started...

As a side note: we can not avoid that some knowledge about the time implementation
leaks out from the support lib; time codes themselves are tightly coupled
to the usage scenario within the session and can not be used as means
for implementing UI concerns. And the more generic time frameworks,
like std::chrono (as much as it is desirable to have some integration here)
will not be of any help for most of our specific usage patterns.
The reason is, for film editing we do not have a global time scale,
rather the truth is when the film starts....
2022-10-30 23:12:34 +01:00

175 lines
6.4 KiB
C++

/*
TIMEQUANT.hpp - quantised (grid aligned) time values
Copyright (C) Lumiera.org
2010, Hermann Vosseler <Ichthyostega@web.de>
This program 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.
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.
*/
/** @file timequant.hpp
** Support library to represent grid-aligned time specifications
** This is part of Lumiera's time and time code handling framework.
** The QuTime entity represents the link between the internal precise
** time specifications and external representation formatted according
** to some well established time code format: QuTime both holds a precise
** internal time::TimeValue entry, plus a reference to the _time grid_ to
** be used, when it comes to _quantising_ (grid-aligning) those values.
** And it offers a dedicated API to "materialise" this (still complete and
** precise) time value into an external representation.
**
** # Collaborations
**
** lib::time::Time is the ubiquitous yet opaque internal time "instant" used
** in Lumiera to designate time points (and by extension also a TimeSpan).
** What such an _internal time instant_ actually means, depends on the context,
** yet it is the implementation's (not the user's) responsibility to keep that
** meaning straight.
**
** However, by creating a [Quantised Time](\ref lib::time::QuTime), the association
** to some time scale or time grid is made explicit; the time value within QuTime
** is stored in full precision though, without performing any "rounding" yet.
**
** Only by building or casting into a _time code,_ the actual quantisation is
** performed, which means to align the time value to the next grid point and
** discard excess timing information. There are several [time formats](\ref formats.hpp)
** available to choose from, like Hour-Minute-Second, or SMPTE (h-m-s + frame) or
** fractional seconds or just a frame count with implicit frame rate.
**
** How the _quantisation_ or _grid alignment_ is actually performed is up to the
** discretion of the lib::time::Quantiser; an actually Quantiser instance also
** represents a common notion of some specific time grid, together with a usage
** context. Notably, a "25 fps grid" means to start timing when the playback
** starts, and to proceed in steps of 1/25sec. Conceptually (not-yet-implemented
** as of 11/2022) a frame grid could also imply to anchor the time at some global
** time zone, allowing to integrate _live content_ into the timeline -- and the
** actual link to wall-clock time would be made explicit by some _placement_
** somewhere on the actual timeline.
**
*/
#ifndef LIB_TIME_TIMEQUANT_H
#define LIB_TIME_TIMEQUANT_H
#include "lib/time/timevalue.hpp"
#include "lib/time/quantiser.hpp"
#include "lib/time/timecode.hpp"
#include "lib/symbol.hpp"
//#include <boost/operators.hpp>
#include <string>
namespace lib {
namespace time {
using lib::Symbol;
/**
* grid aligned time specification, referring to a specific scale.
* A quantised time value allows to access the time specification
* as numeric value in one of the supported timecode formats, and
* relative to the defined time scale. Usually this time scale
* exists already in the Lumiera session and is referred simply
* by symbolic ID, it will be fetched on demand through the
* [advice system](\ref advice.hpp).
*
* By creating a QuTime value, the relation to such a predefined
* time scale is made explicit. This doesn't change the internal
* time value, but the actual creation of a timecode formatted
* value (#formatAs) usually implies to quantise or grid align
* the time to the frame grid specific to this time scale.
*/
class QuTime
: public Time
{
PQuant quantiser_;
public:
QuTime (TimeValue raw, Symbol gridID); ///< @note defined in common-services.cpp
QuTime (TimeValue raw, PQuant quantisation_to_use);
operator PQuant() const; ///< @return `shared-ptr` to the associated time grid (or time scale)
template<class FMT>
bool supports() const; ///< does our implicit time grid support building that timecode format?
template<class FMT>
typename format::Traits<FMT>::TimeCode
formatAs() const; ///< create new time code instance, then #castInto
template<class TC>
void
castInto (TC& timecode) const; ///< quantise into implicit grid, then rebuild the timecode
/** receive change message, which
might cause re-quantisation */
void accept (Mutation const&);
};
/* == implementation == */
inline
QuTime::operator PQuant() const
{
ASSERT (quantiser_);
return quantiser_;
}
template<class FMT>
inline bool
QuTime::supports() const
{
return quantiser_->supports<FMT>();
}
template<class FMT>
inline typename format::Traits<FMT>::TimeCode
QuTime::formatAs() const
{
using TC = typename format::Traits<FMT>::TimeCode;
return TC(*this);
}
/**
* @tparam TC the kind of time code to use as target
* @param timecode existing instance of that timecode, to be overwritten
* @remark this is the basic operation to convert an (internal) time value
* into a time code format. QuTime is already associated with some
* _time grid_ for quantisation, but the internal value is precise.
* This operation creates a quantised (frame aligned) transient copy,
* and uses this to adjust/modify the fields in the given timecode.
*/
template<class TC>
inline void
QuTime::castInto (TC& timecode) const
{
using Format = typename TC::Format;
REQUIRE (supports<Format>());
Format::rebuild (timecode, *quantiser_, TimeValue(*this));
}
}} // lib::time
#endif