changeset 1058:9a13bc339fa9 spectrogram-minor-refactor

Mid-refactor to pull out the bulk of paintDrawBuffer into chunks
author Chris Cannam
date Mon, 13 Jun 2016 16:17:44 +0100
parents 218be6cf2d4f
children e1c2dcc7790e
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h
diffstat 2 files changed, 201 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Mon Jun 13 12:46:36 2016 +0100
+++ b/layer/SpectrogramLayer.cpp	Mon Jun 13 16:17:44 2016 +0100
@@ -2352,6 +2352,187 @@
     return columnCount;
 }
 
+void
+SpectrogramLayer::normalise(vector<float> &values, Normalization norm) const
+{
+    if (norm == NormalizeColumns ||
+        norm == NormalizeHybrid) {
+        
+        float max = 0.f;
+        for (int i = 0; in_range_for(i, values); ++i) {
+            if (i == 0 || values[i] > max) {
+                max = values[i];
+            }
+        }
+        if (max > 0.f) {
+            float scale = 1.f / max;
+            if (norm == NormalizeHybrid) {
+                scale = scale * log10f(max + 1.f);
+            }
+            for (int i = 0; in_range_for(i, values); ++i) {
+                values[i] *= scale;
+            }
+        }
+    }
+}
+
+vector<float>
+SpectrogramLayer::getColumnFromFFTModel(FFTModel *fft,
+                                        int sx, // column number in model
+                                        int minbin,
+                                        int bincount) const
+{
+    vector<float> values(bincount, 0.f);
+    
+    if (m_colourScale == PhaseColourScale) {
+        fft->getPhasesAt(sx, values.data(), minbin, bincount);
+    } else {
+        fft->getMagnitudesAt(sx, values.data(), minbin, bincount);
+    }
+
+    return move(values);
+}
+
+vector<float>
+SpectrogramLayer::getColumnFromGenericModel(DenseThreeDimensionalModel *model,
+                                            int sx, // column number in model
+                                            int minbin,
+                                            int bincount) const
+{
+    if (m_colourScale == PhaseColourScale) {
+        throw std::logic_error("can't use phase scale with generic 3d model");
+    }
+
+    auto col = model->getColumn(sx);
+        
+    return move(vector<float>(col.data() + minbin,
+                              col.data() + minbin + bincount));
+}
+
+void
+SpectrogramLayer::scaleColumn(vector<float> &col)
+{
+    if (m_normalization != NormalizeColumns &&
+        m_normalization != NormalizeHybrid) {
+        float scale = 2.f / float(m_fftSize);
+        int n = int(col.size());
+        for (int i = 0; i < n; ++i) {
+            col[i] *= scale;
+        }
+    }            
+}
+
+static bool
+is_peak(const vector<float> &values, int ix)
+{
+    if (!in_range_for(ix-1, values)) return false;
+    if (!in_range_for(ix+1, values)) return false;
+    if (values[ix] < values[ix+1]) return false;
+    if (values[ix] < values[ix-1]) return false;
+    return true;
+}
+
+vector<float>
+SpectrogramLayer::distributeColumn(const vector<float> &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) {
+        
+        double sy0 = binfory[y] - minbin;
+        double sy1 = sy0 + 1;
+        if (y+1 < h) {
+            sy1 = binfory[y+1] - minbin;
+        }
+        
+        if (interpolate && fabs(sy1 - sy0) < 1.0) {
+            
+            double centre = (sy0 + sy1) / 2;
+            double dist = (centre - 0.5) - rint(centre - 0.5);
+            int bin = int(centre);
+
+            int other = (dist < 0 ? (bin-1) : (bin+1));
+
+            if (bin < 0) bin = 0;
+            if (bin >= bins) bin = bins-1;
+
+            if (other < 0 || other >= bins) {
+                other = bin;
+            }
+
+            if (m_binDisplay == PeakBins) {
+
+                if (is_peak(in, bin)) {
+                    out[y] = in[bin];
+                } else if (other != bin && is_peak(in, other)) {
+                    out[y] = in[other];
+                }
+                
+            } else {
+
+                double prop = 1.0 - fabs(dist);
+
+                double v0 = in[bin];
+                double v1 = in[other];
+                
+                out[y] = float(prop * v0 + (1.0 - prop) * v1);
+            }
+
+        } else { // not interpolating this one
+
+            int by0 = int(sy0 + 0.0001);
+            int by1 = int(sy1 + 0.0001);
+            if (by1 < by0 + 1) by1 = by0 + 1;
+
+            for (int bin = by0; bin < by1; ++bin) {
+
+                if (m_binDisplay == PeakBins && !is_peak(in, bin)) {
+                    continue;
+                }
+                
+                float value = in[bin];
+
+                if (value > out[y] || m_colourScale == PhaseColourScale) {
+                    out[y] = value;
+                }
+            }
+        }
+    }
+
+    return out;
+}
+
+void
+SpectrogramLayer::recordColumnExtents(const vector<float> &col,
+                                      int sx, // column index, for m_columnMags
+                                      MagnitudeRange &overallMag,
+                                      bool &overallMagChanged)
+{
+    //!!! this differs from previous logic when in peak mode - as the
+    //!!! zeros between peaks are now sampled, where they were not
+    //!!! before
+    
+    if (!in_range_for(sx, m_columnMags)) {
+        throw logic_error("sx out of range for m_columnMags");
+    }
+    MagnitudeRange mr;
+    for (auto v: col) {
+        mr.sample(v);
+    }
+    m_columnMags[sx] = mr;
+    if (overallMag.sample(mr)) {
+        overallMagChanged = true;
+    }
+}
+
+// order:
+// get column -> scale -> distribute/interpolate -> record extents -> normalise -> apply display gain
+
 int
 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
                                   int w,
--- a/layer/SpectrogramLayer.h	Mon Jun 13 12:46:36 2016 +0100
+++ b/layer/SpectrogramLayer.h	Mon Jun 13 16:17:44 2016 +0100
@@ -18,6 +18,7 @@
 
 #include "SliceableLayer.h"
 #include "base/Window.h"
+#include "base/MagnitudeRange.h"
 #include "base/RealTime.h"
 #include "base/Thread.h"
 #include "base/PropertyContainer.h"
@@ -360,53 +361,12 @@
     const int m_peakCacheDivisor;
     mutable Model *m_sliceableModel;
 
-    class MagnitudeRange {
-    public:
-        MagnitudeRange() : m_min(0), m_max(0) { }
-        bool operator==(const MagnitudeRange &r) {
-            return r.m_min == m_min && r.m_max == m_max;
-        }
-        bool isSet() const { return (m_min != 0.f || m_max != 0.f); }
-        void set(float min, float max) {
-            m_min = min;
-            m_max = max;
-            if (m_max < m_min) m_max = m_min;
-        }
-        bool sample(float f) {
-            bool changed = false;
-            if (isSet()) {
-                if (f < m_min) { m_min = f; changed = true; }
-                if (f > m_max) { m_max = f; changed = true; }
-            } else {
-                m_max = m_min = f;
-                changed = true;
-            }
-            return changed;
-        }            
-        bool sample(const MagnitudeRange &r) {
-            bool changed = false;
-            if (isSet()) {
-                if (r.m_min < m_min) { m_min = r.m_min; changed = true; }
-                if (r.m_max > m_max) { m_max = r.m_max; changed = true; }
-            } else {
-                m_min = r.m_min;
-                m_max = r.m_max;
-                changed = true;
-            }
-            return changed;
-        }            
-        float getMin() const { return m_min; }
-        float getMax() const { return m_max; }
-    private:
-        float m_min;
-        float m_max;
-    };
-
     typedef std::map<int, MagnitudeRange> ViewMagMap; // key is view id
     mutable ViewMagMap m_viewMags;
     mutable std::vector<MagnitudeRange> m_columnMags;
     void invalidateMagnitudes();
     bool updateViewMagnitudes(LayerGeometryProvider *v) const;
+    
     int paintDrawBuffer(LayerGeometryProvider *v, int w, int h,
                         const std::vector<int> &binforx,
                         const std::vector<double> &binfory,
@@ -427,8 +387,24 @@
                                        bool rightToLeft,
                                        double softTimeLimit) const;
 
-    virtual void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const;
-    virtual void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const;
+    void normalise(std::vector<float> &, Normalization norm) const;
+    
+    std::vector<float> getColumnFromFFTModel(FFTModel *model,
+                                             int sx,
+                                             int minbin,
+                                             int bincount) const;
+
+    std::vector<float> getColumnFromGenericModel(DenseThreeDimensionalModel *model,
+                                                 int sx,
+                                                 int minbin,
+                                                 int bincount) const;
+
+    void scaleColumn(std::vector<float> &col) const;
+    
+    virtual void updateMeasureRectYCoords(LayerGeometryProvider *v,
+                                          const MeasureRect &r) const;
+    virtual void setMeasureRectYCoord(LayerGeometryProvider *v,
+                                      MeasureRect &r, bool start, int y) const;
 };
 
 #endif