changeset 1086:163cb9b98104 spectrogram-minor-refactor

Simplify the oversampling/zero-padding logic. FFT model selection no longer depends on the view.
author Chris Cannam
date Fri, 01 Jul 2016 18:30:42 +0100
parents 179ea8a2f650
children 6d990a24ac1b
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h
diffstat 2 files changed, 43 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Fri Jul 01 17:54:31 2016 +0100
+++ b/layer/SpectrogramLayer.cpp	Fri Jul 01 18:30:42 2016 +0100
@@ -61,7 +61,6 @@
     m_windowSize(1024),
     m_windowType(HanningWindow),
     m_windowHopLevel(2),
-    m_zeroPadLevel(0),
     m_fftSize(1024),
     m_gain(1.0),
     m_initialGain(1.0),
@@ -171,7 +170,6 @@
 //    list.push_back("Min Frequency");
 //    list.push_back("Max Frequency");
     list.push_back("Frequency Scale");
-////    list.push_back("Zero Padding");
     return list;
 }
 
@@ -190,7 +188,6 @@
     if (name == "Min Frequency") return tr("Min Frequency");
     if (name == "Max Frequency") return tr("Max Frequency");
     if (name == "Frequency Scale") return tr("Frequency Scale");
-    if (name == "Zero Padding") return tr("Smoothing");
     return "";
 }
 
@@ -206,7 +203,6 @@
     if (name == "Gain") return RangeProperty;
     if (name == "Colour Rotation") return RangeProperty;
     if (name == "Threshold") return RangeProperty;
-    if (name == "Zero Padding") return ToggleProperty;
     return ValueProperty;
 }
 
@@ -216,8 +212,7 @@
     if (name == "Bin Display" ||
         name == "Frequency Scale") return tr("Bins");
     if (name == "Window Size" ||
-	name == "Window Increment" ||
-        name == "Zero Padding") return tr("Window");
+	name == "Window Increment") return tr("Window");
     if (name == "Colour" ||
 	name == "Threshold" ||
 	name == "Colour Rotation") return tr("Colour");
@@ -306,14 +301,6 @@
 
         val = m_windowHopLevel;
     
-    } else if (name == "Zero Padding") {
-	
-	*min = 0;
-	*max = 1;
-        *deflt = 0;
-	
-        val = m_zeroPadLevel > 0 ? 1 : 0;
-    
     } else if (name == "Min Frequency") {
 
 	*min = 0;
@@ -414,10 +401,6 @@
 	case 5: return tr("93.75 %");
 	}
     }
-    if (name == "Zero Padding") {
-        if (value == 0) return tr("None");
-        return QString("%1x").arg(value + 1);
-    }
     if (name == "Min Frequency") {
 	switch (value) {
 	default:
@@ -510,8 +493,6 @@
 	setWindowSize(32 << value);
     } else if (name == "Window Increment") {
         setWindowHopLevel(value);
-    } else if (name == "Zero Padding") {
-        setZeroPadLevel(value > 0.1 ? 3 : 0);
     } else if (name == "Min Frequency") {
 	switch (value) {
 	default:
@@ -605,6 +586,7 @@
         return;
     }
     if (name == "Spectrogram Y Smoothing") {
+        setWindowSize(m_windowSize);
         invalidateImageCaches();
         invalidateMagnitudes();
         emit layerParametersChanged();
@@ -637,15 +619,35 @@
     return m_channel;
 }
 
+int
+SpectrogramLayer::getFFTOversampling() const
+{
+    if (m_binDisplay != AllBins) {
+        return 1;
+    }
+
+    Preferences::SpectrogramSmoothing smoothing = 
+        Preferences::getInstance()->getSpectrogramSmoothing();
+    
+    if (smoothing == Preferences::NoSpectrogramSmoothing ||
+        smoothing == Preferences::SpectrogramInterpolated) {
+        return 1;
+    }
+
+    return 4;
+}
+
 void
 SpectrogramLayer::setWindowSize(int ws)
 {
-    if (m_windowSize == ws) return;
+    int fftSize = ws * getFFTOversampling();
+
+    if (m_windowSize == ws && m_fftSize == fftSize) return;
 
     invalidateImageCaches();
     
     m_windowSize = ws;
-    m_fftSize = ws * (m_zeroPadLevel + 1);
+    m_fftSize = fftSize;
     
     invalidateFFTModels();
 
@@ -681,27 +683,6 @@
 }
 
 void
-SpectrogramLayer::setZeroPadLevel(int v)
-{
-    if (m_zeroPadLevel == v) return;
-
-    invalidateImageCaches();
-    
-    m_zeroPadLevel = v;
-    m_fftSize = m_windowSize * (v + 1);
-
-    invalidateFFTModels();
-
-    emit layerParametersChanged();
-}
-
-int
-SpectrogramLayer::getZeroPadLevel() const
-{
-    return m_zeroPadLevel;
-}
-
-void
 SpectrogramLayer::setWindowType(WindowType w)
 {
     if (m_windowType == w) return;
@@ -1175,8 +1156,7 @@
     q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
     q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
 
-    // Now map these on to ("proportions of") actual bins, using raw
-    // FFT size (unsmoothed)
+    // Now map these on to ("proportions of") actual bins
 
     q0 = (q0 * m_fftSize) / sr;
     q1 = (q1 * m_fftSize) / sr;
@@ -1184,32 +1164,6 @@
     return true;
 }
 
-bool
-SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
-{
-    Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
-
-    int h = v->getPaintHeight();
-    if (y < 0 || y >= h) return false;
-
-    sv_samplerate_t sr = m_model->getSampleRate();
-    double minf = getEffectiveMinFrequency();
-    double maxf = getEffectiveMaxFrequency();
-
-    bool logarithmic = (m_frequencyScale == LogFrequencyScale);
-
-    q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
-    q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
-
-    // Now map these on to ("proportions of") actual bins, using raw
-    // FFT size (unsmoothed)
-
-    q0 = (q0 * getFFTSize(v)) / sr;
-    q1 = (q1 * getFFTSize(v)) / sr;
-
-    return true;
-}
-
 double
 SpectrogramLayer::getYForBin(LayerGeometryProvider *, double bin) const {
     //!!! not implemented
@@ -1233,8 +1187,7 @@
 
     double q = v->getFrequencyForY(y, minf, maxf, logarithmic);
 
-    // Now map on to ("proportions of") actual bins, using raw FFT
-    // size (unsmoothed)
+    // Now map on to ("proportions of") actual bins
 
     q = (q * getFFTSize(v)) / sr;
 
@@ -1345,7 +1298,10 @@
 
 	    if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
 
-	    if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue;
+	    if (!fft->isOverThreshold
+                (s, q, float(m_threshold * double(m_fftSize)/2.0))) {
+                continue;
+            }
 
             double freq = binfreq;
 	    
@@ -1391,10 +1347,6 @@
 
     bool rv = false;
 
-    int zp = getZeroPadLevel(v);
-    q0i *= zp + 1;
-    q1i *= zp + 1;
-
     FFTModel *fft = getFFTModel(v);
 
     if (fft) {
@@ -1436,52 +1388,10 @@
 }
    
 int
-SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const
+SpectrogramLayer::getFFTSize(const LayerGeometryProvider *) const
 {
-    //!!! tidy all this stuff
-
-    if (m_binDisplay != AllBins) return 0;
-
-    Preferences::SpectrogramSmoothing smoothing = 
-        Preferences::getInstance()->getSpectrogramSmoothing();
-    
-    if (smoothing == Preferences::NoSpectrogramSmoothing ||
-        smoothing == Preferences::SpectrogramInterpolated) return 0;
-
-    if (m_frequencyScale == LogFrequencyScale) return 3;
-
-    sv_samplerate_t sr = m_model->getSampleRate();
-    
-    int maxbin = m_fftSize / 2;
-    if (m_maxFrequency > 0) {
-	maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
-	if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
-    }
-
-    int minbin = 1;
-    if (m_minFrequency > 0) {
-	minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
-	if (minbin < 1) minbin = 1;
-	if (minbin >= maxbin) minbin = maxbin - 1;
-    }
-
-    double perPixel =
-        double(v->getPaintHeight()) /
-        double((maxbin - minbin) / (m_zeroPadLevel + 1));
-
-    if (perPixel > 2.8) {
-        return 3; // 4x oversampling
-    } else if (perPixel > 1.5) {
-        return 1; // 2x
-    } else {
-        return 0; // 1x
-    }
-}
-
-int
-SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const
-{
-    return m_fftSize * (getZeroPadLevel(v) + 1);
+    //!!!
+    return m_fftSize;
 }
 	
 FFTModel *
@@ -1834,12 +1744,10 @@
     // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
     // to the actual scale frequency extents (presumably not zero padded).
 
-    // If we are zero padding, we want to use the zero-padded
-    // equivalents of the bins that we would be using if not zero
-    // padded, to avoid spaces at the top and bottom of the display.
-
-    // Note fftSize is the actual zero-padded fft size, m_fftSize the
-    // nominal fft size.
+    // If we are zero padding (i.e. oversampling) we want to use the
+    // zero-padded equivalents of the bins that we would be using if
+    // not zero padded, to avoid spaces at the top and bottom of the
+    // display.
     
     int maxbin = m_fftSize / 2;
     if (m_maxFrequency > 0) {
@@ -1855,9 +1763,9 @@
 	if (minbin >= maxbin) minbin = maxbin - 1;
     }
 
-    int zpl = getZeroPadLevel(v) + 1;
-    minbin = minbin * zpl;
-    maxbin = (maxbin + 1) * zpl - 1;
+    int over = getFFTOversampling();
+    minbin = minbin * over;
+    maxbin = (maxbin + 1) * over - 1;
 
     double minFreq = (double(minbin) * sr) / fftSize;
     double maxFreq = (double(maxbin) * sr) / fftSize;
@@ -2008,7 +1916,7 @@
 
         for (int y = 0; y < h; ++y) {
             double q0 = 0, q1 = 0;
-            if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
+            if (!getYBinRange(v, h-y-1, q0, q1)) {
                 binfory[y] = -1;
             } else {
                 binfory[y] = q0;
--- a/layer/SpectrogramLayer.h	Fri Jul 01 17:54:31 2016 +0100
+++ b/layer/SpectrogramLayer.h	Fri Jul 01 18:30:42 2016 +0100
@@ -115,9 +115,6 @@
     void setWindowType(WindowType type);
     WindowType getWindowType() const;
 
-    void setZeroPadLevel(int level);
-    int getZeroPadLevel() const;
-
     /**
      * Set the gain multiplier for sample values in this view.
      * The default is 1.0.
@@ -255,8 +252,7 @@
     int                 m_windowSize;
     WindowType          m_windowType;
     int                 m_windowHopLevel;
-    int                 m_zeroPadLevel;
-    int                 m_fftSize;
+    int                 m_fftSize; // m_windowSize * oversampling level
     float               m_gain;
     float               m_initialGain;
     float               m_threshold;
@@ -325,15 +321,8 @@
     double getEffectiveMinFrequency() const;
     double getEffectiveMaxFrequency() const;
 
-    // Note that the getYBin... methods return the nominal bin in the
-    // un-smoothed spectrogram.  This is not necessarily the same bin
-    // as is pulled from the spectrogram and drawn at the given
-    // position, if the spectrogram has oversampling smoothing.  Use
-    // getSmoothedYBinRange to obtain that.
-
     bool getXBinRange(LayerGeometryProvider *v, int x, double &windowMin, double &windowMax) const;
     bool getYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const;
-    bool getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const;
 
     bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const;
     bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y,
@@ -349,7 +338,7 @@
         else return m_windowSize / (1 << (m_windowHopLevel - 1));
     }
 
-    int getZeroPadLevel(const LayerGeometryProvider *v) const;
+    int getFFTOversampling() const;
     int getFFTSize(const LayerGeometryProvider *v) const;
     FFTModel *getFFTModel(const LayerGeometryProvider *v) const;
     Dense3DModelPeakCache *getPeakCache(const LayerGeometryProvider *v) const;