/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2008 Blender Foundation. All rights reserved. */ /** \file * \ingroup bli * * Time-Code string formatting */ #include #include "BLI_math.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_timecode.h" /* own include */ #include "DNA_userdef_types.h" /* for eTimecodeStyles only */ #include "BLI_strict_flags.h" size_t BLI_timecode_string_from_time(char *str, const size_t maxncpy, const int brevity_level, const float time_seconds, const double fps, const short timecode_style) { int hours = 0, minutes = 0, seconds = 0, frames = 0; float time = time_seconds; char neg[2] = {'\0'}; size_t rlen; /* get cframes */ if (time < 0) { /* Correction for negative cframes. */ neg[0] = '-'; time = -time; } if (time >= 3600.0f) { /* hours */ /* XXX should we only display a single digit for hours since clips are * VERY UNLIKELY to be more than 1-2 hours max? However, that would * go against conventions... */ hours = (int)time / 3600; time = fmodf(time, 3600); } if (time >= 60.0f) { /* minutes */ minutes = (int)time / 60; time = fmodf(time, 60); } if (brevity_level <= 0) { /* seconds + frames * Frames are derived from 'fraction' of second. We need to perform some additional rounding * to cope with 'half' frames, etc., which should be fine in most cases */ seconds = (int)time; frames = round_fl_to_int((float)(((double)time - (double)seconds) * fps)); } else { /* seconds (with pixel offset rounding) */ seconds = round_fl_to_int(time); } switch (timecode_style) { case USER_TIMECODE_MINIMAL: { /* - In general, minutes and seconds should be shown, as most clips will be * within this length. Hours will only be included if relevant. * - Only show frames when zoomed in enough for them to be relevant * (using separator of '+' for frames). * When showing frames, use slightly different display to avoid confusion with mm:ss format */ if (brevity_level <= 0) { /* include "frames" in display */ if (hours) { rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d:%02d+%02d", neg, hours, minutes, seconds, frames); } else if (minutes) { rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d+%02d", neg, minutes, seconds, frames); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%s00:%02d+%02d", neg, seconds, frames); } } else { /* don't include 'frames' in display */ if (hours) { rlen = BLI_snprintf_rlen(str, maxncpy, "%s%02d:%02d:%02d", neg, hours, minutes, seconds); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%s%02d:%02d", neg, minutes, seconds); } } break; } case USER_TIMECODE_SMPTE_MSF: { /* reduced SMPTE format that always shows minutes, seconds, frames. * Hours only shown as needed. */ if (hours) { rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%s%02d:%02d:%02d", neg, minutes, seconds, frames); } break; } case USER_TIMECODE_MILLISECONDS: { /* reduced SMPTE. Instead of frames, milliseconds are shown */ /* precision of decimal part */ const int ms_dp = (brevity_level <= 0) ? (1 - brevity_level) : 1; /* to get 2 digit whole-number part for seconds display * (i.e. 3 is for 2 digits + radix, on top of full length) */ const int s_pad = ms_dp + 3; if (hours) { rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d:%0*.*f", neg, hours, minutes, s_pad, ms_dp, time); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%s%02d:%0*.*f", neg, minutes, s_pad, ms_dp, time); } break; } case USER_TIMECODE_SUBRIP: { /* SubRip, like SMPTE milliseconds but seconds and milliseconds * are separated by a comma, not a dot... */ /* precision of decimal part */ const int ms_dp = (brevity_level <= 0) ? (1 - brevity_level) : 1; const int ms = round_fl_to_int((time - (float)seconds) * 1000.0f); rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d:%02d,%0*d", neg, hours, minutes, seconds, ms_dp, ms); break; } case USER_TIMECODE_SECONDS_ONLY: { /* only show the original seconds display */ /* round to whole numbers if brevity_level is >= 1 (i.e. scale is coarse) */ if (brevity_level <= 0) { rlen = BLI_snprintf_rlen(str, maxncpy, "%.*f", 1 - brevity_level, time_seconds); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%d", round_fl_to_int(time_seconds)); } break; } case USER_TIMECODE_SMPTE_FULL: default: { /* full SMPTE format */ rlen = BLI_snprintf_rlen( str, maxncpy, "%s%02d:%02d:%02d:%02d", neg, hours, minutes, seconds, frames); break; } } return rlen; } size_t BLI_timecode_string_from_time_simple(char *str, const size_t maxncpy, const double time_seconds) { size_t rlen; /* format 00:00:00.00 (hr:min:sec) string has to be 12 long */ const int hr = ((int)time_seconds) / (60 * 60); const int min = (((int)time_seconds) / 60) % 60; const int sec = ((int)time_seconds) % 60; const int hun = (int)(fmod(time_seconds, 1.0) * 100); if (hr) { rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d:%.2d.%.2d", hr, min, sec, hun); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%.2d:%.2d.%.2d", min, sec, hun); } return rlen; } size_t BLI_timecode_string_from_time_seconds(char *str, const size_t maxncpy, const int brevity_level, const float time_seconds) { size_t rlen; /* round to whole numbers if brevity_level is >= 1 (i.e. scale is coarse) */ if (brevity_level <= 0) { rlen = BLI_snprintf_rlen(str, maxncpy, "%.*f", 1 - brevity_level, time_seconds); } else { rlen = BLI_snprintf_rlen(str, maxncpy, "%d", round_fl_to_int(time_seconds)); } return rlen; }