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)
5  2008, Christian Thaeter <ct@pipapo.org>
6  2010, Stefan Kangas <skangas@skangas.se>
7  2011, Hermann Vosseler <Ichthyostega@web.de>
8 
9   **Lumiera** is free software; you can redistribute it and/or modify it
10   under the terms of the GNU General Public License as published by the
11   Free Software Foundation; either version 2 of the License, or (at your
12   option) any later version. See the file COPYING for further details.
13 
14 * *****************************************************************/
15 
16 
37 #include "lib/error.hpp"
38 #include "lib/time.h"
39 #include "lib/time/timevalue.hpp"
40 #include "lib/rational.hpp"
41 #include "lib/util-quant.hpp"
42 #include "lib/format-string.hpp"
43 #include "lib/util.hpp"
44 
45 extern "C" {
46 #include "lib/tmpbuf.h"
47 }
48 
49 #include <math.h>
50 #include <limits>
51 #include <string>
52 #include <sstream>
53 #include <boost/rational.hpp>
54 #include <boost/lexical_cast.hpp>
55 
56 using std::string;
57 using util::limited;
58 using util::floordiv;
59 using lib::time::FSecs;
61 using boost::rational_cast;
62 using boost::lexical_cast;
63 
64 #undef MAX
65 #undef MIN
66 
67 
68 namespace error = lumiera::error;
69 
70 
71 namespace lib {
72 namespace meta {
73  extern const std::string FAILURE_INDICATOR;
74 }
75 namespace time {
76 
77 
78  const gavl_time_t TimeValue::SCALE = GAVL_TIME_SCALE;
79 
80 
82  const Time Time::MAX ( TimeValue::buildRaw_(+std::numeric_limits<gavl_time_t>::max() / 30) );
83  const Time Time::MIN ( TimeValue::buildRaw_(-_raw(Time::MAX) ) );
84  const Time Time::ZERO;
85 
86  const Time Time::ANYTIME(Time::MIN);
87  const Time Time::NEVER (Time::MAX);
88 
89  const Offset Offset::ZERO (Time::ZERO);
90 
91  const FSecs FSEC_MAX{std::numeric_limits<int64_t>::max() / lib::time::TimeValue::SCALE};
92 
93  Literal DIAGNOSTIC_FORMAT{"%s%01d:%02d:%02d.%03d"};
94 
95 
100 #define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000)
101 
102 
113  Time::Time ( long millis
114  , uint secs
115  , uint mins
116  , uint hours
117  )
118  : TimeValue(lumiera_build_time (millis,secs,mins,hours))
119  { }
120 
121 
126  Time::Time (FSecs const& fractionalSeconds)
127  : TimeValue(lumiera_rational_to_time (fractionalSeconds))
128  { }
129 
130  Offset::Offset (FSecs const& delta_in_secs)
131  : TimeValue{buildRaw_(symmetricLimit (lumiera_rational_to_time (delta_in_secs)
132  ,Duration::MAX))}
133  { }
134 
135 
139  TimeValue::operator string() const
140  {
141  gavl_time_t time = t_;
142  int64_t millis, seconds;
143  bool negative = (time < 0);
144 
145  if (negative) time = -time;
146  time /= TIME_SCALE_MS;
147  millis = time % 1000;
148  seconds = time / 1000;
149 
150  return string (negative ? "-" : "")
151  + (seconds>0 or time==0? lexical_cast<string> (seconds)+"s" : "")
152  + (millis>0? lexical_cast<string> (millis)+"ms" : "")
153  ;
154  }
155 
164  Time::operator string() const
165  {
166  gavl_time_t time = t_;
167  int millis, seconds, minutes, hours;
168  bool negative = (time < 0);
169 
170  if (negative)
171  time = -time;
172 
173  time /= TIME_SCALE_MS;
174  millis = time % 1000;
175  time /= 1000;
176  seconds = time % 60;
177  time /= 60;
178  minutes = time % 60;
179  time /= 60;
180  hours = time;
181 
182  return util::_Fmt{string(DIAGNOSTIC_FORMAT)}
183  % (negative? "-":"")
184  % hours
185  % minutes
186  % seconds
187  % millis;
188  }
189 
190 
191  Offset::operator string() const
192  {
193  return (t_< 0? "" : "∆")
194  + TimeValue::operator string();
195  }
196 
197  Duration::operator string() const
198  {
199  return "≺"+TimeValue::operator string()+"≻";
200  }
201 
202  TimeSpan::operator string() const
203  {
204  return string (start())
205  + string (duration());
206  }
207 
208 
209  namespace {
210  template<typename RAT>
211  string
212  renderFraction (RAT const& frac, Literal postfx) noexcept
213  try {
214  std::ostringstream buffer;
215  if (1 == frac.denominator() or 0 == frac.numerator())
216  buffer << frac.numerator() << postfx;
217  else
218  buffer << frac <<postfx;
219  return buffer.str();
220  }
221  catch(...)
222  { return meta::FAILURE_INDICATOR; }
223  }
224 
226  FrameRate::operator string() const
227  {
228  return renderFraction (*this, "FPS");
229  }
230 
231 
232 
235  TimeValue
236  TimeValue::buildRaw_ (gavl_time_t raw)
237  {
238  return reinterpret_cast<TimeValue const&> (raw);
239  }
240 
241 
242 
244  const FrameRate FrameRate::PAL (25);
245  const FrameRate FrameRate::NTSC (30000,1001);
246  const FrameRate FrameRate::STEP (1);
247 
248  const FrameRate FrameRate::HALTED (1,std::numeric_limits<int>::max());
249 
250 
253  Duration
255  {
256  if (0 == *this)
257  throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
258  , error::LUMIERA_ERROR_BOTTOM_VALUE);
259 
260  return Duration (1, *this);
261  }
262 
263 
268  const uint RATE_LIMIT{std::numeric_limits<uint>::max() / 1024};
269 
276  boost::rational<uint>
278  {
279  const double UPPER_LIMIT = int64_t(RATE_LIMIT*1024) << 31;
280  const int64_t HAZARD = util::ilog2(RATE_LIMIT);
281 
282  double doo = limited (1.0, fabs(fps) * RATE_LIMIT + 0.5, UPPER_LIMIT);
283  int64_t boo(doo);
284  util::Rat quantised{boo
285  ,int64_t(RATE_LIMIT)
286  };
287 
288  int64_t num = quantised.numerator();
289  int64_t toxic = util::ilog2(abs(num));
290  toxic = util::max (0, toxic - HAZARD);
291 
292  int64_t base = quantised.denominator();
293  if (toxic)
294  {
295  base = util::max (base >> toxic, 1);
296  num = util::reQuant (num, quantised.denominator(), base);
297  }
298  return {limited (1u, num, RATE_LIMIT)
299  ,limited (1u, base, RATE_LIMIT)
300  };
301  }
302 
307  boost::rational<uint>
308  __framerate_approximation (size_t cnt, Duration timeReference)
309  {
310  boost::rational<uint64_t> quot{cnt, _raw(timeReference)};
311  if (quot.denominator() < RATE_LIMIT
312  and quot.numerator() < RATE_LIMIT*1024/1e6)
313  return {uint(quot.numerator()) * uint(Time::SCALE)
314  ,uint(quot.denominator())
315  };
316  // precise computation can not be handled numerically...
317  return __framerate_approximation (rational_cast<double>(quot) * Time::SCALE);
318  }
319 
320 
321 
322 
324  Offset
325  Offset::stretchedByRationalFactor (boost::rational<int64_t> factor) const
326  {
327  boost::rational<int64_t> distance (this->t_);
328  distance *= factor;
329  gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
330  return Offset{buildRaw_(microTicks)};
331  }
332 
333 
335  Offset
336  Offset::stretchedByFloatFactor (double factor) const
337  {
338  double distance(this->t_);
339  distance *= factor;
340  gavl_time_t microTicks = floor (distance);
341  return Offset{buildRaw_(microTicks)};
342  }
343 
344 
346  Offset::Offset (FrameCnt count, FrameRate const& fps)
348  count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
349  :_raw(Duration::NIL))}
350  { }
351 
352 
353 
355  const Duration Duration::NIL {Time::ZERO};
356 
358  const Duration Duration::MAX = []{
359  auto maxDelta {Time::MAX - Time::MIN};
360  // bypass limit check, which requires Duration::MAX
361  return reinterpret_cast<Duration const&> (maxDelta);
362  }();
363 
364  const TimeSpan TimeSpan::ALL {Time::MIN, Duration::MAX};
365 
366 }} // namespace lib::Time
367 
368 namespace util {
369  string
370  StringConv<lib::time::FSecs, void>::invoke (lib::time::FSecs val) noexcept
371  {
372  return lib::time::renderFraction (val, "sec");
373  }
374 } // namespace util
375 
376 
377 
378 
379 
380 
381 
382 /* ===== implementation of the C API functions ===== */
383 
384 
385 char*
387 {
388  int milliseconds, seconds, minutes, hours;
389  bool negative = (time < 0);
390 
391  if (negative)
392  time = -time;
393 
394  time /= TIME_SCALE_MS;
395  milliseconds = time % 1000;
396  time /= 1000;
397  seconds = time % 60;
398  time /= 60;
399  minutes = time % 60;
400  time /= 60;
401  hours = time;
402 
403  char *buffer = lumiera_tmpbuf_snprintf(64, lib::time::DIAGNOSTIC_FORMAT,
404  negative ? "-" : "", hours, minutes, seconds, milliseconds);
405 
406  ENSURE(buffer != NULL);
407  return buffer;
408 }
409 
410 
412 gavl_time_t
413 lumiera_rational_to_time (FSecs const& fractionalSeconds)
414 {
415  // avoid numeric wrap from values not representable as 64bit µ-ticks
416  if (abs(fractionalSeconds) > lib::time::FSEC_MAX)
417  return (fractionalSeconds < 0? -1:+1)
418  * std::numeric_limits<int64_t>::max();
419 
420  return gavl_time_t(util::reQuant (fractionalSeconds.numerator()
421  ,fractionalSeconds.denominator()
423  ));
424 }
425 
426 gavl_time_t
427 lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps)
428 {
429  // convert to 64bit
430  boost::rational<uint64_t> framerate (fps.numerator(), fps.denominator());
431 
432  return rational_cast<gavl_time_t> (lib::time::TimeValue::SCALE * frameCount / framerate);
433 }
434 
435 gavl_time_t
437 {
438  if (!fps)
439  throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
440  , error::LUMIERA_ERROR_BOTTOM_VALUE);
441 
442  FSecs duration = 1 / fps;
443  return lumiera_rational_to_time (duration);
444 }
445 
446 
447 namespace { // implementation: basic frame quantisation....
448 
449  inline int64_t
450  calculate_quantisation (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
451  {
452  time -= origin;
453  return floordiv (time,grid);
454  }
455 
456  inline int64_t
457  calculate_quantisation (gavl_time_t time, gavl_time_t origin, uint framerate, uint framerate_divisor=1)
458  {
459  REQUIRE (framerate);
460  REQUIRE (framerate_divisor);
461 
462  const int64_t limit_num = std::numeric_limits<gavl_time_t>::max() / framerate;
463  const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor;
464  const int64_t microScale {lib::time::TimeValue::SCALE};
465 
466  // protect against numeric overflow
467  if (abs(time) < limit_num and microScale < limit_den)
468  {
469  // safe to calculate "time * framerate"
470  time -= origin;
471  return floordiv (time*framerate, microScale*framerate_divisor);
472  }
473  else
474  {
475  // direct calculation will overflow.
476  // use the less precise method instead...
477  gavl_time_t frameDuration = microScale / framerate; // truncated to µs
478  return calculate_quantisation (time,origin, frameDuration);
479  }
480  }
481 }
482 
483 
484 int64_t
485 lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
486 {
487  return calculate_quantisation (time, origin, grid);
488 }
489 
490 int64_t
491 lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerate)
492 {
493  return calculate_quantisation (time, origin, framerate);
494 }
495 
496 gavl_time_t
497 lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
498 {
499  int64_t count = calculate_quantisation (time, origin, grid);
500  gavl_time_t alignedTime = count * grid;
501  return alignedTime;
502 }
503 
504 gavl_time_t
505 lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid)
506 {
507  gavl_time_t offset = nr * grid;
508  return origin + offset;
509 }
510 
511 
512 gavl_time_t
513 lumiera_build_time(long millis, uint secs, uint mins, uint hours)
514 {
515  gavl_time_t time = millis
516  + 1000 * secs
517  + 1000 * 60 * mins
518  + 1000 * 60 * 60 * hours;
519  time *= TIME_SCALE_MS;
520  return time;
521 }
522 
523 gavl_time_t
524 lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)
525 {
526  gavl_time_t time = 1000LL * frames/fps
527  + 1000 * secs
528  + 1000 * 60 * mins
529  + 1000 * 60 * 60 * hours;
530  time *= TIME_SCALE_MS;
531  return time;
532 }
533 
534 int
535 lumiera_time_hours (gavl_time_t time)
536 {
537  return time / TIME_SCALE_MS / 1000 / 60 / 60;
538 }
539 
540 int
541 lumiera_time_minutes (gavl_time_t time)
542 {
543  return (time / TIME_SCALE_MS / 1000 / 60) % 60;
544 }
545 
546 int
547 lumiera_time_seconds (gavl_time_t time)
548 {
549  return (time / TIME_SCALE_MS / 1000) % 60;
550 }
551 
552 int
553 lumiera_time_millis (gavl_time_t time)
554 {
555  return (time / TIME_SCALE_MS) % 1000;
556 }
557 
558 int
559 lumiera_time_frames (gavl_time_t time, uint fps)
560 {
561  REQUIRE (fps < uint(std::numeric_limits<int>::max()));
562  return floordiv<int> (lumiera_time_millis(time) * int(fps), TIME_SCALE_MS);
563 }
564 
565 
566 
567 
568 /* ===== NTSC drop-frame conversions ===== */
569 
570 
571 namespace { // implementation helper
572 
573  const uint FRAMES_PER_10min = 10*60 * 30000/1001;
574  const uint FRAMES_PER_1min = 1*60 * 30000/1001;
575  const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
576 
577 
583  inline int64_t
584  calculate_drop_frame_number (gavl_time_t time)
585  {
586  int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
587 
588  // partition into 10 minute segments
589  lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min);
590 
591  // ensure the drop-frame incidents happen at full minutes;
592  // at start of each 10-minute segment *no* drop incident happens,
593  // thus we need to correct discrepancy between nominal/real framerate once:
594  int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min;
595 
596  int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
597  return frameNr + 2*dropIncidents;
598  }
599 }
600 
601 int
603 {
604  return calculate_drop_frame_number(time) % 30;
605 }
606 
607 int
609 {
610  return calculate_drop_frame_number(time) / 30 % 60;
611 }
612 
613 int
615 {
616  return calculate_drop_frame_number(time) / 30 / 60 % 60;
617 }
618 
619 int
621 {
622  return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24;
623 }
624 
625 gavl_time_t
626 lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
627 {
628  uint64_t total_mins = 60 * hours + mins;
629  uint64_t total_frames = 30*60*60 * hours
630  + 30*60 * mins
631  + 30 * secs
632  + frames
633  - 2 * (total_mins - total_mins / 10);
634  gavl_time_t result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
635 
636  if (0 != result) // compensate for truncating down on conversion
637  result += 1; // without this adjustment the frame number
638  return result; // would turn out off by -1 on back conversion
639 }
I floordiv(I num, I den)
floor function for integer arithmetics.
Definition: util-quant.hpp:97
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:602
static const Duration MAX
maximum possible temporal extension
Definition: timevalue.hpp:507
int lumiera_time_hours(gavl_time_t time)
Extract the hour part of given time.
Definition: time.cpp:535
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:513
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:427
Framerate specified as frames per second.
Definition: timevalue.hpp:655
inline string literal This is a marker type to indicate that
Definition: symbol.hpp:76
static const gavl_time_t SCALE
Number of micro ticks (µs) per second as basic time scale.
Definition: timevalue.hpp:167
Front-end for printf-style string template interpolation.
Offset stretchedByRationalFactor(boost::rational< int64_t >) const
Definition: time.cpp:325
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:608
int64_t calculate_drop_frame_number(gavl_time_t time)
reverse the drop-frame calculation
Definition: time.cpp:584
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:485
int lumiera_time_frames(gavl_time_t time, uint fps)
Extract the remaining frame part of given time.
Definition: time.cpp:559
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:626
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:553
static const Duration NIL
constant to indicate "no duration"
Definition: timevalue.hpp:506
#define MAX(A, B)
the inevitable MAX macro, sometimes still necessary in template code
Definition: util.hpp:518
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:190
static const FrameRate STEP
1 frame per second
Definition: timevalue.hpp:673
Duration abs() const
interpret the distance given by this offset as a time duration
Definition: timevalue.hpp:831
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
static TimeValue buildRaw_(gavl_time_t)
Definition: time.cpp:236
Utilities for quantisation (grid alignment) and comparisons.
boost::rational< int64_t > FSecs
rational representation of fractional seconds
Definition: timevalue.hpp:220
#define TIME_SCALE_MS
scale factor used locally within this implementation header.
Definition: time.cpp:100
Duration duration() const
duration of one frame
Definition: time.cpp:254
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:620
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:497
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:505
Lumiera error handling (C++ interface).
Offset measures a distance in time.
Definition: timevalue.hpp:358
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:468
boost::rational< uint > __framerate_approximation(double fps)
Definition: time.cpp:277
NUM constexpr limited(NB lowerBound, NUM val, NB upperBound)
force a numeric to be within bounds, inclusively
Definition: util.hpp:91
int lumiera_time_seconds(gavl_time_t time)
Extract the seconds part of given time.
Definition: time.cpp:547
A time interval anchored at a specific point in time.
Definition: timevalue.hpp:573
int64_t FrameCnt
relative framecount or frame number.
Definition: digxel.hpp:312
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
gavl_time_t lumiera_rational_to_time(FSecs const &fractionalSeconds)
Definition: time.cpp:413
static const Time MAX
Definition: timevalue.hpp:309
Offset stretchedByFloatFactor(double) const
Definition: time.cpp:336
gavl_time_t t_
the raw (internal) time value used to implement the time types
Definition: timevalue.hpp:140
friend gavl_time_t _raw(TimeValue const &time)
Definition: timevalue.hpp:181
const uint RATE_LIMIT
a rather arbitrary safety limit imposed on internal numbers used to represent a frame rate...
Definition: time.cpp:268
gavl_time_t lumiera_frame_duration(FrameRate const &fps)
Calculates the duration of one frame in Lumiera time units.
Definition: time.cpp:436
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:614
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:524
static const FrameRate PAL
predefined constant for PAL framerate
Definition: timevalue.hpp:671
char * lumiera_tmpbuf_print_time(gavl_time_t time)
Definition: time.cpp:386
char * lumiera_tmpbuf_snprintf(size_t size, const char *fmt,...)
Construct a string in a tmpbuf.
Definition: tmpbuf.c:116