diff layer/SpectrogramLayer.cpp @ 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 b4fd6c67fce5
children e1c2dcc7790e
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,