clean-up: relocate NTSC drop-frame conversion functions

...extract these functions and the associated test
from the low-level C time handling library and
document them with a dedicated C++ header and test.

''This is unfinished work'' —
the extracted functions provide only the low level computations;
actually, a specialised time quantisation or time code would be required.

------------
Note though,
after extracting these functions, the rest of the plain-C test
can be removed, since equivalent functionality is covered in
much more detail by the tests of the C++ time handling framework.

Notably this allows to get rid of the direct component accessor functions.

------------
__Remark__: the base implementation of many time conversion functions
            and especially NTSC drop-frame was provided by Stefan Kangas

See:
6a44134833
This commit is contained in:
Fischlurch 2025-05-27 23:39:36 +02:00
parent 55b39ae592
commit 255fc82a1b
8 changed files with 305 additions and 661 deletions

View file

@ -140,85 +140,6 @@ lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid);
raw_time_64
lumiera_build_time (long millis, uint secs, uint mins, uint hours);
/**
* Builds a time value by summing up the given components.
* @param fps framerate (frames per second)
* @param frames number of additional frames
* @param secs number of seconds
* @param mins number of minutes
* @param hours number of hours
*/
raw_time_64
lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours);
/**
* Builds a time value by summing up the given components.
* The components are interpreted as a NTSC drop-frame timecode.
* @warning take care not to specify time codes that are illegal NTSC drop-frame times.
*/
raw_time_64
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours);
/** Extract the hour part of given time. */
int
lumiera_time_hours (raw_time_64 time);
/** Extract the minute part of given time. */
int
lumiera_time_minutes (raw_time_64 time);
/** Extract the seconds part of given time. */
int
lumiera_time_seconds (raw_time_64 time);
/** Extract the milliseconds part of given time. */
int
lumiera_time_millis (raw_time_64 time);
/**
* Extract the remaining frame part of given time.
* @param fps frame rate (frames per second)
*/
int
lumiera_time_frames (raw_time_64 time, uint fps);
/**
* Extract the frame part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_frames (raw_time_64 time);
/**
* Extract the second part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_seconds (raw_time_64 time);
/**
* Extract the minute part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_minutes (raw_time_64 time);
/**
* Extract the hour part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_hours (raw_time_64 time);
/**
* @internal Diagnostics helper: render time value in H:MM:SS.mmm format.
* @return `safeclib` temporary buffer containing formatted time string
* @note any time output for real should go through quantisation followed
* by rendering into a suitable timecode format.
*/
char*
lumiera_tmpbuf_print_time (raw_time_64 time);

View file

@ -1,5 +1,5 @@
/*
DROPFRAME.hpp - Utilities for handling time
DROPFRAME.hpp - drop-frame timecode conversions
Copyright (C)
2010, Stefan Kangas <skangas@skangas.se>
@ -11,35 +11,39 @@
*/
/** @file dropframe.hpp
** Common functions for handling of time values.
** Working with time values in sexagesimal format, quantising time and converting
** to/from common timecode formats can be tricky to get right. Thus the goal is
** to concentrate the actual bits of math for these operations into a small number
** of library functions, which are easy to test thoroughly in isolation.
** Calculations to support mapping into NTSC drop frame timecode.
** This is a scheme for mapping frame numbers to a time display in SMPTE format,
** i.e. with hours, minutes, seconds and a frame count. It was introduced in USA
** at the time when colour television with the NTSC standard was introduced.
** Since the existing black and white television broadcast system based on analogue
** signal processing had initially be fixed to the same frequency as the power grid,
** which runs with 60 Hz in USA, the addition of the additional carriers for the
** colour information was confronted with problems of signal cross-talk, due to
** some overtones of the used carriers falling into the same frequency band.
** As a pragmatic workaround, the frame rate was reduced to 29.97 fps.
**
** Built on top of that, the actual time handling in the GUI and within the Lumiera
** session is mostly confined to use the opaque lib::time::Time wrapper objects.
** When time values actually need to be _quantised_ (aligned to a frame grid),
** this is expressed at the API through using the lib::time::QuTime type, which
** then in turn can be materialised into a number of _timecode formats_.
** These definitions ensure that whenever an actual quantisation (rounding)
** operation is performed, the link to the appropriate time grid is available,
** so that multiple output or rendering operations can use differing time origins
** and frame rates simultaneously on the same model.
** This fix of one problem however caused secondary problems for the production
** of content for television broadcast, since at this frame rate, one hour of
** content would not sum up cleanly to a simple number of frames. Since the
** purpose of time code is to label each frame with a time stamp, as a solution,
** the mapping of time stamps to frame numbers is manipulated:
** - every minute, there is one second which is mapped to 28 frames only
** - with the exception of every 10 minutes, where no such gap is applied
** This scheme adds up to allocating 108 frames less per hour, as would be used
** with a full frame rate of 30 fps. It should be noted though that applying such
** a »drop event« every minute does not mean to _omit actual content frames_
** rather it is the dime display in the time stamp which suddenly skips ahead by
** +2 frames every minute (but not every 10th minute).
**
** The Lumiera Vault Layer functions mostly operate on raw frame counts, which in
** this model are defined to be a special kind of timecode, and thus dependent on
** a preceding time quantisation.
** @todo 2025 this header provides some calculation functions to implement such
** a mapping scheme (thereby documenting the know-how). But the project
** never got to the point of integrating this functionality into an
** actual timecode implementation. Doing so will be future work.
** ///////////////////////////////////////////////////TICKET #751
**
** @deprecated 2025 this should not be a "simple" C library set aside from the Lumiera
** time handling framework, rather it should be clarified that these are
** implementation helpers and must not be used by any application code.
** It should be checked which of these functions actually need to be
** exposed through an interface header, since these are typically
** used to implement parts of the time handling framework.
**
** @see lib::time::Time
** @see time-dropframe-test.cpp
** @see timequant.hpp
** @see lib::time::Time
** @see TimeValue_test
**
*/
@ -48,180 +52,28 @@
#ifndef LUMIERA_TIME_DROPFRAME_H
#define LUMIERA_TIME_DROPFRAME_H
#include <inttypes.h>
#ifdef __cplusplus /*=================== C++ facilities ===================== */
#include "lib/time/timevalue.hpp"
using lib::time::raw_time_64;
namespace lib {
namespace time {
/**
* Converts a fraction of seconds to Lumiera's internal opaque time scale.
* @param fractionalSeconds given as rational number
* @note inconsistent with Lumiera's general quantisation behaviour,
* here negative fractional micro-ticks are truncated towards zero.
* This was deemed irrelevant in practice.
* Compute the consecutive frame number from a given time,
* which is interpreted at the NTSC drop frame timecode grid.
*/
raw_time_64
lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds);
int64_t calculate_ntsc_drop_frame_number (raw_time_64 time);
/**
* Converts a frame count into Lumiera's internal time scale.
* based on a framerate given as rational number (e.g. NTSC)
* @note handles only positive frame counts and assumes the
* origin to be at zero.
*/
raw_time_64
lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps);
/**
* Calculates the duration of one frame in Lumiera time units.
* @param framerate underlying framerate as rational number
* @throw error::Logic on zero framerate
*/
raw_time_64
lumiera_frame_duration (lib::time::FrameRate const& fps);
extern "C" { /* ===================== C interface ======================== */
#endif
/**
* Quantise the given time into a fixed grid, relative to the origin.
* The time grid used for quantisation is comprised of equally spaced intervals,
* rooted at the given origin. The interval starting with the origin is numbered
* as zero. Each interval includes its lower bound, but excludes its upper bound.
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
* @return number of the grid interval containing the given time.
* @warning the resulting value is limited to (Time::Min, Time::MAX)
*/
int64_t
lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
int64_t
lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate);
/**
* Similar to #lumiera_quantise_frames, but returns a grid aligned _relative time_.
* @return time of start of the grid interval containing the given time,
* but measured relative to the origin
* @warning because the resulting value needs to be limited to fit into a 64bit long,
* the addressable time range can be considerably reduced. For example, if
* origin = Time::MIN, then all original time values above zero will be
* clipped, because the result, relative to origin, needs to be <= Time::MAX
*/
raw_time_64
lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
/**
* Calculate time of a grid point (frame start)
* @param nr index number of the grid point (0 is at origin)
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
* @return time point (frame start) on the Lumiera internal time scale
*/
raw_time_64
lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid);
/**
* Build a time value by summing up the given components.
* @param millis number of milliseconds
* @param secs number of seconds
* @param mins number of minutes
* @param hours number of hours
*/
raw_time_64
lumiera_build_time (long millis, uint secs, uint mins, uint hours);
/**
* Builds a time value by summing up the given components.
* @param fps framerate (frames per second)
* @param frames number of additional frames
* @param secs number of seconds
* @param mins number of minutes
* @param hours number of hours
*/
raw_time_64
lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours);
/**
* Builds a time value by summing up the given components.
* The components are interpreted as a NTSC drop-frame timecode.
* Build effective time from a NTSC drop frame timecode.
* @warning take care not to specify time codes that are illegal NTSC drop-frame times.
*/
raw_time_64
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours);
/** Extract the hour part of given time. */
int
lumiera_time_hours (raw_time_64 time);
/** Extract the minute part of given time. */
int
lumiera_time_minutes (raw_time_64 time);
/** Extract the seconds part of given time. */
int
lumiera_time_seconds (raw_time_64 time);
/** Extract the milliseconds part of given time. */
int
lumiera_time_millis (raw_time_64 time);
/**
* Extract the remaining frame part of given time.
* @param fps frame rate (frames per second)
*/
int
lumiera_time_frames (raw_time_64 time, uint fps);
/**
* Extract the frame part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_frames (raw_time_64 time);
/**
* Extract the second part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_seconds (raw_time_64 time);
/**
* Extract the minute part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_minutes (raw_time_64 time);
/**
* Extract the hour part of given time, using NTSC drop-frame timecode.
*/
int
lumiera_time_ntsc_drop_hours (raw_time_64 time);
/**
* @internal Diagnostics helper: render time value in H:MM:SS.mmm format.
* @return `safeclib` temporary buffer containing formatted time string
* @note any time output for real should go through quantisation followed
* by rendering into a suitable timecode format.
*/
char*
lumiera_tmpbuf_print_time (raw_time_64 time);
raw_time_64 build_time_from_ntsc_drop_frame (uint frames, uint secs, uint mins, uint hours);
#ifdef __cplusplus
}//extern "C"
#endif
}} // lib::time
#endif /*LUMIERA_TIME_DROPFRAME_H*/

View file

@ -42,10 +42,6 @@
#include "lib/format-string.hpp"
#include "lib/util.hpp"
extern "C" {
#include "lib/tmpbuf.h"
}
#include <math.h>
#include <limits>
#include <string>
@ -382,32 +378,6 @@ namespace util {
/* ===== implementation of the C API functions ===== */
char*
lumiera_tmpbuf_print_time (raw_time_64 time)
{
int milliseconds, seconds, minutes, hours;
bool negative = (time < 0);
if (negative)
time = -time;
time /= TIME_SCALE_MS;
milliseconds = time % 1000;
time /= 1000;
seconds = time % 60;
time /= 60;
minutes = time % 60;
time /= 60;
hours = time;
char *buffer = lumiera_tmpbuf_snprintf(64, lib::time::DIAGNOSTIC_FORMAT,
negative ? "-" : "", hours, minutes, seconds, milliseconds);
ENSURE(buffer != NULL);
return buffer;
}
/// @todo this utility function could be factored out into a `FSecs` or `RSec` class ///////////////////////TICKET #1262
raw_time_64
lumiera_rational_to_time (FSecs const& fractionalSeconds)
@ -564,24 +534,30 @@ lumiera_time_frames (raw_time_64 time, uint fps)
namespace lib {
namespace time { ////////////////////////////////////////////////////////////////////////////////////////////TICKET #1259 : move all calculation functions into a C++ namespace
/* ===== NTSC drop-frame conversions ===== */
namespace { // implementation helper
namespace { // conversion parameters
const uint FRAMES_PER_10min = 10*60 * 30000/1001;
const uint FRAMES_PER_1min = 1*60 * 30000/1001;
const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
}
/** reverse the drop-frame calculation
* @param time absolute time value in micro ticks
* @return the absolute frame number using NTSC drop-frame encoding
* @todo I doubt this works correct for negative times!!
/**
* @remark This function reverses building the drop-frame timecode,
* and thus maps a time into consecutive frame numbers
* at NTSC framerate (i.e. without gaps)
* @param timecode represented as time value in µ-ticks
* @return the absolute frame number as addressed by NTSC drop-frame
* @todo 2011 I doubt this works correct for negative times!!
*/
inline int64_t
calculate_drop_frame_number (raw_time_64 time)
int64_t
calculate_ntsc_drop_frame_number (raw_time_64 time)
{
int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
@ -596,34 +572,17 @@ namespace { // implementation helper
int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
return frameNr + 2*dropIncidents;
}
}
int
lumiera_time_ntsc_drop_frames (raw_time_64 time)
{
return calculate_drop_frame_number(time) % 30;
}
int
lumiera_time_ntsc_drop_seconds (raw_time_64 time)
{
return calculate_drop_frame_number(time) / 30 % 60;
}
int
lumiera_time_ntsc_drop_minutes (raw_time_64 time)
{
return calculate_drop_frame_number(time) / 30 / 60 % 60;
}
int
lumiera_time_ntsc_drop_hours (raw_time_64 time)
{
return calculate_drop_frame_number(time) / 30 / 60 / 60 % 24;
}
/**
* @remark This is the mapping function to translate NTSC drop frame
* timecode specification into an actual time, with the necessary
* skip events every 1.-9. minute, thereby allocating 108 frames
* less per hour, than would be required for full 30 fps.
* @return raw time value on a µ-tick scale
*/
raw_time_64
lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
build_time_from_ntsc_drop_frame (uint frames, uint secs, uint mins, uint hours)
{
uint64_t total_mins = 60 * hours + mins;
uint64_t total_frames = 30*60*60 * hours
@ -637,3 +596,6 @@ lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
result += 1; // without this adjustment the frame number
return result; // would turn out off by -1 on back conversion
}
}} // lib::time

View file

@ -1,5 +0,0 @@
TESTING "Time conversion" ./test-time
TEST "ntsc drop-frame calculations" ntsc_drop_frame << END
return: 0
END

View file

@ -130,7 +130,7 @@ return: 0
END
PLANNED "Drop-Frame calculation" TimeDropframe_test <<END
TEST "NTSC drop-frame calculation" TimeDropframe_test <<END
return: 0
END

View file

@ -20,109 +20,110 @@
*/
#include "lib/test/test.h"
#include <inttypes.h>
#include <ctype.h>
#include <nobug.h>
typedef int64_t raw_time_64; //////////////////////////////////////////////////////////////////////////////TICKET #1259 turn time.h in to a C++ implementation header
#include "lib/time.h" /////////////////////////////////////////////////////////////////////////////////////TICKET #1259 this should not be a general purpose library, but rather an implementation base
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/time/dropframe.hpp"
#include "lib/time/timevalue.hpp"
static int
calculate_framecount (raw_time_64 t, uint fps)
{
return lumiera_quantise_frames_fps (t,0,fps);
}
namespace lib {
namespace time{
namespace test{
using time::Time;
using time::TimeVar;
using time::FSecs;
using time::raw_time_64;
TESTS_BEGIN
namespace {// Test setup
const int FRAMES = 15;
const int MILLIS = 700;
const int SECONDS = 20;
const int MINUTES = 55;
const int HOURS = 3;
const int FPS = 24;
/*
* 1. Basic functionality
*/
TEST (basic)
/* ====== conversion helpers to verify results ====== */
const auto TIME_SCALE_sec{lib::time::TimeValue::SCALE };
const auto TIME_SCALE_ms {lib::time::TimeValue::SCALE / 1'000};
int
dropframe_frames (raw_time_64 timecode)
{
// Zero
raw_time_64 t = lumiera_build_time (0,0,0,0);
CHECK ((raw_time_64) t == 0);
CHECK (lumiera_time_millis (t) == 0);
CHECK (lumiera_time_seconds (t) == 0);
CHECK (lumiera_time_minutes (t) == 0);
CHECK (lumiera_time_hours (t) == 0);
CHECK (lumiera_time_frames (t, FPS) == 0);
CHECK (lumiera_time_frames (t, FPS+5) == 0);
CHECK (calculate_framecount (t,FPS) == 0);
CHECK (calculate_framecount (t, FPS+5) == 0);
ECHO ("%s", lumiera_tmpbuf_print_time (t));
// Non-zero
t = lumiera_build_time (MILLIS, SECONDS, MINUTES, HOURS);
CHECK (lumiera_time_millis (t) == MILLIS);
CHECK (lumiera_time_seconds (t) == SECONDS);
CHECK (lumiera_time_minutes (t) == MINUTES);
CHECK (lumiera_time_hours (t) == HOURS);
CHECK (lumiera_time_frames (t, FPS) == FPS * MILLIS / 1000);
CHECK (lumiera_time_frames (t, FPS+5) == (FPS+5) * MILLIS / 1000);
CHECK (calculate_framecount (t, FPS) == 338896);
CHECK (calculate_framecount (t, FPS+5) == 409500);
ECHO ("%s", lumiera_tmpbuf_print_time (t));
return calculate_ntsc_drop_frame_number(timecode) % 30;
}
/*
* 2. Frame rate dependent calculations.
*/
TEST (fps)
int
dropframe_seconds (raw_time_64 timecode)
{
raw_time_64 t = lumiera_build_time_fps (FPS, FRAMES, SECONDS, MINUTES, HOURS);
CHECK (lumiera_time_millis (t) == FRAMES * 1000 / FPS);
CHECK (lumiera_time_seconds (t) == SECONDS);
CHECK (lumiera_time_minutes (t) == MINUTES);
CHECK (lumiera_time_hours (t) == HOURS);
CHECK (lumiera_time_frames (t, FPS) == FRAMES);
CHECK (lumiera_time_frames (t, FPS+5) == FRAMES * (FPS+5)/FPS);
CHECK (calculate_framecount (t, FPS) == 338895);
CHECK (calculate_framecount (t, FPS+5) == 409498);
return calculate_ntsc_drop_frame_number(timecode) / 30 % 60;
}
/*
* 3. NTSC drop-frame calculations.
*/
int
dropframe_minutes (raw_time_64 timecode)
{
return calculate_ntsc_drop_frame_number(timecode) / 30 / 60 % 60;
}
TEST (ntsc_drop_frame)
int
dropframe_hours (raw_time_64 timecode)
{
return calculate_ntsc_drop_frame_number(timecode) / 30 / 60 / 60 % 24;
}
int
time_hours (raw_time_64 time)
{
return time / TIME_SCALE_sec / 60 / 60;
}
int
time_minutes (raw_time_64 time)
{
return (time / TIME_SCALE_sec / 60) % 60;
}
int
time_seconds (raw_time_64 time)
{
return (time / TIME_SCALE_sec) % 60;
}
int
time_millis (raw_time_64 time)
{
return (time / TIME_SCALE_ms) % 1000;
}
}
/******************************************************************//**
* @test document the computation of NTSC drop frame timecode mapping.
*/
class TimeDropframe_test : public Test
{
virtual void
run (Arg)
{
verify_DropFrame_conv();
verify_completeMapping();
}
/** @test perform a drop-frame timecode conversion
* and verify the complete round-trip is correct.
*/
void
verify_DropFrame_conv()
{
// Make sure frame 0 begins at 0
raw_time_64 t = lumiera_build_time_ntsc_drop (0, 0, 0, 0);
raw_time_64 t = build_time_from_ntsc_drop_frame (0, 0, 0, 0);
CHECK (t == 0);
CHECK ((raw_time_64) t == 0);
CHECK (lumiera_time_millis (t) == 0);
CHECK (lumiera_time_seconds (t) == 0);
CHECK (lumiera_time_minutes (t) == 0);
CHECK (lumiera_time_hours (t) == 0);
CHECK (lumiera_time_frames (t, FPS) == 0);
CHECK (lumiera_time_frames (t, FPS+5) == 0);
CHECK (calculate_framecount (t, FPS) == 0);
CHECK (calculate_framecount (t, FPS+5) == 0);
t = lumiera_build_time_ntsc_drop (FRAMES, SECONDS, MINUTES, HOURS);
t = build_time_from_ntsc_drop_frame (FRAMES, SECONDS, MINUTES, HOURS);
// Calculate manually what result to expect....
int frames = FRAMES + 30*SECONDS + 30*60*MINUTES + 30*60*60*HOURS; // sum up using nominal 30fps
@ -131,33 +132,37 @@ TEST (ntsc_drop_frame)
int64_t expectedMillis = 1000LL * frames * 1001/30000; // now convert frames to time, using the real framerate
expectedMillis %= 1000; // look at the remainder..
CHECK (lumiera_time_millis (t) == expectedMillis);
CHECK (time_millis (t) == expectedMillis);
CHECK (lumiera_time_seconds (t) == SECONDS); // while all other components should come out equal as set
CHECK (lumiera_time_minutes (t) == MINUTES);
CHECK (lumiera_time_hours (t) == HOURS);
CHECK (time_seconds (t) == SECONDS); // while all other components should come out equal as set
CHECK (time_minutes (t) == MINUTES);
CHECK (time_hours (t) == HOURS);
// Reverse calculate frames for NTSC drop
//CHECK (lumiera_quantise_frames (t, 0, dropFrameDuration) == frames); // the total nominal frames
CHECK (lumiera_time_ntsc_drop_frames (t) == FRAMES); // maximum one frame off due to rounding
CHECK (dropframe_frames (t) == FRAMES); // maximum one frame off due to rounding
// Cover the whole value range;
// Manually construct a drop-frame timecode
// Make sure our library function returns the same times.
int min;
int sec;
int frame;
int hrs;
for (hrs = 0; hrs <= 24; hrs += 6)
for (min = 0; min <= 59; min += 1)
for (sec = 0; sec <= 59; sec += 10)
for (frame = 0; frame <= 29; frame++)
}
/** @test Cover the whole value range of a day in drop-frame:
* - manually construct a drop-frame timecode
* - make sure our library function returns the same times.
*/
void
verify_completeMapping()
{
for (int hrs = 0; hrs <= 24; hrs += 6)
for (int min = 0; min <= 59; min += 1)
for (int sec = 0; sec <= 59; sec += 10)
for (int frame = 0; frame <= 29; frame++)
{
// Skip dropped frames
if (min % 10 && sec == 0 && frame < 2)
continue;
t = lumiera_build_time_ntsc_drop(frame, sec, min, hrs);
raw_time_64 t = build_time_from_ntsc_drop_frame(frame, sec, min, hrs);
/*
ECHO ("%02d:%02d:%02d;%02d"
, lumiera_time_ntsc_drop_hours (t)
@ -166,20 +171,26 @@ TEST (ntsc_drop_frame)
, lumiera_time_ntsc_drop_frames (t)
);
*/
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
CHECK (lumiera_time_ntsc_drop_seconds (t) == sec);
CHECK (lumiera_time_ntsc_drop_minutes (t) == min);
CHECK (lumiera_time_ntsc_drop_hours (t) == hrs % 24);
CHECK (dropframe_frames (t) == frame);
CHECK (dropframe_seconds (t) == sec);
CHECK (dropframe_minutes (t) == min);
CHECK (dropframe_hours (t) == hrs % 24);
}
// Make sure we do not get non-existent frames
int i;
for (i = 0; i < 59; i++)
for (int i = 0; i < 59; i++)
{
int frame = (i % 10 == 0) ? 0 : 2;
t = lumiera_build_time_ntsc_drop (frame, 0, i, 0);
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
raw_time_64 t = build_time_from_ntsc_drop_frame (frame, 0, i, 0);
CHECK (dropframe_frames (t) == frame);
}
}
};
TESTS_END
/** Register this test class... */
LAUNCHER (TimeDropframe_test, "unit common");
}}} // namespace lib::time::test

View file

@ -1,130 +0,0 @@
/*
TEST-TIME - test the time conversion lib
Copyright (C)
2010, Stefan Kangas <skangas@skangas.se>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
* *****************************************************************/
/** @file test-time.c
** C unit test to cover the basic low-level time handling operations
** @see time.h
** @see timevalue.hpp
** @see TimeValue_test high-level time entities
*/
#include "lib/test/test.h"
#include <inttypes.h>
#include <ctype.h>
#include <nobug.h>
typedef int64_t raw_time_64; //////////////////////////////////////////////////////////////////////////////TICKET #1259 turn time.h in to a C++ implementation header
#include "lib/time.h" /////////////////////////////////////////////////////////////////////////////////////TICKET #1259 this should not be a general purpose library, but rather an implementation base
static int
calculate_framecount (raw_time_64 t, uint fps)
{
return lumiera_quantise_frames_fps (t,0,fps);
}
TESTS_BEGIN
const int FRAMES = 15;
const int SECONDS = 20;
const int MINUTES = 55;
const int HOURS = 3;
const int FPS = 24;
/*
* 3. NTSC drop-frame calculations.
*/
TEST (ntsc_drop_frame)
{
// Make sure frame 0 begins at 0
raw_time_64 t = lumiera_build_time_ntsc_drop (0, 0, 0, 0);
CHECK ((raw_time_64) t == 0);
CHECK (lumiera_time_millis (t) == 0);
CHECK (lumiera_time_seconds (t) == 0);
CHECK (lumiera_time_minutes (t) == 0);
CHECK (lumiera_time_hours (t) == 0);
CHECK (lumiera_time_frames (t, FPS) == 0);
CHECK (lumiera_time_frames (t, FPS+5) == 0);
CHECK (calculate_framecount (t, FPS) == 0);
CHECK (calculate_framecount (t, FPS+5) == 0);
t = lumiera_build_time_ntsc_drop (FRAMES, SECONDS, MINUTES, HOURS);
// Calculate manually what result to expect....
int frames = FRAMES + 30*SECONDS + 30*60*MINUTES + 30*60*60*HOURS; // sum up using nominal 30fps
int minutes_to_drop_frames = (MINUTES - MINUTES/10) + (HOURS * 54); // but every minute, with the exception of every 10 minutes...
frames -= 2*minutes_to_drop_frames; // ...drop 2 frames
int64_t expectedMillis = 1000LL * frames * 1001/30000; // now convert frames to time, using the real framerate
expectedMillis %= 1000; // look at the remainder..
CHECK (lumiera_time_millis (t) == expectedMillis);
CHECK (lumiera_time_seconds (t) == SECONDS); // while all other components should come out equal as set
CHECK (lumiera_time_minutes (t) == MINUTES);
CHECK (lumiera_time_hours (t) == HOURS);
// Reverse calculate frames for NTSC drop
//CHECK (lumiera_quantise_frames (t, 0, dropFrameDuration) == frames); // the total nominal frames
CHECK (lumiera_time_ntsc_drop_frames (t) == FRAMES); // maximum one frame off due to rounding
// Cover the whole value range;
// Manually construct a drop-frame timecode
// Make sure our library function returns the same times.
int min;
int sec;
int frame;
int hrs;
for (hrs = 0; hrs <= 24; hrs += 6)
for (min = 0; min <= 59; min += 1)
for (sec = 0; sec <= 59; sec += 10)
for (frame = 0; frame <= 29; frame++)
{
// Skip dropped frames
if (min % 10 && sec == 0 && frame < 2)
continue;
t = lumiera_build_time_ntsc_drop(frame, sec, min, hrs);
/*
ECHO ("%02d:%02d:%02d;%02d"
, lumiera_time_ntsc_drop_hours (t)
, lumiera_time_ntsc_drop_minutes (t)
, lumiera_time_ntsc_drop_seconds (t)
, lumiera_time_ntsc_drop_frames (t)
);
*/
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
CHECK (lumiera_time_ntsc_drop_seconds (t) == sec);
CHECK (lumiera_time_ntsc_drop_minutes (t) == min);
CHECK (lumiera_time_ntsc_drop_hours (t) == hrs % 24);
}
// Make sure we do not get non-existent frames
int i;
for (i = 0; i < 59; i++)
{
int frame = (i % 10 == 0) ? 0 : 2;
t = lumiera_build_time_ntsc_drop (frame, 0, i, 0);
CHECK (lumiera_time_ntsc_drop_frames (t) == frame);
}
}
TESTS_END

View file

@ -162886,8 +162886,7 @@ Since then others have made contributions, see the log for the history.</font></
...nachdem gavl_time_t durch einen Typedef ersetzt ist, und ich das Thema durch #1261 bereits abgesteckt habe, k&#246;nnte der Bestand einer eigenst&#228;ndigen Basis-Library durch wenige Umordnungen aufgehoben werden. Denn darum geht es mir: ich will in Lumiera eine Ordnung schaffen, in der gedankenloses einfach-mal-Machen keinen Raum findet.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1747520507617" ID="ID_391130031" MODIFIED="1748350248986" TEXT="sollte die konkreten Verwendungen durchsehen">
<richcontent TYPE="NOTE"><html>
<head/>
@ -162896,8 +162895,7 @@ Since then others have made contributions, see the log for the history.</font></
...m&#246;glicherweise lassen sich diese Funktionen n&#228;mlich in Gruppen einteilen und dann direkt in einen anonymen namespace in die jeweilige Translation-Unit schieben....
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1747520518579" ID="ID_1232667630" MODIFIED="1747520531129" TEXT="m&#xfc;&#xdf;te dann auch den C-Test (test-time) aufl&#xf6;sen">
<node CREATED="1747520690709" ID="ID_920147641" MODIFIED="1747520695437" TEXT="der ist eigentlich nicht lang"/>
@ -163166,7 +163164,8 @@ Since then others have made contributions, see the log for the history.</font></
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
</node>
</node>
<node CREATED="1748363166124" ID="ID_20271646" MODIFIED="1748363183352" TEXT="also: stattdessen rigoros mit Stubs arbeiten">
<node CREATED="1748363166124" ID="ID_20271646" MODIFIED="1748388589043" TEXT="also: stattdessen rigoros mit Stubs arbeiten">
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748363186304" ID="ID_1752179075" MODIFIED="1748363276225" TEXT="d.h. das Timecode-Widget ganz ersichtlich brechen">
<richcontent TYPE="NOTE"><html>
<head/>
@ -163175,8 +163174,7 @@ Since then others have made contributions, see the log for the history.</font></
gebrochen ist es ja ohnehin und sowiso, und weiter damit arbeiten m&#246;chte ich nicht &#8212; ganz ehrlich, das war ein &#187;Griff ins Klo&#171;, das ist kein Code, den man erhalten sollte.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="yes"/>
</node>
<node COLOR="#435e98" CREATED="1748370343922" ID="ID_767625066" MODIFIED="1748370424799" TEXT="weitere Verwendungen der Dezimator-Funktionen direkt inlinen">
@ -163192,11 +163190,46 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1748370446771" ID="ID_514685019" MODIFIED="1748370458042" TEXT="Drop-Frame-Berechnung dokumentieren">
<icon BUILTIN="pencil"/>
<node COLOR="#435e98" CREATED="1748370471401" ID="ID_241597558" MODIFIED="1748370583898" TEXT="teset-time.c &#x27fc; time/time-dropframe-test.cpp">
<node COLOR="#338800" CREATED="1748370446771" ID="ID_514685019" MODIFIED="1748388515380" TEXT="Drop-Frame-Berechnung dokumentieren">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1748370471401" ID="ID_241597558" MODIFIED="1748383700953" TEXT="test-time.c &#x27fc; time/time-dropframe-test.cpp">
<icon BUILTIN="list"/>
</node>
<node COLOR="#338800" CREATED="1748383707870" ID="ID_1871702886" MODIFIED="1748388462890" TEXT="Funktionen umordnen">
<icon BUILTIN="button_ok"/>
<node CREATED="1748383746542" ID="ID_1117132995" MODIFIED="1748383761481" TEXT="umziehen in Namespace lib::time"/>
<node CREATED="1748383717910" ID="ID_6099644" MODIFIED="1748383730804" TEXT="umziehen in Header lib/time/dropframe.hpp"/>
<node CREATED="1748383731428" ID="ID_1472070007" MODIFIED="1748383743307" TEXT="Implementierung verbleibt in lib/time/time.cpp"/>
<node CREATED="1748383780122" ID="ID_170667006" MODIFIED="1748383790175" TEXT="nur die eigentliche Berechnungslogik erhalten">
<node CREATED="1748383816257" ID="ID_1470396784" MODIFIED="1748384512960" TEXT="int64_t calculate_ntsc_drop_frame_number (raw_time_64 timecode)"/>
<node CREATED="1748383835599" ID="ID_328152526" MODIFIED="1748385988435" TEXT="raw_time_64 build_time_from_ntsc_drop_frame (frames, secs, mins, hours)"/>
</node>
<node COLOR="#435e98" CREATED="1748388465815" ID="ID_1825328642" MODIFIED="1748388490811" STYLE="fork" TEXT="all die Konverter / Deizimator-Funktionen direkt lokal im Test definieren">
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#174956" COLOR="#8dfd49" CREATED="1748388518698" ID="ID_1682762507" MODIFIED="1748388574315" STYLE="bubble" TEXT="umgezogener Test l&#xe4;uft GR&#xdc;N">
<edge COLOR="#61fe5b" STYLE="bezier" WIDTH="thin"/>
<font NAME="SansSerif" SIZE="15"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#8b2f98" CREATED="1748388204363" ID="ID_926423450" MODIFIED="1748388454445" TEXT=" &#xd83d;&#xdc80; totmachen &#xd83d;&#xdc80;">
<node COLOR="#435e98" CREATED="1748388215333" ID="ID_1531959638" MODIFIED="1748388508038" TEXT="test-time.c">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="back"/>
</node>
<node COLOR="#435e98" CREATED="1748388277997" ID="ID_1633487385" MODIFIED="1748388508037" TEXT="lumiera_build_time_fps (uint fps, uint frames, uint secs, uint mins, uint hours)"/>
<node COLOR="#435e98" CREATED="1748388277999" ID="ID_260910175" MODIFIED="1748388508037" TEXT="lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)"/>
<node COLOR="#435e98" CREATED="1748388278001" ID="ID_1058883405" MODIFIED="1748388508037" TEXT="lumiera_time_hours (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278002" ID="ID_235432384" MODIFIED="1748388508037" TEXT="lumiera_time_minutes (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278003" ID="ID_493874247" MODIFIED="1748388508036" TEXT="lumiera_time_seconds (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278005" ID="ID_1348006733" MODIFIED="1748388508036" TEXT="lumiera_time_millis (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278006" ID="ID_1001439160" MODIFIED="1748388508036" TEXT="lumiera_time_frames (raw_time_64 time, uint fps)"/>
<node COLOR="#435e98" CREATED="1748388278008" ID="ID_18545784" MODIFIED="1748388508036" TEXT="lumiera_time_ntsc_drop_frames (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278010" ID="ID_1558655339" MODIFIED="1748388508035" TEXT="lumiera_time_ntsc_drop_seconds (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278011" ID="ID_1940069424" MODIFIED="1748388508035" TEXT="lumiera_time_ntsc_drop_minutes (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278013" ID="ID_1868369312" MODIFIED="1748388508035" TEXT="lumiera_time_ntsc_drop_hours (raw_time_64 time)"/>
<node COLOR="#435e98" CREATED="1748388278014" ID="ID_275417741" MODIFIED="1748388508019" TEXT="lumiera_tmpbuf_print_time (raw_time_64 time)"/>
</node>
</node>
</node>
</node>