changeset 1267:1d8418cca63a 3.0-integration

Further column op tests and fixes
author Chris Cannam
date Thu, 17 Nov 2016 14:46:03 +0000
parents dd190086db73
children b7b84ae5f0a7
files base/ColumnOp.cpp base/test/TestColumnOp.h
diffstat 2 files changed, 172 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ColumnOp.cpp	Thu Nov 17 14:46:03 2016 +0000
@@ -0,0 +1,162 @@
+/* -*- 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 file copyright 2006-2016 Chris Cannam and QMUL.
+    
+    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.
+*/
+
+#include "ColumnOp.h"
+
+#include <cmath>
+#include <algorithm>
+#include <iostream>
+
+using namespace std;
+
+ColumnOp::Column
+ColumnOp::fftScale(const Column &in, int fftSize)
+{
+    return applyGain(in, 2.0 / fftSize);
+}
+
+ColumnOp::Column
+ColumnOp::peakPick(const Column &in)
+{
+    vector<float> out(in.size(), 0.f);
+
+    for (int i = 0; in_range_for(in, i); ++i) {
+        if (isPeak(in, i)) {
+            out[i] = in[i];
+        }
+    }
+    
+    return out;
+}
+
+ColumnOp::Column
+ColumnOp::normalize(const Column &in, ColumnNormalization n) {
+
+    if (n == ColumnNormalization::None || in.empty()) {
+        return in;
+    }
+
+    float scale = 1.f;
+        
+    if (n == ColumnNormalization::Sum1) {
+
+        float sum = 0.f;
+
+        for (auto v: in) {
+            sum += fabsf(v);
+        }
+
+        if (sum != 0.f) {
+            scale = 1.f / sum;
+        }
+
+    } else {
+
+        float max = 0.f;
+
+        for (auto v: in) {
+            v = fabsf(v);
+            if (v > max) {
+                max = v;
+            }
+        }
+
+        if (n == ColumnNormalization::Max1) {
+            if (max != 0.f) {
+                scale = 1.f / max;
+            }
+        } else if (n == ColumnNormalization::Hybrid) {
+            if (max > 0.f) {
+                scale = log10f(max + 1.f) / max;
+            }
+        }
+    }
+
+    return applyGain(in, scale);
+}
+
+ColumnOp::Column
+ColumnOp::distribute(const Column &in,
+                     int h,
+                     const vector<double> &binfory,
+                     int minbin,
+                     bool interpolate)
+{
+    vector<float> out(h, 0.f);
+    int bins = int(in.size());
+
+    for (int y = 0; y < h; ++y) {
+
+        if (interpolate && h > bins) {
+
+            double sy = binfory[y] - minbin - 0.5;
+            double syf = floor(sy);
+
+            int mainbin = int(syf);
+            int other = mainbin;
+            if (sy > syf) {
+                other = mainbin + 1;
+            } else if (sy < syf) {
+                other = mainbin - 1;
+            }
+
+            if (mainbin < 0) {
+                mainbin = 0;
+            }
+            if (mainbin >= bins) {
+                mainbin = bins - 1;
+            }
+
+            if (other < 0) {
+                other = 0;
+            }
+            if (other >= bins) {
+                other = bins - 1;
+            }
+
+            double prop = 1.0 - fabs(sy - syf);
+            
+            double v0 = in[mainbin];
+            double v1 = in[other];
+                
+            out[y] = float(prop * v0 + (1.0 - prop) * v1);
+
+        } else {
+            
+            double sy0 = binfory[y] - minbin;
+
+            double sy1;
+            if (y+1 < h) {
+                sy1 = binfory[y+1] - minbin;
+            } else {
+                sy1 = bins;
+            }
+
+            int by0 = int(sy0 + 0.0001);
+            int by1 = int(sy1 + 0.0001);
+                
+            for (int bin = by0; bin == by0 || bin < by1; ++bin) {
+
+                float value = in[bin];
+
+                if (bin == by0 || value > out[y]) {
+                    out[y] = value;
+                }
+            }
+        }
+    }
+
+    return out;
+}
--- a/base/test/TestColumnOp.h	Thu Nov 17 14:33:20 2016 +0000
+++ b/base/test/TestColumnOp.h	Thu Nov 17 14:46:03 2016 +0000
@@ -195,7 +195,14 @@
     void distribute_simple_interpolated() {
         Column in { 1, 2, 3 };
         BinMapping binfory { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5 };
-        Column expected { 1, 1.5, 2, 2.5, 3, 3 };
+        // There is a 0.5-bin offset from the distribution you might
+        // expect, because this corresponds visually to the way that
+        // bin values are duplicated upwards in simple_distribution.
+        // It means that switching between interpolated and
+        // non-interpolated views retains the visual position of each
+        // bin peak as somewhere in the middle of the scale area for
+        // that bin.
+        Column expected { 1, 1, 1.5, 2, 2.5, 3 };
         Column actual(C::distribute(in, 6, binfory, 0, true));
         report(actual);
         QCOMPARE(actual, expected);
@@ -211,9 +218,10 @@
     }
     
     void distribute_nonlinear_interpolated() {
+        // See distribute_simple_interpolated
         Column in { 1, 2, 3 };
         BinMapping binfory { 0.0, 0.2, 0.5, 1.0, 2.0, 2.5 };
-        Column expected { 1, 1.2, 1.5, 2, 3, 3 };
+        Column expected { 1, 1, 1, 1.5, 2.5, 3 };
         Column actual(C::distribute(in, 6, binfory, 0, true));
         report(actual);
         QCOMPARE(actual, expected);