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)
5  2010, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
14 
22 #include "lib/time/timecode.hpp"
23 #include "lib/time/timevalue.hpp"
24 #include "lib/time/timequant.hpp"
25 #include "lib/time/formats.hpp"
26 #include "lib/time.h"
27 #include "lib/util.hpp"
28 #include "lib/util-quant.hpp"
29 
30 #include <regex>
31 #include <functional>
32 #include <boost/lexical_cast.hpp>
33 
34 using util::unConst;
35 using util::isSameObject;
36 using util::floorwrap;
37 using std::string;
38 using std::regex;
39 using std::smatch;
40 using std::regex_search;
41 using boost::lexical_cast;
42 
43 namespace lumiera {
44 namespace error {
45  LUMIERA_ERROR_DEFINE (INVALID_TIMECODE, "timecode format error, illegal value encountered");
46 }}
47 
48 namespace lib {
49 namespace time {
50  namespace error = lumiera::error;
51 
52  namespace format { /* ================= Timecode implementation details ======== */
53 
54 
55 
64  TimeValue
65  Frames::parse (string const& frameNumber, QuantR frameGrid)
66  {
67  static regex frameNr_parser{"(?:^|[^\\d\\.\\-])(\\-?\\d+)#"}; // no leading [.-\d], digit+'#'
68  smatch match; // note: ECMA regexp does not support lookbehind
69  if (regex_search (frameNumber, match, frameNr_parser))
70  return frameGrid.timeOf (lexical_cast<FrameCnt> (match[1]));
71  else
72  throw error::Invalid ("unable to parse framecount \""+frameNumber+"\""
73  , LERR_(INVALID_TIMECODE));
74  }
75 
76 
77  TimeValue
78  Smpte::parse (string const&, QuantR)
79  {
80  UNIMPLEMENTED("parsing SMPTE timecode");
81  }
82 
83 
84  TimeValue
85  Hms::parse (string const&, QuantR)
86  {
87  UNIMPLEMENTED("parse a hours:mins:secs time specification");
88  }
89 
90 
112  TimeValue
113  Seconds::parse (string const& seconds, QuantR grid)
114  {
115  static regex fracSecs_parser ("(?:^|[^\\./\\d\\-])(\\-?\\d+)(?:([\\-\\+]\\d+)?/(\\d+))?sec");
116  //__no leading[./-\d] number [+-] number '/' number 'sec'
117 
118  #define SUB_EXPR(N) lexical_cast<int> (match[N])
119  smatch match;
120  if (regex_search (seconds, match, fracSecs_parser))
121  if (match[2].matched)
122  {
123  // complete spec with all parts
124  FSecs fractionalPart (SUB_EXPR(2), SUB_EXPR(3));
125  int fullSeconds (SUB_EXPR(1));
126  return grid.timeOf (fullSeconds + fractionalPart);
127  }
128  else
129  if (match[3].matched)
130  {
131  // only a fractional part was given
132  return grid.timeOf (FSecs (SUB_EXPR(1), SUB_EXPR(3)));
133  }
134  else
135  {
136  // just simple non-fractional seconds
137  return grid.timeOf (FSecs (SUB_EXPR(1)));
138  }
139  else
140  throw error::Invalid ("unable to parse \""+seconds+"\" as (fractional)seconds"
141  , LERR_(INVALID_TIMECODE));
142  }
143 
144 
145 
146 
150  void
151  Frames::rebuild (FrameNr& frameNr, QuantR quantiser, TimeValue const& rawTime)
152  {
153  frameNr.setValueRaw (quantiser.gridPoint (rawTime));
154  }
155 
157  TimeValue
158  Frames::evaluate (FrameNr const& frameNr, QuantR quantiser)
159  {
160  return quantiser.timeOf (frameNr);
161  }
162 
163 
168  void
169  Smpte::rebuild (SmpteTC& tc, QuantR quantiser, TimeValue const& rawTime)
170  {
171  tc.clear();
172  tc.frames = quantiser.gridPoint (rawTime);
173  // will automatically wrap over to the seconds, minutes and hour fields
174  }
175 
178  TimeValue
179  Smpte::evaluate (SmpteTC const& tc, QuantR quantiser)
180  {
181  uint frameRate = tc.getFps();
182  int64_t gridPoint(tc.frames);
183  gridPoint += int64_t(tc.secs) * frameRate;
184  gridPoint += int64_t(tc.mins) * frameRate * 60;
185  gridPoint += int64_t(tc.hours) * frameRate * 60 * 60;
186  return quantiser.timeOf (tc.sgn * gridPoint);
187  }
188 
202  uint
203  Smpte::getFramerate (QuantR quantiser_, TimeValue const& rawTime)
204  {
205  FrameCnt refCnt = quantiser_.gridPoint(rawTime);
206  FrameCnt newCnt = quantiser_.gridPoint(Time(0,1) + rawTime);
207  FrameCnt effectiveFrames = newCnt - refCnt;
208  ENSURE (1000 > effectiveFrames);
209  ENSURE (0 < effectiveFrames);
210  return uint(effectiveFrames);
211  }
212 
213 
232  void
233  Smpte::applyRangeLimitStrategy (SmpteTC& tc)
234  {
235  if (tc.hours < 0)
236  tc.invertOrientation();
237  }
238  }
239 
240 
241  namespace { // Timecode implementation details
242 
243  typedef util::IDiv<int> Div;
244 
245  void
246  wrapFrames (SmpteTC* thisTC, int rawFrames)
247  {
248  Div scaleRelation = floorwrap<int> (rawFrames, thisTC->getFps());
249  thisTC->frames.setValueRaw (scaleRelation.rem);
250  thisTC->secs += scaleRelation.quot;
251  }
252 
253  void
254  wrapSeconds (SmpteTC* thisTC, int rawSecs)
255  {
256  Div scaleRelation = floorwrap (rawSecs, 60);
257  thisTC->secs.setValueRaw (scaleRelation.rem);
258  thisTC->mins += scaleRelation.quot;
259  }
260 
261  void
262  wrapMinutes (SmpteTC* thisTC, int rawMins)
263  {
264  Div scaleRelation = floorwrap (rawMins, 60);
265  thisTC->mins.setValueRaw (scaleRelation.rem);
266  thisTC->hours += scaleRelation.quot;
267  }
268 
269  void
270  wrapHours (SmpteTC* thisTC, int rawHours)
271  {
272  thisTC->hours.setValueRaw (rawHours);
273  format::Smpte::applyRangeLimitStrategy (*thisTC);
274  }
275 
276 
277  using std::bind;
278  using std::placeholders::_1;
279 
282  inline void
284  {
285  thisTC.hours.installMutator (wrapHours, thisTC);
286  thisTC.mins.installMutator (wrapMinutes, thisTC);
287  thisTC.secs.installMutator (wrapSeconds, thisTC);
288  thisTC.frames.installMutator(wrapFrames, thisTC);
289  }
290 
291  }//(End)implementation details
292 
293 
295  FrameNr::FrameNr (QuTime const& quantisedTime)
296  : TCode(quantisedTime)
297  , CountVal()
298  {
299  quantisedTime.castInto (*this);
300  }
301 
302 
304  SmpteTC::SmpteTC (QuTime const& quantisedTime)
305  : TCode(quantisedTime)
306  , effectiveFramerate_(Format::getFramerate (*quantiser_, quantisedTime))
307  {
309  quantisedTime.castInto (*this);
310  }
311 
312 
313  SmpteTC::SmpteTC (SmpteTC const& o)
314  : TCode(o)
315  , effectiveFramerate_(o.effectiveFramerate_)
316  {
318  sgn = o.sgn;
319  hours = o.hours;
320  mins = o.mins;
321  secs = o.secs;
322  frames = o.frames;
323  }
324 
325 
326  SmpteTC&
327  SmpteTC::operator= (SmpteTC const& o)
328  {
329  if (not isSameObject (*this, o))
330  {
331  TCode::operator= (o);
332  effectiveFramerate_ = o.effectiveFramerate_;
333  sgn = o.sgn;
334  hours = o.hours;
335  mins = o.mins;
336  secs = o.secs;
337  frames = o.frames;
338  }
339  return *this;
340  }
341 
342 
344  HmsTC::HmsTC (QuTime const& quantisedTime)
345  : TCode(quantisedTime)
346 // : tpoint_(quantisedTime) //////////////////////////////////////////////////////////////////////TICKET #736 implement HMS format
347  { }
348 
349 
351  Secs::Secs (QuTime const& quantisedTime)
352  : TCode(quantisedTime)
353 // : sec_(TimeVar(quantisedTime) / GAVL_TIME_SCALE) //////////////////////////////////////////////////////TICKET #736 implement Seconds format
354  { }
355 
356 
357 
358  void
359  SmpteTC::clear()
360  {
361  frames.setValueRaw(0);
362  secs.setValueRaw (0);
363  mins.setValueRaw (0);
364  hours.setValueRaw (0);
365  sgn.setValueRaw (+1);
366  }
367 
368 
369  void
370  SmpteTC::rebuild()
371  {
372  TimeValue point = Format::evaluate (*this, *quantiser_);
373  Format::rebuild (*this, *quantiser_, point);
374  }
375 
376 
388  void
389  SmpteTC::invertOrientation()
390  {
391  int fr (getFps());
392  int f (fr - frames); // revert orientation
393  int s (60 - secs); // of the components
394  int m (60 - mins); //
395  int h = -hours; // assumed to be negative
396  sgn *= -1; // flip sign field
397 
398  if (f < fr) --s; else f -= fr;
399  if (s < 60) --m; else s -= 60;
400  if (m < 60) --h; else m -= 60;
401 
402  hours.setValueRaw(h);
403  mins = m; // invoking setters
404  secs = s; // ensures normalisation
405  frames = f;
406  }
407 
408 
409  uint
410  SmpteTC::getFps() const
411  {
412  return effectiveFramerate_;
413  }
414 
415 
416  string
417  SmpteTC::show() const
418  {
419  string tc;
420  tc.reserve(15);
421  tc += sgn.show();
422  tc += hours.show();
423  tc += ':';
424  tc += mins.show();
425  tc += ':';
426  tc += secs.show();
427  tc += ':';
428  tc += frames.show();
429  return tc;
430  }
431 
432  SmpteTC&
433  SmpteTC::operator++ ()
434  {
435  frames += sgn;
436  return *this;
437  }
438 
439  SmpteTC&
440  SmpteTC::operator-- ()
441  {
442  frames -= sgn;
443  return *this;
444  }
445 
447  int
448  HmsTC::getSecs() const
449  {
450  return lumiera_time_seconds (tpoint_);
451  }
452 
454  int
455  HmsTC::getMins() const
456  {
457  return lumiera_time_minutes (tpoint_);
458  }
459 
461  int
462  HmsTC::getHours() const
463  {
464  return lumiera_time_hours (tpoint_);
465  }
466 
468  double
469  HmsTC::getMillis() const
470  {
471  TODO ("Frame-Quantisation");
472  return lumiera_time_millis (tpoint_);
473  }
474 
480 }} // lib::time
481 
int lumiera_time_hours(gavl_time_t time)
Extract the hour part of given time.
Definition: time.cpp:535
Classical Timecode value reminiscent to SMPTE format.
Definition: timecode.hpp:141
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:243
void setupComponentNormalisation(SmpteTC &thisTC)
bind the individual Digxel mutation functors to normalise raw component values
Definition: timecode.cpp:283
IDiv< I > floorwrap(I num, I den)
scale wrapping operation.
Definition: util-quant.hpp:119
int lumiera_time_millis(gavl_time_t time)
Extract the milliseconds part of given time.
Definition: time.cpp:553
Implementation namespace for support and library code.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
void castInto(TC &timecode) const
quantise into implicit grid, then rebuild the timecode
Definition: timequant.hpp:156
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:217
Utilities for quantisation (grid alignment) and comparisons.
boost::rational< int64_t > FSecs
rational representation of fractional seconds
Definition: timevalue.hpp:220
void invertOrientation()
flip the orientation of min, sec, and frames.
Definition: timecode.cpp:389
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:56
Lumiera public interface.
Definition: advice.cpp:104
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:547
int64_t FrameCnt
relative framecount or frame number.
Definition: digxel.hpp:312
A frame counting timecode value.
Definition: timecode.hpp:96
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:541
basic constant internal time value.
Definition: timevalue.hpp:133
grid aligned time specification, referring to a specific scale.
Definition: timequant.hpp:90
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.
Definition: error.h:71
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee&#39;s memory identities. ...
Definition: util.hpp:421