# HG changeset patch # User Chris Cannam # Date 1479383814 0 # Node ID e2e66bfd4a8840053523ba12279b45ea1053c7f0 # Parent a99641535e0245756f7ddb8bfceb894bea2d844a Start tests for ColumnOp (+ some resulting fixes) diff -r a99641535e02 -r e2e66bfd4a88 base/ColumnOp.h --- 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 +#include +#include +#include /** * 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) { diff -r a99641535e02 -r e2e66bfd4a88 base/test/TestColumnOp.h --- /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 +#include +#include + +#include + +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 + diff -r a99641535e02 -r e2e66bfd4a88 base/test/files.pri --- 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 diff -r a99641535e02 -r e2e66bfd4a88 base/test/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 @@ -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;