Mercurial > hg > svgui
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; |