comparison layer/SpectrogramLayer.cpp @ 1043:fccee028a522 3.0-integration

Merge from branch "spectrogram-minor-refactor"
author Chris Cannam
date Thu, 04 Feb 2016 11:18:08 +0000
parents 96cf499fad62 25b035362c44
children 4e5c1c326794
comparison
equal deleted inserted replaced
1042:cd9e76e755bf 1043:fccee028a522
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>
40 #include <QSettings>
41 41
42 #include <iostream> 42 #include <iostream>
43 43
44 #include <cassert> 44 #include <cassert>
45 #include <cmath> 45 #include <cmath>
48 #include <alloca.h> 48 #include <alloca.h>
49 #endif 49 #endif
50 50
51 //#define DEBUG_SPECTROGRAM_REPAINT 1 51 //#define DEBUG_SPECTROGRAM_REPAINT 1
52 52
53 using std::vector; 53 using namespace std;
54 54
55 SpectrogramLayer::SpectrogramLayer(Configuration config) : 55 SpectrogramLayer::SpectrogramLayer(Configuration config) :
56 m_model(0), 56 m_model(0),
57 m_channel(0), 57 m_channel(0),
58 m_windowSize(1024), 58 m_windowSize(1024),
75 m_binDisplay(AllBins), 75 m_binDisplay(AllBins),
76 m_normalization(NoNormalization), 76 m_normalization(NoNormalization),
77 m_lastEmittedZoomStep(-1), 77 m_lastEmittedZoomStep(-1),
78 m_synchronous(false), 78 m_synchronous(false),
79 m_haveDetailedScale(false), 79 m_haveDetailedScale(false),
80 m_lastPaintBlockWidth(0),
81 m_exiting(false), 80 m_exiting(false),
82 m_sliceableModel(0) 81 m_sliceableModel(0)
83 { 82 {
83 QString colourConfigName = "spectrogram-colour";
84 int colourConfigDefault = int(ColourMapper::Green);
85
84 if (config == FullRangeDb) { 86 if (config == FullRangeDb) {
85 m_initialMaxFrequency = 0; 87 m_initialMaxFrequency = 0;
86 setMaxFrequency(0); 88 setMaxFrequency(0);
87 } else if (config == MelodicRange) { 89 } else if (config == MelodicRange) {
88 setWindowSize(8192); 90 setWindowSize(8192);
91 setMaxFrequency(1500); 93 setMaxFrequency(1500);
92 setMinFrequency(40); 94 setMinFrequency(40);
93 setColourScale(LinearColourScale); 95 setColourScale(LinearColourScale);
94 setColourMap(ColourMapper::Sunset); 96 setColourMap(ColourMapper::Sunset);
95 setFrequencyScale(LogFrequencyScale); 97 setFrequencyScale(LogFrequencyScale);
98 colourConfigName = "spectrogram-melodic-colour";
99 colourConfigDefault = int(ColourMapper::Sunset);
96 // setGain(20); 100 // setGain(20);
97 } else if (config == MelodicPeaks) { 101 } else if (config == MelodicPeaks) {
98 setWindowSize(4096); 102 setWindowSize(4096);
99 setWindowHopLevel(5); 103 setWindowHopLevel(5);
100 m_initialMaxFrequency = 2000; 104 m_initialMaxFrequency = 2000;
102 setMinFrequency(40); 106 setMinFrequency(40);
103 setFrequencyScale(LogFrequencyScale); 107 setFrequencyScale(LogFrequencyScale);
104 setColourScale(LinearColourScale); 108 setColourScale(LinearColourScale);
105 setBinDisplay(PeakFrequencies); 109 setBinDisplay(PeakFrequencies);
106 setNormalization(NormalizeColumns); 110 setNormalization(NormalizeColumns);
107 } 111 colourConfigName = "spectrogram-melodic-colour";
108 112 colourConfigDefault = int(ColourMapper::Sunset);
113 }
114
115 QSettings settings;
116 settings.beginGroup("Preferences");
117 setColourMap(settings.value(colourConfigName, colourConfigDefault).toInt());
118 settings.endGroup();
119
109 Preferences *prefs = Preferences::getInstance(); 120 Preferences *prefs = Preferences::getInstance();
110 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 121 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
111 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 122 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
112 setWindowType(prefs->getWindowType()); 123 setWindowType(prefs->getWindowType());
113 124
571 void 582 void
572 SpectrogramLayer::invalidateImageCaches() 583 SpectrogramLayer::invalidateImageCaches()
573 { 584 {
574 for (ViewImageCache::iterator i = m_imageCaches.begin(); 585 for (ViewImageCache::iterator i = m_imageCaches.begin();
575 i != m_imageCaches.end(); ++i) { 586 i != m_imageCaches.end(); ++i) {
576 i->second.validArea = QRect(); 587 i->second.invalidate();
577 }
578 }
579
580 void
581 SpectrogramLayer::invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame)
582 {
583 for (ViewImageCache::iterator i = m_imageCaches.begin();
584 i != m_imageCaches.end(); ++i) {
585
586 //!!! when are views removed from the map? on setLayerDormant?
587 const LayerGeometryProvider *v = i->first;
588
589 #ifdef DEBUG_SPECTROGRAM_REPAINT
590 cerr << "SpectrogramLayer::invalidateImageCaches("
591 << startFrame << ", " << endFrame << "): view range is "
592 << v->getStartFrame() << ", " << v->getEndFrame()
593 << endl;
594
595 cerr << "Valid area was: " << i->second.validArea.x() << ", "
596 << i->second.validArea.y() << " "
597 << i->second.validArea.width() << "x"
598 << i->second.validArea.height() << endl;
599 #endif
600
601 if (int(startFrame) > v->getStartFrame()) {
602 if (startFrame >= v->getEndFrame()) {
603 #ifdef DEBUG_SPECTROGRAM_REPAINT
604 cerr << "Modified start frame is off right of view" << endl;
605 #endif
606 return;
607 }
608 int x = v->getXForFrame(startFrame);
609 #ifdef DEBUG_SPECTROGRAM_REPAINT
610 cerr << "clipping from 0 to " << x-1 << endl;
611 #endif
612 if (x > 1) {
613 i->second.validArea &=
614 QRect(0, 0, x-1, v->getPaintHeight());
615 } else {
616 i->second.validArea = QRect();
617 }
618 } else {
619 if (int(endFrame) < v->getStartFrame()) {
620 #ifdef DEBUG_SPECTROGRAM_REPAINT
621 cerr << "Modified end frame is off left of view" << endl;
622 #endif
623 return;
624 }
625 int x = v->getXForFrame(endFrame);
626 #ifdef DEBUG_SPECTROGRAM_REPAINT
627 cerr << "clipping from " << x+1 << " to " << v->getPaintWidth()
628 << endl;
629 #endif
630 if (x < v->getPaintWidth()) {
631 i->second.validArea &=
632 QRect(x+1, 0, v->getPaintWidth()-(x+1), v->getPaintHeight());
633 } else {
634 i->second.validArea = QRect();
635 }
636 }
637
638 #ifdef DEBUG_SPECTROGRAM_REPAINT
639 cerr << "Valid area is now: " << i->second.validArea.x() << ", "
640 << i->second.validArea.y() << " "
641 << i->second.validArea.width() << "x"
642 << i->second.validArea.height() << endl;
643 #endif
644 } 588 }
645 } 589 }
646 590
647 void 591 void
648 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) 592 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
975 919
976 const View *view = v->getView(); 920 const View *view = v->getView();
977 921
978 invalidateImageCaches(); 922 invalidateImageCaches();
979 923
980 m_imageCaches.erase(view); 924 m_imageCaches.erase(view->getId());
981 925
982 if (m_fftModels.find(view) != m_fftModels.end()) { 926 if (m_fftModels.find(view->getId()) != m_fftModels.end()) {
983 927
984 if (m_sliceableModel == m_fftModels[view]) { 928 if (m_sliceableModel == m_fftModels[view->getId()]) {
985 bool replaced = false; 929 bool replaced = false;
986 for (ViewFFTMap::iterator i = m_fftModels.begin(); 930 for (ViewFFTMap::iterator i = m_fftModels.begin();
987 i != m_fftModels.end(); ++i) { 931 i != m_fftModels.end(); ++i) {
988 if (i->second != m_sliceableModel) { 932 if (i->second != m_sliceableModel) {
989 emit sliceableModelReplaced(m_sliceableModel, i->second); 933 emit sliceableModelReplaced(m_sliceableModel, i->second);
992 } 936 }
993 } 937 }
994 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); 938 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
995 } 939 }
996 940
997 delete m_fftModels[view]; 941 delete m_fftModels[view->getId()];
998 m_fftModels.erase(view); 942 m_fftModels.erase(view->getId());
999 943
1000 delete m_peakCaches[view]; 944 delete m_peakCaches[view->getId()];
1001 m_peakCaches.erase(view); 945 m_peakCaches.erase(view->getId());
1002 } 946 }
1003 947
1004 } else { 948 } else {
1005 949
1006 Layer::setLayerDormant(v, false); 950 Layer::setLayerDormant(v, false);
1017 invalidateImageCaches(); 961 invalidateImageCaches();
1018 invalidateMagnitudes(); 962 invalidateMagnitudes();
1019 } 963 }
1020 964
1021 void 965 void
1022 SpectrogramLayer::cacheInvalid(sv_frame_t from, sv_frame_t to) 966 SpectrogramLayer::cacheInvalid(
967 #ifdef DEBUG_SPECTROGRAM_REPAINT
968 sv_frame_t from, sv_frame_t to
969 #else
970 sv_frame_t , sv_frame_t
971 #endif
972 )
1023 { 973 {
1024 #ifdef DEBUG_SPECTROGRAM_REPAINT 974 #ifdef DEBUG_SPECTROGRAM_REPAINT
1025 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; 975 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
1026 #endif 976 #endif
1027 977
1028 invalidateImageCaches(from, to); 978 // We used to call invalidateMagnitudes(from, to) to invalidate
979 // only those caches whose views contained some of the (from, to)
980 // range. That's the right thing to do; it has been lost in
981 // pulling out the image cache code, but it might not matter very
982 // much, since the underlying models for spectrogram layers don't
983 // change very often. Let's see.
984 invalidateImageCaches();
1029 invalidateMagnitudes(); 985 invalidateMagnitudes();
1030 } 986 }
1031 987
1032 bool 988 bool
1033 SpectrogramLayer::hasLightBackground() const 989 SpectrogramLayer::hasLightBackground() const
1089 1045
1090 double min = 0.0; 1046 double min = 0.0;
1091 double max = 1.0; 1047 double max = 1.0;
1092 1048
1093 if (m_normalization == NormalizeVisibleArea) { 1049 if (m_normalization == NormalizeVisibleArea) {
1094 min = m_viewMags[v].getMin(); 1050 min = m_viewMags[v->getId()].getMin();
1095 max = m_viewMags[v].getMax(); 1051 max = m_viewMags[v->getId()].getMax();
1096 } else if (m_normalization != NormalizeColumns) { 1052 } else if (m_normalization != NormalizeColumns) {
1097 if (m_colourScale == LinearColourScale //|| 1053 if (m_colourScale == LinearColourScale //||
1098 // m_colourScale == MeterColourScale) { 1054 // m_colourScale == MeterColourScale) {
1099 ) { 1055 ) {
1100 max = 0.1; 1056 max = 0.1;
1343 1299
1344 for (int q = q0i; q <= q1i; ++q) { 1300 for (int q = q0i; q <= q1i; ++q) {
1345 1301
1346 for (int s = s0i; s <= s1i; ++s) { 1302 for (int s = s0i; s <= s1i; ++s) {
1347 1303
1348 if (!fft->isColumnAvailable(s)) continue;
1349
1350 double binfreq = (double(sr) * q) / m_windowSize; 1304 double binfreq = (double(sr) * q) / m_windowSize;
1351 if (q == q0i) freqMin = binfreq; 1305 if (q == q0i) freqMin = binfreq;
1352 if (q == q1i) freqMax = binfreq; 1306 if (q == q1i) freqMax = binfreq;
1353 1307
1354 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; 1308 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1418 1372
1419 for (int q = q0i; q <= q1i; ++q) { 1373 for (int q = q0i; q <= q1i; ++q) {
1420 for (int s = s0i; s <= s1i; ++s) { 1374 for (int s = s0i; s <= s1i; ++s) {
1421 if (s >= 0 && q >= 0 && s < cw && q < ch) { 1375 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1422 1376
1423 if (!fft->isColumnAvailable(s)) continue;
1424
1425 double value; 1377 double value;
1426 1378
1427 value = fft->getPhaseAt(s, q); 1379 value = fft->getPhaseAt(s, q);
1428 if (!have || value < phaseMin) { phaseMin = value; } 1380 if (!have || value < phaseMin) { phaseMin = value; }
1429 if (!have || value > phaseMax) { phaseMax = value; } 1381 if (!have || value > phaseMax) { phaseMax = value; }
1501 1453
1502 int fftSize = getFFTSize(v); 1454 int fftSize = getFFTSize(v);
1503 1455
1504 const View *view = v->getView(); 1456 const View *view = v->getView();
1505 1457
1506 if (m_fftModels.find(view) != m_fftModels.end()) { 1458 if (m_fftModels.find(view->getId()) != m_fftModels.end()) {
1507 if (m_fftModels[view] == 0) { 1459 if (m_fftModels[view->getId()] == 0) {
1508 #ifdef DEBUG_SPECTROGRAM_REPAINT 1460 #ifdef DEBUG_SPECTROGRAM_REPAINT
1509 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; 1461 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
1510 #endif 1462 #endif
1511 return 0; 1463 return 0;
1512 } 1464 }
1513 if (m_fftModels[view]->getHeight() != fftSize / 2 + 1) { 1465 if (m_fftModels[view->getId()]->getHeight() != fftSize / 2 + 1) {
1514 #ifdef DEBUG_SPECTROGRAM_REPAINT 1466 #ifdef DEBUG_SPECTROGRAM_REPAINT
1515 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; 1467 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view->getId()]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
1516 #endif 1468 #endif
1517 delete m_fftModels[view]; 1469 delete m_fftModels[view->getId()];
1518 m_fftModels.erase(view); 1470 m_fftModels.erase(view->getId());
1519 delete m_peakCaches[view]; 1471 delete m_peakCaches[view->getId()];
1520 m_peakCaches.erase(view); 1472 m_peakCaches.erase(view->getId());
1521 } else { 1473 } else {
1522 #ifdef DEBUG_SPECTROGRAM_REPAINT 1474 #ifdef DEBUG_SPECTROGRAM_REPAINT
1523 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view]->getHeight() << endl; 1475 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view->getId()]->getHeight() << endl;
1524 #endif 1476 #endif
1525 return m_fftModels[view]; 1477 return m_fftModels[view->getId()];
1526 } 1478 }
1527 } 1479 }
1528 1480
1529 if (m_fftModels.find(view) == m_fftModels.end()) { 1481 if (m_fftModels.find(view->getId()) == m_fftModels.end()) {
1530 1482
1531 FFTModel *model = new FFTModel(m_model, 1483 FFTModel *model = new FFTModel(m_model,
1532 m_channel, 1484 m_channel,
1533 m_windowType, 1485 m_windowType,
1534 m_windowSize, 1486 m_windowSize,
1539 QMessageBox::critical 1491 QMessageBox::critical
1540 (0, tr("FFT cache failed"), 1492 (0, tr("FFT cache failed"),
1541 tr("Failed to create the FFT model for this spectrogram.\n" 1493 tr("Failed to create the FFT model for this spectrogram.\n"
1542 "There may be insufficient memory or disc space to continue.")); 1494 "There may be insufficient memory or disc space to continue."));
1543 delete model; 1495 delete model;
1544 m_fftModels[view] = 0; 1496 m_fftModels[view->getId()] = 0;
1545 return 0; 1497 return 0;
1546 } 1498 }
1547 1499
1548 if (!m_sliceableModel) { 1500 if (!m_sliceableModel) {
1549 #ifdef DEBUG_SPECTROGRAM 1501 #ifdef DEBUG_SPECTROGRAM
1551 #endif 1503 #endif
1552 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); 1504 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1553 m_sliceableModel = model; 1505 m_sliceableModel = model;
1554 } 1506 }
1555 1507
1556 m_fftModels[view] = model; 1508 m_fftModels[view->getId()] = model;
1557 } 1509 }
1558 1510
1559 return m_fftModels[view]; 1511 return m_fftModels[view->getId()];
1560 } 1512 }
1561 1513
1562 Dense3DModelPeakCache * 1514 Dense3DModelPeakCache *
1563 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const 1515 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const
1564 { 1516 {
1565 const View *view = v->getView(); 1517 const View *view = v->getView();
1566 if (!m_peakCaches[view]) { 1518 if (!m_peakCaches[view->getId()]) {
1567 FFTModel *f = getFFTModel(v); 1519 FFTModel *f = getFFTModel(v);
1568 if (!f) return 0; 1520 if (!f) return 0;
1569 m_peakCaches[view] = new Dense3DModelPeakCache(f, 8); 1521 m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8);
1570 } 1522 }
1571 return m_peakCaches[view]; 1523 return m_peakCaches[view->getId()];
1572 } 1524 }
1573 1525
1574 const Model * 1526 const Model *
1575 SpectrogramLayer::getSliceableModel() const 1527 SpectrogramLayer::getSliceableModel() const
1576 { 1528 {
1604 1556
1605 void 1557 void
1606 SpectrogramLayer::invalidateMagnitudes() 1558 SpectrogramLayer::invalidateMagnitudes()
1607 { 1559 {
1608 m_viewMags.clear(); 1560 m_viewMags.clear();
1609 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); 1561 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin();
1610 i != m_columnMags.end(); ++i) { 1562 i != m_columnMags.end(); ++i) {
1611 *i = MagnitudeRange(); 1563 *i = MagnitudeRange();
1612 } 1564 }
1613 } 1565 }
1614 1566
1626 1578
1627 if (!getXBinRange(v, x1, s10, s11)) { 1579 if (!getXBinRange(v, x1, s10, s11)) {
1628 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); 1580 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement();
1629 } 1581 }
1630 1582
1631 int s0 = int(std::min(s00, s10) + 0.0001); 1583 int s0 = int(min(s00, s10) + 0.0001);
1632 int s1 = int(std::max(s01, s11) + 0.0001); 1584 int s1 = int(max(s01, s11) + 0.0001);
1633 1585
1634 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; 1586 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl;
1635 1587
1636 if (int(m_columnMags.size()) <= s1) { 1588 if (int(m_columnMags.size()) <= s1) {
1637 m_columnMags.resize(s1 + 1); 1589 m_columnMags.resize(s1 + 1);
1647 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " 1599 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
1648 << s0 << " -> " << s1 << " inclusive" << endl; 1600 << s0 << " -> " << s1 << " inclusive" << endl;
1649 #endif 1601 #endif
1650 1602
1651 if (!mag.isSet()) return false; 1603 if (!mag.isSet()) return false;
1652 if (mag == m_viewMags[v]) return false; 1604 if (mag == m_viewMags[v->getId()]) return false;
1653 m_viewMags[v] = mag; 1605 m_viewMags[v->getId()] = mag;
1654 return true; 1606 return true;
1655 } 1607 }
1656 1608
1657 void 1609 void
1658 SpectrogramLayer::setSynchronousPainting(bool synchronous) 1610 SpectrogramLayer::setSynchronousPainting(bool synchronous)
1659 { 1611 {
1660 m_synchronous = synchronous; 1612 m_synchronous = synchronous;
1661 } 1613 }
1662 1614
1615 ScrollableImageCache &
1616 SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const
1617 {
1618 if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) {
1619 m_imageCaches[view->getId()] = ScrollableImageCache(view);
1620 }
1621 return m_imageCaches.at(view->getId());
1622 }
1623
1663 void 1624 void
1664 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const 1625 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1665 { 1626 {
1666 // What a lovely, old-fashioned function this is.
1667 // It's practically FORTRAN 77 in its clarity and linearity.
1668
1669 Profiler profiler("SpectrogramLayer::paint", false); 1627 Profiler profiler("SpectrogramLayer::paint", false);
1670 1628
1671 #ifdef DEBUG_SPECTROGRAM_REPAINT 1629 #ifdef DEBUG_SPECTROGRAM_REPAINT
1672 cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; 1630 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
1673 1631
1674 cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; 1632 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
1675 #endif 1633 #endif
1676 1634
1677 sv_frame_t startFrame = v->getStartFrame(); 1635 sv_frame_t startFrame = v->getStartFrame();
1678 1636
1679 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1637 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1690 // in the cache-fill thread above. 1648 // in the cache-fill thread above.
1691 //!!! no inter use cache-fill thread 1649 //!!! no inter use cache-fill thread
1692 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); 1650 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1693 1651
1694 int fftSize = getFFTSize(v); 1652 int fftSize = getFFTSize(v);
1695 /*
1696 FFTModel *fft = getFFTModel(v);
1697 if (!fft) {
1698 cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl;
1699 return;
1700 }
1701 */
1702 1653
1703 const View *view = v->getView(); 1654 const View *view = v->getView();
1704 1655 ScrollableImageCache &cache = getImageCacheReference(view);
1705 ImageCache &cache = m_imageCaches[view]; 1656
1706 1657 #ifdef DEBUG_SPECTROGRAM_REPAINT
1707 #ifdef DEBUG_SPECTROGRAM_REPAINT 1658 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl;
1708 cerr << "SpectrogramLayer::paint(): image cache valid area " << cache. 1659 if (rect.x() + rect.width() + 1 < cache.getValidLeft() ||
1709 1660 rect.x() > cache.getValidRight()) {
1710 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; 1661 cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl;
1662 }
1711 #endif 1663 #endif
1712 1664
1713 int zoomLevel = v->getZoomLevel(); 1665 int zoomLevel = v->getZoomLevel();
1714 1666
1715 int x0 = 0; 1667 int x0 = v->getXForViewX(rect.x());
1716 int x1 = v->getPaintWidth(); 1668 int x1 = v->getXForViewX(rect.x() + rect.width());
1717 1669 if (x0 < 0) x0 = 0;
1718 bool recreateWholeImageCache = true; 1670 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
1719 1671
1720 x0 = rect.left(); 1672 if (updateViewMagnitudes(v)) {
1721 x1 = rect.right() + 1; 1673 #ifdef DEBUG_SPECTROGRAM_REPAINT
1722 /* 1674 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
1723 double xPixelRatio = double(fft->getResolution()) / double(zoomLevel); 1675 #endif
1724 cerr << "xPixelRatio = " << xPixelRatio << endl; 1676 if (m_normalization == NormalizeVisibleArea) {
1725 if (xPixelRatio < 1.f) xPixelRatio = 1.f; 1677 cache.invalidate();
1726 */ 1678 }
1727 if (cache.validArea.width() > 0) { 1679 }
1728 1680
1729 int cw = cache.image.width(); 1681 if (cache.getZoomLevel() != zoomLevel ||
1730 int ch = cache.image.height(); 1682 cache.getSize() != v->getPaintSize()) {
1683 #ifdef DEBUG_SPECTROGRAM_REPAINT
1684 cerr << "SpectrogramLayer: resizing image cache from "
1685 << cache.getSize().width() << "x" << cache.getSize().height()
1686 << " to "
1687 << v->getPaintSize().width() << "x" << v->getPaintSize().height()
1688 << " and updating zoom level from " << cache.getZoomLevel()
1689 << " to " << zoomLevel
1690 << endl;
1691 #endif
1692 cache.resize(v->getPaintSize());
1693 cache.setZoomLevel(zoomLevel);
1694 cache.setStartFrame(startFrame);
1695 }
1696
1697 if (cache.isValid()) {
1731 1698
1732 if (int(cache.zoomLevel) == zoomLevel && 1699 if (v->getXForFrame(cache.getStartFrame()) ==
1733 cw == v->getPaintWidth() && 1700 v->getXForFrame(startFrame) &&
1734 ch == v->getPaintHeight()) { 1701 cache.getValidLeft() <= x0 &&
1735 1702 cache.getValidRight() >= x1) {
1736 if (v->getXForFrame(cache.startFrame) == 1703
1737 v->getXForFrame(startFrame) && 1704 #ifdef DEBUG_SPECTROGRAM_REPAINT
1738 cache.validArea.x() <= x0 && 1705 cerr << "SpectrogramLayer: image cache hit!" << endl;
1739 cache.validArea.x() + cache.validArea.width() >= x1) { 1706 #endif
1740 1707
1741 #ifdef DEBUG_SPECTROGRAM_REPAINT 1708 paint.drawImage(rect, cache.getImage(), rect);
1742 cerr << "SpectrogramLayer: image cache good" << endl; 1709
1743 #endif 1710 illuminateLocalFeatures(v, paint);
1744 1711 return;
1745 paint.drawImage(rect, cache.image, rect); 1712
1746 //!!! 1713 } else {
1747 // paint.drawImage(v->rect(), cache.image, 1714
1748 // QRect(QPoint(0, 0), cache.image.size())); 1715 // cache doesn't begin at the right frame or doesn't
1749 1716 // contain the complete view, but might be scrollable or
1750 illuminateLocalFeatures(v, paint); 1717 // partially usable
1751 return; 1718
1752 1719 #ifdef DEBUG_SPECTROGRAM_REPAINT
1753 } else { 1720 cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl;
1754 1721 #endif
1755 #ifdef DEBUG_SPECTROGRAM_REPAINT 1722
1756 cerr << "SpectrogramLayer: image cache partially OK" << endl; 1723 cache.scrollTo(startFrame);
1757 #endif 1724
1758 1725 #ifdef DEBUG_SPECTROGRAM_REPAINT
1759 recreateWholeImageCache = false; 1726 cerr << "SpectrogramLayer: after scrolling, cache valid from "
1760 1727 << cache.getValidLeft() << " width " << cache.getValidWidth()
1761 int dx = v->getXForFrame(cache.startFrame) - 1728 << endl;
1762 v->getXForFrame(startFrame); 1729 #endif
1763 1730 }
1764 #ifdef DEBUG_SPECTROGRAM_REPAINT 1731 }
1765 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; 1732
1766 #endif 1733 bool rightToLeft = false;
1767 1734
1768 if (dx != 0 && 1735 if (!cache.isValid()) {
1769 dx > -cw && 1736 if (!m_synchronous) {
1770 dx < cw) { 1737 // When rendering the whole thing, start from somewhere near
1771 1738 // the middle so that the region of interest appears first
1772 int dxp = dx; 1739
1773 if (dxp < 0) dxp = -dxp; 1740 //!!! (perhaps we should have some cunning test to avoid
1774 size_t copy = (cw - dxp) * sizeof(QRgb); 1741 //!!! doing this if past repaints have appeared fast
1775 for (int y = 0; y < ch; ++y) { 1742 //!!! enough to do the whole width in one shot)
1776 QRgb *line = (QRgb *)cache.image.scanLine(y); 1743 if (x0 == 0 && x1 == v->getPaintWidth()) {
1777 if (dx < 0) { 1744 x0 = int(x1 * 0.3);
1778 memmove(line, line + dxp, copy);
1779 } else {
1780 memmove(line + dxp, line, copy);
1781 }
1782 }
1783
1784 int px = cache.validArea.x();
1785 int pw = cache.validArea.width();
1786
1787 if (dx < 0) {
1788 x0 = cw + dx;
1789 x1 = cw;
1790 px += dx;
1791 if (px < 0) {
1792 pw += px;
1793 px = 0;
1794 if (pw < 0) pw = 0;
1795 }
1796 } else {
1797 x0 = 0;
1798 x1 = dx;
1799 px += dx;
1800 if (px + pw > cw) {
1801 pw = int(cw) - px;
1802 if (pw < 0) pw = 0;
1803 }
1804 }
1805
1806 cache.validArea =
1807 QRect(px, cache.validArea.y(),
1808 pw, cache.validArea.height());
1809
1810 #ifdef DEBUG_SPECTROGRAM_REPAINT
1811 cerr << "valid area now "
1812 << px << "," << cache.validArea.y()
1813 << " " << pw << "x" << cache.validArea.height()
1814 << endl;
1815 #endif
1816 /*
1817 paint.drawImage(rect & cache.validArea,
1818 cache.image,
1819 rect & cache.validArea);
1820 */
1821 } else if (dx != 0) {
1822
1823 // we scrolled too far to be of use
1824
1825 #ifdef DEBUG_SPECTROGRAM_REPAINT
1826 cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl;
1827 #endif
1828
1829 cache.validArea = QRect();
1830 recreateWholeImageCache = true;
1831 }
1832 }
1833 } else {
1834 #ifdef DEBUG_SPECTROGRAM_REPAINT
1835 cerr << "SpectrogramLayer: image cache useless" << endl;
1836 if (int(cache.zoomLevel) != zoomLevel) {
1837 cerr << "(cache zoomLevel " << cache.zoomLevel
1838 << " != " << zoomLevel << ")" << endl;
1839 } 1745 }
1840 if (cw != v->getPaintWidth()) {
1841 cerr << "(cache width " << cw
1842 << " != " << v->getPaintWidth();
1843 }
1844 if (ch != v->getPaintHeight()) {
1845 cerr << "(cache height " << ch
1846 << " != " << v->getPaintHeight();
1847 }
1848 #endif
1849 cache.validArea = QRect();
1850 // recreateWholeImageCache = true;
1851 }
1852 }
1853
1854 if (updateViewMagnitudes(v)) {
1855 #ifdef DEBUG_SPECTROGRAM_REPAINT
1856 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
1857 #endif
1858 if (m_normalization == NormalizeVisibleArea) {
1859 cache.validArea = QRect();
1860 recreateWholeImageCache = true;
1861 } 1746 }
1862 } else { 1747 } else {
1863 #ifdef DEBUG_SPECTROGRAM_REPAINT 1748 // When rendering only a part of the cache, we need to make
1864 cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; 1749 // sure that the part we're rendering is adjacent to (or
1865 #endif 1750 // overlapping) a valid area of cache, if we have one. The
1866 } 1751 // alternative is to ditch the valid area of cache and render
1867 1752 // only the requested area, but that's risky because this can
1868 if (recreateWholeImageCache) { 1753 // happen when just waving the pointer over a small part of
1869 x0 = 0; 1754 // the view -- if we lose the partly-built cache every time
1870 x1 = v->getPaintWidth(); 1755 // the user does that, we'll never finish building it.
1871 } 1756 int left = x0;
1872 1757 int width = x1 - x0;
1873 struct timeval tv; 1758 bool isLeftOfValidArea = false;
1874 (void)gettimeofday(&tv, 0); 1759 cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
1875 RealTime mainPaintStart = RealTime::fromTimeval(tv); 1760 x0 = left;
1876 1761 x1 = x0 + width;
1877 int paintBlockWidth = m_lastPaintBlockWidth; 1762
1878 1763 // That call also told us whether we should be painting
1879 if (m_synchronous) { 1764 // sub-regions of our target region in right-to-left order in
1880 if (paintBlockWidth < x1 - x0) { 1765 // order to ensure contiguity
1881 // always paint full width 1766 rightToLeft = isLeftOfValidArea;
1882 paintBlockWidth = x1 - x0; 1767 }
1883 } 1768
1884 } else {
1885 if (paintBlockWidth == 0) {
1886 paintBlockWidth = (300000 / zoomLevel);
1887 } else {
1888 RealTime lastTime = m_lastPaintTime;
1889 while (lastTime > RealTime::fromMilliseconds(200) &&
1890 paintBlockWidth > 100) {
1891 paintBlockWidth /= 2;
1892 lastTime = lastTime / 2;
1893 }
1894 while (lastTime < RealTime::fromMilliseconds(90) &&
1895 paintBlockWidth < 1500) {
1896 paintBlockWidth *= 2;
1897 lastTime = lastTime * 2;
1898 }
1899 }
1900
1901 if (paintBlockWidth < 50) paintBlockWidth = 50;
1902 }
1903
1904 #ifdef DEBUG_SPECTROGRAM_REPAINT
1905 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl;
1906 #endif
1907
1908 // We always paint the full height when refreshing the cache. 1769 // We always paint the full height when refreshing the cache.
1909 // Smaller heights can be used when painting direct from cache 1770 // Smaller heights can be used when painting direct from cache
1910 // (further up in this function), but we want to ensure the cache 1771 // (further up in this function), but we want to ensure the cache
1911 // is coherent without having to worry about vertical matching of 1772 // is coherent without having to worry about vertical matching of
1912 // required and valid areas as well as horizontal. 1773 // required and valid areas as well as horizontal.
1913
1914 int h = v->getPaintHeight(); 1774 int h = v->getPaintHeight();
1915 1775
1916 if (cache.validArea.width() > 0) { 1776 int repaintWidth = x1 - x0;
1917 1777
1918 // If part of the cache is known to be valid, select a strip 1778 #ifdef DEBUG_SPECTROGRAM_REPAINT
1919 // immediately to left or right of the valid part 1779 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1
1920 1780 << ", repaintWidth " << repaintWidth << ", h " << h
1921 //!!! this really needs to be coordinated with the selection 1781 << ", rightToLeft " << rightToLeft << endl;
1922 //!!! of m_drawBuffer boundaries in the bufferBinResolution
1923 //!!! case below
1924
1925 int vx0 = 0, vx1 = 0;
1926 vx0 = cache.validArea.x();
1927 vx1 = cache.validArea.x() + cache.validArea.width();
1928
1929 #ifdef DEBUG_SPECTROGRAM_REPAINT
1930 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl;
1931 #endif
1932 if (x0 < vx0) {
1933 if (x0 + paintBlockWidth < vx0) {
1934 x0 = vx0 - paintBlockWidth;
1935 }
1936 x1 = vx0;
1937 } else if (x0 >= vx1) {
1938 x0 = vx1;
1939 if (x1 > x0 + paintBlockWidth) {
1940 x1 = x0 + paintBlockWidth;
1941 }
1942 } else {
1943 // x0 is within the valid area
1944 if (x1 > vx1) {
1945 x0 = vx1;
1946 if (x0 + paintBlockWidth < x1) {
1947 x1 = x0 + paintBlockWidth;
1948 }
1949 } else {
1950 x1 = x0; // it's all valid, paint nothing
1951 }
1952 }
1953
1954 cache.validArea = QRect
1955 (std::min(vx0, x0), cache.validArea.y(),
1956 std::max(vx1 - std::min(vx0, x0),
1957 x1 - std::min(vx0, x0)),
1958 cache.validArea.height());
1959
1960 #ifdef DEBUG_SPECTROGRAM_REPAINT
1961 cerr << "Valid area becomes " << cache.validArea.x()
1962 << ", " << cache.validArea.y() << ", "
1963 << cache.validArea.width() << "x"
1964 << cache.validArea.height() << endl;
1965 #endif
1966
1967 } else {
1968 if (x1 > x0 + paintBlockWidth) {
1969 int sfx = x1;
1970 if (startFrame < 0) sfx = v->getXForFrame(0);
1971 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
1972 x0 = sfx;
1973 x1 = x0 + paintBlockWidth;
1974 } else {
1975 int mid = (x1 + x0) / 2;
1976 x0 = mid - paintBlockWidth/2;
1977 x1 = x0 + paintBlockWidth;
1978 }
1979 }
1980 #ifdef DEBUG_SPECTROGRAM_REPAINT
1981 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0)
1982 << "x" << h << endl;
1983 #endif
1984 cache.validArea = QRect(x0, 0, x1 - x0, h);
1985 }
1986
1987 /*
1988 if (xPixelRatio != 1.f) {
1989 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001);
1990 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001);
1991 }
1992 */
1993 int w = x1 - x0;
1994
1995 #ifdef DEBUG_SPECTROGRAM_REPAINT
1996 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl;
1997 #endif 1782 #endif
1998 1783
1999 sv_samplerate_t sr = m_model->getSampleRate(); 1784 sv_samplerate_t sr = m_model->getSampleRate();
2000 1785
2001 // Set minFreq and maxFreq to the frequency extents of the possibly 1786 // Set minFreq and maxFreq to the frequency extents of the possibly
2041 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl; 1826 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
2042 1827
2043 int increment = getWindowIncrement(); 1828 int increment = getWindowIncrement();
2044 1829
2045 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1830 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2046 /* 1831
2047 double yforbin[maxbin - minbin + 1]; 1832 MagnitudeRange overallMag = m_viewMags[v->getId()];
2048
2049 for (int q = minbin; q <= maxbin; ++q) {
2050 double f0 = (double(q) * sr) / fftSize;
2051 yforbin[q - minbin] =
2052 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2053 logarithmic);
2054 }
2055 */
2056 MagnitudeRange overallMag = m_viewMags[v];
2057 bool overallMagChanged = false; 1833 bool overallMagChanged = false;
2058 1834
2059 #ifdef DEBUG_SPECTROGRAM_REPAINT 1835 #ifdef DEBUG_SPECTROGRAM_REPAINT
2060 cerr << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; 1836 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
2061 #endif 1837 #endif
2062 1838
2063 if (w == 0) { 1839 if (repaintWidth == 0) {
2064 SVDEBUG << "*** NOTE: w == 0" << endl; 1840 SVDEBUG << "*** NOTE: repaintWidth == 0" << endl;
2065 } 1841 }
2066 1842
2067 Profiler outerprof("SpectrogramLayer::paint: all cols"); 1843 Profiler outerprof("SpectrogramLayer::paint: all cols");
2068 1844
2069 // The draw buffer contains a fragment at either our pixel 1845 // The draw buffer contains a fragment at either our pixel
2077 // If (getFrameForX(x) / increment) * increment == 1853 // If (getFrameForX(x) / increment) * increment ==
2078 // getFrameForX(x), then x is a time-bin boundary. We want two 1854 // getFrameForX(x), then x is a time-bin boundary. We want two
2079 // such boundaries at either side of the draw buffer -- one which 1855 // such boundaries at either side of the draw buffer -- one which
2080 // we draw up to, and one which we subsequently crop at. 1856 // we draw up to, and one which we subsequently crop at.
2081 1857
2082 bool bufferBinResolution = false; 1858 bool bufferIsBinResolution = false;
2083 if (increment > zoomLevel) bufferBinResolution = true; 1859 if (increment > zoomLevel) bufferIsBinResolution = true;
2084 1860
2085 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1; 1861 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
2086 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1; 1862 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
2087 1863
2088 int bufwid; 1864 int bufwid;
2089 1865
2090 if (bufferBinResolution) { 1866 if (bufferIsBinResolution) {
2091 1867
2092 for (int x = x0; ; --x) { 1868 for (int x = x0; ; --x) {
2093 sv_frame_t f = v->getFrameForX(x); 1869 sv_frame_t f = v->getFrameForX(x);
2094 if ((f / increment) * increment == f) { 1870 if ((f / increment) * increment == f) {
2095 if (leftCropFrame == -1) leftCropFrame = f; 1871 if (leftCropFrame == -1) leftCropFrame = f;
2096 else if (x < x0 - 2) { leftBoundaryFrame = f; break; } 1872 else if (x < x0 - 2) {
1873 leftBoundaryFrame = f;
1874 break;
1875 }
2097 } 1876 }
2098 } 1877 }
2099 for (int x = x0 + w; ; ++x) { 1878 for (int x = x0 + repaintWidth; ; ++x) {
2100 sv_frame_t f = v->getFrameForX(x); 1879 sv_frame_t f = v->getFrameForX(x);
2101 if ((f / increment) * increment == f) { 1880 if ((f / increment) * increment == f) {
2102 if (rightCropFrame == -1) rightCropFrame = f; 1881 if (rightCropFrame == -1) rightCropFrame = f;
2103 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; } 1882 else if (x > x0 + repaintWidth + 2) {
1883 rightBoundaryFrame = f;
1884 break;
1885 }
2104 } 1886 }
2105 } 1887 }
2106 #ifdef DEBUG_SPECTROGRAM_REPAINT 1888 #ifdef DEBUG_SPECTROGRAM_REPAINT
2107 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl; 1889 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
2108 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; 1890 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
2110 1892
2111 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment); 1893 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment);
2112 1894
2113 } else { 1895 } else {
2114 1896
2115 bufwid = w; 1897 bufwid = repaintWidth;
2116 } 1898 }
2117 1899
2118 vector<int> binforx(bufwid); 1900 vector<int> binforx(bufwid);
2119 vector<double> binfory(h); 1901 vector<double> binfory(h);
2120 1902
2121 bool usePeaksCache = false; 1903 bool usePeaksCache = false;
2122 1904
2123 if (bufferBinResolution) { 1905 if (bufferIsBinResolution) {
2124 for (int x = 0; x < bufwid; ++x) { 1906 for (int x = 0; x < bufwid; ++x) {
2125 binforx[x] = int(leftBoundaryFrame / increment) + x; 1907 binforx[x] = int(leftBoundaryFrame / increment) + x;
2126 // cerr << "binforx[" << x << "] = " << binforx[x] << endl;
2127 } 1908 }
2128 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); 1909 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2129 } else { 1910 } else {
2130 for (int x = 0; x < bufwid; ++x) { 1911 for (int x = 0; x < bufwid; ++x) {
2131 double s0 = 0, s1 = 0; 1912 double s0 = 0, s1 = 0;
2133 binforx[x] = int(s0 + 0.0001); 1914 binforx[x] = int(s0 + 0.0001);
2134 } else { 1915 } else {
2135 binforx[x] = -1; //??? 1916 binforx[x] = -1; //???
2136 } 1917 }
2137 } 1918 }
2138 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) { 1919 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
2139 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); 1920 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2140 } 1921 }
2141 usePeaksCache = (increment * 8) < zoomLevel; 1922 usePeaksCache = (increment * 8) < zoomLevel;
2142 if (m_colourScale == PhaseColourScale) usePeaksCache = false; 1923 if (m_colourScale == PhaseColourScale) usePeaksCache = false;
2143 } 1924 }
2144 1925
2145 // No longer exists in Qt5: m_drawBuffer.setNumColors(256);
2146 for (int pixel = 0; pixel < 256; ++pixel) { 1926 for (int pixel = 0; pixel < 256; ++pixel) {
2147 m_drawBuffer.setColor((unsigned char)pixel, 1927 m_drawBuffer.setColor((unsigned char)pixel,
2148 m_palette.getColour((unsigned char)pixel).rgb()); 1928 m_palette.getColour((unsigned char)pixel).rgb());
2149 } 1929 }
2150 1930
2151 m_drawBuffer.fill(0); 1931 m_drawBuffer.fill(0);
2152 1932 int attainedBufwid = bufwid;
1933
1934 double softTimeLimit;
1935
1936 if (m_synchronous) {
1937
1938 // must paint the whole thing for synchronous mode, so give
1939 // "no timeout"
1940 softTimeLimit = 0.0;
1941
1942 } else if (bufferIsBinResolution) {
1943
1944 // calculating boundaries later will be too fiddly for partial
1945 // paints, and painting should be fast anyway when this is the
1946 // case because it means we're well zoomed in
1947 softTimeLimit = 0.0;
1948
1949 } else {
1950
1951 // neither limitation applies, so use a short soft limit
1952
1953 if (m_binDisplay == PeakFrequencies) {
1954 softTimeLimit = 0.15;
1955 } else {
1956 softTimeLimit = 0.1;
1957 }
1958 }
1959
2153 if (m_binDisplay != PeakFrequencies) { 1960 if (m_binDisplay != PeakFrequencies) {
2154 1961
2155 for (int y = 0; y < h; ++y) { 1962 for (int y = 0; y < h; ++y) {
2156 double q0 = 0, q1 = 0; 1963 double q0 = 0, q1 = 0;
2157 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { 1964 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
2158 binfory[y] = -1; 1965 binfory[y] = -1;
2159 } else { 1966 } else {
2160 binfory[y] = q0; 1967 binfory[y] = q0;
2161 // cerr << "binfory[" << y << "] = " << binfory[y] << endl;
2162 } 1968 }
2163 } 1969 }
2164 1970
2165 paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache, 1971 attainedBufwid =
2166 overallMag, overallMagChanged); 1972 paintDrawBuffer(v, bufwid, h, binforx, binfory,
1973 usePeaksCache,
1974 overallMag, overallMagChanged,
1975 rightToLeft,
1976 softTimeLimit);
2167 1977
2168 } else { 1978 } else {
2169 1979
2170 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, 1980 attainedBufwid =
2171 minbin, maxbin, 1981 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
2172 displayMinFreq, displayMaxFreq, 1982 minbin, maxbin,
2173 logarithmic, 1983 displayMinFreq, displayMaxFreq,
2174 overallMag, overallMagChanged); 1984 logarithmic,
2175 } 1985 overallMag, overallMagChanged,
2176 1986 rightToLeft,
2177 /* 1987 softTimeLimit);
2178 for (int x = 0; x < w / xPixelRatio; ++x) { 1988 }
2179 1989
2180 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); 1990 int failedToRepaint = bufwid - attainedBufwid;
2181 1991
2182 runOutOfData = !paintColumnValues(v, fft, x0, x, 1992 int paintedLeft = x0;
2183 minbin, maxbin, 1993 int paintedWidth = x1 - x0;
2184 displayMinFreq, displayMaxFreq, 1994
2185 xPixelRatio, 1995 if (failedToRepaint > 0) {
2186 h, yforbin); 1996
2187 1997 #ifdef DEBUG_SPECTROGRAM_REPAINT
2188 if (runOutOfData) { 1998 cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid
2189 #ifdef DEBUG_SPECTROGRAM_REPAINT 1999 << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl;
2190 cerr << "Run out of data -- dropping out of loop" << endl; 2000 #endif
2191 #endif 2001
2192 break; 2002 if (rightToLeft) {
2193 } 2003 paintedLeft += failedToRepaint;
2194 } 2004 }
2195 */ 2005
2196 #ifdef DEBUG_SPECTROGRAM_REPAINT 2006 paintedWidth -= failedToRepaint;
2197 // cerr << pixels << " pixels drawn" << endl; 2007
2198 #endif 2008 if (paintedWidth < 0) {
2009 paintedWidth = 0;
2010 }
2011
2012 } else if (failedToRepaint < 0) {
2013 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")"
2014 << endl;
2015 failedToRepaint = 0;
2016 }
2199 2017
2200 if (overallMagChanged) { 2018 if (overallMagChanged) {
2201 m_viewMags[v] = overallMag; 2019 m_viewMags[v->getId()] = overallMag;
2202 #ifdef DEBUG_SPECTROGRAM_REPAINT 2020 #ifdef DEBUG_SPECTROGRAM_REPAINT
2203 cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; 2021 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl;
2204 #endif
2205 } else {
2206 #ifdef DEBUG_SPECTROGRAM_REPAINT
2207 cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
2208 #endif 2022 #endif
2209 } 2023 }
2210 2024
2211 outerprof.end(); 2025 outerprof.end();
2212 2026
2213 Profiler profiler2("SpectrogramLayer::paint: draw image"); 2027 Profiler profiler2("SpectrogramLayer::paint: draw image");
2214 2028
2215 if (recreateWholeImageCache) { 2029 if (paintedWidth > 0) {
2216 #ifdef DEBUG_SPECTROGRAM_REPAINT 2030
2217 cerr << "Recreating image cache: width = " << v->getPaintWidth() 2031 #ifdef DEBUG_SPECTROGRAM_REPAINT
2218 << ", height = " << h << endl; 2032 cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h
2219 #endif 2033 << " from draw buffer at " << paintedLeft - x0 << "," << 0
2220 cache.image = QImage(v->getPaintWidth(), h, QImage::Format_ARGB32_Premultiplied); 2034 << " to " << paintedWidth << "x" << h << " on cache at "
2221 }
2222
2223 if (w > 0) {
2224 #ifdef DEBUG_SPECTROGRAM_REPAINT
2225 cerr << "Painting " << w << "x" << h
2226 << " from draw buffer at " << 0 << "," << 0
2227 << " to " << w << "x" << h << " on cache at "
2228 << x0 << "," << 0 << endl; 2035 << x0 << "," << 0 << endl;
2229 #endif 2036 #endif
2230 2037
2231 QPainter cachePainter(&cache.image); 2038 if (bufferIsBinResolution) {
2232 2039
2233 if (bufferBinResolution) {
2234 int scaledLeft = v->getXForFrame(leftBoundaryFrame); 2040 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2235 int scaledRight = v->getXForFrame(rightBoundaryFrame); 2041 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2236 #ifdef DEBUG_SPECTROGRAM_REPAINT 2042
2237 cerr << "Rescaling image from " << bufwid 2043 #ifdef DEBUG_SPECTROGRAM_REPAINT
2044 cerr << "SpectrogramLayer: Rescaling image from " << bufwid
2238 << "x" << h << " to " 2045 << "x" << h << " to "
2239 << scaledRight-scaledLeft << "x" << h << endl; 2046 << scaledRight-scaledLeft << "x" << h << endl;
2240 #endif 2047 #endif
2048
2241 Preferences::SpectrogramXSmoothing xsmoothing = 2049 Preferences::SpectrogramXSmoothing xsmoothing =
2242 Preferences::getInstance()->getSpectrogramXSmoothing(); 2050 Preferences::getInstance()->getSpectrogramXSmoothing();
2243 // SVDEBUG << "xsmoothing == " << xsmoothing << endl; 2051
2244 QImage scaled = m_drawBuffer.scaled 2052 QImage scaled = m_drawBuffer.scaled
2245 (scaledRight - scaledLeft, h, 2053 (scaledRight - scaledLeft, h,
2246 Qt::IgnoreAspectRatio, 2054 Qt::IgnoreAspectRatio,
2247 ((xsmoothing == Preferences::SpectrogramXInterpolated) ? 2055 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2248 Qt::SmoothTransformation : Qt::FastTransformation)); 2056 Qt::SmoothTransformation : Qt::FastTransformation));
2057
2249 int scaledLeftCrop = v->getXForFrame(leftCropFrame); 2058 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2250 int scaledRightCrop = v->getXForFrame(rightCropFrame); 2059 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2251 #ifdef DEBUG_SPECTROGRAM_REPAINT 2060
2252 cerr << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " 2061 #ifdef DEBUG_SPECTROGRAM_REPAINT
2062 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2253 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; 2063 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2254 #endif 2064 #endif
2255 cachePainter.drawImage 2065
2256 (QRect(scaledLeftCrop, 0, 2066 int targetLeft = scaledLeftCrop;
2257 scaledRightCrop - scaledLeftCrop, h), 2067 if (targetLeft < 0) {
2258 scaled, 2068 targetLeft = 0;
2259 QRect(scaledLeftCrop - scaledLeft, 0, 2069 }
2260 scaledRightCrop - scaledLeftCrop, h)); 2070
2071 int targetWidth = scaledRightCrop - targetLeft;
2072 if (targetLeft + targetWidth > cache.getSize().width()) {
2073 targetWidth = cache.getSize().width() - targetLeft;
2074 }
2075
2076 int sourceLeft = targetLeft - scaledLeft;
2077 if (sourceLeft < 0) {
2078 sourceLeft = 0;
2079 }
2080
2081 int sourceWidth = targetWidth;
2082
2083 if (targetWidth > 0) {
2084 cache.drawImage
2085 (targetLeft,
2086 targetWidth,
2087 scaled,
2088 sourceLeft,
2089 sourceWidth);
2090 }
2091
2261 } else { 2092 } else {
2262 cachePainter.drawImage(QRect(x0, 0, w, h), 2093
2263 m_drawBuffer, 2094 cache.drawImage(paintedLeft, paintedWidth,
2264 QRect(0, 0, w, h)); 2095 m_drawBuffer,
2265 } 2096 paintedLeft - x0, paintedWidth);
2266 2097 }
2267 cachePainter.end(); 2098 }
2268 } 2099
2269 2100 #ifdef DEBUG_SPECTROGRAM_REPAINT
2270 QRect pr = rect & cache.validArea; 2101 cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft()
2271 2102 << " width " << cache.getValidWidth() << ", height "
2272 #ifdef DEBUG_SPECTROGRAM_REPAINT 2103 << cache.getSize().height() << endl;
2273 cerr << "Painting " << pr.width() << "x" << pr.height() 2104 #endif
2105
2106 QRect pr = rect & cache.getValidArea();
2107
2108 #ifdef DEBUG_SPECTROGRAM_REPAINT
2109 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height()
2274 << " from cache at " << pr.x() << "," << pr.y() 2110 << " from cache at " << pr.x() << "," << pr.y()
2275 << " to window" << endl; 2111 << " to window" << endl;
2276 #endif 2112 #endif
2277 2113
2278 paint.drawImage(pr.x(), pr.y(), cache.image, 2114 paint.drawImage(pr.x(), pr.y(), cache.getImage(),
2279 pr.x(), pr.y(), pr.width(), pr.height()); 2115 pr.x(), pr.y(), pr.width(), pr.height());
2280 //!!!
2281 // paint.drawImage(v->rect(), cache.image,
2282 // QRect(QPoint(0, 0), cache.image.size()));
2283
2284 cache.startFrame = startFrame;
2285 cache.zoomLevel = zoomLevel;
2286 2116
2287 if (!m_synchronous) { 2117 if (!m_synchronous) {
2288 2118
2289 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { 2119 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) {
2290 2120
2291 if (cache.validArea.x() > 0) { 2121 QRect areaLeft(0, 0, cache.getValidLeft(), h);
2292 #ifdef DEBUG_SPECTROGRAM_REPAINT 2122 QRect areaRight(cache.getValidRight(), 0,
2293 cerr << "SpectrogramLayer::paint() updating left (0, " 2123 cache.getSize().width() - cache.getValidRight(), h);
2294 << cache.validArea.x() << ")" << endl; 2124
2295 #endif 2125 bool haveSpaceLeft = (areaLeft.width() > 0);
2296 v->getView()->update(0, 0, cache.validArea.x(), h); 2126 bool haveSpaceRight = (areaRight.width() > 0);
2127
2128 bool updateLeft = haveSpaceLeft;
2129 bool updateRight = haveSpaceRight;
2130
2131 if (updateLeft && updateRight) {
2132 if (rightToLeft) {
2133 // we just did something adjoining the cache on
2134 // its left side, so now do something on its right
2135 updateLeft = false;
2136 } else {
2137 updateRight = false;
2138 }
2297 } 2139 }
2298 2140
2299 if (cache.validArea.x() + cache.validArea.width() < 2141 if (updateLeft) {
2300 cache.image.width()) { 2142 #ifdef DEBUG_SPECTROGRAM_REPAINT
2143 cerr << "SpectrogramLayer::paint() updating left ("
2144 << areaLeft.x() << ", "
2145 << areaLeft.width() << ")" << endl;
2146 #endif
2147 v->updatePaintRect(areaLeft);
2148 }
2149
2150 if (updateRight) {
2301 #ifdef DEBUG_SPECTROGRAM_REPAINT 2151 #ifdef DEBUG_SPECTROGRAM_REPAINT
2302 cerr << "SpectrogramLayer::paint() updating right (" 2152 cerr << "SpectrogramLayer::paint() updating right ("
2303 << cache.validArea.x() + cache.validArea.width() 2153 << areaRight.x() << ", "
2304 << ", " 2154 << areaRight.width() << ")" << endl;
2305 << cache.image.width() - (cache.validArea.x() + 2155 #endif
2306 cache.validArea.width()) 2156 v->updatePaintRect(areaRight);
2307 << ")" << endl;
2308 #endif
2309 v->getView()->update(cache.validArea.x() + cache.validArea.width(),
2310 0,
2311 cache.image.width() - (cache.validArea.x() +
2312 cache.validArea.width()),
2313 h);
2314 } 2157 }
2158
2315 } else { 2159 } else {
2316 // overallMagChanged 2160 // overallMagChanged
2317 cerr << "\noverallMagChanged - updating all\n" << endl; 2161 cerr << "\noverallMagChanged - updating all\n" << endl;
2318 cache.validArea = QRect(); 2162 cache.invalidate();
2319 v->getView()->update(); 2163 v->updatePaintRect(v->getPaintRect());
2320 } 2164 }
2321 } 2165 }
2322 2166
2323 illuminateLocalFeatures(v, paint); 2167 illuminateLocalFeatures(v, paint);
2324 2168
2325 #ifdef DEBUG_SPECTROGRAM_REPAINT 2169 #ifdef DEBUG_SPECTROGRAM_REPAINT
2326 cerr << "SpectrogramLayer::paint() returning" << endl; 2170 cerr << "SpectrogramLayer::paint() returning" << endl;
2327 #endif 2171 #endif
2328 2172 }
2329 if (!m_synchronous) { 2173
2330 m_lastPaintBlockWidth = paintBlockWidth; 2174 int
2331 (void)gettimeofday(&tv, 0);
2332 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
2333 }
2334 }
2335
2336 bool
2337 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, 2175 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v,
2338 int w, 2176 int w,
2339 int h, 2177 int h,
2340 const vector<int> &binforx, 2178 const vector<int> &binforx,
2341 int minbin, 2179 int minbin,
2342 int maxbin, 2180 int maxbin,
2343 double displayMinFreq, 2181 double displayMinFreq,
2344 double displayMaxFreq, 2182 double displayMaxFreq,
2345 bool logarithmic, 2183 bool logarithmic,
2346 MagnitudeRange &overallMag, 2184 MagnitudeRange &overallMag,
2347 bool &overallMagChanged) const 2185 bool &overallMagChanged,
2186 bool rightToLeft,
2187 double softTimeLimit) const
2348 { 2188 {
2349 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); 2189 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
2350 2190
2351 #ifdef DEBUG_SPECTROGRAM_REPAINT 2191 #ifdef DEBUG_SPECTROGRAM_REPAINT
2352 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; 2192 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2353 #endif 2193 #endif
2354 if (minbin < 0) minbin = 0; 2194 if (minbin < 0) minbin = 0;
2355 if (maxbin < 0) maxbin = minbin+1; 2195 if (maxbin < 0) maxbin = minbin+1;
2356 2196
2357 FFTModel *fft = getFFTModel(v); 2197 FFTModel *fft = getFFTModel(v);
2358 if (!fft) return false; 2198 if (!fft) return 0;
2359 2199
2360 FFTModel::PeakSet peakfreqs; 2200 FFTModel::PeakSet peakfreqs;
2361 2201
2362 int psx = -1; 2202 int psx = -1;
2363 2203
2365 float values[maxbin - minbin + 1]; 2205 float values[maxbin - minbin + 1];
2366 #else 2206 #else
2367 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); 2207 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2368 #endif 2208 #endif
2369 2209
2370 for (int x = 0; x < w; ++x) { 2210 int minColumns = 4;
2211 bool haveTimeLimits = (softTimeLimit > 0.0);
2212 double hardTimeLimit = softTimeLimit * 2.0;
2213 bool overridingSoftLimit = false;
2214 auto startTime = chrono::steady_clock::now();
2215
2216 int start = 0;
2217 int finish = w;
2218 int step = 1;
2219
2220 if (rightToLeft) {
2221 start = w-1;
2222 finish = -1;
2223 step = -1;
2224 }
2225
2226 int columnCount = 0;
2227
2228 for (int x = start; x != finish; x += step) {
2229
2230 ++columnCount;
2371 2231
2372 if (binforx[x] < 0) continue; 2232 if (binforx[x] < 0) continue;
2373 2233
2374 int sx0 = binforx[x]; 2234 int sx0 = binforx[x];
2375 int sx1 = sx0; 2235 int sx1 = sx0;
2379 if (sx1 <= sx0) sx1 = sx0 + 1; 2239 if (sx1 <= sx0) sx1 = sx0 + 1;
2380 2240
2381 for (int sx = sx0; sx < sx1; ++sx) { 2241 for (int sx = sx0; sx < sx1; ++sx) {
2382 2242
2383 if (sx < 0 || sx >= int(fft->getWidth())) continue; 2243 if (sx < 0 || sx >= int(fft->getWidth())) continue;
2384
2385 if (!m_synchronous) {
2386 if (!fft->isColumnAvailable(sx)) {
2387 #ifdef DEBUG_SPECTROGRAM_REPAINT
2388 cerr << "Met unavailable column at col " << sx << endl;
2389 #endif
2390 return false;
2391 }
2392 }
2393 2244
2394 MagnitudeRange mag; 2245 MagnitudeRange mag;
2395 2246
2396 if (sx != psx) { 2247 if (sx != psx) {
2397 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, 2248 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2454 m_columnMags[sx].sample(mag); 2305 m_columnMags[sx].sample(mag);
2455 if (overallMag.sample(mag)) overallMagChanged = true; 2306 if (overallMag.sample(mag)) overallMagChanged = true;
2456 } 2307 }
2457 } 2308 }
2458 } 2309 }
2459 } 2310
2460 2311 if (haveTimeLimits) {
2461 return true; 2312 if (columnCount >= minColumns) {
2462 } 2313 auto t = chrono::steady_clock::now();
2463 2314 double diff = chrono::duration<double>(t - startTime).count();
2464 bool 2315 if (diff > hardTimeLimit) {
2316 #ifdef DEBUG_SPECTROGRAM_REPAINT
2317 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: hard limit " << hardTimeLimit << " sec exceeded after "
2318 << columnCount << " columns with time " << diff << endl;
2319 #endif
2320 return columnCount;
2321 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2322 // If we're more than half way through by the time
2323 // we reach the soft limit, ignore it (though
2324 // still respect the hard limit, above). Otherwise
2325 // respect the soft limit and return now.
2326 if (columnCount > w/2) {
2327 overridingSoftLimit = true;
2328 } else {
2329 #ifdef DEBUG_SPECTROGRAM_REPAINT
2330 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: soft limit " << softTimeLimit << " sec exceeded after "
2331 << columnCount << " columns with time " << diff << endl;
2332 #endif
2333 return columnCount;
2334 }
2335 }
2336 }
2337 }
2338 }
2339
2340 return columnCount;
2341 }
2342
2343 int
2465 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v, 2344 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
2466 int w, 2345 int w,
2467 int h, 2346 int h,
2468 const vector<int> &binforx, 2347 const vector<int> &binforx,
2469 const vector<double> &binfory, 2348 const vector<double> &binfory,
2470 bool usePeaksCache, 2349 bool usePeaksCache,
2471 MagnitudeRange &overallMag, 2350 MagnitudeRange &overallMag,
2472 bool &overallMagChanged) const 2351 bool &overallMagChanged,
2352 bool rightToLeft,
2353 double softTimeLimit) const
2473 { 2354 {
2474 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); 2355 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2475 2356
2476 int minbin = int(binfory[0] + 0.0001); 2357 int minbin = int(binfory[0] + 0.0001);
2477 int maxbin = int(binfory[h-1]); 2358 int maxbin = int(binfory[h-1]);
2478 2359
2479 #ifdef DEBUG_SPECTROGRAM_REPAINT 2360 #ifdef DEBUG_SPECTROGRAM_REPAINT
2480 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; 2361 cerr << "SpectrogramLayer::paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2481 #endif 2362 #endif
2482 if (minbin < 0) minbin = 0; 2363 if (minbin < 0) minbin = 0;
2483 if (maxbin < 0) maxbin = minbin+1; 2364 if (maxbin < 0) maxbin = minbin+1;
2484 2365
2485 DenseThreeDimensionalModel *sourceModel = 0; 2366 DenseThreeDimensionalModel *sourceModel = 0;
2486 FFTModel *fft = 0; 2367 FFTModel *fft = 0;
2487 int divisor = 1; 2368 int divisor = 1;
2488 #ifdef DEBUG_SPECTROGRAM_REPAINT 2369 #ifdef DEBUG_SPECTROGRAM_REPAINT
2489 cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; 2370 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2490 #endif 2371 #endif
2491 if (usePeaksCache) { //!!! 2372 if (usePeaksCache) { //!!!
2492 sourceModel = getPeakCache(v); 2373 sourceModel = getPeakCache(v);
2493 divisor = 8;//!!! 2374 divisor = 8;//!!!
2494 minbin = 0; 2375 minbin = 0;
2495 maxbin = sourceModel->getHeight(); 2376 maxbin = sourceModel->getHeight();
2496 } else { 2377 } else {
2497 sourceModel = fft = getFFTModel(v); 2378 sourceModel = fft = getFFTModel(v);
2498 } 2379 }
2499 2380
2500 if (!sourceModel) return false; 2381 if (!sourceModel) return 0;
2501 2382
2502 bool interpolate = false; 2383 bool interpolate = false;
2503 Preferences::SpectrogramSmoothing smoothing = 2384 Preferences::SpectrogramSmoothing smoothing =
2504 Preferences::getInstance()->getSpectrogramSmoothing(); 2385 Preferences::getInstance()->getSpectrogramSmoothing();
2505 if (smoothing == Preferences::SpectrogramInterpolated || 2386 if (smoothing == Preferences::SpectrogramInterpolated ||
2521 #endif 2402 #endif
2522 2403
2523 const float *values = autoarray; 2404 const float *values = autoarray;
2524 DenseThreeDimensionalModel::Column c; 2405 DenseThreeDimensionalModel::Column c;
2525 2406
2526 for (int x = 0; x < w; ++x) { 2407 int minColumns = 4;
2408 bool haveTimeLimits = (softTimeLimit > 0.0);
2409 double hardTimeLimit = softTimeLimit * 2.0;
2410 bool overridingSoftLimit = false;
2411 auto startTime = chrono::steady_clock::now();
2412
2413 int start = 0;
2414 int finish = w;
2415 int step = 1;
2416
2417 if (rightToLeft) {
2418 start = w-1;
2419 finish = -1;
2420 step = -1;
2421 }
2422
2423 int columnCount = 0;
2424
2425 for (int x = start; x != finish; x += step) {
2426
2427 ++columnCount;
2527 2428
2528 if (binforx[x] < 0) continue; 2429 if (binforx[x] < 0) continue;
2529 2430
2530 // float columnGain = m_gain; 2431 // float columnGain = m_gain;
2531 float columnMax = 0.f; 2432 float columnMax = 0.f;
2545 // cerr << "sx = " << sx << endl; 2446 // cerr << "sx = " << sx << endl;
2546 #endif 2447 #endif
2547 2448
2548 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; 2449 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue;
2549 2450
2550 if (!m_synchronous) {
2551 if (!sourceModel->isColumnAvailable(sx)) {
2552 #ifdef DEBUG_SPECTROGRAM_REPAINT
2553 cerr << "Met unavailable column at col " << sx << endl;
2554 #endif
2555 return false;
2556 }
2557 }
2558
2559 MagnitudeRange mag; 2451 MagnitudeRange mag;
2560 2452
2561 if (sx != psx) { 2453 if (sx != psx) {
2562 if (fft) { 2454 if (fft) {
2563 #ifdef DEBUG_SPECTROGRAM_REPAINT 2455 #ifdef DEBUG_SPECTROGRAM_REPAINT
2564 cerr << "Retrieving column " << sx << " from fft directly" << endl; 2456 // cerr << "Retrieving column " << sx << " from fft directly" << endl;
2565 #endif 2457 #endif
2566 if (m_colourScale == PhaseColourScale) { 2458 if (m_colourScale == PhaseColourScale) {
2567 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2459 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2568 } else if (m_normalization == NormalizeColumns) { 2460 } else if (m_normalization == NormalizeColumns) {
2569 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2461 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2577 } else { 2469 } else {
2578 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); 2470 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2579 } 2471 }
2580 } else { 2472 } else {
2581 #ifdef DEBUG_SPECTROGRAM_REPAINT 2473 #ifdef DEBUG_SPECTROGRAM_REPAINT
2582 cerr << "Retrieving column " << sx << " from peaks cache" << endl; 2474 // cerr << "Retrieving column " << sx << " from peaks cache" << endl;
2583 #endif 2475 #endif
2584 c = sourceModel->getColumn(sx); 2476 c = sourceModel->getColumn(sx);
2585 if (m_normalization == NormalizeColumns || 2477 if (m_normalization == NormalizeColumns ||
2586 m_normalization == NormalizeHybrid) { 2478 m_normalization == NormalizeHybrid) {
2587 for (int y = 0; y < h; ++y) { 2479 for (int y = 0; y < h; ++y) {
2588 if (c[y] > columnMax) columnMax = c[y]; 2480 if (c[y] > columnMax) columnMax = c[y];
2589 } 2481 }
2590 } 2482 }
2591 values = c.constData() + minbin; 2483 values = c.data() + minbin;
2592 } 2484 }
2593 psx = sx; 2485 psx = sx;
2594 } 2486 }
2595 2487
2596 for (int y = 0; y < h; ++y) { 2488 for (int y = 0; y < h; ++y) {
2698 2590
2699 unsigned char peakpix = getDisplayValue(v, peak); 2591 unsigned char peakpix = getDisplayValue(v, peak);
2700 2592
2701 m_drawBuffer.setPixel(x, h-y-1, peakpix); 2593 m_drawBuffer.setPixel(x, h-y-1, peakpix);
2702 } 2594 }
2703 } 2595
2704 2596 if (haveTimeLimits) {
2705 return true; 2597 if (columnCount >= minColumns) {
2598 auto t = chrono::steady_clock::now();
2599 double diff = chrono::duration<double>(t - startTime).count();
2600 if (diff > hardTimeLimit) {
2601 #ifdef DEBUG_SPECTROGRAM_REPAINT
2602 cerr << "SpectrogramLayer::paintDrawBuffer: hard limit " << hardTimeLimit << " sec exceeded after "
2603 << columnCount << " columns with time " << diff << endl;
2604 #endif
2605 return columnCount;
2606 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2607 // If we're more than half way through by the time
2608 // we reach the soft limit, ignore it (though
2609 // still respect the hard limit, above). Otherwise
2610 // respect the soft limit and return now.
2611 if (columnCount > w/2) {
2612 overridingSoftLimit = true;
2613 } else {
2614 #ifdef DEBUG_SPECTROGRAM_REPAINT
2615 cerr << "SpectrogramLayer::paintDrawBuffer: soft limit " << softTimeLimit << " sec exceeded after "
2616 << columnCount << " columns with time " << diff << endl;
2617 #endif
2618 return columnCount;
2619 }
2620 }
2621 }
2622 }
2623 }
2624
2625 return columnCount;
2706 } 2626 }
2707 2627
2708 void 2628 void
2709 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const 2629 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
2710 { 2630 {
2765 int 2685 int
2766 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const 2686 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const
2767 { 2687 {
2768 const View *view = v->getView(); 2688 const View *view = v->getView();
2769 2689
2770 if (m_fftModels.find(view) == m_fftModels.end()) return 100; 2690 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return 100;
2771 2691
2772 int completion = m_fftModels[view]->getCompletion(); 2692 int completion = m_fftModels[view->getId()]->getCompletion();
2773 #ifdef DEBUG_SPECTROGRAM_REPAINT 2693 #ifdef DEBUG_SPECTROGRAM_REPAINT
2774 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl; 2694 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
2775 #endif 2695 #endif
2776 return completion; 2696 return completion;
2777 } 2697 }
2778 2698
2779 QString 2699 QString
2780 SpectrogramLayer::getError(LayerGeometryProvider *v) const 2700 SpectrogramLayer::getError(LayerGeometryProvider *v) const
2781 { 2701 {
2782 const View *view = v->getView(); 2702 const View *view = v->getView();
2783 if (m_fftModels.find(view) == m_fftModels.end()) return ""; 2703 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return "";
2784 return m_fftModels[view]->getError(); 2704 return m_fftModels[view->getId()]->getError();
2785 } 2705 }
2786 2706
2787 bool 2707 bool
2788 SpectrogramLayer::getValueExtents(double &min, double &max, 2708 SpectrogramLayer::getValueExtents(double &min, double &max,
2789 bool &logarithmic, QString &unit) const 2709 bool &logarithmic, QString &unit) const
2875 2795
2876 void 2796 void
2877 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) 2797 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
2878 { 2798 {
2879 const View *view = v->getView(); 2799 const View *view = v->getView();
2880 ImageCache &cache = m_imageCaches[view]; 2800 ScrollableImageCache &cache = getImageCacheReference(view);
2881 2801
2882 cerr << "cache width: " << cache.image.width() << ", height: " 2802 cerr << "cache width: " << cache.getSize().width() << ", height: "
2883 << cache.image.height() << endl; 2803 << cache.getSize().height() << endl;
2884 2804
2885 QImage image = cache.image; 2805 QImage image = cache.getImage();
2886 2806
2887 ImageRegionFinder finder; 2807 ImageRegionFinder finder;
2888 QRect rect = finder.findRegionExtents(&image, e->pos()); 2808 QRect rect = finder.findRegionExtents(&image, e->pos());
2889 if (rect.isValid()) { 2809 if (rect.isValid()) {
2890 MeasureRect mr; 2810 MeasureRect mr;
2895 } 2815 }
2896 2816
2897 bool 2817 bool
2898 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, 2818 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
2899 QPoint cursorPos, 2819 QPoint cursorPos,
2900 std::vector<QRect> &extents) const 2820 vector<QRect> &extents) const
2901 { 2821 {
2902 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); 2822 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight());
2903 extents.push_back(vertical); 2823 extents.push_back(vertical);
2904 2824
2905 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); 2825 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
3195 int ch = h - textHeight * (topLines + 1) - 8; 3115 int ch = h - textHeight * (topLines + 1) - 8;
3196 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); 3116 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
3197 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); 3117 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3198 3118
3199 QString top, bottom; 3119 QString top, bottom;
3200 double min = m_viewMags[v].getMin(); 3120 double min = m_viewMags[v->getId()].getMin();
3201 double max = m_viewMags[v].getMax(); 3121 double max = m_viewMags[v->getId()].getMax();
3202 3122
3203 double dBmin = AudioLevel::multiplier_to_dB(min); 3123 double dBmin = AudioLevel::multiplier_to_dB(min);
3204 double dBmax = AudioLevel::multiplier_to_dB(max); 3124 double dBmax = AudioLevel::multiplier_to_dB(max);
3205 3125
3206 if (dBmax < -60.f) dBmax = -60.f; 3126 if (dBmax < -60.f) dBmax = -60.f;
3295 QString text = QString("%1").arg(freq); 3215 QString text = QString("%1").arg(freq);
3296 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC 3216 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
3297 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); 3217 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
3298 3218
3299 if (h - vy - textHeight >= -2) { 3219 if (h - vy - textHeight >= -2) {
3300 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); 3220 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw);
3301 paint.drawText(tx, h - vy + toff, text); 3221 paint.drawText(tx, h - vy + toff, text);
3302 } 3222 }
3303 3223
3304 py = vy; 3224 py = vy;
3305 } 3225 }