Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 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 |
comparison
equal
deleted
inserted
replaced
329:bbc9666cb961 | 330:846746e4e865 |
---|---|
115 if (m_paintThread) { | 115 if (m_paintThread) { |
116 m_paintThread->exiting(); | 116 m_paintThread->exiting(); |
117 m_paintThread->wait(); | 117 m_paintThread->wait(); |
118 delete m_paintThread; | 118 delete m_paintThread; |
119 } | 119 } |
120 | |
121 m_pixmapCacheMutex.lock(); | |
122 | |
123 while (!m_pixmapCaches.empty()) { | |
124 | |
125 PixmapCache *cache = m_pixmapCaches.begin()->second; | |
126 | |
127 cache->mutex.lock(); | |
128 delete cache->pixmap; | |
129 cache->pixmap = 0; | |
130 cache->mutex.unlock(); | |
131 | |
132 delete cache; | |
133 m_pixmapCaches.erase(m_pixmapCaches.begin()); | |
134 } | |
135 | |
136 m_pixmapCacheMutex.unlock(); | |
120 | 137 |
121 delete m_updateTimer; | 138 delete m_updateTimer; |
122 m_updateTimer = 0; | 139 m_updateTimer = 0; |
123 | 140 |
124 invalidateFFTModels(); | 141 invalidateFFTModels(); |
554 | 571 |
555 void | 572 void |
556 SpectrogramLayer::invalidatePixmapCaches() | 573 SpectrogramLayer::invalidatePixmapCaches() |
557 { | 574 { |
558 QMutexLocker locker(&m_pixmapCacheMutex); | 575 QMutexLocker locker(&m_pixmapCacheMutex); |
576 | |
559 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); | 577 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); |
560 i != m_pixmapCaches.end(); ++i) { | 578 i != m_pixmapCaches.end(); ++i) { |
579 | |
561 QMutexLocker locker(&i->second->mutex); | 580 QMutexLocker locker(&i->second->mutex); |
562 i->second->validArea = QRect(); | 581 i->second->validArea = QRect(); |
563 } | 582 } |
564 } | 583 } |
565 | 584 |
566 void | 585 void |
567 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame) | 586 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame) |
568 { | 587 { |
569 QMutexLocker locker(&m_pixmapCacheMutex); | 588 QMutexLocker locker(&m_pixmapCacheMutex); |
589 | |
570 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); | 590 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin(); |
571 i != m_pixmapCaches.end(); ++i) { | 591 i != m_pixmapCaches.end(); ++i) { |
572 | 592 |
573 QMutexLocker locker(&i->second->mutex); | 593 QMutexLocker locker(&i->second->mutex); |
574 | 594 |
922 Layer::setLayerDormant(v, true); | 942 Layer::setLayerDormant(v, true); |
923 | 943 |
924 invalidatePixmapCaches(); | 944 invalidatePixmapCaches(); |
925 | 945 |
926 m_pixmapCacheMutex.lock(); | 946 m_pixmapCacheMutex.lock(); |
947 | |
927 m_pixmapCaches[v]->mutex.lock(); | 948 m_pixmapCaches[v]->mutex.lock(); |
949 delete m_pixmapCaches[v]->pixmap; | |
950 m_pixmapCaches[v]->pixmap = 0; | |
928 m_pixmapCaches[v]->mutex.unlock(); | 951 m_pixmapCaches[v]->mutex.unlock(); |
929 // that only works if the paint thread never locks the | 952 |
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]; | 953 delete m_pixmapCaches[v]; |
934 m_pixmapCaches.erase(v); | 954 m_pixmapCaches.erase(v); |
955 | |
935 m_pixmapCacheMutex.unlock(); | 956 m_pixmapCacheMutex.unlock(); |
936 | 957 |
937 if (m_fftModels.find(v) != m_fftModels.end()) { | 958 if (m_fftModels.find(v) != m_fftModels.end()) { |
938 | 959 |
939 if (m_sliceableModel == m_fftModels[v].first) { | 960 if (m_sliceableModel == m_fftModels[v].first) { |
1680 if (mag == m_viewMags[v]) return false; | 1701 if (mag == m_viewMags[v]) return false; |
1681 m_viewMags[v] = mag; | 1702 m_viewMags[v] = mag; |
1682 return true; | 1703 return true; |
1683 } | 1704 } |
1684 | 1705 |
1706 | |
1707 | |
1685 // Plan: | 1708 // Plan: |
1686 // | 1709 // |
1687 // - the QImage cache is managed by the GUI thread (which creates, | 1710 // - the QImage cache is managed by the GUI thread (which creates, |
1688 // sizes and destroys it). | 1711 // sizes and destroys it). |
1689 // | 1712 // |
1692 // - the GUI thread paint method should always just draw straight from | 1715 // - 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 | 1716 // cache without any lock, but it needs to check whether the cache is |
1694 // complete and schedule another update if it isn't | 1717 // complete and schedule another update if it isn't |
1695 // | 1718 // |
1696 // - we need a mutex between cache construction/destruction activity | 1719 // - we need a mutex between cache construction/destruction activity |
1697 // and the paint thread | 1720 // and the paint thread (I wonder whether we need a general mutex |
1721 // between GUI thread changes to various parameters and paint thread | |
1722 // access to those parameters?) | |
1723 //!!! -> e.g. how is the access to FFTModels synchronised? | |
1724 //!!! -> and view magnitudes array? | |
1698 // | 1725 // |
1699 // - the paint thread needs to release the mutex occasionally so that | 1726 // - the paint thread needs to release the mutex occasionally so that |
1700 // cache positioning adjustments don't take forever. | 1727 // cache positioning adjustments don't take forever. |
1728 // | |
1729 // The cache covers a range from "somewhat before the start of the | |
1730 // view" to "somewhat after the end of it", and only (more or less) | |
1731 // the height of the view. (Or is it better for it to cover the full | |
1732 // height?) It is always at full resolution -- 1 pixel per bin in | |
1733 // both directions. When drawing from cache, the GUI thread does the | |
1734 // scaling. | |
1735 | |
1736 //!!! and split into parent class (called e.g. DenseDataLayer) and | |
1737 //subclass; parent has GUI thread, subclass has paint thread and most | |
1738 //of the code. | |
1739 | |
1701 | 1740 |
1702 void | 1741 void |
1703 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const | 1742 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const |
1704 { | 1743 { |
1705 // If the mutex for our cache is held, we should probably schedule | 1744 // If the mutex for our cache is held, we should probably schedule |
1709 // cache mutex only long enough to look up the cache object in the | 1748 // 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 | 1749 // map, and then release it, retaining the specific mutex while it |
1711 // paints. The GUI thread should then never modify the cache | 1750 // paints. The GUI thread should then never modify the cache |
1712 // image without holding its specific mutex.) | 1751 // image without holding its specific mutex.) |
1713 | 1752 |
1714 Profiler profiler("SpectrogramLayer::paint", true); | 1753 Profiler profiler("SpectrogramLayer::paint", false); |
1715 | 1754 |
1716 QMutexLocker locker(&m_pixmapCacheMutex); | 1755 QMutexLocker locker(&m_pixmapCacheMutex); |
1756 | |
1757 rect = v->rect(); //!!! | |
1717 | 1758 |
1718 PixmapCache *cache = m_pixmapCaches[v]; | 1759 PixmapCache *cache = m_pixmapCaches[v]; |
1719 if (!cache) { | 1760 if (!cache) { |
1720 cache = new PixmapCache; | 1761 cache = new PixmapCache; |
1721 cache->pixmap = QImage(v->width(), v->height(), | 1762 cache->pixmap = 0; |
1722 QImage::Format_RGB32); | 1763 m_pixmapCaches[v] = cache; |
1764 cache->mutex.lock(); | |
1765 cache->pixmap = new QImage(v->width(), v->height(), | |
1766 QImage::Format_RGB32); | |
1767 cache->mutex.unlock(); | |
1723 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1768 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1724 std::cerr << "SpectrogramLayer::paint: Created new cache, size " | 1769 std::cerr << "SpectrogramLayer::paint: Created new cache, size " |
1725 << v->width() << "x" << v->height() << " (" | 1770 << v->width() << "x" << v->height() << " (" |
1726 << v->width() * v->height() * 4 << " bytes)" << std::endl; | 1771 << v->width() * v->height() * 4 << " bytes)" << std::endl; |
1727 #endif | 1772 #endif |
1728 m_pixmapCaches[v] = cache; | |
1729 return; //!!! prod paint thread | 1773 return; //!!! prod paint thread |
1730 } | 1774 } |
1731 | 1775 |
1732 if (!cache->mutex.tryLock()) { | 1776 if (!cache->mutex.tryLock()) { |
1733 std::cerr << "lock unavailable" << std::endl; | 1777 std::cerr << "lock unavailable" << std::endl; |
1734 paint.drawImage(rect,// & cache->validArea, | 1778 paint.drawImage(rect,// & cache->validArea, |
1735 cache->pixmap, | 1779 *cache->pixmap, |
1736 rect);// & cache->validArea); | 1780 rect);// & cache->validArea); |
1737 // v->update(rect); | 1781 // v->update(rect); |
1738 return; | 1782 return; |
1739 } | 1783 } |
1740 | 1784 |
1741 if (cache->pixmap.width() != v->width() || | 1785 if (cache->pixmap->width() != v->width() || |
1742 cache->pixmap.height() != v->height()) { | 1786 cache->pixmap->height() != v->height()) { |
1743 cache->pixmap = cache->pixmap.scaled(v->width(), v->height()); | 1787 QImage *newImage = new QImage |
1788 (cache->pixmap->scaled(v->width(), v->height())); | |
1744 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1789 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1745 std::cerr << "SpectrogramLayer::paint: Rescaled cache to size " | 1790 std::cerr << "SpectrogramLayer::paint: Rescaling cache to size " |
1746 << v->width() << "x" << v->height() << " (" | 1791 << v->width() << "x" << v->height() << " (" |
1747 << v->width() * v->height() * 4 << " bytes)" << std::endl; | 1792 << v->width() * v->height() * 4 << " bytes)" << std::endl; |
1748 #endif | 1793 #endif |
1749 } | 1794 delete cache->pixmap; |
1795 cache->pixmap = newImage; | |
1796 } | |
1797 | |
1798 // The cache has the correct resolution for the view in the y | |
1799 // axis, but with a resolution of one pixel per increment in the x | |
1800 // axis. We may need to rescale when drawing. | |
1801 | |
1802 paint.setRenderHint(QPainter::SmoothPixmapTransform, true); | |
1803 | |
1804 //!!! not quite right in any case except integer ratio -- either | |
1805 //source or target rect will have edges at non-integral | |
1806 //coordinates | |
1807 | |
1808 float ratio = float(v->getZoomLevel()) / float(getWindowIncrement()); | |
1809 | |
1810 long myStartFrame = v->getStartFrame(); | |
1811 long cacheStartFrame = cache->startFrame; | |
1812 long xoff = (myStartFrame - cacheStartFrame) / getWindowIncrement(); | |
1813 | |
1814 std::cerr << "my start frame = " << myStartFrame << ", cache start frame = " << cacheStartFrame << std::endl; | |
1815 | |
1816 QRect sourceRect = QRect(lrintf(rect.x() * ratio) + xoff, | |
1817 rect.y(), | |
1818 lrintf(rect.width() * ratio), | |
1819 rect.height()); | |
1820 | |
1821 std::cerr << "ratio = " << ratio << " (zoom " << v->getZoomLevel() | |
1822 << ", incr " << getWindowIncrement() << "); source rect " | |
1823 << sourceRect.x() << "," << sourceRect.y() | |
1824 << " " << sourceRect.width() << "x" << sourceRect.height() | |
1825 << ", target rect " | |
1826 << rect.x() << "," << rect.y() | |
1827 << " " << rect.width() << "x" << rect.height() | |
1828 << " (image " << cache->pixmap->width() << "x" | |
1829 << cache->pixmap->height() << ")" | |
1830 << std::endl; | |
1750 | 1831 |
1751 paint.drawImage(rect,// & cache->validArea, | 1832 paint.drawImage(rect,// & cache->validArea, |
1752 cache->pixmap, | 1833 *cache->pixmap, |
1753 rect);// & cache->validArea); | 1834 sourceRect);// & cache->validArea); |
1754 | 1835 |
1755 if (cache->startFrame != v->getStartFrame()) { | 1836 if (cache->startFrame != v->getStartFrame()) { |
1756 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1837 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1757 std::cerr << "SpectrogramLayer::paint(): Cache start frame " | 1838 std::cerr << "SpectrogramLayer::paint(): Cache start frame " |
1758 << cache->startFrame << " differs from view start frame " | 1839 << cache->startFrame << " differs from view start frame " |
1789 for (ViewPixmapCache::iterator i = m_layer->m_pixmapCaches.begin(); | 1870 for (ViewPixmapCache::iterator i = m_layer->m_pixmapCaches.begin(); |
1790 i != m_layer->m_pixmapCaches.end(); ++i) { | 1871 i != m_layer->m_pixmapCaches.end(); ++i) { |
1791 views.insert(const_cast<View *>(i->first)); //!!! | 1872 views.insert(const_cast<View *>(i->first)); //!!! |
1792 } | 1873 } |
1793 m_layer->m_pixmapCacheMutex.unlock(); | 1874 m_layer->m_pixmapCacheMutex.unlock(); |
1875 | |
1876 bool workToDo = false; | |
1794 | 1877 |
1795 for (std::set<View *>::iterator vi = views.begin(); | 1878 for (std::set<View *>::iterator vi = views.begin(); |
1796 vi != views.end(); ++vi) { | 1879 vi != views.end(); ++vi) { |
1797 | 1880 |
1798 m_layer->paintCache(*vi); | 1881 if (m_layer->paintCache(*vi)) { |
1882 if (!m_exiting) { | |
1883 std::cerr << "Cache complete: asking view to update" | |
1884 << std::endl; | |
1885 (*vi)->update(); | |
1886 } | |
1887 } else { | |
1888 workToDo = true; | |
1889 } | |
1890 | |
1799 if (m_exiting) break; | 1891 if (m_exiting) break; |
1800 } | 1892 } |
1801 | 1893 |
1802 if (!m_exiting) sleep(1); | 1894 if (!m_exiting) { |
1895 //!!! wait on condition | |
1896 if (workToDo) usleep(100); | |
1897 else sleep(1); | |
1898 } | |
1803 } | 1899 } |
1804 } | 1900 } |
1805 | 1901 |
1806 // return true if no more work to do, or can do no more | 1902 // return true if no more work to do, or can do no more |
1807 | 1903 |
1808 bool | 1904 bool |
1809 SpectrogramLayer::paintCache(View *v) const | 1905 SpectrogramLayer::paintCache(View *v) const |
1810 { | 1906 { |
1811 Profiler profiler("SpectrogramLayer::paintCache", true); | 1907 Profiler profiler("SpectrogramLayer::paintCache", false); |
1812 | 1908 |
1813 m_pixmapCacheMutex.lock(); | 1909 m_pixmapCacheMutex.lock(); |
1814 | 1910 |
1815 PixmapCache *cacheptr = m_pixmapCaches[v]; | 1911 PixmapCache *cacheptr = m_pixmapCaches[v]; |
1816 if (!cacheptr || cacheptr->pixmap.width() == 0) { | 1912 if (!cacheptr || !cacheptr->pixmap || cacheptr->pixmap->width() == 0) { |
1817 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1913 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1818 std::cerr << "SpectrogramLayer::paintCache(): No cache to paint onto" | 1914 std::cerr << "SpectrogramLayer::paintCache(): No cache to paint onto" |
1819 << std::endl; | 1915 << std::endl; |
1820 #endif | 1916 #endif |
1821 m_pixmapCacheMutex.unlock(); | 1917 m_pixmapCacheMutex.unlock(); |
1824 | 1920 |
1825 PixmapCache &cache = *cacheptr; | 1921 PixmapCache &cache = *cacheptr; |
1826 QMutexLocker locker(&cache.mutex); | 1922 QMutexLocker locker(&cache.mutex); |
1827 m_pixmapCacheMutex.unlock(); | 1923 m_pixmapCacheMutex.unlock(); |
1828 | 1924 |
1829 // establish rect to paint -- we want to paint the whole area that | 1925 QImage &image = *cache.pixmap; //!!! rename to cache.image |
1830 // is not known to be valid | 1926 |
1831 | 1927 const DenseTimeValueModel *model = m_model; |
1832 QRect rect = QRect(0, 0, cache.pixmap.width(), cache.pixmap.height()); | 1928 if (!model || !model->isOK() || !model->isReady()) { |
1833 | 1929 return false; |
1834 QPainter paint(&cache.pixmap); | 1930 } |
1931 | |
1932 //!!! mutex here -- we have the specific pixmap cache mutex held, | |
1933 //but only that one currently and there's no mutex for this stuff | |
1934 //in the other thread | |
1935 | |
1936 //!!! what a big pile of crap | |
1937 long startFrame = v->getStartFrame(); | |
1938 int increment = getWindowIncrement(); // signed for arithmetic purposes | |
1939 size_t fftNominal = m_fftSize; // the user-specified fft size | |
1940 size_t fftSize = getFFTSize(v); // the actual fft size (maybe padded) | |
1941 FFTModel *fft = getFFTModel(v); | |
1942 int sr = model->getSampleRate(); | |
1943 size_t fmin = m_minFrequency; | |
1944 size_t fmax = m_maxFrequency; | |
1945 FrequencyScale frequencyScale = m_frequencyScale; | |
1946 BinDisplay binDisplay = m_binDisplay; | |
1947 float threshold = m_threshold; | |
1948 bool normalizeColumns = m_normalizeColumns; | |
1949 bool normalizeVisibleArea = m_normalizeVisibleArea; | |
1950 ColourScale colourScale = m_colourScale; | |
1951 float gain = m_gain; | |
1952 const Palette &palette = m_palette; | |
1953 int zpl = getZeroPadLevel(v) + 1; | |
1954 | |
1955 if (!fft) { | |
1956 std::cerr << "ERROR: SpectrogramLayer::paintCache(): No FFT model, returning" << std::endl; | |
1957 return true; | |
1958 } | |
1959 | |
1960 | |
1961 std::cerr << "SpectrogramLayer::paintCache: start frame " | |
1962 << startFrame << ", increment " << increment << std::endl; | |
1963 | |
1964 | |
1965 int w = image.width(); | |
1966 int h = image.height(); | |
1967 | |
1968 | |
1969 // Establish rect to paint. We want to paint a not-too-large area | |
1970 // that is not known to be valid, adjacent to an area that is | |
1971 // known to be valid, such that the resulting known-valid area is | |
1972 // rectangular still. This doesn't really do that. | |
1973 | |
1974 QRect rect = QRect(0, 0, w, h); | |
1975 | |
1976 QPainter paint(cache.pixmap); | |
1835 | 1977 |
1836 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1978 #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; | 1979 std::cerr << "SpectrogramLayer::paintCache(): m_model is " << model << ", window increment " << increment << std::endl; |
1838 | 1980 |
1839 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl; | 1981 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl; |
1840 #endif | 1982 #endif |
1841 | 1983 |
1842 long startFrame = v->getStartFrame(); | |
1843 if (startFrame < 0) m_candidateFillStartFrame = 0; | 1984 if (startFrame < 0) m_candidateFillStartFrame = 0; |
1844 else m_candidateFillStartFrame = startFrame; | 1985 else m_candidateFillStartFrame = startFrame; |
1845 | 1986 |
1846 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | |
1847 return false; | |
1848 } | |
1849 /* | 1987 /* |
1850 if (isLayerDormant(v)) { | 1988 if (isLayerDormant(v)) { |
1851 std::cerr << "SpectrogramLayer::paintCache(): Layer is dormant, making it undormant again" << std::endl; | 1989 std::cerr << "SpectrogramLayer::paintCache(): Layer is dormant, making it undormant again" << std::endl; |
1852 } | 1990 } |
1853 | 1991 |
1856 // and accountable for when determining whether we need the cache | 1994 // and accountable for when determining whether we need the cache |
1857 // in the cache-fill thread above. | 1995 // in the cache-fill thread above. |
1858 //!!! no longer use cache-fill thread | 1996 //!!! no longer use cache-fill thread |
1859 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); | 1997 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); |
1860 */ | 1998 */ |
1861 size_t fftSize = getFFTSize(v); | |
1862 FFTModel *fft = getFFTModel(v); | |
1863 if (!fft) { | |
1864 std::cerr << "ERROR: SpectrogramLayer::paintCache(): No FFT model, returning" << std::endl; | |
1865 return true; | |
1866 } | |
1867 | 1999 |
1868 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2000 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1869 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache size " << cache.pixmap.width() << "x" << cache.pixmap.height() << std::endl; | 2001 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache size " << w << "x" << h << std::endl; |
1870 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl; | 2002 std::cerr << "SpectrogramLayer::paintCache(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl; |
1871 #endif | 2003 #endif |
1872 | 2004 |
1873 int zoomLevel = v->getZoomLevel(); | 2005 bool recreateWholeCache = true; |
1874 | 2006 |
1875 int x0 = 0; | 2007 int x0 = rect.left(); |
1876 int x1 = v->width(); | 2008 int x1 = rect.right() + 1; |
1877 | |
1878 bool recreateWholePixmapCache = true; | |
1879 | |
1880 x0 = rect.left(); | |
1881 x1 = rect.right() + 1; | |
1882 | 2009 |
1883 if (cache.validArea.width() > 0) { | 2010 if (cache.validArea.width() > 0) { |
1884 | 2011 |
1885 if (int(cache.zoomLevel) == zoomLevel && | 2012 //!!! no, the gui thread will handle resizes -- we just draw |
1886 cache.pixmap.width() == v->width() && | 2013 //the area that's available in the cache. If the image is |
1887 cache.pixmap.height() == v->height()) { | 2014 //enlarged, our valid area should remain the same and we'll |
1888 | 2015 //pick up on the discrepancy naturally (we hope) |
1889 if (v->getXForFrame(cache.startFrame) == | 2016 // if (cache.pixmap->width() == v->width() && |
1890 v->getXForFrame(startFrame) && | 2017 // cache.pixmap->height() == v->height()) { |
2018 | |
2019 if (cache.startFrame / increment == startFrame / increment && | |
1891 cache.validArea.x() <= x0 && | 2020 cache.validArea.x() <= x0 && |
1892 cache.validArea.x() + cache.validArea.width() >= x1) { | 2021 cache.validArea.x() + cache.validArea.width() >= x1) { |
1893 | 2022 |
1894 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2023 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1895 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl; | 2024 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl; |
1903 | 2032 |
1904 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2033 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1905 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl; | 2034 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl; |
1906 #endif | 2035 #endif |
1907 | 2036 |
1908 recreateWholePixmapCache = false; | 2037 recreateWholeCache = false; |
1909 | 2038 |
1910 int dx = v->getXForFrame(cache.startFrame) - | 2039 int dx = cache.startFrame / increment - startFrame / increment; |
1911 v->getXForFrame(startFrame); | |
1912 | 2040 |
1913 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2041 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1914 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl; | 2042 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap->width() << "x" << cache.pixmap->height() << ")" << std::endl; |
1915 #endif | 2043 #endif |
1916 | 2044 |
1917 if (dx != 0 && | 2045 if (dx != 0 && dx > -w && dx < w) { |
1918 dx > -cache.pixmap.width() && | |
1919 dx < cache.pixmap.width()) { | |
1920 | 2046 |
1921 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC) | 2047 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC) |
1922 // Copying a pixmap to itself doesn't work | 2048 // Copying a pixmap to itself doesn't work |
1923 // properly on Windows or Mac (it only works when | 2049 // properly on Windows or Mac (it only works when |
1924 // moving in one direction). | 2050 // moving in one direction). |
1925 | 2051 |
1926 //!!! Need a utility function for this | 2052 //!!! Need a utility function for this |
1927 | 2053 |
2054 //!!! test again on Win and OS/X for images (above | |
2055 //comment applies to pixmaps) | |
2056 | |
2057 //!!! indeed, test again on linux for images too | |
2058 //-- I bet it doesn't work | |
2059 | |
1928 static QImage *tmpPixmap = 0; | 2060 static QImage *tmpPixmap = 0; |
1929 if (!tmpPixmap || | 2061 if (!tmpPixmap || |
1930 tmpPixmap->width() != cache.pixmap.width() || | 2062 tmpPixmap->width() != cache.pixmap->width() || |
1931 tmpPixmap->height() != cache.pixmap.height()) { | 2063 tmpPixmap->height() != cache.pixmap->height()) { |
1932 delete tmpPixmap; | 2064 delete tmpPixmap; |
1933 tmpPixmap = new QImage(cache.pixmap.width(), | 2065 tmpPixmap = new QImage(cache.pixmap->width(), |
1934 cache.pixmap.height(), | 2066 cache.pixmap->height(), |
1935 QImage::Format_RGB32); | 2067 QImage::Format_RGB32); |
1936 } | 2068 } |
1937 QPainter tmpPainter; | 2069 QPainter tmpPainter; |
1938 tmpPainter.begin(tmpPixmap); | 2070 tmpPainter.begin(tmpPixmap); |
1939 tmpPainter.drawImage(0, 0, cache.pixmap); | 2071 tmpPainter.drawImage(0, 0, image); |
1940 tmpPainter.end(); | 2072 tmpPainter.end(); |
1941 paint.drawImage(dx, 0, *tmpPixmap); | 2073 paint.drawImage(dx, 0, *tmpPixmap); |
1942 #else | 2074 #else |
1943 paint.drawImage(dx, 0, cache.pixmap); | 2075 paint.drawImage(dx, 0, image); //!!! see above |
1944 #endif | 2076 #endif |
1945 | 2077 |
1946 int px = cache.validArea.x(); | 2078 int px = cache.validArea.x(); |
1947 int pw = cache.validArea.width(); | 2079 int pw = cache.validArea.width(); |
1948 | 2080 |
1949 if (dx < 0) { | 2081 if (dx < 0) { |
1950 x0 = cache.pixmap.width() + dx; | 2082 x0 = w + dx; |
1951 x1 = cache.pixmap.width(); | 2083 x1 = w; |
1952 px += dx; | 2084 px += dx; |
1953 if (px < 0) { | 2085 if (px < 0) { |
1954 pw += px; | 2086 pw += px; |
1955 px = 0; | 2087 px = 0; |
1956 if (pw < 0) pw = 0; | 2088 if (pw < 0) pw = 0; |
1957 } | 2089 } |
1958 } else { | 2090 } else { |
1959 x0 = 0; | 2091 x0 = 0; |
1960 x1 = dx; | 2092 x1 = dx; |
1961 px += dx; | 2093 px += dx; |
1962 if (px + pw > cache.pixmap.width()) { | 2094 if (px + pw > w) { |
1963 pw = int(cache.pixmap.width()) - px; | 2095 pw = int(w) - px; |
1964 if (pw < 0) pw = 0; | 2096 if (pw < 0) pw = 0; |
1965 } | 2097 } |
1966 } | 2098 } |
1967 | 2099 |
1968 cache.validArea = | 2100 cache.validArea = |
1969 QRect(px, cache.validArea.y(), | 2101 QRect(px, cache.validArea.y(), |
1970 pw, cache.validArea.height()); | 2102 pw, cache.validArea.height()); |
1971 } | 2103 } |
1972 } | 2104 } |
1973 } else { | 2105 // } else { |
1974 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2106 //#ifdef DEBUG_SPECTROGRAM_REPAINT |
1975 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl; | 2107 // std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl; |
1976 if (int(cache.zoomLevel) != zoomLevel) { | 2108 // if (cache.pixmap->width() != v->width()) { |
1977 std::cerr << "(cache zoomLevel " << cache.zoomLevel | 2109 // std::cerr << "(cache width " << cache.pixmap->width() |
1978 << " != " << zoomLevel << ")" << std::endl; | 2110 // << " != " << v->width(); |
1979 } | 2111 // } |
1980 if (cache.pixmap.width() != v->width()) { | 2112 // if (cache.pixmap->height() != v->height()) { |
1981 std::cerr << "(cache width " << cache.pixmap.width() | 2113 // std::cerr << "(cache height " << cache.pixmap->height() |
1982 << " != " << v->width(); | 2114 // << " != " << v->height(); |
1983 } | 2115 // } |
1984 if (cache.pixmap.height() != v->height()) { | 2116 //#endif |
1985 std::cerr << "(cache height " << cache.pixmap.height() | 2117 // cache.validArea = QRect(); |
1986 << " != " << v->height(); | 2118 // } |
1987 } | 2119 } |
1988 #endif | 2120 |
1989 cache.validArea = QRect(); | |
1990 } | |
1991 } | |
1992 /*!!! | 2121 /*!!! |
1993 if (updateViewMagnitudes(v)) { | 2122 if (updateViewMagnitudes(v)) { |
1994 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2123 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1995 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; | 2124 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; |
1996 #endif | 2125 #endif |
1997 recreateWholePixmapCache = true; | 2126 recreateWholeCache = true; |
1998 } else { | 2127 } else { |
1999 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2128 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2000 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; | 2129 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl; |
2001 #endif | 2130 #endif |
2002 } | 2131 } |
2003 */ | 2132 */ |
2004 if (recreateWholePixmapCache) { | 2133 if (recreateWholeCache) { |
2005 x0 = 0; | 2134 x0 = 0; |
2006 x1 = rect.width(); | 2135 x1 = w; |
2007 } | 2136 } |
2008 | 2137 |
2009 struct timeval tv; | 2138 struct timeval tv; |
2010 (void)gettimeofday(&tv, 0); | 2139 (void)gettimeofday(&tv, 0); |
2011 RealTime mainPaintStart = RealTime::fromTimeval(tv); | 2140 RealTime mainPaintStart = RealTime::fromTimeval(tv); |
2012 | 2141 |
2013 int paintBlockWidth = m_lastPaintBlockWidth; | 2142 int paintBlockWidth = m_lastPaintBlockWidth; |
2014 | 2143 |
2015 if (paintBlockWidth == 0) { | 2144 if (paintBlockWidth == 0) { |
2016 paintBlockWidth = (300000 / zoomLevel); | 2145 paintBlockWidth = (300000 / increment); |
2017 } else { | 2146 } else { |
2018 RealTime lastTime = m_lastPaintTime; | 2147 RealTime lastTime = m_lastPaintTime; |
2019 while (lastTime > RealTime::fromMilliseconds(200) && | 2148 while (lastTime > RealTime::fromMilliseconds(200) && |
2020 paintBlockWidth > 50) { | 2149 paintBlockWidth > 50) { |
2021 paintBlockWidth /= 2; | 2150 paintBlockWidth /= 2; |
2038 // Smaller heights can be used when painting from cache, but we | 2167 // Smaller heights can be used when painting from cache, but we |
2039 // want to ensure the cache is coherent without having to worry | 2168 // want to ensure the cache is coherent without having to worry |
2040 // about vertical matching of required and valid areas as well as | 2169 // about vertical matching of required and valid areas as well as |
2041 // horizontal. | 2170 // horizontal. |
2042 | 2171 |
2043 int h = v->height(); | 2172 // int h = v->height(); |
2044 int w = x1 - x0; | 2173 // int w = x1 - x0; |
2045 | 2174 |
2046 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2175 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2047 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; | 2176 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; |
2048 #endif | 2177 #endif |
2049 | 2178 |
2051 // m_drawBuffer = QImage(w, h, QImage::Format_RGB32); | 2180 // m_drawBuffer = QImage(w, h, QImage::Format_RGB32); |
2052 // } | 2181 // } |
2053 | 2182 |
2054 // m_drawBuffer.fill(m_palette.getColour(0).rgb()); | 2183 // m_drawBuffer.fill(m_palette.getColour(0).rgb()); |
2055 | 2184 |
2056 int sr = m_model->getSampleRate(); | |
2057 | |
2058 // Set minFreq and maxFreq to the frequency extents of the possibly | 2185 // Set minFreq and maxFreq to the frequency extents of the possibly |
2059 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq | 2186 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq |
2060 // to the actual scale frequency extents (presumably not zero padded). | 2187 // to the actual scale frequency extents (presumably not zero padded). |
2061 | 2188 |
2062 // If we are zero padding, we want to use the zero-padded | 2189 // 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 | 2190 // 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. | 2191 // padded, to avoid spaces at the top and bottom of the display. |
2065 | 2192 |
2066 // Note fftSize is the actual zero-padded fft size, m_fftSize the | 2193 //!!! --> we weren't actually using minFreq or maxFreq -- so just |
2194 // set our new fmin/fmax to the display extents | |
2195 | |
2196 // Note fftSize is the actual zero-padded fft size, fftNominal the | |
2067 // nominal fft size. | 2197 // nominal fft size. |
2068 | 2198 |
2069 size_t maxbin = m_fftSize / 2; | 2199 size_t maxbin = fftNominal / 2; |
2070 if (m_maxFrequency > 0) { | 2200 if (fmax > 0) { |
2071 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); | 2201 maxbin = int((double(fmax) * fftNominal) / sr + 0.001); |
2072 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; | 2202 if (maxbin > fftNominal / 2) maxbin = fftNominal / 2; |
2073 } | 2203 } |
2074 | 2204 |
2075 size_t minbin = 1; | 2205 size_t minbin = 1; |
2076 if (m_minFrequency > 0) { | 2206 if (fmin > 0) { |
2077 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001); | 2207 minbin = int((double(fmin) * fftNominal) / sr + 0.001); |
2078 // std::cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << std::endl; | |
2079 if (minbin < 1) minbin = 1; | 2208 if (minbin < 1) minbin = 1; |
2080 if (minbin >= maxbin) minbin = maxbin - 1; | 2209 if (minbin >= maxbin) minbin = maxbin - 1; |
2081 } | 2210 } |
2082 | 2211 |
2083 int zpl = getZeroPadLevel(v) + 1; | |
2084 minbin = minbin * zpl; | 2212 minbin = minbin * zpl; |
2085 maxbin = (maxbin + 1) * zpl - 1; | 2213 maxbin = (maxbin + 1) * zpl - 1; |
2086 | 2214 |
2087 float minFreq = (float(minbin) * sr) / fftSize; | 2215 fmin = (float(minbin) * sr) / fftNominal; |
2088 float maxFreq = (float(maxbin) * sr) / fftSize; | 2216 fmax = (float(maxbin) * sr) / fftNominal; |
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 | 2217 |
2100 float ymag[h]; | 2218 float ymag[h]; |
2101 float ydiv[h]; | 2219 float ydiv[h]; |
2102 float yval[maxbin + 1]; //!!! cache this? | 2220 float yval[maxbin + 1]; //!!! cache this? |
2103 | 2221 |
2104 size_t increment = getWindowIncrement(); | 2222 bool logarithmic = (frequencyScale == LogFrequencyScale); |
2105 | |
2106 bool logarithmic = (m_frequencyScale == LogFrequencyScale); | |
2107 | 2223 |
2108 for (size_t q = minbin; q <= maxbin; ++q) { | 2224 for (size_t q = minbin; q <= maxbin; ++q) { |
2109 float f0 = (float(q) * sr) / fftSize; | 2225 float f0 = (float(q) * sr) / fftSize; |
2110 yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, | 2226 yval[q] = v->getYForFrequency(f0, fmin, fmax, logarithmic); |
2111 logarithmic); | |
2112 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl; | 2227 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl; |
2113 } | 2228 } |
2114 | 2229 |
2115 MagnitudeRange overallMag = m_viewMags[v]; | 2230 MagnitudeRange overallMag = m_viewMags[v]; |
2116 // bool overallMagChanged = false; | 2231 // bool overallMagChanged = false; |
2120 bool interpolate = false; | 2235 bool interpolate = false; |
2121 Preferences::SpectrogramSmoothing smoothing = | 2236 Preferences::SpectrogramSmoothing smoothing = |
2122 Preferences::getInstance()->getSpectrogramSmoothing(); | 2237 Preferences::getInstance()->getSpectrogramSmoothing(); |
2123 if (smoothing == Preferences::SpectrogramInterpolated || | 2238 if (smoothing == Preferences::SpectrogramInterpolated || |
2124 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { | 2239 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) { |
2125 if (m_binDisplay != PeakBins && | 2240 if (binDisplay != PeakBins && |
2126 m_binDisplay != PeakFrequencies) { | 2241 binDisplay != PeakFrequencies) { |
2127 interpolate = true; | 2242 interpolate = true; |
2128 } | 2243 } |
2129 } | 2244 } |
2130 | 2245 |
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; | 2246 bool runOutOfData = false; |
2136 | 2247 |
2137 for (int x = 0; x < w; ++x) { | 2248 for (int x = 0; x < (x1 - x0); ++x) { |
2138 | 2249 |
2139 if (runOutOfData) break; | 2250 if (runOutOfData) break; |
2140 | 2251 |
2141 for (int y = 0; y < h; ++y) { | 2252 for (int y = 0; y < h; ++y) { |
2142 ymag[y] = 0.f; | 2253 ymag[y] = 0.f; |
2143 ydiv[y] = 0.f; | 2254 ydiv[y] = 0.f; |
2144 } | 2255 } |
2145 | 2256 |
2146 float s0 = 0, s1 = 0; | 2257 int col = (startFrame / increment) + x0 + x; |
2147 | 2258 |
2148 if (!getXBinRange(v, x0 + x, s0, s1)) { | 2259 // std::cerr << "x = " << x << ", col = " << col << std::endl; |
2149 // assert(x <= m_drawBuffer.width()); | 2260 |
2150 continue; | 2261 if (col >= int(fft->getWidth())) { |
2262 continue; | |
2151 } | 2263 } |
2152 | 2264 |
2153 int s0i = int(s0 + 0.001); | 2265 if (!fft->isColumnAvailable(col)) { |
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 | 2266 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2168 std::cerr << "Met unavailable column at col " << s << std::endl; | 2267 std::cerr << "Met unavailable column at col " << col << std::endl; |
2169 #endif | 2268 #endif |
2170 // continue; | 2269 runOutOfData = true; |
2171 runOutOfData = true; | 2270 break; |
2172 break; | 2271 } |
2173 } | |
2174 | 2272 |
2175 /* | 2273 /* |
2176 if (!fftSuspended) { | 2274 if (!fftSuspended) { |
2177 fft->suspendWrites(); | 2275 fft->suspendWrites(); |
2178 fftSuspended = true; | 2276 fftSuspended = true; |
2179 } | 2277 } |
2180 */ | 2278 */ |
2181 MagnitudeRange mag; | 2279 |
2182 | 2280 MagnitudeRange mag; |
2183 FFTModel::PeakSet peaks; | 2281 |
2184 if (m_binDisplay == PeakFrequencies && | 2282 FFTModel::PeakSet peaks; |
2185 s < int(fft->getWidth()) - 1) { | 2283 if (binDisplay == PeakFrequencies && |
2186 peaks = fft->getPeakFrequencies(FFTModel::AllPeaks, | 2284 col < int(fft->getWidth()) - 1) { |
2187 s, | 2285 peaks = fft->getPeakFrequencies(FFTModel::AllPeaks, |
2188 minbin, maxbin - 1); | 2286 col, |
2287 minbin, maxbin - 1); | |
2288 } | |
2289 | |
2290 for (size_t q = minbin; q < maxbin; ++q) { | |
2291 | |
2292 float y0 = yval[q + 1]; | |
2293 float y1 = yval[q]; | |
2294 | |
2295 if (binDisplay == PeakBins) { | |
2296 if (!fft->isLocalPeak(col, q)) continue; | |
2189 } | 2297 } |
2190 | 2298 if (binDisplay == PeakFrequencies) { |
2191 for (size_t q = minbin; q < maxbin; ++q) { | 2299 if (peaks.find(q) == peaks.end()) continue; |
2192 | 2300 } |
2193 float y0 = yval[q + 1]; | 2301 |
2194 float y1 = yval[q]; | 2302 if (threshold != 0.f && |
2195 | 2303 !fft->isOverThreshold(col, q, threshold * (fftNominal/2))) { |
2196 if (m_binDisplay == PeakBins) { | 2304 continue; |
2197 if (!fft->isLocalPeak(s, q)) continue; | 2305 } |
2198 } | 2306 |
2199 if (m_binDisplay == PeakFrequencies) { | 2307 if (binDisplay == PeakFrequencies) { |
2200 if (peaks.find(q) == peaks.end()) continue; | 2308 y0 = y1 = v->getYForFrequency |
2309 (peaks[q], fmin, fmax, logarithmic); | |
2310 } | |
2311 | |
2312 int y0i = int(y0 + 0.001); | |
2313 int y1i = int(y1); | |
2314 | |
2315 float value; | |
2316 | |
2317 if (colourScale == PhaseColourScale) { | |
2318 value = fft->getPhaseAt(col, q); | |
2319 } else if (normalizeColumns) { | |
2320 value = fft->getNormalizedMagnitudeAt(col, q); | |
2321 mag.sample(value); | |
2322 value *= gain; | |
2323 } else { | |
2324 value = fft->getMagnitudeAt(col, q) / (fftNominal/2); | |
2325 mag.sample(value); | |
2326 value *= gain; | |
2327 } | |
2328 | |
2329 if (interpolate) { | |
2330 | |
2331 int ypi = y0i; | |
2332 if (q < maxbin - 1) ypi = int(yval[q + 2]); | |
2333 | |
2334 for (int y = ypi; y <= y1i; ++y) { | |
2335 | |
2336 if (y < 0 || y >= h) continue; | |
2337 | |
2338 float yprop = 1.f; | |
2339 float iprop = yprop; | |
2340 | |
2341 if (ypi < y0i && y <= y0i) { | |
2342 | |
2343 float half = float(y0i - ypi) / 2; | |
2344 float dist = y - (ypi + half); | |
2345 | |
2346 if (dist >= 0) { | |
2347 iprop = (iprop * dist) / half; | |
2348 ymag[y] += iprop * value; | |
2349 } | |
2350 } else { | |
2351 if (y1i > y0i) { | |
2352 | |
2353 float half = float(y1i - y0i) / 2; | |
2354 float dist = y - (y0i + half); | |
2355 | |
2356 if (dist >= 0) { | |
2357 iprop = (iprop * (half - dist)) / half; | |
2358 } | |
2359 } | |
2360 | |
2361 ymag[y] += iprop * value; | |
2362 ydiv[y] += yprop; | |
2363 } | |
2201 } | 2364 } |
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 | 2365 |
2222 if (m_colourScale == PhaseColourScale) { | 2366 } else { |
2223 value = fft->getPhaseAt(s, q); | 2367 |
2224 } else if (m_normalizeColumns) { | 2368 for (int y = y0i; y <= y1i; ++y) { |
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 | 2369 |
2236 int ypi = y0i; | 2370 if (y < 0 || y >= h) continue; |
2237 if (q < maxbin - 1) ypi = int(yval[q + 2]); | |
2238 | |
2239 for (int y = ypi; y <= y1i; ++y) { | |
2240 | 2371 |
2372 float yprop = 1.f; | |
2373 if (y == y0i) yprop *= (y + 1) - y0; | |
2374 if (y == y1i) yprop *= y1 - y; | |
2375 | |
2376 for (int y = y0i; y <= y1i; ++y) { | |
2377 | |
2241 if (y < 0 || y >= h) continue; | 2378 if (y < 0 || y >= h) continue; |
2242 | 2379 |
2243 float yprop = sprop; | 2380 float yprop = 1.f; |
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; | 2381 if (y == y0i) yprop *= (y + 1) - y0; |
2279 if (y == y1i) yprop *= y1 - y; | 2382 if (y == y1i) yprop *= y1 - y; |
2280 | 2383 ymag[y] += yprop * value; |
2281 for (int y = y0i; y <= y1i; ++y) { | 2384 ydiv[y] += yprop; |
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 } | 2385 } |
2292 } | 2386 } |
2293 } | 2387 } |
2388 } | |
2294 /* | 2389 /* |
2295 if (mag.isSet()) { | 2390 if (mag.isSet()) { |
2296 | 2391 |
2297 if (s >= int(m_columnMags.size())) { | 2392 if (s >= int(m_columnMags.size())) { |
2298 std::cerr << "INTERNAL ERROR: " << s << " >= " | 2393 std::cerr << "INTERNAL ERROR: " << s << " >= " |
2308 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl; | 2403 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl; |
2309 #endif | 2404 #endif |
2310 } | 2405 } |
2311 } | 2406 } |
2312 */ | 2407 */ |
2313 } | |
2314 | 2408 |
2315 for (int y = 0; y < h; ++y) { | 2409 for (int y = 0; y < h; ++y) { |
2316 | 2410 |
2317 if (ydiv[y] > 0.0) { | 2411 if (ydiv[y] > 0.0) { |
2318 | 2412 |
2319 unsigned char pixel = 0; | 2413 unsigned char pixel = 0; |
2320 | 2414 |
2321 float avg = ymag[y] / ydiv[y]; | 2415 float avg = ymag[y] / ydiv[y]; |
2322 pixel = getDisplayValue(v, avg); | 2416 pixel = getDisplayValue(v, avg); //!!!??? |
2323 | 2417 |
2324 // assert(x <= m_drawBuffer.width()); | 2418 // assert(x <= m_drawBuffer.width()); |
2325 assert(x <= cache.pixmap.width()); | 2419 assert(x <= w); |
2326 QColor c = m_palette.getColour(pixel); | 2420 QColor c = palette.getColour(pixel); |
2327 | 2421 |
2328 if (x0 + x >= cache.pixmap.width()) { | 2422 if (x0 + x >= w) { |
2329 std::cerr << "INTERNAL ERROR: " << x0 << "+" | 2423 std::cerr << "INTERNAL ERROR: " << x0 << "+" |
2330 << x << " >= " << cache.pixmap.width() | 2424 << x << " >= " << w |
2331 << " at SpectrogramLayer.cpp:" | 2425 << " at SpectrogramLayer.cpp:" |
2332 << __LINE__ << std::endl; | 2426 << __LINE__ << std::endl; |
2333 continue; | 2427 continue; |
2334 } | 2428 } |
2335 | 2429 |
2336 if (y >= cache.pixmap.height()) { | 2430 if (y >= h) { |
2337 std::cerr << "INTERNAL ERROR: " << y | 2431 std::cerr << "INTERNAL ERROR: " << y |
2338 << " >= " << cache.pixmap.height() | 2432 << " >= " << h |
2339 << " at SpectrogramLayer.cpp:" | 2433 << " at SpectrogramLayer.cpp:" |
2340 << __LINE__ << std::endl; | 2434 << __LINE__ << std::endl; |
2341 continue; | 2435 continue; |
2342 } | 2436 } |
2343 | 2437 |
2344 assert(x0 + x < cache.pixmap.width()); | 2438 assert(x0 + x < w); |
2345 assert(y < cache.pixmap.height()); | 2439 assert(y < h); |
2346 | 2440 |
2347 cache.pixmap.setPixel(x0 + x, y, | 2441 image.setPixel(x0 + x, y, |
2348 qRgb(c.red(), c.green(), c.blue())); | 2442 qRgb(c.red(), c.green(), c.blue())); |
2349 } | 2443 } |
2350 } | 2444 } |
2351 } | 2445 } |
2352 /* | 2446 /* |
2353 if (overallMagChanged) { | 2447 if (overallMagChanged) { |
2397 cache.validArea.height()); | 2491 cache.validArea.height()); |
2398 | 2492 |
2399 } else { | 2493 } else { |
2400 if (x1 > x0 + paintBlockWidth) { | 2494 if (x1 > x0 + paintBlockWidth) { |
2401 int sfx = x1; | 2495 int sfx = x1; |
2402 if (startFrame < 0) sfx = v->getXForFrame(0); | 2496 if (startFrame < 0) sfx = v->getXForFrame(0); //!!! |
2403 if (sfx >= x0 && sfx + paintBlockWidth <= x1) { | 2497 if (sfx >= x0 && sfx + paintBlockWidth <= x1) { |
2404 x0 = sfx; | 2498 x0 = sfx; |
2405 x1 = x0 + paintBlockWidth; | 2499 x1 = x0 + paintBlockWidth; |
2406 } else { | 2500 } else { |
2407 int mid = (x1 + x0) / 2; | 2501 int mid = (x1 + x0) / 2; |
2420 << " to window at " << x0 << "," << rect.y() << std::endl; | 2514 << " to window at " << x0 << "," << rect.y() << std::endl; |
2421 #endif | 2515 #endif |
2422 | 2516 |
2423 paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height()); | 2517 paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height()); |
2424 | 2518 |
2425 if (recreateWholePixmapCache) { | 2519 if (recreateWholeCache) { |
2426 // cache.pixmap = QPixmap(v->width(), h); | 2520 // cache.pixmap = QPixmap(v->width(), h); |
2427 cache.pixmap = QImage(v->width(), h, QImage::Format_RGB32); | 2521 cache.pixmap = QImage(v->width(), h, QImage::Format_RGB32); |
2428 } | 2522 } |
2429 | 2523 |
2430 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2524 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2439 */ | 2533 */ |
2440 /* | 2534 /* |
2441 if (!m_normalizeVisibleArea || !overallMagChanged) { | 2535 if (!m_normalizeVisibleArea || !overallMagChanged) { |
2442 */ | 2536 */ |
2443 cache.startFrame = startFrame; | 2537 cache.startFrame = startFrame; |
2444 cache.zoomLevel = zoomLevel; | |
2445 | 2538 |
2446 bool morePaintingRequired = false; | 2539 bool morePaintingRequired = false; |
2447 | 2540 |
2448 if (cache.validArea.x() > 0) { | 2541 if (cache.validArea.x() > 0) { |
2449 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2542 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2453 // v->update(0, 0, cache.validArea.x(), h); | 2546 // v->update(0, 0, cache.validArea.x(), h); |
2454 morePaintingRequired = true; | 2547 morePaintingRequired = true; |
2455 } | 2548 } |
2456 | 2549 |
2457 if (cache.validArea.x() + cache.validArea.width() < | 2550 if (cache.validArea.x() + cache.validArea.width() < |
2458 cache.pixmap.width()) { | 2551 image.width()) { |
2459 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2552 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2460 std::cerr << "SpectrogramLayer::paintCache() updating right (" | 2553 std::cerr << "SpectrogramLayer::paintCache() updating right (" |
2461 << cache.validArea.x() + cache.validArea.width() | 2554 << cache.validArea.x() + cache.validArea.width() |
2462 << ", " | 2555 << ", " |
2463 << cache.pixmap.width() - (cache.validArea.x() + | 2556 << image.width() - (cache.validArea.x() + |
2464 cache.validArea.width()) | 2557 cache.validArea.width()) |
2465 << ")" << std::endl; | 2558 << ")" << std::endl; |
2466 #endif | 2559 #endif |
2467 // v->update(cache.validArea.x() + cache.validArea.width(), | 2560 // v->update(cache.validArea.x() + cache.validArea.width(), |
2468 // 0, | 2561 // 0, |
2469 // cache.pixmap.width() - (cache.validArea.x() + | 2562 // image.width() - (cache.validArea.x() + |
2470 // cache.validArea.width()), | 2563 // cache.validArea.width()), |
2471 // h); | 2564 // h); |
2472 morePaintingRequired = true; | 2565 morePaintingRequired = true; |
2473 } | 2566 } |
2474 /* | 2567 /* |
2482 // illuminateLocalFeatures(v, paint); | 2575 // illuminateLocalFeatures(v, paint); |
2483 | 2576 |
2484 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2577 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2485 std::cerr << "SpectrogramLayer::paintCache() returning" << std::endl; | 2578 std::cerr << "SpectrogramLayer::paintCache() returning" << std::endl; |
2486 #endif | 2579 #endif |
2580 | |
2581 paint.end(); | |
2487 | 2582 |
2488 m_lastPaintBlockWidth = paintBlockWidth; | 2583 m_lastPaintBlockWidth = paintBlockWidth; |
2489 (void)gettimeofday(&tv, 0); | 2584 (void)gettimeofday(&tv, 0); |
2490 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; | 2585 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; |
2491 | 2586 |
2655 { | 2750 { |
2656 //!!! check cache consistency | 2751 //!!! check cache consistency |
2657 QMutexLocker locker(&m_pixmapCacheMutex); | 2752 QMutexLocker locker(&m_pixmapCacheMutex); |
2658 | 2753 |
2659 PixmapCache *cache = m_pixmapCaches[v]; | 2754 PixmapCache *cache = m_pixmapCaches[v]; |
2660 if (!cache) return; | 2755 if (!cache || !cache->pixmap) return; |
2661 | 2756 |
2662 std::cerr << "cache width: " << cache->pixmap.width() << ", height: " | 2757 std::cerr << "cache width: " << cache->pixmap->width() << ", height: " |
2663 << cache->pixmap.height() << std::endl; | 2758 << cache->pixmap->height() << std::endl; |
2664 | 2759 |
2665 QImage image = cache->pixmap /*.toImage() */; | 2760 //!!! lock |
2761 | |
2762 QImage image = *cache->pixmap /*.toImage() */; | |
2666 | 2763 |
2667 ImageRegionFinder finder; | 2764 ImageRegionFinder finder; |
2668 QRect rect = finder.findRegionExtents(&image, e->pos()); | 2765 QRect rect = finder.findRegionExtents(&image, e->pos()); |
2669 if (rect.isValid()) { | 2766 if (rect.isValid()) { |
2670 MeasureRect mr; | 2767 MeasureRect mr; |
2938 { | 3035 { |
2939 if (!m_model || !m_model->isOK()) { | 3036 if (!m_model || !m_model->isOK()) { |
2940 return; | 3037 return; |
2941 } | 3038 } |
2942 | 3039 |
2943 Profiler profiler("SpectrogramLayer::paintVerticalScale", true); | 3040 Profiler profiler("SpectrogramLayer::paintVerticalScale", false); |
2944 | 3041 |
2945 //!!! cache this? | 3042 //!!! cache this? |
2946 | 3043 |
2947 int h = rect.height(), w = rect.width(); | 3044 int h = rect.height(), w = rect.width(); |
2948 | 3045 |