comparison layer/SpectrogramLayer.cpp @ 329:bbc9666cb961 spectrogram-cache-rejig

* First hack towards more efficient paint mechanism from cache filled in a background thread. This doesn't work properly.
author Chris Cannam
date Wed, 14 Nov 2007 16:23:17 +0000
parents 29fcf125f98b
children 846746e4e865
comparison
equal deleted inserted replaced
328:b6df8b44b98d 329:bbc9666cb961
40 #include <iostream> 40 #include <iostream>
41 41
42 #include <cassert> 42 #include <cassert>
43 #include <cmath> 43 #include <cmath>
44 44
45 //#define DEBUG_SPECTROGRAM_REPAINT 1 45 #define DEBUG_SPECTROGRAM_REPAINT 1
46 46
47 SpectrogramLayer::SpectrogramLayer(Configuration config) : 47 SpectrogramLayer::SpectrogramLayer(Configuration config) :
48 m_model(0), 48 m_model(0),
49 m_channel(0), 49 m_channel(0),
50 m_windowSize(1024), 50 m_windowSize(1024),
103 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 103 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
104 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 104 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
105 setWindowType(prefs->getWindowType()); 105 setWindowType(prefs->getWindowType());
106 106
107 initialisePalette(); 107 initialisePalette();
108
109 m_paintThread = new PaintThread(this);
110 m_paintThread->start();
108 } 111 }
109 112
110 SpectrogramLayer::~SpectrogramLayer() 113 SpectrogramLayer::~SpectrogramLayer()
111 { 114 {
115 if (m_paintThread) {
116 m_paintThread->exiting();
117 m_paintThread->wait();
118 delete m_paintThread;
119 }
120
112 delete m_updateTimer; 121 delete m_updateTimer;
113 m_updateTimer = 0; 122 m_updateTimer = 0;
114 123
115 invalidateFFTModels(); 124 invalidateFFTModels();
116 } 125 }
544 } 553 }
545 554
546 void 555 void
547 SpectrogramLayer::invalidatePixmapCaches() 556 SpectrogramLayer::invalidatePixmapCaches()
548 { 557 {
558 QMutexLocker locker(&m_pixmapCacheMutex);
549 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); 559 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
550 i != m_pixmapCaches.end(); ++i) { 560 i != m_pixmapCaches.end(); ++i) {
551 i->second.validArea = QRect(); 561 QMutexLocker locker(&i->second->mutex);
562 i->second->validArea = QRect();
552 } 563 }
553 } 564 }
554 565
555 void 566 void
556 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame) 567 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
557 { 568 {
569 QMutexLocker locker(&m_pixmapCacheMutex);
558 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); 570 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
559 i != m_pixmapCaches.end(); ++i) { 571 i != m_pixmapCaches.end(); ++i) {
560 572
573 QMutexLocker locker(&i->second->mutex);
574
561 //!!! when are views removed from the map? on setLayerDormant? 575 //!!! when are views removed from the map? on setLayerDormant?
562 const View *v = i->first; 576 const View *v = i->first;
563 577
564 if (startFrame < v->getEndFrame() && int(endFrame) >= v->getStartFrame()) { 578 if (startFrame < v->getEndFrame() && int(endFrame) >= v->getStartFrame()) {
565 i->second.validArea = QRect(); 579 i->second->validArea = QRect();
566 } 580 }
567 } 581 }
568 } 582 }
569 583
570 void 584 void
906 } 920 }
907 921
908 Layer::setLayerDormant(v, true); 922 Layer::setLayerDormant(v, true);
909 923
910 invalidatePixmapCaches(); 924 invalidatePixmapCaches();
925
926 m_pixmapCacheMutex.lock();
927 m_pixmapCaches[v]->mutex.lock();
928 m_pixmapCaches[v]->mutex.unlock();
929 // that only works if the paint thread never locks the
930 // specific mutex more than once for a given instance of a
931 // lock on the general mutex. really the data structure is
932 // unsuitable here.
933 delete m_pixmapCaches[v];
911 m_pixmapCaches.erase(v); 934 m_pixmapCaches.erase(v);
935 m_pixmapCacheMutex.unlock();
912 936
913 if (m_fftModels.find(v) != m_fftModels.end()) { 937 if (m_fftModels.find(v) != m_fftModels.end()) {
914 938
915 if (m_sliceableModel == m_fftModels[v].first) { 939 if (m_sliceableModel == m_fftModels[v].first) {
916 bool replaced = false; 940 bool replaced = false;
1507 } 1531 }
1508 1532
1509 FFTModel * 1533 FFTModel *
1510 SpectrogramLayer::getFFTModel(const View *v) const 1534 SpectrogramLayer::getFFTModel(const View *v) const
1511 { 1535 {
1536 //!!! need lock
1537
1512 if (!m_model) return 0; 1538 if (!m_model) return 0;
1513 1539
1514 size_t fftSize = getFFTSize(v); 1540 size_t fftSize = getFFTSize(v);
1515 1541
1516 if (m_fftModels.find(v) != m_fftModels.end()) { 1542 if (m_fftModels.find(v) != m_fftModels.end()) {
1654 if (mag == m_viewMags[v]) return false; 1680 if (mag == m_viewMags[v]) return false;
1655 m_viewMags[v] = mag; 1681 m_viewMags[v] = mag;
1656 return true; 1682 return true;
1657 } 1683 }
1658 1684
1685 // Plan:
1686 //
1687 // - the QImage cache is managed by the GUI thread (which creates,
1688 // sizes and destroys it).
1689 //
1690 // - but the cache is drawn on by another thread (paint thread)
1691 //
1692 // - the GUI thread paint method should always just draw straight from
1693 // cache without any lock, but it needs to check whether the cache is
1694 // complete and schedule another update if it isn't
1695 //
1696 // - we need a mutex between cache construction/destruction activity
1697 // and the paint thread
1698 //
1699 // - the paint thread needs to release the mutex occasionally so that
1700 // cache positioning adjustments don't take forever.
1701
1659 void 1702 void
1660 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const 1703 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
1661 { 1704 {
1662 // What a lovely, old-fashioned function this is. 1705 // If the mutex for our cache is held, we should probably schedule
1663 // It's practically FORTRAN 77 in its clarity and linearity. 1706 // another update and return immediately. The general cache mutex
1707 // should never be held long enough by any other thread for this
1708 // to be an issue. (The paint thread should hold the general
1709 // cache mutex only long enough to look up the cache object in the
1710 // map, and then release it, retaining the specific mutex while it
1711 // paints. The GUI thread should then never modify the cache
1712 // image without holding its specific mutex.)
1664 1713
1665 Profiler profiler("SpectrogramLayer::paint", true); 1714 Profiler profiler("SpectrogramLayer::paint", true);
1666 #ifdef DEBUG_SPECTROGRAM_REPAINT 1715
1667 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl; 1716 QMutexLocker locker(&m_pixmapCacheMutex);
1717
1718 PixmapCache *cache = m_pixmapCaches[v];
1719 if (!cache) {
1720 cache = new PixmapCache;
1721 cache->pixmap = QImage(v->width(), v->height(),
1722 QImage::Format_RGB32);
1723 #ifdef DEBUG_SPECTROGRAM_REPAINT
1724 std::cerr << "SpectrogramLayer::paint: Created new cache, size "
1725 << v->width() << "x" << v->height() << " ("
1726 << v->width() * v->height() * 4 << " bytes)" << std::endl;
1727 #endif
1728 m_pixmapCaches[v] = cache;
1729 return; //!!! prod paint thread
1730 }
1731
1732 if (!cache->mutex.tryLock()) {
1733 std::cerr << "lock unavailable" << std::endl;
1734 paint.drawImage(rect,// & cache->validArea,
1735 cache->pixmap,
1736 rect);// & cache->validArea);
1737 // v->update(rect);
1738 return;
1739 }
1740
1741 if (cache->pixmap.width() != v->width() ||
1742 cache->pixmap.height() != v->height()) {
1743 cache->pixmap = cache->pixmap.scaled(v->width(), v->height());
1744 #ifdef DEBUG_SPECTROGRAM_REPAINT
1745 std::cerr << "SpectrogramLayer::paint: Rescaled cache to size "
1746 << v->width() << "x" << v->height() << " ("
1747 << v->width() * v->height() * 4 << " bytes)" << std::endl;
1748 #endif
1749 }
1750
1751 paint.drawImage(rect,// & cache->validArea,
1752 cache->pixmap,
1753 rect);// & cache->validArea);
1754
1755 if (cache->startFrame != v->getStartFrame()) {
1756 #ifdef DEBUG_SPECTROGRAM_REPAINT
1757 std::cerr << "SpectrogramLayer::paint(): Cache start frame "
1758 << cache->startFrame << " differs from view start frame "
1759 << v->getStartFrame() << std::endl;
1760 #endif
1761 cache->mutex.unlock();
1762 // v->update(); //!!! and prod
1763 return;
1764 }
1765
1766 if (!cache->validArea.contains(rect)) {
1767 //!!! don't want to be sending events; just want to flag up
1768 //that we need a repaint and let some higher power arrange to
1769 //do it
1770 // v->update(rect); //!!!??? prod paint thread
1771 }
1772
1773 // paint.drawImage(rect & cache->validArea,
1774 // cache->pixmap,
1775 // rect & cache->validArea);
1776
1777 cache->mutex.unlock();
1778 }
1779
1780
1781 void
1782 SpectrogramLayer::PaintThread::run()
1783 {
1784 while (!m_exiting) {
1785
1786 std::set<View *> views;
1787
1788 m_layer->m_pixmapCacheMutex.lock();
1789 for (ViewPixmapCache::iterator i = m_layer->m_pixmapCaches.begin();
1790 i != m_layer->m_pixmapCaches.end(); ++i) {
1791 views.insert(const_cast<View *>(i->first)); //!!!
1792 }
1793 m_layer->m_pixmapCacheMutex.unlock();
1794
1795 for (std::set<View *>::iterator vi = views.begin();
1796 vi != views.end(); ++vi) {
1797
1798 m_layer->paintCache(*vi);
1799 if (m_exiting) break;
1800 }
1801
1802 if (!m_exiting) sleep(1);
1803 }
1804 }
1805
1806 // return true if no more work to do, or can do no more
1807
1808 bool
1809 SpectrogramLayer::paintCache(View *v) const
1810 {
1811 Profiler profiler("SpectrogramLayer::paintCache", true);
1812
1813 m_pixmapCacheMutex.lock();
1814
1815 PixmapCache *cacheptr = m_pixmapCaches[v];
1816 if (!cacheptr || cacheptr->pixmap.width() == 0) {
1817 #ifdef DEBUG_SPECTROGRAM_REPAINT
1818 std::cerr << "SpectrogramLayer::paintCache(): No cache to paint onto"
1819 << std::endl;
1820 #endif
1821 m_pixmapCacheMutex.unlock();
1822 return false;
1823 }
1824
1825 PixmapCache &cache = *cacheptr;
1826 QMutexLocker locker(&cache.mutex);
1827 m_pixmapCacheMutex.unlock();
1828
1829 // establish rect to paint -- we want to paint the whole area that
1830 // is not known to be valid
1831
1832 QRect rect = QRect(0, 0, cache.pixmap.width(), cache.pixmap.height());
1833
1834 QPainter paint(&cache.pixmap);
1835
1836 #ifdef DEBUG_SPECTROGRAM_REPAINT
1837 std::cerr << "SpectrogramLayer::paintCache(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
1668 1838
1669 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl; 1839 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
1670 #endif 1840 #endif
1671 1841
1672 long startFrame = v->getStartFrame(); 1842 long startFrame = v->getStartFrame();
1673 if (startFrame < 0) m_candidateFillStartFrame = 0; 1843 if (startFrame < 0) m_candidateFillStartFrame = 0;
1674 else m_candidateFillStartFrame = startFrame; 1844 else m_candidateFillStartFrame = startFrame;
1675 1845
1676 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1846 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1677 return; 1847 return false;
1678 } 1848 }
1679 1849 /*
1680 if (isLayerDormant(v)) { 1850 if (isLayerDormant(v)) {
1681 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl; 1851 std::cerr << "SpectrogramLayer::paintCache(): Layer is dormant, making it undormant again" << std::endl;
1682 } 1852 }
1683 1853
1684 // Need to do this even if !isLayerDormant, as that could mean v 1854 // Need to do this even if !isLayerDormant, as that could mean v
1685 // is not in the dormancy map at all -- we need it to be present 1855 // is not in the dormancy map at all -- we need it to be present
1686 // and accountable for when determining whether we need the cache 1856 // and accountable for when determining whether we need the cache
1687 // in the cache-fill thread above. 1857 // in the cache-fill thread above.
1688 //!!! no longer use cache-fill thread 1858 //!!! no longer use cache-fill thread
1689 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); 1859 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1690 1860 */
1691 size_t fftSize = getFFTSize(v); 1861 size_t fftSize = getFFTSize(v);
1692 FFTModel *fft = getFFTModel(v); 1862 FFTModel *fft = getFFTModel(v);
1693 if (!fft) { 1863 if (!fft) {
1694 std::cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << std::endl; 1864 std::cerr << "ERROR: SpectrogramLayer::paintCache(): No FFT model, returning" << std::endl;
1695 return; 1865 return true;
1696 } 1866 }
1697 1867
1698 PixmapCache &cache = m_pixmapCaches[v]; 1868 #ifdef DEBUG_SPECTROGRAM_REPAINT
1699 1869 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache size " << cache.pixmap.width() << "x" << cache.pixmap.height() << std::endl;
1700 #ifdef DEBUG_SPECTROGRAM_REPAINT 1870 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
1701 std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
1702 #endif
1703
1704 #ifdef DEBUG_SPECTROGRAM_REPAINT
1705 bool stillCacheing = (m_updateTimer != 0);
1706 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
1707 #endif 1871 #endif
1708 1872
1709 int zoomLevel = v->getZoomLevel(); 1873 int zoomLevel = v->getZoomLevel();
1710 1874
1711 int x0 = 0; 1875 int x0 = 0;
1729 1893
1730 #ifdef DEBUG_SPECTROGRAM_REPAINT 1894 #ifdef DEBUG_SPECTROGRAM_REPAINT
1731 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl; 1895 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
1732 #endif 1896 #endif
1733 1897
1734 paint.drawPixmap(rect, cache.pixmap, rect); 1898 //!!! set a flag to that effect?
1735 illuminateLocalFeatures(v, paint); 1899
1736 return; 1900 return true;
1737 1901
1738 } else { 1902 } else {
1739 1903
1740 #ifdef DEBUG_SPECTROGRAM_REPAINT 1904 #ifdef DEBUG_SPECTROGRAM_REPAINT
1741 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl; 1905 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
1759 // properly on Windows or Mac (it only works when 1923 // properly on Windows or Mac (it only works when
1760 // moving in one direction). 1924 // moving in one direction).
1761 1925
1762 //!!! Need a utility function for this 1926 //!!! Need a utility function for this
1763 1927
1764 static QPixmap *tmpPixmap = 0; 1928 static QImage *tmpPixmap = 0;
1765 if (!tmpPixmap || 1929 if (!tmpPixmap ||
1766 tmpPixmap->width() != cache.pixmap.width() || 1930 tmpPixmap->width() != cache.pixmap.width() ||
1767 tmpPixmap->height() != cache.pixmap.height()) { 1931 tmpPixmap->height() != cache.pixmap.height()) {
1768 delete tmpPixmap; 1932 delete tmpPixmap;
1769 tmpPixmap = new QPixmap(cache.pixmap.width(), 1933 tmpPixmap = new QImage(cache.pixmap.width(),
1770 cache.pixmap.height()); 1934 cache.pixmap.height(),
1935 QImage::Format_RGB32);
1771 } 1936 }
1772 QPainter cachePainter; 1937 QPainter tmpPainter;
1773 cachePainter.begin(tmpPixmap); 1938 tmpPainter.begin(tmpPixmap);
1774 cachePainter.drawPixmap(0, 0, cache.pixmap); 1939 tmpPainter.drawImage(0, 0, cache.pixmap);
1775 cachePainter.end(); 1940 tmpPainter.end();
1776 cachePainter.begin(&cache.pixmap); 1941 paint.drawImage(dx, 0, *tmpPixmap);
1777 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
1778 cachePainter.end();
1779 #else 1942 #else
1780 QPainter cachePainter(&cache.pixmap); 1943 paint.drawImage(dx, 0, cache.pixmap);
1781 cachePainter.drawPixmap(dx, 0, cache.pixmap);
1782 cachePainter.end();
1783 #endif 1944 #endif
1784 1945
1785 int px = cache.validArea.x(); 1946 int px = cache.validArea.x();
1786 int pw = cache.validArea.width(); 1947 int pw = cache.validArea.width();
1787 1948
1805 } 1966 }
1806 1967
1807 cache.validArea = 1968 cache.validArea =
1808 QRect(px, cache.validArea.y(), 1969 QRect(px, cache.validArea.y(),
1809 pw, cache.validArea.height()); 1970 pw, cache.validArea.height());
1810
1811 paint.drawPixmap(rect & cache.validArea,
1812 cache.pixmap,
1813 rect & cache.validArea);
1814 } 1971 }
1815 } 1972 }
1816 } else { 1973 } else {
1817 #ifdef DEBUG_SPECTROGRAM_REPAINT 1974 #ifdef DEBUG_SPECTROGRAM_REPAINT
1818 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl; 1975 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
1830 } 1987 }
1831 #endif 1988 #endif
1832 cache.validArea = QRect(); 1989 cache.validArea = QRect();
1833 } 1990 }
1834 } 1991 }
1835 1992 /*!!!
1836 if (updateViewMagnitudes(v)) { 1993 if (updateViewMagnitudes(v)) {
1837 #ifdef DEBUG_SPECTROGRAM_REPAINT 1994 #ifdef DEBUG_SPECTROGRAM_REPAINT
1838 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; 1995 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1839 #endif 1996 #endif
1840 recreateWholePixmapCache = true; 1997 recreateWholePixmapCache = true;
1841 } else { 1998 } else {
1842 #ifdef DEBUG_SPECTROGRAM_REPAINT 1999 #ifdef DEBUG_SPECTROGRAM_REPAINT
1843 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; 2000 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1844 #endif 2001 #endif
1845 } 2002 }
1846 2003 */
1847 if (recreateWholePixmapCache) { 2004 if (recreateWholePixmapCache) {
1848 x0 = 0; 2005 x0 = 0;
1849 x1 = v->width(); 2006 x1 = rect.width();
1850 } 2007 }
1851 2008
1852 struct timeval tv; 2009 struct timeval tv;
1853 (void)gettimeofday(&tv, 0); 2010 (void)gettimeofday(&tv, 0);
1854 RealTime mainPaintStart = RealTime::fromTimeval(tv); 2011 RealTime mainPaintStart = RealTime::fromTimeval(tv);
1876 #ifdef DEBUG_SPECTROGRAM_REPAINT 2033 #ifdef DEBUG_SPECTROGRAM_REPAINT
1877 std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl; 2034 std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl;
1878 #endif 2035 #endif
1879 2036
1880 // We always paint the full height when refreshing the cache. 2037 // We always paint the full height when refreshing the cache.
1881 // Smaller heights can be used when painting direct from cache 2038 // Smaller heights can be used when painting from cache, but we
1882 // (further up in this function), but we want to ensure the cache 2039 // want to ensure the cache is coherent without having to worry
1883 // is coherent without having to worry about vertical matching of 2040 // about vertical matching of required and valid areas as well as
1884 // required and valid areas as well as horizontal. 2041 // horizontal.
1885 2042
1886 int h = v->height(); 2043 int h = v->height();
1887 2044 int w = x1 - x0;
2045
2046 #ifdef DEBUG_SPECTROGRAM_REPAINT
2047 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
2048 #endif
2049
2050 // if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
2051 // m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
2052 // }
2053
2054 // m_drawBuffer.fill(m_palette.getColour(0).rgb());
2055
2056 int sr = m_model->getSampleRate();
2057
2058 // Set minFreq and maxFreq to the frequency extents of the possibly
2059 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
2060 // to the actual scale frequency extents (presumably not zero padded).
2061
2062 // If we are zero padding, we want to use the zero-padded
2063 // equivalents of the bins that we would be using if not zero
2064 // padded, to avoid spaces at the top and bottom of the display.
2065
2066 // Note fftSize is the actual zero-padded fft size, m_fftSize the
2067 // nominal fft size.
2068
2069 size_t maxbin = m_fftSize / 2;
2070 if (m_maxFrequency > 0) {
2071 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
2072 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
2073 }
2074
2075 size_t minbin = 1;
2076 if (m_minFrequency > 0) {
2077 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
2078 // std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl;
2079 if (minbin < 1) minbin = 1;
2080 if (minbin >= maxbin) minbin = maxbin - 1;
2081 }
2082
2083 int zpl = getZeroPadLevel(v) + 1;
2084 minbin = minbin * zpl;
2085 maxbin = (maxbin + 1) * zpl - 1;
2086
2087 float minFreq = (float(minbin) * sr) / fftSize;
2088 float maxFreq = (float(maxbin) * sr) / fftSize;
2089
2090 float displayMinFreq = minFreq;
2091 float displayMaxFreq = maxFreq;
2092
2093 if (fftSize != m_fftSize) {
2094 displayMinFreq = getEffectiveMinFrequency();
2095 displayMaxFreq = getEffectiveMaxFrequency();
2096 }
2097
2098 // std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl;
2099
2100 float ymag[h];
2101 float ydiv[h];
2102 float yval[maxbin + 1]; //!!! cache this?
2103
2104 size_t increment = getWindowIncrement();
2105
2106 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2107
2108 for (size_t q = minbin; q <= maxbin; ++q) {
2109 float f0 = (float(q) * sr) / fftSize;
2110 yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2111 logarithmic);
2112 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl;
2113 }
2114
2115 MagnitudeRange overallMag = m_viewMags[v];
2116 // bool overallMagChanged = false;
2117
2118 // bool fftSuspended = false;
2119
2120 bool interpolate = false;
2121 Preferences::SpectrogramSmoothing smoothing =
2122 Preferences::getInstance()->getSpectrogramSmoothing();
2123 if (smoothing == Preferences::SpectrogramInterpolated ||
2124 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2125 if (m_binDisplay != PeakBins &&
2126 m_binDisplay != PeakFrequencies) {
2127 interpolate = true;
2128 }
2129 }
2130
2131 #ifdef DEBUG_SPECTROGRAM_REPAINT
2132 std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl;
2133 #endif
2134
2135 bool runOutOfData = false;
2136
2137 for (int x = 0; x < w; ++x) {
2138
2139 if (runOutOfData) break;
2140
2141 for (int y = 0; y < h; ++y) {
2142 ymag[y] = 0.f;
2143 ydiv[y] = 0.f;
2144 }
2145
2146 float s0 = 0, s1 = 0;
2147
2148 if (!getXBinRange(v, x0 + x, s0, s1)) {
2149 // assert(x <= m_drawBuffer.width());
2150 continue;
2151 }
2152
2153 int s0i = int(s0 + 0.001);
2154 int s1i = int(s1);
2155
2156 if (s1i >= int(fft->getWidth())) {
2157 if (s0i >= int(fft->getWidth())) {
2158 continue;
2159 } else {
2160 s1i = s0i;
2161 }
2162 }
2163
2164 for (int s = s0i; s <= s1i; ++s) {
2165
2166 if (!fft->isColumnAvailable(s)) {
2167 #ifdef DEBUG_SPECTROGRAM_REPAINT
2168 std::cerr << "Met unavailable column at col " << s << std::endl;
2169 #endif
2170 // continue;
2171 runOutOfData = true;
2172 break;
2173 }
2174
2175 /*
2176 if (!fftSuspended) {
2177 fft->suspendWrites();
2178 fftSuspended = true;
2179 }
2180 */
2181 MagnitudeRange mag;
2182
2183 FFTModel::PeakSet peaks;
2184 if (m_binDisplay == PeakFrequencies &&
2185 s < int(fft->getWidth()) - 1) {
2186 peaks = fft->getPeakFrequencies(FFTModel::AllPeaks,
2187 s,
2188 minbin, maxbin - 1);
2189 }
2190
2191 for (size_t q = minbin; q < maxbin; ++q) {
2192
2193 float y0 = yval[q + 1];
2194 float y1 = yval[q];
2195
2196 if (m_binDisplay == PeakBins) {
2197 if (!fft->isLocalPeak(s, q)) continue;
2198 }
2199 if (m_binDisplay == PeakFrequencies) {
2200 if (peaks.find(q) == peaks.end()) continue;
2201 }
2202
2203 if (m_threshold != 0.f &&
2204 !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) {
2205 continue;
2206 }
2207
2208 float sprop = 1.0;
2209 if (s == s0i) sprop *= (s + 1) - s0;
2210 if (s == s1i) sprop *= s1 - s;
2211
2212 if (m_binDisplay == PeakFrequencies) {
2213 y0 = y1 = v->getYForFrequency
2214 (peaks[q], displayMinFreq, displayMaxFreq, logarithmic);
2215 }
2216
2217 int y0i = int(y0 + 0.001);
2218 int y1i = int(y1);
2219
2220 float value;
2221
2222 if (m_colourScale == PhaseColourScale) {
2223 value = fft->getPhaseAt(s, q);
2224 } else if (m_normalizeColumns) {
2225 value = fft->getNormalizedMagnitudeAt(s, q);
2226 mag.sample(value);
2227 value *= m_gain;
2228 } else {
2229 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
2230 mag.sample(value);
2231 value *= m_gain;
2232 }
2233
2234 if (interpolate) {
2235
2236 int ypi = y0i;
2237 if (q < maxbin - 1) ypi = int(yval[q + 2]);
2238
2239 for (int y = ypi; y <= y1i; ++y) {
2240
2241 if (y < 0 || y >= h) continue;
2242
2243 float yprop = sprop;
2244 float iprop = yprop;
2245
2246 if (ypi < y0i && y <= y0i) {
2247
2248 float half = float(y0i - ypi) / 2;
2249 float dist = y - (ypi + half);
2250
2251 if (dist >= 0) {
2252 iprop = (iprop * dist) / half;
2253 ymag[y] += iprop * value;
2254 }
2255 } else {
2256 if (y1i > y0i) {
2257
2258 float half = float(y1i - y0i) / 2;
2259 float dist = y - (y0i + half);
2260
2261 if (dist >= 0) {
2262 iprop = (iprop * (half - dist)) / half;
2263 }
2264 }
2265
2266 ymag[y] += iprop * value;
2267 ydiv[y] += yprop;
2268 }
2269 }
2270
2271 } else {
2272
2273 for (int y = y0i; y <= y1i; ++y) {
2274
2275 if (y < 0 || y >= h) continue;
2276
2277 float yprop = sprop;
2278 if (y == y0i) yprop *= (y + 1) - y0;
2279 if (y == y1i) yprop *= y1 - y;
2280
2281 for (int y = y0i; y <= y1i; ++y) {
2282
2283 if (y < 0 || y >= h) continue;
2284
2285 float yprop = sprop;
2286 if (y == y0i) yprop *= (y + 1) - y0;
2287 if (y == y1i) yprop *= y1 - y;
2288 ymag[y] += yprop * value;
2289 ydiv[y] += yprop;
2290 }
2291 }
2292 }
2293 }
2294 /*
2295 if (mag.isSet()) {
2296
2297 if (s >= int(m_columnMags.size())) {
2298 std::cerr << "INTERNAL ERROR: " << s << " >= "
2299 << m_columnMags.size() << " at SpectrogramLayer.cpp:" << __LINE__ << std::endl;
2300 }
2301
2302 m_columnMags[s].sample(mag);
2303
2304 if (overallMag.sample(mag)) {
2305 //!!! scaling would change here
2306 overallMagChanged = true;
2307 #ifdef DEBUG_SPECTROGRAM_REPAINT
2308 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl;
2309 #endif
2310 }
2311 }
2312 */
2313 }
2314
2315 for (int y = 0; y < h; ++y) {
2316
2317 if (ydiv[y] > 0.0) {
2318
2319 unsigned char pixel = 0;
2320
2321 float avg = ymag[y] / ydiv[y];
2322 pixel = getDisplayValue(v, avg);
2323
2324 // assert(x <= m_drawBuffer.width());
2325 assert(x <= cache.pixmap.width());
2326 QColor c = m_palette.getColour(pixel);
2327
2328 if (x0 + x >= cache.pixmap.width()) {
2329 std::cerr << "INTERNAL ERROR: " << x0 << "+"
2330 << x << " >= " << cache.pixmap.width()
2331 << " at SpectrogramLayer.cpp:"
2332 << __LINE__ << std::endl;
2333 continue;
2334 }
2335
2336 if (y >= cache.pixmap.height()) {
2337 std::cerr << "INTERNAL ERROR: " << y
2338 << " >= " << cache.pixmap.height()
2339 << " at SpectrogramLayer.cpp:"
2340 << __LINE__ << std::endl;
2341 continue;
2342 }
2343
2344 assert(x0 + x < cache.pixmap.width());
2345 assert(y < cache.pixmap.height());
2346
2347 cache.pixmap.setPixel(x0 + x, y,
2348 qRgb(c.red(), c.green(), c.blue()));
2349 }
2350 }
2351 }
2352 /*
2353 if (overallMagChanged) {
2354 m_viewMags[v] = overallMag;
2355 #ifdef DEBUG_SPECTROGRAM_REPAINT
2356 std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl;
2357 #endif
2358 } else {
2359 #ifdef DEBUG_SPECTROGRAM_REPAINT
2360 std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
2361 #endif
2362 }
2363 */
1888 if (cache.validArea.width() > 0) { 2364 if (cache.validArea.width() > 0) {
1889 2365
1890 int vx0 = 0, vx1 = 0; 2366 int vx0 = 0, vx1 = 0;
1891 vx0 = cache.validArea.x(); 2367 vx0 = cache.validArea.x();
1892 vx1 = cache.validArea.x() + cache.validArea.width(); 2368 vx1 = cache.validArea.x() + cache.validArea.width();
1915 } 2391 }
1916 2392
1917 cache.validArea = QRect 2393 cache.validArea = QRect
1918 (std::min(vx0, x0), cache.validArea.y(), 2394 (std::min(vx0, x0), cache.validArea.y(),
1919 std::max(vx1 - std::min(vx0, x0), 2395 std::max(vx1 - std::min(vx0, x0),
1920 x1 - std::min(vx0, x0)), 2396 x1 - std::min(vx0, x0)),
1921 cache.validArea.height()); 2397 cache.validArea.height());
1922 2398
1923 } else { 2399 } else {
1924 if (x1 > x0 + paintBlockWidth) { 2400 if (x1 > x0 + paintBlockWidth) {
1925 int sfx = x1; 2401 int sfx = x1;
1933 x1 = x0 + paintBlockWidth; 2409 x1 = x0 + paintBlockWidth;
1934 } 2410 }
1935 } 2411 }
1936 cache.validArea = QRect(x0, 0, x1 - x0, h); 2412 cache.validArea = QRect(x0, 0, x1 - x0, h);
1937 } 2413 }
1938 2414 /*
1939 int w = x1 - x0; 2415 Profiler profiler2("SpectrogramLayer::paintCache: draw image", true);
1940
1941 #ifdef DEBUG_SPECTROGRAM_REPAINT
1942 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
1943 #endif
1944
1945 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
1946 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
1947 }
1948
1949 m_drawBuffer.fill(m_palette.getColour(0).rgb());
1950
1951 int sr = m_model->getSampleRate();
1952
1953 // Set minFreq and maxFreq to the frequency extents of the possibly
1954 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
1955 // to the actual scale frequency extents (presumably not zero padded).
1956
1957 // If we are zero padding, we want to use the zero-padded
1958 // equivalents of the bins that we would be using if not zero
1959 // padded, to avoid spaces at the top and bottom of the display.
1960
1961 // Note fftSize is the actual zero-padded fft size, m_fftSize the
1962 // nominal fft size.
1963
1964 size_t maxbin = m_fftSize / 2;
1965 if (m_maxFrequency > 0) {
1966 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
1967 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1968 }
1969
1970 size_t minbin = 1;
1971 if (m_minFrequency > 0) {
1972 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
1973 // std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl;
1974 if (minbin < 1) minbin = 1;
1975 if (minbin >= maxbin) minbin = maxbin - 1;
1976 }
1977
1978 int zpl = getZeroPadLevel(v) + 1;
1979 minbin = minbin * zpl;
1980 maxbin = (maxbin + 1) * zpl - 1;
1981
1982 float minFreq = (float(minbin) * sr) / fftSize;
1983 float maxFreq = (float(maxbin) * sr) / fftSize;
1984
1985 float displayMinFreq = minFreq;
1986 float displayMaxFreq = maxFreq;
1987
1988 if (fftSize != m_fftSize) {
1989 displayMinFreq = getEffectiveMinFrequency();
1990 displayMaxFreq = getEffectiveMaxFrequency();
1991 }
1992
1993 // std::cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << std::endl;
1994
1995 float ymag[h];
1996 float ydiv[h];
1997 float yval[maxbin + 1]; //!!! cache this?
1998
1999 size_t increment = getWindowIncrement();
2000
2001 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2002
2003 for (size_t q = minbin; q <= maxbin; ++q) {
2004 float f0 = (float(q) * sr) / fftSize;
2005 yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2006 logarithmic);
2007 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl;
2008 }
2009
2010 MagnitudeRange overallMag = m_viewMags[v];
2011 bool overallMagChanged = false;
2012
2013 bool fftSuspended = false;
2014
2015 bool interpolate = false;
2016 Preferences::SpectrogramSmoothing smoothing =
2017 Preferences::getInstance()->getSpectrogramSmoothing();
2018 if (smoothing == Preferences::SpectrogramInterpolated ||
2019 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2020 if (m_binDisplay != PeakBins &&
2021 m_binDisplay != PeakFrequencies) {
2022 interpolate = true;
2023 }
2024 }
2025
2026 #ifdef DEBUG_SPECTROGRAM_REPAINT
2027 std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl;
2028 #endif
2029
2030 bool runOutOfData = false;
2031
2032 for (int x = 0; x < w; ++x) {
2033
2034 if (runOutOfData) break;
2035
2036 for (int y = 0; y < h; ++y) {
2037 ymag[y] = 0.f;
2038 ydiv[y] = 0.f;
2039 }
2040
2041 float s0 = 0, s1 = 0;
2042
2043 if (!getXBinRange(v, x0 + x, s0, s1)) {
2044 assert(x <= m_drawBuffer.width());
2045 continue;
2046 }
2047
2048 int s0i = int(s0 + 0.001);
2049 int s1i = int(s1);
2050
2051 if (s1i >= int(fft->getWidth())) {
2052 if (s0i >= int(fft->getWidth())) {
2053 continue;
2054 } else {
2055 s1i = s0i;
2056 }
2057 }
2058
2059 for (int s = s0i; s <= s1i; ++s) {
2060
2061 if (!fft->isColumnAvailable(s)) {
2062 #ifdef DEBUG_SPECTROGRAM_REPAINT
2063 std::cerr << "Met unavailable column at col " << s << std::endl;
2064 #endif
2065 // continue;
2066 runOutOfData = true;
2067 break;
2068 }
2069
2070 if (!fftSuspended) {
2071 fft->suspendWrites();
2072 fftSuspended = true;
2073 }
2074
2075 MagnitudeRange mag;
2076
2077 FFTModel::PeakSet peaks;
2078 if (m_binDisplay == PeakFrequencies &&
2079 s < int(fft->getWidth()) - 1) {
2080 peaks = fft->getPeakFrequencies(FFTModel::AllPeaks,
2081 s,
2082 minbin, maxbin - 1);
2083 }
2084
2085 for (size_t q = minbin; q < maxbin; ++q) {
2086
2087 float y0 = yval[q + 1];
2088 float y1 = yval[q];
2089
2090 if (m_binDisplay == PeakBins) {
2091 if (!fft->isLocalPeak(s, q)) continue;
2092 }
2093 if (m_binDisplay == PeakFrequencies) {
2094 if (peaks.find(q) == peaks.end()) continue;
2095 }
2096
2097 if (m_threshold != 0.f &&
2098 !fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) {
2099 continue;
2100 }
2101
2102 float sprop = 1.0;
2103 if (s == s0i) sprop *= (s + 1) - s0;
2104 if (s == s1i) sprop *= s1 - s;
2105
2106 if (m_binDisplay == PeakFrequencies) {
2107 y0 = y1 = v->getYForFrequency
2108 (peaks[q], displayMinFreq, displayMaxFreq, logarithmic);
2109 }
2110
2111 int y0i = int(y0 + 0.001);
2112 int y1i = int(y1);
2113
2114 float value;
2115
2116 if (m_colourScale == PhaseColourScale) {
2117 value = fft->getPhaseAt(s, q);
2118 } else if (m_normalizeColumns) {
2119 value = fft->getNormalizedMagnitudeAt(s, q);
2120 mag.sample(value);
2121 value *= m_gain;
2122 } else {
2123 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
2124 mag.sample(value);
2125 value *= m_gain;
2126 }
2127
2128 if (interpolate) {
2129
2130 int ypi = y0i;
2131 if (q < maxbin - 1) ypi = int(yval[q + 2]);
2132
2133 for (int y = ypi; y <= y1i; ++y) {
2134
2135 if (y < 0 || y >= h) continue;
2136
2137 float yprop = sprop;
2138 float iprop = yprop;
2139
2140 if (ypi < y0i && y <= y0i) {
2141
2142 float half = float(y0i - ypi) / 2;
2143 float dist = y - (ypi + half);
2144
2145 if (dist >= 0) {
2146 iprop = (iprop * dist) / half;
2147 ymag[y] += iprop * value;
2148 }
2149 } else {
2150 if (y1i > y0i) {
2151
2152 float half = float(y1i - y0i) / 2;
2153 float dist = y - (y0i + half);
2154
2155 if (dist >= 0) {
2156 iprop = (iprop * (half - dist)) / half;
2157 }
2158 }
2159
2160 ymag[y] += iprop * value;
2161 ydiv[y] += yprop;
2162 }
2163 }
2164
2165 } else {
2166
2167 for (int y = y0i; y <= y1i; ++y) {
2168
2169 if (y < 0 || y >= h) continue;
2170
2171 float yprop = sprop;
2172 if (y == y0i) yprop *= (y + 1) - y0;
2173 if (y == y1i) yprop *= y1 - y;
2174
2175 for (int y = y0i; y <= y1i; ++y) {
2176
2177 if (y < 0 || y >= h) continue;
2178
2179 float yprop = sprop;
2180 if (y == y0i) yprop *= (y + 1) - y0;
2181 if (y == y1i) yprop *= y1 - y;
2182 ymag[y] += yprop * value;
2183 ydiv[y] += yprop;
2184 }
2185 }
2186 }
2187 }
2188
2189 if (mag.isSet()) {
2190
2191 if (s >= int(m_columnMags.size())) {
2192 std::cerr << "INTERNAL ERROR: " << s << " >= "
2193 << m_columnMags.size() << " at SpectrogramLayer.cpp:2087" << std::endl;
2194 }
2195
2196 m_columnMags[s].sample(mag);
2197
2198 if (overallMag.sample(mag)) {
2199 //!!! scaling would change here
2200 overallMagChanged = true;
2201 #ifdef DEBUG_SPECTROGRAM_REPAINT
2202 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl;
2203 #endif
2204 }
2205 }
2206 }
2207
2208 for (int y = 0; y < h; ++y) {
2209
2210 if (ydiv[y] > 0.0) {
2211
2212 unsigned char pixel = 0;
2213
2214 float avg = ymag[y] / ydiv[y];
2215 pixel = getDisplayValue(v, avg);
2216
2217 assert(x <= m_drawBuffer.width());
2218 QColor c = m_palette.getColour(pixel);
2219 m_drawBuffer.setPixel(x, y,
2220 qRgb(c.red(), c.green(), c.blue()));
2221 }
2222 }
2223 }
2224
2225 if (overallMagChanged) {
2226 m_viewMags[v] = overallMag;
2227 #ifdef DEBUG_SPECTROGRAM_REPAINT
2228 std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl;
2229 #endif
2230 } else {
2231 #ifdef DEBUG_SPECTROGRAM_REPAINT
2232 std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
2233 #endif
2234 }
2235
2236 Profiler profiler2("SpectrogramLayer::paint: draw image", true);
2237 2416
2238 #ifdef DEBUG_SPECTROGRAM_REPAINT 2417 #ifdef DEBUG_SPECTROGRAM_REPAINT
2239 std::cerr << "Painting " << w << "x" << rect.height() 2418 std::cerr << "Painting " << w << "x" << rect.height()
2240 << " from draw buffer at " << 0 << "," << rect.y() 2419 << " from draw buffer at " << 0 << "," << rect.y()
2241 << " to window at " << x0 << "," << rect.y() << std::endl; 2420 << " to window at " << x0 << "," << rect.y() << std::endl;
2242 #endif 2421 #endif
2243 2422
2244 paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height()); 2423 paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height());
2245 2424
2246 if (recreateWholePixmapCache) { 2425 if (recreateWholePixmapCache) {
2247 cache.pixmap = QPixmap(v->width(), h); 2426 // cache.pixmap = QPixmap(v->width(), h);
2427 cache.pixmap = QImage(v->width(), h, QImage::Format_RGB32);
2248 } 2428 }
2249 2429
2250 #ifdef DEBUG_SPECTROGRAM_REPAINT 2430 #ifdef DEBUG_SPECTROGRAM_REPAINT
2251 std::cerr << "Painting " << w << "x" << h 2431 std::cerr << "Painting " << w << "x" << h
2252 << " from draw buffer at " << 0 << "," << 0 2432 << " from draw buffer at " << 0 << "," << 0
2254 #endif 2434 #endif
2255 2435
2256 QPainter cachePainter(&cache.pixmap); 2436 QPainter cachePainter(&cache.pixmap);
2257 cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h); 2437 cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h);
2258 cachePainter.end(); 2438 cachePainter.end();
2259 2439 */
2440 /*
2260 if (!m_normalizeVisibleArea || !overallMagChanged) { 2441 if (!m_normalizeVisibleArea || !overallMagChanged) {
2261 2442 */
2262 cache.startFrame = startFrame; 2443 cache.startFrame = startFrame;
2263 cache.zoomLevel = zoomLevel; 2444 cache.zoomLevel = zoomLevel;
2264 2445
2446 bool morePaintingRequired = false;
2447
2265 if (cache.validArea.x() > 0) { 2448 if (cache.validArea.x() > 0) {
2266 #ifdef DEBUG_SPECTROGRAM_REPAINT 2449 #ifdef DEBUG_SPECTROGRAM_REPAINT
2267 std::cerr << "SpectrogramLayer::paint() updating left (0, " 2450 std::cerr << "SpectrogramLayer::paintCache() updating left (0, "
2268 << cache.validArea.x() << ")" << std::endl; 2451 << cache.validArea.x() << ")" << std::endl;
2269 #endif 2452 #endif
2270 v->update(0, 0, cache.validArea.x(), h); 2453 // v->update(0, 0, cache.validArea.x(), h);
2454 morePaintingRequired = true;
2271 } 2455 }
2272 2456
2273 if (cache.validArea.x() + cache.validArea.width() < 2457 if (cache.validArea.x() + cache.validArea.width() <
2274 cache.pixmap.width()) { 2458 cache.pixmap.width()) {
2275 #ifdef DEBUG_SPECTROGRAM_REPAINT 2459 #ifdef DEBUG_SPECTROGRAM_REPAINT
2276 std::cerr << "SpectrogramLayer::paint() updating right (" 2460 std::cerr << "SpectrogramLayer::paintCache() updating right ("
2277 << cache.validArea.x() + cache.validArea.width() 2461 << cache.validArea.x() + cache.validArea.width()
2278 << ", " 2462 << ", "
2279 << cache.pixmap.width() - (cache.validArea.x() + 2463 << cache.pixmap.width() - (cache.validArea.x() +
2280 cache.validArea.width()) 2464 cache.validArea.width())
2281 << ")" << std::endl; 2465 << ")" << std::endl;
2282 #endif 2466 #endif
2283 v->update(cache.validArea.x() + cache.validArea.width(), 2467 // v->update(cache.validArea.x() + cache.validArea.width(),
2284 0, 2468 // 0,
2285 cache.pixmap.width() - (cache.validArea.x() + 2469 // cache.pixmap.width() - (cache.validArea.x() +
2286 cache.validArea.width()), 2470 // cache.validArea.width()),
2287 h); 2471 // h);
2472 morePaintingRequired = true;
2288 } 2473 }
2474 /*
2289 } else { 2475 } else {
2290 // overallMagChanged 2476 // overallMagChanged
2291 cache.validArea = QRect(); 2477 cache.validArea = QRect();
2292 v->update(); 2478 morePaintingRequired = true;
2293 } 2479 // v->update();
2294 2480 }
2295 illuminateLocalFeatures(v, paint); 2481 */
2296 2482 // illuminateLocalFeatures(v, paint);
2297 #ifdef DEBUG_SPECTROGRAM_REPAINT 2483
2298 std::cerr << "SpectrogramLayer::paint() returning" << std::endl; 2484 #ifdef DEBUG_SPECTROGRAM_REPAINT
2485 std::cerr << "SpectrogramLayer::paintCache() returning" << std::endl;
2299 #endif 2486 #endif
2300 2487
2301 m_lastPaintBlockWidth = paintBlockWidth; 2488 m_lastPaintBlockWidth = paintBlockWidth;
2302 (void)gettimeofday(&tv, 0); 2489 (void)gettimeofday(&tv, 0);
2303 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; 2490 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
2304 2491
2305 if (fftSuspended) fft->resume(); 2492 return !morePaintingRequired;
2493
2494 // if (fftSuspended) fft->resume();
2306 } 2495 }
2307 2496
2308 void 2497 void
2309 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const 2498 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
2310 { 2499 {
2462 } 2651 }
2463 2652
2464 void 2653 void
2465 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e) 2654 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e)
2466 { 2655 {
2467 PixmapCache &cache = m_pixmapCaches[v]; 2656 //!!! check cache consistency
2468 2657 QMutexLocker locker(&m_pixmapCacheMutex);
2469 std::cerr << "cache width: " << cache.pixmap.width() << ", height: " 2658
2470 << cache.pixmap.height() << std::endl; 2659 PixmapCache *cache = m_pixmapCaches[v];
2471 2660 if (!cache) return;
2472 QImage image = cache.pixmap.toImage(); 2661
2662 std::cerr << "cache width: " << cache->pixmap.width() << ", height: "
2663 << cache->pixmap.height() << std::endl;
2664
2665 QImage image = cache->pixmap /*.toImage() */;
2473 2666
2474 ImageRegionFinder finder; 2667 ImageRegionFinder finder;
2475 QRect rect = finder.findRegionExtents(&image, e->pos()); 2668 QRect rect = finder.findRegionExtents(&image, e->pos());
2476 if (rect.isValid()) { 2669 if (rect.isValid()) {
2477 MeasureRect mr; 2670 MeasureRect mr;