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