changeset 1070:b8a788c9a6f1

Fixes to, and tests for, RealTime-to-text methods
author Chris Cannam
date Tue, 12 May 2015 16:19:45 +0100
parents 32ab6c48efaa
children f4ad0bfceeb7
files base/RealTime.cpp base/RealTime.h base/test/TestRealTime.h
diffstat 3 files changed, 186 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/base/RealTime.cpp	Mon Apr 20 09:11:34 2015 +0100
+++ b/base/RealTime.cpp	Tue May 12 16:19:45 2015 +0100
@@ -258,7 +258,10 @@
     if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp);
 
     Preferences *p = Preferences::getInstance();
+    bool hms = true;
+    
     if (p) {
+        hms = p->getShowHMS();
         int fps = 0;
         switch (p->getTimeToTextMode()) {
         case Preferences::TimeToTextMs: break;
@@ -269,19 +272,24 @@
         case Preferences::TimeToText50Frame: fps = 50; break;
         case Preferences::TimeToText60Frame: fps = 60; break;
         }
-        if (fps != 0) return toFrameText(fps);
+        if (fps != 0) return toFrameText(fps, hms);
     }
 
-    std::stringstream out;
+    return toMSText(fixedDp, hms);
+}
 
-    if (p->getShowHMS()) {
-    
+static void
+writeSecPart(std::stringstream &out, bool hms, int sec)
+{
+    if (hms) {
         if (sec >= 3600) {
             out << (sec / 3600) << ":";
         }
 
         if (sec >= 60) {
-            out << (sec % 3600) / 60 << ":";
+            int minutes = (sec % 3600) / 60;
+            if (sec >= 3600 && minutes < 10) out << "0";
+            out << minutes << ":";
         }
 
         if (sec >= 10) {
@@ -293,6 +301,16 @@
     } else {
         out << sec;
     }
+}
+
+std::string
+RealTime::toMSText(bool fixedDp, bool hms) const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms);
+
+    std::stringstream out;
+
+    writeSecPart(out, hms, sec);
     
     int ms = msec();
 
@@ -321,35 +339,18 @@
 }
 
 std::string
-RealTime::toFrameText(int fps) const
+RealTime::toFrameText(int fps, bool hms) const
 {
-    if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps);
-
-    Preferences *p = Preferences::getInstance();
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms);
 
     std::stringstream out;
 
-    if (p->getShowHMS()) {
-    
-        if (sec >= 3600) {
-            out << (sec / 3600) << ":";
-        }
+    writeSecPart(out, hms, sec);
 
-        if (sec >= 60) {
-            out << (sec % 3600) / 60 << ":";
-        }
-
-        if (sec >= 10) {
-            out << ((sec % 60) / 10);
-        }
-
-        out << (sec % 10);
-
-    } else {
-        out << sec;
-    }
-    
-    int f = nsec / (ONE_BILLION / fps);
+    // avoid rounding error if fps does not divide into ONE_BILLION
+    int64_t fbig = nsec;
+    fbig *= fps;
+    int f = int(fbig / ONE_BILLION);
 
     int div = 1;
     int n = fps - 1;
@@ -381,19 +382,7 @@
 
     std::stringstream out;
 
-    if (sec >= 3600) {
-	out << (sec / 3600) << ":";
-    }
-
-    if (sec >= 60) {
-	out << (sec % 3600) / 60 << ":";
-    }
-
-    if (sec >= 10) {
-	out << ((sec % 60) / 10);
-    }
-
-    out << (sec % 10);
+    writeSecPart(out, true, sec);
     
     if (sec < 60) {
         out << "s";
--- a/base/RealTime.h	Mon Apr 20 09:11:34 2015 +0100
+++ b/base/RealTime.h	Tue May 12 16:19:45 2015 +0100
@@ -18,8 +18,8 @@
    This file copyright 2000-2006 Chris Cannam.
 */
 
-#ifndef _REAL_TIME_H_
-#define _REAL_TIME_H_
+#ifndef SV_REAL_TIME_H
+#define SV_REAL_TIME_H
 
 #include "BaseTypes.h"
 
@@ -128,23 +128,55 @@
     static RealTime fromString(std::string);
 
     /**
-     * Return a user-readable string to the nearest millisecond, in a
-     * form like HH:MM:SS.mmm
+     * Return a user-readable string to the nearest millisecond,
+     * typically in a form like HH:MM:SS.mmm. The exact format will
+     * depend on the application preferences for time display
+     * precision and hours:minutes:seconds format -- this function
+     * simply dispatches to toMSText or toFrameText with appropriate
+     * arguments depending on the preferences.
+     *
+     * If fixedDp is true, the result will be padded to 3 dp,
+     * i.e. millisecond resolution, even if the number of milliseconds
+     * is a multiple of 10.
      */
     std::string toText(bool fixedDp = false) const;
 
+    /** 
+     * Return a user-readable string to the nearest millisecond.
+     *
+     * If fixedDp is true, the result will be padded to 3 dp,
+     * i.e. millisecond resolution, even if the number of milliseconds
+     * is a multiple of 10.
+     *
+     * If hms is true, results may be returned in the form
+     * HH:MM:SS.mmm (if the time is large enough). If hms is false,
+     * the result will always be a (fractional) number of seconds.
+     *
+     * Unlike toText, this function does not depend on the application
+     * preferences.
+     */
+    std::string toMSText(bool fixedDp, bool hms) const;
+    
     /**
      * Return a user-readable string in which seconds are divided into
      * frames (presumably at a lower frame rate than audio rate,
      * e.g. 24 or 25 video frames), in a form like HH:MM:SS:FF.  fps
      * gives the number of frames per second, and must be integral
      * (29.97 not supported).
+     *
+     * Unlike toText, this function does not depend on the application
+     * preferences.
      */
-    std::string toFrameText(int fps) const;
+    std::string toFrameText(int fps, bool hms) const;
 
     /**
-     * Return a user-readable string to the nearest second, in a form
-     * like "6s" (for less than a minute) or "2:21" (for more).
+     * Return a user-readable string to the nearest second, in H:M:S
+     * form. Does not include milliseconds or frames. The result will
+     * be suffixed "s" if it contains only seconds (no hours or
+     * minutes).
+     *
+     * Unlike toText, this function does not depend on the application
+     * preferences.
      */
     std::string toSecText() const;
 
--- a/base/test/TestRealTime.h	Mon Apr 20 09:11:34 2015 +0100
+++ b/base/test/TestRealTime.h	Tue May 12 16:19:45 2015 +0100
@@ -29,6 +29,10 @@
 {
     Q_OBJECT
 
+    void compareTexts(string s, const char *e) {
+        QCOMPARE(QString(s.c_str()), QString(e));
+    }
+
 private slots:
 
 #define ONE_MILLION 1000000
@@ -296,6 +300,118 @@
             }
         }
     }
+    
+    void toText()
+    {
+        // we want to use QStrings, because then the Qt test library
+        // will print out any conflicts. The compareTexts function
+        // does this for us
+
+        int halfSec = ONE_BILLION/2; // nsec
+        
+        RealTime rt = RealTime(0, 0);
+        compareTexts(rt.toMSText(false, false), "0");
+        compareTexts(rt.toMSText(true, false), "0.000");
+        compareTexts(rt.toMSText(false, true), "0");
+        compareTexts(rt.toMSText(true, true), "0.000");
+        compareTexts(rt.toFrameText(24, false), "0:00");
+        compareTexts(rt.toFrameText(24, true), "0:00");
+        compareTexts(rt.toSecText(), "0s");
+
+        rt = RealTime(1, halfSec);
+        compareTexts(rt.toMSText(false, false), "1.5");
+        compareTexts(rt.toMSText(true, false), "1.500");
+        compareTexts(rt.toMSText(false, true), "1.5");
+        compareTexts(rt.toMSText(true, true), "1.500");
+        compareTexts(rt.toFrameText(24, false), "1:12");
+        compareTexts(rt.toFrameText(24, true), "1:12");
+        compareTexts(rt.toFrameText(25, false), "1:12");
+        compareTexts(rt.toFrameText(25, true), "1:12");
+        compareTexts(rt.toSecText(), "1s");
+
+        rt = RealTime::fromSeconds(-1.5);
+        compareTexts(rt.toMSText(false, false), "-1.5");
+        compareTexts(rt.toMSText(true, false), "-1.500");
+        compareTexts(rt.toMSText(false, true), "-1.5");
+        compareTexts(rt.toMSText(true, true), "-1.500");
+        compareTexts(rt.toFrameText(24, false), "-1:12");
+        compareTexts(rt.toFrameText(24, true), "-1:12");
+        compareTexts(rt.toSecText(), "-1s");
+
+        rt = RealTime(1, 1000);
+        compareTexts(rt.toMSText(false, false), "1");
+        compareTexts(rt.toFrameText(24, false), "1:00");
+        compareTexts(rt.toFrameText(ONE_MILLION, false), "1:000001");
+        compareTexts(rt.toSecText(), "1s");
+
+        rt = RealTime(1, 100000);
+        compareTexts(rt.toFrameText(ONE_MILLION, false), "1:000100");
+        compareTexts(rt.toSecText(), "1s");
+
+        rt = RealTime::fromSeconds(60);
+        compareTexts(rt.toMSText(false, false), "60");
+        compareTexts(rt.toMSText(true, false), "60.000");
+        compareTexts(rt.toMSText(false, true), "1:00");
+        compareTexts(rt.toMSText(true, true), "1:00.000");
+        compareTexts(rt.toFrameText(24, false), "60:00");
+        compareTexts(rt.toFrameText(24, true), "1:00:00");
+        compareTexts(rt.toSecText(), "1:00");
+
+        rt = RealTime::fromSeconds(61.05);
+        compareTexts(rt.toMSText(false, false), "61.05");
+        compareTexts(rt.toMSText(true, false), "61.050");
+        compareTexts(rt.toMSText(false, true), "1:01.05");
+        compareTexts(rt.toMSText(true, true), "1:01.050");
+        compareTexts(rt.toFrameText(24, false), "61:01");
+        compareTexts(rt.toFrameText(24, true), "1:01:01");
+        compareTexts(rt.toSecText(), "1:01");
+        
+        rt = RealTime::fromSeconds(601.05);
+        compareTexts(rt.toMSText(false, false), "601.05");
+        compareTexts(rt.toMSText(true, false), "601.050");
+        compareTexts(rt.toMSText(false, true), "10:01.05");
+        compareTexts(rt.toMSText(true, true), "10:01.050");
+        compareTexts(rt.toFrameText(24, false), "601:01");
+        compareTexts(rt.toFrameText(24, true), "10:01:01");
+        compareTexts(rt.toSecText(), "10:01");
+        
+        rt = RealTime::fromSeconds(3600);
+        compareTexts(rt.toMSText(false, false), "3600");
+        compareTexts(rt.toMSText(true, false), "3600.000");
+        compareTexts(rt.toMSText(false, true), "1:00:00");
+        compareTexts(rt.toMSText(true, true), "1:00:00.000");
+        compareTexts(rt.toFrameText(24, false), "3600:00");
+        compareTexts(rt.toFrameText(24, true), "1:00:00:00");
+        compareTexts(rt.toSecText(), "1:00:00");
+
+        // For practical reasons our time display always rounds down
+        rt = RealTime(3599, ONE_BILLION-1);
+        compareTexts(rt.toMSText(false, false), "3599.999");
+        compareTexts(rt.toMSText(true, false), "3599.999");
+        compareTexts(rt.toMSText(false, true), "59:59.999");
+        compareTexts(rt.toMSText(true, true), "59:59.999");
+        compareTexts(rt.toFrameText(24, false), "3599:23");
+        compareTexts(rt.toFrameText(24, true), "59:59:23");
+        compareTexts(rt.toSecText(), "59:59");
+
+        rt = RealTime::fromSeconds(3600 * 4 + 60 * 5 + 3 + 0.01);
+        compareTexts(rt.toMSText(false, false), "14703.01");
+        compareTexts(rt.toMSText(true, false), "14703.010");
+        compareTexts(rt.toMSText(false, true), "4:05:03.01");
+        compareTexts(rt.toMSText(true, true), "4:05:03.010");
+        compareTexts(rt.toFrameText(24, false), "14703:00");
+        compareTexts(rt.toFrameText(24, true), "4:05:03:00");
+        compareTexts(rt.toSecText(), "4:05:03");
+
+        rt = RealTime::fromSeconds(-(3600 * 4 + 60 * 5 + 3 + 0.01));
+        compareTexts(rt.toMSText(false, false), "-14703.01");
+        compareTexts(rt.toMSText(true, false), "-14703.010");
+        compareTexts(rt.toMSText(false, true), "-4:05:03.01");
+        compareTexts(rt.toMSText(true, true), "-4:05:03.010");
+        compareTexts(rt.toFrameText(24, false), "-14703:00");
+        compareTexts(rt.toFrameText(24, true), "-4:05:03:00");
+        compareTexts(rt.toSecText(), "-4:05:03");
+    }
 };
 
 #endif