Lumiera  0.pre.03
»edit your freedom«
timecode.cpp
Go to the documentation of this file.
1 /*
2  Timecode - implementation of fixed grid aligned time specifications
3 
4  Copyright (C) Lumiera.org
5  2010, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
31 #include "lib/time/timecode.hpp"
32 #include "lib/time/timevalue.hpp"
33 #include "lib/time/timequant.hpp"
34 #include "lib/time/formats.hpp"
35 #include "lib/time.h"
36 #include "lib/util.hpp"
37 #include "lib/util-quant.hpp"
38 
39 #include <regex>
40 #include <functional>
41 #include <boost/lexical_cast.hpp>
42 
43 using util::unConst;
44 using util::isSameObject;
45 using util::floorwrap;
46 using std::string;
47 using std::regex;
48 using std::smatch;
49 using std::regex_search;
50 using boost::lexical_cast;
51 
52 namespace lumiera {
53 namespace error {
54  LUMIERA_ERROR_DEFINE (INVALID_TIMECODE, "timecode format error, illegal value encountered");
55 }}
56 
57 namespace lib {
58 namespace time {
59  namespace error = lumiera::error;
60 
61  namespace format { /* ================= Timecode implementation details ======== */
62 
63 
64 
73  TimeValue
74  Frames::parse (string const& frameNumber, QuantR frameGrid)
75  {
76  static regex frameNr_parser{"(?:^|[^\\d\\.\\-])(\\-?\\d+)#"}; // no leading [.-\d], digit+'#'
77  smatch match; // note: ECMA regexp does not support lookbehind
78  if (regex_search (frameNumber, match, frameNr_parser))
79  return frameGrid.timeOf (lexical_cast<FrameCnt> (match[1]));
80  else
81  throw error::Invalid ("unable to parse framecount \""+frameNumber+"\""
82  , LERR_(INVALID_TIMECODE));
83  }
84 
85 
86  TimeValue
87  Smpte::parse (string const&, QuantR)
88  {
89  UNIMPLEMENTED("parsing SMPTE timecode");
90  }
91 
92 
93  TimeValue
94  Hms::parse (string const&, QuantR)
95  {
96  UNIMPLEMENTED("parse a hours:mins:secs time specification");
97  }
98 
99 
121  TimeValue
122  Seconds::parse (string const& seconds, QuantR grid)
123  {
124  static regex fracSecs_parser ("(?:^|[^\\./\\d\\-])(\\-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec");
125  //__no leading[./-\d] number [+-] number '/' number 'sec'
126 
127  #define SUB_EXPR(N) lexical_cast<int> (match[N])
128  smatch match;
129  if (regex_search (seconds, match, fracSecs_parser))
130  if (match[2].matched)
131  {
132  // complete spec with all parts
133  FSecs fractionalPart (SUB_EXPR(2), SUB_EXPR(3));
134  int fullSeconds (SUB_EXPR(1));
135  return grid.timeOf (fullSeconds + fractionalPart);
136  }
137  else
138  if (match[3].matched)
139  {
140  // only a fractional part was given
141  return grid.timeOf (FSecs (SUB_EXPR(1), SUB_EXPR(3)));
142  }
143  else
144  {
145  // just simple non-fractional seconds
146  return grid.timeOf (FSecs (SUB_EXPR(1)));
147  }
148  else
149  throw error::Invalid ("unable to parse \""+seconds+"\" as (fractional)seconds"
150  , LERR_(INVALID_TIMECODE));
151  }
152 
153 
154 
155 
159  void
160  Frames::rebuild (FrameNr& frameNr, QuantR quantiser, TimeValue const& rawTime)
161  {
162  frameNr.setValueRaw (quantiser.gridPoint (rawTime));
163  }
164 
166  TimeValue
167  Frames::evaluate (FrameNr const& frameNr, QuantR quantiser)
168  {
169  return quantiser.timeOf (frameNr);
170  }
171 
172 
177  void
178  Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime)
179  {
180  tc.clear();
181  tc.frames = quantiser.gridPoint (rawTime);
182  // will automatically wrap over to the seconds, minutes and hour fields
183  }
184 
187  TimeValue
188  Smpte::evaluate (SmpteTC const& tc, QuantR quantiser)
189  {
190  uint frameRate = tc.getFps();
191  int64_t gridPoint(tc.frames);
192  gridPoint += int64_t(tc.secs) * frameRate;
193  gridPoint += int64_t(tc.mins) * frameRate * 60;
194  gridPoint += int64_t(tc.hours) * frameRate * 60 * 60;
195  return quantiser.timeOf (tc.sgn * gridPoint);
196  }
197 
211  uint
212  Smpte::getFramerate (QuantR quantiser_, TimeValue const& rawTime)
213  {
214  FrameCnt refCnt = quantiser_.gridPoint(rawTime);
215  FrameCnt newCnt = quantiser_.gridPoint(Time(0,1) + rawTime);
216  FrameCnt effectiveFrames = newCnt - refCnt;
217  ENSURE (1000 > effectiveFrames);
218  ENSURE (0 < effectiveFrames);
219  return uint(effectiveFrames);
220  }
221 
222 
241  void
242  Smpte::applyRangeLimitStrategy (SmpteTC& tc)
243  {
244  if (tc.hours < 0)
245  tc.invertOrientation();
246  }
247  }
248 
249 
250  namespace { // Timecode implementation details
251 
252  typedef util::IDiv<int> Div;
253 
254  void
255  wrapFrames (SmpteTC* thisTC, int rawFrames)
256  {
257  Div scaleRelation = floorwrap<int> (rawFrames, thisTC->getFps());
258  thisTC->frames.setValueRaw (scaleRelation.rem);
259  thisTC->secs += scaleRelation.quot;
260  }
261 
262  void
263  wrapSeconds (SmpteTC* thisTC, int rawSecs)
264  {
265  Div scaleRelation = floorwrap (rawSecs, 60);
266  thisTC->secs.setValueRaw (scaleRelation.rem);
267  thisTC->mins += scaleRelation.quot;
268  }
269 
270  void
271  wrapMinutes (SmpteTC* thisTC, int rawMins)
272  {
273  Div scaleRelation = floorwrap (rawMins, 60);
274  thisTC->mins.setValueRaw (scaleRelation.rem);
275  thisTC->hours += scaleRelation.quot;
276  }
277 
278  void
279  wrapHours (SmpteTC* thisTC, int rawHours)
280  {
281  thisTC->hours.setValueRaw (rawHours);
282  format::Smpte::applyRangeLimitStrategy (*thisTC);
283  }
284 
285 
286  using std::bind;
287  using std::placeholders::_1;
288 
291  inline void
293  {
294  thisTC.hours.installMutator (wrapHours, thisTC);
295  thisTC.mins.installMutator (wrapMinutes, thisTC);
296  thisTC.secs.installMutator (wrapSeconds, thisTC);
297  thisTC.frames.installMutator(wrapFrames, thisTC);
298  }
299 
300  }//(End)implementation details
301 
302 
304  FrameNr::FrameNr (QuTime const& quantisedTime)
305  : TCode(quantisedTime)
306  , CountVal()
307  {
308  quantisedTime.castInto (*this);
309  }
310 
311 
313  SmpteTC::SmpteTC (QuTime const& quantisedTime)
314  : TCode(quantisedTime)
315  , effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime))
316  {
318  quantisedTime.castInto (*this);
319  }
320 
321 
322  SmpteTC::SmpteTC (SmpteTC const& o)
323  : TCode(o)
324  , effectiveFramerate_(o.effectiveFramerate_)
325  {
327  sgn = o.sgn;
328  hours = o.hours;
329  mins = o.mins;
330  secs = o.secs;
331  frames = o.frames;
332  }
333 
334 
335  SmpteTC&
336  SmpteTC::operator= (SmpteTC const& o)
337  {
338  if (!isSameObject (*this, o))
339  {
340  TCode::operator= (o);
341  effectiveFramerate_ = o.effectiveFramerate_;
342  sgn = o.sgn;
343  hours = o.hours;
344  mins = o.mins;
345  secs = o.secs;
346  frames = o.frames;
347  }
348  return *this;
349  }
350 
351 
353  HmsTC::HmsTC (QuTime const& quantisedTime)
354  : TCode(quantisedTime)
355 // : tpoint_(quantisedTime) /////////////////////////////TODO bullshit
356  { }
357 
358 
360  Secs::Secs (QuTime const& quantisedTime)
361  : TCode(quantisedTime)
362 // : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) /////////////TODO bullshit
363  { }
364 
365 
366 
367  void
368  SmpteTC::clear()
369  {
370  frames.setValueRaw(0);
371  secs.setValueRaw (0);
372  mins.setValueRaw (0);
373  hours.setValueRaw (0);
374  sgn.setValueRaw (+1);
375  }
376 
377 
378  void
379  SmpteTC::rebuild()
380  {
381  TimeValue point = Format::evaluate (*this, *quantiser_);
382  Format::rebuild (*this, *quantiser_, point);
383  }
384 
385 
397  void
398  SmpteTC::invertOrientation()
399  {
400  int fr (getFps());
401  int f (fr - frames); // revert orientation
402  int s (60 - secs); // of the components
403  int m (60 - mins); //
404  int h = -hours; // assumed to be negative
405  sgn *= -1; // flip sign field
406 
407  if (f < fr) --s; else f -= fr;
408  if (s < 60) --m; else s -= 60;
409  if (m < 60) --h; else m -= 60;
410 
411  hours.setValueRaw(h);
412  mins = m; // invoking setters
413  secs = s; // ensures normalisation
414  frames = f;
415  }
416 
417 
418  uint
419  SmpteTC::getFps() const
420  {
421  return effectiveFramerate_;
422  }
423 
424 
425  string
426  SmpteTC::show() const
427  {
428  string tc;
429  tc.reserve(15);
430  tc += sgn.show();
431  tc += hours.show();
432  tc += ':';
433  tc += mins.show();
434  tc += ':';
435  tc += secs.show();
436  tc += ':';
437  tc += frames.show();
438  return tc;
439  }
440 
441  SmpteTC&
442  SmpteTC::operator++ ()
443  {
444  frames += sgn;
445  return *this;
446  }
447 
448  SmpteTC&
449  SmpteTC::operator-- ()
450  {
451  frames -= sgn;
452  return *this;
453  }
454 
456  int
457  HmsTC::getSecs() const
458  {
459  return lumiera_time_seconds (tpoint_);
460  }
461 
463  int
464  HmsTC::getMins() const
465  {
466  return lumiera_time_minutes (tpoint_);
467  }
468 
470  int
471  HmsTC::getHours() const
472  {
473  return lumiera_time_hours (tpoint_);
474  }
475 
477  double
478  HmsTC::getMillis() const
479  {
480  TODO ("Frame-Quantisation");
481  return lumiera_time_millis (tpoint_);
482  }
483 
489 }} // lib::time
490 
int lumiera_time_hours(gavl_time_t time)
Extract the hour part of given time.
Definition: time.cpp:544
Classical Timecode value reminiscent to SMPTE format.
Definition: timecode.hpp:150
Common functions for handling of time values.
void installMutator(FUN mutate, THIS &self)
install an external functor to be applied on any new digxel value.
Definition: digxel.hpp:252
void setupComponentNormalisation(SmpteTC &thisTC)
bind the individual Digxel mutation functors to normalise raw component values
Definition: timecode.cpp:292
IDiv< I > floorwrap(I num, I den)
scale wrapping operation.
Definition: util-quant.hpp:121
int lumiera_time_millis(gavl_time_t time)
Extract the milliseconds part of given time.
Definition: time.cpp:562
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
void castInto(TC &timecode) const
quantise into implicit grid, then rebuild the timecode
Definition: timequant.hpp:165
Timecode handling library This header defines the foundation interface TCode to represent a grid alig...
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
A number element for building structured numeric displays.
Definition: digxel.hpp:226
Utilities for quantisation (grid alignment) and comparisons.
boost::rational< int64_t > FSecs
rational representation of fractional seconds
Definition: timevalue.hpp:229
void invertOrientation()
flip the orientation of min, sec, and frames.
Definition: timecode.cpp:398
Support library to represent grid-aligned time specifications This is part of Lumiera&#39;s time and time...
Interface: fixed format timecode specification.
Definition: timecode.hpp:65
Lumiera public interface.
Definition: advice.cpp:113
Definition of time code formats This header is part of the Lumiera time and timecode handling library...
int lumiera_time_seconds(gavl_time_t time)
Extract the seconds part of given time.
Definition: time.cpp:556
int64_t FrameCnt
relative framecount or frame number.
Definition: digxel.hpp:321
A frame counting timecode value.
Definition: timecode.hpp:105
a family of time value like entities and their relationships.
int lumiera_time_minutes(gavl_time_t time)
Extract the minute part of given time.
Definition: time.cpp:550
basic constant internal time value.
Definition: timevalue.hpp:142
grid aligned time specification, referring to a specific scale.
Definition: timequant.hpp:99
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:80
bool isSameObject(A const &a, B const &b)
compare plain object identity, bypassing any custom comparison operators.
Definition: util.hpp:372