Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 1045:f535f6e5dbb0 alignment-simple
Merge in from SV 3.0-integration branches
| author | Chris Cannam |
|---|---|
| date | Wed, 02 Mar 2016 17:25:27 +0000 |
| parents | 4e5c1c326794 |
| children | 5e5873c24142 |
comparison
equal
deleted
inserted
replaced
| 976:f2c63ec85901 | 1045:f535f6e5dbb0 |
|---|---|
| 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> |
| 46 | 46 |
| 47 #ifndef __GNUC__ | 47 #ifndef __GNUC__ |
| 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), |
| 71 m_initialMaxFrequency(8000), | 71 m_initialMaxFrequency(8000), |
| 72 m_colourScale(dBColourScale), | 72 m_colourScale(dBColourScale), |
| 73 m_colourMap(0), | 73 m_colourMap(0), |
| 74 m_frequencyScale(LinearFrequencyScale), | 74 m_frequencyScale(LinearFrequencyScale), |
| 75 m_binDisplay(AllBins), | 75 m_binDisplay(AllBins), |
| 76 m_normalizeColumns(false), | 76 m_normalization(NoNormalization), |
| 77 m_normalizeVisibleArea(false), | |
| 78 m_normalizeHybrid(false), | |
| 79 m_lastEmittedZoomStep(-1), | 77 m_lastEmittedZoomStep(-1), |
| 80 m_synchronous(false), | 78 m_synchronous(false), |
| 81 m_haveDetailedScale(false), | 79 m_haveDetailedScale(false), |
| 82 m_lastPaintBlockWidth(0), | |
| 83 m_updateTimer(0), | |
| 84 m_candidateFillStartFrame(0), | |
| 85 m_exiting(false), | 80 m_exiting(false), |
| 86 m_sliceableModel(0) | 81 m_sliceableModel(0) |
| 87 { | 82 { |
| 83 QString colourConfigName = "spectrogram-colour"; | |
| 84 int colourConfigDefault = int(ColourMapper::Green); | |
| 85 | |
| 88 if (config == FullRangeDb) { | 86 if (config == FullRangeDb) { |
| 89 m_initialMaxFrequency = 0; | 87 m_initialMaxFrequency = 0; |
| 90 setMaxFrequency(0); | 88 setMaxFrequency(0); |
| 91 } else if (config == MelodicRange) { | 89 } else if (config == MelodicRange) { |
| 92 setWindowSize(8192); | 90 setWindowSize(8192); |
| 95 setMaxFrequency(1500); | 93 setMaxFrequency(1500); |
| 96 setMinFrequency(40); | 94 setMinFrequency(40); |
| 97 setColourScale(LinearColourScale); | 95 setColourScale(LinearColourScale); |
| 98 setColourMap(ColourMapper::Sunset); | 96 setColourMap(ColourMapper::Sunset); |
| 99 setFrequencyScale(LogFrequencyScale); | 97 setFrequencyScale(LogFrequencyScale); |
| 98 colourConfigName = "spectrogram-melodic-colour"; | |
| 99 colourConfigDefault = int(ColourMapper::Sunset); | |
| 100 // setGain(20); | 100 // setGain(20); |
| 101 } else if (config == MelodicPeaks) { | 101 } else if (config == MelodicPeaks) { |
| 102 setWindowSize(4096); | 102 setWindowSize(4096); |
| 103 setWindowHopLevel(5); | 103 setWindowHopLevel(5); |
| 104 m_initialMaxFrequency = 2000; | 104 m_initialMaxFrequency = 2000; |
| 105 setMaxFrequency(2000); | 105 setMaxFrequency(2000); |
| 106 setMinFrequency(40); | 106 setMinFrequency(40); |
| 107 setFrequencyScale(LogFrequencyScale); | 107 setFrequencyScale(LogFrequencyScale); |
| 108 setColourScale(LinearColourScale); | 108 setColourScale(LinearColourScale); |
| 109 setBinDisplay(PeakFrequencies); | 109 setBinDisplay(PeakFrequencies); |
| 110 setNormalizeColumns(true); | 110 setNormalization(NormalizeColumns); |
| 111 } | 111 colourConfigName = "spectrogram-melodic-colour"; |
| 112 | 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 | |
| 113 Preferences *prefs = Preferences::getInstance(); | 120 Preferences *prefs = Preferences::getInstance(); |
| 114 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | 121 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), |
| 115 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); | 122 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); |
| 116 setWindowType(prefs->getWindowType()); | 123 setWindowType(prefs->getWindowType()); |
| 117 | 124 |
| 118 initialisePalette(); | 125 initialisePalette(); |
| 119 } | 126 } |
| 120 | 127 |
| 121 SpectrogramLayer::~SpectrogramLayer() | 128 SpectrogramLayer::~SpectrogramLayer() |
| 122 { | 129 { |
| 123 delete m_updateTimer; | |
| 124 m_updateTimer = 0; | |
| 125 | |
| 126 invalidateFFTModels(); | 130 invalidateFFTModels(); |
| 127 } | 131 } |
| 128 | 132 |
| 129 void | 133 void |
| 130 SpectrogramLayer::setModel(const DenseTimeValueModel *model) | 134 SpectrogramLayer::setModel(const DenseTimeValueModel *model) |
| 153 PropertyList list; | 157 PropertyList list; |
| 154 list.push_back("Colour"); | 158 list.push_back("Colour"); |
| 155 list.push_back("Colour Scale"); | 159 list.push_back("Colour Scale"); |
| 156 list.push_back("Window Size"); | 160 list.push_back("Window Size"); |
| 157 list.push_back("Window Increment"); | 161 list.push_back("Window Increment"); |
| 158 list.push_back("Normalize Columns"); | 162 list.push_back("Normalization"); |
| 159 list.push_back("Normalize Visible Area"); | |
| 160 list.push_back("Bin Display"); | 163 list.push_back("Bin Display"); |
| 161 list.push_back("Threshold"); | 164 list.push_back("Threshold"); |
| 162 list.push_back("Gain"); | 165 list.push_back("Gain"); |
| 163 list.push_back("Colour Rotation"); | 166 list.push_back("Colour Rotation"); |
| 164 // list.push_back("Min Frequency"); | 167 // list.push_back("Min Frequency"); |
| 173 { | 176 { |
| 174 if (name == "Colour") return tr("Colour"); | 177 if (name == "Colour") return tr("Colour"); |
| 175 if (name == "Colour Scale") return tr("Colour Scale"); | 178 if (name == "Colour Scale") return tr("Colour Scale"); |
| 176 if (name == "Window Size") return tr("Window Size"); | 179 if (name == "Window Size") return tr("Window Size"); |
| 177 if (name == "Window Increment") return tr("Window Overlap"); | 180 if (name == "Window Increment") return tr("Window Overlap"); |
| 178 if (name == "Normalize Columns") return tr("Normalize Columns"); | 181 if (name == "Normalization") return tr("Normalization"); |
| 179 if (name == "Normalize Visible Area") return tr("Normalize Visible Area"); | |
| 180 if (name == "Bin Display") return tr("Bin Display"); | 182 if (name == "Bin Display") return tr("Bin Display"); |
| 181 if (name == "Threshold") return tr("Threshold"); | 183 if (name == "Threshold") return tr("Threshold"); |
| 182 if (name == "Gain") return tr("Gain"); | 184 if (name == "Gain") return tr("Gain"); |
| 183 if (name == "Colour Rotation") return tr("Colour Rotation"); | 185 if (name == "Colour Rotation") return tr("Colour Rotation"); |
| 184 if (name == "Min Frequency") return tr("Min Frequency"); | 186 if (name == "Min Frequency") return tr("Min Frequency"); |
| 187 if (name == "Zero Padding") return tr("Smoothing"); | 189 if (name == "Zero Padding") return tr("Smoothing"); |
| 188 return ""; | 190 return ""; |
| 189 } | 191 } |
| 190 | 192 |
| 191 QString | 193 QString |
| 192 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const | 194 SpectrogramLayer::getPropertyIconName(const PropertyName &) const |
| 193 { | 195 { |
| 194 if (name == "Normalize Columns") return "normalise-columns"; | |
| 195 if (name == "Normalize Visible Area") return "normalise"; | |
| 196 return ""; | 196 return ""; |
| 197 } | 197 } |
| 198 | 198 |
| 199 Layer::PropertyType | 199 Layer::PropertyType |
| 200 SpectrogramLayer::getPropertyType(const PropertyName &name) const | 200 SpectrogramLayer::getPropertyType(const PropertyName &name) const |
| 201 { | 201 { |
| 202 if (name == "Gain") return RangeProperty; | 202 if (name == "Gain") return RangeProperty; |
| 203 if (name == "Colour Rotation") return RangeProperty; | 203 if (name == "Colour Rotation") return RangeProperty; |
| 204 if (name == "Normalize Columns") return ToggleProperty; | |
| 205 if (name == "Normalize Visible Area") return ToggleProperty; | |
| 206 if (name == "Threshold") return RangeProperty; | 204 if (name == "Threshold") return RangeProperty; |
| 207 if (name == "Zero Padding") return ToggleProperty; | 205 if (name == "Zero Padding") return ToggleProperty; |
| 208 return ValueProperty; | 206 return ValueProperty; |
| 209 } | 207 } |
| 210 | 208 |
| 217 name == "Window Increment" || | 215 name == "Window Increment" || |
| 218 name == "Zero Padding") return tr("Window"); | 216 name == "Zero Padding") return tr("Window"); |
| 219 if (name == "Colour" || | 217 if (name == "Colour" || |
| 220 name == "Threshold" || | 218 name == "Threshold" || |
| 221 name == "Colour Rotation") return tr("Colour"); | 219 name == "Colour Rotation") return tr("Colour"); |
| 222 if (name == "Normalize Columns" || | 220 if (name == "Normalization" || |
| 223 name == "Normalize Visible Area" || | |
| 224 name == "Gain" || | 221 name == "Gain" || |
| 225 name == "Colour Scale") return tr("Scale"); | 222 name == "Colour Scale") return tr("Scale"); |
| 226 return QString(); | 223 return QString(); |
| 227 } | 224 } |
| 228 | 225 |
| 363 *min = 0; | 360 *min = 0; |
| 364 *max = 2; | 361 *max = 2; |
| 365 *deflt = int(AllBins); | 362 *deflt = int(AllBins); |
| 366 val = (int)m_binDisplay; | 363 val = (int)m_binDisplay; |
| 367 | 364 |
| 368 } else if (name == "Normalize Columns") { | 365 } else if (name == "Normalization") { |
| 369 | 366 |
| 370 *deflt = 0; | 367 *min = 0; |
| 371 val = (m_normalizeColumns ? 1 : 0); | 368 *max = 3; |
| 372 | 369 *deflt = int(NoNormalization); |
| 373 } else if (name == "Normalize Visible Area") { | 370 val = (int)m_normalization; |
| 374 | |
| 375 *deflt = 0; | |
| 376 val = (m_normalizeVisibleArea ? 1 : 0); | |
| 377 | 371 |
| 378 } else { | 372 } else { |
| 379 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); | 373 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); |
| 380 } | 374 } |
| 381 | 375 |
| 396 case 1: return tr("Meter"); | 390 case 1: return tr("Meter"); |
| 397 case 2: return tr("dBV^2"); | 391 case 2: return tr("dBV^2"); |
| 398 case 3: return tr("dBV"); | 392 case 3: return tr("dBV"); |
| 399 case 4: return tr("Phase"); | 393 case 4: return tr("Phase"); |
| 400 } | 394 } |
| 395 } | |
| 396 if (name == "Normalization") { | |
| 397 return ""; // icon only | |
| 401 } | 398 } |
| 402 if (name == "Window Size") { | 399 if (name == "Window Size") { |
| 403 return QString("%1").arg(32 << value); | 400 return QString("%1").arg(32 << value); |
| 404 } | 401 } |
| 405 if (name == "Window Increment") { | 402 if (name == "Window Increment") { |
| 461 case 1: return tr("Peak Bins"); | 458 case 1: return tr("Peak Bins"); |
| 462 case 2: return tr("Frequencies"); | 459 case 2: return tr("Frequencies"); |
| 463 } | 460 } |
| 464 } | 461 } |
| 465 return tr("<unknown>"); | 462 return tr("<unknown>"); |
| 463 } | |
| 464 | |
| 465 QString | |
| 466 SpectrogramLayer::getPropertyValueIconName(const PropertyName &name, | |
| 467 int value) const | |
| 468 { | |
| 469 if (name == "Normalization") { | |
| 470 switch(value) { | |
| 471 default: | |
| 472 case 0: return "normalise-none"; | |
| 473 case 1: return "normalise-columns"; | |
| 474 case 2: return "normalise"; | |
| 475 case 3: return "normalise-hybrid"; | |
| 476 } | |
| 477 } | |
| 478 return ""; | |
| 466 } | 479 } |
| 467 | 480 |
| 468 RangeMapper * | 481 RangeMapper * |
| 469 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const | 482 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const |
| 470 { | 483 { |
| 553 default: | 566 default: |
| 554 case 0: setBinDisplay(AllBins); break; | 567 case 0: setBinDisplay(AllBins); break; |
| 555 case 1: setBinDisplay(PeakBins); break; | 568 case 1: setBinDisplay(PeakBins); break; |
| 556 case 2: setBinDisplay(PeakFrequencies); break; | 569 case 2: setBinDisplay(PeakFrequencies); break; |
| 557 } | 570 } |
| 558 } else if (name == "Normalize Columns") { | 571 } else if (name == "Normalization") { |
| 559 setNormalizeColumns(value ? true : false); | 572 switch (value) { |
| 560 } else if (name == "Normalize Visible Area") { | 573 default: |
| 561 setNormalizeVisibleArea(value ? true : false); | 574 case 0: setNormalization(NoNormalization); break; |
| 575 case 1: setNormalization(NormalizeColumns); break; | |
| 576 case 2: setNormalization(NormalizeVisibleArea); break; | |
| 577 case 3: setNormalization(NormalizeHybrid); break; | |
| 578 } | |
| 562 } | 579 } |
| 563 } | 580 } |
| 564 | 581 |
| 565 void | 582 void |
| 566 SpectrogramLayer::invalidateImageCaches() | 583 SpectrogramLayer::invalidateImageCaches() |
| 567 { | 584 { |
| 585 #ifdef DEBUG_SPECTROGRAM | |
| 586 cerr << "SpectrogramLayer::invalidateImageCaches called" << endl; | |
| 587 #endif | |
| 568 for (ViewImageCache::iterator i = m_imageCaches.begin(); | 588 for (ViewImageCache::iterator i = m_imageCaches.begin(); |
| 569 i != m_imageCaches.end(); ++i) { | 589 i != m_imageCaches.end(); ++i) { |
| 570 i->second.validArea = QRect(); | 590 i->second.invalidate(); |
| 571 } | |
| 572 } | |
| 573 | |
| 574 void | |
| 575 SpectrogramLayer::invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame) | |
| 576 { | |
| 577 for (ViewImageCache::iterator i = m_imageCaches.begin(); | |
| 578 i != m_imageCaches.end(); ++i) { | |
| 579 | |
| 580 //!!! when are views removed from the map? on setLayerDormant? | |
| 581 const View *v = i->first; | |
| 582 | |
| 583 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 584 SVDEBUG << "SpectrogramLayer::invalidateImageCaches(" | |
| 585 << startFrame << ", " << endFrame << "): view range is " | |
| 586 << v->getStartFrame() << ", " << v->getEndFrame() | |
| 587 << endl; | |
| 588 | |
| 589 cerr << "Valid area was: " << i->second.validArea.x() << ", " | |
| 590 << i->second.validArea.y() << " " | |
| 591 << i->second.validArea.width() << "x" | |
| 592 << i->second.validArea.height() << endl; | |
| 593 #endif | |
| 594 | |
| 595 if (int(startFrame) > v->getStartFrame()) { | |
| 596 if (startFrame >= v->getEndFrame()) { | |
| 597 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 598 cerr << "Modified start frame is off right of view" << endl; | |
| 599 #endif | |
| 600 return; | |
| 601 } | |
| 602 int x = v->getXForFrame(startFrame); | |
| 603 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 604 SVDEBUG << "clipping from 0 to " << x-1 << endl; | |
| 605 #endif | |
| 606 if (x > 1) { | |
| 607 i->second.validArea &= | |
| 608 QRect(0, 0, x-1, v->height()); | |
| 609 } else { | |
| 610 i->second.validArea = QRect(); | |
| 611 } | |
| 612 } else { | |
| 613 if (int(endFrame) < v->getStartFrame()) { | |
| 614 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 615 cerr << "Modified end frame is off left of view" << endl; | |
| 616 #endif | |
| 617 return; | |
| 618 } | |
| 619 int x = v->getXForFrame(endFrame); | |
| 620 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 621 SVDEBUG << "clipping from " << x+1 << " to " << v->width() | |
| 622 << endl; | |
| 623 #endif | |
| 624 if (x < v->width()) { | |
| 625 i->second.validArea &= | |
| 626 QRect(x+1, 0, v->width()-(x+1), v->height()); | |
| 627 } else { | |
| 628 i->second.validArea = QRect(); | |
| 629 } | |
| 630 } | |
| 631 | |
| 632 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 633 cerr << "Valid area is now: " << i->second.validArea.x() << ", " | |
| 634 << i->second.validArea.y() << " " | |
| 635 << i->second.validArea.width() << "x" | |
| 636 << i->second.validArea.height() << endl; | |
| 637 #endif | |
| 638 } | 591 } |
| 639 } | 592 } |
| 640 | 593 |
| 641 void | 594 void |
| 642 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) | 595 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) |
| 932 { | 885 { |
| 933 return m_binDisplay; | 886 return m_binDisplay; |
| 934 } | 887 } |
| 935 | 888 |
| 936 void | 889 void |
| 937 SpectrogramLayer::setNormalizeColumns(bool n) | 890 SpectrogramLayer::setNormalization(Normalization n) |
| 938 { | 891 { |
| 939 if (m_normalizeColumns == n) return; | 892 if (m_normalization == n) return; |
| 940 | 893 |
| 941 invalidateImageCaches(); | 894 invalidateImageCaches(); |
| 942 invalidateMagnitudes(); | 895 invalidateMagnitudes(); |
| 943 m_normalizeColumns = n; | 896 m_normalization = n; |
| 944 | 897 |
| 945 emit layerParametersChanged(); | 898 emit layerParametersChanged(); |
| 946 } | 899 } |
| 947 | 900 |
| 948 bool | 901 SpectrogramLayer::Normalization |
| 949 SpectrogramLayer::getNormalizeColumns() const | 902 SpectrogramLayer::getNormalization() const |
| 950 { | 903 { |
| 951 return m_normalizeColumns; | 904 return m_normalization; |
| 952 } | 905 } |
| 953 | 906 |
| 954 void | 907 void |
| 955 SpectrogramLayer::setNormalizeHybrid(bool n) | 908 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) |
| 956 { | |
| 957 if (m_normalizeHybrid == n) return; | |
| 958 | |
| 959 invalidateImageCaches(); | |
| 960 invalidateMagnitudes(); | |
| 961 m_normalizeHybrid = n; | |
| 962 | |
| 963 emit layerParametersChanged(); | |
| 964 } | |
| 965 | |
| 966 bool | |
| 967 SpectrogramLayer::getNormalizeHybrid() const | |
| 968 { | |
| 969 return m_normalizeHybrid; | |
| 970 } | |
| 971 | |
| 972 void | |
| 973 SpectrogramLayer::setNormalizeVisibleArea(bool n) | |
| 974 { | |
| 975 SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n | |
| 976 << ") (from " << m_normalizeVisibleArea << ")" << endl; | |
| 977 | |
| 978 if (m_normalizeVisibleArea == n) return; | |
| 979 | |
| 980 invalidateImageCaches(); | |
| 981 invalidateMagnitudes(); | |
| 982 m_normalizeVisibleArea = n; | |
| 983 | |
| 984 emit layerParametersChanged(); | |
| 985 } | |
| 986 | |
| 987 bool | |
| 988 SpectrogramLayer::getNormalizeVisibleArea() const | |
| 989 { | |
| 990 return m_normalizeVisibleArea; | |
| 991 } | |
| 992 | |
| 993 void | |
| 994 SpectrogramLayer::setLayerDormant(const View *v, bool dormant) | |
| 995 { | 909 { |
| 996 if (dormant) { | 910 if (dormant) { |
| 997 | 911 |
| 998 #ifdef DEBUG_SPECTROGRAM_REPAINT | 912 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 999 SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")" | 913 cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")" |
| 1000 << endl; | 914 << endl; |
| 1001 #endif | 915 #endif |
| 1002 | 916 |
| 1003 if (isLayerDormant(v)) { | 917 if (isLayerDormant(v)) { |
| 1004 return; | 918 return; |
| 1005 } | 919 } |
| 1006 | 920 |
| 1007 Layer::setLayerDormant(v, true); | 921 Layer::setLayerDormant(v, true); |
| 1008 | 922 |
| 923 const View *view = v->getView(); | |
| 924 | |
| 1009 invalidateImageCaches(); | 925 invalidateImageCaches(); |
| 1010 m_imageCaches.erase(v); | 926 |
| 1011 | 927 m_imageCaches.erase(view->getId()); |
| 1012 if (m_fftModels.find(v) != m_fftModels.end()) { | 928 |
| 1013 | 929 if (m_fftModels.find(view->getId()) != m_fftModels.end()) { |
| 1014 if (m_sliceableModel == m_fftModels[v].first) { | 930 |
| 931 if (m_sliceableModel == m_fftModels[view->getId()]) { | |
| 1015 bool replaced = false; | 932 bool replaced = false; |
| 1016 for (ViewFFTMap::iterator i = m_fftModels.begin(); | 933 for (ViewFFTMap::iterator i = m_fftModels.begin(); |
| 1017 i != m_fftModels.end(); ++i) { | 934 i != m_fftModels.end(); ++i) { |
| 1018 if (i->second.first != m_sliceableModel) { | 935 if (i->second != m_sliceableModel) { |
| 1019 emit sliceableModelReplaced(m_sliceableModel, i->second.first); | 936 emit sliceableModelReplaced(m_sliceableModel, i->second); |
| 1020 replaced = true; | 937 replaced = true; |
| 1021 break; | 938 break; |
| 1022 } | 939 } |
| 1023 } | 940 } |
| 1024 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); | 941 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0); |
| 1025 } | 942 } |
| 1026 | 943 |
| 1027 delete m_fftModels[v].first; | 944 delete m_fftModels[view->getId()]; |
| 1028 m_fftModels.erase(v); | 945 m_fftModels.erase(view->getId()); |
| 1029 | 946 |
| 1030 delete m_peakCaches[v]; | 947 delete m_peakCaches[view->getId()]; |
| 1031 m_peakCaches.erase(v); | 948 m_peakCaches.erase(view->getId()); |
| 1032 } | 949 } |
| 1033 | 950 |
| 1034 } else { | 951 } else { |
| 1035 | 952 |
| 1036 Layer::setLayerDormant(v, false); | 953 Layer::setLayerDormant(v, false); |
| 1039 | 956 |
| 1040 void | 957 void |
| 1041 SpectrogramLayer::cacheInvalid() | 958 SpectrogramLayer::cacheInvalid() |
| 1042 { | 959 { |
| 1043 #ifdef DEBUG_SPECTROGRAM_REPAINT | 960 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1044 SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl; | 961 cerr << "SpectrogramLayer::cacheInvalid()" << endl; |
| 1045 #endif | 962 #endif |
| 1046 | 963 |
| 1047 invalidateImageCaches(); | 964 invalidateImageCaches(); |
| 1048 invalidateMagnitudes(); | 965 invalidateMagnitudes(); |
| 1049 } | 966 } |
| 1050 | 967 |
| 1051 void | 968 void |
| 1052 SpectrogramLayer::cacheInvalid(sv_frame_t from, sv_frame_t to) | 969 SpectrogramLayer::cacheInvalid( |
| 1053 { | 970 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1054 #ifdef DEBUG_SPECTROGRAM_REPAINT | 971 sv_frame_t from, sv_frame_t to |
| 1055 SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; | 972 #else |
| 1056 #endif | 973 sv_frame_t , sv_frame_t |
| 1057 | 974 #endif |
| 1058 invalidateImageCaches(from, to); | 975 ) |
| 976 { | |
| 977 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 978 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; | |
| 979 #endif | |
| 980 | |
| 981 // We used to call invalidateMagnitudes(from, to) to invalidate | |
| 982 // only those caches whose views contained some of the (from, to) | |
| 983 // range. That's the right thing to do; it has been lost in | |
| 984 // pulling out the image cache code, but it might not matter very | |
| 985 // much, since the underlying models for spectrogram layers don't | |
| 986 // change very often. Let's see. | |
| 987 invalidateImageCaches(); | |
| 1059 invalidateMagnitudes(); | 988 invalidateMagnitudes(); |
| 1060 } | |
| 1061 | |
| 1062 void | |
| 1063 SpectrogramLayer::fillTimerTimedOut() | |
| 1064 { | |
| 1065 if (!m_model) return; | |
| 1066 | |
| 1067 bool allDone = true; | |
| 1068 | |
| 1069 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1070 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl; | |
| 1071 #endif | |
| 1072 | |
| 1073 for (ViewFFTMap::iterator i = m_fftModels.begin(); | |
| 1074 i != m_fftModels.end(); ++i) { | |
| 1075 | |
| 1076 const FFTModel *model = i->second.first; | |
| 1077 sv_frame_t lastFill = i->second.second; | |
| 1078 | |
| 1079 if (model) { | |
| 1080 | |
| 1081 sv_frame_t fill = model->getFillExtent(); | |
| 1082 | |
| 1083 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1084 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl; | |
| 1085 #endif | |
| 1086 | |
| 1087 if (fill >= lastFill) { | |
| 1088 if (fill >= m_model->getEndFrame() && lastFill > 0) { | |
| 1089 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1090 cerr << "complete!" << endl; | |
| 1091 #endif | |
| 1092 invalidateImageCaches(); | |
| 1093 i->second.second = -1; | |
| 1094 emit modelChanged(); | |
| 1095 | |
| 1096 } else if (fill > lastFill) { | |
| 1097 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1098 cerr << "SpectrogramLayer: emitting modelChanged(" | |
| 1099 << lastFill << "," << fill << ")" << endl; | |
| 1100 #endif | |
| 1101 invalidateImageCaches(lastFill, fill); | |
| 1102 i->second.second = fill; | |
| 1103 emit modelChangedWithin(lastFill, fill); | |
| 1104 } | |
| 1105 } else { | |
| 1106 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1107 cerr << "SpectrogramLayer: going backwards, emitting modelChanged(" | |
| 1108 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl; | |
| 1109 #endif | |
| 1110 invalidateImageCaches(); | |
| 1111 i->second.second = fill; | |
| 1112 emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame()); | |
| 1113 } | |
| 1114 | |
| 1115 if (i->second.second >= 0) { | |
| 1116 allDone = false; | |
| 1117 } | |
| 1118 } | |
| 1119 } | |
| 1120 | |
| 1121 if (allDone) { | |
| 1122 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1123 cerr << "SpectrogramLayer: all complete!" << endl; | |
| 1124 #endif | |
| 1125 delete m_updateTimer; | |
| 1126 m_updateTimer = 0; | |
| 1127 } | |
| 1128 } | 989 } |
| 1129 | 990 |
| 1130 bool | 991 bool |
| 1131 SpectrogramLayer::hasLightBackground() const | 992 SpectrogramLayer::hasLightBackground() const |
| 1132 { | 993 { |
| 1179 | 1040 |
| 1180 m_drawBuffer = QImage(); | 1041 m_drawBuffer = QImage(); |
| 1181 } | 1042 } |
| 1182 | 1043 |
| 1183 unsigned char | 1044 unsigned char |
| 1184 SpectrogramLayer::getDisplayValue(View *v, double input) const | 1045 SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const |
| 1185 { | 1046 { |
| 1186 int value; | 1047 int value; |
| 1187 | 1048 |
| 1188 double min = 0.0; | 1049 double min = 0.0; |
| 1189 double max = 1.0; | 1050 double max = 1.0; |
| 1190 | 1051 |
| 1191 if (m_normalizeVisibleArea) { | 1052 if (m_normalization == NormalizeVisibleArea) { |
| 1192 min = m_viewMags[v].getMin(); | 1053 min = m_viewMags[v->getId()].getMin(); |
| 1193 max = m_viewMags[v].getMax(); | 1054 max = m_viewMags[v->getId()].getMax(); |
| 1194 } else if (!m_normalizeColumns) { | 1055 } else if (m_normalization != NormalizeColumns) { |
| 1195 if (m_colourScale == LinearColourScale //|| | 1056 if (m_colourScale == LinearColourScale //|| |
| 1196 // m_colourScale == MeterColourScale) { | 1057 // m_colourScale == MeterColourScale) { |
| 1197 ) { | 1058 ) { |
| 1198 max = 0.1; | 1059 max = 0.1; |
| 1199 } | 1060 } |
| 1292 | 1153 |
| 1293 return maxf; | 1154 return maxf; |
| 1294 } | 1155 } |
| 1295 | 1156 |
| 1296 bool | 1157 bool |
| 1297 SpectrogramLayer::getYBinRange(View *v, int y, double &q0, double &q1) const | 1158 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const |
| 1298 { | 1159 { |
| 1299 Profiler profiler("SpectrogramLayer::getYBinRange"); | 1160 Profiler profiler("SpectrogramLayer::getYBinRange"); |
| 1300 | 1161 |
| 1301 int h = v->height(); | 1162 int h = v->getPaintHeight(); |
| 1302 if (y < 0 || y >= h) return false; | 1163 if (y < 0 || y >= h) return false; |
| 1303 | 1164 |
| 1304 sv_samplerate_t sr = m_model->getSampleRate(); | 1165 sv_samplerate_t sr = m_model->getSampleRate(); |
| 1305 double minf = getEffectiveMinFrequency(); | 1166 double minf = getEffectiveMinFrequency(); |
| 1306 double maxf = getEffectiveMaxFrequency(); | 1167 double maxf = getEffectiveMaxFrequency(); |
| 1318 | 1179 |
| 1319 return true; | 1180 return true; |
| 1320 } | 1181 } |
| 1321 | 1182 |
| 1322 bool | 1183 bool |
| 1323 SpectrogramLayer::getSmoothedYBinRange(View *v, int y, double &q0, double &q1) const | 1184 SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const |
| 1324 { | 1185 { |
| 1325 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange"); | 1186 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange"); |
| 1326 | 1187 |
| 1327 int h = v->height(); | 1188 int h = v->getPaintHeight(); |
| 1328 if (y < 0 || y >= h) return false; | 1189 if (y < 0 || y >= h) return false; |
| 1329 | 1190 |
| 1330 sv_samplerate_t sr = m_model->getSampleRate(); | 1191 sv_samplerate_t sr = m_model->getSampleRate(); |
| 1331 double minf = getEffectiveMinFrequency(); | 1192 double minf = getEffectiveMinFrequency(); |
| 1332 double maxf = getEffectiveMaxFrequency(); | 1193 double maxf = getEffectiveMaxFrequency(); |
| 1344 | 1205 |
| 1345 return true; | 1206 return true; |
| 1346 } | 1207 } |
| 1347 | 1208 |
| 1348 bool | 1209 bool |
| 1349 SpectrogramLayer::getXBinRange(View *v, int x, double &s0, double &s1) const | 1210 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const |
| 1350 { | 1211 { |
| 1351 sv_frame_t modelStart = m_model->getStartFrame(); | 1212 sv_frame_t modelStart = m_model->getStartFrame(); |
| 1352 sv_frame_t modelEnd = m_model->getEndFrame(); | 1213 sv_frame_t modelEnd = m_model->getEndFrame(); |
| 1353 | 1214 |
| 1354 // Each pixel column covers an exact range of sample frames: | 1215 // Each pixel column covers an exact range of sample frames: |
| 1368 | 1229 |
| 1369 return true; | 1230 return true; |
| 1370 } | 1231 } |
| 1371 | 1232 |
| 1372 bool | 1233 bool |
| 1373 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const | 1234 SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const |
| 1374 { | 1235 { |
| 1375 double s0 = 0, s1 = 0; | 1236 double s0 = 0, s1 = 0; |
| 1376 if (!getXBinRange(v, x, s0, s1)) return false; | 1237 if (!getXBinRange(v, x, s0, s1)) return false; |
| 1377 | 1238 |
| 1378 int s0i = int(s0 + 0.001); | 1239 int s0i = int(s0 + 0.001); |
| 1387 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); | 1248 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); |
| 1388 return true; | 1249 return true; |
| 1389 } | 1250 } |
| 1390 | 1251 |
| 1391 bool | 1252 bool |
| 1392 SpectrogramLayer::getYBinSourceRange(View *v, int y, double &freqMin, double &freqMax) | 1253 SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) |
| 1393 const | 1254 const |
| 1394 { | 1255 { |
| 1395 double q0 = 0, q1 = 0; | 1256 double q0 = 0, q1 = 0; |
| 1396 if (!getYBinRange(v, y, q0, q1)) return false; | 1257 if (!getYBinRange(v, y, q0, q1)) return false; |
| 1397 | 1258 |
| 1406 } | 1267 } |
| 1407 return true; | 1268 return true; |
| 1408 } | 1269 } |
| 1409 | 1270 |
| 1410 bool | 1271 bool |
| 1411 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y, | 1272 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, |
| 1412 double &freqMin, double &freqMax, | 1273 double &freqMin, double &freqMax, |
| 1413 double &adjFreqMin, double &adjFreqMax) | 1274 double &adjFreqMin, double &adjFreqMax) |
| 1414 const | 1275 const |
| 1415 { | 1276 { |
| 1416 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1277 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
| 1440 m_binDisplay == PeakFrequencies); | 1301 m_binDisplay == PeakFrequencies); |
| 1441 | 1302 |
| 1442 for (int q = q0i; q <= q1i; ++q) { | 1303 for (int q = q0i; q <= q1i; ++q) { |
| 1443 | 1304 |
| 1444 for (int s = s0i; s <= s1i; ++s) { | 1305 for (int s = s0i; s <= s1i; ++s) { |
| 1445 | |
| 1446 if (!fft->isColumnAvailable(s)) continue; | |
| 1447 | 1306 |
| 1448 double binfreq = (double(sr) * q) / m_windowSize; | 1307 double binfreq = (double(sr) * q) / m_windowSize; |
| 1449 if (q == q0i) freqMin = binfreq; | 1308 if (q == q0i) freqMin = binfreq; |
| 1450 if (q == q1i) freqMax = binfreq; | 1309 if (q == q1i) freqMax = binfreq; |
| 1451 | 1310 |
| 1473 | 1332 |
| 1474 return haveAdj; | 1333 return haveAdj; |
| 1475 } | 1334 } |
| 1476 | 1335 |
| 1477 bool | 1336 bool |
| 1478 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y, | 1337 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, |
| 1479 double &min, double &max, | 1338 double &min, double &max, |
| 1480 double &phaseMin, double &phaseMax) const | 1339 double &phaseMin, double &phaseMax) const |
| 1481 { | 1340 { |
| 1482 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1341 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
| 1483 return false; | 1342 return false; |
| 1516 | 1375 |
| 1517 for (int q = q0i; q <= q1i; ++q) { | 1376 for (int q = q0i; q <= q1i; ++q) { |
| 1518 for (int s = s0i; s <= s1i; ++s) { | 1377 for (int s = s0i; s <= s1i; ++s) { |
| 1519 if (s >= 0 && q >= 0 && s < cw && q < ch) { | 1378 if (s >= 0 && q >= 0 && s < cw && q < ch) { |
| 1520 | 1379 |
| 1521 if (!fft->isColumnAvailable(s)) continue; | |
| 1522 | |
| 1523 double value; | 1380 double value; |
| 1524 | 1381 |
| 1525 value = fft->getPhaseAt(s, q); | 1382 value = fft->getPhaseAt(s, q); |
| 1526 if (!have || value < phaseMin) { phaseMin = value; } | 1383 if (!have || value < phaseMin) { phaseMin = value; } |
| 1527 if (!have || value > phaseMax) { phaseMax = value; } | 1384 if (!have || value > phaseMax) { phaseMax = value; } |
| 1542 | 1399 |
| 1543 return rv; | 1400 return rv; |
| 1544 } | 1401 } |
| 1545 | 1402 |
| 1546 int | 1403 int |
| 1547 SpectrogramLayer::getZeroPadLevel(const View *v) const | 1404 SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const |
| 1548 { | 1405 { |
| 1549 //!!! tidy all this stuff | 1406 //!!! tidy all this stuff |
| 1550 | 1407 |
| 1551 if (m_binDisplay != AllBins) return 0; | 1408 if (m_binDisplay != AllBins) return 0; |
| 1552 | 1409 |
| 1572 if (minbin < 1) minbin = 1; | 1429 if (minbin < 1) minbin = 1; |
| 1573 if (minbin >= maxbin) minbin = maxbin - 1; | 1430 if (minbin >= maxbin) minbin = maxbin - 1; |
| 1574 } | 1431 } |
| 1575 | 1432 |
| 1576 double perPixel = | 1433 double perPixel = |
| 1577 double(v->height()) / | 1434 double(v->getPaintHeight()) / |
| 1578 double((maxbin - minbin) / (m_zeroPadLevel + 1)); | 1435 double((maxbin - minbin) / (m_zeroPadLevel + 1)); |
| 1579 | 1436 |
| 1580 if (perPixel > 2.8) { | 1437 if (perPixel > 2.8) { |
| 1581 return 3; // 4x oversampling | 1438 return 3; // 4x oversampling |
| 1582 } else if (perPixel > 1.5) { | 1439 } else if (perPixel > 1.5) { |
| 1585 return 0; // 1x | 1442 return 0; // 1x |
| 1586 } | 1443 } |
| 1587 } | 1444 } |
| 1588 | 1445 |
| 1589 int | 1446 int |
| 1590 SpectrogramLayer::getFFTSize(const View *v) const | 1447 SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const |
| 1591 { | 1448 { |
| 1592 return m_fftSize * (getZeroPadLevel(v) + 1); | 1449 return m_fftSize * (getZeroPadLevel(v) + 1); |
| 1593 } | 1450 } |
| 1594 | 1451 |
| 1595 FFTModel * | 1452 FFTModel * |
| 1596 SpectrogramLayer::getFFTModel(const View *v) const | 1453 SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const |
| 1597 { | 1454 { |
| 1598 if (!m_model) return 0; | 1455 if (!m_model) return 0; |
| 1599 | 1456 |
| 1600 int fftSize = getFFTSize(v); | 1457 int fftSize = getFFTSize(v); |
| 1601 | 1458 |
| 1602 if (m_fftModels.find(v) != m_fftModels.end()) { | 1459 const View *view = v->getView(); |
| 1603 if (m_fftModels[v].first == 0) { | 1460 |
| 1604 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1461 if (m_fftModels.find(view->getId()) != m_fftModels.end()) { |
| 1605 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; | 1462 if (m_fftModels[view->getId()] == 0) { |
| 1463 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1464 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; | |
| 1606 #endif | 1465 #endif |
| 1607 return 0; | 1466 return 0; |
| 1608 } | 1467 } |
| 1609 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) { | 1468 if (m_fftModels[view->getId()]->getHeight() != fftSize / 2 + 1) { |
| 1610 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1469 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1611 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; | 1470 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view->getId()]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; |
| 1612 #endif | 1471 #endif |
| 1613 delete m_fftModels[v].first; | 1472 delete m_fftModels[view->getId()]; |
| 1614 m_fftModels.erase(v); | 1473 m_fftModels.erase(view->getId()); |
| 1615 delete m_peakCaches[v]; | 1474 delete m_peakCaches[view->getId()]; |
| 1616 m_peakCaches.erase(v); | 1475 m_peakCaches.erase(view->getId()); |
| 1617 } else { | 1476 } else { |
| 1618 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1477 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1619 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl; | 1478 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view->getId()]->getHeight() << endl; |
| 1620 #endif | 1479 #endif |
| 1621 return m_fftModels[v].first; | 1480 return m_fftModels[view->getId()]; |
| 1622 } | 1481 } |
| 1623 } | 1482 } |
| 1624 | 1483 |
| 1625 if (m_fftModels.find(v) == m_fftModels.end()) { | 1484 if (m_fftModels.find(view->getId()) == m_fftModels.end()) { |
| 1626 | 1485 |
| 1627 FFTModel *model = new FFTModel(m_model, | 1486 FFTModel *model = new FFTModel(m_model, |
| 1628 m_channel, | 1487 m_channel, |
| 1629 m_windowType, | 1488 m_windowType, |
| 1630 m_windowSize, | 1489 m_windowSize, |
| 1635 QMessageBox::critical | 1494 QMessageBox::critical |
| 1636 (0, tr("FFT cache failed"), | 1495 (0, tr("FFT cache failed"), |
| 1637 tr("Failed to create the FFT model for this spectrogram.\n" | 1496 tr("Failed to create the FFT model for this spectrogram.\n" |
| 1638 "There may be insufficient memory or disc space to continue.")); | 1497 "There may be insufficient memory or disc space to continue.")); |
| 1639 delete model; | 1498 delete model; |
| 1640 m_fftModels[v] = FFTFillPair(0, 0); | 1499 m_fftModels[view->getId()] = 0; |
| 1641 return 0; | 1500 return 0; |
| 1642 } | 1501 } |
| 1643 | 1502 |
| 1644 if (!m_sliceableModel) { | 1503 if (!m_sliceableModel) { |
| 1645 #ifdef DEBUG_SPECTROGRAM | 1504 #ifdef DEBUG_SPECTROGRAM |
| 1647 #endif | 1506 #endif |
| 1648 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); | 1507 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model); |
| 1649 m_sliceableModel = model; | 1508 m_sliceableModel = model; |
| 1650 } | 1509 } |
| 1651 | 1510 |
| 1652 m_fftModels[v] = FFTFillPair(model, 0); | 1511 m_fftModels[view->getId()] = model; |
| 1653 | 1512 } |
| 1654 delete m_updateTimer; | 1513 |
| 1655 m_updateTimer = new QTimer((SpectrogramLayer *)this); | 1514 return m_fftModels[view->getId()]; |
| 1656 connect(m_updateTimer, SIGNAL(timeout()), | |
| 1657 this, SLOT(fillTimerTimedOut())); | |
| 1658 m_updateTimer->start(200); | |
| 1659 } | |
| 1660 | |
| 1661 return m_fftModels[v].first; | |
| 1662 } | 1515 } |
| 1663 | 1516 |
| 1664 Dense3DModelPeakCache * | 1517 Dense3DModelPeakCache * |
| 1665 SpectrogramLayer::getPeakCache(const View *v) const | 1518 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const |
| 1666 { | 1519 { |
| 1667 if (!m_peakCaches[v]) { | 1520 const View *view = v->getView(); |
| 1521 if (!m_peakCaches[view->getId()]) { | |
| 1668 FFTModel *f = getFFTModel(v); | 1522 FFTModel *f = getFFTModel(v); |
| 1669 if (!f) return 0; | 1523 if (!f) return 0; |
| 1670 m_peakCaches[v] = new Dense3DModelPeakCache(f, 8); | 1524 m_peakCaches[view->getId()] = new Dense3DModelPeakCache(f, 8); |
| 1671 } | 1525 } |
| 1672 return m_peakCaches[v]; | 1526 return m_peakCaches[view->getId()]; |
| 1673 } | 1527 } |
| 1674 | 1528 |
| 1675 const Model * | 1529 const Model * |
| 1676 SpectrogramLayer::getSliceableModel() const | 1530 SpectrogramLayer::getSliceableModel() const |
| 1677 { | 1531 { |
| 1678 if (m_sliceableModel) return m_sliceableModel; | 1532 if (m_sliceableModel) return m_sliceableModel; |
| 1679 if (m_fftModels.empty()) return 0; | 1533 if (m_fftModels.empty()) return 0; |
| 1680 m_sliceableModel = m_fftModels.begin()->second.first; | 1534 m_sliceableModel = m_fftModels.begin()->second; |
| 1681 return m_sliceableModel; | 1535 return m_sliceableModel; |
| 1682 } | 1536 } |
| 1683 | 1537 |
| 1684 void | 1538 void |
| 1685 SpectrogramLayer::invalidateFFTModels() | 1539 SpectrogramLayer::invalidateFFTModels() |
| 1686 { | 1540 { |
| 1541 #ifdef DEBUG_SPECTROGRAM | |
| 1542 cerr << "SpectrogramLayer::invalidateFFTModels called" << endl; | |
| 1543 #endif | |
| 1687 for (ViewFFTMap::iterator i = m_fftModels.begin(); | 1544 for (ViewFFTMap::iterator i = m_fftModels.begin(); |
| 1688 i != m_fftModels.end(); ++i) { | 1545 i != m_fftModels.end(); ++i) { |
| 1689 delete i->second.first; | 1546 delete i->second; |
| 1690 } | 1547 } |
| 1691 for (PeakCacheMap::iterator i = m_peakCaches.begin(); | 1548 for (PeakCacheMap::iterator i = m_peakCaches.begin(); |
| 1692 i != m_peakCaches.end(); ++i) { | 1549 i != m_peakCaches.end(); ++i) { |
| 1693 delete i->second; | 1550 delete i->second; |
| 1694 } | 1551 } |
| 1704 } | 1561 } |
| 1705 | 1562 |
| 1706 void | 1563 void |
| 1707 SpectrogramLayer::invalidateMagnitudes() | 1564 SpectrogramLayer::invalidateMagnitudes() |
| 1708 { | 1565 { |
| 1566 #ifdef DEBUG_SPECTROGRAM | |
| 1567 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl; | |
| 1568 #endif | |
| 1709 m_viewMags.clear(); | 1569 m_viewMags.clear(); |
| 1710 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); | 1570 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin(); |
| 1711 i != m_columnMags.end(); ++i) { | 1571 i != m_columnMags.end(); ++i) { |
| 1712 *i = MagnitudeRange(); | 1572 *i = MagnitudeRange(); |
| 1713 } | 1573 } |
| 1714 } | 1574 } |
| 1715 | 1575 |
| 1716 bool | 1576 bool |
| 1717 SpectrogramLayer::updateViewMagnitudes(View *v) const | 1577 SpectrogramLayer::updateViewMagnitudes(LayerGeometryProvider *v) const |
| 1718 { | 1578 { |
| 1719 MagnitudeRange mag; | 1579 MagnitudeRange mag; |
| 1720 | 1580 |
| 1721 int x0 = 0, x1 = v->width(); | 1581 int x0 = 0, x1 = v->getPaintWidth(); |
| 1722 double s00 = 0, s01 = 0, s10 = 0, s11 = 0; | 1582 double s00 = 0, s01 = 0, s10 = 0, s11 = 0; |
| 1723 | 1583 |
| 1724 if (!getXBinRange(v, x0, s00, s01)) { | 1584 if (!getXBinRange(v, x0, s00, s01)) { |
| 1725 s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); | 1585 s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); |
| 1726 } | 1586 } |
| 1727 | 1587 |
| 1728 if (!getXBinRange(v, x1, s10, s11)) { | 1588 if (!getXBinRange(v, x1, s10, s11)) { |
| 1729 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); | 1589 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); |
| 1730 } | 1590 } |
| 1731 | 1591 |
| 1732 int s0 = int(std::min(s00, s10) + 0.0001); | 1592 int s0 = int(min(s00, s10) + 0.0001); |
| 1733 int s1 = int(std::max(s01, s11) + 0.0001); | 1593 int s1 = int(max(s01, s11) + 0.0001); |
| 1734 | 1594 |
| 1735 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; | 1595 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; |
| 1736 | 1596 |
| 1737 if (int(m_columnMags.size()) <= s1) { | 1597 if (int(m_columnMags.size()) <= s1) { |
| 1738 m_columnMags.resize(s1 + 1); | 1598 m_columnMags.resize(s1 + 1); |
| 1743 mag.sample(m_columnMags[s]); | 1603 mag.sample(m_columnMags[s]); |
| 1744 } | 1604 } |
| 1745 } | 1605 } |
| 1746 | 1606 |
| 1747 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1607 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1748 SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols " | 1608 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " |
| 1749 << s0 << " -> " << s1 << " inclusive" << endl; | 1609 << s0 << " -> " << s1 << " inclusive" << endl; |
| 1610 cerr << "SpectrogramLayer::updateViewMagnitudes: for view id " << v->getId() | |
| 1611 << ": min is " << mag.getMin() << ", max is " << mag.getMax() << endl; | |
| 1750 #endif | 1612 #endif |
| 1751 | 1613 |
| 1752 if (!mag.isSet()) return false; | 1614 if (!mag.isSet()) return false; |
| 1753 if (mag == m_viewMags[v]) return false; | 1615 if (mag == m_viewMags[v->getId()]) return false; |
| 1754 m_viewMags[v] = mag; | 1616 m_viewMags[v->getId()] = mag; |
| 1755 return true; | 1617 return true; |
| 1756 } | 1618 } |
| 1757 | 1619 |
| 1758 void | 1620 void |
| 1759 SpectrogramLayer::setSynchronousPainting(bool synchronous) | 1621 SpectrogramLayer::setSynchronousPainting(bool synchronous) |
| 1760 { | 1622 { |
| 1761 m_synchronous = synchronous; | 1623 m_synchronous = synchronous; |
| 1762 } | 1624 } |
| 1763 | 1625 |
| 1626 ScrollableImageCache & | |
| 1627 SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const | |
| 1628 { | |
| 1629 if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) { | |
| 1630 m_imageCaches[view->getId()] = ScrollableImageCache(view); | |
| 1631 } | |
| 1632 return m_imageCaches.at(view->getId()); | |
| 1633 } | |
| 1634 | |
| 1764 void | 1635 void |
| 1765 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const | 1636 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const |
| 1766 { | 1637 { |
| 1767 // What a lovely, old-fashioned function this is. | |
| 1768 // It's practically FORTRAN 77 in its clarity and linearity. | |
| 1769 | |
| 1770 Profiler profiler("SpectrogramLayer::paint", false); | 1638 Profiler profiler("SpectrogramLayer::paint", false); |
| 1771 | 1639 |
| 1772 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1640 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1773 SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl; | 1641 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; |
| 1774 | 1642 |
| 1775 cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; | 1643 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; |
| 1776 #endif | 1644 #endif |
| 1777 | 1645 |
| 1778 sv_frame_t startFrame = v->getStartFrame(); | 1646 sv_frame_t startFrame = v->getStartFrame(); |
| 1779 if (startFrame < 0) m_candidateFillStartFrame = 0; | |
| 1780 else m_candidateFillStartFrame = startFrame; | |
| 1781 | 1647 |
| 1782 if (!m_model || !m_model->isOK() || !m_model->isReady()) { | 1648 if (!m_model || !m_model->isOK() || !m_model->isReady()) { |
| 1783 return; | 1649 return; |
| 1784 } | 1650 } |
| 1785 | 1651 |
| 1793 // in the cache-fill thread above. | 1659 // in the cache-fill thread above. |
| 1794 //!!! no inter use cache-fill thread | 1660 //!!! no inter use cache-fill thread |
| 1795 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); | 1661 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false); |
| 1796 | 1662 |
| 1797 int fftSize = getFFTSize(v); | 1663 int fftSize = getFFTSize(v); |
| 1798 /* | 1664 |
| 1799 FFTModel *fft = getFFTModel(v); | 1665 const View *view = v->getView(); |
| 1800 if (!fft) { | 1666 ScrollableImageCache &cache = getImageCacheReference(view); |
| 1801 cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl; | 1667 |
| 1802 return; | 1668 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1803 } | 1669 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl; |
| 1804 */ | 1670 if (rect.x() + rect.width() + 1 < cache.getValidLeft() || |
| 1805 ImageCache &cache = m_imageCaches[v]; | 1671 rect.x() > cache.getValidRight()) { |
| 1806 | 1672 cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl; |
| 1807 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1673 } |
| 1808 SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache. | |
| 1809 | |
| 1810 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl; | |
| 1811 #endif | |
| 1812 | |
| 1813 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1814 bool stillCacheing = (m_updateTimer != 0); | |
| 1815 SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl; | |
| 1816 #endif | 1674 #endif |
| 1817 | 1675 |
| 1818 int zoomLevel = v->getZoomLevel(); | 1676 int zoomLevel = v->getZoomLevel(); |
| 1819 | 1677 |
| 1820 int x0 = 0; | 1678 int x0 = v->getXForViewX(rect.x()); |
| 1821 int x1 = v->width(); | 1679 int x1 = v->getXForViewX(rect.x() + rect.width()); |
| 1822 | 1680 if (x0 < 0) x0 = 0; |
| 1823 bool recreateWholeImageCache = true; | 1681 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth(); |
| 1824 | 1682 |
| 1825 x0 = rect.left(); | 1683 if (updateViewMagnitudes(v)) { |
| 1826 x1 = rect.right() + 1; | 1684 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1827 /* | 1685 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl; |
| 1828 double xPixelRatio = double(fft->getResolution()) / double(zoomLevel); | 1686 #endif |
| 1829 cerr << "xPixelRatio = " << xPixelRatio << endl; | 1687 if (m_normalization == NormalizeVisibleArea) { |
| 1830 if (xPixelRatio < 1.f) xPixelRatio = 1.f; | 1688 cache.invalidate(); |
| 1831 */ | 1689 } |
| 1832 if (cache.validArea.width() > 0) { | 1690 } |
| 1833 | 1691 |
| 1834 int cw = cache.image.width(); | 1692 if (cache.getZoomLevel() != zoomLevel || |
| 1835 int ch = cache.image.height(); | 1693 cache.getSize() != v->getPaintSize()) { |
| 1694 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1695 cerr << "SpectrogramLayer: resizing image cache from " | |
| 1696 << cache.getSize().width() << "x" << cache.getSize().height() | |
| 1697 << " to " | |
| 1698 << v->getPaintSize().width() << "x" << v->getPaintSize().height() | |
| 1699 << " and updating zoom level from " << cache.getZoomLevel() | |
| 1700 << " to " << zoomLevel | |
| 1701 << endl; | |
| 1702 #endif | |
| 1703 cache.resize(v->getPaintSize()); | |
| 1704 cache.setZoomLevel(zoomLevel); | |
| 1705 cache.setStartFrame(startFrame); | |
| 1706 } | |
| 1707 | |
| 1708 if (cache.isValid()) { | |
| 1836 | 1709 |
| 1837 if (int(cache.zoomLevel) == zoomLevel && | 1710 if (v->getXForFrame(cache.getStartFrame()) == |
| 1838 cw == v->width() && | 1711 v->getXForFrame(startFrame) && |
| 1839 ch == v->height()) { | 1712 cache.getValidLeft() <= x0 && |
| 1840 | 1713 cache.getValidRight() >= x1) { |
| 1841 if (v->getXForFrame(cache.startFrame) == | 1714 |
| 1842 v->getXForFrame(startFrame) && | 1715 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1843 cache.validArea.x() <= x0 && | 1716 cerr << "SpectrogramLayer: image cache hit!" << endl; |
| 1844 cache.validArea.x() + cache.validArea.width() >= x1) { | 1717 #endif |
| 1845 | 1718 |
| 1846 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1719 paint.drawImage(rect, cache.getImage(), rect); |
| 1847 cerr << "SpectrogramLayer: image cache good" << endl; | 1720 |
| 1848 #endif | 1721 illuminateLocalFeatures(v, paint); |
| 1849 | 1722 return; |
| 1850 paint.drawImage(rect, cache.image, rect); | 1723 |
| 1851 //!!! | 1724 } else { |
| 1852 // paint.drawImage(v->rect(), cache.image, | 1725 |
| 1853 // QRect(QPoint(0, 0), cache.image.size())); | 1726 // cache doesn't begin at the right frame or doesn't |
| 1854 | 1727 // contain the complete view, but might be scrollable or |
| 1855 illuminateLocalFeatures(v, paint); | 1728 // partially usable |
| 1856 return; | 1729 |
| 1857 | 1730 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1858 } else { | 1731 cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl; |
| 1859 | 1732 #endif |
| 1860 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1733 |
| 1861 cerr << "SpectrogramLayer: image cache partially OK" << endl; | 1734 cache.scrollTo(startFrame); |
| 1862 #endif | 1735 |
| 1863 | 1736 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 1864 recreateWholeImageCache = false; | 1737 cerr << "SpectrogramLayer: after scrolling, cache valid from " |
| 1865 | 1738 << cache.getValidLeft() << " width " << cache.getValidWidth() |
| 1866 int dx = v->getXForFrame(cache.startFrame) - | 1739 << endl; |
| 1867 v->getXForFrame(startFrame); | 1740 #endif |
| 1868 | 1741 } |
| 1869 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1742 } |
| 1870 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl; | 1743 |
| 1871 #endif | 1744 bool rightToLeft = false; |
| 1872 | 1745 |
| 1873 if (dx != 0 && | 1746 if (!cache.isValid()) { |
| 1874 dx > -cw && | 1747 if (!m_synchronous) { |
| 1875 dx < cw) { | 1748 // When rendering the whole thing, start from somewhere near |
| 1876 | 1749 // the middle so that the region of interest appears first |
| 1877 int dxp = dx; | 1750 |
| 1878 if (dxp < 0) dxp = -dxp; | 1751 //!!! (perhaps we should have some cunning test to avoid |
| 1879 size_t copy = (cw - dxp) * sizeof(QRgb); | 1752 //!!! doing this if past repaints have appeared fast |
| 1880 for (int y = 0; y < ch; ++y) { | 1753 //!!! enough to do the whole width in one shot) |
| 1881 QRgb *line = (QRgb *)cache.image.scanLine(y); | 1754 if (x0 == 0 && x1 == v->getPaintWidth()) { |
| 1882 if (dx < 0) { | 1755 x0 = int(x1 * 0.3); |
| 1883 memmove(line, line + dxp, copy); | |
| 1884 } else { | |
| 1885 memmove(line + dxp, line, copy); | |
| 1886 } | |
| 1887 } | |
| 1888 | |
| 1889 int px = cache.validArea.x(); | |
| 1890 int pw = cache.validArea.width(); | |
| 1891 | |
| 1892 if (dx < 0) { | |
| 1893 x0 = cw + dx; | |
| 1894 x1 = cw; | |
| 1895 px += dx; | |
| 1896 if (px < 0) { | |
| 1897 pw += px; | |
| 1898 px = 0; | |
| 1899 if (pw < 0) pw = 0; | |
| 1900 } | |
| 1901 } else { | |
| 1902 x0 = 0; | |
| 1903 x1 = dx; | |
| 1904 px += dx; | |
| 1905 if (px + pw > cw) { | |
| 1906 pw = int(cw) - px; | |
| 1907 if (pw < 0) pw = 0; | |
| 1908 } | |
| 1909 } | |
| 1910 | |
| 1911 cache.validArea = | |
| 1912 QRect(px, cache.validArea.y(), | |
| 1913 pw, cache.validArea.height()); | |
| 1914 | |
| 1915 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1916 cerr << "valid area now " | |
| 1917 << px << "," << cache.validArea.y() | |
| 1918 << " " << pw << "x" << cache.validArea.height() | |
| 1919 << endl; | |
| 1920 #endif | |
| 1921 /* | |
| 1922 paint.drawImage(rect & cache.validArea, | |
| 1923 cache.image, | |
| 1924 rect & cache.validArea); | |
| 1925 */ | |
| 1926 } else if (dx != 0) { | |
| 1927 | |
| 1928 // we scrolled too far to be of use | |
| 1929 | |
| 1930 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1931 cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl; | |
| 1932 #endif | |
| 1933 | |
| 1934 cache.validArea = QRect(); | |
| 1935 recreateWholeImageCache = true; | |
| 1936 } | |
| 1937 } | |
| 1938 } else { | |
| 1939 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1940 cerr << "SpectrogramLayer: image cache useless" << endl; | |
| 1941 if (int(cache.zoomLevel) != zoomLevel) { | |
| 1942 cerr << "(cache zoomLevel " << cache.zoomLevel | |
| 1943 << " != " << zoomLevel << ")" << endl; | |
| 1944 } | 1756 } |
| 1945 if (cw != v->width()) { | |
| 1946 cerr << "(cache width " << cw | |
| 1947 << " != " << v->width(); | |
| 1948 } | |
| 1949 if (ch != v->height()) { | |
| 1950 cerr << "(cache height " << ch | |
| 1951 << " != " << v->height(); | |
| 1952 } | |
| 1953 #endif | |
| 1954 cache.validArea = QRect(); | |
| 1955 // recreateWholeImageCache = true; | |
| 1956 } | |
| 1957 } | |
| 1958 | |
| 1959 if (updateViewMagnitudes(v)) { | |
| 1960 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 1961 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | |
| 1962 #endif | |
| 1963 if (m_normalizeVisibleArea) { | |
| 1964 cache.validArea = QRect(); | |
| 1965 recreateWholeImageCache = true; | |
| 1966 } | 1757 } |
| 1967 } else { | 1758 } else { |
| 1968 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1759 // When rendering only a part of the cache, we need to make |
| 1969 cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | 1760 // sure that the part we're rendering is adjacent to (or |
| 1970 #endif | 1761 // overlapping) a valid area of cache, if we have one. The |
| 1971 } | 1762 // alternative is to ditch the valid area of cache and render |
| 1972 | 1763 // only the requested area, but that's risky because this can |
| 1973 if (recreateWholeImageCache) { | 1764 // happen when just waving the pointer over a small part of |
| 1974 x0 = 0; | 1765 // the view -- if we lose the partly-built cache every time |
| 1975 x1 = v->width(); | 1766 // the user does that, we'll never finish building it. |
| 1976 } | 1767 int left = x0; |
| 1977 | 1768 int width = x1 - x0; |
| 1978 struct timeval tv; | 1769 bool isLeftOfValidArea = false; |
| 1979 (void)gettimeofday(&tv, 0); | 1770 cache.adjustToTouchValidArea(left, width, isLeftOfValidArea); |
| 1980 RealTime mainPaintStart = RealTime::fromTimeval(tv); | 1771 x0 = left; |
| 1981 | 1772 x1 = x0 + width; |
| 1982 int paintBlockWidth = m_lastPaintBlockWidth; | 1773 |
| 1983 | 1774 // That call also told us whether we should be painting |
| 1984 if (m_synchronous) { | 1775 // sub-regions of our target region in right-to-left order in |
| 1985 if (paintBlockWidth < x1 - x0) { | 1776 // order to ensure contiguity |
| 1986 // always paint full width | 1777 rightToLeft = isLeftOfValidArea; |
| 1987 paintBlockWidth = x1 - x0; | 1778 } |
| 1988 } | 1779 |
| 1989 } else { | |
| 1990 if (paintBlockWidth == 0) { | |
| 1991 paintBlockWidth = (300000 / zoomLevel); | |
| 1992 } else { | |
| 1993 RealTime lastTime = m_lastPaintTime; | |
| 1994 while (lastTime > RealTime::fromMilliseconds(200) && | |
| 1995 paintBlockWidth > 50) { | |
| 1996 paintBlockWidth /= 2; | |
| 1997 lastTime = lastTime / 2; | |
| 1998 } | |
| 1999 while (lastTime < RealTime::fromMilliseconds(90) && | |
| 2000 paintBlockWidth < 1500) { | |
| 2001 paintBlockWidth *= 2; | |
| 2002 lastTime = lastTime * 2; | |
| 2003 } | |
| 2004 } | |
| 2005 | |
| 2006 if (paintBlockWidth < 20) paintBlockWidth = 20; | |
| 2007 } | |
| 2008 | |
| 2009 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2010 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl; | |
| 2011 #endif | |
| 2012 | |
| 2013 // We always paint the full height when refreshing the cache. | 1780 // We always paint the full height when refreshing the cache. |
| 2014 // Smaller heights can be used when painting direct from cache | 1781 // Smaller heights can be used when painting direct from cache |
| 2015 // (further up in this function), but we want to ensure the cache | 1782 // (further up in this function), but we want to ensure the cache |
| 2016 // is coherent without having to worry about vertical matching of | 1783 // is coherent without having to worry about vertical matching of |
| 2017 // required and valid areas as well as horizontal. | 1784 // required and valid areas as well as horizontal. |
| 2018 | 1785 int h = v->getPaintHeight(); |
| 2019 int h = v->height(); | 1786 |
| 2020 | 1787 int repaintWidth = x1 - x0; |
| 2021 if (cache.validArea.width() > 0) { | 1788 |
| 2022 | 1789 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2023 // If part of the cache is known to be valid, select a strip | 1790 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1 |
| 2024 // immediately to left or right of the valid part | 1791 << ", repaintWidth " << repaintWidth << ", h " << h |
| 2025 | 1792 << ", rightToLeft " << rightToLeft << endl; |
| 2026 //!!! this really needs to be coordinated with the selection | |
| 2027 //!!! of m_drawBuffer boundaries in the bufferBinResolution | |
| 2028 //!!! case below | |
| 2029 | |
| 2030 int vx0 = 0, vx1 = 0; | |
| 2031 vx0 = cache.validArea.x(); | |
| 2032 vx1 = cache.validArea.x() + cache.validArea.width(); | |
| 2033 | |
| 2034 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2035 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl; | |
| 2036 #endif | |
| 2037 if (x0 < vx0) { | |
| 2038 if (x0 + paintBlockWidth < vx0) { | |
| 2039 x0 = vx0 - paintBlockWidth; | |
| 2040 } | |
| 2041 x1 = vx0; | |
| 2042 } else if (x0 >= vx1) { | |
| 2043 x0 = vx1; | |
| 2044 if (x1 > x0 + paintBlockWidth) { | |
| 2045 x1 = x0 + paintBlockWidth; | |
| 2046 } | |
| 2047 } else { | |
| 2048 // x0 is within the valid area | |
| 2049 if (x1 > vx1) { | |
| 2050 x0 = vx1; | |
| 2051 if (x0 + paintBlockWidth < x1) { | |
| 2052 x1 = x0 + paintBlockWidth; | |
| 2053 } | |
| 2054 } else { | |
| 2055 x1 = x0; // it's all valid, paint nothing | |
| 2056 } | |
| 2057 } | |
| 2058 | |
| 2059 cache.validArea = QRect | |
| 2060 (std::min(vx0, x0), cache.validArea.y(), | |
| 2061 std::max(vx1 - std::min(vx0, x0), | |
| 2062 x1 - std::min(vx0, x0)), | |
| 2063 cache.validArea.height()); | |
| 2064 | |
| 2065 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2066 cerr << "Valid area becomes " << cache.validArea.x() | |
| 2067 << ", " << cache.validArea.y() << ", " | |
| 2068 << cache.validArea.width() << "x" | |
| 2069 << cache.validArea.height() << endl; | |
| 2070 #endif | |
| 2071 | |
| 2072 } else { | |
| 2073 if (x1 > x0 + paintBlockWidth) { | |
| 2074 int sfx = x1; | |
| 2075 if (startFrame < 0) sfx = v->getXForFrame(0); | |
| 2076 if (sfx >= x0 && sfx + paintBlockWidth <= x1) { | |
| 2077 x0 = sfx; | |
| 2078 x1 = x0 + paintBlockWidth; | |
| 2079 } else { | |
| 2080 int mid = (x1 + x0) / 2; | |
| 2081 x0 = mid - paintBlockWidth/2; | |
| 2082 x1 = x0 + paintBlockWidth; | |
| 2083 } | |
| 2084 } | |
| 2085 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2086 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0) | |
| 2087 << "x" << h << endl; | |
| 2088 #endif | |
| 2089 cache.validArea = QRect(x0, 0, x1 - x0, h); | |
| 2090 } | |
| 2091 | |
| 2092 /* | |
| 2093 if (xPixelRatio != 1.f) { | |
| 2094 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001); | |
| 2095 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001); | |
| 2096 } | |
| 2097 */ | |
| 2098 int w = x1 - x0; | |
| 2099 | |
| 2100 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2101 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl; | |
| 2102 #endif | 1793 #endif |
| 2103 | 1794 |
| 2104 sv_samplerate_t sr = m_model->getSampleRate(); | 1795 sv_samplerate_t sr = m_model->getSampleRate(); |
| 2105 | 1796 |
| 2106 // Set minFreq and maxFreq to the frequency extents of the possibly | 1797 // Set minFreq and maxFreq to the frequency extents of the possibly |
| 2146 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl; | 1837 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl; |
| 2147 | 1838 |
| 2148 int increment = getWindowIncrement(); | 1839 int increment = getWindowIncrement(); |
| 2149 | 1840 |
| 2150 bool logarithmic = (m_frequencyScale == LogFrequencyScale); | 1841 bool logarithmic = (m_frequencyScale == LogFrequencyScale); |
| 2151 /* | 1842 |
| 2152 double yforbin[maxbin - minbin + 1]; | 1843 MagnitudeRange overallMag = m_viewMags[v->getId()]; |
| 2153 | |
| 2154 for (int q = minbin; q <= maxbin; ++q) { | |
| 2155 double f0 = (double(q) * sr) / fftSize; | |
| 2156 yforbin[q - minbin] = | |
| 2157 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq, | |
| 2158 logarithmic); | |
| 2159 } | |
| 2160 */ | |
| 2161 MagnitudeRange overallMag = m_viewMags[v]; | |
| 2162 bool overallMagChanged = false; | 1844 bool overallMagChanged = false; |
| 2163 | 1845 |
| 2164 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1846 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2165 cerr << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; | 1847 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl; |
| 2166 #endif | 1848 #endif |
| 2167 | 1849 |
| 2168 if (w == 0) { | 1850 if (repaintWidth == 0) { |
| 2169 SVDEBUG << "*** NOTE: w == 0" << endl; | 1851 SVDEBUG << "*** NOTE: repaintWidth == 0" << endl; |
| 2170 } | 1852 } |
| 2171 | |
| 2172 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2173 int pixels = 0; | |
| 2174 #endif | |
| 2175 | 1853 |
| 2176 Profiler outerprof("SpectrogramLayer::paint: all cols"); | 1854 Profiler outerprof("SpectrogramLayer::paint: all cols"); |
| 2177 | 1855 |
| 2178 // The draw buffer contains a fragment at either our pixel | 1856 // The draw buffer contains a fragment at either our pixel |
| 2179 // resolution (if there is more than one time-bin per pixel) or | 1857 // resolution (if there is more than one time-bin per pixel) or |
| 2186 // If (getFrameForX(x) / increment) * increment == | 1864 // If (getFrameForX(x) / increment) * increment == |
| 2187 // getFrameForX(x), then x is a time-bin boundary. We want two | 1865 // getFrameForX(x), then x is a time-bin boundary. We want two |
| 2188 // such boundaries at either side of the draw buffer -- one which | 1866 // such boundaries at either side of the draw buffer -- one which |
| 2189 // we draw up to, and one which we subsequently crop at. | 1867 // we draw up to, and one which we subsequently crop at. |
| 2190 | 1868 |
| 2191 bool bufferBinResolution = false; | 1869 bool bufferIsBinResolution = false; |
| 2192 if (increment > zoomLevel) bufferBinResolution = true; | 1870 if (increment > zoomLevel) bufferIsBinResolution = true; |
| 2193 | 1871 |
| 2194 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1; | 1872 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1; |
| 2195 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1; | 1873 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1; |
| 2196 | 1874 |
| 2197 int bufwid; | 1875 int bufwid; |
| 2198 | 1876 |
| 2199 if (bufferBinResolution) { | 1877 if (bufferIsBinResolution) { |
| 2200 | 1878 |
| 2201 for (int x = x0; ; --x) { | 1879 for (int x = x0; ; --x) { |
| 2202 sv_frame_t f = v->getFrameForX(x); | 1880 sv_frame_t f = v->getFrameForX(x); |
| 2203 if ((f / increment) * increment == f) { | 1881 if ((f / increment) * increment == f) { |
| 2204 if (leftCropFrame == -1) leftCropFrame = f; | 1882 if (leftCropFrame == -1) leftCropFrame = f; |
| 2205 else if (x < x0 - 2) { leftBoundaryFrame = f; break; } | 1883 else if (x < x0 - 2) { |
| 1884 leftBoundaryFrame = f; | |
| 1885 break; | |
| 1886 } | |
| 2206 } | 1887 } |
| 2207 } | 1888 } |
| 2208 for (int x = x0 + w; ; ++x) { | 1889 for (int x = x0 + repaintWidth; ; ++x) { |
| 2209 sv_frame_t f = v->getFrameForX(x); | 1890 sv_frame_t f = v->getFrameForX(x); |
| 2210 if ((f / increment) * increment == f) { | 1891 if ((f / increment) * increment == f) { |
| 2211 if (rightCropFrame == -1) rightCropFrame = f; | 1892 if (rightCropFrame == -1) rightCropFrame = f; |
| 2212 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; } | 1893 else if (x > x0 + repaintWidth + 2) { |
| 1894 rightBoundaryFrame = f; | |
| 1895 break; | |
| 1896 } | |
| 2213 } | 1897 } |
| 2214 } | 1898 } |
| 2215 #ifdef DEBUG_SPECTROGRAM_REPAINT | 1899 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2216 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl; | 1900 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl; |
| 2217 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; | 1901 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl; |
| 2219 | 1903 |
| 2220 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment); | 1904 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment); |
| 2221 | 1905 |
| 2222 } else { | 1906 } else { |
| 2223 | 1907 |
| 2224 bufwid = w; | 1908 bufwid = repaintWidth; |
| 2225 } | 1909 } |
| 2226 | 1910 |
| 2227 vector<int> binforx(bufwid); | 1911 vector<int> binforx(bufwid); |
| 2228 vector<double> binfory(h); | 1912 vector<double> binfory(h); |
| 2229 | 1913 |
| 2230 bool usePeaksCache = false; | 1914 bool usePeaksCache = false; |
| 2231 | 1915 |
| 2232 if (bufferBinResolution) { | 1916 if (bufferIsBinResolution) { |
| 2233 for (int x = 0; x < bufwid; ++x) { | 1917 for (int x = 0; x < bufwid; ++x) { |
| 2234 binforx[x] = int(leftBoundaryFrame / increment) + x; | 1918 binforx[x] = int(leftBoundaryFrame / increment) + x; |
| 2235 // cerr << "binforx[" << x << "] = " << binforx[x] << endl; | |
| 2236 } | 1919 } |
| 2237 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); | 1920 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); |
| 2238 } else { | 1921 } else { |
| 2239 for (int x = 0; x < bufwid; ++x) { | 1922 for (int x = 0; x < bufwid; ++x) { |
| 2240 double s0 = 0, s1 = 0; | 1923 double s0 = 0, s1 = 0; |
| 2242 binforx[x] = int(s0 + 0.0001); | 1925 binforx[x] = int(s0 + 0.0001); |
| 2243 } else { | 1926 } else { |
| 2244 binforx[x] = -1; //??? | 1927 binforx[x] = -1; //??? |
| 2245 } | 1928 } |
| 2246 } | 1929 } |
| 2247 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) { | 1930 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) { |
| 2248 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); | 1931 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8); |
| 2249 } | 1932 } |
| 2250 usePeaksCache = (increment * 8) < zoomLevel; | 1933 usePeaksCache = (increment * 8) < zoomLevel; |
| 2251 if (m_colourScale == PhaseColourScale) usePeaksCache = false; | 1934 if (m_colourScale == PhaseColourScale) usePeaksCache = false; |
| 2252 } | 1935 } |
| 2253 | 1936 |
| 2254 // No longer exists in Qt5: m_drawBuffer.setNumColors(256); | |
| 2255 for (int pixel = 0; pixel < 256; ++pixel) { | 1937 for (int pixel = 0; pixel < 256; ++pixel) { |
| 2256 m_drawBuffer.setColor((unsigned char)pixel, | 1938 m_drawBuffer.setColor((unsigned char)pixel, |
| 2257 m_palette.getColour((unsigned char)pixel).rgb()); | 1939 m_palette.getColour((unsigned char)pixel).rgb()); |
| 2258 } | 1940 } |
| 2259 | 1941 |
| 2260 m_drawBuffer.fill(0); | 1942 m_drawBuffer.fill(0); |
| 2261 | 1943 int attainedBufwid = bufwid; |
| 1944 | |
| 1945 double softTimeLimit; | |
| 1946 | |
| 1947 if (m_synchronous) { | |
| 1948 | |
| 1949 // must paint the whole thing for synchronous mode, so give | |
| 1950 // "no timeout" | |
| 1951 softTimeLimit = 0.0; | |
| 1952 | |
| 1953 } else if (bufferIsBinResolution) { | |
| 1954 | |
| 1955 // calculating boundaries later will be too fiddly for partial | |
| 1956 // paints, and painting should be fast anyway when this is the | |
| 1957 // case because it means we're well zoomed in | |
| 1958 softTimeLimit = 0.0; | |
| 1959 | |
| 1960 } else { | |
| 1961 | |
| 1962 // neither limitation applies, so use a short soft limit | |
| 1963 | |
| 1964 if (m_binDisplay == PeakFrequencies) { | |
| 1965 softTimeLimit = 0.15; | |
| 1966 } else { | |
| 1967 softTimeLimit = 0.1; | |
| 1968 } | |
| 1969 } | |
| 1970 | |
| 2262 if (m_binDisplay != PeakFrequencies) { | 1971 if (m_binDisplay != PeakFrequencies) { |
| 2263 | 1972 |
| 2264 for (int y = 0; y < h; ++y) { | 1973 for (int y = 0; y < h; ++y) { |
| 2265 double q0 = 0, q1 = 0; | 1974 double q0 = 0, q1 = 0; |
| 2266 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { | 1975 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { |
| 2267 binfory[y] = -1; | 1976 binfory[y] = -1; |
| 2268 } else { | 1977 } else { |
| 2269 binfory[y] = q0; | 1978 binfory[y] = q0; |
| 2270 // cerr << "binfory[" << y << "] = " << binfory[y] << endl; | |
| 2271 } | 1979 } |
| 2272 } | 1980 } |
| 2273 | 1981 |
| 2274 paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache, | 1982 attainedBufwid = |
| 2275 overallMag, overallMagChanged); | 1983 paintDrawBuffer(v, bufwid, h, binforx, binfory, |
| 1984 usePeaksCache, | |
| 1985 overallMag, overallMagChanged, | |
| 1986 rightToLeft, | |
| 1987 softTimeLimit); | |
| 2276 | 1988 |
| 2277 } else { | 1989 } else { |
| 2278 | 1990 |
| 2279 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, | 1991 attainedBufwid = |
| 2280 minbin, maxbin, | 1992 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx, |
| 2281 displayMinFreq, displayMaxFreq, | 1993 minbin, maxbin, |
| 2282 logarithmic, | 1994 displayMinFreq, displayMaxFreq, |
| 2283 overallMag, overallMagChanged); | 1995 logarithmic, |
| 2284 } | 1996 overallMag, overallMagChanged, |
| 2285 | 1997 rightToLeft, |
| 2286 /* | 1998 softTimeLimit); |
| 2287 for (int x = 0; x < w / xPixelRatio; ++x) { | 1999 } |
| 2288 | 2000 |
| 2289 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column"); | 2001 int failedToRepaint = bufwid - attainedBufwid; |
| 2290 | 2002 |
| 2291 runOutOfData = !paintColumnValues(v, fft, x0, x, | 2003 int paintedLeft = x0; |
| 2292 minbin, maxbin, | 2004 int paintedWidth = x1 - x0; |
| 2293 displayMinFreq, displayMaxFreq, | 2005 |
| 2294 xPixelRatio, | 2006 if (failedToRepaint > 0) { |
| 2295 h, yforbin); | 2007 |
| 2296 | 2008 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2297 if (runOutOfData) { | 2009 cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid |
| 2298 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2010 << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl; |
| 2299 cerr << "Run out of data -- dropping out of loop" << endl; | 2011 #endif |
| 2300 #endif | 2012 |
| 2301 break; | 2013 if (rightToLeft) { |
| 2302 } | 2014 paintedLeft += failedToRepaint; |
| 2303 } | 2015 } |
| 2304 */ | 2016 |
| 2305 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2017 paintedWidth -= failedToRepaint; |
| 2306 // cerr << pixels << " pixels drawn" << endl; | 2018 |
| 2307 #endif | 2019 if (paintedWidth < 0) { |
| 2020 paintedWidth = 0; | |
| 2021 } | |
| 2022 | |
| 2023 } else if (failedToRepaint < 0) { | |
| 2024 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")" | |
| 2025 << endl; | |
| 2026 failedToRepaint = 0; | |
| 2027 } | |
| 2308 | 2028 |
| 2309 if (overallMagChanged) { | 2029 if (overallMagChanged) { |
| 2310 m_viewMags[v] = overallMag; | 2030 m_viewMags[v->getId()] = overallMag; |
| 2311 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2031 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2312 cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl; | 2032 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl; |
| 2313 #endif | |
| 2314 } else { | |
| 2315 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2316 cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl; | |
| 2317 #endif | 2033 #endif |
| 2318 } | 2034 } |
| 2319 | 2035 |
| 2320 outerprof.end(); | 2036 outerprof.end(); |
| 2321 | 2037 |
| 2322 Profiler profiler2("SpectrogramLayer::paint: draw image"); | 2038 Profiler profiler2("SpectrogramLayer::paint: draw image"); |
| 2323 | 2039 |
| 2324 if (recreateWholeImageCache) { | 2040 if (paintedWidth > 0) { |
| 2325 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2041 |
| 2326 SVDEBUG << "Recreating image cache: width = " << v->width() | 2042 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2327 << ", height = " << h << endl; | 2043 cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h |
| 2328 #endif | 2044 << " from draw buffer at " << paintedLeft - x0 << "," << 0 |
| 2329 cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied); | 2045 << " to " << paintedWidth << "x" << h << " on cache at " |
| 2330 } | |
| 2331 | |
| 2332 if (w > 0) { | |
| 2333 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2334 SVDEBUG << "Painting " << w << "x" << h | |
| 2335 << " from draw buffer at " << 0 << "," << 0 | |
| 2336 << " to " << w << "x" << h << " on cache at " | |
| 2337 << x0 << "," << 0 << endl; | 2046 << x0 << "," << 0 << endl; |
| 2338 #endif | 2047 #endif |
| 2339 | 2048 |
| 2340 QPainter cachePainter(&cache.image); | 2049 if (bufferIsBinResolution) { |
| 2341 | 2050 |
| 2342 if (bufferBinResolution) { | |
| 2343 int scaledLeft = v->getXForFrame(leftBoundaryFrame); | 2051 int scaledLeft = v->getXForFrame(leftBoundaryFrame); |
| 2344 int scaledRight = v->getXForFrame(rightBoundaryFrame); | 2052 int scaledRight = v->getXForFrame(rightBoundaryFrame); |
| 2345 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2053 |
| 2346 SVDEBUG << "Rescaling image from " << bufwid | 2054 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2055 cerr << "SpectrogramLayer: Rescaling image from " << bufwid | |
| 2347 << "x" << h << " to " | 2056 << "x" << h << " to " |
| 2348 << scaledRight-scaledLeft << "x" << h << endl; | 2057 << scaledRight-scaledLeft << "x" << h << endl; |
| 2349 #endif | 2058 #endif |
| 2059 | |
| 2350 Preferences::SpectrogramXSmoothing xsmoothing = | 2060 Preferences::SpectrogramXSmoothing xsmoothing = |
| 2351 Preferences::getInstance()->getSpectrogramXSmoothing(); | 2061 Preferences::getInstance()->getSpectrogramXSmoothing(); |
| 2352 // SVDEBUG << "xsmoothing == " << xsmoothing << endl; | 2062 |
| 2353 QImage scaled = m_drawBuffer.scaled | 2063 QImage scaled = m_drawBuffer.scaled |
| 2354 (scaledRight - scaledLeft, h, | 2064 (scaledRight - scaledLeft, h, |
| 2355 Qt::IgnoreAspectRatio, | 2065 Qt::IgnoreAspectRatio, |
| 2356 ((xsmoothing == Preferences::SpectrogramXInterpolated) ? | 2066 ((xsmoothing == Preferences::SpectrogramXInterpolated) ? |
| 2357 Qt::SmoothTransformation : Qt::FastTransformation)); | 2067 Qt::SmoothTransformation : Qt::FastTransformation)); |
| 2068 | |
| 2358 int scaledLeftCrop = v->getXForFrame(leftCropFrame); | 2069 int scaledLeftCrop = v->getXForFrame(leftCropFrame); |
| 2359 int scaledRightCrop = v->getXForFrame(rightCropFrame); | 2070 int scaledRightCrop = v->getXForFrame(rightCropFrame); |
| 2360 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2071 |
| 2361 SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " | 2072 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2073 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to " | |
| 2362 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; | 2074 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl; |
| 2363 #endif | 2075 #endif |
| 2364 cachePainter.drawImage | 2076 |
| 2365 (QRect(scaledLeftCrop, 0, | 2077 int targetLeft = scaledLeftCrop; |
| 2366 scaledRightCrop - scaledLeftCrop, h), | 2078 if (targetLeft < 0) { |
| 2367 scaled, | 2079 targetLeft = 0; |
| 2368 QRect(scaledLeftCrop - scaledLeft, 0, | 2080 } |
| 2369 scaledRightCrop - scaledLeftCrop, h)); | 2081 |
| 2082 int targetWidth = scaledRightCrop - targetLeft; | |
| 2083 if (targetLeft + targetWidth > cache.getSize().width()) { | |
| 2084 targetWidth = cache.getSize().width() - targetLeft; | |
| 2085 } | |
| 2086 | |
| 2087 int sourceLeft = targetLeft - scaledLeft; | |
| 2088 if (sourceLeft < 0) { | |
| 2089 sourceLeft = 0; | |
| 2090 } | |
| 2091 | |
| 2092 int sourceWidth = targetWidth; | |
| 2093 | |
| 2094 if (targetWidth > 0) { | |
| 2095 cache.drawImage | |
| 2096 (targetLeft, | |
| 2097 targetWidth, | |
| 2098 scaled, | |
| 2099 sourceLeft, | |
| 2100 sourceWidth); | |
| 2101 } | |
| 2102 | |
| 2370 } else { | 2103 } else { |
| 2371 cachePainter.drawImage(QRect(x0, 0, w, h), | 2104 |
| 2372 m_drawBuffer, | 2105 cache.drawImage(paintedLeft, paintedWidth, |
| 2373 QRect(0, 0, w, h)); | 2106 m_drawBuffer, |
| 2374 } | 2107 paintedLeft - x0, paintedWidth); |
| 2375 | 2108 } |
| 2376 cachePainter.end(); | 2109 } |
| 2377 } | 2110 |
| 2378 | 2111 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2379 QRect pr = rect & cache.validArea; | 2112 cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft() |
| 2380 | 2113 << " width " << cache.getValidWidth() << ", height " |
| 2381 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2114 << cache.getSize().height() << endl; |
| 2382 SVDEBUG << "Painting " << pr.width() << "x" << pr.height() | 2115 #endif |
| 2116 | |
| 2117 QRect pr = rect & cache.getValidArea(); | |
| 2118 | |
| 2119 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2120 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height() | |
| 2383 << " from cache at " << pr.x() << "," << pr.y() | 2121 << " from cache at " << pr.x() << "," << pr.y() |
| 2384 << " to window" << endl; | 2122 << " to window" << endl; |
| 2385 #endif | 2123 #endif |
| 2386 | 2124 |
| 2387 paint.drawImage(pr.x(), pr.y(), cache.image, | 2125 paint.drawImage(pr.x(), pr.y(), cache.getImage(), |
| 2388 pr.x(), pr.y(), pr.width(), pr.height()); | 2126 pr.x(), pr.y(), pr.width(), pr.height()); |
| 2389 //!!! | |
| 2390 // paint.drawImage(v->rect(), cache.image, | |
| 2391 // QRect(QPoint(0, 0), cache.image.size())); | |
| 2392 | |
| 2393 cache.startFrame = startFrame; | |
| 2394 cache.zoomLevel = zoomLevel; | |
| 2395 | 2127 |
| 2396 if (!m_synchronous) { | 2128 if (!m_synchronous) { |
| 2397 | 2129 |
| 2398 if (!m_normalizeVisibleArea || !overallMagChanged) { | 2130 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) { |
| 2399 | 2131 |
| 2400 if (cache.validArea.x() > 0) { | 2132 QRect areaLeft(0, 0, cache.getValidLeft(), h); |
| 2401 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2133 QRect areaRight(cache.getValidRight(), 0, |
| 2402 SVDEBUG << "SpectrogramLayer::paint() updating left (0, " | 2134 cache.getSize().width() - cache.getValidRight(), h); |
| 2403 << cache.validArea.x() << ")" << endl; | 2135 |
| 2404 #endif | 2136 bool haveSpaceLeft = (areaLeft.width() > 0); |
| 2405 v->update(0, 0, cache.validArea.x(), h); | 2137 bool haveSpaceRight = (areaRight.width() > 0); |
| 2138 | |
| 2139 bool updateLeft = haveSpaceLeft; | |
| 2140 bool updateRight = haveSpaceRight; | |
| 2141 | |
| 2142 if (updateLeft && updateRight) { | |
| 2143 if (rightToLeft) { | |
| 2144 // we just did something adjoining the cache on | |
| 2145 // its left side, so now do something on its right | |
| 2146 updateLeft = false; | |
| 2147 } else { | |
| 2148 updateRight = false; | |
| 2149 } | |
| 2406 } | 2150 } |
| 2407 | 2151 |
| 2408 if (cache.validArea.x() + cache.validArea.width() < | 2152 if (updateLeft) { |
| 2409 cache.image.width()) { | 2153 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2410 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2154 cerr << "SpectrogramLayer::paint() updating left (" |
| 2411 SVDEBUG << "SpectrogramLayer::paint() updating right (" | 2155 << areaLeft.x() << ", " |
| 2412 << cache.validArea.x() + cache.validArea.width() | 2156 << areaLeft.width() << ")" << endl; |
| 2413 << ", " | 2157 #endif |
| 2414 << cache.image.width() - (cache.validArea.x() + | 2158 v->updatePaintRect(areaLeft); |
| 2415 cache.validArea.width()) | |
| 2416 << ")" << endl; | |
| 2417 #endif | |
| 2418 v->update(cache.validArea.x() + cache.validArea.width(), | |
| 2419 0, | |
| 2420 cache.image.width() - (cache.validArea.x() + | |
| 2421 cache.validArea.width()), | |
| 2422 h); | |
| 2423 } | 2159 } |
| 2160 | |
| 2161 if (updateRight) { | |
| 2162 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2163 cerr << "SpectrogramLayer::paint() updating right (" | |
| 2164 << areaRight.x() << ", " | |
| 2165 << areaRight.width() << ")" << endl; | |
| 2166 #endif | |
| 2167 v->updatePaintRect(areaRight); | |
| 2168 } | |
| 2169 | |
| 2424 } else { | 2170 } else { |
| 2425 // overallMagChanged | 2171 // overallMagChanged |
| 2426 cerr << "\noverallMagChanged - updating all\n" << endl; | 2172 cerr << "\noverallMagChanged - updating all\n" << endl; |
| 2427 cache.validArea = QRect(); | 2173 cache.invalidate(); |
| 2428 v->update(); | 2174 v->updatePaintRect(v->getPaintRect()); |
| 2429 } | 2175 } |
| 2430 } | 2176 } |
| 2431 | 2177 |
| 2432 illuminateLocalFeatures(v, paint); | 2178 illuminateLocalFeatures(v, paint); |
| 2433 | 2179 |
| 2434 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2180 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2435 SVDEBUG << "SpectrogramLayer::paint() returning" << endl; | 2181 cerr << "SpectrogramLayer::paint() returning" << endl; |
| 2436 #endif | 2182 #endif |
| 2437 | 2183 } |
| 2438 if (!m_synchronous) { | 2184 |
| 2439 m_lastPaintBlockWidth = paintBlockWidth; | 2185 int |
| 2440 (void)gettimeofday(&tv, 0); | 2186 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v, |
| 2441 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart; | |
| 2442 } | |
| 2443 } | |
| 2444 | |
| 2445 bool | |
| 2446 SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v, | |
| 2447 int w, | 2187 int w, |
| 2448 int h, | 2188 int h, |
| 2449 const vector<int> &binforx, | 2189 const vector<int> &binforx, |
| 2450 int minbin, | 2190 int minbin, |
| 2451 int maxbin, | 2191 int maxbin, |
| 2452 double displayMinFreq, | 2192 double displayMinFreq, |
| 2453 double displayMaxFreq, | 2193 double displayMaxFreq, |
| 2454 bool logarithmic, | 2194 bool logarithmic, |
| 2455 MagnitudeRange &overallMag, | 2195 MagnitudeRange &overallMag, |
| 2456 bool &overallMagChanged) const | 2196 bool &overallMagChanged, |
| 2197 bool rightToLeft, | |
| 2198 double softTimeLimit) const | |
| 2457 { | 2199 { |
| 2458 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); | 2200 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies"); |
| 2459 | 2201 |
| 2460 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2202 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2461 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; | 2203 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; |
| 2462 #endif | 2204 #endif |
| 2463 if (minbin < 0) minbin = 0; | 2205 if (minbin < 0) minbin = 0; |
| 2464 if (maxbin < 0) maxbin = minbin+1; | 2206 if (maxbin < 0) maxbin = minbin+1; |
| 2465 | 2207 |
| 2466 FFTModel *fft = getFFTModel(v); | 2208 FFTModel *fft = getFFTModel(v); |
| 2467 if (!fft) return false; | 2209 if (!fft) return 0; |
| 2468 | 2210 |
| 2469 FFTModel::PeakSet peakfreqs; | 2211 FFTModel::PeakSet peakfreqs; |
| 2470 | 2212 |
| 2471 int psx = -1; | 2213 int psx = -1; |
| 2472 | 2214 |
| 2474 float values[maxbin - minbin + 1]; | 2216 float values[maxbin - minbin + 1]; |
| 2475 #else | 2217 #else |
| 2476 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); | 2218 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float)); |
| 2477 #endif | 2219 #endif |
| 2478 | 2220 |
| 2479 for (int x = 0; x < w; ++x) { | 2221 int minColumns = 4; |
| 2222 bool haveTimeLimits = (softTimeLimit > 0.0); | |
| 2223 double hardTimeLimit = softTimeLimit * 2.0; | |
| 2224 bool overridingSoftLimit = false; | |
| 2225 auto startTime = chrono::steady_clock::now(); | |
| 2226 | |
| 2227 int start = 0; | |
| 2228 int finish = w; | |
| 2229 int step = 1; | |
| 2230 | |
| 2231 if (rightToLeft) { | |
| 2232 start = w-1; | |
| 2233 finish = -1; | |
| 2234 step = -1; | |
| 2235 } | |
| 2236 | |
| 2237 int columnCount = 0; | |
| 2238 | |
| 2239 for (int x = start; x != finish; x += step) { | |
| 2240 | |
| 2241 ++columnCount; | |
| 2480 | 2242 |
| 2481 if (binforx[x] < 0) continue; | 2243 if (binforx[x] < 0) continue; |
| 2482 | 2244 |
| 2483 int sx0 = binforx[x]; | 2245 int sx0 = binforx[x]; |
| 2484 int sx1 = sx0; | 2246 int sx1 = sx0; |
| 2489 | 2251 |
| 2490 for (int sx = sx0; sx < sx1; ++sx) { | 2252 for (int sx = sx0; sx < sx1; ++sx) { |
| 2491 | 2253 |
| 2492 if (sx < 0 || sx >= int(fft->getWidth())) continue; | 2254 if (sx < 0 || sx >= int(fft->getWidth())) continue; |
| 2493 | 2255 |
| 2494 if (!m_synchronous) { | |
| 2495 if (!fft->isColumnAvailable(sx)) { | |
| 2496 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2497 cerr << "Met unavailable column at col " << sx << endl; | |
| 2498 #endif | |
| 2499 return false; | |
| 2500 } | |
| 2501 } | |
| 2502 | |
| 2503 MagnitudeRange mag; | 2256 MagnitudeRange mag; |
| 2504 | 2257 |
| 2505 if (sx != psx) { | 2258 if (sx != psx) { |
| 2506 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, | 2259 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx, |
| 2507 minbin, maxbin - 1); | 2260 minbin, maxbin - 1); |
| 2508 if (m_colourScale == PhaseColourScale) { | 2261 if (m_colourScale == PhaseColourScale) { |
| 2509 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); | 2262 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1); |
| 2510 } else if (m_normalizeColumns) { | 2263 } else if (m_normalization == NormalizeColumns) { |
| 2511 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 2264 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); |
| 2512 } else if (m_normalizeHybrid) { | 2265 } else if (m_normalization == NormalizeHybrid) { |
| 2513 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 2266 float max = fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); |
| 2514 double max = fft->getMaximumMagnitudeAt(sx); | |
| 2515 if (max > 0.f) { | 2267 if (max > 0.f) { |
| 2516 for (int i = minbin; i <= maxbin; ++i) { | 2268 for (int i = minbin; i <= maxbin; ++i) { |
| 2517 values[i - minbin] = float(values[i - minbin] * log10(max)); | 2269 values[i - minbin] = float(values[i - minbin] * |
| 2270 log10f(max)); | |
| 2518 } | 2271 } |
| 2519 } | 2272 } |
| 2520 } else { | 2273 } else { |
| 2521 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); | 2274 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1); |
| 2522 } | 2275 } |
| 2533 if (bin > maxbin) break; | 2286 if (bin > maxbin) break; |
| 2534 | 2287 |
| 2535 double value = values[bin - minbin]; | 2288 double value = values[bin - minbin]; |
| 2536 | 2289 |
| 2537 if (m_colourScale != PhaseColourScale) { | 2290 if (m_colourScale != PhaseColourScale) { |
| 2538 if (!m_normalizeColumns && !m_normalizeHybrid) { | 2291 if (m_normalization != NormalizeColumns) { |
| 2539 value /= (m_fftSize/2.0); | 2292 value /= (m_fftSize/2.0); |
| 2540 } | 2293 } |
| 2541 mag.sample(float(value)); | 2294 mag.sample(float(value)); |
| 2542 value *= m_gain; | 2295 value *= m_gain; |
| 2543 } | 2296 } |
| 2563 m_columnMags[sx].sample(mag); | 2316 m_columnMags[sx].sample(mag); |
| 2564 if (overallMag.sample(mag)) overallMagChanged = true; | 2317 if (overallMag.sample(mag)) overallMagChanged = true; |
| 2565 } | 2318 } |
| 2566 } | 2319 } |
| 2567 } | 2320 } |
| 2568 } | 2321 |
| 2569 | 2322 if (haveTimeLimits) { |
| 2570 return true; | 2323 if (columnCount >= minColumns) { |
| 2571 } | 2324 auto t = chrono::steady_clock::now(); |
| 2572 | 2325 double diff = chrono::duration<double>(t - startTime).count(); |
| 2573 bool | 2326 if (diff > hardTimeLimit) { |
| 2574 SpectrogramLayer::paintDrawBuffer(View *v, | 2327 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2328 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: hard limit " << hardTimeLimit << " sec exceeded after " | |
| 2329 << columnCount << " columns with time " << diff << endl; | |
| 2330 #endif | |
| 2331 return columnCount; | |
| 2332 } else if (diff > softTimeLimit && !overridingSoftLimit) { | |
| 2333 // If we're more than half way through by the time | |
| 2334 // we reach the soft limit, ignore it (though | |
| 2335 // still respect the hard limit, above). Otherwise | |
| 2336 // respect the soft limit and return now. | |
| 2337 if (columnCount > w/2) { | |
| 2338 overridingSoftLimit = true; | |
| 2339 } else { | |
| 2340 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2341 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: soft limit " << softTimeLimit << " sec exceeded after " | |
| 2342 << columnCount << " columns with time " << diff << endl; | |
| 2343 #endif | |
| 2344 return columnCount; | |
| 2345 } | |
| 2346 } | |
| 2347 } | |
| 2348 } | |
| 2349 } | |
| 2350 | |
| 2351 return columnCount; | |
| 2352 } | |
| 2353 | |
| 2354 int | |
| 2355 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v, | |
| 2575 int w, | 2356 int w, |
| 2576 int h, | 2357 int h, |
| 2577 const vector<int> &binforx, | 2358 const vector<int> &binforx, |
| 2578 const vector<double> &binfory, | 2359 const vector<double> &binfory, |
| 2579 bool usePeaksCache, | 2360 bool usePeaksCache, |
| 2580 MagnitudeRange &overallMag, | 2361 MagnitudeRange &overallMag, |
| 2581 bool &overallMagChanged) const | 2362 bool &overallMagChanged, |
| 2363 bool rightToLeft, | |
| 2364 double softTimeLimit) const | |
| 2582 { | 2365 { |
| 2583 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); | 2366 Profiler profiler("SpectrogramLayer::paintDrawBuffer"); |
| 2584 | 2367 |
| 2585 int minbin = int(binfory[0] + 0.0001); | 2368 int minbin = int(binfory[0] + 0.0001); |
| 2586 int maxbin = int(binfory[h-1]); | 2369 int maxbin = int(binfory[h-1]); |
| 2587 | 2370 |
| 2588 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2371 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2589 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; | 2372 cerr << "SpectrogramLayer::paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl; |
| 2590 #endif | 2373 #endif |
| 2591 if (minbin < 0) minbin = 0; | 2374 if (minbin < 0) minbin = 0; |
| 2592 if (maxbin < 0) maxbin = minbin+1; | 2375 if (maxbin < 0) maxbin = minbin+1; |
| 2593 | 2376 |
| 2594 DenseThreeDimensionalModel *sourceModel = 0; | 2377 DenseThreeDimensionalModel *sourceModel = 0; |
| 2595 FFTModel *fft = 0; | 2378 FFTModel *fft = 0; |
| 2596 int divisor = 1; | 2379 int divisor = 1; |
| 2597 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2380 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2598 cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; | 2381 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl; |
| 2599 #endif | 2382 #endif |
| 2600 if (usePeaksCache) { //!!! | 2383 if (usePeaksCache) { //!!! |
| 2601 sourceModel = getPeakCache(v); | 2384 sourceModel = getPeakCache(v); |
| 2602 divisor = 8;//!!! | 2385 divisor = 8;//!!! |
| 2603 minbin = 0; | 2386 minbin = 0; |
| 2604 maxbin = sourceModel->getHeight(); | 2387 maxbin = sourceModel->getHeight(); |
| 2605 } else { | 2388 } else { |
| 2606 sourceModel = fft = getFFTModel(v); | 2389 sourceModel = fft = getFFTModel(v); |
| 2607 } | 2390 } |
| 2608 | 2391 |
| 2609 if (!sourceModel) return false; | 2392 if (!sourceModel) return 0; |
| 2610 | 2393 |
| 2611 bool interpolate = false; | 2394 bool interpolate = false; |
| 2612 Preferences::SpectrogramSmoothing smoothing = | 2395 Preferences::SpectrogramSmoothing smoothing = |
| 2613 Preferences::getInstance()->getSpectrogramSmoothing(); | 2396 Preferences::getInstance()->getSpectrogramSmoothing(); |
| 2614 if (smoothing == Preferences::SpectrogramInterpolated || | 2397 if (smoothing == Preferences::SpectrogramInterpolated || |
| 2630 #endif | 2413 #endif |
| 2631 | 2414 |
| 2632 const float *values = autoarray; | 2415 const float *values = autoarray; |
| 2633 DenseThreeDimensionalModel::Column c; | 2416 DenseThreeDimensionalModel::Column c; |
| 2634 | 2417 |
| 2635 for (int x = 0; x < w; ++x) { | 2418 int minColumns = 4; |
| 2419 bool haveTimeLimits = (softTimeLimit > 0.0); | |
| 2420 double hardTimeLimit = softTimeLimit * 2.0; | |
| 2421 bool overridingSoftLimit = false; | |
| 2422 auto startTime = chrono::steady_clock::now(); | |
| 2423 | |
| 2424 int start = 0; | |
| 2425 int finish = w; | |
| 2426 int step = 1; | |
| 2427 | |
| 2428 if (rightToLeft) { | |
| 2429 start = w-1; | |
| 2430 finish = -1; | |
| 2431 step = -1; | |
| 2432 } | |
| 2433 | |
| 2434 int columnCount = 0; | |
| 2435 | |
| 2436 for (int x = start; x != finish; x += step) { | |
| 2437 | |
| 2438 ++columnCount; | |
| 2636 | 2439 |
| 2637 if (binforx[x] < 0) continue; | 2440 if (binforx[x] < 0) continue; |
| 2638 | 2441 |
| 2639 // float columnGain = m_gain; | 2442 // float columnGain = m_gain; |
| 2640 float columnMax = 0.f; | 2443 float columnMax = 0.f; |
| 2654 // cerr << "sx = " << sx << endl; | 2457 // cerr << "sx = " << sx << endl; |
| 2655 #endif | 2458 #endif |
| 2656 | 2459 |
| 2657 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; | 2460 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue; |
| 2658 | 2461 |
| 2659 if (!m_synchronous) { | |
| 2660 if (!sourceModel->isColumnAvailable(sx)) { | |
| 2661 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2662 cerr << "Met unavailable column at col " << sx << endl; | |
| 2663 #endif | |
| 2664 return false; | |
| 2665 } | |
| 2666 } | |
| 2667 | |
| 2668 MagnitudeRange mag; | 2462 MagnitudeRange mag; |
| 2669 | 2463 |
| 2670 if (sx != psx) { | 2464 if (sx != psx) { |
| 2671 if (fft) { | 2465 if (fft) { |
| 2672 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2466 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2673 SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl; | 2467 // cerr << "Retrieving column " << sx << " from fft directly" << endl; |
| 2674 #endif | 2468 #endif |
| 2675 if (m_colourScale == PhaseColourScale) { | 2469 if (m_colourScale == PhaseColourScale) { |
| 2676 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 2470 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1); |
| 2677 } else if (m_normalizeColumns) { | 2471 } else if (m_normalization == NormalizeColumns) { |
| 2678 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 2472 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); |
| 2679 } else if (m_normalizeHybrid) { | 2473 } else if (m_normalization == NormalizeHybrid) { |
| 2680 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 2474 float max = fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); |
| 2681 double max = fft->getMaximumMagnitudeAt(sx); | 2475 float scale = log10f(max + 1.f); |
| 2476 // cout << "sx = " << sx << ", max = " << max << ", log10(max) = " << log10(max) << ", scale = " << scale << endl; | |
| 2682 for (int i = minbin; i <= maxbin; ++i) { | 2477 for (int i = minbin; i <= maxbin; ++i) { |
| 2683 if (max > 0.0) { | 2478 autoarray[i - minbin] *= scale; |
| 2684 autoarray[i - minbin] = float(autoarray[i - minbin] * log10(max)); | |
| 2685 } | |
| 2686 } | 2479 } |
| 2687 } else { | 2480 } else { |
| 2688 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); | 2481 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1); |
| 2689 } | 2482 } |
| 2690 } else { | 2483 } else { |
| 2691 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2484 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2692 SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl; | 2485 // cerr << "Retrieving column " << sx << " from peaks cache" << endl; |
| 2693 #endif | 2486 #endif |
| 2694 c = sourceModel->getColumn(sx); | 2487 c = sourceModel->getColumn(sx); |
| 2695 if (m_normalizeColumns || m_normalizeHybrid) { | 2488 if (m_normalization == NormalizeColumns || |
| 2489 m_normalization == NormalizeHybrid) { | |
| 2696 for (int y = 0; y < h; ++y) { | 2490 for (int y = 0; y < h; ++y) { |
| 2697 if (c[y] > columnMax) columnMax = c[y]; | 2491 if (c[y] > columnMax) columnMax = c[y]; |
| 2698 } | 2492 } |
| 2699 } | 2493 } |
| 2700 values = c.constData() + minbin; | 2494 values = c.data() + minbin; |
| 2701 } | 2495 } |
| 2702 psx = sx; | 2496 psx = sx; |
| 2703 } | 2497 } |
| 2704 | 2498 |
| 2705 for (int y = 0; y < h; ++y) { | 2499 for (int y = 0; y < h; ++y) { |
| 2733 } | 2527 } |
| 2734 if (v0 == 0.0 && v1 == 0.0) continue; | 2528 if (v0 == 0.0 && v1 == 0.0) continue; |
| 2735 value = prop * v0 + (1.0 - prop) * v1; | 2529 value = prop * v0 + (1.0 - prop) * v1; |
| 2736 | 2530 |
| 2737 if (m_colourScale != PhaseColourScale) { | 2531 if (m_colourScale != PhaseColourScale) { |
| 2738 if (!m_normalizeColumns) { | 2532 if (m_normalization != NormalizeColumns && |
| 2533 m_normalization != NormalizeHybrid) { | |
| 2739 value /= (m_fftSize/2.0); | 2534 value /= (m_fftSize/2.0); |
| 2740 } | 2535 } |
| 2741 mag.sample(float(value)); | 2536 mag.sample(float(value)); |
| 2742 value *= m_gain; | 2537 value *= m_gain; |
| 2743 } | 2538 } |
| 2758 value < values[bin-minbin-1] || | 2553 value < values[bin-minbin-1] || |
| 2759 value < values[bin-minbin+1]) continue; | 2554 value < values[bin-minbin+1]) continue; |
| 2760 } | 2555 } |
| 2761 | 2556 |
| 2762 if (m_colourScale != PhaseColourScale) { | 2557 if (m_colourScale != PhaseColourScale) { |
| 2763 if (!m_normalizeColumns) { | 2558 if (m_normalization != NormalizeColumns && |
| 2559 m_normalization != NormalizeHybrid) { | |
| 2764 value /= (m_fftSize/2.0); | 2560 value /= (m_fftSize/2.0); |
| 2765 } | 2561 } |
| 2766 mag.sample(float(value)); | 2562 mag.sample(float(value)); |
| 2767 value *= m_gain; | 2563 value *= m_gain; |
| 2768 } | 2564 } |
| 2792 for (int y = 0; y < h; ++y) { | 2588 for (int y = 0; y < h; ++y) { |
| 2793 | 2589 |
| 2794 double peak = peaks[y]; | 2590 double peak = peaks[y]; |
| 2795 | 2591 |
| 2796 if (m_colourScale != PhaseColourScale && | 2592 if (m_colourScale != PhaseColourScale && |
| 2797 (m_normalizeColumns || m_normalizeHybrid) && | 2593 (m_normalization == NormalizeColumns || |
| 2594 m_normalization == NormalizeHybrid) && | |
| 2798 columnMax > 0.f) { | 2595 columnMax > 0.f) { |
| 2799 peak /= columnMax; | 2596 peak /= columnMax; |
| 2800 if (m_normalizeHybrid) { | 2597 if (m_normalization == NormalizeHybrid) { |
| 2801 peak *= log10(columnMax); | 2598 peak *= log10(columnMax + 1.f); |
| 2802 } | 2599 } |
| 2803 } | 2600 } |
| 2804 | 2601 |
| 2805 unsigned char peakpix = getDisplayValue(v, peak); | 2602 unsigned char peakpix = getDisplayValue(v, peak); |
| 2806 | 2603 |
| 2807 m_drawBuffer.setPixel(x, h-y-1, peakpix); | 2604 m_drawBuffer.setPixel(x, h-y-1, peakpix); |
| 2808 } | 2605 } |
| 2809 } | 2606 |
| 2810 | 2607 if (haveTimeLimits) { |
| 2811 return true; | 2608 if (columnCount >= minColumns) { |
| 2609 auto t = chrono::steady_clock::now(); | |
| 2610 double diff = chrono::duration<double>(t - startTime).count(); | |
| 2611 if (diff > hardTimeLimit) { | |
| 2612 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2613 cerr << "SpectrogramLayer::paintDrawBuffer: hard limit " << hardTimeLimit << " sec exceeded after " | |
| 2614 << columnCount << " columns with time " << diff << endl; | |
| 2615 #endif | |
| 2616 return columnCount; | |
| 2617 } else if (diff > softTimeLimit && !overridingSoftLimit) { | |
| 2618 // If we're more than half way through by the time | |
| 2619 // we reach the soft limit, ignore it (though | |
| 2620 // still respect the hard limit, above). Otherwise | |
| 2621 // respect the soft limit and return now. | |
| 2622 if (columnCount > w/2) { | |
| 2623 overridingSoftLimit = true; | |
| 2624 } else { | |
| 2625 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 2626 cerr << "SpectrogramLayer::paintDrawBuffer: soft limit " << softTimeLimit << " sec exceeded after " | |
| 2627 << columnCount << " columns with time " << diff << endl; | |
| 2628 #endif | |
| 2629 return columnCount; | |
| 2630 } | |
| 2631 } | |
| 2632 } | |
| 2633 } | |
| 2634 } | |
| 2635 | |
| 2636 return columnCount; | |
| 2812 } | 2637 } |
| 2813 | 2638 |
| 2814 void | 2639 void |
| 2815 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const | 2640 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const |
| 2816 { | 2641 { |
| 2817 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); | 2642 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); |
| 2818 | 2643 |
| 2819 QPoint localPos; | 2644 QPoint localPos; |
| 2820 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { | 2645 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { |
| 2849 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); | 2674 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); |
| 2850 } | 2675 } |
| 2851 } | 2676 } |
| 2852 | 2677 |
| 2853 double | 2678 double |
| 2854 SpectrogramLayer::getYForFrequency(const View *v, double frequency) const | 2679 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const |
| 2855 { | 2680 { |
| 2856 return v->getYForFrequency(frequency, | 2681 return v->getYForFrequency(frequency, |
| 2857 getEffectiveMinFrequency(), | 2682 getEffectiveMinFrequency(), |
| 2858 getEffectiveMaxFrequency(), | 2683 getEffectiveMaxFrequency(), |
| 2859 m_frequencyScale == LogFrequencyScale); | 2684 m_frequencyScale == LogFrequencyScale); |
| 2860 } | 2685 } |
| 2861 | 2686 |
| 2862 double | 2687 double |
| 2863 SpectrogramLayer::getFrequencyForY(const View *v, int y) const | 2688 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const |
| 2864 { | 2689 { |
| 2865 return v->getFrequencyForY(y, | 2690 return v->getFrequencyForY(y, |
| 2866 getEffectiveMinFrequency(), | 2691 getEffectiveMinFrequency(), |
| 2867 getEffectiveMaxFrequency(), | 2692 getEffectiveMaxFrequency(), |
| 2868 m_frequencyScale == LogFrequencyScale); | 2693 m_frequencyScale == LogFrequencyScale); |
| 2869 } | 2694 } |
| 2870 | 2695 |
| 2871 int | 2696 int |
| 2872 SpectrogramLayer::getCompletion(View *v) const | 2697 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const |
| 2873 { | 2698 { |
| 2874 if (m_updateTimer == 0) return 100; | 2699 const View *view = v->getView(); |
| 2875 if (m_fftModels.find(v) == m_fftModels.end()) return 100; | 2700 |
| 2876 | 2701 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return 100; |
| 2877 int completion = m_fftModels[v].first->getCompletion(); | 2702 |
| 2878 #ifdef DEBUG_SPECTROGRAM_REPAINT | 2703 int completion = m_fftModels[view->getId()]->getCompletion(); |
| 2879 SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl; | 2704 #ifdef DEBUG_SPECTROGRAM_REPAINT |
| 2705 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl; | |
| 2880 #endif | 2706 #endif |
| 2881 return completion; | 2707 return completion; |
| 2882 } | 2708 } |
| 2883 | 2709 |
| 2884 QString | 2710 QString |
| 2885 SpectrogramLayer::getError(View *v) const | 2711 SpectrogramLayer::getError(LayerGeometryProvider *v) const |
| 2886 { | 2712 { |
| 2887 if (m_fftModels.find(v) == m_fftModels.end()) return ""; | 2713 const View *view = v->getView(); |
| 2888 return m_fftModels[v].first->getError(); | 2714 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return ""; |
| 2715 return m_fftModels[view->getId()]->getError(); | |
| 2889 } | 2716 } |
| 2890 | 2717 |
| 2891 bool | 2718 bool |
| 2892 SpectrogramLayer::getValueExtents(double &min, double &max, | 2719 SpectrogramLayer::getValueExtents(double &min, double &max, |
| 2893 bool &logarithmic, QString &unit) const | 2720 bool &logarithmic, QString &unit) const |
| 2944 | 2771 |
| 2945 return true; | 2772 return true; |
| 2946 } | 2773 } |
| 2947 | 2774 |
| 2948 bool | 2775 bool |
| 2949 SpectrogramLayer::getYScaleValue(const View *v, int y, | 2776 SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y, |
| 2950 double &value, QString &unit) const | 2777 double &value, QString &unit) const |
| 2951 { | 2778 { |
| 2952 value = getFrequencyForY(v, y); | 2779 value = getFrequencyForY(v, y); |
| 2953 unit = "Hz"; | 2780 unit = "Hz"; |
| 2954 return true; | 2781 return true; |
| 2955 } | 2782 } |
| 2956 | 2783 |
| 2957 bool | 2784 bool |
| 2958 SpectrogramLayer::snapToFeatureFrame(View *, | 2785 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *, |
| 2959 sv_frame_t &frame, | 2786 sv_frame_t &frame, |
| 2960 int &resolution, | 2787 int &resolution, |
| 2961 SnapType snap) const | 2788 SnapType snap) const |
| 2962 { | 2789 { |
| 2963 resolution = getWindowIncrement(); | 2790 resolution = getWindowIncrement(); |
| 2976 | 2803 |
| 2977 return true; | 2804 return true; |
| 2978 } | 2805 } |
| 2979 | 2806 |
| 2980 void | 2807 void |
| 2981 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e) | 2808 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) |
| 2982 { | 2809 { |
| 2983 ImageCache &cache = m_imageCaches[v]; | 2810 const View *view = v->getView(); |
| 2984 | 2811 ScrollableImageCache &cache = getImageCacheReference(view); |
| 2985 cerr << "cache width: " << cache.image.width() << ", height: " | 2812 |
| 2986 << cache.image.height() << endl; | 2813 cerr << "cache width: " << cache.getSize().width() << ", height: " |
| 2987 | 2814 << cache.getSize().height() << endl; |
| 2988 QImage image = cache.image; | 2815 |
| 2816 QImage image = cache.getImage(); | |
| 2989 | 2817 |
| 2990 ImageRegionFinder finder; | 2818 ImageRegionFinder finder; |
| 2991 QRect rect = finder.findRegionExtents(&image, e->pos()); | 2819 QRect rect = finder.findRegionExtents(&image, e->pos()); |
| 2992 if (rect.isValid()) { | 2820 if (rect.isValid()) { |
| 2993 MeasureRect mr; | 2821 MeasureRect mr; |
| 2996 (new AddMeasurementRectCommand(this, mr)); | 2824 (new AddMeasurementRectCommand(this, mr)); |
| 2997 } | 2825 } |
| 2998 } | 2826 } |
| 2999 | 2827 |
| 3000 bool | 2828 bool |
| 3001 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint, | 2829 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, |
| 3002 QPoint cursorPos, | 2830 QPoint cursorPos, |
| 3003 std::vector<QRect> &extents) const | 2831 vector<QRect> &extents) const |
| 3004 { | 2832 { |
| 3005 QRect vertical(cursorPos.x() - 12, 0, 12, v->height()); | 2833 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight()); |
| 3006 extents.push_back(vertical); | 2834 extents.push_back(vertical); |
| 3007 | 2835 |
| 3008 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); | 2836 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); |
| 3009 extents.push_back(horizontal); | 2837 extents.push_back(horizontal); |
| 3010 | 2838 |
| 3019 paint.fontMetrics().width("C#10+50c") + 2, | 2847 paint.fontMetrics().width("C#10+50c") + 2, |
| 3020 paint.fontMetrics().height()); | 2848 paint.fontMetrics().height()); |
| 3021 extents.push_back(pitch); | 2849 extents.push_back(pitch); |
| 3022 | 2850 |
| 3023 QRect rt(cursorPos.x(), | 2851 QRect rt(cursorPos.x(), |
| 3024 v->height() - paint.fontMetrics().height() - 2, | 2852 v->getPaintHeight() - paint.fontMetrics().height() - 2, |
| 3025 paint.fontMetrics().width("1234.567 s"), | 2853 paint.fontMetrics().width("1234.567 s"), |
| 3026 paint.fontMetrics().height()); | 2854 paint.fontMetrics().height()); |
| 3027 extents.push_back(rt); | 2855 extents.push_back(rt); |
| 3028 | 2856 |
| 3029 int w(paint.fontMetrics().width("1234567890") + 2); | 2857 int w(paint.fontMetrics().width("1234567890") + 2); |
| 3030 QRect frame(cursorPos.x() - w - 2, | 2858 QRect frame(cursorPos.x() - w - 2, |
| 3031 v->height() - paint.fontMetrics().height() - 2, | 2859 v->getPaintHeight() - paint.fontMetrics().height() - 2, |
| 3032 w, | 2860 w, |
| 3033 paint.fontMetrics().height()); | 2861 paint.fontMetrics().height()); |
| 3034 extents.push_back(frame); | 2862 extents.push_back(frame); |
| 3035 | 2863 |
| 3036 return true; | 2864 return true; |
| 3037 } | 2865 } |
| 3038 | 2866 |
| 3039 void | 2867 void |
| 3040 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint, | 2868 SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint, |
| 3041 QPoint cursorPos) const | 2869 QPoint cursorPos) const |
| 3042 { | 2870 { |
| 3043 paint.save(); | 2871 paint.save(); |
| 3044 | 2872 |
| 3045 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); | 2873 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); |
| 3050 paint.setFont(fn); | 2878 paint.setFont(fn); |
| 3051 } | 2879 } |
| 3052 paint.setPen(m_crosshairColour); | 2880 paint.setPen(m_crosshairColour); |
| 3053 | 2881 |
| 3054 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); | 2882 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); |
| 3055 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height()); | 2883 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight()); |
| 3056 | 2884 |
| 3057 double fundamental = getFrequencyForY(v, cursorPos.y()); | 2885 double fundamental = getFrequencyForY(v, cursorPos.y()); |
| 3058 | 2886 |
| 3059 v->drawVisibleText(paint, | 2887 v->drawVisibleText(paint, |
| 3060 sw + 2, | 2888 sw + 2, |
| 3075 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); | 2903 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); |
| 3076 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); | 2904 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); |
| 3077 QString frameLabel = QString("%1").arg(frame); | 2905 QString frameLabel = QString("%1").arg(frame); |
| 3078 v->drawVisibleText(paint, | 2906 v->drawVisibleText(paint, |
| 3079 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, | 2907 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, |
| 3080 v->height() - 2, | 2908 v->getPaintHeight() - 2, |
| 3081 frameLabel, | 2909 frameLabel, |
| 3082 View::OutlinedText); | 2910 View::OutlinedText); |
| 3083 v->drawVisibleText(paint, | 2911 v->drawVisibleText(paint, |
| 3084 cursorPos.x() + 2, | 2912 cursorPos.x() + 2, |
| 3085 v->height() - 2, | 2913 v->getPaintHeight() - 2, |
| 3086 rtLabel, | 2914 rtLabel, |
| 3087 View::OutlinedText); | 2915 View::OutlinedText); |
| 3088 | 2916 |
| 3089 int harmonic = 2; | 2917 int harmonic = 2; |
| 3090 | 2918 |
| 3091 while (harmonic < 100) { | 2919 while (harmonic < 100) { |
| 3092 | 2920 |
| 3093 int hy = int(lrint(getYForFrequency(v, fundamental * harmonic))); | 2921 int hy = int(lrint(getYForFrequency(v, fundamental * harmonic))); |
| 3094 if (hy < 0 || hy > v->height()) break; | 2922 if (hy < 0 || hy > v->getPaintHeight()) break; |
| 3095 | 2923 |
| 3096 int len = 7; | 2924 int len = 7; |
| 3097 | 2925 |
| 3098 if (harmonic % 2 == 0) { | 2926 if (harmonic % 2 == 0) { |
| 3099 if (harmonic % 4 == 0) { | 2927 if (harmonic % 4 == 0) { |
| 3113 | 2941 |
| 3114 paint.restore(); | 2942 paint.restore(); |
| 3115 } | 2943 } |
| 3116 | 2944 |
| 3117 QString | 2945 QString |
| 3118 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const | 2946 SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const |
| 3119 { | 2947 { |
| 3120 int x = pos.x(); | 2948 int x = pos.x(); |
| 3121 int y = pos.y(); | 2949 int y = pos.y(); |
| 3122 | 2950 |
| 3123 if (!m_model || !m_model->isOK()) return ""; | 2951 if (!m_model || !m_model->isOK()) return ""; |
| 3235 | 3063 |
| 3236 return cw; | 3064 return cw; |
| 3237 } | 3065 } |
| 3238 | 3066 |
| 3239 int | 3067 int |
| 3240 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const | 3068 SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const |
| 3241 { | 3069 { |
| 3242 if (!m_model || !m_model->isOK()) return 0; | 3070 if (!m_model || !m_model->isOK()) return 0; |
| 3243 | 3071 |
| 3244 int cw = 0; | 3072 int cw = 0; |
| 3245 if (detailed) cw = getColourScaleWidth(paint); | 3073 if (detailed) cw = getColourScaleWidth(paint); |
| 3256 | 3084 |
| 3257 return cw + tickw + tw + 13; | 3085 return cw + tickw + tw + 13; |
| 3258 } | 3086 } |
| 3259 | 3087 |
| 3260 void | 3088 void |
| 3261 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const | 3089 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const |
| 3262 { | 3090 { |
| 3263 if (!m_model || !m_model->isOK()) { | 3091 if (!m_model || !m_model->isOK()) { |
| 3264 return; | 3092 return; |
| 3265 } | 3093 } |
| 3266 | 3094 |
| 3298 int ch = h - textHeight * (topLines + 1) - 8; | 3126 int ch = h - textHeight * (topLines + 1) - 8; |
| 3299 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); | 3127 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1); |
| 3300 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); | 3128 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1); |
| 3301 | 3129 |
| 3302 QString top, bottom; | 3130 QString top, bottom; |
| 3303 double min = m_viewMags[v].getMin(); | 3131 double min = m_viewMags[v->getId()].getMin(); |
| 3304 double max = m_viewMags[v].getMax(); | 3132 double max = m_viewMags[v->getId()].getMax(); |
| 3305 | 3133 |
| 3306 double dBmin = AudioLevel::multiplier_to_dB(min); | 3134 double dBmin = AudioLevel::multiplier_to_dB(min); |
| 3307 double dBmax = AudioLevel::multiplier_to_dB(max); | 3135 double dBmax = AudioLevel::multiplier_to_dB(max); |
| 3308 | 3136 |
| 3137 #ifdef DEBUG_SPECTROGRAM_REPAINT | |
| 3138 cerr << "paintVerticalScale: for view id " << v->getId() | |
| 3139 << ": min = " << min << ", max = " << max | |
| 3140 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl; | |
| 3141 #endif | |
| 3142 | |
| 3309 if (dBmax < -60.f) dBmax = -60.f; | 3143 if (dBmax < -60.f) dBmax = -60.f; |
| 3310 else top = QString("%1").arg(lrint(dBmax)); | 3144 else top = QString("%1").arg(lrint(dBmax)); |
| 3311 | 3145 |
| 3312 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; | 3146 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f; |
| 3313 bottom = QString("%1").arg(lrint(dBmin)); | 3147 bottom = QString("%1").arg(lrint(dBmin)); |
| 3370 | 3204 |
| 3371 paint.drawLine(cw + 7, 0, cw + 7, h); | 3205 paint.drawLine(cw + 7, 0, cw + 7, h); |
| 3372 | 3206 |
| 3373 int bin = -1; | 3207 int bin = -1; |
| 3374 | 3208 |
| 3375 for (int y = 0; y < v->height(); ++y) { | 3209 for (int y = 0; y < v->getPaintHeight(); ++y) { |
| 3376 | 3210 |
| 3377 double q0, q1; | 3211 double q0, q1; |
| 3378 if (!getYBinRange(v, v->height() - y, q0, q1)) continue; | 3212 if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue; |
| 3379 | 3213 |
| 3380 int vy; | 3214 int vy; |
| 3381 | 3215 |
| 3382 if (int(q0) > bin) { | 3216 if (int(q0) > bin) { |
| 3383 vy = y; | 3217 vy = y; |
| 3398 QString text = QString("%1").arg(freq); | 3232 QString text = QString("%1").arg(freq); |
| 3399 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC | 3233 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC |
| 3400 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); | 3234 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); |
| 3401 | 3235 |
| 3402 if (h - vy - textHeight >= -2) { | 3236 if (h - vy - textHeight >= -2) { |
| 3403 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); | 3237 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw); |
| 3404 paint.drawText(tx, h - vy + toff, text); | 3238 paint.drawText(tx, h - vy + toff, text); |
| 3405 } | 3239 } |
| 3406 | 3240 |
| 3407 py = vy; | 3241 py = vy; |
| 3408 } | 3242 } |
| 3587 if (!m_model) return 0; | 3421 if (!m_model) return 0; |
| 3588 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); | 3422 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); |
| 3589 } | 3423 } |
| 3590 | 3424 |
| 3591 void | 3425 void |
| 3592 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const | 3426 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const |
| 3593 { | 3427 { |
| 3594 int y0 = 0; | 3428 int y0 = 0; |
| 3595 if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY)); | 3429 if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY)); |
| 3596 | 3430 |
| 3597 int y1 = y0; | 3431 int y1 = y0; |
| 3601 | 3435 |
| 3602 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); | 3436 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); |
| 3603 } | 3437 } |
| 3604 | 3438 |
| 3605 void | 3439 void |
| 3606 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const | 3440 SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const |
| 3607 { | 3441 { |
| 3608 if (start) { | 3442 if (start) { |
| 3609 r.startY = getFrequencyForY(v, y); | 3443 r.startY = getFrequencyForY(v, y); |
| 3610 r.endY = r.startY; | 3444 r.endY = r.startY; |
| 3611 } else { | 3445 } else { |
| 3645 .arg(m_colourMap) | 3479 .arg(m_colourMap) |
| 3646 .arg(m_colourRotation) | 3480 .arg(m_colourRotation) |
| 3647 .arg(m_frequencyScale) | 3481 .arg(m_frequencyScale) |
| 3648 .arg(m_binDisplay); | 3482 .arg(m_binDisplay); |
| 3649 | 3483 |
| 3650 s += QString("normalizeColumns=\"%1\" " | 3484 // New-style normalization attributes, allowing for more types of |
| 3651 "normalizeVisibleArea=\"%2\" " | 3485 // normalization in future: write out the column normalization |
| 3652 "normalizeHybrid=\"%3\" ") | 3486 // type separately, and then whether we are normalizing visible |
| 3653 .arg(m_normalizeColumns ? "true" : "false") | 3487 // area as well afterwards |
| 3654 .arg(m_normalizeVisibleArea ? "true" : "false") | 3488 |
| 3655 .arg(m_normalizeHybrid ? "true" : "false"); | 3489 s += QString("columnNormalization=\"%1\" ") |
| 3656 | 3490 .arg(m_normalization == NormalizeColumns ? "peak" : |
| 3491 m_normalization == NormalizeHybrid ? "hybrid" : "none"); | |
| 3492 | |
| 3493 // Old-style normalization attribute. We *don't* write out | |
| 3494 // normalizeHybrid here because the only release that would accept | |
| 3495 // it (Tony v1.0) has a totally different scale factor for | |
| 3496 // it. We'll just have to accept that session files from Tony | |
| 3497 // v2.0+ will look odd in Tony v1.0 | |
| 3498 | |
| 3499 s += QString("normalizeColumns=\"%1\" ") | |
| 3500 .arg(m_normalization == NormalizeColumns ? "true" : "false"); | |
| 3501 | |
| 3502 // And this applies to both old- and new-style attributes | |
| 3503 | |
| 3504 s += QString("normalizeVisibleArea=\"%1\" ") | |
| 3505 .arg(m_normalization == NormalizeVisibleArea ? "true" : "false"); | |
| 3506 | |
| 3657 Layer::toXml(stream, indent, extraAttributes + " " + s); | 3507 Layer::toXml(stream, indent, extraAttributes + " " + s); |
| 3658 } | 3508 } |
| 3659 | 3509 |
| 3660 void | 3510 void |
| 3661 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) | 3511 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) |
| 3716 | 3566 |
| 3717 BinDisplay binDisplay = (BinDisplay) | 3567 BinDisplay binDisplay = (BinDisplay) |
| 3718 attributes.value("binDisplay").toInt(&ok); | 3568 attributes.value("binDisplay").toInt(&ok); |
| 3719 if (ok) setBinDisplay(binDisplay); | 3569 if (ok) setBinDisplay(binDisplay); |
| 3720 | 3570 |
| 3721 bool normalizeColumns = | 3571 bool haveNewStyleNormalization = false; |
| 3722 (attributes.value("normalizeColumns").trimmed() == "true"); | 3572 |
| 3723 setNormalizeColumns(normalizeColumns); | 3573 QString columnNormalization = attributes.value("columnNormalization"); |
| 3574 | |
| 3575 if (columnNormalization != "") { | |
| 3576 | |
| 3577 haveNewStyleNormalization = true; | |
| 3578 | |
| 3579 if (columnNormalization == "peak") { | |
| 3580 setNormalization(NormalizeColumns); | |
| 3581 } else if (columnNormalization == "hybrid") { | |
| 3582 setNormalization(NormalizeHybrid); | |
| 3583 } else if (columnNormalization == "none") { | |
| 3584 // do nothing | |
| 3585 } else { | |
| 3586 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" | |
| 3587 << columnNormalization << "\"" << endl; | |
| 3588 } | |
| 3589 } | |
| 3590 | |
| 3591 if (!haveNewStyleNormalization) { | |
| 3592 | |
| 3593 bool normalizeColumns = | |
| 3594 (attributes.value("normalizeColumns").trimmed() == "true"); | |
| 3595 if (normalizeColumns) { | |
| 3596 setNormalization(NormalizeColumns); | |
| 3597 } | |
| 3598 | |
| 3599 bool normalizeHybrid = | |
| 3600 (attributes.value("normalizeHybrid").trimmed() == "true"); | |
| 3601 if (normalizeHybrid) { | |
| 3602 setNormalization(NormalizeHybrid); | |
| 3603 } | |
| 3604 } | |
| 3724 | 3605 |
| 3725 bool normalizeVisibleArea = | 3606 bool normalizeVisibleArea = |
| 3726 (attributes.value("normalizeVisibleArea").trimmed() == "true"); | 3607 (attributes.value("normalizeVisibleArea").trimmed() == "true"); |
| 3727 setNormalizeVisibleArea(normalizeVisibleArea); | 3608 if (normalizeVisibleArea) { |
| 3728 | 3609 setNormalization(NormalizeVisibleArea); |
| 3729 bool normalizeHybrid = | 3610 } |
| 3730 (attributes.value("normalizeHybrid").trimmed() == "true"); | 3611 |
| 3731 setNormalizeHybrid(normalizeHybrid); | 3612 if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) { |
| 3732 } | 3613 // Tony v1.0 is (and hopefully will remain!) the only released |
| 3733 | 3614 // SV-a-like to use old-style attributes when saving sessions |
| 3615 // that ask for hybrid normalization. It saves them with the | |
| 3616 // wrong gain factor, so hack in a fix for that here -- this | |
| 3617 // gives us backward but not forward compatibility. | |
| 3618 setGain(m_gain / float(m_fftSize / 2)); | |
| 3619 } | |
| 3620 } | |
| 3621 |
