changeset 1265:e2e66bfd4a88 3.0-integration

Start tests for ColumnOp (+ some resulting fixes)
author Chris Cannam
date Thu, 17 Nov 2016 11:56:54 +0000
parents a99641535e02
children dd190086db73
files base/ColumnOp.h base/test/TestColumnOp.h base/test/files.pri base/test/svcore-base-test.cpp
diffstat 4 files changed, 190 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/base/ColumnOp.h	Wed Nov 16 16:12:42 2016 +0000
+++ b/base/ColumnOp.h	Thu Nov 17 11:56:54 2016 +0000
@@ -19,6 +19,9 @@
 #include "BaseTypes.h"
 
 #include <cmath>
+#include <vector>
+#include <algorithm>
+#include <iostream>
 
 /**
  * Display normalization types for columns in e.g. grid plots.
@@ -68,7 +71,7 @@
     }
 
     /**
-     * Scale an FFT output by half the FFT size.
+     * Scale an FFT output downward by half the FFT size.
      */
     static Column fftScale(const Column &in, int fftSize) {
         return applyGain(in, 2.0 / fftSize);
@@ -78,12 +81,21 @@
      * Determine whether an index points to a local peak.
      */
     static bool isPeak(const Column &in, int ix) {
-	
-	if (!in_range_for(in, ix-1)) return false;
-	if (!in_range_for(in, ix+1)) return false;
-	if (in[ix] < in[ix+1]) return false;
-	if (in[ix] < in[ix-1]) return false;
-	
+        if (!in_range_for(in, ix)) {
+            return false;
+        }
+        if (ix == 0) {
+            return in[0] >= in[1];
+        }
+        if (!in_range_for(in, ix+1)) {
+            return in[ix] > in[ix-1];
+        }
+	if (in[ix] < in[ix+1]) {
+            return false;
+        }
+	if (in[ix] <= in[ix-1]) {
+            return false;
+        }
 	return true;
     }
 
@@ -109,7 +121,7 @@
      */
     static Column normalize(const Column &in, ColumnNormalization n) {
 
-	if (n == ColumnNormalization::None) {
+	if (n == ColumnNormalization::None || in.empty()) {
 	    return in;
 	}
 
@@ -148,7 +160,10 @@
      * Distribute the given column into a target vector of a different
      * size, optionally using linear interpolation. The binfory vector
      * contains a mapping from y coordinate (i.e. index into the
-     * target vector) to bin (i.e. index into the source column).
+     * target vector) to bin (i.e. index into the source column). The
+     * source column ("in") may be a partial column; it's assumed to
+     * contain enough bins to span the destination range, starting
+     * with the bin of index minbin.
      */
     static Column distribute(const Column &in,
 			     int h,
@@ -166,6 +181,8 @@
 	    if (y+1 < h) {
 		sy1 = binfory[y+1] - minbin;
 	    }
+
+            std::cerr << "y = " << y << " of " << h << ", sy0 = " << sy0 << ", sy1 = " << sy1 << std::endl;
         
 	    if (interpolate && fabs(sy1 - sy0) < 1.0) {
             
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/test/TestColumnOp.h	Thu Nov 17 11:56:54 2016 +0000
@@ -0,0 +1,156 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program 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 included with this distribution for more information.
+*/
+
+#ifndef TEST_COLUMN_OP_H
+#define TEST_COLUMN_OP_H
+
+#include "../ColumnOp.h"
+
+#include <QObject>
+#include <QtTest>
+#include <QDir>
+
+#include <iostream>
+
+using namespace std;
+
+class TestColumnOp : public QObject
+{
+    Q_OBJECT
+
+    typedef ColumnOp C;
+    typedef ColumnOp::Column Column;
+    
+private slots:
+    void applyGain() {
+        QCOMPARE(C::applyGain({}, 1.0), Column());
+        Column c { 1, 2, 3, -4, 5, 6 };
+        Column actual(C::applyGain(c, 1.5));
+        Column expected { 1.5, 3, 4.5, -6, 7.5, 9 };
+        QCOMPARE(actual, expected);
+        actual = C::applyGain(c, 1.0);
+        QCOMPARE(actual, c);
+        actual = C::applyGain(c, 0.0);
+        expected = { 0, 0, 0, 0, 0, 0 };
+        QCOMPARE(actual, expected);
+    }
+
+    void fftScale() {
+        QCOMPARE(C::fftScale({}, 2.0), Column());
+        Column c { 1, 2, 3, -4, 5 };
+        Column actual(C::fftScale(c, 8));
+        Column expected { 0.25, 0.5, 0.75, -1, 1.25 };
+        QCOMPARE(actual, expected);
+    }
+
+    void isPeak_null() {
+        QVERIFY(!C::isPeak({}, 0));
+        QVERIFY(!C::isPeak({}, 1));
+        QVERIFY(!C::isPeak({}, -1));
+    }
+
+    void isPeak_obvious() {
+        Column c { 0.4, 0.5, 0.3 };
+        QVERIFY(!C::isPeak(c, 0));
+        QVERIFY(C::isPeak(c, 1));
+        QVERIFY(!C::isPeak(c, 2));
+    }
+
+    void isPeak_edges() {
+        Column c { 0.5, 0.4, 0.3 };
+        QVERIFY(C::isPeak(c, 0));
+        QVERIFY(!C::isPeak(c, 1));
+        QVERIFY(!C::isPeak(c, 2));
+        QVERIFY(!C::isPeak(c, 3));
+        QVERIFY(!C::isPeak(c, -1));
+        c = { 1.4, 1.5 };
+        QVERIFY(!C::isPeak(c, 0));
+        QVERIFY(C::isPeak(c, 1));
+    }
+
+    void isPeak_flat() {
+        Column c { 0.0, 0.0, 0.0 };
+        QVERIFY(C::isPeak(c, 0));
+        QVERIFY(!C::isPeak(c, 1));
+        QVERIFY(!C::isPeak(c, 2));
+    }
+
+    void isPeak_mixedSign() {
+        Column c { 0.4, -0.5, -0.3, -0.6, 0.1, -0.3 };
+        QVERIFY(C::isPeak(c, 0));
+        QVERIFY(!C::isPeak(c, 1));
+        QVERIFY(C::isPeak(c, 2));
+        QVERIFY(!C::isPeak(c, 3));
+        QVERIFY(C::isPeak(c, 4));
+        QVERIFY(!C::isPeak(c, 5));
+    }
+
+    void isPeak_duplicate() {
+        Column c({ 0.5, 0.5, 0.4, 0.4 });
+        QVERIFY(C::isPeak(c, 0));
+        QVERIFY(!C::isPeak(c, 1));
+        QVERIFY(!C::isPeak(c, 2));
+        QVERIFY(!C::isPeak(c, 3));
+        c = { 0.4, 0.4, 0.5, 0.5 };
+        QVERIFY(C::isPeak(c, 0)); // counterintuitive but necessary
+        QVERIFY(!C::isPeak(c, 1));
+        QVERIFY(C::isPeak(c, 2));
+        QVERIFY(!C::isPeak(c, 3));
+    }
+
+    void peakPick() {
+        QCOMPARE(C::peakPick({}), Column());
+        Column c({ 0.5, 0.5, 0.4, 0.4 });
+        QCOMPARE(C::peakPick(c), Column({ 0.5, 0.0, 0.0, 0.0 }));
+        c = Column({ 0.4, -0.5, -0.3, -0.6, 0.1, -0.3 });
+        QCOMPARE(C::peakPick(c), Column({ 0.4, 0.0, -0.3, 0.0, 0.1, 0.0 }));
+    }
+
+    void normalize_null() {
+        QCOMPARE(C::normalize({}, ColumnNormalization::None), Column());
+        QCOMPARE(C::normalize({}, ColumnNormalization::Sum1), Column());
+        QCOMPARE(C::normalize({}, ColumnNormalization::Max1), Column());
+        QCOMPARE(C::normalize({}, ColumnNormalization::Hybrid), Column());
+    }
+
+    void normalize_none() {
+        Column c { 1, 2, 3, 4 };
+        QCOMPARE(C::normalize(c, ColumnNormalization::None), c);
+    }
+
+    void normalize_sum1() {
+        Column c { 1, 2, 4, 3 };
+        QCOMPARE(C::normalize(c, ColumnNormalization::Sum1),
+                 Column({ 0.1, 0.2, 0.4, 0.3 }));
+    }
+
+    void normalize_max1() {
+        Column c { 4, 3, 2, 1 };
+        QCOMPARE(C::normalize(c, ColumnNormalization::Max1),
+                 Column({ 1.0, 0.75, 0.5, 0.25 }));
+    }
+
+    void normalize_hybrid() {
+        // with max == 99, log10(max+1) == 2 so scale factor will be 2/99
+        Column c { 22, 44, 99, 66 };
+        QCOMPARE(C::normalize(c, ColumnNormalization::Hybrid),
+                 Column({ 44.0/99.0, 88.0/99.0, 2.0, 132.0/99.0 }));
+    }
+
+    
+        
+};
+    
+#endif
+
--- a/base/test/files.pri	Wed Nov 16 16:12:42 2016 +0000
+++ b/base/test/files.pri	Thu Nov 17 11:56:54 2016 +0000
@@ -3,7 +3,8 @@
 	     TestPitch.h \
 	     TestOurRealTime.h \
 	     TestVampRealTime.h \
-	     TestStringBits.h
+	     TestStringBits.h \
+	     TestColumnOp.h
 	     
 TEST_SOURCES += \
 	     svcore-base-test.cpp
--- a/base/test/svcore-base-test.cpp	Wed Nov 16 16:12:42 2016 +0000
+++ b/base/test/svcore-base-test.cpp	Thu Nov 17 11:56:54 2016 +0000
@@ -16,6 +16,7 @@
 #include "TestStringBits.h"
 #include "TestOurRealTime.h"
 #include "TestVampRealTime.h"
+#include "TestColumnOp.h"
 
 #include <QtTest>
 
@@ -54,6 +55,11 @@
 	if (QTest::qExec(&t, argc, argv) == 0) ++good;
 	else ++bad;
     }
+    {
+	TestColumnOp t;
+	if (QTest::qExec(&t, argc, argv) == 0) ++good;
+	else ++bad;
+    }
 
     if (bad > 0) {
 	cerr << "\n********* " << bad << " test suite(s) failed!\n" << endl;