changeset 224:9465b5375235

* Fix #1672407 confused by plugin-named files in cwd (or home?) * Fix #1491848 crash when loading new file while transform plugin runs * Fix #1502287 Background remains black after spectrogram layer deleted * Fix #1604477 Replacing the main audio file silences secondary audio file * Fix failure to initialise property box layout to last preference on startup * Fix resample/wrong-rate display in Pane, ensure that right rate is chosen if all current models have an acceptable rate even if previous main model had a different one * Fix "global zoom" broken in previous commit * Some fixes to spectrogram cache area updating (makes spectrogram appear more quickly, previously it had a tendency to refresh with empty space) * Fixes to colour 3d plot normalization
author Chris Cannam
date Thu, 08 Mar 2007 16:53:08 +0000
parents 403bfb88d8d6
children 6f46179086c0
files layer/Colour3DPlotLayer.cpp layer/Layer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h view/Pane.cpp view/View.cpp view/View.h view/ViewManager.cpp view/ViewManager.h
diffstat 9 files changed, 150 insertions(+), 76 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp	Wed Mar 07 18:00:49 2007 +0000
+++ b/layer/Colour3DPlotLayer.cpp	Thu Mar 08 16:53:08 2007 +0000
@@ -384,7 +384,7 @@
 {
     m_model->getColumn(col, values);
 
-    float colMax = 0.f;
+    float colMax = 0.f, colMin = 0.f;
 
     float min = 0.f, max = 0.f;
     if (m_normalizeColumns) {
@@ -394,11 +394,10 @@
 
     if (m_normalizeColumns) {
         for (size_t y = 0; y < values.size(); ++y) {
-            if (values[y] > colMax || y == 0) colMax = values[y];
+            if (y == 0 || values[y] > colMax) colMax = values[y];
+            if (y == 0 || values[y] < colMin) colMin = values[y];
         }
-        if (m_colourScale == LogScale) {
-            colMax = LogRange::map(colMax);
-        }
+        if (colMin == colMax) colMax = colMin + 1;
     }
     
     for (size_t y = 0; y < values.size(); ++y) {
@@ -406,16 +405,10 @@
         float value = min;
 
         value = values[y];
-        if (m_colourScale == LogScale) {
-            value = LogRange::map(value);
-        }
 
         if (m_normalizeColumns) {
-            if (colMax != 0) {
-                value = max * (value / colMax);
-            } else {
-                value = 0;
-            }
+            float norm = (value - colMin) / (colMax - colMin);
+            value = min + (max - min) * norm;
         }
 
         values[y] = value;
@@ -429,7 +422,7 @@
     size_t modelEnd = m_model->getEndFrame();
     size_t modelResolution = m_model->getResolution();
 
-    std::cerr << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << std::endl;
+//    std::cerr << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << std::endl;
 
     if (!m_normalizeVisibleArea || m_normalizeColumns) {
         firstBin = modelStart / modelResolution;
@@ -453,7 +446,7 @@
     m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
     m_cacheStart = firstBin;
 
-    std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " starting " << m_cacheStart << std::endl;
+//    std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " starting " << m_cacheStart << std::endl;
 
     m_cache->setNumColors(256);
     DenseThreeDimensionalModel::Column values;
@@ -480,7 +473,7 @@
     
     m_cache->fill(0);
 
-    float visibleMax = 0.f;
+    float visibleMax = 0.f, visibleMin = 0.f;
 
     if (m_normalizeVisibleArea && !m_normalizeColumns) {
         
@@ -489,17 +482,21 @@
             values.clear();
             getColumn(c, values);
 
-            float colMax = 0.f;
+            float colMax = 0.f, colMin = 0.f;
 
             for (size_t y = 0; y < m_model->getHeight(); ++y) {
                 if (y >= values.size()) break;
                 if (y == 0 || values[y] > colMax) colMax = values[y];
+                if (y == 0 || values[y] < colMin) colMin = values[y];
             }
 
             if (c == firstBin || colMax > visibleMax) visibleMax = colMax;
+            if (c == firstBin || colMin < visibleMin) visibleMin = colMin;
         }
     }
     
+    if (visibleMin == visibleMax) visibleMax = visibleMin + 1;
+
     for (size_t c = firstBin; c <= lastBin; ++c) {
 	
         values.clear();
@@ -513,9 +510,12 @@
             }
             
             if (m_normalizeVisibleArea && !m_normalizeColumns) {
-                if (visibleMax != 0) {
-                    value = max * (value / visibleMax);
-                }
+                float norm = (value - visibleMin) / (visibleMax - visibleMin);
+                value = min + (max - min) * norm;
+            }
+
+            if (m_colourScale == LogScale) {
+                value = LogRange::map(value);
             }
 
             int pixel = int(((value - min) * 256) / (max - min));
--- a/layer/Layer.h	Wed Mar 07 18:00:49 2007 +0000
+++ b/layer/Layer.h	Thu Mar 08 16:53:08 2007 +0000
@@ -84,6 +84,9 @@
     virtual VerticalPosition getPreferredFrameCountPosition() const {
 	return PositionBottom;
     }
+    virtual bool hasLightBackground() const {
+        return true;
+    }
 
     virtual QString getPropertyContainerIconName() const;
 
--- a/layer/SpectrogramLayer.cpp	Wed Mar 07 18:00:49 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Thu Mar 08 16:53:08 2007 +0000
@@ -81,7 +81,7 @@
 	setColourScale(LinearColourScale);
         setColourMap(ColourMapper::Sunset);
         setFrequencyScale(LogFrequencyScale);
-        setGain(20);
+//        setGain(20);
     } else if (config == MelodicPeaks) {
 	setWindowSize(4096);
 	setWindowHopLevel(5);
@@ -1015,6 +1015,12 @@
     }
 }
 
+bool
+SpectrogramLayer::hasLightBackground() const 
+{
+    return (m_colourMap == (int)ColourMapper::BlackOnWhite);
+}
+
 void
 SpectrogramLayer::initialisePalette()
 {
@@ -1112,9 +1118,10 @@
         min = m_viewMags[v].getMin();
         max = m_viewMags[v].getMax();
     } else if (!m_normalizeColumns) {
-        if (m_colourScale == LinearColourScale ||
-            m_colourScale == MeterColourScale) {
-//            max = 0.1f;
+        if (m_colourScale == LinearColourScale //||
+//            m_colourScale == MeterColourScale) {
+            ) {
+            max = 0.1f;
         }
     }
 
@@ -1687,12 +1694,6 @@
 void
 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
-    if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
-	v->setLightBackground(true);
-    } else {
-	v->setLightBackground(false);
-    }
-
     Profiler profiler("SpectrogramLayer::paint", true);
 #ifdef DEBUG_SPECTROGRAM_REPAINT
     std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
@@ -1742,15 +1743,11 @@
 
     int x0 = 0;
     int x1 = v->width();
-    int y0 = 0;
-    int y1 = v->height();
 
     bool recreateWholePixmapCache = true;
 
     x0 = rect.left();
     x1 = rect.right() + 1;
-    y0 = rect.top();
-    y1 = rect.bottom() + 1;
 
     if (cache.validArea.width() > 0) {
 
@@ -1852,20 +1849,23 @@
 	} else {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
 	    std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
+            if (int(cache.zoomLevel) != zoomLevel) {
+                std::cerr << "(cache zoomLevel " << cache.zoomLevel
+                          << " != " << zoomLevel << ")" << std::endl;
+            }
+            if (cache.pixmap.width() != v->width()) {
+                std::cerr << "(cache width " << cache.pixmap.width()
+                          << " != " << v->width();
+            }
+            if (cache.pixmap.height() != v->height()) {
+                std::cerr << "(cache height " << cache.pixmap.height()
+                          << " != " << v->height();
+            }
 #endif
             cache.validArea = QRect();
 	}
     }
 
-/*
-    if (stillCacheing) {
-	x0 = rect.left();
-	x1 = rect.right() + 1;
-	y0 = rect.top();
-	y1 = rect.bottom() + 1;
-    }
-*/
-
     if (updateViewMagnitudes(v)) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
@@ -1906,7 +1906,17 @@
 
     if (paintBlockWidth < 20) paintBlockWidth = 20;
 
+#ifdef DEBUG_SPECTROGRAM_REPAINT
     std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl;
+#endif
+
+    // We always paint the full height when refreshing the cache.
+    // Smaller heights can be used when painting direct from cache
+    // (further up in this function), but we want to ensure the cache
+    // is coherent without having to worry about vertical matching of
+    // required and valid areas as well as horizontal.
+
+    int h = v->height();
 
     if (cache.validArea.width() > 0) {
 
@@ -1956,11 +1966,10 @@
                 x1 = x0 + paintBlockWidth;
             }
         }
-        cache.validArea = QRect(x0, 0, x1 - x0, v->height());
+        cache.validArea = QRect(x0, 0, x1 - x0, h);
     }
 
     int w = x1 - x0;
-    int h = y1 - y0;
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
     std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
@@ -2033,13 +2042,16 @@
         }
     }
 
-
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    std::cerr << (float(v->getFrameForX(1) - v->getFrameForX(0)) / increment) << " bins per pixel" << std::endl;
+    std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl;
 #endif
 
+    bool runOutOfData = false;
+
     for (int x = 0; x < w; ++x) {
 
+        if (runOutOfData) break;
+
 	for (int y = 0; y < h; ++y) {
 	    ymag[y] = 0.f;
 	    ydiv[y] = 0.f;
@@ -2065,7 +2077,14 @@
 
         for (int s = s0i; s <= s1i; ++s) {
 
-            if (!fft->isColumnAvailable(s)) continue;
+            if (!fft->isColumnAvailable(s)) {
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+                std::cerr << "Met unavailable column at col " << s << std::endl;
+#endif
+//                continue;
+                runOutOfData = true;
+                break;
+            }
             
             if (!fftSuspended) {
                 fft->suspendWrites();
@@ -2236,14 +2255,26 @@
 
     Profiler profiler2("SpectrogramLayer::paint: draw image", true);
 
-    paint.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+    std::cerr << "Painting " << w << "x" << rect.height()
+              << " from draw buffer at " << 0 << "," << rect.y()
+              << " to window at " << x0 << "," << rect.y() << std::endl;
+#endif
+
+    paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height());
 
     if (recreateWholePixmapCache) {
-	cache.pixmap = QPixmap(v->width(), v->height());
+	cache.pixmap = QPixmap(v->width(), h);
     }
 
+#ifdef DEBUG_SPECTROGRAM_REPAINT
+    std::cerr << "Painting " << w << "x" << h
+              << " from draw buffer at " << 0 << "," << 0
+              << " to cache at " << x0 << "," << 0 << std::endl;
+#endif
+
     QPainter cachePainter(&cache.pixmap);
-    cachePainter.drawImage(x0, y0, m_drawBuffer, 0, 0, w, h);
+    cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h);
     cachePainter.end();
 
     if (!m_normalizeVisibleArea || !overallMagChanged) {
@@ -2256,7 +2287,7 @@
             std::cerr << "SpectrogramLayer::paint() updating left (0, "
                       << cache.validArea.x() << ")" << std::endl;
 #endif
-            v->update(0, 0, cache.validArea.x(), v->height());
+            v->update(0, 0, cache.validArea.x(), h);
         }
         
         if (cache.validArea.x() + cache.validArea.width() <
@@ -2273,7 +2304,7 @@
                       0,
                       cache.pixmap.width() - (cache.validArea.x() +
                                               cache.validArea.width()),
-                      v->height());
+                      h);
         }
     } else {
         // overallMagChanged
@@ -2356,7 +2387,9 @@
     if (m_fftModels.find(v) == m_fftModels.end()) return 100;
 
     size_t completion = m_fftModels[v].first->getCompletion();
+#ifdef DEBUG_SPECTROGRAM_REPAINT
     std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
+#endif
     return completion;
 }
 
--- a/layer/SpectrogramLayer.h	Wed Mar 07 18:00:49 2007 +0000
+++ b/layer/SpectrogramLayer.h	Thu Mar 08 16:53:08 2007 +0000
@@ -71,6 +71,8 @@
 				    size_t &resolution,
 				    SnapType snap) const;
 
+    virtual bool hasLightBackground() const;
+
     void setModel(const DenseTimeValueModel *model);
 
     virtual PropertyList getProperties() const;
--- a/view/Pane.cpp	Wed Mar 07 18:00:49 2007 +0000
+++ b/view/Pane.cpp	Thu Mar 08 16:53:08 2007 +0000
@@ -233,8 +233,6 @@
         width() > 120 && height() > 100) {
         if (!m_headsUpDisplay->isVisible()) {
             m_headsUpDisplay->show();
-            connect(m_manager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
-                    this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
         }
         if (haveVThumb) {
             m_headsUpDisplay->setFixedHeight(m_vthumb->height() + m_hthumb->height());
@@ -245,10 +243,6 @@
         }
     } else {
         m_headsUpDisplay->hide();
-        if (m_manager) {
-            disconnect(m_manager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
-                       this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
-        }
     }
 }
 
@@ -571,22 +565,24 @@
 	r.y() + r.height() >= height() - fontHeight - 6) {
 
         size_t modelRate = waveformModel->getSampleRate();
-	size_t mainModelRate = m_manager->getMainModelSampleRate();
 	size_t playbackRate = m_manager->getPlaybackSampleRate();
-	    
+        size_t outputRate = m_manager->getOutputSampleRate();
+        
 	QString srNote = "";
 
 	// Show (R) for waveform models that will be resampled on
 	// playback, and (X) for waveform models that will be played
-	// at the wrong rate because their rate differs from that of
-	// the main model.
+	// at the wrong rate because their rate differs from the
+	// current playback rate (which is not necessarily that of the
+	// main model).
 
-	if (modelRate == mainModelRate) {
-	    if (modelRate != playbackRate) srNote = " " + tr("(R)");
-	} else {
-//	    std::cerr << "Sample rate = " << modelRate << ", main model rate = " << mainModelRate << std::endl;
-	    srNote = " " + tr("(X)");
-	}
+        if (playbackRate != 0) {
+            if (modelRate == playbackRate) {
+                if (modelRate != outputRate) srNote = " " + tr("(R)");
+            } else {
+                srNote = " " + tr("(X)");
+            }
+        }
 
 	QString desc = tr("%1 / %2Hz%3")
 	    .arg(RealTime::frame2RealTime(waveformModel->getEndFrame(),
@@ -1628,11 +1624,15 @@
 }
 
 void
-Pane::viewZoomLevelChanged(View *v, unsigned long, bool locked)
+Pane::viewZoomLevelChanged(View *v, unsigned long z, bool locked)
 {
 //    std::cerr << "Pane[" << this << "]::zoomLevelChanged (global now "
 //              << (m_manager ? m_manager->getGlobalZoom() : 0) << ")" << std::endl;
 
+    View::viewZoomLevelChanged(v, z, locked);
+
+    if (!m_vthumb->isVisible()) return;
+
     if (v != this) {
         if (!locked || !m_followZoom) return;
     }
--- a/view/View.cpp	Wed Mar 07 18:00:49 2007 +0000
+++ b/view/View.cpp	Thu Mar 08 16:53:08 2007 +0000
@@ -44,7 +44,6 @@
     m_followZoom(true),
     m_followPlay(PlaybackScrollPage),
     m_playPointerFrame(0),
-    m_lightBackground(true),
     m_showProgress(showProgress),
     m_cache(0),
     m_cacheCentreFrame(0),
@@ -431,6 +430,16 @@
     }
 }
 
+bool
+View::hasLightBackground() const
+{
+    for (LayerList::const_iterator i = m_layers.begin();
+         i != m_layers.end(); ++i) {
+        if (!(*i)->hasLightBackground()) return false;
+    }
+    return true;
+}
+
 View::LayerProgressBar::LayerProgressBar(QWidget *parent) :
     QProgressBar(parent)
 {
@@ -1614,14 +1623,13 @@
 		 "followPan=\"%3\" "
 		 "followZoom=\"%4\" "
 		 "tracking=\"%5\" "
-		 "light=\"%6\" %7>\n")
+		 " %6>\n")
 	.arg(m_centreFrame)
 	.arg(m_zoomLevel)
 	.arg(m_followPan)
 	.arg(m_followZoom)
 	.arg(m_followPlay == PlaybackScrollContinuous ? "scroll" :
 	     m_followPlay == PlaybackScrollPage ? "page" : "ignore")
-	.arg(m_lightBackground)
 	.arg(extraAttributes);
 
     for (size_t i = 0; i < m_layers.size(); ++i) {
--- a/view/View.h	Wed Mar 07 18:00:49 2007 +0000
+++ b/view/View.h	Thu Mar 08 16:53:08 2007 +0000
@@ -171,8 +171,7 @@
     virtual void setFollowGlobalZoom(bool f);
     virtual bool getFollowGlobalZoom() const { return m_followZoom; }
 
-    virtual void setLightBackground(bool lb) { m_lightBackground = lb; }
-    virtual bool hasLightBackground() const { return m_lightBackground; }
+    virtual bool hasLightBackground() const;
 
     enum TextStyle {
 	BoxedText,
--- a/view/ViewManager.cpp	Wed Mar 07 18:00:49 2007 +0000
+++ b/view/ViewManager.cpp	Thu Mar 08 16:53:08 2007 +0000
@@ -263,8 +263,17 @@
     }
 }
 
+size_t 
+ViewManager::getPlaybackSampleRate() const
+{
+    if (m_playSource) {
+        return m_playSource->getSourceSampleRate();
+    }
+    return 0;
+}
+
 size_t
-ViewManager::getPlaybackSampleRate() const
+ViewManager::getOutputSampleRate() const
 {
     if (m_playSource) {
 	return m_playSource->getTargetSampleRate();
--- a/view/ViewManager.h	Wed Mar 07 18:00:49 2007 +0000
+++ b/view/ViewManager.h	Thu Mar 08 16:53:08 2007 +0000
@@ -102,8 +102,28 @@
     bool getPlaySelectionMode() const { return m_playSelectionMode; }
     void setPlaySelectionMode(bool on);
 
+    /**
+     * The sample rate that is used for playback.  This is usually the
+     * rate of the main model, but not always.  Models whose rates
+     * differ from this will play back at the wrong speed -- there is
+     * no per-model resampler.
+     */
     size_t getPlaybackSampleRate() const;
+
+    /**
+     * The sample rate of the audio output device.  If the playback
+     * sample rate differs from this, everything will be resampled at
+     * the output stage.
+     */
+    size_t getOutputSampleRate() const;
+
+    /**
+     * The sample rate of the current main model.  This may in theory
+     * differ from the playback sample rate, in which case even the
+     * main model will play at the wrong speed.
+     */
     size_t getMainModelSampleRate() const { return m_mainModelSampleRate; }
+
     void setMainModelSampleRate(size_t sr) { m_mainModelSampleRate = sr; }
 
     enum OverlayMode {