changeset 1389:1eb560b363e7 spectrogramparam

Make "zoom to region" work sensibly for slice/spectrum layers; ensure that min/max bin are remapped properly when changing fft size
author Chris Cannam
date Tue, 13 Nov 2018 14:06:48 +0000
parents 81dda64a7edc
children 3c99083a4d83
files layer/Layer.h layer/SliceLayer.cpp layer/SliceLayer.h layer/SpectrumLayer.cpp view/Pane.cpp
diffstat 5 files changed, 82 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Layer.h	Tue Nov 13 13:39:01 2018 +0000
+++ b/layer/Layer.h	Tue Nov 13 14:06:48 2018 +0000
@@ -414,9 +414,24 @@
 
     virtual bool needsTextLabelHeight() const { return false; }
 
+    /**
+     * Return true if the X axis on the layer is time proportional to
+     * audio frames, false otherwise. Almost all layer types return
+     * true here: the exceptions are spectrum and slice layers.
+     */
     virtual bool hasTimeXAxis() const { return true; }
 
     /**
+     * Update the X and Y axis scales, where appropriate, to focus on
+     * the given rectangular region. This should *only* be overridden
+     * by layers whose hasTimeXAxis() returns false - the pane handles
+     * zooming appropriately in every "normal" case.
+     */
+    virtual void zoomToRegion(const LayerGeometryProvider *, QRect) {
+        return;
+    }
+
+    /**
      * Return the minimum and maximum values for the y axis of the
      * model in this layer, as well as whether the layer is configured
      * to use a logarithmic y axis display.  Also return the unit for
--- a/layer/SliceLayer.cpp	Tue Nov 13 13:39:01 2018 +0000
+++ b/layer/SliceLayer.cpp	Tue Nov 13 14:06:48 2018 +0000
@@ -74,8 +74,10 @@
 
     connectSignals(m_sliceableModel);
 
-    m_minbin = 0;
-    m_maxbin = m_sliceableModel->getHeight();
+    if (m_minbin == 0 && m_maxbin == 0) {
+        m_minbin = 0;
+        m_maxbin = m_sliceableModel->getHeight();
+    }
     
     emit modelReplaced();
     emit layerParametersChanged();
@@ -403,18 +405,23 @@
     }
 
     int mh = m_sliceableModel->getHeight();
+    int bin0 = 0;
+    if (m_maxbin > m_minbin) {
+        mh = m_maxbin - m_minbin;
+        bin0 = m_minbin;
+    }
     
     if (m_plotStyle == PlotBlocks) {
         // Must use actual zero-width pen, too slow otherwise
         paint.setPen(QPen(getBaseQColor(), 0));
     } else {
-        // Similarly, if there are very many bins here, let's drop to
-        // a precise 1-pixel-width pen
-        if (mh > 10000) {
-            paint.setPen(QPen(getBaseQColor(), 1));
-        } else {
-            paint.setPen(PaintAssistant::scalePen(getBaseQColor()));
+        // Similarly, if there are very many bins here, we use a
+        // thinner pen
+        QPen pen(getBaseQColor(), 1);
+        if (mh < 10000) {
+            pen = PaintAssistant::scalePen(pen);
         }
+        paint.setPen(pen);
     }
 
     int xorigin = getVerticalScaleWidth(v, true, paint) + 1;
@@ -430,13 +437,6 @@
     if (h <= 0) return;
 
     QPainterPath path;
-
-    int bin0 = 0;
-
-    if (m_maxbin > m_minbin) {
-        mh = m_maxbin - m_minbin;
-        bin0 = m_minbin;
-    }
     
     int divisor = 0;
 
@@ -1210,3 +1210,18 @@
     return new LinearRangeMapper(0, m_sliceableModel->getHeight(),
                                  0, m_sliceableModel->getHeight(), "");
 }
+
+void
+SliceLayer::zoomToRegion(const LayerGeometryProvider *v, QRect rect)
+{
+    double bin0 = getBinForX(v, rect.x());
+    double bin1 = getBinForX(v, rect.x() + rect.width());
+
+    // ignore y for now...
+
+    SVDEBUG << "SliceLayer::zoomToRegion: zooming to bin range "
+            << bin0 << " -> " << bin1 << endl;
+    
+    setDisplayExtents(floor(bin0), ceil(bin1));
+}
+
--- a/layer/SliceLayer.h	Tue Nov 13 13:39:01 2018 +0000
+++ b/layer/SliceLayer.h	Tue Nov 13 14:06:48 2018 +0000
@@ -75,7 +75,9 @@
     virtual void setVerticalZoomStep(int);
     virtual RangeMapper *getNewVerticalZoomRangeMapper() const;
 
-    virtual bool hasTimeXAxis() const { return false; }
+    virtual bool hasTimeXAxis() const override { return false; }
+
+    virtual void zoomToRegion(const LayerGeometryProvider *, QRect) override;
 
     virtual bool isLayerScrollable(const LayerGeometryProvider *) const { return false; }
 
--- a/layer/SpectrumLayer.cpp	Tue Nov 13 13:39:01 2018 +0000
+++ b/layer/SpectrumLayer.cpp	Tue Nov 13 14:06:48 2018 +0000
@@ -287,6 +287,16 @@
 SpectrumLayer::setWindowSize(int ws)
 {
     if (m_windowSize == ws) return;
+
+    SVDEBUG << "setWindowSize: from " << m_windowSize
+            << " to " << ws << ": updating min and max bins from "
+            << m_minbin << " and " << m_maxbin << " to ";
+    
+    m_minbin = int(round((double(m_minbin) / m_windowSize) * ws));
+    m_maxbin = int(round((double(m_maxbin) / m_windowSize) * ws));
+
+    SVDEBUG << m_minbin << " and " << m_maxbin << endl;
+
     m_windowSize = ws;
     m_newFFTNeeded = true;
     emit layerParametersChanged();
@@ -314,8 +324,19 @@
 SpectrumLayer::setOversampling(int oversampling)
 {
     if (m_oversampling == oversampling) return;
+
+    SVDEBUG << "setOversampling: from " << m_oversampling
+            << " to " << oversampling << ": updating min and max bins from "
+            << m_minbin << " and " << m_maxbin << " to ";
+    
+    m_minbin = int(round((double(m_minbin) / m_oversampling) * oversampling));
+    m_maxbin = int(round((double(m_maxbin) / m_oversampling) * oversampling));
+
+    SVDEBUG << m_minbin << " and " << m_maxbin << endl;
+    
     m_oversampling = oversampling;
     m_newFFTNeeded = true;
+    
     emit layerParametersChanged();
 }
 
--- a/view/Pane.cpp	Tue Nov 13 13:39:01 2018 +0000
+++ b/view/Pane.cpp	Tue Nov 13 14:06:48 2018 +0000
@@ -27,6 +27,7 @@
 #include "layer/WaveformLayer.h"
 #include "layer/TimeRulerLayer.h"
 #include "layer/PaintAssistant.h"
+#include "ViewProxy.h"
 
 // GF: added so we can propagate the mouse move event to the note layer for context handling.
 #include "layer/LayerFactory.h"
@@ -1822,6 +1823,18 @@
     int x1 = r.x() + r.width();
     int y1 = r.y() + r.height();
 
+    SVDEBUG << "Pane::zoomToRegion: region defined by pixel rect ("
+            << r.x() << "," << r.y() << "), " << r.width() << "x" << r.height()
+            << endl;
+
+    Layer *interactionLayer = getInteractionLayer();
+    if (interactionLayer && !(interactionLayer->hasTimeXAxis())) {
+        SVDEBUG << "Interaction layer does not have time X axis - delegating to it to decide what to do" << endl;
+        ViewProxy proxy(this, effectiveDevicePixelRatio());
+        interactionLayer->zoomToRegion(&proxy, r);
+        return;
+    }
+    
     sv_frame_t newStartFrame = getFrameForX(x0);
     sv_frame_t newEndFrame = getFrameForX(x1);
     sv_frame_t dist = newEndFrame - newStartFrame;