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