comparison layer/SpectrogramLayer.cpp @ 1146:74f2706995b7 3.0-integration

Merge work on unified spectrogram and colour 3d plot caching renderer
author Chris Cannam
date Fri, 05 Aug 2016 15:05:02 +0100
parents 17f999cd0a22
children 1badacff7ab2
comparison
equal deleted inserted replaced
1056:b4fd6c67fce5 1146:74f2706995b7
21 #include "base/Window.h" 21 #include "base/Window.h"
22 #include "base/Pitch.h" 22 #include "base/Pitch.h"
23 #include "base/Preferences.h" 23 #include "base/Preferences.h"
24 #include "base/RangeMapper.h" 24 #include "base/RangeMapper.h"
25 #include "base/LogRange.h" 25 #include "base/LogRange.h"
26 #include "base/ColumnOp.h"
26 #include "widgets/CommandHistory.h" 27 #include "widgets/CommandHistory.h"
28 #include "data/model/Dense3DModelPeakCache.h"
29
27 #include "ColourMapper.h" 30 #include "ColourMapper.h"
28 #include "ImageRegionFinder.h"
29 #include "data/model/Dense3DModelPeakCache.h"
30 #include "PianoScale.h" 31 #include "PianoScale.h"
32 #include "PaintAssistant.h"
33 #include "Colour3DPlotRenderer.h"
31 34
32 #include <QPainter> 35 #include <QPainter>
33 #include <QImage> 36 #include <QImage>
34 #include <QPixmap> 37 #include <QPixmap>
35 #include <QRect> 38 #include <QRect>
46 49
47 #ifndef __GNUC__ 50 #ifndef __GNUC__
48 #include <alloca.h> 51 #include <alloca.h>
49 #endif 52 #endif
50 53
51 #define DEBUG_SPECTROGRAM_REPAINT 1 54 //#define DEBUG_SPECTROGRAM 1
55 //#define DEBUG_SPECTROGRAM_REPAINT 1
52 56
53 using namespace std; 57 using namespace std;
54 58
55 SpectrogramLayer::SpectrogramLayer(Configuration config) : 59 SpectrogramLayer::SpectrogramLayer(Configuration config) :
56 m_model(0), 60 m_model(0),
57 m_channel(0), 61 m_channel(0),
58 m_windowSize(1024), 62 m_windowSize(1024),
59 m_windowType(HanningWindow), 63 m_windowType(HanningWindow),
60 m_windowHopLevel(2), 64 m_windowHopLevel(2),
61 m_zeroPadLevel(0),
62 m_fftSize(1024),
63 m_gain(1.0), 65 m_gain(1.0),
64 m_initialGain(1.0), 66 m_initialGain(1.0),
65 m_threshold(0.0), 67 m_threshold(1.0e-8f),
66 m_initialThreshold(0.0), 68 m_initialThreshold(1.0e-8f),
67 m_colourRotation(0), 69 m_colourRotation(0),
68 m_initialRotation(0), 70 m_initialRotation(0),
69 m_minFrequency(10), 71 m_minFrequency(10),
70 m_maxFrequency(8000), 72 m_maxFrequency(8000),
71 m_initialMaxFrequency(8000), 73 m_initialMaxFrequency(8000),
72 m_colourScale(dBColourScale), 74 m_colourScale(ColourScaleType::Log),
75 m_colourScaleMultiple(1.0),
73 m_colourMap(0), 76 m_colourMap(0),
74 m_frequencyScale(LinearFrequencyScale), 77 m_binScale(BinScale::Linear),
75 m_binDisplay(AllBins), 78 m_binDisplay(BinDisplay::AllBins),
76 m_normalization(NoNormalization), 79 m_normalization(ColumnNormalization::None),
80 m_normalizeVisibleArea(false),
77 m_lastEmittedZoomStep(-1), 81 m_lastEmittedZoomStep(-1),
78 m_synchronous(false), 82 m_synchronous(false),
79 m_haveDetailedScale(false), 83 m_haveDetailedScale(false),
80 m_exiting(false), 84 m_exiting(false),
81 m_peakCacheDivisor(8), 85 m_fftModel(0),
82 m_sliceableModel(0) 86 m_peakCache(0),
87 m_peakCacheDivisor(8)
83 { 88 {
84 QString colourConfigName = "spectrogram-colour"; 89 QString colourConfigName = "spectrogram-colour";
85 int colourConfigDefault = int(ColourMapper::Green); 90 int colourConfigDefault = int(ColourMapper::Green);
86 91
87 if (config == FullRangeDb) { 92 if (config == FullRangeDb) {
91 setWindowSize(8192); 96 setWindowSize(8192);
92 setWindowHopLevel(4); 97 setWindowHopLevel(4);
93 m_initialMaxFrequency = 1500; 98 m_initialMaxFrequency = 1500;
94 setMaxFrequency(1500); 99 setMaxFrequency(1500);
95 setMinFrequency(40); 100 setMinFrequency(40);
96 setColourScale(LinearColourScale); 101 setColourScale(ColourScaleType::Linear);
97 setColourMap(ColourMapper::Sunset); 102 setColourMap(ColourMapper::Sunset);
98 setFrequencyScale(LogFrequencyScale); 103 setBinScale(BinScale::Log);
99 colourConfigName = "spectrogram-melodic-colour"; 104 colourConfigName = "spectrogram-melodic-colour";
100 colourConfigDefault = int(ColourMapper::Sunset); 105 colourConfigDefault = int(ColourMapper::Sunset);
101 // setGain(20); 106 // setGain(20);
102 } else if (config == MelodicPeaks) { 107 } else if (config == MelodicPeaks) {
103 setWindowSize(4096); 108 setWindowSize(4096);
104 setWindowHopLevel(5); 109 setWindowHopLevel(5);
105 m_initialMaxFrequency = 2000; 110 m_initialMaxFrequency = 2000;
106 setMaxFrequency(2000); 111 setMaxFrequency(2000);
107 setMinFrequency(40); 112 setMinFrequency(40);
108 setFrequencyScale(LogFrequencyScale); 113 setBinScale(BinScale::Log);
109 setColourScale(LinearColourScale); 114 setColourScale(ColourScaleType::Linear);
110 setBinDisplay(PeakFrequencies); 115 setBinDisplay(BinDisplay::PeakFrequencies);
111 setNormalization(NormalizeColumns); 116 setNormalization(ColumnNormalization::Max1);
112 colourConfigName = "spectrogram-melodic-colour"; 117 colourConfigName = "spectrogram-melodic-colour";
113 colourConfigDefault = int(ColourMapper::Sunset); 118 colourConfigDefault = int(ColourMapper::Sunset);
114 } 119 }
115 120
116 QSettings settings; 121 QSettings settings;
120 125
121 Preferences *prefs = Preferences::getInstance(); 126 Preferences *prefs = Preferences::getInstance();
122 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 127 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
123 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 128 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
124 setWindowType(prefs->getWindowType()); 129 setWindowType(prefs->getWindowType());
125
126 initialisePalette();
127 } 130 }
128 131
129 SpectrogramLayer::~SpectrogramLayer() 132 SpectrogramLayer::~SpectrogramLayer()
130 { 133 {
131 invalidateFFTModels(); 134 invalidateRenderers();
135 invalidateFFTModel();
136 }
137
138 pair<ColourScaleType, double>
139 SpectrogramLayer::convertToColourScale(int value)
140 {
141 switch (value) {
142 case 0: return { ColourScaleType::Linear, 1.0 };
143 case 1: return { ColourScaleType::Meter, 1.0 };
144 case 2: return { ColourScaleType::Log, 2.0 }; // dB^2 (i.e. log of power)
145 case 3: return { ColourScaleType::Log, 1.0 }; // dB (of magnitude)
146 case 4: return { ColourScaleType::Phase, 1.0 };
147 default: return { ColourScaleType::Linear, 1.0 };
148 }
149 }
150
151 int
152 SpectrogramLayer::convertFromColourScale(ColourScaleType scale, double multiple)
153 {
154 switch (scale) {
155 case ColourScaleType::Linear: return 0;
156 case ColourScaleType::Meter: return 1;
157 case ColourScaleType::Log: return (multiple > 1.5 ? 2 : 3);
158 case ColourScaleType::Phase: return 4;
159 case ColourScaleType::PlusMinusOne:
160 case ColourScaleType::Absolute:
161 default: return 0;
162 }
163 }
164
165 std::pair<ColumnNormalization, bool>
166 SpectrogramLayer::convertToColumnNorm(int value)
167 {
168 switch (value) {
169 default:
170 case 0: return { ColumnNormalization::None, false };
171 case 1: return { ColumnNormalization::Max1, false };
172 case 2: return { ColumnNormalization::None, true }; // visible area
173 case 3: return { ColumnNormalization::Hybrid, false };
174 }
175 }
176
177 int
178 SpectrogramLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
179 {
180 if (visible) return 2;
181 switch (norm) {
182 case ColumnNormalization::None: return 0;
183 case ColumnNormalization::Max1: return 1;
184 case ColumnNormalization::Hybrid: return 3;
185
186 case ColumnNormalization::Sum1:
187 default: return 0;
188 }
132 } 189 }
133 190
134 void 191 void
135 SpectrogramLayer::setModel(const DenseTimeValueModel *model) 192 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
136 { 193 {
137 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; 194 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl;
138 195
139 if (model == m_model) return; 196 if (model == m_model) return;
140 197
141 m_model = model; 198 m_model = model;
142 invalidateFFTModels(); 199 invalidateFFTModel();
143 200
144 if (!m_model || !m_model->isOK()) return; 201 if (!m_model || !m_model->isOK()) return;
145 202
146 connectSignals(m_model); 203 connectSignals(m_model);
147 204
166 list.push_back("Gain"); 223 list.push_back("Gain");
167 list.push_back("Colour Rotation"); 224 list.push_back("Colour Rotation");
168 // list.push_back("Min Frequency"); 225 // list.push_back("Min Frequency");
169 // list.push_back("Max Frequency"); 226 // list.push_back("Max Frequency");
170 list.push_back("Frequency Scale"); 227 list.push_back("Frequency Scale");
171 //// list.push_back("Zero Padding");
172 return list; 228 return list;
173 } 229 }
174 230
175 QString 231 QString
176 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const 232 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
185 if (name == "Gain") return tr("Gain"); 241 if (name == "Gain") return tr("Gain");
186 if (name == "Colour Rotation") return tr("Colour Rotation"); 242 if (name == "Colour Rotation") return tr("Colour Rotation");
187 if (name == "Min Frequency") return tr("Min Frequency"); 243 if (name == "Min Frequency") return tr("Min Frequency");
188 if (name == "Max Frequency") return tr("Max Frequency"); 244 if (name == "Max Frequency") return tr("Max Frequency");
189 if (name == "Frequency Scale") return tr("Frequency Scale"); 245 if (name == "Frequency Scale") return tr("Frequency Scale");
190 if (name == "Zero Padding") return tr("Smoothing");
191 return ""; 246 return "";
192 } 247 }
193 248
194 QString 249 QString
195 SpectrogramLayer::getPropertyIconName(const PropertyName &) const 250 SpectrogramLayer::getPropertyIconName(const PropertyName &) const
201 SpectrogramLayer::getPropertyType(const PropertyName &name) const 256 SpectrogramLayer::getPropertyType(const PropertyName &name) const
202 { 257 {
203 if (name == "Gain") return RangeProperty; 258 if (name == "Gain") return RangeProperty;
204 if (name == "Colour Rotation") return RangeProperty; 259 if (name == "Colour Rotation") return RangeProperty;
205 if (name == "Threshold") return RangeProperty; 260 if (name == "Threshold") return RangeProperty;
206 if (name == "Zero Padding") return ToggleProperty;
207 return ValueProperty; 261 return ValueProperty;
208 } 262 }
209 263
210 QString 264 QString
211 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 265 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
212 { 266 {
213 if (name == "Bin Display" || 267 if (name == "Bin Display" ||
214 name == "Frequency Scale") return tr("Bins"); 268 name == "Frequency Scale") return tr("Bins");
215 if (name == "Window Size" || 269 if (name == "Window Size" ||
216 name == "Window Increment" || 270 name == "Window Increment") return tr("Window");
217 name == "Zero Padding") return tr("Window");
218 if (name == "Colour" || 271 if (name == "Colour" ||
219 name == "Threshold" || 272 name == "Threshold" ||
220 name == "Colour Rotation") return tr("Colour"); 273 name == "Colour Rotation") return tr("Colour");
221 if (name == "Normalization" || 274 if (name == "Normalization" ||
222 name == "Gain" || 275 name == "Gain" ||
248 if (val < *min) val = *min; 301 if (val < *min) val = *min;
249 if (val > *max) val = *max; 302 if (val > *max) val = *max;
250 303
251 } else if (name == "Threshold") { 304 } else if (name == "Threshold") {
252 305
253 *min = -50; 306 *min = -81;
254 *max = 0; 307 *max = -1;
255 308
256 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); 309 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
257 if (*deflt < *min) *deflt = *min; 310 if (*deflt < *min) *deflt = *min;
258 if (*deflt > *max) *deflt = *max; 311 if (*deflt > *max) *deflt = *max;
259 312
269 322
270 val = m_colourRotation; 323 val = m_colourRotation;
271 324
272 } else if (name == "Colour Scale") { 325 } else if (name == "Colour Scale") {
273 326
327 // linear, meter, db^2, db, phase
274 *min = 0; 328 *min = 0;
275 *max = 4; 329 *max = 4;
276 *deflt = int(dBColourScale); 330 *deflt = 2;
277 331
278 val = (int)m_colourScale; 332 val = convertFromColourScale(m_colourScale, m_colourScaleMultiple);
279 333
280 } else if (name == "Colour") { 334 } else if (name == "Colour") {
281 335
282 *min = 0; 336 *min = 0;
283 *max = ColourMapper::getColourMapCount() - 1; 337 *max = ColourMapper::getColourMapCount() - 1;
300 *min = 0; 354 *min = 0;
301 *max = 5; 355 *max = 5;
302 *deflt = 2; 356 *deflt = 2;
303 357
304 val = m_windowHopLevel; 358 val = m_windowHopLevel;
305
306 } else if (name == "Zero Padding") {
307
308 *min = 0;
309 *max = 1;
310 *deflt = 0;
311
312 val = m_zeroPadLevel > 0 ? 1 : 0;
313 359
314 } else if (name == "Min Frequency") { 360 } else if (name == "Min Frequency") {
315 361
316 *min = 0; 362 *min = 0;
317 *max = 9; 363 *max = 9;
351 397
352 } else if (name == "Frequency Scale") { 398 } else if (name == "Frequency Scale") {
353 399
354 *min = 0; 400 *min = 0;
355 *max = 1; 401 *max = 1;
356 *deflt = int(LinearFrequencyScale); 402 *deflt = int(BinScale::Linear);
357 val = (int)m_frequencyScale; 403 val = (int)m_binScale;
358 404
359 } else if (name == "Bin Display") { 405 } else if (name == "Bin Display") {
360 406
361 *min = 0; 407 *min = 0;
362 *max = 2; 408 *max = 2;
363 *deflt = int(AllBins); 409 *deflt = int(BinDisplay::AllBins);
364 val = (int)m_binDisplay; 410 val = (int)m_binDisplay;
365 411
366 } else if (name == "Normalization") { 412 } else if (name == "Normalization") {
367 413
368 *min = 0; 414 *min = 0;
369 *max = 3; 415 *max = 3;
370 *deflt = int(NoNormalization); 416 *deflt = 0;
371 val = (int)m_normalization; 417
418 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
372 419
373 } else { 420 } else {
374 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); 421 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
375 } 422 }
376 423
408 case 2: return tr("50 %"); 455 case 2: return tr("50 %");
409 case 3: return tr("75 %"); 456 case 3: return tr("75 %");
410 case 4: return tr("87.5 %"); 457 case 4: return tr("87.5 %");
411 case 5: return tr("93.75 %"); 458 case 5: return tr("93.75 %");
412 } 459 }
413 }
414 if (name == "Zero Padding") {
415 if (value == 0) return tr("None");
416 return QString("%1x").arg(value + 1);
417 } 460 }
418 if (name == "Min Frequency") { 461 if (name == "Min Frequency") {
419 switch (value) { 462 switch (value) {
420 default: 463 default:
421 case 0: return tr("No min"); 464 case 0: return tr("No min");
484 { 527 {
485 if (name == "Gain") { 528 if (name == "Gain") {
486 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); 529 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
487 } 530 }
488 if (name == "Threshold") { 531 if (name == "Threshold") {
489 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB")); 532 return new LinearRangeMapper(-81, -1, -81, -1, tr("dB"));
490 } 533 }
491 return 0; 534 return 0;
492 } 535 }
493 536
494 void 537 void
495 SpectrogramLayer::setProperty(const PropertyName &name, int value) 538 SpectrogramLayer::setProperty(const PropertyName &name, int value)
496 { 539 {
497 if (name == "Gain") { 540 if (name == "Gain") {
498 setGain(float(pow(10, float(value)/20.0))); 541 setGain(float(pow(10, float(value)/20.0)));
499 } else if (name == "Threshold") { 542 } else if (name == "Threshold") {
500 if (value == -50) setThreshold(0.0); 543 if (value == -81) setThreshold(0.0);
501 else setThreshold(float(AudioLevel::dB_to_multiplier(value))); 544 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
502 } else if (name == "Colour Rotation") { 545 } else if (name == "Colour Rotation") {
503 setColourRotation(value); 546 setColourRotation(value);
504 } else if (name == "Colour") { 547 } else if (name == "Colour") {
505 setColourMap(value); 548 setColourMap(value);
506 } else if (name == "Window Size") { 549 } else if (name == "Window Size") {
507 setWindowSize(32 << value); 550 setWindowSize(32 << value);
508 } else if (name == "Window Increment") { 551 } else if (name == "Window Increment") {
509 setWindowHopLevel(value); 552 setWindowHopLevel(value);
510 } else if (name == "Zero Padding") {
511 setZeroPadLevel(value > 0.1 ? 3 : 0);
512 } else if (name == "Min Frequency") { 553 } else if (name == "Min Frequency") {
513 switch (value) { 554 switch (value) {
514 default: 555 default:
515 case 0: setMinFrequency(0); break; 556 case 0: setMinFrequency(0); break;
516 case 1: setMinFrequency(10); break; 557 case 1: setMinFrequency(10); break;
546 if (vs != m_lastEmittedZoomStep) { 587 if (vs != m_lastEmittedZoomStep) {
547 emit verticalZoomChanged(); 588 emit verticalZoomChanged();
548 m_lastEmittedZoomStep = vs; 589 m_lastEmittedZoomStep = vs;
549 } 590 }
550 } else if (name == "Colour Scale") { 591 } else if (name == "Colour Scale") {
592 setColourScaleMultiple(1.0);
551 switch (value) { 593 switch (value) {
552 default: 594 default:
553 case 0: setColourScale(LinearColourScale); break; 595 case 0: setColourScale(ColourScaleType::Linear); break;
554 case 1: setColourScale(MeterColourScale); break; 596 case 1: setColourScale(ColourScaleType::Meter); break;
555 case 2: setColourScale(dBSquaredColourScale); break; 597 case 2:
556 case 3: setColourScale(dBColourScale); break; 598 setColourScale(ColourScaleType::Log);
557 case 4: setColourScale(PhaseColourScale); break; 599 setColourScaleMultiple(2.0);
600 break;
601 case 3: setColourScale(ColourScaleType::Log); break;
602 case 4: setColourScale(ColourScaleType::Phase); break;
558 } 603 }
559 } else if (name == "Frequency Scale") { 604 } else if (name == "Frequency Scale") {
560 switch (value) { 605 switch (value) {
561 default: 606 default:
562 case 0: setFrequencyScale(LinearFrequencyScale); break; 607 case 0: setBinScale(BinScale::Linear); break;
563 case 1: setFrequencyScale(LogFrequencyScale); break; 608 case 1: setBinScale(BinScale::Log); break;
564 } 609 }
565 } else if (name == "Bin Display") { 610 } else if (name == "Bin Display") {
566 switch (value) { 611 switch (value) {
567 default: 612 default:
568 case 0: setBinDisplay(AllBins); break; 613 case 0: setBinDisplay(BinDisplay::AllBins); break;
569 case 1: setBinDisplay(PeakBins); break; 614 case 1: setBinDisplay(BinDisplay::PeakBins); break;
570 case 2: setBinDisplay(PeakFrequencies); break; 615 case 2: setBinDisplay(BinDisplay::PeakFrequencies); break;
571 } 616 }
572 } else if (name == "Normalization") { 617 } else if (name == "Normalization") {
573 switch (value) { 618 auto n = convertToColumnNorm(value);
574 default: 619 setNormalization(n.first);
575 case 0: setNormalization(NoNormalization); break; 620 setNormalizeVisibleArea(n.second);
576 case 1: setNormalization(NormalizeColumns); break; 621 }
577 case 2: setNormalization(NormalizeVisibleArea); break; 622 }
578 case 3: setNormalization(NormalizeHybrid); break; 623
579 } 624 void
580 } 625 SpectrogramLayer::invalidateRenderers()
581 }
582
583 void
584 SpectrogramLayer::invalidateImageCaches()
585 { 626 {
586 #ifdef DEBUG_SPECTROGRAM 627 #ifdef DEBUG_SPECTROGRAM
587 cerr << "SpectrogramLayer::invalidateImageCaches called" << endl; 628 cerr << "SpectrogramLayer::invalidateRenderers called" << endl;
588 #endif 629 #endif
589 for (ViewImageCache::iterator i = m_imageCaches.begin(); 630
590 i != m_imageCaches.end(); ++i) { 631 for (ViewRendererMap::iterator i = m_renderers.begin();
591 i->second.invalidate(); 632 i != m_renderers.end(); ++i) {
592 } 633 delete i->second;
634 }
635 m_renderers.clear();
593 } 636 }
594 637
595 void 638 void
596 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) 639 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
597 { 640 {
600 if (name == "Window Type") { 643 if (name == "Window Type") {
601 setWindowType(Preferences::getInstance()->getWindowType()); 644 setWindowType(Preferences::getInstance()->getWindowType());
602 return; 645 return;
603 } 646 }
604 if (name == "Spectrogram Y Smoothing") { 647 if (name == "Spectrogram Y Smoothing") {
605 invalidateImageCaches(); 648 setWindowSize(m_windowSize);
649 invalidateRenderers();
606 invalidateMagnitudes(); 650 invalidateMagnitudes();
607 emit layerParametersChanged(); 651 emit layerParametersChanged();
608 } 652 }
609 if (name == "Spectrogram X Smoothing") { 653 if (name == "Spectrogram X Smoothing") {
610 invalidateImageCaches(); 654 invalidateRenderers();
611 invalidateMagnitudes(); 655 invalidateMagnitudes();
612 emit layerParametersChanged(); 656 emit layerParametersChanged();
613 } 657 }
614 if (name == "Tuning Frequency") { 658 if (name == "Tuning Frequency") {
615 emit layerParametersChanged(); 659 emit layerParametersChanged();
619 void 663 void
620 SpectrogramLayer::setChannel(int ch) 664 SpectrogramLayer::setChannel(int ch)
621 { 665 {
622 if (m_channel == ch) return; 666 if (m_channel == ch) return;
623 667
624 invalidateImageCaches(); 668 invalidateRenderers();
625 m_channel = ch; 669 m_channel = ch;
626 invalidateFFTModels(); 670 invalidateFFTModel();
627 671
628 emit layerParametersChanged(); 672 emit layerParametersChanged();
629 } 673 }
630 674
631 int 675 int
632 SpectrogramLayer::getChannel() const 676 SpectrogramLayer::getChannel() const
633 { 677 {
634 return m_channel; 678 return m_channel;
635 } 679 }
636 680
681 int
682 SpectrogramLayer::getFFTOversampling() const
683 {
684 if (m_binDisplay != BinDisplay::AllBins) {
685 return 1;
686 }
687
688 Preferences::SpectrogramSmoothing smoothing =
689 Preferences::getInstance()->getSpectrogramSmoothing();
690
691 if (smoothing == Preferences::NoSpectrogramSmoothing ||
692 smoothing == Preferences::SpectrogramInterpolated) {
693 return 1;
694 }
695
696 return 4;
697 }
698
699 int
700 SpectrogramLayer::getFFTSize() const
701 {
702 return m_windowSize * getFFTOversampling();
703 }
704
637 void 705 void
638 SpectrogramLayer::setWindowSize(int ws) 706 SpectrogramLayer::setWindowSize(int ws)
639 { 707 {
640 if (m_windowSize == ws) return; 708 if (m_windowSize == ws) return;
641 709
642 invalidateImageCaches(); 710 invalidateRenderers();
643 711
644 m_windowSize = ws; 712 m_windowSize = ws;
645 m_fftSize = ws * (m_zeroPadLevel + 1); 713
646 714 invalidateFFTModel();
647 invalidateFFTModels();
648 715
649 emit layerParametersChanged(); 716 emit layerParametersChanged();
650 } 717 }
651 718
652 int 719 int
658 void 725 void
659 SpectrogramLayer::setWindowHopLevel(int v) 726 SpectrogramLayer::setWindowHopLevel(int v)
660 { 727 {
661 if (m_windowHopLevel == v) return; 728 if (m_windowHopLevel == v) return;
662 729
663 invalidateImageCaches(); 730 invalidateRenderers();
664 731
665 m_windowHopLevel = v; 732 m_windowHopLevel = v;
666 733
667 invalidateFFTModels(); 734 invalidateFFTModel();
668 735
669 emit layerParametersChanged(); 736 emit layerParametersChanged();
670 737
671 // fillCache(); 738 // fillCache();
672 } 739 }
676 { 743 {
677 return m_windowHopLevel; 744 return m_windowHopLevel;
678 } 745 }
679 746
680 void 747 void
681 SpectrogramLayer::setZeroPadLevel(int v)
682 {
683 if (m_zeroPadLevel == v) return;
684
685 invalidateImageCaches();
686
687 m_zeroPadLevel = v;
688 m_fftSize = m_windowSize * (v + 1);
689
690 invalidateFFTModels();
691
692 emit layerParametersChanged();
693 }
694
695 int
696 SpectrogramLayer::getZeroPadLevel() const
697 {
698 return m_zeroPadLevel;
699 }
700
701 void
702 SpectrogramLayer::setWindowType(WindowType w) 748 SpectrogramLayer::setWindowType(WindowType w)
703 { 749 {
704 if (m_windowType == w) return; 750 if (m_windowType == w) return;
705 751
706 invalidateImageCaches(); 752 invalidateRenderers();
707 753
708 m_windowType = w; 754 m_windowType = w;
709 755
710 invalidateFFTModels(); 756 invalidateFFTModel();
711 757
712 emit layerParametersChanged(); 758 emit layerParametersChanged();
713 } 759 }
714 760
715 WindowType 761 WindowType
724 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " 770 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
725 // << m_gain << ")" << endl; 771 // << m_gain << ")" << endl;
726 772
727 if (m_gain == gain) return; 773 if (m_gain == gain) return;
728 774
729 invalidateImageCaches(); 775 invalidateRenderers();
730 776
731 m_gain = gain; 777 m_gain = gain;
732 778
733 emit layerParametersChanged(); 779 emit layerParametersChanged();
734 } 780 }
742 void 788 void
743 SpectrogramLayer::setThreshold(float threshold) 789 SpectrogramLayer::setThreshold(float threshold)
744 { 790 {
745 if (m_threshold == threshold) return; 791 if (m_threshold == threshold) return;
746 792
747 invalidateImageCaches(); 793 invalidateRenderers();
748 794
749 m_threshold = threshold; 795 m_threshold = threshold;
750 796
751 emit layerParametersChanged(); 797 emit layerParametersChanged();
752 } 798 }
762 { 808 {
763 if (m_minFrequency == mf) return; 809 if (m_minFrequency == mf) return;
764 810
765 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl; 811 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl;
766 812
767 invalidateImageCaches(); 813 invalidateRenderers();
768 invalidateMagnitudes(); 814 invalidateMagnitudes();
769 815
770 m_minFrequency = mf; 816 m_minFrequency = mf;
771 817
772 emit layerParametersChanged(); 818 emit layerParametersChanged();
783 { 829 {
784 if (m_maxFrequency == mf) return; 830 if (m_maxFrequency == mf) return;
785 831
786 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl; 832 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl;
787 833
788 invalidateImageCaches(); 834 invalidateRenderers();
789 invalidateMagnitudes(); 835 invalidateMagnitudes();
790 836
791 m_maxFrequency = mf; 837 m_maxFrequency = mf;
792 838
793 emit layerParametersChanged(); 839 emit layerParametersChanged();
800 } 846 }
801 847
802 void 848 void
803 SpectrogramLayer::setColourRotation(int r) 849 SpectrogramLayer::setColourRotation(int r)
804 { 850 {
805 invalidateImageCaches();
806
807 if (r < 0) r = 0; 851 if (r < 0) r = 0;
808 if (r > 256) r = 256; 852 if (r > 256) r = 256;
809 int distance = r - m_colourRotation; 853 int distance = r - m_colourRotation;
810 854
811 if (distance != 0) { 855 if (distance != 0) {
812 rotatePalette(-distance);
813 m_colourRotation = r; 856 m_colourRotation = r;
814 } 857 }
858
859 // Initially the idea with colour rotation was that we would just
860 // rotate the palette of an already-generated cache. That's not
861 // really practical now that cacheing is handled in a separate
862 // class in which the main cache no longer has a palette.
863 invalidateRenderers();
815 864
816 emit layerParametersChanged(); 865 emit layerParametersChanged();
817 } 866 }
818 867
819 void 868 void
820 SpectrogramLayer::setColourScale(ColourScale colourScale) 869 SpectrogramLayer::setColourScale(ColourScaleType colourScale)
821 { 870 {
822 if (m_colourScale == colourScale) return; 871 if (m_colourScale == colourScale) return;
823 872
824 invalidateImageCaches(); 873 invalidateRenderers();
825 874
826 m_colourScale = colourScale; 875 m_colourScale = colourScale;
827 876
828 emit layerParametersChanged(); 877 emit layerParametersChanged();
829 } 878 }
830 879
831 SpectrogramLayer::ColourScale 880 ColourScaleType
832 SpectrogramLayer::getColourScale() const 881 SpectrogramLayer::getColourScale() const
833 { 882 {
834 return m_colourScale; 883 return m_colourScale;
835 } 884 }
836 885
837 void 886 void
887 SpectrogramLayer::setColourScaleMultiple(double multiple)
888 {
889 if (m_colourScaleMultiple == multiple) return;
890
891 invalidateRenderers();
892
893 m_colourScaleMultiple = multiple;
894
895 emit layerParametersChanged();
896 }
897
898 double
899 SpectrogramLayer::getColourScaleMultiple() const
900 {
901 return m_colourScaleMultiple;
902 }
903
904 void
838 SpectrogramLayer::setColourMap(int map) 905 SpectrogramLayer::setColourMap(int map)
839 { 906 {
840 if (m_colourMap == map) return; 907 if (m_colourMap == map) return;
841 908
842 invalidateImageCaches(); 909 invalidateRenderers();
843 910
844 m_colourMap = map; 911 m_colourMap = map;
845 initialisePalette();
846 912
847 emit layerParametersChanged(); 913 emit layerParametersChanged();
848 } 914 }
849 915
850 int 916 int
852 { 918 {
853 return m_colourMap; 919 return m_colourMap;
854 } 920 }
855 921
856 void 922 void
857 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) 923 SpectrogramLayer::setBinScale(BinScale binScale)
858 { 924 {
859 if (m_frequencyScale == frequencyScale) return; 925 if (m_binScale == binScale) return;
860 926
861 invalidateImageCaches(); 927 invalidateRenderers();
862 m_frequencyScale = frequencyScale; 928 m_binScale = binScale;
863 929
864 emit layerParametersChanged(); 930 emit layerParametersChanged();
865 } 931 }
866 932
867 SpectrogramLayer::FrequencyScale 933 BinScale
868 SpectrogramLayer::getFrequencyScale() const 934 SpectrogramLayer::getBinScale() const
869 { 935 {
870 return m_frequencyScale; 936 return m_binScale;
871 } 937 }
872 938
873 void 939 void
874 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) 940 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
875 { 941 {
876 if (m_binDisplay == binDisplay) return; 942 if (m_binDisplay == binDisplay) return;
877 943
878 invalidateImageCaches(); 944 invalidateRenderers();
879 m_binDisplay = binDisplay; 945 m_binDisplay = binDisplay;
880 946
881 emit layerParametersChanged(); 947 emit layerParametersChanged();
882 } 948 }
883 949
884 SpectrogramLayer::BinDisplay 950 BinDisplay
885 SpectrogramLayer::getBinDisplay() const 951 SpectrogramLayer::getBinDisplay() const
886 { 952 {
887 return m_binDisplay; 953 return m_binDisplay;
888 } 954 }
889 955
890 void 956 void
891 SpectrogramLayer::setNormalization(Normalization n) 957 SpectrogramLayer::setNormalization(ColumnNormalization n)
892 { 958 {
893 if (m_normalization == n) return; 959 if (m_normalization == n) return;
894 960
895 invalidateImageCaches(); 961 invalidateRenderers();
896 invalidateMagnitudes(); 962 invalidateMagnitudes();
897 m_normalization = n; 963 m_normalization = n;
898 964
899 emit layerParametersChanged(); 965 emit layerParametersChanged();
900 } 966 }
901 967
902 SpectrogramLayer::Normalization 968 ColumnNormalization
903 SpectrogramLayer::getNormalization() const 969 SpectrogramLayer::getNormalization() const
904 { 970 {
905 return m_normalization; 971 return m_normalization;
972 }
973
974 void
975 SpectrogramLayer::setNormalizeVisibleArea(bool n)
976 {
977 if (m_normalizeVisibleArea == n) return;
978
979 invalidateRenderers();
980 invalidateMagnitudes();
981 m_normalizeVisibleArea = n;
982
983 emit layerParametersChanged();
984 }
985
986 bool
987 SpectrogramLayer::getNormalizeVisibleArea() const
988 {
989 return m_normalizeVisibleArea;
906 } 990 }
907 991
908 void 992 void
909 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant) 993 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
910 { 994 {
919 return; 1003 return;
920 } 1004 }
921 1005
922 Layer::setLayerDormant(v, true); 1006 Layer::setLayerDormant(v, true);
923 1007
924 const View *view = v->getView(); 1008 invalidateRenderers();
925
926 invalidateImageCaches();
927
928 m_imageCaches.erase(view->getId());
929
930 if (m_fftModels.find(view->getId()) != m_fftModels.end()) {
931
932 if (m_sliceableModel == m_fftModels[view->getId()]) {
933 bool replaced = false;
934 for (ViewFFTMap::iterator i = m_fftModels.begin();
935 i != m_fftModels.end(); ++i) {
936 if (i->second != m_sliceableModel) {
937 emit sliceableModelReplaced(m_sliceableModel, i->second);
938 replaced = true;
939 break;
940 }
941 }
942 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
943 }
944
945 delete m_fftModels[view->getId()];
946 m_fftModels.erase(view->getId());
947
948 delete m_peakCaches[view->getId()];
949 m_peakCaches.erase(view->getId());
950 }
951 1009
952 } else { 1010 } else {
953 1011
954 Layer::setLayerDormant(v, false); 1012 Layer::setLayerDormant(v, false);
955 } 1013 }
960 { 1018 {
961 #ifdef DEBUG_SPECTROGRAM_REPAINT 1019 #ifdef DEBUG_SPECTROGRAM_REPAINT
962 cerr << "SpectrogramLayer::cacheInvalid()" << endl; 1020 cerr << "SpectrogramLayer::cacheInvalid()" << endl;
963 #endif 1021 #endif
964 1022
965 invalidateImageCaches(); 1023 invalidateRenderers();
966 invalidateMagnitudes(); 1024 invalidateMagnitudes();
967 } 1025 }
968 1026
969 void 1027 void
970 SpectrogramLayer::cacheInvalid( 1028 SpectrogramLayer::cacheInvalid(
983 // only those caches whose views contained some of the (from, to) 1041 // only those caches whose views contained some of the (from, to)
984 // range. That's the right thing to do; it has been lost in 1042 // range. That's the right thing to do; it has been lost in
985 // pulling out the image cache code, but it might not matter very 1043 // pulling out the image cache code, but it might not matter very
986 // much, since the underlying models for spectrogram layers don't 1044 // much, since the underlying models for spectrogram layers don't
987 // change very often. Let's see. 1045 // change very often. Let's see.
988 invalidateImageCaches(); 1046 invalidateRenderers();
989 invalidateMagnitudes(); 1047 invalidateMagnitudes();
990 } 1048 }
991 1049
992 bool 1050 bool
993 SpectrogramLayer::hasLightBackground() const 1051 SpectrogramLayer::hasLightBackground() const
994 { 1052 {
995 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground(); 1053 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
996 } 1054 }
997 1055
998 void
999 SpectrogramLayer::initialisePalette()
1000 {
1001 int formerRotation = m_colourRotation;
1002
1003 if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
1004 m_palette.setColour(NO_VALUE, Qt::white);
1005 } else {
1006 m_palette.setColour(NO_VALUE, Qt::black);
1007 }
1008
1009 ColourMapper mapper(m_colourMap, 1.f, 255.f);
1010
1011 for (int pixel = 1; pixel < 256; ++pixel) {
1012 m_palette.setColour((unsigned char)pixel, mapper.map(pixel));
1013 }
1014
1015 m_crosshairColour = mapper.getContrastingColour();
1016
1017 m_colourRotation = 0;
1018 rotatePalette(m_colourRotation - formerRotation);
1019 m_colourRotation = formerRotation;
1020
1021 m_drawBuffer = QImage();
1022 }
1023
1024 void
1025 SpectrogramLayer::rotatePalette(int distance)
1026 {
1027 QColor newPixels[256];
1028
1029 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
1030
1031 for (int pixel = 1; pixel < 256; ++pixel) {
1032 int target = pixel + distance;
1033 while (target < 1) target += 255;
1034 while (target > 255) target -= 255;
1035 newPixels[target] = m_palette.getColour((unsigned char)pixel);
1036 }
1037
1038 for (int pixel = 0; pixel < 256; ++pixel) {
1039 m_palette.setColour((unsigned char)pixel, newPixels[pixel]);
1040 }
1041
1042 m_drawBuffer = QImage();
1043 }
1044
1045 unsigned char
1046 SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const
1047 {
1048 int value;
1049
1050 double min = 0.0;
1051 double max = 1.0;
1052
1053 if (m_normalization == NormalizeVisibleArea) {
1054 min = m_viewMags[v->getId()].getMin();
1055 max = m_viewMags[v->getId()].getMax();
1056 } else if (m_normalization != NormalizeColumns) {
1057 if (m_colourScale == LinearColourScale //||
1058 // m_colourScale == MeterColourScale) {
1059 ) {
1060 max = 0.1;
1061 }
1062 }
1063
1064 double thresh = -80.0;
1065
1066 if (max == 0.0) max = 1.0;
1067 if (max == min) min = max - 0.0001;
1068
1069 switch (m_colourScale) {
1070
1071 default:
1072 case LinearColourScale:
1073 value = int(((input - min) / (max - min)) * 255.0) + 1;
1074 break;
1075
1076 case MeterColourScale:
1077 value = AudioLevel::multiplier_to_preview
1078 ((input - min) / (max - min), 254) + 1;
1079 break;
1080
1081 case dBSquaredColourScale:
1082 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1083 if (input > 0.0) {
1084 input = 10.0 * log10(input);
1085 } else {
1086 input = thresh;
1087 }
1088 if (min > 0.0) {
1089 thresh = 10.0 * log10(min * min);
1090 if (thresh < -80.0) thresh = -80.0;
1091 }
1092 input = (input - thresh) / (-thresh);
1093 if (input < 0.0) input = 0.0;
1094 if (input > 1.0) input = 1.0;
1095 value = int(input * 255.0) + 1;
1096 break;
1097
1098 case dBColourScale:
1099 //!!! experiment with normalizing the visible area this way.
1100 //In any case, we need to have some indication of what the dB
1101 //scale is relative to.
1102 input = (input - min) / (max - min);
1103 if (input > 0.0) {
1104 input = 10.0 * log10(input);
1105 } else {
1106 input = thresh;
1107 }
1108 if (min > 0.0) {
1109 thresh = 10.0 * log10(min);
1110 if (thresh < -80.0) thresh = -80.0;
1111 }
1112 input = (input - thresh) / (-thresh);
1113 if (input < 0.0) input = 0.0;
1114 if (input > 1.0) input = 1.0;
1115 value = int(input * 255.0) + 1;
1116 break;
1117
1118 case PhaseColourScale:
1119 value = int((input * 127.0 / M_PI) + 128);
1120 break;
1121 }
1122
1123 if (value > UCHAR_MAX) value = UCHAR_MAX;
1124 if (value < 0) value = 0;
1125 return (unsigned char)value;
1126 }
1127
1128 double 1056 double
1129 SpectrogramLayer::getEffectiveMinFrequency() const 1057 SpectrogramLayer::getEffectiveMinFrequency() const
1130 { 1058 {
1131 sv_samplerate_t sr = m_model->getSampleRate(); 1059 sv_samplerate_t sr = m_model->getSampleRate();
1132 double minf = double(sr) / m_fftSize; 1060 double minf = double(sr) / getFFTSize();
1133 1061
1134 if (m_minFrequency > 0.0) { 1062 if (m_minFrequency > 0.0) {
1135 int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01); 1063 int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01);
1136 if (minbin < 1) minbin = 1; 1064 if (minbin < 1) minbin = 1;
1137 minf = minbin * sr / m_fftSize; 1065 minf = minbin * sr / getFFTSize();
1138 } 1066 }
1139 1067
1140 return minf; 1068 return minf;
1141 } 1069 }
1142 1070
1145 { 1073 {
1146 sv_samplerate_t sr = m_model->getSampleRate(); 1074 sv_samplerate_t sr = m_model->getSampleRate();
1147 double maxf = double(sr) / 2; 1075 double maxf = double(sr) / 2;
1148 1076
1149 if (m_maxFrequency > 0.0) { 1077 if (m_maxFrequency > 0.0) {
1150 int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 1078 int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
1151 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 1079 if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2;
1152 maxf = maxbin * sr / m_fftSize; 1080 maxf = maxbin * sr / getFFTSize();
1153 } 1081 }
1154 1082
1155 return maxf; 1083 return maxf;
1156 } 1084 }
1157 1085
1158 bool 1086 bool
1159 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const 1087 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
1160 { 1088 {
1161 Profiler profiler("SpectrogramLayer::getYBinRange"); 1089 Profiler profiler("SpectrogramLayer::getYBinRange");
1162
1163 int h = v->getPaintHeight(); 1090 int h = v->getPaintHeight();
1164 if (y < 0 || y >= h) return false; 1091 if (y < 0 || y >= h) return false;
1165 1092 q0 = getBinForY(v, y);
1093 q1 = getBinForY(v, y-1);
1094 return true;
1095 }
1096
1097 double
1098 SpectrogramLayer::getYForBin(const LayerGeometryProvider *v, double bin) const
1099 {
1100 double minf = getEffectiveMinFrequency();
1101 double maxf = getEffectiveMaxFrequency();
1102 bool logarithmic = (m_binScale == BinScale::Log);
1103 sv_samplerate_t sr = m_model->getSampleRate();
1104
1105 double freq = (bin * sr) / getFFTSize();
1106
1107 double y = v->getYForFrequency(freq, minf, maxf, logarithmic);
1108
1109 return y;
1110 }
1111
1112 double
1113 SpectrogramLayer::getBinForY(const LayerGeometryProvider *v, double y) const
1114 {
1166 sv_samplerate_t sr = m_model->getSampleRate(); 1115 sv_samplerate_t sr = m_model->getSampleRate();
1167 double minf = getEffectiveMinFrequency(); 1116 double minf = getEffectiveMinFrequency();
1168 double maxf = getEffectiveMaxFrequency(); 1117 double maxf = getEffectiveMaxFrequency();
1169 1118
1170 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1119 bool logarithmic = (m_binScale == BinScale::Log);
1171 1120
1172 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); 1121 double freq = v->getFrequencyForY(y, minf, maxf, logarithmic);
1173 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); 1122
1174 1123 // Now map on to ("proportion of") actual bins
1175 // Now map these on to ("proportions of") actual bins, using raw 1124 double bin = (freq * getFFTSize()) / sr;
1176 // FFT size (unsmoothed) 1125
1177 1126 return bin;
1178 q0 = (q0 * m_fftSize) / sr; 1127 }
1179 q1 = (q1 * m_fftSize) / sr; 1128
1180
1181 return true;
1182 }
1183
1184 bool
1185 SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
1186 {
1187 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
1188
1189 int h = v->getPaintHeight();
1190 if (y < 0 || y >= h) return false;
1191
1192 sv_samplerate_t sr = m_model->getSampleRate();
1193 double minf = getEffectiveMinFrequency();
1194 double maxf = getEffectiveMaxFrequency();
1195
1196 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1197
1198 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1199 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1200
1201 // Now map these on to ("proportions of") actual bins, using raw
1202 // FFT size (unsmoothed)
1203
1204 q0 = (q0 * getFFTSize(v)) / sr;
1205 q1 = (q1 * getFFTSize(v)) / sr;
1206
1207 return true;
1208 }
1209
1210 bool 1129 bool
1211 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const 1130 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const
1212 { 1131 {
1213 sv_frame_t modelStart = m_model->getStartFrame(); 1132 sv_frame_t modelStart = m_model->getStartFrame();
1214 sv_frame_t modelEnd = m_model->getEndFrame(); 1133 sv_frame_t modelEnd = m_model->getEndFrame();
1261 int q1i = int(q1); 1180 int q1i = int(q1);
1262 1181
1263 sv_samplerate_t sr = m_model->getSampleRate(); 1182 sv_samplerate_t sr = m_model->getSampleRate();
1264 1183
1265 for (int q = q0i; q <= q1i; ++q) { 1184 for (int q = q0i; q <= q1i; ++q) {
1266 if (q == q0i) freqMin = (sr * q) / m_fftSize; 1185 if (q == q0i) freqMin = (sr * q) / getFFTSize();
1267 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize; 1186 if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize();
1268 } 1187 }
1269 return true; 1188 return true;
1270 } 1189 }
1271 1190
1272 bool 1191 bool
1277 { 1196 {
1278 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1197 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1279 return false; 1198 return false;
1280 } 1199 }
1281 1200
1282 FFTModel *fft = getFFTModel(v); 1201 FFTModel *fft = getFFTModel();
1283 if (!fft) return false; 1202 if (!fft) return false;
1284 1203
1285 double s0 = 0, s1 = 0; 1204 double s0 = 0, s1 = 0;
1286 if (!getXBinRange(v, x, s0, s1)) return false; 1205 if (!getXBinRange(v, x, s0, s1)) return false;
1287 1206
1296 1215
1297 sv_samplerate_t sr = m_model->getSampleRate(); 1216 sv_samplerate_t sr = m_model->getSampleRate();
1298 1217
1299 bool haveAdj = false; 1218 bool haveAdj = false;
1300 1219
1301 bool peaksOnly = (m_binDisplay == PeakBins || 1220 bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins ||
1302 m_binDisplay == PeakFrequencies); 1221 m_binDisplay == BinDisplay::PeakFrequencies);
1303 1222
1304 for (int q = q0i; q <= q1i; ++q) { 1223 for (int q = q0i; q <= q1i; ++q) {
1305 1224
1306 for (int s = s0i; s <= s1i; ++s) { 1225 for (int s = s0i; s <= s1i; ++s) {
1307 1226
1309 if (q == q0i) freqMin = binfreq; 1228 if (q == q0i) freqMin = binfreq;
1310 if (q == q1i) freqMax = binfreq; 1229 if (q == q1i) freqMax = binfreq;
1311 1230
1312 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; 1231 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1313 1232
1314 if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue; 1233 if (!fft->isOverThreshold
1234 (s, q, float(m_threshold * double(getFFTSize())/2.0))) {
1235 continue;
1236 }
1315 1237
1316 double freq = binfreq; 1238 double freq = binfreq;
1317 1239
1318 if (s < int(fft->getWidth()) - 1) { 1240 if (s < int(fft->getWidth()) - 1) {
1319 1241
1355 int s0i = int(s0 + 0.001); 1277 int s0i = int(s0 + 0.001);
1356 int s1i = int(s1); 1278 int s1i = int(s1);
1357 1279
1358 bool rv = false; 1280 bool rv = false;
1359 1281
1360 int zp = getZeroPadLevel(v); 1282 FFTModel *fft = getFFTModel();
1361 q0i *= zp + 1;
1362 q1i *= zp + 1;
1363
1364 FFTModel *fft = getFFTModel(v);
1365 1283
1366 if (fft) { 1284 if (fft) {
1367 1285
1368 int cw = fft->getWidth(); 1286 int cw = fft->getWidth();
1369 int ch = fft->getHeight(); 1287 int ch = fft->getHeight();
1382 1300
1383 value = fft->getPhaseAt(s, q); 1301 value = fft->getPhaseAt(s, q);
1384 if (!have || value < phaseMin) { phaseMin = value; } 1302 if (!have || value < phaseMin) { phaseMin = value; }
1385 if (!have || value > phaseMax) { phaseMax = value; } 1303 if (!have || value > phaseMax) { phaseMax = value; }
1386 1304
1387 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2.0); 1305 value = fft->getMagnitudeAt(s, q) / (getFFTSize()/2.0);
1388 if (!have || value < min) { min = value; } 1306 if (!have || value < min) { min = value; }
1389 if (!have || value > max) { max = value; } 1307 if (!have || value > max) { max = value; }
1390 1308
1391 have = true; 1309 have = true;
1392 } 1310 }
1398 } 1316 }
1399 } 1317 }
1400 1318
1401 return rv; 1319 return rv;
1402 } 1320 }
1403
1404 int
1405 SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const
1406 {
1407 //!!! tidy all this stuff
1408
1409 if (m_binDisplay != AllBins) return 0;
1410
1411 Preferences::SpectrogramSmoothing smoothing =
1412 Preferences::getInstance()->getSpectrogramSmoothing();
1413
1414 if (smoothing == Preferences::NoSpectrogramSmoothing ||
1415 smoothing == Preferences::SpectrogramInterpolated) return 0;
1416
1417 if (m_frequencyScale == LogFrequencyScale) return 3;
1418
1419 sv_samplerate_t sr = m_model->getSampleRate();
1420
1421 int maxbin = m_fftSize / 2;
1422 if (m_maxFrequency > 0) {
1423 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1424 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1425 }
1426
1427 int minbin = 1;
1428 if (m_minFrequency > 0) {
1429 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
1430 if (minbin < 1) minbin = 1;
1431 if (minbin >= maxbin) minbin = maxbin - 1;
1432 }
1433
1434 double perPixel =
1435 double(v->getPaintHeight()) /
1436 double((maxbin - minbin) / (m_zeroPadLevel + 1));
1437
1438 if (perPixel > 2.8) {
1439 return 3; // 4x oversampling
1440 } else if (perPixel > 1.5) {
1441 return 1; // 2x
1442 } else {
1443 return 0; // 1x
1444 }
1445 }
1446
1447 int
1448 SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const
1449 {
1450 return m_fftSize * (getZeroPadLevel(v) + 1);
1451 }
1452 1321
1453 FFTModel * 1322 FFTModel *
1454 SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const 1323 SpectrogramLayer::getFFTModel() const
1455 { 1324 {
1456 if (!m_model) return 0; 1325 if (!m_model) return 0;
1457 1326
1458 int fftSize = getFFTSize(v); 1327 int fftSize = getFFTSize();
1459 1328
1460 const View *view = v->getView(); 1329 //!!! it is now surely slower to do this on every getFFTModel()
1461 1330 //!!! request than it would be to recreate the model immediately
1462 if (m_fftModels.find(view->getId()) != m_fftModels.end()) { 1331 //!!! when something changes instead of just invalidating it
1463 if (m_fftModels[view->getId()] == 0) { 1332
1464 #ifdef DEBUG_SPECTROGRAM_REPAINT 1333 if (m_fftModel &&
1465 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; 1334 m_fftModel->getHeight() == fftSize / 2 + 1 &&
1466 #endif 1335 m_fftModel->getWindowIncrement() == getWindowIncrement()) {
1467 return 0; 1336 return m_fftModel;
1468 } 1337 }
1469 if (m_fftModels[view->getId()]->getHeight() != fftSize / 2 + 1) { 1338
1470 #ifdef DEBUG_SPECTROGRAM_REPAINT 1339 delete m_peakCache;
1471 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[view->getId()]->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; 1340 m_peakCache = 0;
1472 #endif 1341
1473 delete m_fftModels[view->getId()]; 1342 delete m_fftModel;
1474 m_fftModels.erase(view->getId()); 1343 m_fftModel = new FFTModel(m_model,
1475 delete m_peakCaches[view->getId()]; 1344 m_channel,
1476 m_peakCaches.erase(view->getId()); 1345 m_windowType,
1477 } else { 1346 m_windowSize,
1478 #ifdef DEBUG_SPECTROGRAM_REPAINT 1347 getWindowIncrement(),
1479 cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[view->getId()]->getHeight() << endl; 1348 fftSize);
1480 #endif 1349
1481 return m_fftModels[view->getId()]; 1350 if (!m_fftModel->isOK()) {
1482 } 1351 QMessageBox::critical
1483 } 1352 (0, tr("FFT cache failed"),
1484 1353 tr("Failed to create the FFT model for this spectrogram.\n"
1485 if (m_fftModels.find(view->getId()) == m_fftModels.end()) { 1354 "There may be insufficient memory or disc space to continue."));
1486 1355 delete m_fftModel;
1487 FFTModel *model = new FFTModel(m_model, 1356 m_fftModel = 0;
1488 m_channel, 1357 return 0;
1489 m_windowType, 1358 }
1490 m_windowSize, 1359
1491 getWindowIncrement(), 1360 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, m_fftModel);
1492 fftSize); 1361
1493 1362 return m_fftModel;
1494 if (!model->isOK()) {
1495 QMessageBox::critical
1496 (0, tr("FFT cache failed"),
1497 tr("Failed to create the FFT model for this spectrogram.\n"
1498 "There may be insufficient memory or disc space to continue."));
1499 delete model;
1500 m_fftModels[view->getId()] = 0;
1501 return 0;
1502 }
1503
1504 if (!m_sliceableModel) {
1505 #ifdef DEBUG_SPECTROGRAM
1506 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl;
1507 #endif
1508 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1509 m_sliceableModel = model;
1510 }
1511
1512 m_fftModels[view->getId()] = model;
1513 }
1514
1515 return m_fftModels[view->getId()];
1516 } 1363 }
1517 1364
1518 Dense3DModelPeakCache * 1365 Dense3DModelPeakCache *
1519 SpectrogramLayer::getPeakCache(const LayerGeometryProvider *v) const 1366 SpectrogramLayer::getPeakCache() const
1520 { 1367 {
1521 const View *view = v->getView(); 1368 //!!! see comment in getFFTModel
1522 if (!m_peakCaches[view->getId()]) { 1369
1523 FFTModel *f = getFFTModel(v); 1370 if (!m_peakCache) {
1371 FFTModel *f = getFFTModel();
1524 if (!f) return 0; 1372 if (!f) return 0;
1525 m_peakCaches[view->getId()] = 1373 m_peakCache = new Dense3DModelPeakCache(f, m_peakCacheDivisor);
1526 new Dense3DModelPeakCache(f, m_peakCacheDivisor); 1374 }
1527 } 1375 return m_peakCache;
1528 return m_peakCaches[view->getId()];
1529 } 1376 }
1530 1377
1531 const Model * 1378 const Model *
1532 SpectrogramLayer::getSliceableModel() const 1379 SpectrogramLayer::getSliceableModel() const
1533 { 1380 {
1534 if (m_sliceableModel) return m_sliceableModel; 1381 return m_fftModel;
1535 if (m_fftModels.empty()) return 0; 1382 }
1536 m_sliceableModel = m_fftModels.begin()->second; 1383
1537 return m_sliceableModel; 1384 void
1538 } 1385 SpectrogramLayer::invalidateFFTModel()
1539
1540 void
1541 SpectrogramLayer::invalidateFFTModels()
1542 { 1386 {
1543 #ifdef DEBUG_SPECTROGRAM 1387 #ifdef DEBUG_SPECTROGRAM
1544 cerr << "SpectrogramLayer::invalidateFFTModels called" << endl; 1388 cerr << "SpectrogramLayer::invalidateFFTModel called" << endl;
1545 #endif 1389 #endif
1546 for (ViewFFTMap::iterator i = m_fftModels.begin(); 1390
1547 i != m_fftModels.end(); ++i) { 1391 emit sliceableModelReplaced(m_fftModel, 0);
1548 delete i->second; 1392
1549 } 1393 delete m_fftModel;
1550 for (PeakCacheMap::iterator i = m_peakCaches.begin(); 1394 delete m_peakCache;
1551 i != m_peakCaches.end(); ++i) { 1395
1552 delete i->second; 1396 m_fftModel = 0;
1553 } 1397 m_peakCache = 0;
1554
1555 m_fftModels.clear();
1556 m_peakCaches.clear();
1557
1558 if (m_sliceableModel) {
1559 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl;
1560 emit sliceableModelReplaced(m_sliceableModel, 0);
1561 m_sliceableModel = 0;
1562 }
1563 } 1398 }
1564 1399
1565 void 1400 void
1566 SpectrogramLayer::invalidateMagnitudes() 1401 SpectrogramLayer::invalidateMagnitudes()
1567 { 1402 {
1568 #ifdef DEBUG_SPECTROGRAM 1403 #ifdef DEBUG_SPECTROGRAM
1569 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl; 1404 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl;
1570 #endif 1405 #endif
1571 m_viewMags.clear(); 1406 m_viewMags.clear();
1572 for (vector<MagnitudeRange>::iterator i = m_columnMags.begin(); 1407 }
1573 i != m_columnMags.end(); ++i) { 1408
1574 *i = MagnitudeRange(); 1409 void
1575 } 1410 SpectrogramLayer::setSynchronousPainting(bool synchronous)
1576 } 1411 {
1577 1412 m_synchronous = synchronous;
1578 bool 1413 }
1579 SpectrogramLayer::updateViewMagnitudes(LayerGeometryProvider *v) const 1414
1580 { 1415 Colour3DPlotRenderer *
1581 MagnitudeRange mag; 1416 SpectrogramLayer::getRenderer(LayerGeometryProvider *v) const
1582 1417 {
1583 int x0 = 0, x1 = v->getPaintWidth(); 1418 int viewId = v->getId();
1584 double s00 = 0, s01 = 0, s10 = 0, s11 = 0; 1419
1585 1420 if (m_renderers.find(viewId) == m_renderers.end()) {
1586 if (!getXBinRange(v, x0, s00, s01)) { 1421
1587 s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); 1422 Colour3DPlotRenderer::Sources sources;
1588 } 1423 sources.verticalBinLayer = this;
1589 1424 sources.fft = getFFTModel();
1590 if (!getXBinRange(v, x1, s10, s11)) { 1425 sources.source = sources.fft;
1591 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); 1426 sources.peaks = getPeakCache();
1592 } 1427
1593 1428 ColourScale::Parameters cparams;
1594 int s0 = int(min(s00, s10) + 0.0001); 1429 cparams.colourMap = m_colourMap;
1595 int s1 = int(max(s01, s11) + 0.0001); 1430 cparams.scaleType = m_colourScale;
1596 1431 cparams.multiple = m_colourScaleMultiple;
1597 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; 1432
1598 1433 if (m_colourScale != ColourScaleType::Phase) {
1599 if (int(m_columnMags.size()) <= s1) { 1434 cparams.gain = m_gain;
1600 m_columnMags.resize(s1 + 1); 1435 cparams.threshold = m_threshold;
1601 }
1602
1603 for (int s = s0; s <= s1; ++s) {
1604 if (m_columnMags[s].isSet()) {
1605 mag.sample(m_columnMags[s]);
1606 } 1436 }
1607 } 1437
1438 float minValue = 0.0f;
1439 float maxValue = 1.0f;
1440
1441 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) {
1442 minValue = m_viewMags[viewId].getMin();
1443 maxValue = m_viewMags[viewId].getMax();
1444 } else if (m_colourScale == ColourScaleType::Linear &&
1445 m_normalization == ColumnNormalization::None) {
1446 maxValue = 0.1f;
1447 }
1448
1449 if (maxValue <= minValue) {
1450 maxValue = minValue + 0.1f;
1451 }
1452 if (maxValue <= m_threshold) {
1453 maxValue = m_threshold + 0.1f;
1454 }
1455
1456 cparams.minValue = minValue;
1457 cparams.maxValue = maxValue;
1458
1459 m_lastRenderedMags[viewId] = MagnitudeRange(minValue, maxValue);
1460
1461 Colour3DPlotRenderer::Parameters params;
1462 params.colourScale = ColourScale(cparams);
1463 params.normalization = m_normalization;
1464 params.binDisplay = m_binDisplay;
1465 params.binScale = m_binScale;
1466 params.alwaysOpaque = true;
1467 params.invertVertical = false;
1468 params.scaleFactor = 1.0;
1469 params.colourRotation = m_colourRotation;
1470
1471 if (m_colourScale != ColourScaleType::Phase &&
1472 m_normalization != ColumnNormalization::Hybrid) {
1473 params.scaleFactor *= 2.f / float(getFFTSize());
1474 }
1475
1476 Preferences::SpectrogramSmoothing smoothing =
1477 Preferences::getInstance()->getSpectrogramSmoothing();
1478 params.interpolate =
1479 (smoothing == Preferences::SpectrogramInterpolated ||
1480 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated);
1481
1482 m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params);
1483 }
1484
1485 return m_renderers[v->getId()];
1486 }
1487
1488 void
1489 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1490 {
1491 Colour3DPlotRenderer *renderer = getRenderer(v);
1492
1493 Colour3DPlotRenderer::RenderResult result;
1494 MagnitudeRange magRange;
1495 int viewId = v->getId();
1496
1497 bool continuingPaint = !renderer->geometryChanged(v);
1498
1499 if (continuingPaint) {
1500 magRange = m_viewMags[viewId];
1501 }
1502
1503 if (m_synchronous) {
1504
1505 result = renderer->render(v, paint, rect);
1506
1507 } else {
1508
1509 result = renderer->renderTimeConstrained(v, paint, rect);
1608 1510
1609 #ifdef DEBUG_SPECTROGRAM_REPAINT 1511 #ifdef DEBUG_SPECTROGRAM_REPAINT
1610 cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols " 1512 cerr << "rect width from this paint: " << result.rendered.width()
1611 << s0 << " -> " << s1 << " inclusive" << endl; 1513 << ", mag range in this paint: " << result.range.getMin() << " -> "
1612 cerr << "SpectrogramLayer::updateViewMagnitudes: for view id " << v->getId() 1514 << result.range.getMax() << endl;
1613 << ": min is " << mag.getMin() << ", max is " << mag.getMax() << endl;
1614 #endif 1515 #endif
1615 1516
1616 if (!mag.isSet()) return false; 1517 QRect uncached = renderer->getLargestUncachedRect(v);
1617 if (mag == m_viewMags[v->getId()]) return false; 1518 if (uncached.width() > 0) {
1618 m_viewMags[v->getId()] = mag; 1519 v->updatePaintRect(uncached);
1619 return true; 1520 }
1620 } 1521 }
1621 1522
1622 void 1523 magRange.sample(result.range);
1623 SpectrogramLayer::setSynchronousPainting(bool synchronous) 1524
1624 { 1525 if (magRange.isSet()) {
1625 m_synchronous = synchronous; 1526 if (m_viewMags[viewId] != magRange) {
1626 } 1527 m_viewMags[viewId] = magRange;
1627 1528 #ifdef DEBUG_SPECTROGRAM_REPAINT
1628 ScrollableImageCache & 1529 cerr << "mag range in this view has changed: "
1629 SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const 1530 << magRange.getMin() << " -> " << magRange.getMax() << endl;
1630 { 1531 #endif
1631 if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) { 1532 }
1632 m_imageCaches[view->getId()] = ScrollableImageCache(view); 1533 }
1633 } 1534
1634 return m_imageCaches.at(view->getId()); 1535 if (!continuingPaint && m_normalizeVisibleArea &&
1536 m_viewMags[viewId] != m_lastRenderedMags[viewId]) {
1537 #ifdef DEBUG_SPECTROGRAM_REPAINT
1538 cerr << "mag range has changed from last rendered range: re-rendering"
1539 << endl;
1540 #endif
1541 delete m_renderers[viewId];
1542 m_renderers.erase(viewId);
1543 v->updatePaintRect(v->getPaintRect());
1544 }
1635 } 1545 }
1636 1546
1637 void 1547 void
1638 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const 1548 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1639 { 1549 {
1643 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; 1553 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
1644 1554
1645 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; 1555 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
1646 #endif 1556 #endif
1647 1557
1648 sv_frame_t startFrame = v->getStartFrame();
1649
1650 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1558 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1651 return; 1559 return;
1652 } 1560 }
1653 1561
1654 if (isLayerDormant(v)) { 1562 if (isLayerDormant(v)) {
1655 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; 1563 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
1656 } 1564 }
1657 1565
1658 // Need to do this even if !isLayerDormant, as that could mean v 1566 paintWithRenderer(v, paint, rect);
1659 // is not in the dormancy map at all -- we need it to be present
1660 // and accountable for when determining whether we need the cache
1661 // in the cache-fill thread above.
1662 //!!! no inter use cache-fill thread
1663 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1664
1665 int fftSize = getFFTSize(v);
1666
1667 const View *view = v->getView();
1668 ScrollableImageCache &cache = getImageCacheReference(view);
1669
1670 #ifdef DEBUG_SPECTROGRAM_REPAINT
1671 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl;
1672 if (rect.x() + rect.width() + 1 < cache.getValidLeft() ||
1673 rect.x() > cache.getValidRight()) {
1674 cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl;
1675 }
1676 #endif
1677
1678 int zoomLevel = v->getZoomLevel();
1679
1680 int x0 = v->getXForViewX(rect.x());
1681 int x1 = v->getXForViewX(rect.x() + rect.width());
1682 if (x0 < 0) x0 = 0;
1683 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
1684
1685 if (updateViewMagnitudes(v)) {
1686 #ifdef DEBUG_SPECTROGRAM_REPAINT
1687 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
1688 #endif
1689 if (m_normalization == NormalizeVisibleArea) {
1690 cache.invalidate();
1691 }
1692 }
1693
1694 if (cache.getZoomLevel() != zoomLevel ||
1695 cache.getSize() != v->getPaintSize()) {
1696 #ifdef DEBUG_SPECTROGRAM_REPAINT
1697 cerr << "SpectrogramLayer: resizing image cache from "
1698 << cache.getSize().width() << "x" << cache.getSize().height()
1699 << " to "
1700 << v->getPaintSize().width() << "x" << v->getPaintSize().height()
1701 << " and updating zoom level from " << cache.getZoomLevel()
1702 << " to " << zoomLevel
1703 << endl;
1704 #endif
1705 cache.resize(v->getPaintSize());
1706 cache.setZoomLevel(zoomLevel);
1707 cache.setStartFrame(startFrame);
1708 }
1709
1710 if (cache.isValid()) {
1711
1712 if (v->getXForFrame(cache.getStartFrame()) ==
1713 v->getXForFrame(startFrame) &&
1714 cache.getValidLeft() <= x0 &&
1715 cache.getValidRight() >= x1) {
1716
1717 #ifdef DEBUG_SPECTROGRAM_REPAINT
1718 cerr << "SpectrogramLayer: image cache hit!" << endl;
1719 #endif
1720
1721 paint.drawImage(rect, cache.getImage(), rect);
1722
1723 illuminateLocalFeatures(v, paint);
1724 return;
1725
1726 } else {
1727
1728 // cache doesn't begin at the right frame or doesn't
1729 // contain the complete view, but might be scrollable or
1730 // partially usable
1731
1732 #ifdef DEBUG_SPECTROGRAM_REPAINT
1733 cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl;
1734 #endif
1735
1736 cache.scrollTo(startFrame);
1737
1738 #ifdef DEBUG_SPECTROGRAM_REPAINT
1739 cerr << "SpectrogramLayer: after scrolling, cache valid from "
1740 << cache.getValidLeft() << " width " << cache.getValidWidth()
1741 << endl;
1742 #endif
1743 }
1744 }
1745
1746 bool rightToLeft = false;
1747
1748 if (!cache.isValid()) {
1749 if (!m_synchronous) {
1750 // When rendering the whole thing, start from somewhere near
1751 // the middle so that the region of interest appears first
1752
1753 //!!! (perhaps we should have some cunning test to avoid
1754 //!!! doing this if past repaints have appeared fast
1755 //!!! enough to do the whole width in one shot)
1756 if (x0 == 0 && x1 == v->getPaintWidth()) {
1757 x0 = int(x1 * 0.3);
1758 }
1759 }
1760 } else {
1761 // When rendering only a part of the cache, we need to make
1762 // sure that the part we're rendering is adjacent to (or
1763 // overlapping) a valid area of cache, if we have one. The
1764 // alternative is to ditch the valid area of cache and render
1765 // only the requested area, but that's risky because this can
1766 // happen when just waving the pointer over a small part of
1767 // the view -- if we lose the partly-built cache every time
1768 // the user does that, we'll never finish building it.
1769 int left = x0;
1770 int width = x1 - x0;
1771 bool isLeftOfValidArea = false;
1772 cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
1773 x0 = left;
1774 x1 = x0 + width;
1775
1776 // That call also told us whether we should be painting
1777 // sub-regions of our target region in right-to-left order in
1778 // order to ensure contiguity
1779 rightToLeft = isLeftOfValidArea;
1780 }
1781
1782 // We always paint the full height when refreshing the cache.
1783 // Smaller heights can be used when painting direct from cache
1784 // (further up in this function), but we want to ensure the cache
1785 // is coherent without having to worry about vertical matching of
1786 // required and valid areas as well as horizontal.
1787 int h = v->getPaintHeight();
1788
1789 int repaintWidth = x1 - x0;
1790
1791 #ifdef DEBUG_SPECTROGRAM_REPAINT
1792 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1
1793 << ", repaintWidth " << repaintWidth << ", h " << h
1794 << ", rightToLeft " << rightToLeft << endl;
1795 #endif
1796
1797 sv_samplerate_t sr = m_model->getSampleRate();
1798
1799 // Set minFreq and maxFreq to the frequency extents of the possibly
1800 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
1801 // to the actual scale frequency extents (presumably not zero padded).
1802
1803 // If we are zero padding, we want to use the zero-padded
1804 // equivalents of the bins that we would be using if not zero
1805 // padded, to avoid spaces at the top and bottom of the display.
1806
1807 // Note fftSize is the actual zero-padded fft size, m_fftSize the
1808 // nominal fft size.
1809
1810 int maxbin = m_fftSize / 2;
1811 if (m_maxFrequency > 0) {
1812 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
1813 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1814 }
1815
1816 int minbin = 1;
1817 if (m_minFrequency > 0) {
1818 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
1819 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
1820 if (minbin < 1) minbin = 1;
1821 if (minbin >= maxbin) minbin = maxbin - 1;
1822 }
1823
1824 int zpl = getZeroPadLevel(v) + 1;
1825 minbin = minbin * zpl;
1826 maxbin = (maxbin + 1) * zpl - 1;
1827
1828 double minFreq = (double(minbin) * sr) / fftSize;
1829 double maxFreq = (double(maxbin) * sr) / fftSize;
1830
1831 double displayMinFreq = minFreq;
1832 double displayMaxFreq = maxFreq;
1833
1834 if (fftSize != m_fftSize) {
1835 displayMinFreq = getEffectiveMinFrequency();
1836 displayMaxFreq = getEffectiveMaxFrequency();
1837 }
1838
1839 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
1840
1841 int increment = getWindowIncrement();
1842
1843 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1844
1845 MagnitudeRange overallMag = m_viewMags[v->getId()];
1846 bool overallMagChanged = false;
1847
1848 #ifdef DEBUG_SPECTROGRAM_REPAINT
1849 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
1850 #endif
1851
1852 if (repaintWidth == 0) {
1853 SVDEBUG << "*** NOTE: repaintWidth == 0" << endl;
1854 }
1855
1856 Profiler outerprof("SpectrogramLayer::paint: all cols");
1857
1858 // The draw buffer contains a fragment at either our pixel
1859 // resolution (if there is more than one time-bin per pixel) or
1860 // time-bin resolution (if a time-bin spans more than one pixel).
1861 // We need to ensure that it starts and ends at points where a
1862 // time-bin boundary occurs at an exact pixel boundary, and with a
1863 // certain amount of overlap across existing pixels so that we can
1864 // scale and draw from it without smoothing errors at the edges.
1865
1866 // If (getFrameForX(x) / increment) * increment ==
1867 // getFrameForX(x), then x is a time-bin boundary. We want two
1868 // such boundaries at either side of the draw buffer -- one which
1869 // we draw up to, and one which we subsequently crop at.
1870
1871 bool bufferIsBinResolution = false;
1872 if (increment > zoomLevel) bufferIsBinResolution = true;
1873
1874 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
1875 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
1876
1877 int bufwid;
1878
1879 if (bufferIsBinResolution) {
1880
1881 for (int x = x0; ; --x) {
1882 sv_frame_t f = v->getFrameForX(x);
1883 if ((f / increment) * increment == f) {
1884 if (leftCropFrame == -1) leftCropFrame = f;
1885 else if (x < x0 - 2) {
1886 leftBoundaryFrame = f;
1887 break;
1888 }
1889 }
1890 }
1891 for (int x = x0 + repaintWidth; ; ++x) {
1892 sv_frame_t f = v->getFrameForX(x);
1893 if ((f / increment) * increment == f) {
1894 if (rightCropFrame == -1) rightCropFrame = f;
1895 else if (x > x0 + repaintWidth + 2) {
1896 rightBoundaryFrame = f;
1897 break;
1898 }
1899 }
1900 }
1901 #ifdef DEBUG_SPECTROGRAM_REPAINT
1902 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
1903 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
1904 #endif
1905
1906 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment);
1907
1908 } else {
1909
1910 bufwid = repaintWidth;
1911 }
1912
1913 vector<int> binforx(bufwid);
1914 vector<double> binfory(h);
1915
1916 bool usePeaksCache = false;
1917
1918 if (bufferIsBinResolution) {
1919 for (int x = 0; x < bufwid; ++x) {
1920 binforx[x] = int(leftBoundaryFrame / increment) + x;
1921 }
1922 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
1923 } else {
1924 for (int x = 0; x < bufwid; ++x) {
1925 double s0 = 0, s1 = 0;
1926 if (getXBinRange(v, x + x0, s0, s1)) {
1927 binforx[x] = int(s0 + 0.0001);
1928 } else {
1929 binforx[x] = -1; //???
1930 }
1931 }
1932 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
1933 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
1934 }
1935 usePeaksCache = (increment * m_peakCacheDivisor) < zoomLevel;
1936 if (m_colourScale == PhaseColourScale) usePeaksCache = false;
1937 }
1938
1939 for (int pixel = 0; pixel < 256; ++pixel) {
1940 m_drawBuffer.setColor((unsigned char)pixel,
1941 m_palette.getColour((unsigned char)pixel).rgb());
1942 }
1943
1944 m_drawBuffer.fill(0);
1945 int attainedBufwid = bufwid;
1946
1947 double softTimeLimit;
1948
1949 if (m_synchronous) {
1950
1951 // must paint the whole thing for synchronous mode, so give
1952 // "no timeout"
1953 softTimeLimit = 0.0;
1954
1955 } else if (bufferIsBinResolution) {
1956
1957 // calculating boundaries later will be too fiddly for partial
1958 // paints, and painting should be fast anyway when this is the
1959 // case because it means we're well zoomed in
1960 softTimeLimit = 0.0;
1961
1962 } else {
1963
1964 // neither limitation applies, so use a short soft limit
1965
1966 if (m_binDisplay == PeakFrequencies) {
1967 softTimeLimit = 0.15;
1968 } else {
1969 softTimeLimit = 0.1;
1970 }
1971 }
1972
1973 if (m_binDisplay != PeakFrequencies) {
1974
1975 for (int y = 0; y < h; ++y) {
1976 double q0 = 0, q1 = 0;
1977 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
1978 binfory[y] = -1;
1979 } else {
1980 binfory[y] = q0;
1981 }
1982 }
1983
1984 attainedBufwid =
1985 paintDrawBuffer(v, bufwid, h, binforx, binfory,
1986 usePeaksCache,
1987 overallMag, overallMagChanged,
1988 rightToLeft,
1989 softTimeLimit);
1990
1991 } else {
1992
1993 attainedBufwid =
1994 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
1995 minbin, maxbin,
1996 displayMinFreq, displayMaxFreq,
1997 logarithmic,
1998 overallMag, overallMagChanged,
1999 rightToLeft,
2000 softTimeLimit);
2001 }
2002
2003 int failedToRepaint = bufwid - attainedBufwid;
2004
2005 int paintedLeft = x0;
2006 int paintedWidth = x1 - x0;
2007
2008 if (failedToRepaint > 0) {
2009
2010 #ifdef DEBUG_SPECTROGRAM_REPAINT
2011 cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid
2012 << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl;
2013 #endif
2014
2015 if (rightToLeft) {
2016 paintedLeft += failedToRepaint;
2017 }
2018
2019 paintedWidth -= failedToRepaint;
2020
2021 if (paintedWidth < 0) {
2022 paintedWidth = 0;
2023 }
2024
2025 } else if (failedToRepaint < 0) {
2026 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")"
2027 << endl;
2028 failedToRepaint = 0;
2029 }
2030
2031 if (overallMagChanged) {
2032 m_viewMags[v->getId()] = overallMag;
2033 #ifdef DEBUG_SPECTROGRAM_REPAINT
2034 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl;
2035 #endif
2036 }
2037
2038 outerprof.end();
2039
2040 Profiler profiler2("SpectrogramLayer::paint: draw image");
2041
2042 if (paintedWidth > 0) {
2043
2044 #ifdef DEBUG_SPECTROGRAM_REPAINT
2045 cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h
2046 << " from draw buffer at " << paintedLeft - x0 << "," << 0
2047 << " to " << paintedWidth << "x" << h << " on cache at "
2048 << x0 << "," << 0 << endl;
2049 #endif
2050
2051 if (bufferIsBinResolution) {
2052
2053 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2054 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2055
2056 #ifdef DEBUG_SPECTROGRAM_REPAINT
2057 cerr << "SpectrogramLayer: Rescaling image from " << bufwid
2058 << "x" << h << " to "
2059 << scaledRight-scaledLeft << "x" << h << endl;
2060 #endif
2061
2062 Preferences::SpectrogramXSmoothing xsmoothing =
2063 Preferences::getInstance()->getSpectrogramXSmoothing();
2064
2065 QImage scaled = m_drawBuffer.scaled
2066 (scaledRight - scaledLeft, h,
2067 Qt::IgnoreAspectRatio,
2068 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2069 Qt::SmoothTransformation : Qt::FastTransformation));
2070
2071 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2072 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2073
2074 #ifdef DEBUG_SPECTROGRAM_REPAINT
2075 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2076 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2077 #endif
2078
2079 int targetLeft = scaledLeftCrop;
2080 if (targetLeft < 0) {
2081 targetLeft = 0;
2082 }
2083
2084 int targetWidth = scaledRightCrop - targetLeft;
2085 if (targetLeft + targetWidth > cache.getSize().width()) {
2086 targetWidth = cache.getSize().width() - targetLeft;
2087 }
2088
2089 int sourceLeft = targetLeft - scaledLeft;
2090 if (sourceLeft < 0) {
2091 sourceLeft = 0;
2092 }
2093
2094 int sourceWidth = targetWidth;
2095
2096 if (targetWidth > 0) {
2097 cache.drawImage
2098 (targetLeft,
2099 targetWidth,
2100 scaled,
2101 sourceLeft,
2102 sourceWidth);
2103 }
2104
2105 } else {
2106
2107 cache.drawImage(paintedLeft, paintedWidth,
2108 m_drawBuffer,
2109 paintedLeft - x0, paintedWidth);
2110 }
2111 }
2112
2113 #ifdef DEBUG_SPECTROGRAM_REPAINT
2114 cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft()
2115 << " width " << cache.getValidWidth() << ", height "
2116 << cache.getSize().height() << endl;
2117 #endif
2118
2119 QRect pr = rect & cache.getValidArea();
2120
2121 #ifdef DEBUG_SPECTROGRAM_REPAINT
2122 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height()
2123 << " from cache at " << pr.x() << "," << pr.y()
2124 << " to window" << endl;
2125 #endif
2126
2127 paint.drawImage(pr.x(), pr.y(), cache.getImage(),
2128 pr.x(), pr.y(), pr.width(), pr.height());
2129
2130 if (!m_synchronous) {
2131
2132 if ((m_normalization != NormalizeVisibleArea) || !overallMagChanged) {
2133
2134 QRect areaLeft(0, 0, cache.getValidLeft(), h);
2135 QRect areaRight(cache.getValidRight(), 0,
2136 cache.getSize().width() - cache.getValidRight(), h);
2137
2138 bool haveSpaceLeft = (areaLeft.width() > 0);
2139 bool haveSpaceRight = (areaRight.width() > 0);
2140
2141 bool updateLeft = haveSpaceLeft;
2142 bool updateRight = haveSpaceRight;
2143
2144 if (updateLeft && updateRight) {
2145 if (rightToLeft) {
2146 // we just did something adjoining the cache on
2147 // its left side, so now do something on its right
2148 updateLeft = false;
2149 } else {
2150 updateRight = false;
2151 }
2152 }
2153
2154 if (updateLeft) {
2155 #ifdef DEBUG_SPECTROGRAM_REPAINT
2156 cerr << "SpectrogramLayer::paint() updating left ("
2157 << areaLeft.x() << ", "
2158 << areaLeft.width() << ")" << endl;
2159 #endif
2160 v->updatePaintRect(areaLeft);
2161 }
2162
2163 if (updateRight) {
2164 #ifdef DEBUG_SPECTROGRAM_REPAINT
2165 cerr << "SpectrogramLayer::paint() updating right ("
2166 << areaRight.x() << ", "
2167 << areaRight.width() << ")" << endl;
2168 #endif
2169 v->updatePaintRect(areaRight);
2170 }
2171
2172 } else {
2173 // overallMagChanged
2174 cerr << "\noverallMagChanged - updating all\n" << endl;
2175 cache.invalidate();
2176 v->updatePaintRect(v->getPaintRect());
2177 }
2178 }
2179 1567
2180 illuminateLocalFeatures(v, paint); 1568 illuminateLocalFeatures(v, paint);
2181
2182 #ifdef DEBUG_SPECTROGRAM_REPAINT
2183 cerr << "SpectrogramLayer::paint() returning" << endl;
2184 #endif
2185 }
2186
2187 int
2188 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v,
2189 int w,
2190 int h,
2191 const vector<int> &binforx,
2192 int minbin,
2193 int maxbin,
2194 double displayMinFreq,
2195 double displayMaxFreq,
2196 bool logarithmic,
2197 MagnitudeRange &overallMag,
2198 bool &overallMagChanged,
2199 bool rightToLeft,
2200 double softTimeLimit) const
2201 {
2202 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
2203
2204 #ifdef DEBUG_SPECTROGRAM_REPAINT
2205 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2206 #endif
2207 if (minbin < 0) minbin = 0;
2208 if (maxbin < 0) maxbin = minbin+1;
2209
2210 FFTModel *fft = getFFTModel(v);
2211 if (!fft) return 0;
2212
2213 FFTModel::PeakSet peakfreqs;
2214
2215 int psx = -1;
2216
2217 #ifdef __GNUC__
2218 float values[maxbin - minbin + 1];
2219 #else
2220 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2221 #endif
2222
2223 int minColumns = 4;
2224 bool haveTimeLimits = (softTimeLimit > 0.0);
2225 double hardTimeLimit = softTimeLimit * 2.0;
2226 bool overridingSoftLimit = false;
2227 auto startTime = chrono::steady_clock::now();
2228
2229 int start = 0;
2230 int finish = w;
2231 int step = 1;
2232
2233 if (rightToLeft) {
2234 start = w-1;
2235 finish = -1;
2236 step = -1;
2237 }
2238
2239 int columnCount = 0;
2240
2241 for (int x = start; x != finish; x += step) {
2242
2243 ++columnCount;
2244
2245 if (binforx[x] < 0) continue;
2246
2247 int sx0 = binforx[x];
2248 int sx1 = sx0;
2249 if (x+1 < w) sx1 = binforx[x+1];
2250 if (sx0 < 0) sx0 = sx1 - 1;
2251 if (sx0 < 0) continue;
2252 if (sx1 <= sx0) sx1 = sx0 + 1;
2253
2254 for (int sx = sx0; sx < sx1; ++sx) {
2255
2256 if (sx < 0 || sx >= int(fft->getWidth())) continue;
2257
2258 MagnitudeRange mag;
2259
2260 if (sx != psx) {
2261 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2262 minbin, maxbin - 1);
2263 if (m_colourScale == PhaseColourScale) {
2264 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2265 } else if (m_normalization == NormalizeColumns) {
2266 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2267 } else if (m_normalization == NormalizeHybrid) {
2268 float max = fft->getNormalizedMagnitudesAt
2269 (sx, values, minbin, maxbin - minbin + 1);
2270 float scale = log10f(max + 1.f);
2271 for (int i = minbin; i <= maxbin; ++i) {
2272 values[i - minbin] *= scale;
2273 }
2274 } else {
2275 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2276 }
2277 psx = sx;
2278 }
2279
2280 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
2281 pi != peakfreqs.end(); ++pi) {
2282
2283 int bin = pi->first;
2284 double freq = pi->second;
2285
2286 if (bin < minbin) continue;
2287 if (bin > maxbin) break;
2288
2289 double value = values[bin - minbin];
2290
2291 if (m_colourScale != PhaseColourScale) {
2292 if (m_normalization != NormalizeColumns) {
2293 value /= (m_fftSize/2.0);
2294 }
2295 mag.sample(float(value));
2296 value *= m_gain;
2297 }
2298
2299 double y = v->getYForFrequency
2300 (freq, displayMinFreq, displayMaxFreq, logarithmic);
2301
2302 int iy = int(y + 0.5);
2303 if (iy < 0 || iy >= h) continue;
2304
2305 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value));
2306 }
2307
2308 if (mag.isSet()) {
2309 if (sx >= int(m_columnMags.size())) {
2310 #ifdef DEBUG_SPECTROGRAM
2311 cerr << "INTERNAL ERROR: " << sx << " >= "
2312 << m_columnMags.size()
2313 << " at SpectrogramLayer.cpp::paintDrawBuffer"
2314 << endl;
2315 #endif
2316 } else {
2317 m_columnMags[sx].sample(mag);
2318 if (overallMag.sample(mag)) overallMagChanged = true;
2319 }
2320 }
2321 }
2322
2323 if (haveTimeLimits) {
2324 if (columnCount >= minColumns) {
2325 auto t = chrono::steady_clock::now();
2326 double diff = chrono::duration<double>(t - startTime).count();
2327 if (diff > hardTimeLimit) {
2328 #ifdef DEBUG_SPECTROGRAM_REPAINT
2329 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: hard limit " << hardTimeLimit << " sec exceeded after "
2330 << columnCount << " columns with time " << diff << endl;
2331 #endif
2332 return columnCount;
2333 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2334 // If we're more than half way through by the time
2335 // we reach the soft limit, ignore it (though
2336 // still respect the hard limit, above). Otherwise
2337 // respect the soft limit and return now.
2338 if (columnCount > w/2) {
2339 overridingSoftLimit = true;
2340 } else {
2341 #ifdef DEBUG_SPECTROGRAM_REPAINT
2342 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: soft limit " << softTimeLimit << " sec exceeded after "
2343 << columnCount << " columns with time " << diff << endl;
2344 #endif
2345 return columnCount;
2346 }
2347 }
2348 }
2349 }
2350 }
2351
2352 return columnCount;
2353 }
2354
2355 int
2356 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
2357 int w,
2358 int h,
2359 const vector<int> &binforx,
2360 const vector<double> &binfory,
2361 bool usePeaksCache,
2362 MagnitudeRange &overallMag,
2363 bool &overallMagChanged,
2364 bool rightToLeft,
2365 double softTimeLimit) const
2366 {
2367 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2368
2369 int minbin = int(binfory[0] + 0.0001);
2370 int maxbin = int(binfory[h-1]);
2371
2372 #ifdef DEBUG_SPECTROGRAM_REPAINT
2373 cerr << "SpectrogramLayer::paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2374 #endif
2375 if (minbin < 0) minbin = 0;
2376 if (maxbin < 0) maxbin = minbin+1;
2377
2378 DenseThreeDimensionalModel *sourceModel = 0;
2379 FFTModel *fft = 0;
2380 int divisor = 1;
2381 #ifdef DEBUG_SPECTROGRAM_REPAINT
2382 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2383 #endif
2384 if (usePeaksCache) {
2385 sourceModel = getPeakCache(v);
2386 divisor = m_peakCacheDivisor;
2387 minbin = 0;
2388 maxbin = sourceModel->getHeight();
2389 } else {
2390 sourceModel = fft = getFFTModel(v);
2391 }
2392
2393 if (!sourceModel) return 0;
2394
2395 bool interpolate = false;
2396 Preferences::SpectrogramSmoothing smoothing =
2397 Preferences::getInstance()->getSpectrogramSmoothing();
2398 if (smoothing == Preferences::SpectrogramInterpolated ||
2399 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2400 if (m_binDisplay != PeakBins &&
2401 m_binDisplay != PeakFrequencies) {
2402 interpolate = true;
2403 }
2404 }
2405
2406 int psx = -1;
2407
2408 #ifdef __GNUC__
2409 float autoarray[maxbin - minbin + 1];
2410 float peaks[h];
2411 #else
2412 float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2413 float *peaks = (float *)alloca(h * sizeof(float));
2414 #endif
2415
2416 const float *values = autoarray;
2417 DenseThreeDimensionalModel::Column c;
2418
2419 int minColumns = 4;
2420 bool haveTimeLimits = (softTimeLimit > 0.0);
2421 double hardTimeLimit = softTimeLimit * 2.0;
2422 bool overridingSoftLimit = false;
2423 auto startTime = chrono::steady_clock::now();
2424
2425 int start = 0;
2426 int finish = w;
2427 int step = 1;
2428
2429 if (rightToLeft) {
2430 start = w-1;
2431 finish = -1;
2432 step = -1;
2433 }
2434
2435 int columnCount = 0;
2436
2437 for (int x = start; x != finish; x += step) {
2438
2439 // x is the on-canvas pixel coord; sx (later) will be the
2440 // source column index
2441
2442 ++columnCount;
2443
2444 if (binforx[x] < 0) continue;
2445
2446 float columnMax = 0.f;
2447
2448 int sx0 = binforx[x] / divisor;
2449 int sx1 = sx0;
2450 if (x+1 < w) sx1 = binforx[x+1] / divisor;
2451 if (sx0 < 0) sx0 = sx1 - 1;
2452 if (sx0 < 0) continue;
2453 if (sx1 <= sx0) sx1 = sx0 + 1;
2454
2455 for (int y = 0; y < h; ++y) peaks[y] = 0.f;
2456
2457 for (int sx = sx0; sx < sx1; ++sx) {
2458
2459 #ifdef DEBUG_SPECTROGRAM_REPAINT
2460 // cerr << "sx = " << sx << endl;
2461 #endif
2462
2463 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue;
2464
2465 MagnitudeRange mag;
2466
2467 if (sx != psx) {
2468 if (fft) {
2469 #ifdef DEBUG_SPECTROGRAM_REPAINT
2470 // cerr << "Retrieving column " << sx << " from fft directly" << endl;
2471 #endif
2472 if (m_colourScale == PhaseColourScale) {
2473 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2474 } else if (m_normalization == NormalizeColumns) {
2475 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2476 } else if (m_normalization == NormalizeHybrid) {
2477 float max = fft->getNormalizedMagnitudesAt
2478 (sx, autoarray, minbin, maxbin - minbin + 1);
2479 float scale = log10f(max + 1.f);
2480 for (int i = minbin; i <= maxbin; ++i) {
2481 autoarray[i - minbin] *= scale;
2482 }
2483 } else {
2484 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2485 }
2486 } else {
2487 #ifdef DEBUG_SPECTROGRAM_REPAINT
2488 // cerr << "Retrieving column " << sx << " from peaks cache" << endl;
2489 #endif
2490 c = sourceModel->getColumn(sx);
2491 if (m_normalization == NormalizeColumns ||
2492 m_normalization == NormalizeHybrid) {
2493 for (int y = 0; y < h; ++y) {
2494 if (c[y] > columnMax) columnMax = c[y];
2495 }
2496 }
2497 values = c.data() + minbin;
2498 }
2499 psx = sx;
2500 }
2501
2502 for (int y = 0; y < h; ++y) {
2503
2504 double sy0 = binfory[y];
2505 double sy1 = sy0 + 1;
2506 if (y+1 < h) sy1 = binfory[y+1];
2507
2508 double value = 0.0;
2509
2510 if (interpolate && fabs(sy1 - sy0) < 1.0) {
2511
2512 double centre = (sy0 + sy1) / 2;
2513 double dist = (centre - 0.5) - rint(centre - 0.5);
2514 int bin = int(centre);
2515 int other = (dist < 0 ? (bin-1) : (bin+1));
2516 if (bin < minbin) bin = minbin;
2517 if (bin > maxbin) bin = maxbin;
2518 if (other < minbin || other > maxbin) other = bin;
2519 double prop = 1.0 - fabs(dist);
2520
2521 double v0 = values[bin - minbin];
2522 double v1 = values[other - minbin];
2523 if (m_binDisplay == PeakBins) {
2524 if (bin == minbin || bin == maxbin ||
2525 v0 < values[bin-minbin-1] ||
2526 v0 < values[bin-minbin+1]) v0 = 0.0;
2527 if (other == minbin || other == maxbin ||
2528 v1 < values[other-minbin-1] ||
2529 v1 < values[other-minbin+1]) v1 = 0.0;
2530 }
2531 if (v0 == 0.0 && v1 == 0.0) continue;
2532 value = prop * v0 + (1.0 - prop) * v1;
2533
2534 if (m_colourScale != PhaseColourScale) {
2535 if (m_normalization != NormalizeColumns &&
2536 m_normalization != NormalizeHybrid) {
2537 value /= (m_fftSize/2.0);
2538 }
2539 mag.sample(float(value));
2540 value *= m_gain;
2541 }
2542
2543 peaks[y] = float(value);
2544
2545 } else {
2546
2547 int by0 = int(sy0 + 0.0001);
2548 int by1 = int(sy1 + 0.0001);
2549 if (by1 < by0 + 1) by1 = by0 + 1;
2550
2551 for (int bin = by0; bin < by1; ++bin) {
2552
2553 value = values[bin - minbin];
2554 if (m_binDisplay == PeakBins) {
2555 if (bin == minbin || bin == maxbin ||
2556 value < values[bin-minbin-1] ||
2557 value < values[bin-minbin+1]) continue;
2558 }
2559
2560 if (m_colourScale != PhaseColourScale) {
2561 if (m_normalization != NormalizeColumns &&
2562 m_normalization != NormalizeHybrid) {
2563 value /= (m_fftSize/2.0);
2564 }
2565 mag.sample(float(value));
2566 value *= m_gain;
2567 }
2568
2569 if (value > peaks[y]) {
2570 peaks[y] = float(value); //!!! not right for phase!
2571 }
2572 }
2573 }
2574 }
2575
2576 if (mag.isSet()) {
2577 if (sx >= int(m_columnMags.size())) {
2578 #ifdef DEBUG_SPECTROGRAM
2579 cerr << "INTERNAL ERROR: " << sx << " >= "
2580 << m_columnMags.size()
2581 << " at SpectrogramLayer.cpp::paintDrawBuffer"
2582 << endl;
2583 #endif
2584 } else {
2585 m_columnMags[sx].sample(mag);
2586 if (overallMag.sample(mag)) overallMagChanged = true;
2587 }
2588 }
2589 }
2590
2591 // at this point we have updated m_columnMags and overallMag
2592 // -- used elsewhere for calculating the overall view range
2593 // for NormalizeVisibleArea mode -- and calculated "peaks"
2594 // (the possibly scaled and interpolated value array from
2595 // which we actually draw the column) and "columnMax" (maximum
2596 // value used for normalisation)
2597
2598 for (int y = 0; y < h; ++y) {
2599
2600 double peak = peaks[y];
2601
2602 if (m_colourScale != PhaseColourScale &&
2603 (m_normalization == NormalizeColumns ||
2604 m_normalization == NormalizeHybrid) &&
2605 columnMax > 0.f) {
2606 peak /= columnMax;
2607 if (m_normalization == NormalizeHybrid) {
2608 peak *= log10(columnMax + 1.f);
2609 }
2610 }
2611
2612 unsigned char peakpix = getDisplayValue(v, peak);
2613
2614 m_drawBuffer.setPixel(x, h-y-1, peakpix);
2615 }
2616
2617 if (haveTimeLimits) {
2618 if (columnCount >= minColumns) {
2619 auto t = chrono::steady_clock::now();
2620 double diff = chrono::duration<double>(t - startTime).count();
2621 if (diff > hardTimeLimit) {
2622 #ifdef DEBUG_SPECTROGRAM_REPAINT
2623 cerr << "SpectrogramLayer::paintDrawBuffer: hard limit " << hardTimeLimit << " sec exceeded after "
2624 << columnCount << " columns with time " << diff << endl;
2625 #endif
2626 return columnCount;
2627 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2628 // If we're more than half way through by the time
2629 // we reach the soft limit, ignore it (though
2630 // still respect the hard limit, above). Otherwise
2631 // respect the soft limit and return now.
2632 if (columnCount > w/2) {
2633 overridingSoftLimit = true;
2634 } else {
2635 #ifdef DEBUG_SPECTROGRAM_REPAINT
2636 cerr << "SpectrogramLayer::paintDrawBuffer: soft limit " << softTimeLimit << " sec exceeded after "
2637 << columnCount << " columns with time " << diff << endl;
2638 #endif
2639 return columnCount;
2640 }
2641 }
2642 }
2643 }
2644 }
2645
2646 return columnCount;
2647 } 1569 }
2648 1570
2649 void 1571 void
2650 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const 1572 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
2651 { 1573 {
2654 QPoint localPos; 1576 QPoint localPos;
2655 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { 1577 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
2656 return; 1578 return;
2657 } 1579 }
2658 1580
2659 // cerr << "SpectrogramLayer: illuminateLocalFeatures(" 1581 #ifdef DEBUG_SPECTROGRAM_REPAINT
2660 // << localPos.x() << "," << localPos.y() << ")" << endl; 1582 cerr << "SpectrogramLayer: illuminateLocalFeatures("
1583 << localPos.x() << "," << localPos.y() << ")" << endl;
1584 #endif
2661 1585
2662 double s0, s1; 1586 double s0, s1;
2663 double f0, f1; 1587 double f0, f1;
2664 1588
2665 if (getXBinRange(v, localPos.x(), s0, s1) && 1589 if (getXBinRange(v, localPos.x(), s0, s1) &&
2672 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement()); 1596 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
2673 1597
2674 int y1 = int(getYForFrequency(v, f1)); 1598 int y1 = int(getYForFrequency(v, f1));
2675 int y0 = int(getYForFrequency(v, f0)); 1599 int y0 = int(getYForFrequency(v, f0));
2676 1600
2677 // cerr << "SpectrogramLayer: illuminate " 1601 #ifdef DEBUG_SPECTROGRAM_REPAINT
2678 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl; 1602 cerr << "SpectrogramLayer: illuminate "
1603 << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl;
1604 #endif
2679 1605
2680 paint.setPen(v->getForeground()); 1606 paint.setPen(v->getForeground());
2681 1607
2682 //!!! should we be using paintCrosshairs for this? 1608 //!!! should we be using paintCrosshairs for this?
2683 1609
2689 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const 1615 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const
2690 { 1616 {
2691 return v->getYForFrequency(frequency, 1617 return v->getYForFrequency(frequency,
2692 getEffectiveMinFrequency(), 1618 getEffectiveMinFrequency(),
2693 getEffectiveMaxFrequency(), 1619 getEffectiveMaxFrequency(),
2694 m_frequencyScale == LogFrequencyScale); 1620 m_binScale == BinScale::Log);
2695 } 1621 }
2696 1622
2697 double 1623 double
2698 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const 1624 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const
2699 { 1625 {
2700 return v->getFrequencyForY(y, 1626 return v->getFrequencyForY(y,
2701 getEffectiveMinFrequency(), 1627 getEffectiveMinFrequency(),
2702 getEffectiveMaxFrequency(), 1628 getEffectiveMaxFrequency(),
2703 m_frequencyScale == LogFrequencyScale); 1629 m_binScale == BinScale::Log);
2704 } 1630 }
2705 1631
2706 int 1632 int
2707 SpectrogramLayer::getCompletion(LayerGeometryProvider *v) const 1633 SpectrogramLayer::getCompletion(LayerGeometryProvider *) const
2708 { 1634 {
2709 const View *view = v->getView(); 1635 if (!m_fftModel) return 100;
2710 1636 int completion = m_fftModel->getCompletion();
2711 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return 100;
2712
2713 int completion = m_fftModels[view->getId()]->getCompletion();
2714 #ifdef DEBUG_SPECTROGRAM_REPAINT 1637 #ifdef DEBUG_SPECTROGRAM_REPAINT
2715 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl; 1638 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
2716 #endif 1639 #endif
2717 return completion; 1640 return completion;
2718 } 1641 }
2719 1642
2720 QString 1643 QString
2721 SpectrogramLayer::getError(LayerGeometryProvider *v) const 1644 SpectrogramLayer::getError(LayerGeometryProvider *) const
2722 { 1645 {
2723 const View *view = v->getView(); 1646 if (!m_fftModel) return "";
2724 if (m_fftModels.find(view->getId()) == m_fftModels.end()) return ""; 1647 return m_fftModel->getError();
2725 return m_fftModels[view->getId()]->getError();
2726 } 1648 }
2727 1649
2728 bool 1650 bool
2729 SpectrogramLayer::getValueExtents(double &min, double &max, 1651 SpectrogramLayer::getValueExtents(double &min, double &max,
2730 bool &logarithmic, QString &unit) const 1652 bool &logarithmic, QString &unit) const
2731 { 1653 {
2732 if (!m_model) return false; 1654 if (!m_model) return false;
2733 1655
2734 sv_samplerate_t sr = m_model->getSampleRate(); 1656 sv_samplerate_t sr = m_model->getSampleRate();
2735 min = double(sr) / m_fftSize; 1657 min = double(sr) / getFFTSize();
2736 max = double(sr) / 2; 1658 max = double(sr) / 2;
2737 1659
2738 logarithmic = (m_frequencyScale == LogFrequencyScale); 1660 logarithmic = (m_binScale == BinScale::Log);
2739 unit = "Hz"; 1661 unit = "Hz";
2740 return true; 1662 return true;
2741 } 1663 }
2742 1664
2743 bool 1665 bool
2763 int minf = int(lrint(min)); 1685 int minf = int(lrint(min));
2764 int maxf = int(lrint(max)); 1686 int maxf = int(lrint(max));
2765 1687
2766 if (m_minFrequency == minf && m_maxFrequency == maxf) return true; 1688 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
2767 1689
2768 invalidateImageCaches(); 1690 invalidateRenderers();
2769 invalidateMagnitudes(); 1691 invalidateMagnitudes();
2770 1692
2771 m_minFrequency = minf; 1693 m_minFrequency = minf;
2772 m_maxFrequency = maxf; 1694 m_maxFrequency = maxf;
2773 1695
2815 } 1737 }
2816 1738
2817 void 1739 void
2818 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) 1740 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
2819 { 1741 {
2820 const View *view = v->getView(); 1742 const Colour3DPlotRenderer *renderer = getRenderer(v);
2821 ScrollableImageCache &cache = getImageCacheReference(view); 1743 if (!renderer) return;
2822 1744
2823 cerr << "cache width: " << cache.getSize().width() << ", height: " 1745 QRect rect = renderer->findSimilarRegionExtents(e->pos());
2824 << cache.getSize().height() << endl;
2825
2826 QImage image = cache.getImage();
2827
2828 ImageRegionFinder finder;
2829 QRect rect = finder.findRegionExtents(&image, e->pos());
2830 if (rect.isValid()) { 1746 if (rect.isValid()) {
2831 MeasureRect mr; 1747 MeasureRect mr;
2832 setMeasureRectFromPixrect(v, mr, rect); 1748 setMeasureRectFromPixrect(v, mr, rect);
2833 CommandHistory::getInstance()->addCommand 1749 CommandHistory::getInstance()->addCommand
2834 (new AddMeasurementRectCommand(this, mr)); 1750 (new AddMeasurementRectCommand(this, mr));
2892 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); 1808 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
2893 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight()); 1809 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight());
2894 1810
2895 double fundamental = getFrequencyForY(v, cursorPos.y()); 1811 double fundamental = getFrequencyForY(v, cursorPos.y());
2896 1812
2897 v->drawVisibleText(paint, 1813 PaintAssistant::drawVisibleText(v, paint,
2898 sw + 2, 1814 sw + 2,
2899 cursorPos.y() - 2, 1815 cursorPos.y() - 2,
2900 QString("%1 Hz").arg(fundamental), 1816 QString("%1 Hz").arg(fundamental),
2901 View::OutlinedText); 1817 PaintAssistant::OutlinedText);
2902 1818
2903 if (Pitch::isFrequencyInMidiRange(fundamental)) { 1819 if (Pitch::isFrequencyInMidiRange(fundamental)) {
2904 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); 1820 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
2905 v->drawVisibleText(paint, 1821 PaintAssistant::drawVisibleText(v, paint,
2906 sw + 2, 1822 sw + 2,
2907 cursorPos.y() + paint.fontMetrics().ascent() + 2, 1823 cursorPos.y() + paint.fontMetrics().ascent() + 2,
2908 pitchLabel, 1824 pitchLabel,
2909 View::OutlinedText); 1825 PaintAssistant::OutlinedText);
2910 } 1826 }
2911 1827
2912 sv_frame_t frame = v->getFrameForX(cursorPos.x()); 1828 sv_frame_t frame = v->getFrameForX(cursorPos.x());
2913 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); 1829 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
2914 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); 1830 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
2915 QString frameLabel = QString("%1").arg(frame); 1831 QString frameLabel = QString("%1").arg(frame);
2916 v->drawVisibleText(paint, 1832 PaintAssistant::drawVisibleText(v, paint,
2917 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, 1833 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
2918 v->getPaintHeight() - 2, 1834 v->getPaintHeight() - 2,
2919 frameLabel, 1835 frameLabel,
2920 View::OutlinedText); 1836 PaintAssistant::OutlinedText);
2921 v->drawVisibleText(paint, 1837 PaintAssistant::drawVisibleText(v, paint,
2922 cursorPos.x() + 2, 1838 cursorPos.x() + 2,
2923 v->getPaintHeight() - 2, 1839 v->getPaintHeight() - 2,
2924 rtLabel, 1840 rtLabel,
2925 View::OutlinedText); 1841 PaintAssistant::OutlinedText);
2926 1842
2927 int harmonic = 2; 1843 int harmonic = 2;
2928 1844
2929 while (harmonic < 100) { 1845 while (harmonic < 100) {
2930 1846
2976 haveValues = true; 1892 haveValues = true;
2977 } 1893 }
2978 1894
2979 QString adjFreqText = "", adjPitchText = ""; 1895 QString adjFreqText = "", adjPitchText = "";
2980 1896
2981 if (m_binDisplay == PeakFrequencies) { 1897 if (m_binDisplay == BinDisplay::PeakFrequencies) {
2982 1898
2983 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, 1899 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
2984 adjFreqMin, adjFreqMax)) { 1900 adjFreqMin, adjFreqMax)) {
2985 return ""; 1901 return "";
2986 } 1902 }
3088 m_model->getSampleRate() / 2)); 2004 m_model->getSampleRate() / 2));
3089 2005
3090 int fw = paint.fontMetrics().width(tr("43Hz")); 2006 int fw = paint.fontMetrics().width(tr("43Hz"));
3091 if (tw < fw) tw = fw; 2007 if (tw < fw) tw = fw;
3092 2008
3093 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 2009 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
3094 2010
3095 return cw + tickw + tw + 13; 2011 return cw + tickw + tw + 13;
3096 } 2012 }
3097 2013
3098 void 2014 void
3099 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const 2015 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed,
2016 QPainter &paint, QRect rect) const
3100 { 2017 {
3101 if (!m_model || !m_model->isOK()) { 2018 if (!m_model || !m_model->isOK()) {
3102 return; 2019 return;
3103 } 2020 }
3104 2021
3105 Profiler profiler("SpectrogramLayer::paintVerticalScale"); 2022 Profiler profiler("SpectrogramLayer::paintVerticalScale");
3106 2023
3107 //!!! cache this? 2024 //!!! cache this?
3108 2025
3109 int h = rect.height(), w = rect.width(); 2026 int h = rect.height(), w = rect.width();
3110 2027 int textHeight = paint.fontMetrics().height();
3111 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 2028
3112 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); 2029 if (detailed && (h > textHeight * 3 + 10)) {
3113 2030 paintDetailedScale(v, paint, rect);
3114 int bins = m_fftSize / 2; 2031 }
2032 m_haveDetailedScale = detailed;
2033
2034 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
2035 int pkw = (m_binScale == BinScale::Log ? 10 : 0);
2036
2037 int bins = getFFTSize() / 2;
3115 sv_samplerate_t sr = m_model->getSampleRate(); 2038 sv_samplerate_t sr = m_model->getSampleRate();
3116 2039
3117 if (m_maxFrequency > 0) { 2040 if (m_maxFrequency > 0) {
3118 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 2041 bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
3119 if (bins > m_fftSize / 2) bins = m_fftSize / 2; 2042 if (bins > getFFTSize() / 2) bins = getFFTSize() / 2;
3120 } 2043 }
3121 2044
3122 int cw = 0; 2045 int cw = 0;
3123
3124 if (detailed) cw = getColourScaleWidth(paint); 2046 if (detailed) cw = getColourScaleWidth(paint);
3125 int cbw = paint.fontMetrics().width("dB");
3126 2047
3127 int py = -1; 2048 int py = -1;
3128 int textHeight = paint.fontMetrics().height();
3129 int toff = -textHeight + paint.fontMetrics().ascent() + 2; 2049 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
3130
3131 if (detailed && (h > textHeight * 3 + 10)) {
3132
3133 int topLines = 2;
3134 if (m_colourScale == PhaseColourScale) topLines = 1;
3135
3136 int ch = h - textHeight * (topLines + 1) - 8;
3137 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
3138 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3139
3140 QString top, bottom;
3141 double min = m_viewMags[v->getId()].getMin();
3142 double max = m_viewMags[v->getId()].getMax();
3143
3144 double dBmin = AudioLevel::multiplier_to_dB(min);
3145 double dBmax = AudioLevel::multiplier_to_dB(max);
3146
3147 #ifdef DEBUG_SPECTROGRAM_REPAINT
3148 cerr << "paintVerticalScale: for view id " << v->getId()
3149 << ": min = " << min << ", max = " << max
3150 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
3151 #endif
3152
3153 if (dBmax < -60.f) dBmax = -60.f;
3154 else top = QString("%1").arg(lrint(dBmax));
3155
3156 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
3157 bottom = QString("%1").arg(lrint(dBmin));
3158
3159 //!!! & phase etc
3160
3161 if (m_colourScale != PhaseColourScale) {
3162 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
3163 2 + textHeight + toff, "dBFS");
3164 }
3165
3166 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
3167 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
3168 2 + textHeight * topLines + toff + textHeight/2, top);
3169
3170 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
3171 h + toff - 3 - textHeight/2, bottom);
3172
3173 paint.save();
3174 paint.setBrush(Qt::NoBrush);
3175
3176 int lasty = 0;
3177 int lastdb = 0;
3178
3179 for (int i = 0; i < ch; ++i) {
3180
3181 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
3182 int idb = int(dBval);
3183
3184 double value = AudioLevel::dB_to_multiplier(dBval);
3185 int colour = getDisplayValue(v, value * m_gain);
3186
3187 paint.setPen(m_palette.getColour((unsigned char)colour));
3188
3189 int y = textHeight * topLines + 4 + ch - i;
3190
3191 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
3192
3193 if (i == 0) {
3194 lasty = y;
3195 lastdb = idb;
3196 } else if (i < ch - paint.fontMetrics().ascent() &&
3197 idb != lastdb &&
3198 ((abs(y - lasty) > textHeight &&
3199 idb % 10 == 0) ||
3200 (abs(y - lasty) > paint.fontMetrics().ascent() &&
3201 idb % 5 == 0))) {
3202 paint.setPen(v->getBackground());
3203 QString text = QString("%1").arg(idb);
3204 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
3205 y + toff + textHeight/2, text);
3206 paint.setPen(v->getForeground());
3207 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
3208 lasty = y;
3209 lastdb = idb;
3210 }
3211 }
3212 paint.restore();
3213 }
3214 2050
3215 paint.drawLine(cw + 7, 0, cw + 7, h); 2051 paint.drawLine(cw + 7, 0, cw + 7, h);
3216 2052
3217 int bin = -1; 2053 int bin = -1;
3218 2054
3228 bin = int(q0); 2064 bin = int(q0);
3229 } else { 2065 } else {
3230 continue; 2066 continue;
3231 } 2067 }
3232 2068
3233 int freq = int((sr * bin) / m_fftSize); 2069 int freq = int((sr * bin) / getFFTSize());
3234 2070
3235 if (py >= 0 && (vy - py) < textHeight - 1) { 2071 if (py >= 0 && (vy - py) < textHeight - 1) {
3236 if (m_frequencyScale == LinearFrequencyScale) { 2072 if (m_binScale == BinScale::Linear) {
3237 paint.drawLine(w - tickw, h - vy, w, h - vy); 2073 paint.drawLine(w - tickw, h - vy, w, h - vy);
3238 } 2074 }
3239 continue; 2075 continue;
3240 } 2076 }
3241 2077
3249 } 2085 }
3250 2086
3251 py = vy; 2087 py = vy;
3252 } 2088 }
3253 2089
3254 if (m_frequencyScale == LogFrequencyScale) { 2090 if (m_binScale == BinScale::Log) {
3255 2091
3256 // piano keyboard 2092 // piano keyboard
3257 2093
3258 PianoScale().paintPianoVertical 2094 PianoScale().paintPianoVertical
3259 (v, paint, QRect(w - pkw - 1, 0, pkw, h), 2095 (v, paint, QRect(w - pkw - 1, 0, pkw, h),
3260 getEffectiveMinFrequency(), getEffectiveMaxFrequency()); 2096 getEffectiveMinFrequency(), getEffectiveMaxFrequency());
3261 } 2097 }
3262 2098
3263 m_haveDetailedScale = detailed; 2099 m_haveDetailedScale = detailed;
2100 }
2101
2102 void
2103 SpectrogramLayer::paintDetailedScale(LayerGeometryProvider *v,
2104 QPainter &paint, QRect rect) const
2105 {
2106 // The colour scale
2107
2108 if (m_colourScale == ColourScaleType::Phase) {
2109 paintDetailedScalePhase(v, paint, rect);
2110 return;
2111 }
2112
2113 int h = rect.height();
2114 int textHeight = paint.fontMetrics().height();
2115 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2116
2117 int cw = getColourScaleWidth(paint);
2118 int cbw = paint.fontMetrics().width("dB");
2119
2120 int topLines = 2;
2121
2122 int ch = h - textHeight * (topLines + 1) - 8;
2123 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
2124 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2125
2126 QString top, bottom;
2127 double min = m_viewMags[v->getId()].getMin();
2128 double max = m_viewMags[v->getId()].getMax();
2129
2130 if (min < m_threshold) min = m_threshold;
2131 if (max <= min) max = min + 0.1;
2132
2133 double dBmin = AudioLevel::multiplier_to_dB(min);
2134 double dBmax = AudioLevel::multiplier_to_dB(max);
2135
2136 #ifdef DEBUG_SPECTROGRAM_REPAINT
2137 cerr << "paintVerticalScale: for view id " << v->getId()
2138 << ": min = " << min << ", max = " << max
2139 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
2140 #endif
2141
2142 if (dBmax < -60.f) dBmax = -60.f;
2143 else top = QString("%1").arg(lrint(dBmax));
2144
2145 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
2146 bottom = QString("%1").arg(lrint(dBmin));
2147
2148 #ifdef DEBUG_SPECTROGRAM_REPAINT
2149 cerr << "adjusted dB range to min = " << dBmin << ", max = " << dBmax
2150 << endl;
2151 #endif
2152
2153 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
2154 2 + textHeight + toff, "dBFS");
2155
2156 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2157 2 + textHeight * topLines + toff + textHeight/2, top);
2158
2159 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2160 h + toff - 3 - textHeight/2, bottom);
2161
2162 paint.save();
2163 paint.setBrush(Qt::NoBrush);
2164
2165 int lasty = 0;
2166 int lastdb = 0;
2167
2168 for (int i = 0; i < ch; ++i) {
2169
2170 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
2171 int idb = int(dBval);
2172
2173 double value = AudioLevel::dB_to_multiplier(dBval);
2174 paint.setPen(getRenderer(v)->getColour(value));
2175
2176 int y = textHeight * topLines + 4 + ch - i;
2177
2178 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2179
2180 if (i == 0) {
2181 lasty = y;
2182 lastdb = idb;
2183 } else if (i < ch - paint.fontMetrics().ascent() &&
2184 idb != lastdb &&
2185 ((abs(y - lasty) > textHeight &&
2186 idb % 10 == 0) ||
2187 (abs(y - lasty) > paint.fontMetrics().ascent() &&
2188 idb % 5 == 0))) {
2189 paint.setPen(v->getForeground());
2190 QString text = QString("%1").arg(idb);
2191 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
2192 y + toff + textHeight/2, text);
2193 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
2194 lasty = y;
2195 lastdb = idb;
2196 }
2197 }
2198 paint.restore();
2199 }
2200
2201 void
2202 SpectrogramLayer::paintDetailedScalePhase(LayerGeometryProvider *v,
2203 QPainter &paint, QRect rect) const
2204 {
2205 // The colour scale in phase mode
2206
2207 int h = rect.height();
2208 int textHeight = paint.fontMetrics().height();
2209 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2210
2211 int cw = getColourScaleWidth(paint);
2212
2213 // Phase is not measured in dB of course, but this places the
2214 // scale at the same position as in the magnitude spectrogram
2215 int cbw = paint.fontMetrics().width("dB");
2216
2217 int topLines = 1;
2218
2219 int ch = h - textHeight * (topLines + 1) - 8;
2220 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2221
2222 QString top, bottom, middle;
2223 top = QString("%1").arg(QChar(0x3c0)); // pi
2224 bottom = "-" + top;
2225 middle = "0";
2226
2227 double min = -M_PI;
2228 double max = M_PI;
2229
2230 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2231 2 + textHeight * topLines + toff + textHeight/2, top);
2232
2233 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(middle),
2234 2 + textHeight * topLines + ch/2 + toff + textHeight/2, middle);
2235
2236 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2237 h + toff - 3 - textHeight/2, bottom);
2238
2239 paint.save();
2240 paint.setBrush(Qt::NoBrush);
2241
2242 for (int i = 0; i < ch; ++i) {
2243 double val = min + (((max - min) * i) / (ch - 1));
2244 paint.setPen(getRenderer(v)->getColour(val));
2245 int y = textHeight * topLines + 4 + ch - i;
2246 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2247 }
2248 paint.restore();
3264 } 2249 }
3265 2250
3266 class SpectrogramRangeMapper : public RangeMapper 2251 class SpectrogramRangeMapper : public RangeMapper
3267 { 2252 {
3268 public: 2253 public:
3325 { 2310 {
3326 if (!m_model) return 0; 2311 if (!m_model) return 0;
3327 2312
3328 sv_samplerate_t sr = m_model->getSampleRate(); 2313 sv_samplerate_t sr = m_model->getSampleRate();
3329 2314
3330 SpectrogramRangeMapper mapper(sr, m_fftSize); 2315 SpectrogramRangeMapper mapper(sr, getFFTSize());
3331 2316
3332 // int maxStep = mapper.getPositionForValue((double(sr) / m_fftSize) + 0.001); 2317 // int maxStep = mapper.getPositionForValue((double(sr) / getFFTSize()) + 0.001);
3333 int maxStep = mapper.getPositionForValue(0); 2318 int maxStep = mapper.getPositionForValue(0);
3334 int minStep = mapper.getPositionForValue(double(sr) / 2); 2319 int minStep = mapper.getPositionForValue(double(sr) / 2);
3335 2320
3336 int initialMax = m_initialMaxFrequency; 2321 int initialMax = m_initialMaxFrequency;
3337 if (initialMax == 0) initialMax = int(sr / 2); 2322 if (initialMax == 0) initialMax = int(sr / 2);
3349 if (!m_model) return 0; 2334 if (!m_model) return 0;
3350 2335
3351 double dmin, dmax; 2336 double dmin, dmax;
3352 getDisplayExtents(dmin, dmax); 2337 getDisplayExtents(dmin, dmax);
3353 2338
3354 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); 2339 SpectrogramRangeMapper mapper(m_model->getSampleRate(), getFFTSize());
3355 int n = mapper.getPositionForValue(dmax - dmin); 2340 int n = mapper.getPositionForValue(dmax - dmin);
3356 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl; 2341 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
3357 return n; 2342 return n;
3358 } 2343 }
3359 2344
3366 // getDisplayExtents(dmin, dmax); 2351 // getDisplayExtents(dmin, dmax);
3367 2352
3368 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl; 2353 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl;
3369 2354
3370 sv_samplerate_t sr = m_model->getSampleRate(); 2355 sv_samplerate_t sr = m_model->getSampleRate();
3371 SpectrogramRangeMapper mapper(sr, m_fftSize); 2356 SpectrogramRangeMapper mapper(sr, getFFTSize());
3372 double newdist = mapper.getValueForPosition(step); 2357 double newdist = mapper.getValueForPosition(step);
3373 2358
3374 double newmin, newmax; 2359 double newmin, newmax;
3375 2360
3376 if (m_frequencyScale == LogFrequencyScale) { 2361 if (m_binScale == BinScale::Log) {
3377 2362
3378 // need to pick newmin and newmax such that 2363 // need to pick newmin and newmax such that
3379 // 2364 //
3380 // (log(newmin) + log(newmax)) / 2 == logmid 2365 // (log(newmin) + log(newmax)) / 2 == logmid
3381 // and 2366 // and
3427 2412
3428 RangeMapper * 2413 RangeMapper *
3429 SpectrogramLayer::getNewVerticalZoomRangeMapper() const 2414 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
3430 { 2415 {
3431 if (!m_model) return 0; 2416 if (!m_model) return 0;
3432 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); 2417 return new SpectrogramRangeMapper(m_model->getSampleRate(), getFFTSize());
3433 } 2418 }
3434 2419
3435 void 2420 void
3436 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const 2421 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
3437 { 2422 {
3483 "colourRotation=\"%5\" " 2468 "colourRotation=\"%5\" "
3484 "frequencyScale=\"%6\" " 2469 "frequencyScale=\"%6\" "
3485 "binDisplay=\"%7\" ") 2470 "binDisplay=\"%7\" ")
3486 .arg(m_minFrequency) 2471 .arg(m_minFrequency)
3487 .arg(m_maxFrequency) 2472 .arg(m_maxFrequency)
3488 .arg(m_colourScale) 2473 .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple))
3489 .arg(m_colourMap) 2474 .arg(m_colourMap)
3490 .arg(m_colourRotation) 2475 .arg(m_colourRotation)
3491 .arg(m_frequencyScale) 2476 .arg(int(m_binScale))
3492 .arg(m_binDisplay); 2477 .arg(int(m_binDisplay));
3493 2478
3494 // New-style normalization attributes, allowing for more types of 2479 // New-style normalization attributes, allowing for more types of
3495 // normalization in future: write out the column normalization 2480 // normalization in future: write out the column normalization
3496 // type separately, and then whether we are normalizing visible 2481 // type separately, and then whether we are normalizing visible
3497 // area as well afterwards 2482 // area as well afterwards
3498 2483
3499 s += QString("columnNormalization=\"%1\" ") 2484 s += QString("columnNormalization=\"%1\" ")
3500 .arg(m_normalization == NormalizeColumns ? "peak" : 2485 .arg(m_normalization == ColumnNormalization::Max1 ? "peak" :
3501 m_normalization == NormalizeHybrid ? "hybrid" : "none"); 2486 m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
3502 2487
3503 // Old-style normalization attribute. We *don't* write out 2488 // Old-style normalization attribute. We *don't* write out
3504 // normalizeHybrid here because the only release that would accept 2489 // normalizeHybrid here because the only release that would accept
3505 // it (Tony v1.0) has a totally different scale factor for 2490 // it (Tony v1.0) has a totally different scale factor for
3506 // it. We'll just have to accept that session files from Tony 2491 // it. We'll just have to accept that session files from Tony
3507 // v2.0+ will look odd in Tony v1.0 2492 // v2.0+ will look odd in Tony v1.0
3508 2493
3509 s += QString("normalizeColumns=\"%1\" ") 2494 s += QString("normalizeColumns=\"%1\" ")
3510 .arg(m_normalization == NormalizeColumns ? "true" : "false"); 2495 .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false");
3511 2496
3512 // And this applies to both old- and new-style attributes 2497 // And this applies to both old- and new-style attributes
3513 2498
3514 s += QString("normalizeVisibleArea=\"%1\" ") 2499 s += QString("normalizeVisibleArea=\"%1\" ")
3515 .arg(m_normalization == NormalizeVisibleArea ? "true" : "false"); 2500 .arg(m_normalizeVisibleArea ? "true" : "false");
3516 2501
3517 Layer::toXml(stream, indent, extraAttributes + " " + s); 2502 Layer::toXml(stream, indent, extraAttributes + " " + s);
3518 } 2503 }
3519 2504
3520 void 2505 void
3558 if (ok) { 2543 if (ok) {
3559 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl; 2544 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
3560 setMaxFrequency(maxFrequency); 2545 setMaxFrequency(maxFrequency);
3561 } 2546 }
3562 2547
3563 ColourScale colourScale = (ColourScale) 2548 auto colourScale = convertToColourScale
3564 attributes.value("colourScale").toInt(&ok); 2549 (attributes.value("colourScale").toInt(&ok));
3565 if (ok) setColourScale(colourScale); 2550 if (ok) {
2551 setColourScale(colourScale.first);
2552 setColourScaleMultiple(colourScale.second);
2553 }
3566 2554
3567 int colourMap = attributes.value("colourScheme").toInt(&ok); 2555 int colourMap = attributes.value("colourScheme").toInt(&ok);
3568 if (ok) setColourMap(colourMap); 2556 if (ok) setColourMap(colourMap);
3569 2557
3570 int colourRotation = attributes.value("colourRotation").toInt(&ok); 2558 int colourRotation = attributes.value("colourRotation").toInt(&ok);
3571 if (ok) setColourRotation(colourRotation); 2559 if (ok) setColourRotation(colourRotation);
3572 2560
3573 FrequencyScale frequencyScale = (FrequencyScale) 2561 BinScale binScale = (BinScale)
3574 attributes.value("frequencyScale").toInt(&ok); 2562 attributes.value("frequencyScale").toInt(&ok);
3575 if (ok) setFrequencyScale(frequencyScale); 2563 if (ok) setBinScale(binScale);
3576 2564
3577 BinDisplay binDisplay = (BinDisplay) 2565 BinDisplay binDisplay = (BinDisplay)
3578 attributes.value("binDisplay").toInt(&ok); 2566 attributes.value("binDisplay").toInt(&ok);
3579 if (ok) setBinDisplay(binDisplay); 2567 if (ok) setBinDisplay(binDisplay);
3580 2568
3585 if (columnNormalization != "") { 2573 if (columnNormalization != "") {
3586 2574
3587 haveNewStyleNormalization = true; 2575 haveNewStyleNormalization = true;
3588 2576
3589 if (columnNormalization == "peak") { 2577 if (columnNormalization == "peak") {
3590 setNormalization(NormalizeColumns); 2578 setNormalization(ColumnNormalization::Max1);
3591 } else if (columnNormalization == "hybrid") { 2579 } else if (columnNormalization == "hybrid") {
3592 setNormalization(NormalizeHybrid); 2580 setNormalization(ColumnNormalization::Hybrid);
3593 } else if (columnNormalization == "none") { 2581 } else if (columnNormalization == "none") {
3594 // do nothing 2582 setNormalization(ColumnNormalization::None);
3595 } else { 2583 } else {
3596 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \"" 2584 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
3597 << columnNormalization << "\"" << endl; 2585 << columnNormalization << "\"" << endl;
3598 } 2586 }
3599 } 2587 }
3601 if (!haveNewStyleNormalization) { 2589 if (!haveNewStyleNormalization) {
3602 2590
3603 bool normalizeColumns = 2591 bool normalizeColumns =
3604 (attributes.value("normalizeColumns").trimmed() == "true"); 2592 (attributes.value("normalizeColumns").trimmed() == "true");
3605 if (normalizeColumns) { 2593 if (normalizeColumns) {
3606 setNormalization(NormalizeColumns); 2594 setNormalization(ColumnNormalization::Max1);
3607 } 2595 }
3608 2596
3609 bool normalizeHybrid = 2597 bool normalizeHybrid =
3610 (attributes.value("normalizeHybrid").trimmed() == "true"); 2598 (attributes.value("normalizeHybrid").trimmed() == "true");
3611 if (normalizeHybrid) { 2599 if (normalizeHybrid) {
3612 setNormalization(NormalizeHybrid); 2600 setNormalization(ColumnNormalization::Hybrid);
3613 } 2601 }
3614 } 2602 }
3615 2603
3616 bool normalizeVisibleArea = 2604 bool normalizeVisibleArea =
3617 (attributes.value("normalizeVisibleArea").trimmed() == "true"); 2605 (attributes.value("normalizeVisibleArea").trimmed() == "true");
3618 if (normalizeVisibleArea) { 2606 setNormalizeVisibleArea(normalizeVisibleArea);
3619 setNormalization(NormalizeVisibleArea); 2607
3620 } 2608 if (!haveNewStyleNormalization && m_normalization == ColumnNormalization::Hybrid) {
3621
3622 if (!haveNewStyleNormalization && m_normalization == NormalizeHybrid) {
3623 // Tony v1.0 is (and hopefully will remain!) the only released 2609 // Tony v1.0 is (and hopefully will remain!) the only released
3624 // SV-a-like to use old-style attributes when saving sessions 2610 // SV-a-like to use old-style attributes when saving sessions
3625 // that ask for hybrid normalization. It saves them with the 2611 // that ask for hybrid normalization. It saves them with the
3626 // wrong gain factor, so hack in a fix for that here -- this 2612 // wrong gain factor, so hack in a fix for that here -- this
3627 // gives us backward but not forward compatibility. 2613 // gives us backward but not forward compatibility.
3628 setGain(m_gain / float(m_fftSize / 2)); 2614 setGain(m_gain / float(getFFTSize() / 2));
3629 } 2615 }
3630 } 2616 }
3631 2617