Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 1025:c02de0e34233 spectrogram-minor-refactor
Make paint method do its own timing and abandon if it's taking too long (still needs some work on queueing correct repaints of the remainder)
author | Chris Cannam |
---|---|
date | Mon, 25 Jan 2016 16:16:21 +0000 |
parents | 3bce4c45b681 |
children | 53110ace211f |
comparison
equal
deleted
inserted
replaced
1024:3bce4c45b681 | 1025:c02de0e34233 |
---|---|
31 | 31 |
32 #include <QPainter> | 32 #include <QPainter> |
33 #include <QImage> | 33 #include <QImage> |
34 #include <QPixmap> | 34 #include <QPixmap> |
35 #include <QRect> | 35 #include <QRect> |
36 #include <QTimer> | |
37 #include <QApplication> | 36 #include <QApplication> |
38 #include <QMessageBox> | 37 #include <QMessageBox> |
39 #include <QMouseEvent> | 38 #include <QMouseEvent> |
40 #include <QTextStream> | 39 #include <QTextStream> |
41 #include <QSettings> | 40 #include <QSettings> |
47 | 46 |
48 #ifndef __GNUC__ | 47 #ifndef __GNUC__ |
49 #include <alloca.h> | 48 #include <alloca.h> |
50 #endif | 49 #endif |
51 | 50 |
52 //#define DEBUG_SPECTROGRAM_REPAINT 1 | 51 #define DEBUG_SPECTROGRAM_REPAINT 1 |
53 | 52 |
54 using std::vector; | 53 using namespace std; |
55 | 54 |
56 SpectrogramLayer::SpectrogramLayer(Configuration config) : | 55 SpectrogramLayer::SpectrogramLayer(Configuration config) : |
57 m_model(0), | 56 m_model(0), |
58 m_channel(0), | 57 m_channel(0), |
59 m_windowSize(1024), | 58 m_windowSize(1024), |
76 m_binDisplay(AllBins), | 75 m_binDisplay(AllBins), |
77 m_normalization(NoNormalization), | 76 m_normalization(NoNormalization), |
78 m_lastEmittedZoomStep(-1), | 77 m_lastEmittedZoomStep(-1), |
79 m_synchronous(false), | 78 m_synchronous(false), |
80 m_haveDetailedScale(false), | 79 m_haveDetailedScale(false), |
81 m_lastPaintBlockWidth(0), | |
82 m_exiting(false), | 80 m_exiting(false), |
83 m_sliceableModel(0) | 81 m_sliceableModel(0) |
84 { | 82 { |
85 QString colourConfigName = "spectrogram-colour"; | 83 QString colourConfigName = "spectrogram-colour"; |
86 int colourConfigDefault = int(ColourMapper::Green); | 84 int colourConfigDefault = int(ColourMapper::Green); |
1613 | 1611 |
1614 void | 1612 void |
1615 SpectrogramLayer::invalidateMagnitudes() | 1613 SpectrogramLayer::invalidateMagnitudes() |
1616 { | 1614 { |
1617 m_viewMags.clear(); | 1615 m_viewMags.clear(); |
1618 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); | 1616 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin(); |
1619 i != m_columnMags.end(); ++i) { | 1617 i != m_columnMags.end(); ++i) { |
1620 *i = MagnitudeRange(); | 1618 *i = MagnitudeRange(); |
1621 } | 1619 } |
1622 } | 1620 } |
1623 | 1621 |
1635 | 1633 |
1636 if (!getXBinRange(v, x1, s10, s11)) { | 1634 if (!getXBinRange(v, x1, s10, s11)) { |
1637 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); | 1635 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); |
1638 } | 1636 } |
1639 | 1637 |
1640 int s0 = int(std::min(s00, s10) + 0.0001); | 1638 int s0 = int(min(s00, s10) + 0.0001); |
1641 int s1 = int(std::max(s01, s11) + 0.0001); | 1639 int s1 = int(max(s01, s11) + 0.0001); |
1642 | 1640 |
1643 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; | 1641 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; |
1644 | 1642 |
1645 if (int(m_columnMags.size()) <= s1) { | 1643 if (int(m_columnMags.size()) <= s1) { |
1646 m_columnMags.resize(s1 + 1); | 1644 m_columnMags.resize(s1 + 1); |
1727 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | 1725 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; |
1728 #endif | 1726 #endif |
1729 if (m_normalization == NormalizeVisibleArea) { | 1727 if (m_normalization == NormalizeVisibleArea) { |
1730 cache.validArea = QRect(); | 1728 cache.validArea = QRect(); |
1731 } | 1729 } |
1732 } | |
1733 | |
1734 if (cache.zoomLevel != zoomLevel) { | |
1735 // no matter what we do with the cache, we'll need to | |
1736 // recalculate our paint width again because each block will | |
1737 // take a different time to render from previously | |
1738 m_lastPaintBlockWidth = 0; | |
1739 } | 1730 } |
1740 | 1731 |
1741 if (cache.validArea.width() > 0) { | 1732 if (cache.validArea.width() > 0) { |
1742 | 1733 |
1743 int cw = cache.image.width(); | 1734 int cw = cache.image.width(); |
1873 if (recreateWholeImageCache) { | 1864 if (recreateWholeImageCache) { |
1874 x0 = 0; | 1865 x0 = 0; |
1875 x1 = v->getPaintWidth(); | 1866 x1 = v->getPaintWidth(); |
1876 } | 1867 } |
1877 | 1868 |
1878 auto mainPaintStart = std::chrono::steady_clock::now(); | |
1879 | |
1880 int paintBlockWidth = m_lastPaintBlockWidth; | |
1881 | |
1882 if (m_synchronous) { | |
1883 if (paintBlockWidth < x1 - x0) { | |
1884 // always paint full width | |
1885 paintBlockWidth = x1 - x0; | |
1886 } | |
1887 } else { | |
1888 if (paintBlockWidth == 0) { | |
1889 paintBlockWidth = (300000 / zoomLevel); | |
1890 } else { | |
1891 double lastTime = m_lastPaintTime; | |
1892 while (lastTime > 0.2 && | |
1893 paintBlockWidth > 30) { | |
1894 paintBlockWidth /= 2; | |
1895 lastTime = lastTime / 2; | |
1896 } | |
1897 while (lastTime < 0.09 && | |
1898 paintBlockWidth < std::max(50, 1200000 / zoomLevel) && | |
1899 paintBlockWidth < 1500) { | |
1900 paintBlockWidth *= 2; | |
1901 lastTime = lastTime * 2; | |
1902 } | |
1903 } | |
1904 | |
1905 if (paintBlockWidth < 20) paintBlockWidth = 20; | |
1906 } | |
1907 | |
1908 //#ifdef DEBUG_SPECTROGRAM_REPAINT | |
1909 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl; | |
1910 //#endif | |
1911 | |
1912 // We always paint the full height when refreshing the cache. | 1869 // We always paint the full height when refreshing the cache. |
1913 // Smaller heights can be used when painting direct from cache | 1870 // Smaller heights can be used when painting direct from cache |
1914 // (further up in this function), but we want to ensure the cache | 1871 // (further up in this function), but we want to ensure the cache |
1915 // is coherent without having to worry about vertical matching of | 1872 // is coherent without having to worry about vertical matching of |
1916 // required and valid areas as well as horizontal. | 1873 // required and valid areas as well as horizontal. |
1917 | 1874 |
1918 int h = v->getPaintHeight(); | 1875 int h = v->getPaintHeight(); |
1919 | 1876 |
1877 /* | |
1878 auto mainPaintStart = chrono::steady_clock::now(); | |
1879 | |
1880 //!!! nb full-width case goes like | |
1881 paintBlockWidth = x1 - x0; | |
1882 | |
1920 if (cache.validArea.width() > 0) { | 1883 if (cache.validArea.width() > 0) { |
1921 | 1884 |
1922 // If part of the cache is known to be valid, select a strip | 1885 // If part of the cache is known to be valid, select a strip |
1923 // immediately to left or right of the valid part | 1886 // immediately to left or right of the valid part |
1924 | 1887 |
1929 int vx0 = 0, vx1 = 0; | 1892 int vx0 = 0, vx1 = 0; |
1930 vx0 = cache.validArea.x(); | 1893 vx0 = cache.validArea.x(); |
1931 vx1 = cache.validArea.x() + cache.validArea.width(); | 1894 vx1 = cache.validArea.x() + cache.validArea.width(); |
1932 | 1895 |
1933 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1896 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1934 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl; | 1897 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << endl; |
1935 #endif | 1898 #endif |
1936 if (x0 < vx0) { | 1899 if (x0 < vx0) { |
1937 if (x0 + paintBlockWidth < vx0) { | 1900 if (x0 + paintBlockWidth < vx0) { |
1938 x0 = vx0 - paintBlockWidth; | 1901 x0 = vx0 - paintBlockWidth; |
1939 } | 1902 } |
1967 x0 = mid - paintBlockWidth/2; | 1930 x0 = mid - paintBlockWidth/2; |
1968 x1 = x0 + paintBlockWidth; | 1931 x1 = x0 + paintBlockWidth; |
1969 } | 1932 } |
1970 } | 1933 } |
1971 } | 1934 } |
1972 | 1935 */ |
1936 | |
1973 int repaintWidth = x1 - x0; | 1937 int repaintWidth = x1 - x0; |
1974 | 1938 |
1975 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1939 #ifdef DEBUG_SPECTROGRAM_REPAINT |
1976 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl; | 1940 cerr << "x0 " << x0 << ", x1 " << x1 |
1941 << ", repaintWidth " << repaintWidth << ", h " << h << endl; | |
1977 #endif | 1942 #endif |
1978 | 1943 |
1979 sv_samplerate_t sr = m_model->getSampleRate(); | 1944 sv_samplerate_t sr = m_model->getSampleRate(); |
1980 | 1945 |
1981 // Set minFreq and maxFreq to the frequency extents of the possibly | 1946 // Set minFreq and maxFreq to the frequency extents of the possibly |
2161 logarithmic, | 2126 logarithmic, |
2162 overallMag, overallMagChanged); | 2127 overallMag, overallMagChanged); |
2163 } | 2128 } |
2164 | 2129 |
2165 int failedToRepaint = bufwid - attainedBufwid; | 2130 int failedToRepaint = bufwid - attainedBufwid; |
2166 if (failedToRepaint < 0) { | 2131 if (failedToRepaint > 0) { |
2132 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
2133 cerr << "Failed to repaint " << failedToRepaint << " of " << bufwid | |
2134 << " columns in time" << endl; | |
2135 #endif | |
2136 } else if (failedToRepaint < 0) { | |
2167 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")" | 2137 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")" |
2168 << endl; | 2138 << endl; |
2169 failedToRepaint = 0; | 2139 failedToRepaint = 0; |
2170 } | 2140 } |
2171 | 2141 |
2239 } | 2209 } |
2240 | 2210 |
2241 // update cache valid area based on painted area | 2211 // update cache valid area based on painted area |
2242 if (cache.validArea.width() > 0) { | 2212 if (cache.validArea.width() > 0) { |
2243 | 2213 |
2244 int left = std::min(cache.validArea.x(), x0); | 2214 int left = min(cache.validArea.x(), x0); |
2245 | 2215 |
2246 int wid = std::max(cache.validArea.x() + cache.validArea.width() - left, | 2216 int wid = max(cache.validArea.x() + cache.validArea.width() - left, |
2247 x1 - left); | 2217 x1 - left); |
2248 | |
2249 wid = wid - failedToRepaint; | 2218 wid = wid - failedToRepaint; |
2250 if (wid < 0) wid = 0; | 2219 if (wid < 0) wid = 0; |
2251 | 2220 |
2252 cache.validArea = QRect | 2221 cache.validArea = QRect |
2253 (left, cache.validArea.y(), wid, cache.validArea.height()); | 2222 (left, cache.validArea.y(), wid, cache.validArea.height()); |
2254 | 2223 |
2255 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
2256 cerr << "Valid area becomes " << cache.validArea.x() | |
2257 << ", " << cache.validArea.y() << ", " | |
2258 << cache.validArea.width() << "x" | |
2259 << cache.validArea.height() << endl; | |
2260 #endif | |
2261 | |
2262 } else { | 2224 } else { |
2263 | 2225 |
2264 cache.validArea = QRect(x0, 0, x1 - x0, h); | 2226 int wid = x1 - x0; |
2265 | 2227 wid = wid - failedToRepaint; |
2266 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2228 if (wid < 0) wid = 0; |
2267 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0) | 2229 |
2268 << "x" << h << endl; | 2230 cache.validArea = QRect(x0, 0, wid, h); |
2269 #endif | 2231 } |
2270 } | 2232 |
2233 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
2234 cerr << "Cache valid area becomes " << cache.validArea.x() | |
2235 << ", " << cache.validArea.y() << ", " | |
2236 << cache.validArea.width() << "x" | |
2237 << cache.validArea.height() << endl; | |
2238 #endif | |
2271 | 2239 |
2272 QRect pr = rect & cache.validArea; | 2240 QRect pr = rect & cache.validArea; |
2273 | 2241 |
2274 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2242 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2275 cerr << "Painting " << pr.width() << "x" << pr.height() | 2243 cerr << "Painting " << pr.width() << "x" << pr.height() |
2323 illuminateLocalFeatures(v, paint); | 2291 illuminateLocalFeatures(v, paint); |
2324 | 2292 |
2325 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2293 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2326 cerr << "SpectrogramLayer::paint() returning" << endl; | 2294 cerr << "SpectrogramLayer::paint() returning" << endl; |
2327 #endif | 2295 #endif |
2328 | 2296 /*!!! |
2329 if (!m_synchronous) { | 2297 if (!m_synchronous) { |
2330 auto mainPaintEnd = std::chrono::steady_clock::now(); | 2298 auto mainPaintEnd = chrono::steady_clock::now(); |
2331 auto diff = mainPaintEnd - mainPaintStart; | 2299 auto diff = mainPaintEnd - mainPaintStart; |
2332 m_lastPaintTime = std::chrono::duration<double>(diff).count(); | 2300 m_lastPaintTime = chrono::duration<double>(diff).count(); |
2333 m_lastPaintBlockWidth = paintBlockWidth; | 2301 } |
2334 } | 2302 */ |
2335 } | 2303 } |
2336 | 2304 |
2337 int | 2305 int |
2338 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, | 2306 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, |
2339 int w, | 2307 int w, |
2463 MagnitudeRange &overallMag, | 2431 MagnitudeRange &overallMag, |
2464 bool &overallMagChanged) const | 2432 bool &overallMagChanged) const |
2465 { | 2433 { |
2466 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); | 2434 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); |
2467 | 2435 |
2436 //!!! todo: propagate to paintDrawBufferPeakFrequencies | |
2437 | |
2438 int minColumns = 4; | |
2439 double maxTime = 0.1; // seconds; only for non-synchronous drawing | |
2440 | |
2441 auto startTime = chrono::steady_clock::now(); | |
2442 | |
2468 int minbin = int(binfory[0] + 0.0001); | 2443 int minbin = int(binfory[0] + 0.0001); |
2469 int maxbin = int(binfory[h-1]); | 2444 int maxbin = int(binfory[h-1]); |
2470 | 2445 |
2471 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2446 #ifdef DEBUG_SPECTROGRAM_REPAINT |
2472 cerr << "paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; | 2447 cerr << "paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; |
2682 | 2657 |
2683 unsigned char peakpix = getDisplayValue(v, peak); | 2658 unsigned char peakpix = getDisplayValue(v, peak); |
2684 | 2659 |
2685 m_drawBuffer.setPixel(x, h-y-1, peakpix); | 2660 m_drawBuffer.setPixel(x, h-y-1, peakpix); |
2686 } | 2661 } |
2662 | |
2663 if (!m_synchronous) { | |
2664 if (x >= minColumns) { | |
2665 auto t = chrono::steady_clock::now(); | |
2666 double diff = chrono::duration<double>(t - startTime).count(); | |
2667 if (diff > maxTime) { | |
2668 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
2669 cerr << "Max time " << maxTime << " sec exceeded after " | |
2670 << x << " columns with time " << diff << endl; | |
2671 #endif | |
2672 return x; | |
2673 } | |
2674 } | |
2675 } | |
2687 } | 2676 } |
2688 | 2677 |
2689 return w; | 2678 return w; |
2690 } | 2679 } |
2691 | 2680 |
2879 } | 2868 } |
2880 | 2869 |
2881 bool | 2870 bool |
2882 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, | 2871 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, |
2883 QPoint cursorPos, | 2872 QPoint cursorPos, |
2884 std::vector<QRect> &extents) const | 2873 vector<QRect> &extents) const |
2885 { | 2874 { |
2886 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); | 2875 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); |
2887 extents.push_back(vertical); | 2876 extents.push_back(vertical); |
2888 | 2877 |
2889 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); | 2878 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); |
3279 QString text = QString("%1").arg(freq); | 3268 QString text = QString("%1").arg(freq); |
3280 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC | 3269 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC |
3281 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); | 3270 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); |
3282 | 3271 |
3283 if (h - vy - textHeight >= -2) { | 3272 if (h - vy - textHeight >= -2) { |
3284 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); | 3273 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); |
3285 paint.drawText(tx, h - vy + toff, text); | 3274 paint.drawText(tx, h - vy + toff, text); |
3286 } | 3275 } |
3287 | 3276 |
3288 py = vy; | 3277 py = vy; |
3289 } | 3278 } |