Lumiera  0.pre.03
»edit your freedom«
time.cpp
Go to the documentation of this file.
1 /*
2  Time - Lumiera time handling foundation
3 
4  Copyright (C) Lumiera.org
5  2008, Christian Thaeter <ct@pipapo.org>
6  2010, Stefan Kangas <skangas@skangas.se>
7  2011, Hermann Vosseler <Ichthyostega@web.de>
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of
12  the License, or (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 
23 * *****************************************************/
24 
25 
46 #include "lib/error.hpp"
47 #include "lib/time.h"
48 #include "lib/time/timevalue.hpp"
49 #include "lib/rational.hpp"
50 #include "lib/util-quant.hpp"
51 #include "lib/format-string.hpp"
52 
53 extern "C" {
54 #include "lib/tmpbuf.h"
55 }
56 
57 #include <math.h>
58 #include <limits>
59 #include <string>
60 #include <sstream>
61 #include <boost/rational.hpp>
62 #include <boost/lexical_cast.hpp>
63 
64 using std::string;
65 using util::floordiv;
66 using lib::time::FSecs;
68 using boost::rational_cast;
69 using boost::lexical_cast;
70 
71 
72 
73 namespace error = lumiera::error;
74 
75 
76 namespace lib {
77 namespace meta {
78  extern const std::string FAILURE_INDICATOR;
79 }
80 namespace time {
81 
82 
83  const gavl_time_t TimeValue::SCALE = GAVL_TIME_SCALE;
84 
85 
87  const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<gavl_time_t>::max() / 30) );
88  const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
89  const Time Time::ZERO;
90 
91  const Time Time::ANYTIME(Time::MIN);
92  const Time Time::NEVER (Time::MAX);
93 
94  const Offset Offset::ZERO (Time::ZERO);
95 
96  const FSecs FSEC_MAX{std::numeric_limits<int64_t>::max() / lib::time::TimeValue::SCALE};
97 
98  Literal DIAGNOSTIC_FORMAT{"%s%01d:%02d:%02d.%03d"};
99 
100 
105 #define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000)
106 
107 
118  Time::Time ( long millis
119  , uint secs
120  , uint mins
121  , uint hours
122  )
123  : TimeValue(lumiera_build_time (millis,secs,mins,hours))
124  { }
125 
126 
131  Time::Time (FSecs const& fractionalSeconds)
132  : TimeValue(lumiera_rational_to_time (fractionalSeconds))
133  { }
134 
135  Offset::Offset (FSecs const& delta_in_secs)
136  : TimeValue{buildRaw_(symmetricLimit (lumiera_rational_to_time (delta_in_secs)
137  ,Duration::MAX))}
138  { }
139 
140 
144  TimeValue::operator string() const
145  {
146  gavl_time_t time = t_;
147  int64_t millis, seconds;
148  bool negative = (time < 0);
149 
150  if (negative) time = -time;
151  time /= TIME_SCALE_MS;
152  millis = time % 1000;
153  seconds = time / 1000;
154 
155  return string (negative ? "-" : "")
156  + (seconds>0 or time==0? lexical_cast<string> (seconds)+"s" : "")
157  + (millis>0? lexical_cast<string> (millis)+"ms" : "")
158  ;
159  }
160 
169  Time::operator string() const
170  {
171  gavl_time_t time = t_;
172  int millis, seconds, minutes, hours;
173  bool negative = (time < 0);
174 
175  if (negative)
176  time = -time;
177 
178  time /= TIME_SCALE_MS;
179  millis = time % 1000;
180  time /= 1000;
181  seconds = time % 60;
182  time /= 60;
183  minutes = time % 60;
184  time /= 60;
185  hours = time;
186 
187  return util::_Fmt{string(DIAGNOSTIC_FORMAT)}
188  % (negative? "-":"")
189  % hours
190  % minutes
191  % seconds
192  % millis;
193  }
194 
195 
196  Offset::operator string() const
197  {
198  return (t_< 0? "" : "∆")
199  + TimeValue::operator string();
200  }
201 
202  Duration::operator string() const
203  {
204  return "≺"+TimeValue::operator string()+"≻";
205  }
206 
207  TimeSpan::operator string() const
208  {
209  return string (start())
210  + string (duration());
211  }
212 
213 
214  namespace {
215  template<typename RAT>
216  string
217  renderFraction (RAT const& frac, Literal postfx) noexcept
218  try {
219  std::ostringstream buffer;
220  if (1 == frac.denominator() or 0 == frac.numerator())
221  buffer << frac.numerator() << postfx;
222  else
223  buffer << frac <<postfx;
224  return buffer.str();
225  }
226  catch(...)
227  { return meta::FAILURE_INDICATOR; }
228  }
229 
231  FrameRate::operator string() const
232  {
233  return renderFraction (*this, "FPS");
234  }
235 
236 
237 
240  TimeValue
241  TimeValue::buildRaw_ (gavl_time_t raw)
242  {
243  return reinterpret_cast<TimeValue const&> (raw);
244  }
245 
246 
247 
249  const FrameRate FrameRate::PAL (25);
250  const FrameRate FrameRate::NTSC (30000,1001);
251 
252  const FrameRate FrameRate::HALTED (1,std::numeric_limits<int>::max());
253 
254 
257  Duration
259  {
260  if (0 == *this)
261  throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
262  , error::LUMIERA_ERROR_BOTTOM_VALUE);
263 
264  return Duration (1, *this);
265  }
266 
267 
268 
270  Offset
271  Offset::stretchedByRationalFactor (boost::rational<int64_t> factor) const
272  {
273  boost::rational<int64_t> distance (this->t_);
274  distance *= factor;
275  gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
276  return Offset{buildRaw_(microTicks)};
277  }
278 
279 
280  Offset
281  Offset::stretchedByFloatFactor (double factor) const
282  {
283  double distance(this->t_);
284  distance *= factor;
285  gavl_time_t microTicks = floor (distance);
286  return Offset{buildRaw_(microTicks)};
287  }
288 
289 
291  Offset::Offset (FrameCnt count, FrameRate const& fps)
293  count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
294  :_raw(Duration::NIL))}
295  { }
296 
297 
298 
300  const Duration Duration::NIL {Time::ZERO};
301 
303  const Duration Duration::MAX = []{
304  auto maxDelta {Time::MAX - Time::MIN};
305  // bypass limit check, which requires Duration::MAX
306  return reinterpret_cast<Duration const&> (maxDelta);
307  }();
308 
309  const TimeSpan TimeSpan::ALL {Time::MIN, Duration::MAX};
310 
311 }} // namespace lib::Time
312 
313 namespace util {
314  string
315  StringConv<lib::time::FSecs, void>::invoke (lib::time::FSecs val) noexcept
316  {
317  return lib::time::renderFraction (val, "sec");
318  }
319 } // namespace util
320 
321 
322 
323 
324 
325 
326 
327 /* ===== implementation of the C API functions ===== */
328 
329 
330 char*
332 {
333  int milliseconds, seconds, minutes, hours;
334  bool negative = (time < 0);
335 
336  if (negative)
337  time = -time;
338 
339  time /= TIME_SCALE_MS;
340  milliseconds = time % 1000;
341  time /= 1000;
342  seconds = time % 60;
343  time /= 60;
344  minutes = time % 60;
345  time /= 60;
346  hours = time;
347 
348  char *buffer = lumiera_tmpbuf_snprintf(64, lib::time::DIAGNOSTIC_FORMAT,
349  negative ? "-" : "", hours, minutes, seconds, milliseconds);
350 
351  ENSURE(buffer != NULL);
352  return buffer;
353 }
354 
355 
357 gavl_time_t
358 lumiera_rational_to_time (FSecs const& fractionalSeconds)
359 {
360  // avoid numeric wrap from values not representable as 64bit µ-ticks
361  if (abs(fractionalSeconds) > lib::time::FSEC_MAX)
362  return (fractionalSeconds < 0? -1:+1)
363  * std::numeric_limits<int64_t>::max();
364 
365  return gavl_time_t(util::reQuant (fractionalSeconds.numerator()
366  ,fractionalSeconds.denominator()
368  ));
369 }
370 
371 gavl_time_t
372 lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps)
373 {
374  // convert to 64bit
375  boost::rational<uint64_t> framerate (fps.numerator(), fps.denominator());
376 
377  return rational_cast<gavl_time_t> (lib::time::TimeValue::SCALE * frameCount / framerate);
378 }
379 
380 gavl_time_t
382 {
383  if (!fps)
384  throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
385  , error::LUMIERA_ERROR_BOTTOM_VALUE);
386 
387  FSecs duration = 1 / fps;
388  return lumiera_rational_to_time (duration);
389 }
390 
391 
392 namespace { // implementation: basic frame quantisation....
393 
394  inline int64_t
395  calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
396  {
397  time -= origin;
398  return floordiv (time,grid);
399  }
400 
401  inline int64_t
402  calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1)
403  {
404  REQUIRE (framerate);
405  REQUIRE (framerate_divisor);
406 
407  const int64_t limit_num = std::numeric_limits<gavl_time_t>::max() / framerate;
408  const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor;
409  const int64_t microScale {lib::time::TimeValue::SCALE};
410 
411  // protect against numeric overflow
412  if (abs(time) < limit_num and microScale < limit_den)
413  {
414  // safe to calculate "time * framerate"
415  time -= origin;
416  return floordiv (time*framerate, microScale*framerate_divisor);
417  }
418  else
419  {
420  // direct calculation will overflow.
421  // use the less precise method instead...
422  gavl_time_t frameDuration = microScale / framerate; // truncated to µs
423  return calculate_quantisation (time,origin, frameDuration);
424  }
425  }
426 }
427 
428 
429 int64_t
430 lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
431 {
432  return calculate_quantisation (time, origin, grid);
433 }
434 
435 int64_t
436 lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate)
437 {
438  return calculate_quantisation (time, origin, framerate);
439 }
440 
441 gavl_time_t
442 lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
443 {
444  int64_t count = calculate_quantisation (time, origin, grid);
445  gavl_time_t alignedTime = count * grid;
446  return alignedTime;
447 }
448 
449 gavl_time_t
450 lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid)
451 {
452  gavl_time_t offset = nr * grid;
453  return origin + offset;
454 }
455 
456 
457 gavl_time_t
458 lumiera_build_time(long millis, uint secs, uint mins, uint hours)
459 {
460  gavl_time_t time = millis
461  + 1000 * secs
462  + 1000 * 60 * mins
463  + 1000 * 60 * 60 * hours;
464  time *= TIME_SCALE_MS;
465  return time;
466 }
467 
468 gavl_time_t
469 lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
470 {
471  gavl_time_t time = 1000LL * frames/fps
472  + 1000 * secs
473  + 1000 * 60 * mins
474  + 1000 * 60 * 60 * hours;
475  time *= TIME_SCALE_MS;
476  return time;
477 }
478 
479 int
480 lumiera_time_hours (gavl_time_t time)
481 {
482  return time / TIME_SCALE_MS / 1000 / 60 / 60;
483 }
484 
485 int
486 lumiera_time_minutes (gavl_time_t time)
487 {
488  return (time / TIME_SCALE_MS / 1000 / 60) % 60;
489 }
490 
491 int
492 lumiera_time_seconds (gavl_time_t time)
493 {
494  return (time / TIME_SCALE_MS / 1000) % 60;
495 }
496 
497 int
498 lumiera_time_millis (gavl_time_t time)
499 {
500  return (time / TIME_SCALE_MS) % 1000;
501 }
502 
503 int
504 lumiera_time_frames (gavl_time_t time, uint fps)
505 {
506  REQUIRE (fps < uint(std::numeric_limits<int>::max()));
507  return floordiv<int> (lumiera_time_millis(time) * int(fps), TIME_SCALE_MS);
508 }
509 
510 
511 
512 
513 /* ===== NTSC drop-frame conversions ===== */
514 
515 
516 namespace { // implementation helper
517 
518  const uint FRAMES_PER_10min = 10*60 * 30000/1001;
519  const uint FRAMES_PER_1min = 1*60 * 30000/1001;
520  const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
521 
522 
528  inline int64_t
529  calculate_drop_frame_number (gavl_time_t time)
530  {
531  int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
532 
533  // partition into 10 minute segments
534  lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min);
535 
536  // ensure the drop-frame incidents happen at full minutes;
537  // at start of each 10-minute segment *no* drop incident happens,
538  // thus we need to correct discrepancy between nominal/real framerate once:
539  int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min;
540 
541  int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
542  return frameNr + 2*dropIncidents;
543  }
544 }
545 
546 int
548 {
549  return calculate_drop_frame_number(time) % 30;
550 }
551 
552 int
554 {
555  return calculate_drop_frame_number(time) / 30 % 60;
556 }
557 
558 int
560 {
561  return calculate_drop_frame_number(time) / 30 / 60 % 60;
562 }
563 
564 int
566 {
567  return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24;
568 }
569 
570 gavl_time_t
571 lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
572 {
573  uint64_t total_mins = 60 * hours + mins;
574  uint64_t total_frames = 30*60*60 * hours
575  + 30*60 * mins
576  + 30 * secs
577  + frames
578  - 2 * (total_mins - total_mins / 10);
579  gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
580 
581  if (0 != result) // compensate for truncating down on conversion
582  result += 1; // without this adjustment the frame number
583  return result; // would turn out off by -1 on back conversion
584 }
I floordiv(I num, I den)
floor function for integer arithmetics.
Definition: util-quant.hpp:98
int lumiera_time_ntsc_drop_frames(gavl_time_t time)
Extract the frame part of given time, using NTSC drop-frame timecode.
Definition: time.cpp:547
static const Duration MAX
maximum possible temporal extension
Definition: timevalue.hpp:513
int lumiera_time_hours(gavl_time_t time)
Extract the hour part of given time.
Definition: time.cpp:480
gavl_time_t lumiera_build_time(long millis, uint secs, uint mins, uint hours)
Build a time value by summing up the given components.
Definition: time.cpp:458
Common functions for handling of time values.
Rational number support, based on boost::rational.
gavl_time_t lumiera_framecount_to_time(uint64_t frameCount, FrameRate const &fps)
Converts a frame count into Lumiera&#39;s internal time scale.
Definition: time.cpp:372
Framerate specified as frames per second.
Definition: timevalue.hpp:661
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:75
static const gavl_time_t SCALE
Number of micro ticks (µs) per second as basic time scale.
Definition: timevalue.hpp:176
Front-end for printf-style string template interpolation.
Offset stretchedByRationalFactor(boost::rational< int64_t >) const
Definition: time.cpp:271
int lumiera_time_ntsc_drop_seconds(gavl_time_t time)
Extract the second part of given time, using NTSC drop-frame timecode.
Definition: time.cpp:553
int64_t calculate_drop_frame_number(gavl_time_t time)
reverse the drop-frame calculation
Definition: time.cpp:529
int64_t lumiera_quantise_frames(gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
Quantise the given time into a fixed grid, relative to the origin.
Definition: time.cpp:430
int lumiera_time_frames(gavl_time_t time, uint fps)
Extract the remaining frame part of given time.
Definition: time.cpp:504
gavl_time_t lumiera_build_time_ntsc_drop(uint frames, uint secs, uint mins, uint hours)
Builds a time value by summing up the given components.
Definition: time.cpp:571
Round robin temporary buffers.
A front-end for using printf-style formatting.
int lumiera_time_millis(gavl_time_t time)
Extract the milliseconds part of given time.
Definition: time.cpp:498
static const Duration NIL
constant to indicate "no duration"
Definition: timevalue.hpp:512
#define MAX(A, B)
the inevitable MAX macro, sometimes still necessary in template code
Definition: util.hpp:451
Implementation namespace for support and library code.
Time(int)
suppress possible direct conversions
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:196
Duration abs() const
interpret the distance given by this offset as a time duration
Definition: timevalue.hpp:810
static TimeValue buildRaw_(gavl_time_t)
Definition: time.cpp:241
Utilities for quantisation (grid alignment) and comparisons.
boost::rational< int64_t > FSecs
rational representation of fractional seconds
Definition: timevalue.hpp:226
#define TIME_SCALE_MS
scale factor used locally within this implementation header.
Definition: time.cpp:105
Duration duration() const
duration of one frame
Definition: time.cpp:258
int lumiera_time_ntsc_drop_hours(gavl_time_t time)
Extract the hour part of given time, using NTSC drop-frame timecode.
Definition: time.cpp:565
gavl_time_t lumiera_quantise_time(gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
Similar to lumiera_quantise_frames, but returns a grid aligned relative time.
Definition: time.cpp:442
gavl_time_t lumiera_time_of_gridpoint(int64_t nr, gavl_time_t origin, gavl_time_t grid)
Calculate time of a grid point (frame start)
Definition: time.cpp:450
Lumiera error handling (C++ interface).
Offset measures a distance in time.
Definition: timevalue.hpp:364
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:474
int lumiera_time_seconds(gavl_time_t time)
Extract the seconds part of given time.
Definition: time.cpp:492
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:579
int64_t FrameCnt
relative framecount or frame number.
Definition: digxel.hpp:322
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:486
basic constant internal time value.
Definition: timevalue.hpp:142
gavl_time_t lumiera_rational_to_time(FSecs const &fractionalSeconds)
Definition: time.cpp:358
static const Time MAX
Definition: timevalue.hpp:315
gavl_time_t t_
the raw (internal) time value used to implement the time types
Definition: timevalue.hpp:149
friend gavl_time_t _raw(TimeValue const &time)
Definition: timevalue.hpp:190
gavl_time_t lumiera_frame_duration(FrameRate const &fps)
Calculates the duration of one frame in Lumiera time units.
Definition: time.cpp:381
int lumiera_time_ntsc_drop_minutes(gavl_time_t time)
Extract the minute part of given time, using NTSC drop-frame timecode.
Definition: time.cpp:559
gavl_time_t lumiera_build_time_fps(uint fps, uint frames, uint secs, uint mins, uint hours)
Builds a time value by summing up the given components.
Definition: time.cpp:469
static const FrameRate PAL
predefined constant for PAL framerate
Definition: timevalue.hpp:673
char * lumiera_tmpbuf_print_time(gavl_time_t time)
Definition: time.cpp:331
char * lumiera_tmpbuf_snprintf(size_t size, const char *fmt,...)
Construct a string in a tmpbuf.
Definition: tmpbuf.c:125