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 }