From 255fc82a1b32b420cfbb85add0efc66fdb1280be Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 27 May 2025 23:39:36 +0200 Subject: [PATCH] clean-up: relocate NTSC drop-frame conversion functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...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 --- src/lib/time.h | 79 ------ src/lib/time/dropframe.hpp | 248 ++++------------- src/lib/time/time.cpp | 126 +++------ tests/11time.tests | 5 - tests/25fundamental.tests | 2 +- tests/basics/time/time-dropframe-test.cpp | 323 +++++++++++----------- tests/library/c-lib/test-time.c | 130 --------- wiki/thinkPad.ichthyo.mm | 53 +++- 8 files changed, 305 insertions(+), 661 deletions(-) delete mode 100644 tests/11time.tests delete mode 100644 tests/library/c-lib/test-time.c diff --git a/src/lib/time.h b/src/lib/time.h index 8996aa3ce..93c84893a 100644 --- a/src/lib/time.h +++ b/src/lib/time.h @@ -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); diff --git a/src/lib/time/dropframe.hpp b/src/lib/time/dropframe.hpp index 1ef9e39e1..fe3bc8ff1 100644 --- a/src/lib/time/dropframe.hpp +++ b/src/lib/time/dropframe.hpp @@ -1,5 +1,5 @@ /* - DROPFRAME.hpp - Utilities for handling time + DROPFRAME.hpp - drop-frame timecode conversions Copyright (C) 2010, Stefan Kangas @@ -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 - -#ifdef __cplusplus /*=================== C++ facilities ===================== */ #include "lib/time/timevalue.hpp" -using lib::time::raw_time_64; -/** - * 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. - */ -raw_time_64 -lumiera_rational_to_time (lib::time::FSecs const& fractionalSeconds); - - -/** - * 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. - * @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); - - - -#ifdef __cplusplus -}//extern "C" -#endif +namespace lib { +namespace time { + + + /** + * Compute the consecutive frame number from a given time, + * which is interpreted at the NTSC drop frame timecode grid. + */ + int64_t calculate_ntsc_drop_frame_number (raw_time_64 time); + + + /** + * 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 build_time_from_ntsc_drop_frame (uint frames, uint secs, uint mins, uint hours); + + + +}} // lib::time #endif /*LUMIERA_TIME_DROPFRAME_H*/ diff --git a/src/lib/time/time.cpp b/src/lib/time/time.cpp index 83d94783e..f819f26f7 100644 --- a/src/lib/time/time.cpp +++ b/src/lib/time/time.cpp @@ -42,10 +42,6 @@ #include "lib/format-string.hpp" #include "lib/util.hpp" -extern "C" { -#include "lib/tmpbuf.h" -} - #include #include #include @@ -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) - -/* ===== NTSC drop-frame conversions ===== */ +namespace lib { +namespace time { ////////////////////////////////////////////////////////////////////////////////////////////TICKET #1259 : move all calculation functions into a C++ namespace -namespace { // implementation helper + /* ===== NTSC drop-frame conversions ===== */ + + 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; + 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,44 +572,30 @@ 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; -} - -raw_time_64 -lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours) -{ - uint64_t total_mins = 60 * hours + mins; - uint64_t total_frames = 30*60*60 * hours - + 30*60 * mins - + 30 * secs - + frames - - 2 * (total_mins - total_mins / 10); - raw_time_64 result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC); - if (0 != result) // compensate for truncating down on conversion - result += 1; // without this adjustment the frame number - return result; // would turn out off by -1 on back conversion -} + + /** + * @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 + 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 + + 30*60 * mins + + 30 * secs + + frames + - 2 * (total_mins - total_mins / 10); + raw_time_64 result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC); + + if (0 != result) // compensate for truncating down on conversion + result += 1; // without this adjustment the frame number + return result; // would turn out off by -1 on back conversion + } + + +}} // lib::time diff --git a/tests/11time.tests b/tests/11time.tests deleted file mode 100644 index 087363243..000000000 --- a/tests/11time.tests +++ /dev/null @@ -1,5 +0,0 @@ -TESTING "Time conversion" ./test-time - -TEST "ntsc drop-frame calculations" ntsc_drop_frame << END -return: 0 -END diff --git a/tests/25fundamental.tests b/tests/25fundamental.tests index b7f2b2151..c89047464 100644 --- a/tests/25fundamental.tests +++ b/tests/25fundamental.tests @@ -130,7 +130,7 @@ return: 0 END -PLANNED "Drop-Frame calculation" TimeDropframe_test < -#include -#include - -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); -} - - -TESTS_BEGIN - -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) -{ - // 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)); -} - -/* - * 2. Frame rate dependent calculations. - */ - -TEST (fps) -{ - 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); -} - -/* - * 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); +namespace lib { +namespace time{ +namespace test{ + using time::Time; + using time::TimeVar; + using time::FSecs; + using time::raw_time_64; - - 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++) + namespace {// Test setup + const int FRAMES = 15; + const int MILLIS = 700; + const int SECONDS = 20; + const int MINUTES = 55; + const int HOURS = 3; + + + /* ====== 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) { - 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); + return calculate_ntsc_drop_frame_number(timecode) % 30; } -} + + int + dropframe_seconds (raw_time_64 timecode) + { + return calculate_ntsc_drop_frame_number(timecode) / 30 % 60; + } + + int + dropframe_minutes (raw_time_64 timecode) + { + return calculate_ntsc_drop_frame_number(timecode) / 30 / 60 % 60; + } + + 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 = build_time_from_ntsc_drop_frame (0, 0, 0, 0); + CHECK (t == 0); + + 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 + 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 (time_millis (t) == expectedMillis); + + 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 (dropframe_frames (t) == FRAMES); // maximum one frame off due to rounding -TESTS_END + } + + + + /** @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; + + 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) + , lumiera_time_ntsc_drop_minutes (t) + , lumiera_time_ntsc_drop_seconds (t) + , lumiera_time_ntsc_drop_frames (t) + ); + */ + 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 + for (int i = 0; i < 59; i++) + { + int frame = (i % 10 == 0) ? 0 : 2; + raw_time_64 t = build_time_from_ntsc_drop_frame (frame, 0, i, 0); + CHECK (dropframe_frames (t) == frame); + } + } + }; + + + /** Register this test class... */ + LAUNCHER (TimeDropframe_test, "unit common"); + + + +}}} // namespace lib::time::test diff --git a/tests/library/c-lib/test-time.c b/tests/library/c-lib/test-time.c deleted file mode 100644 index b808070ba..000000000 --- a/tests/library/c-lib/test-time.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - TEST-TIME - test the time conversion lib - - Copyright (C) - 2010, Stefan Kangas - -  **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 -#include -#include - -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 diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 4a6c6235a..3820781ed 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -162886,8 +162886,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -162896,8 +162895,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -163166,7 +163164,8 @@ Since then others have made contributions, see the log for the history. - + + @@ -163175,8 +163174,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -163192,11 +163190,46 @@ Since then others have made contributions, see the log for the history. - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +