2011-01-02 01:21:39 +01:00
|
|
|
/*
|
|
|
|
|
Timecode - implementation of fixed grid aligned time specifications
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/lumitime.hpp"
|
|
|
|
|
#include "lib/time/timecode.hpp"
|
2011-01-05 18:15:44 +01:00
|
|
|
#include "lib/time/timevalue.hpp"
|
2011-01-02 01:21:39 +01:00
|
|
|
#include "lib/time/timequant.hpp"
|
|
|
|
|
#include "lib/time/formats.hpp"
|
2011-01-05 18:15:44 +01:00
|
|
|
#include "lib/time.h"
|
2011-01-18 05:01:25 +01:00
|
|
|
#include "lib/util.hpp"
|
2011-01-05 18:15:44 +01:00
|
|
|
|
2011-01-21 11:42:29 +01:00
|
|
|
#include <tr1/functional>
|
2011-01-05 18:15:44 +01:00
|
|
|
|
2011-01-02 01:21:39 +01:00
|
|
|
using std::string;
|
2011-01-20 22:17:25 +01:00
|
|
|
using util::unConst;
|
2011-01-21 16:22:01 +01:00
|
|
|
using util::isSameObject;
|
2011-01-02 01:21:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace time {
|
|
|
|
|
|
|
|
|
|
|
2011-01-16 15:41:34 +01:00
|
|
|
TCode::~TCode() { } // emit VTable here....
|
2011-01-02 01:21:39 +01:00
|
|
|
|
2011-01-13 23:56:52 +01:00
|
|
|
namespace format {
|
|
|
|
|
|
|
|
|
|
/** build up a frame count
|
|
|
|
|
* by quantising the given time value
|
|
|
|
|
*/
|
|
|
|
|
void
|
2011-01-15 00:52:02 +01:00
|
|
|
Frames::rebuild (FrameNr& framecnt, QuantR quantiser, TimeValue const& rawTime)
|
2011-01-13 23:56:52 +01:00
|
|
|
{
|
|
|
|
|
framecnt.setValueRaw(quantiser.gridPoint (rawTime));
|
|
|
|
|
}
|
2011-01-14 05:33:50 +01:00
|
|
|
|
|
|
|
|
/** calculate the time point denoted by this frame count */
|
|
|
|
|
TimeValue
|
2011-01-15 00:52:02 +01:00
|
|
|
Frames::evaluate (FrameNr const& framecnt, QuantR quantiser)
|
2011-01-14 05:33:50 +01:00
|
|
|
{
|
2011-01-15 00:52:02 +01:00
|
|
|
return quantiser.timeOf (framecnt);
|
2011-01-14 05:33:50 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-18 05:01:25 +01:00
|
|
|
|
2011-01-20 13:21:14 +01:00
|
|
|
/** build up a SMPTE timecode
|
|
|
|
|
* by quantising the given time value and then splitting it
|
2011-01-21 11:42:29 +01:00
|
|
|
* into hours, minutes, seconds and frame offset.
|
2011-01-18 05:01:25 +01:00
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime)
|
|
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
tc.frames = quantiser.gridPoint (rawTime);
|
|
|
|
|
// will automatically wrap over to the secs, minutes and hour fields
|
2011-01-18 05:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-20 13:21:14 +01:00
|
|
|
/** calculate the time point denoted by this SMPTE timecode,
|
|
|
|
|
* by summing up the timecode's components */
|
2011-01-18 05:01:25 +01:00
|
|
|
TimeValue
|
|
|
|
|
Smpte::evaluate (SmpteTC const& tc, QuantR quantiser)
|
|
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
uint frameRate = tc.getFps();
|
|
|
|
|
long gridPoint = tc.frames
|
|
|
|
|
+ tc.secs * frameRate
|
|
|
|
|
+ tc.mins * frameRate * 60
|
|
|
|
|
+ tc.hours * frameRate * 60 * 60;
|
|
|
|
|
return quantiser.timeOf (gridPoint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** yield the Framerate in effect at that point.
|
|
|
|
|
* Especially Timecode in SMPTE format exposes a "frames" field
|
|
|
|
|
* to contain the remainder of frames in addition to the h:m:s value.
|
|
|
|
|
* Obviously this value has to be kept below the number of frames for
|
|
|
|
|
* a full second and wrap around accordingly.
|
|
|
|
|
* @note SMPTE format assumes this framerate to be constant. Actually,
|
|
|
|
|
* in this implementation the value returned here neither needs
|
|
|
|
|
* to be constant (independent of the given rawTime), nor does
|
|
|
|
|
* it need to be the actual framerate used by the quantiser.
|
|
|
|
|
* Especially in case of NTSC drop-frame, the timecode
|
|
|
|
|
* uses 30fps here, while the quantisation uses 29.97
|
|
|
|
|
* @todo this design just doesn't feel quite right...
|
|
|
|
|
*/
|
|
|
|
|
uint
|
|
|
|
|
Smpte::getFramerate (QuantR quantiser_, TimeValue const& rawTime)
|
|
|
|
|
{
|
|
|
|
|
long refCnt = quantiser_.gridPoint(rawTime);
|
|
|
|
|
long newCnt = quantiser_.gridPoint(Time(0.5,1) + rawTime);
|
|
|
|
|
long effectiveFrames = newCnt - refCnt;
|
|
|
|
|
ENSURE (1000 > effectiveFrames);
|
|
|
|
|
ENSURE (0 < effectiveFrames);
|
|
|
|
|
return uint(effectiveFrames);
|
2011-01-18 05:01:25 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-13 23:56:52 +01:00
|
|
|
}
|
2011-01-05 18:15:44 +01:00
|
|
|
|
2011-01-20 13:21:14 +01:00
|
|
|
namespace { // Timecode implementation details
|
|
|
|
|
|
2011-01-21 11:42:29 +01:00
|
|
|
typedef util::IDiv<int> Div;
|
2011-01-20 13:21:14 +01:00
|
|
|
|
|
|
|
|
int
|
2011-01-21 16:22:01 +01:00
|
|
|
wrapFrames (SmpteTC* thisTC, int rawFrames)
|
2011-01-20 13:21:14 +01:00
|
|
|
{
|
2011-01-21 16:22:01 +01:00
|
|
|
Div scaleRelation(rawFrames, thisTC->getFps());
|
|
|
|
|
thisTC->secs += scaleRelation.quot;
|
2011-01-20 22:17:25 +01:00
|
|
|
return scaleRelation.rem;
|
2011-01-20 13:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2011-01-21 16:22:01 +01:00
|
|
|
wrapSeconds (SmpteTC* thisTC, int rawSecs)
|
2011-01-20 13:21:14 +01:00
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
Div scaleRelation(rawSecs, 60);
|
2011-01-21 16:22:01 +01:00
|
|
|
thisTC->mins += scaleRelation.quot;
|
2011-01-20 22:17:25 +01:00
|
|
|
return scaleRelation.rem;
|
2011-01-20 13:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2011-01-21 16:22:01 +01:00
|
|
|
wrapMinutes (SmpteTC* thisTC, int rawMins)
|
2011-01-20 13:21:14 +01:00
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
Div scaleRelation(rawMins, 60);
|
2011-01-21 16:22:01 +01:00
|
|
|
thisTC->hours += scaleRelation.quot;
|
2011-01-20 22:17:25 +01:00
|
|
|
return scaleRelation.rem;
|
2011-01-20 13:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2011-01-21 16:22:01 +01:00
|
|
|
wrapHours (SmpteTC* thisTC, int rawHours)
|
2011-01-20 13:21:14 +01:00
|
|
|
{
|
2011-01-21 16:22:01 +01:00
|
|
|
thisTC->sgn = rawHours;
|
2011-01-20 22:17:25 +01:00
|
|
|
return rawHours;
|
2011-01-20 13:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-21 16:22:01 +01:00
|
|
|
using std::tr1::bind;
|
|
|
|
|
using std::tr1::placeholders::_1;
|
|
|
|
|
|
|
|
|
|
/** bind the individual Digxel mutation functors
|
|
|
|
|
* to normalise raw component values */
|
|
|
|
|
inline void
|
|
|
|
|
setupComponentNormalisation (SmpteTC * thisTC)
|
|
|
|
|
{
|
|
|
|
|
thisTC->hours.mutator = bind (wrapHours, thisTC, _1 );
|
|
|
|
|
thisTC->mins.mutator = bind (wrapMinutes, thisTC, _1 );
|
|
|
|
|
thisTC->secs.mutator = bind (wrapSeconds, thisTC, _1 );
|
|
|
|
|
thisTC->frames.mutator = bind (wrapFrames, thisTC, _1 );
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-20 13:21:14 +01:00
|
|
|
}//(End)implementation details
|
2011-01-05 18:15:44 +01:00
|
|
|
|
2011-01-14 05:33:50 +01:00
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
FrameNr::FrameNr (QuTime const& quantisedTime)
|
|
|
|
|
: TCode(quantisedTime)
|
|
|
|
|
, CountVal()
|
|
|
|
|
{
|
|
|
|
|
quantisedTime.castInto (*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-05 18:15:44 +01:00
|
|
|
/** */
|
|
|
|
|
SmpteTC::SmpteTC (QuTime const& quantisedTime)
|
2011-01-14 05:33:50 +01:00
|
|
|
: TCode(quantisedTime)
|
2011-01-21 11:42:29 +01:00
|
|
|
, effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime))
|
|
|
|
|
{
|
2011-01-21 16:22:01 +01:00
|
|
|
setupComponentNormalisation (this);
|
2011-01-21 11:42:29 +01:00
|
|
|
quantisedTime.castInto (*this);
|
|
|
|
|
}
|
2011-01-05 18:15:44 +01:00
|
|
|
|
|
|
|
|
|
2011-01-21 16:22:01 +01:00
|
|
|
SmpteTC::SmpteTC (SmpteTC const& o)
|
|
|
|
|
: TCode(o)
|
|
|
|
|
, effectiveFramerate_(o.effectiveFramerate_)
|
|
|
|
|
{
|
|
|
|
|
setupComponentNormalisation (this);
|
|
|
|
|
sgn = o.sgn;
|
|
|
|
|
hours = o.hours;
|
|
|
|
|
mins = o.mins;
|
|
|
|
|
secs = o.secs;
|
|
|
|
|
frames = o.frames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SmpteTC&
|
|
|
|
|
SmpteTC::operator= (SmpteTC const& o)
|
|
|
|
|
{
|
|
|
|
|
if (!isSameObject (*this, o))
|
|
|
|
|
{
|
|
|
|
|
TCode::operator= (o);
|
|
|
|
|
effectiveFramerate_ = o.effectiveFramerate_;
|
|
|
|
|
sgn = o.sgn;
|
|
|
|
|
hours = o.hours;
|
|
|
|
|
mins = o.mins;
|
|
|
|
|
secs = o.secs;
|
|
|
|
|
frames = o.frames;
|
|
|
|
|
}
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-01-05 18:15:44 +01:00
|
|
|
/** */
|
|
|
|
|
HmsTC::HmsTC (QuTime const& quantisedTime)
|
2011-01-14 05:33:50 +01:00
|
|
|
: TCode(quantisedTime)
|
|
|
|
|
// : tpoint_(quantisedTime) /////////////////////////////TODO bullshit
|
2011-01-05 18:15:44 +01:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
Secs::Secs (QuTime const& quantisedTime)
|
2011-01-14 05:33:50 +01:00
|
|
|
: TCode(quantisedTime)
|
|
|
|
|
// : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) /////////////TODO bullshit
|
2011-01-05 18:15:44 +01:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-01-18 05:01:25 +01:00
|
|
|
void
|
|
|
|
|
SmpteTC::rebuild() const
|
2011-01-05 18:15:44 +01:00
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
TimeValue point = Format::evaluate (*this, *quantiser_);
|
|
|
|
|
Format::rebuild (unConst(*this), *quantiser_, point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint
|
|
|
|
|
SmpteTC::getFps() const
|
|
|
|
|
{
|
|
|
|
|
return effectiveFramerate_; //////////////////////////////////TODO better design. Shouldn't Format::getFramerate(QuantR, TimeValue) be moved here?
|
2011-01-05 18:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-18 05:01:25 +01:00
|
|
|
|
|
|
|
|
string
|
|
|
|
|
SmpteTC::show() const
|
2011-01-05 18:15:44 +01:00
|
|
|
{
|
2011-01-18 05:01:25 +01:00
|
|
|
string tc;
|
|
|
|
|
tc.reserve(15);
|
|
|
|
|
tc += sgn.show();
|
|
|
|
|
tc += hours.show();
|
|
|
|
|
tc += ':';
|
|
|
|
|
tc += mins.show();
|
|
|
|
|
tc += ':';
|
|
|
|
|
tc += secs.show();
|
|
|
|
|
tc += ':';
|
|
|
|
|
tc += frames.show();
|
|
|
|
|
return tc;
|
2011-01-05 18:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-18 05:01:25 +01:00
|
|
|
SmpteTC&
|
|
|
|
|
SmpteTC::operator++ ()
|
2011-01-05 18:15:44 +01:00
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
++frames;
|
|
|
|
|
return *this;
|
2011-01-05 18:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
2011-01-18 05:01:25 +01:00
|
|
|
SmpteTC&
|
|
|
|
|
SmpteTC::operator-- ()
|
2011-01-05 18:15:44 +01:00
|
|
|
{
|
2011-01-21 11:42:29 +01:00
|
|
|
--frames;
|
|
|
|
|
return *this;
|
2011-01-05 18:15:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
int
|
|
|
|
|
HmsTC::getSecs() const
|
|
|
|
|
{
|
|
|
|
|
return lumiera_time_seconds (tpoint_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
int
|
|
|
|
|
HmsTC::getMins() const
|
|
|
|
|
{
|
|
|
|
|
return lumiera_time_minutes (tpoint_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
int
|
|
|
|
|
HmsTC::getHours() const
|
|
|
|
|
{
|
|
|
|
|
return lumiera_time_hours (tpoint_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
double
|
|
|
|
|
HmsTC::getMillis() const
|
|
|
|
|
{
|
|
|
|
|
TODO ("Frame-Quantisation");
|
|
|
|
|
return lumiera_time_millis (tpoint_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** */
|
|
|
|
|
|
2011-01-02 01:21:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // lib::time
|
|
|
|
|
|