diff layer/SpectrogramLayer.cpp @ 1057:218be6cf2d4f spectrogram-minor-refactor

Merge from default branch
author Chris Cannam
date Mon, 13 Jun 2016 12:46:36 +0100
parents b4fd6c67fce5
children 9a13bc339fa9
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Wed Feb 03 16:51:37 2016 +0000
+++ b/layer/SpectrogramLayer.cpp	Mon Jun 13 12:46:36 2016 +0100
@@ -48,7 +48,7 @@
 #include <alloca.h>
 #endif
 
-//#define DEBUG_SPECTROGRAM_REPAINT 1
+#define DEBUG_SPECTROGRAM_REPAINT 1
 
 using namespace std;
 
@@ -78,6 +78,7 @@
     m_synchronous(false),
     m_haveDetailedScale(false),
     m_exiting(false),
+    m_peakCacheDivisor(8),
     m_sliceableModel(0)
 {
     QString colourConfigName = "spectrogram-colour";
@@ -582,6 +583,9 @@
 void
 SpectrogramLayer::invalidateImageCaches()
 {
+#ifdef DEBUG_SPECTROGRAM
+    cerr << "SpectrogramLayer::invalidateImageCaches called" << endl;
+#endif
     for (ViewImageCache::iterator i = m_imageCaches.begin();
          i != m_imageCaches.end(); ++i) {
         i->second.invalidate();
@@ -1518,7 +1522,8 @@
     if (!m_peakCaches[view->getId()]) {
         FFTModel *f = getFFTModel(v);
         if (!f) return 0;
-        m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8);
+        m_peakCaches[view->getId()] =
+            new Dense3DModelPeakCache(f, m_peakCacheDivisor);
     }
     return m_peakCaches[view->getId()];
 }
@@ -1535,6 +1540,9 @@
 void
 SpectrogramLayer::invalidateFFTModels()
 {
+#ifdef DEBUG_SPECTROGRAM
+    cerr << "SpectrogramLayer::invalidateFFTModels called" << endl;
+#endif
     for (ViewFFTMap::iterator i = m_fftModels.begin();
          i != m_fftModels.end(); ++i) {
         delete i->second;
@@ -1557,6 +1565,9 @@
 void
 SpectrogramLayer::invalidateMagnitudes()
 {
+#ifdef DEBUG_SPECTROGRAM
+    cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl;
+#endif
     m_viewMags.clear();
     for (vector<MagnitudeRange>::iterator i = m_columnMags.begin();
          i != m_columnMags.end(); ++i) {
@@ -1597,7 +1608,9 @@
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
     cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
-              << s0 << " -> " << s1 << " inclusive" << endl;
+         << s0 << " -> " << s1 << " inclusive" << endl;
+    cerr << "SpectrogramLayer::updateViewMagnitudes: for view id " << v->getId()
+         << ": min is " << mag.getMin() << ", max is " << mag.getMax() << endl;
 #endif
 
     if (!mag.isSet()) return false;
@@ -1919,7 +1932,7 @@
         if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
             m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
         }
-        usePeaksCache = (increment * 8) < zoomLevel;
+        usePeaksCache = (increment * m_peakCacheDivisor) < zoomLevel;
         if (m_colourScale == PhaseColourScale) usePeaksCache = false;
     }
 
@@ -2252,12 +2265,11 @@
                 } else if (m_normalization == NormalizeColumns) {
                     fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
                 } else if (m_normalization == NormalizeHybrid) {
-                    fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
-                    double max = fft->getMaximumMagnitudeAt(sx);
-                    if (max > 0.f) {
-                        for (int i = minbin; i <= maxbin; ++i) {
-                            values[i - minbin] = float(values[i - minbin] * log10(max));
-                        }
+                    float max = fft->getNormalizedMagnitudesAt
+                        (sx, values, minbin, maxbin - minbin + 1);
+                    float scale = log10f(max + 1.f);
+                    for (int i = minbin; i <= maxbin; ++i) {
+                        values[i - minbin] *= scale;
                     }
                 } else {
                     fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
@@ -2369,9 +2381,9 @@
 #ifdef DEBUG_SPECTROGRAM_REPAINT
     cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
 #endif
-    if (usePeaksCache) { //!!!
+    if (usePeaksCache) {
         sourceModel = getPeakCache(v);
-        divisor = 8;//!!!
+        divisor = m_peakCacheDivisor;
         minbin = 0;
         maxbin = sourceModel->getHeight();
     } else {
@@ -2424,11 +2436,13 @@
     
     for (int x = start; x != finish; x += step) {
 
+        // x is the on-canvas pixel coord; sx (later) will be the
+        // source column index
+        
         ++columnCount;
         
         if (binforx[x] < 0) continue;
 
-//        float columnGain = m_gain;
         float columnMax = 0.f;
 
         int sx0 = binforx[x] / divisor;
@@ -2460,10 +2474,9 @@
                     } else if (m_normalization == NormalizeColumns) {
                         fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
                     } else if (m_normalization == NormalizeHybrid) {
-                        fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
-                        float max = fft->getMaximumMagnitudeAt(sx);
+                        float max = fft->getNormalizedMagnitudesAt
+                            (sx, autoarray, minbin, maxbin - minbin + 1);
                         float scale = log10f(max + 1.f);
-//                        cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl;
                         for (int i = minbin; i <= maxbin; ++i) {
                             autoarray[i - minbin] *= scale;
                         }
@@ -2575,6 +2588,13 @@
             }
         }
 
+        // at this point we have updated m_columnMags and overallMag
+        // -- used elsewhere for calculating the overall view range
+        // for NormalizeVisibleArea mode -- and calculated "peaks"
+        // (the possibly scaled and interpolated value array from
+        // which we actually draw the column) and "columnMax" (maximum
+        // value used for normalisation)
+        
         for (int y = 0; y < h; ++y) {
 
             double peak = peaks[y];
@@ -3124,6 +3144,12 @@
         double dBmin = AudioLevel::multiplier_to_dB(min);
         double dBmax = AudioLevel::multiplier_to_dB(max);
 
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+        cerr << "paintVerticalScale: for view id " << v->getId()
+             << ": min = " << min << ", max = " << max
+             << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
+#endif
+        
         if (dBmax < -60.f) dBmax = -60.f;
         else top = QString("%1").arg(lrint(dBmax));
 
@@ -3465,13 +3491,29 @@
 	.arg(m_frequencyScale)
 	.arg(m_binDisplay);
 
-    s += QString("normalizeColumns=\"%1\" "
-                 "normalizeVisibleArea=\"%2\" "
-                 "normalizeHybrid=\"%3\" ")
-	.arg(m_normalization == NormalizeColumns ? "true" : "false")
-        .arg(m_normalization == NormalizeVisibleArea ? "true" : "false")
-        .arg(m_normalization == NormalizeHybrid ? "true" : "false");
-
+    // New-style normalization attributes, allowing for more types of
+    // normalization in future: write out the column normalization
+    // type separately, and then whether we are normalizing visible
+    // area as well afterwards
+    
+    s += QString("columnNormalization=\"%1\" ")
+        .arg(m_normalization == NormalizeColumns ? "peak" :
+             m_normalization == NormalizeHybrid ? "hybrid" : "none");
+
+    // Old-style normalization attribute. We *don't* write out
+    // normalizeHybrid here because the only release that would accept
+    // it (Tony v1.0) has a totally different scale factor for
+    // it. We'll just have to accept that session files from Tony
+    // v2.0+ will look odd in Tony v1.0
+    
+    s += QString("normalizeColumns=\"%1\" ")
+	.arg(m_normalization == NormalizeColumns ? "true" : "false");
+
+    // And this applies to both old- and new-style attributes
+    
+    s += QString("normalizeVisibleArea=\"%1\" ")
+        .arg(m_normalization == NormalizeVisibleArea ? "true" : "false");
+    
     Layer::toXml(stream, indent, extraAttributes + " " + s);
 }
 
@@ -3536,10 +3578,39 @@
 	attributes.value("binDisplay").toInt(&ok);
     if (ok) setBinDisplay(binDisplay);
 
-    bool normalizeColumns =
-	(attributes.value("normalizeColumns").trimmed() == "true");
-    if (normalizeColumns) {
-        setNormalization(NormalizeColumns);
+    bool haveNewStyleNormalization = false;
+    
+    QString columnNormalization = attributes.value("columnNormalization");
+
+    if (columnNormalization != "") {
+
+        haveNewStyleNormalization = true;
+
+        if (columnNormalization == "peak") {
+            setNormalization(NormalizeColumns);
+        } else if (columnNormalization == "hybrid") {
+            setNormalization(NormalizeHybrid);
+        } else if (columnNormalization == "none") {
+            // do nothing
+        } else {
+            cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
+                 << columnNormalization << "\"" << endl;
+        }
+    }
+
+    if (!haveNewStyleNormalization) {
+
+        bool normalizeColumns =
+            (attributes.value("normalizeColumns").trimmed() == "true");
+        if (normalizeColumns) {
+            setNormalization(NormalizeColumns);
+        }
+
+        bool normalizeHybrid =
+            (attributes.value("normalizeHybrid").trimmed() == "true");
+        if (normalizeHybrid) {
+            setNormalization(NormalizeHybrid);
+        }
     }
 
     bool normalizeVisibleArea =
@@ -3548,10 +3619,13 @@
         setNormalization(NormalizeVisibleArea);
     }
 
-    bool normalizeHybrid =
-	(attributes.value("normalizeHybrid").trimmed() == "true");
-    if (normalizeHybrid) {
-        setNormalization(NormalizeHybrid);
+    if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) {
+        // Tony v1.0 is (and hopefully will remain!) the only released
+        // SV-a-like to use old-style attributes when saving sessions
+        // that ask for hybrid normalization. It saves them with the
+        // wrong gain factor, so hack in a fix for that here -- this
+        // gives us backward but not forward compatibility.
+        setGain(m_gain / float(m_fftSize / 2));
     }
 }