changeset 330:846746e4e865 spectrogram-cache-rejig

* cache bits & bobs (wholly broken at the moment)
author Chris Cannam
date Fri, 16 Nov 2007 17:15:30 +0000
parents bbc9666cb961
children 6440e280122e
files layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h widgets/Thumbwheel.cpp
diffstat 3 files changed, 383 insertions(+), 287 deletions(-) [+]
line wrap: on
line diff
--- a/layer/SpectrogramLayer.cpp	Wed Nov 14 16:23:17 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Fri Nov 16 17:15:30 2007 +0000
@@ -118,6 +118,23 @@
         delete m_paintThread;
     }
 
+    m_pixmapCacheMutex.lock();
+
+    while (!m_pixmapCaches.empty()) {
+
+        PixmapCache *cache = m_pixmapCaches.begin()->second;
+
+        cache->mutex.lock();
+        delete cache->pixmap;
+        cache->pixmap = 0;
+        cache->mutex.unlock();
+
+        delete cache;
+        m_pixmapCaches.erase(m_pixmapCaches.begin());
+    }
+
+    m_pixmapCacheMutex.unlock();
+
     delete m_updateTimer;
     m_updateTimer = 0;
     
@@ -556,8 +573,10 @@
 SpectrogramLayer::invalidatePixmapCaches()
 {
     QMutexLocker locker(&m_pixmapCacheMutex);
+
     for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
          i != m_pixmapCaches.end(); ++i) {
+
         QMutexLocker locker(&i->second->mutex);
         i->second->validArea = QRect();
     }
@@ -567,6 +586,7 @@
 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
 {
     QMutexLocker locker(&m_pixmapCacheMutex);
+
     for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
          i != m_pixmapCaches.end(); ++i) {
 
@@ -924,14 +944,15 @@
 	invalidatePixmapCaches();
 
         m_pixmapCacheMutex.lock();
+
         m_pixmapCaches[v]->mutex.lock();
+        delete m_pixmapCaches[v]->pixmap;
+        m_pixmapCaches[v]->pixmap = 0;
         m_pixmapCaches[v]->mutex.unlock();
-        // that only works if the paint thread never locks the
-        // specific mutex more than once for a given instance of a
-        // lock on the general mutex.  really the data structure is
-        // unsuitable here.
+
         delete m_pixmapCaches[v];
         m_pixmapCaches.erase(v);
+
         m_pixmapCacheMutex.unlock();
 
         if (m_fftModels.find(v) != m_fftModels.end()) {
@@ -1682,6 +1703,8 @@
     return true;
 }
 
+
+
 // Plan:
 //
 // - the QImage cache is managed by the GUI thread (which creates,
@@ -1694,10 +1717,26 @@
 // complete and schedule another update if it isn't
 //
 // - we need a mutex between cache construction/destruction activity
-// and the paint thread
+// and the paint thread (I wonder whether we need a general mutex
+// between GUI thread changes to various parameters and paint thread
+// access to those parameters?)
+//!!! -> e.g. how is the access to FFTModels synchronised?
+//!!! -> and view magnitudes array?
 //
 // - the paint thread needs to release the mutex occasionally so that
 // cache positioning adjustments don't take forever.
+//
+// The cache covers a range from "somewhat before the start of the
+// view" to "somewhat after the end of it", and only (more or less)
+// the height of the view.  (Or is it better for it to cover the full
+// height?)  It is always at full resolution -- 1 pixel per bin in
+// both directions.  When drawing from cache, the GUI thread does the
+// scaling.
+
+//!!! and split into parent class (called e.g. DenseDataLayer) and
+//subclass; parent has GUI thread, subclass has paint thread and most
+//of the code.
+
 
 void
 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
@@ -1711,46 +1750,88 @@
     // paints.  The GUI thread should then never modify the cache
     // image without holding its specific mutex.)
 
-    Profiler profiler("SpectrogramLayer::paint", true);
+    Profiler profiler("SpectrogramLayer::paint", false);
 
     QMutexLocker locker(&m_pixmapCacheMutex);
 
+    rect = v->rect(); //!!!
+
     PixmapCache *cache = m_pixmapCaches[v];
     if (!cache) {
         cache = new PixmapCache;
-        cache->pixmap = QImage(v->width(), v->height(),
-                               QImage::Format_RGB32);
+        cache->pixmap = 0;
+        m_pixmapCaches[v] = cache;
+        cache->mutex.lock();
+        cache->pixmap = new QImage(v->width(), v->height(),
+                                   QImage::Format_RGB32);
+        cache->mutex.unlock();
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "SpectrogramLayer::paint: Created new cache, size "
                   << v->width() << "x" << v->height() << " ("
                   << v->width() * v->height() * 4 << " bytes)" << std::endl;
 #endif
-        m_pixmapCaches[v] = cache;
         return; //!!! prod paint thread
     }
 
     if (!cache->mutex.tryLock()) {
         std::cerr << "lock unavailable" << std::endl;
         paint.drawImage(rect,// & cache->validArea,
-                        cache->pixmap,
+                        *cache->pixmap,
                         rect);// & cache->validArea);
 //        v->update(rect);
         return;
     }
 
-    if (cache->pixmap.width() != v->width() ||
-        cache->pixmap.height() != v->height()) {
-        cache->pixmap = cache->pixmap.scaled(v->width(), v->height());
+    if (cache->pixmap->width() != v->width() ||
+        cache->pixmap->height() != v->height()) {
+        QImage *newImage = new QImage
+            (cache->pixmap->scaled(v->width(), v->height()));
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-        std::cerr << "SpectrogramLayer::paint: Rescaled cache to size "
+        std::cerr << "SpectrogramLayer::paint: Rescaling cache to size "
                   << v->width() << "x" << v->height() << " ("
                   << v->width() * v->height() * 4 << " bytes)" << std::endl;
 #endif
+        delete cache->pixmap;
+        cache->pixmap = newImage;
     }
 
+    // The cache has the correct resolution for the view in the y
+    // axis, but with a resolution of one pixel per increment in the x
+    // axis.  We may need to rescale when drawing.
+
+    paint.setRenderHint(QPainter::SmoothPixmapTransform, true);
+
+    //!!! not quite right in any case except integer ratio -- either
+    //source or target rect will have edges at non-integral
+    //coordinates
+
+    float ratio = float(v->getZoomLevel()) / float(getWindowIncrement());
+
+    long myStartFrame = v->getStartFrame();
+    long cacheStartFrame = cache->startFrame;
+    long xoff = (myStartFrame - cacheStartFrame) / getWindowIncrement();
+
+    std::cerr << "my start frame = " << myStartFrame << ", cache start frame = " << cacheStartFrame << std::endl;
+
+    QRect sourceRect = QRect(lrintf(rect.x() * ratio) + xoff,
+                             rect.y(),
+                             lrintf(rect.width() * ratio),
+                             rect.height());
+
+    std::cerr << "ratio = " << ratio << " (zoom " << v->getZoomLevel()
+              << ", incr " << getWindowIncrement() << "); source rect "
+              << sourceRect.x() << "," << sourceRect.y()
+              << " " << sourceRect.width() << "x" << sourceRect.height()
+              << ", target rect "
+              << rect.x() << "," << rect.y()
+              << " " << rect.width() << "x" << rect.height()
+              << " (image " << cache->pixmap->width() << "x"
+              << cache->pixmap->height() << ")"
+              << std::endl;
+
     paint.drawImage(rect,// & cache->validArea,
-                    cache->pixmap,
-                    rect);// & cache->validArea);
+                    *cache->pixmap,
+                    sourceRect);// & cache->validArea);
 
     if (cache->startFrame != v->getStartFrame()) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
@@ -1791,15 +1872,30 @@
             views.insert(const_cast<View *>(i->first)); //!!!
         }
         m_layer->m_pixmapCacheMutex.unlock();
+
+        bool workToDo = false;
         
         for (std::set<View *>::iterator vi = views.begin();
              vi != views.end(); ++vi) {
             
-            m_layer->paintCache(*vi);
+            if (m_layer->paintCache(*vi)) {
+                if (!m_exiting) {
+                    std::cerr << "Cache complete: asking view to update"
+                              << std::endl;
+                    (*vi)->update();
+                }
+            } else {
+                workToDo = true;
+            }
+
             if (m_exiting) break;
         }
 
-        if (!m_exiting) sleep(1);
+        if (!m_exiting) {
+            //!!! wait on condition
+            if (workToDo) usleep(100);
+            else sleep(1);
+        }
     }
 }
 
@@ -1808,12 +1904,12 @@
 bool
 SpectrogramLayer::paintCache(View *v) const
 {
-    Profiler profiler("SpectrogramLayer::paintCache", true);
+    Profiler profiler("SpectrogramLayer::paintCache", false);
 
     m_pixmapCacheMutex.lock();
     
     PixmapCache *cacheptr = m_pixmapCaches[v];
-    if (!cacheptr || cacheptr->pixmap.width() == 0) {
+    if (!cacheptr || !cacheptr->pixmap || cacheptr->pixmap->width() == 0) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "SpectrogramLayer::paintCache(): No cache to paint onto"
                   << std::endl;
@@ -1826,26 +1922,68 @@
     QMutexLocker locker(&cache.mutex);
     m_pixmapCacheMutex.unlock();
 
-    // establish rect to paint -- we want to paint the whole area that
-    // is not known to be valid
+    QImage &image = *cache.pixmap; //!!! rename to cache.image
+
+    const DenseTimeValueModel *model = m_model;
+    if (!model || !model->isOK() || !model->isReady()) {
+	return false;
+    }
+
+    //!!! mutex here -- we have the specific pixmap cache mutex held,
+    //but only that one currently and there's no mutex for this stuff
+    //in the other thread
+
+    //!!! what a big pile of crap
+    long startFrame = v->getStartFrame();
+    int increment = getWindowIncrement(); // signed for arithmetic purposes
+    size_t fftNominal = m_fftSize; // the user-specified fft size
+    size_t fftSize = getFFTSize(v); // the actual fft size (maybe padded)
+    FFTModel *fft = getFFTModel(v);
+    int sr = model->getSampleRate();
+    size_t fmin = m_minFrequency;
+    size_t fmax = m_maxFrequency;
+    FrequencyScale frequencyScale = m_frequencyScale;
+    BinDisplay binDisplay = m_binDisplay;
+    float threshold = m_threshold;
+    bool normalizeColumns = m_normalizeColumns;
+    bool normalizeVisibleArea = m_normalizeVisibleArea;
+    ColourScale colourScale = m_colourScale;
+    float gain = m_gain;
+    const Palette &palette = m_palette;
+    int zpl = getZeroPadLevel(v) + 1;
+
+    if (!fft) {
+	std::cerr << "ERROR: SpectrogramLayer::paintCache(): No FFT model, returning" << std::endl;
+	return true;
+    }
+
+
+    std::cerr << "SpectrogramLayer::paintCache: start frame "
+              << startFrame << ", increment " << increment << std::endl;
+
+
+    int w = image.width();
+    int h = image.height();
+
+
+    // Establish rect to paint.  We want to paint a not-too-large area
+    // that is not known to be valid, adjacent to an area that is
+    // known to be valid, such that the resulting known-valid area is
+    // rectangular still.  This doesn't really do that.
     
-    QRect rect = QRect(0, 0, cache.pixmap.width(), cache.pixmap.height());
-
-    QPainter paint(&cache.pixmap);
+    QRect rect = QRect(0, 0, w, h);
+
+    QPainter paint(cache.pixmap);
     
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    std::cerr << "SpectrogramLayer::paintCache(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
+    std::cerr << "SpectrogramLayer::paintCache(): m_model is " << model << ", window increment " << increment << std::endl;
     
     std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
 #endif
 
-    long startFrame = v->getStartFrame();
     if (startFrame < 0) m_candidateFillStartFrame = 0;
     else m_candidateFillStartFrame = startFrame;
 
-    if (!m_model || !m_model->isOK() || !m_model->isReady()) {
-	return false;
-    }
 /*
     if (isLayerDormant(v)) {
 	std::cerr << "SpectrogramLayer::paintCache(): Layer is dormant, making it undormant again" << std::endl;
@@ -1858,36 +1996,27 @@
     //!!! no longer use cache-fill thread
     const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
 */
-    size_t fftSize = getFFTSize(v);
-    FFTModel *fft = getFFTModel(v);
-    if (!fft) {
-	std::cerr << "ERROR: SpectrogramLayer::paintCache(): No FFT model, returning" << std::endl;
-	return true;
-    }
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-    std::cerr << "SpectrogramLayer::paintCache(): pixmap cache size " << cache.pixmap.width() << "x" << cache.pixmap.height() << std::endl;
+    std::cerr << "SpectrogramLayer::paintCache(): pixmap cache size " << w << "x" << h << std::endl;
     std::cerr << "SpectrogramLayer::paintCache(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
 #endif
 
-    int zoomLevel = v->getZoomLevel();
-
-    int x0 = 0;
-    int x1 = v->width();
-
-    bool recreateWholePixmapCache = true;
-
-    x0 = rect.left();
-    x1 = rect.right() + 1;
+    bool recreateWholeCache = true;
+
+    int x0 = rect.left();
+    int x1 = rect.right() + 1;
 
     if (cache.validArea.width() > 0) {
 
-	if (int(cache.zoomLevel) == zoomLevel &&
-	    cache.pixmap.width() == v->width() &&
-	    cache.pixmap.height() == v->height()) {
-
-	    if (v->getXForFrame(cache.startFrame) ==
-		v->getXForFrame(startFrame) &&
+        //!!! no, the gui thread will handle resizes -- we just draw
+        //the area that's available in the cache.  If the image is
+        //enlarged, our valid area should remain the same and we'll
+        //pick up on the discrepancy naturally (we hope)
+//	if (cache.pixmap->width() == v->width() &&
+//	    cache.pixmap->height() == v->height()) {
+
+	    if (cache.startFrame / increment == startFrame / increment &&
                 cache.validArea.x() <= x0 &&
                 cache.validArea.x() + cache.validArea.width() >= x1) {
 	    
@@ -1905,18 +2034,15 @@
 		std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
 #endif
 
-		recreateWholePixmapCache = false;
-
-		int dx = v->getXForFrame(cache.startFrame) -
-		         v->getXForFrame(startFrame);
+		recreateWholeCache = false;
+
+		int dx = cache.startFrame / increment - startFrame / increment;
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-		std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
+		std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap->width() << "x" << cache.pixmap->height() << ")" << std::endl;
 #endif
 
-		if (dx != 0 &&
-                    dx > -cache.pixmap.width() &&
-                    dx <  cache.pixmap.width()) {
+		if (dx != 0 && dx > -w && dx < w) {
 
 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
 		    // Copying a pixmap to itself doesn't work
@@ -1925,30 +2051,36 @@
 
 		    //!!! Need a utility function for this
 
+                    //!!! test again on Win and OS/X for images (above
+                    //comment applies to pixmaps)
+
+                    //!!! indeed, test again on linux for images too
+                    //-- I bet it doesn't work
+
 		    static QImage *tmpPixmap = 0;
 		    if (!tmpPixmap ||
-			tmpPixmap->width() != cache.pixmap.width() ||
-			tmpPixmap->height() != cache.pixmap.height()) {
+			tmpPixmap->width() != cache.pixmap->width() ||
+			tmpPixmap->height() != cache.pixmap->height()) {
 			delete tmpPixmap;
-			tmpPixmap = new QImage(cache.pixmap.width(),
-                                               cache.pixmap.height(),
+			tmpPixmap = new QImage(cache.pixmap->width(),
+                                               cache.pixmap->height(),
                                                QImage::Format_RGB32);
 		    }
 		    QPainter tmpPainter;
 		    tmpPainter.begin(tmpPixmap);
-		    tmpPainter.drawImage(0, 0, cache.pixmap);
+		    tmpPainter.drawImage(0, 0, image);
 		    tmpPainter.end();
                     paint.drawImage(dx, 0, *tmpPixmap);
 #else
-                    paint.drawImage(dx, 0, cache.pixmap);
+                    paint.drawImage(dx, 0, image); //!!! see above
 #endif
 
                     int px = cache.validArea.x();
                     int pw = cache.validArea.width();
 
 		    if (dx < 0) {
-			x0 = cache.pixmap.width() + dx;
-			x1 = cache.pixmap.width();
+			x0 = w + dx;
+			x1 = w;
                         px += dx;
                         if (px < 0) {
                             pw += px;
@@ -1959,8 +2091,8 @@
 			x0 = 0;
 			x1 = dx;
                         px += dx;
-                        if (px + pw > cache.pixmap.width()) {
-                            pw = int(cache.pixmap.width()) - px;
+                        if (px + pw > w) {
+                            pw = int(w) - px;
                             if (pw < 0) pw = 0;
                         }
 		    }
@@ -1970,40 +2102,37 @@
                               pw, cache.validArea.height());
 		}
 	    }
-	} 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();
-	}
-    }
+//	} else {
+//#ifdef DEBUG_SPECTROGRAM_REPAINT
+//	    std::cerr << "SpectrogramLayer: pixmap cache useless" << 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 (updateViewMagnitudes(v)) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
 #endif
-        recreateWholePixmapCache = true;
+        recreateWholeCache = true;
     } else {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
 #endif
     }
 */
-    if (recreateWholePixmapCache) {
+    if (recreateWholeCache) {
         x0 = 0;
-        x1 = rect.width();
+        x1 = w;
     }
 
     struct timeval tv;
@@ -2013,7 +2142,7 @@
     int paintBlockWidth = m_lastPaintBlockWidth;
 
     if (paintBlockWidth == 0) {
-        paintBlockWidth = (300000 / zoomLevel);
+        paintBlockWidth = (300000 / increment);
     } else {
         RealTime lastTime = m_lastPaintTime;
         while (lastTime > RealTime::fromMilliseconds(200) &&
@@ -2040,8 +2169,8 @@
     // about vertical matching of required and valid areas as well as
     // horizontal.
 
-    int h = v->height();
-    int w = x1 - x0;
+//    int h = v->height();
+//    int w = x1 - x0;
 
 #ifdef DEBUG_SPECTROGRAM_REPAINT
     std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
@@ -2053,8 +2182,6 @@
 
 //    m_drawBuffer.fill(m_palette.getColour(0).rgb());
 
-    int sr = m_model->getSampleRate();
-
     // Set minFreq and maxFreq to the frequency extents of the possibly
     // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
     // to the actual scale frequency extents (presumably not zero padded).
@@ -2063,52 +2190,40 @@
     // 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
+    //!!! --> we weren't actually using minFreq or maxFreq -- so just
+    // set our new fmin/fmax to the display extents
+
+    // Note fftSize is the actual zero-padded fft size, fftNominal the
     // nominal fft size.
     
-    size_t maxbin = m_fftSize / 2;
-    if (m_maxFrequency > 0) {
-	maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
-	if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
+    size_t maxbin = fftNominal / 2;
+    if (fmax > 0) {
+	maxbin = int((double(fmax) * fftNominal) / sr + 0.001);
+	if (maxbin > fftNominal / 2) maxbin = fftNominal / 2;
     }
 
     size_t minbin = 1;
-    if (m_minFrequency > 0) {
-	minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
-//        std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl;
+    if (fmin > 0) {
+	minbin = int((double(fmin) * fftNominal) / sr + 0.001);
 	if (minbin < 1) minbin = 1;
 	if (minbin >= maxbin) minbin = maxbin - 1;
     }
 
-    int zpl = getZeroPadLevel(v) + 1;
     minbin = minbin * zpl;
     maxbin = (maxbin + 1) * zpl - 1;
 
-    float minFreq = (float(minbin) * sr) / fftSize;
-    float maxFreq = (float(maxbin) * sr) / fftSize;
-
-    float displayMinFreq = minFreq;
-    float displayMaxFreq = maxFreq;
-
-    if (fftSize != m_fftSize) {
-        displayMinFreq = getEffectiveMinFrequency();
-        displayMaxFreq = getEffectiveMaxFrequency();
-    }
-
-//    std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl;
+    fmin = (float(minbin) * sr) / fftNominal;
+    fmax = (float(maxbin) * sr) / fftNominal;
 
     float ymag[h];
     float ydiv[h];
     float yval[maxbin + 1]; //!!! cache this?
 
-    size_t increment = getWindowIncrement();
-    
-    bool logarithmic = (m_frequencyScale == LogFrequencyScale);
+    bool logarithmic = (frequencyScale == LogFrequencyScale);
 
     for (size_t q = minbin; q <= maxbin; ++q) {
         float f0 = (float(q) * sr) / fftSize;
-        yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
-                                      logarithmic);
+        yval[q] = v->getYForFrequency(f0, fmin, fmax, logarithmic);
 //        std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl;
     }
 
@@ -2122,19 +2237,15 @@
         Preferences::getInstance()->getSpectrogramSmoothing();
     if (smoothing == Preferences::SpectrogramInterpolated ||
         smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
-        if (m_binDisplay != PeakBins &&
-            m_binDisplay != PeakFrequencies) {
+        if (binDisplay != PeakBins &&
+            binDisplay != PeakFrequencies) {
             interpolate = true;
         }
     }
 
-#ifdef DEBUG_SPECTROGRAM_REPAINT
-    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) {
+    for (int x = 0; x < (x1 - x0); ++x) {
 
         if (runOutOfData) break;
 
@@ -2143,34 +2254,21 @@
 	    ydiv[y] = 0.f;
 	}
 
-	float s0 = 0, s1 = 0;
-
-	if (!getXBinRange(v, x0 + x, s0, s1)) {
-//	    assert(x <= m_drawBuffer.width());
-	    continue;
+        int col = (startFrame / increment) + x0 + x;
+
+//        std::cerr << "x = " << x << ", col = " << col << std::endl;
+
+	if (col >= int(fft->getWidth())) {
+            continue;
 	}
 
-	int s0i = int(s0 + 0.001);
-	int s1i = int(s1);
-
-	if (s1i >= int(fft->getWidth())) {
-	    if (s0i >= int(fft->getWidth())) {
-		continue;
-	    } else {
-		s1i = s0i;
-	    }
-	}
-
-        for (int s = s0i; s <= s1i; ++s) {
-
-            if (!fft->isColumnAvailable(s)) {
+        if (!fft->isColumnAvailable(col)) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
-                std::cerr << "Met unavailable column at col " << s << std::endl;
+            std::cerr << "Met unavailable column at col " << col << std::endl;
 #endif
-//                continue;
-                runOutOfData = true;
-                break;
-            }
+            runOutOfData = true;
+            break;
+        }
 
 /*            
             if (!fftSuspended) {
@@ -2178,119 +2276,116 @@
                 fftSuspended = true;
             }
 */
-            MagnitudeRange mag;
-
-            FFTModel::PeakSet peaks;
-            if (m_binDisplay == PeakFrequencies &&
-                s < int(fft->getWidth()) - 1) {
-                peaks = fft->getPeakFrequencies(FFTModel::AllPeaks,
-                                                s,
-                                                minbin, maxbin - 1);
+
+        MagnitudeRange mag;
+
+        FFTModel::PeakSet peaks;
+        if (binDisplay == PeakFrequencies &&
+            col < int(fft->getWidth()) - 1) {
+            peaks = fft->getPeakFrequencies(FFTModel::AllPeaks,
+                                            col,
+                                            minbin, maxbin - 1);
+        }
+
+        for (size_t q = minbin; q < maxbin; ++q) {
+
+            float y0 = yval[q + 1];
+            float y1 = yval[q];
+
+            if (binDisplay == PeakBins) {
+                if (!fft->isLocalPeak(col, q)) continue;
             }
-
-            for (size_t q = minbin; q < maxbin; ++q) {
-
-                float y0 = yval[q + 1];
-                float y1 = yval[q];
-
-		if (m_binDisplay == PeakBins) {
-		    if (!fft->isLocalPeak(s, q)) continue;
-		}
-                if (m_binDisplay == PeakFrequencies) {
-                    if (peaks.find(q) == peaks.end()) continue;
+            if (binDisplay == PeakFrequencies) {
+                if (peaks.find(q) == peaks.end()) continue;
+            }
+            
+            if (threshold != 0.f &&
+                !fft->isOverThreshold(col, q, threshold * (fftNominal/2))) {
+                continue;
+            }
+
+            if (binDisplay == PeakFrequencies) {
+                y0 = y1 = v->getYForFrequency
+                    (peaks[q], fmin, fmax, logarithmic);
+            }
+		
+            int y0i = int(y0 + 0.001);
+            int y1i = int(y1);
+            
+            float value;
+            
+            if (colourScale == PhaseColourScale) {
+                value = fft->getPhaseAt(col, q);
+            } else if (normalizeColumns) {
+                value = fft->getNormalizedMagnitudeAt(col, q);
+                mag.sample(value);
+                value *= gain;
+            } else {
+                value = fft->getMagnitudeAt(col, q) / (fftNominal/2);
+                mag.sample(value);
+                value *= gain;
+            }
+
+            if (interpolate) {
+                    
+                int ypi = y0i;
+                if (q < maxbin - 1) ypi = int(yval[q + 2]);
+                
+                for (int y = ypi; y <= y1i; ++y) {
+                    
+                    if (y < 0 || y >= h) continue;
+                    
+                    float yprop = 1.f;
+                    float iprop = yprop;
+                    
+                    if (ypi < y0i && y <= y0i) {
+                        
+                        float half = float(y0i - ypi) / 2;
+                        float dist = y - (ypi + half);
+                        
+                        if (dist >= 0) {
+                            iprop = (iprop * dist) / half;
+                            ymag[y] += iprop * value;
+                        }
+                    } else {
+                        if (y1i > y0i) {
+                            
+                            float half = float(y1i - y0i) / 2;
+                            float dist = y - (y0i + half);
+                            
+                            if (dist >= 0) {
+                                iprop = (iprop * (half - dist)) / half;
+                            }
+                        }
+                        
+                        ymag[y] += iprop * value;
+                        ydiv[y] += yprop;
+                    }
                 }
-
-                if (m_threshold != 0.f &&
-                    !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) {
-                    continue;
-                }
-
-		float sprop = 1.0;
-		if (s == s0i) sprop *= (s + 1) - s0;
-		if (s == s1i) sprop *= s1 - s;
- 
-		if (m_binDisplay == PeakFrequencies) {
-		    y0 = y1 = v->getYForFrequency
-			(peaks[q], displayMinFreq, displayMaxFreq, logarithmic);
-		}
-		
-		int y0i = int(y0 + 0.001);
-		int y1i = int(y1);
-
-                float value;
                 
-                if (m_colourScale == PhaseColourScale) {
-                    value = fft->getPhaseAt(s, q);
-                } else if (m_normalizeColumns) {
-                    value = fft->getNormalizedMagnitudeAt(s, q);
-                    mag.sample(value);
-                    value *= m_gain;
-                } else {
-                    value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
-                    mag.sample(value);
-                    value *= m_gain;
-                }
-
-                if (interpolate) {
+            } else {
+                
+                for (int y = y0i; y <= y1i; ++y) {
                     
-                    int ypi = y0i;
-                    if (q < maxbin - 1) ypi = int(yval[q + 2]);
-
-                    for (int y = ypi; y <= y1i; ++y) {
+                    if (y < 0 || y >= h) continue;
                     
+                    float yprop = 1.f;
+                    if (y == y0i) yprop *= (y + 1) - y0;
+                    if (y == y1i) yprop *= y1 - y;
+                    
+                    for (int y = y0i; y <= y1i; ++y) {
+                        
                         if (y < 0 || y >= h) continue;
-                    
-                        float yprop = sprop;
-                        float iprop = yprop;
-
-                        if (ypi < y0i && y <= y0i) {
-
-                            float half = float(y0i - ypi) / 2;
-                            float dist = y - (ypi + half);
-
-                            if (dist >= 0) {
-                                iprop = (iprop * dist) / half;
-                                ymag[y] += iprop * value;
-                            }
-                        } else {
-                            if (y1i > y0i) {
-
-                                float half = float(y1i - y0i) / 2;
-                                float dist = y - (y0i + half);
-                                
-                                if (dist >= 0) {
-                                    iprop = (iprop * (half - dist)) / half;
-                                }
-                            }
-
-                            ymag[y] += iprop * value;
-                            ydiv[y] += yprop;
-                        }
-                    }
-
-                } else {
-
-                    for (int y = y0i; y <= y1i; ++y) {
-                    
-                        if (y < 0 || y >= h) continue;
-                    
-                        float yprop = sprop;
+                        
+                        float yprop = 1.f;
                         if (y == y0i) yprop *= (y + 1) - y0;
                         if (y == y1i) yprop *= y1 - y;
-
-                        for (int y = y0i; y <= y1i; ++y) {
-		    
-                            if (y < 0 || y >= h) continue;
-
-                            float yprop = sprop;
-                            if (y == y0i) yprop *= (y + 1) - y0;
-                            if (y == y1i) yprop *= y1 - y;
-                            ymag[y] += yprop * value;
-                            ydiv[y] += yprop;
-                        }
+                        ymag[y] += yprop * value;
+                        ydiv[y] += yprop;
                     }
                 }
-	    }
+            }
+        }
 /*
             if (mag.isSet()) {
 
@@ -2310,7 +2405,6 @@
                 }
             }
 */
-	}
 
 	for (int y = 0; y < h; ++y) {
 
@@ -2319,33 +2413,33 @@
 		unsigned char pixel = 0;
 
 		float avg = ymag[y] / ydiv[y];
-		pixel = getDisplayValue(v, avg);
+		pixel = getDisplayValue(v, avg); //!!!???
 
 //		assert(x <= m_drawBuffer.width());
-                assert(x <= cache.pixmap.width());
-		QColor c = m_palette.getColour(pixel);
-
-                if (x0 + x >= cache.pixmap.width()) {
+                assert(x <= w);
+		QColor c = palette.getColour(pixel);
+
+                if (x0 + x >= w) {
                     std::cerr << "INTERNAL ERROR: " << x0 << "+" 
-                              << x << " >= " << cache.pixmap.width()
+                              << x << " >= " << w
                               << " at SpectrogramLayer.cpp:"
                               << __LINE__ << std::endl;
                     continue;
                 }
 
-                if (y >= cache.pixmap.height()) {
+                if (y >= h) {
                     std::cerr << "INTERNAL ERROR: " << y
-                              << " >= " << cache.pixmap.height()
+                              << " >= " << h
                               << " at SpectrogramLayer.cpp:"
                               << __LINE__ << std::endl;
                     continue;
                 }
 
-                assert(x0 + x < cache.pixmap.width());
-                assert(y < cache.pixmap.height());
-
-		cache.pixmap.setPixel(x0 + x, y,
-                                      qRgb(c.red(), c.green(), c.blue()));
+                assert(x0 + x < w);
+                assert(y < h);
+
+		image.setPixel(x0 + x, y,
+                               qRgb(c.red(), c.green(), c.blue()));
 	    }
 	}
     }
@@ -2399,7 +2493,7 @@
     } else {
         if (x1 > x0 + paintBlockWidth) {
             int sfx = x1;
-            if (startFrame < 0) sfx = v->getXForFrame(0);
+            if (startFrame < 0) sfx = v->getXForFrame(0);            //!!!
             if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
                 x0 = sfx;
                 x1 = x0 + paintBlockWidth;
@@ -2422,7 +2516,7 @@
 
     paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height());
 
-    if (recreateWholePixmapCache) {
+    if (recreateWholeCache) {
 //	cache.pixmap = QPixmap(v->width(), h);
         cache.pixmap = QImage(v->width(), h, QImage::Format_RGB32);
     }
@@ -2441,7 +2535,6 @@
     if (!m_normalizeVisibleArea || !overallMagChanged) {
 */  
         cache.startFrame = startFrame;
-        cache.zoomLevel = zoomLevel;
 
         bool morePaintingRequired = false;
 
@@ -2455,18 +2548,18 @@
         }
         
         if (cache.validArea.x() + cache.validArea.width() <
-            cache.pixmap.width()) {
+            image.width()) {
 #ifdef DEBUG_SPECTROGRAM_REPAINT
             std::cerr << "SpectrogramLayer::paintCache() updating right ("
                       << cache.validArea.x() + cache.validArea.width()
                       << ", "
-                      << cache.pixmap.width() - (cache.validArea.x() +
+                      << image.width() - (cache.validArea.x() +
                                                  cache.validArea.width())
                       << ")" << std::endl;
 #endif
 //            v->update(cache.validArea.x() + cache.validArea.width(),
 //                      0,
-//                      cache.pixmap.width() - (cache.validArea.x() +
+//                      image.width() - (cache.validArea.x() +
 //                                              cache.validArea.width()),
 //                      h);
             morePaintingRequired = true;
@@ -2485,6 +2578,8 @@
     std::cerr << "SpectrogramLayer::paintCache() returning" << std::endl;
 #endif
 
+    paint.end();
+
     m_lastPaintBlockWidth = paintBlockWidth;
     (void)gettimeofday(&tv, 0);
     m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
@@ -2657,12 +2752,14 @@
     QMutexLocker locker(&m_pixmapCacheMutex);
 
     PixmapCache *cache = m_pixmapCaches[v];
-    if (!cache) return;
-
-    std::cerr << "cache width: " << cache->pixmap.width() << ", height: "
-              << cache->pixmap.height() << std::endl;
-
-    QImage image = cache->pixmap /*.toImage() */;
+    if (!cache || !cache->pixmap) return;
+
+    std::cerr << "cache width: " << cache->pixmap->width() << ", height: "
+              << cache->pixmap->height() << std::endl;
+
+    //!!! lock
+
+    QImage image = *cache->pixmap /*.toImage() */;
 
     ImageRegionFinder finder;
     QRect rect = finder.findRegionExtents(&image, e->pos());
@@ -2940,7 +3037,7 @@
 	return;
     }
 
-    Profiler profiler("SpectrogramLayer::paintVerticalScale", true);
+    Profiler profiler("SpectrogramLayer::paintVerticalScale", false);
 
     //!!! cache this?
 
--- a/layer/SpectrogramLayer.h	Wed Nov 14 16:23:17 2007 +0000
+++ b/layer/SpectrogramLayer.h	Fri Nov 16 17:15:30 2007 +0000
@@ -283,10 +283,9 @@
     struct PixmapCache
     {
         QMutex mutex;
-        QImage pixmap;
+        QImage *pixmap;
         QRect validArea;
         long startFrame;
-        size_t zoomLevel;
     };
     typedef std::map<const View *, PixmapCache *> ViewPixmapCache;
     void invalidatePixmapCaches();
--- a/widgets/Thumbwheel.cpp	Wed Nov 14 16:23:17 2007 +0000
+++ b/widgets/Thumbwheel.cpp	Fri Nov 16 17:15:30 2007 +0000
@@ -436,7 +436,7 @@
 void
 Thumbwheel::paintEvent(QPaintEvent *)
 {
-    Profiler profiler("Thumbwheel::paintEvent", true);
+    Profiler profiler("Thumbwheel::paintEvent", false);
 
     int bw = 3;