comparison layer/SpectrogramLayer.cpp @ 1216:dc2af6616c83

Merge from branch 3.0-integration
author Chris Cannam
date Fri, 13 Jan 2017 10:29:50 +0000
parents a1ee3108d1d3
children de1792daae07
comparison
equal deleted inserted replaced
1048:e8102ff5573b 1216:dc2af6616c83
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"
27 #include "base/Strings.h"
28 #include "base/StorageAdviser.h"
29 #include "base/Exceptions.h"
26 #include "widgets/CommandHistory.h" 30 #include "widgets/CommandHistory.h"
31 #include "data/model/Dense3DModelPeakCache.h"
32
27 #include "ColourMapper.h" 33 #include "ColourMapper.h"
28 #include "ImageRegionFinder.h"
29 #include "data/model/Dense3DModelPeakCache.h"
30 #include "PianoScale.h" 34 #include "PianoScale.h"
35 #include "PaintAssistant.h"
36 #include "Colour3DPlotRenderer.h"
31 37
32 #include <QPainter> 38 #include <QPainter>
33 #include <QImage> 39 #include <QImage>
34 #include <QPixmap> 40 #include <QPixmap>
35 #include <QRect> 41 #include <QRect>
36 #include <QTimer>
37 #include <QApplication> 42 #include <QApplication>
38 #include <QMessageBox> 43 #include <QMessageBox>
39 #include <QMouseEvent> 44 #include <QMouseEvent>
40 #include <QTextStream> 45 #include <QTextStream>
46 #include <QSettings>
41 47
42 #include <iostream> 48 #include <iostream>
43 49
44 #include <cassert> 50 #include <cassert>
45 #include <cmath> 51 #include <cmath>
46 52
47 #ifndef __GNUC__ 53 //#define DEBUG_SPECTROGRAM 1
48 #include <alloca.h>
49 #endif
50
51 //#define DEBUG_SPECTROGRAM_REPAINT 1 54 //#define DEBUG_SPECTROGRAM_REPAINT 1
52 55
53 using std::vector; 56 using namespace std;
54 57
55 SpectrogramLayer::SpectrogramLayer(Configuration config) : 58 SpectrogramLayer::SpectrogramLayer(Configuration config) :
56 m_model(0), 59 m_model(0),
57 m_channel(0), 60 m_channel(0),
58 m_windowSize(1024), 61 m_windowSize(1024),
59 m_windowType(HanningWindow), 62 m_windowType(HanningWindow),
60 m_windowHopLevel(2), 63 m_windowHopLevel(2),
61 m_zeroPadLevel(0),
62 m_fftSize(1024),
63 m_gain(1.0), 64 m_gain(1.0),
64 m_initialGain(1.0), 65 m_initialGain(1.0),
65 m_threshold(0.0), 66 m_threshold(1.0e-8f),
66 m_initialThreshold(0.0), 67 m_initialThreshold(1.0e-8f),
67 m_colourRotation(0), 68 m_colourRotation(0),
68 m_initialRotation(0), 69 m_initialRotation(0),
69 m_minFrequency(10), 70 m_minFrequency(10),
70 m_maxFrequency(8000), 71 m_maxFrequency(8000),
71 m_initialMaxFrequency(8000), 72 m_initialMaxFrequency(8000),
72 m_colourScale(dBColourScale), 73 m_colourScale(ColourScaleType::Log),
74 m_colourScaleMultiple(1.0),
73 m_colourMap(0), 75 m_colourMap(0),
74 m_frequencyScale(LinearFrequencyScale), 76 m_binScale(BinScale::Linear),
75 m_binDisplay(AllBins), 77 m_binDisplay(BinDisplay::AllBins),
76 m_normalizeColumns(false), 78 m_normalization(ColumnNormalization::None),
77 m_normalizeVisibleArea(false), 79 m_normalizeVisibleArea(false),
78 m_normalizeHybrid(false),
79 m_lastEmittedZoomStep(-1), 80 m_lastEmittedZoomStep(-1),
80 m_synchronous(false), 81 m_synchronous(false),
81 m_haveDetailedScale(false), 82 m_haveDetailedScale(false),
82 m_lastPaintBlockWidth(0),
83 m_updateTimer(0),
84 m_candidateFillStartFrame(0),
85 m_exiting(false), 83 m_exiting(false),
86 m_sliceableModel(0) 84 m_fftModel(0),
87 { 85 m_wholeCache(0),
86 m_peakCache(0),
87 m_peakCacheDivisor(8)
88 {
89 QString colourConfigName = "spectrogram-colour";
90 int colourConfigDefault = int(ColourMapper::Green);
91
88 if (config == FullRangeDb) { 92 if (config == FullRangeDb) {
89 m_initialMaxFrequency = 0; 93 m_initialMaxFrequency = 0;
90 setMaxFrequency(0); 94 setMaxFrequency(0);
91 } else if (config == MelodicRange) { 95 } else if (config == MelodicRange) {
92 setWindowSize(8192); 96 setWindowSize(8192);
93 setWindowHopLevel(4); 97 setWindowHopLevel(4);
94 m_initialMaxFrequency = 1500; 98 m_initialMaxFrequency = 1500;
95 setMaxFrequency(1500); 99 setMaxFrequency(1500);
96 setMinFrequency(40); 100 setMinFrequency(40);
97 setColourScale(LinearColourScale); 101 setColourScale(ColourScaleType::Linear);
98 setColourMap(ColourMapper::Sunset); 102 setColourMap(ColourMapper::Sunset);
99 setFrequencyScale(LogFrequencyScale); 103 setBinScale(BinScale::Log);
104 colourConfigName = "spectrogram-melodic-colour";
105 colourConfigDefault = int(ColourMapper::Sunset);
100 // setGain(20); 106 // setGain(20);
101 } else if (config == MelodicPeaks) { 107 } else if (config == MelodicPeaks) {
102 setWindowSize(4096); 108 setWindowSize(4096);
103 setWindowHopLevel(5); 109 setWindowHopLevel(5);
104 m_initialMaxFrequency = 2000; 110 m_initialMaxFrequency = 2000;
105 setMaxFrequency(2000); 111 setMaxFrequency(2000);
106 setMinFrequency(40); 112 setMinFrequency(40);
107 setFrequencyScale(LogFrequencyScale); 113 setBinScale(BinScale::Log);
108 setColourScale(LinearColourScale); 114 setColourScale(ColourScaleType::Linear);
109 setBinDisplay(PeakFrequencies); 115 setBinDisplay(BinDisplay::PeakFrequencies);
110 setNormalizeColumns(true); 116 setNormalization(ColumnNormalization::Max1);
111 } 117 colourConfigName = "spectrogram-melodic-colour";
112 118 colourConfigDefault = int(ColourMapper::Sunset);
119 }
120
121 QSettings settings;
122 settings.beginGroup("Preferences");
123 setColourMap(settings.value(colourConfigName, colourConfigDefault).toInt());
124 settings.endGroup();
125
113 Preferences *prefs = Preferences::getInstance(); 126 Preferences *prefs = Preferences::getInstance();
114 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 127 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
115 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 128 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
116 setWindowType(prefs->getWindowType()); 129 setWindowType(prefs->getWindowType());
117
118 initialisePalette();
119 } 130 }
120 131
121 SpectrogramLayer::~SpectrogramLayer() 132 SpectrogramLayer::~SpectrogramLayer()
122 { 133 {
123 delete m_updateTimer; 134 invalidateRenderers();
124 m_updateTimer = 0; 135
125 136 delete m_fftModel;
126 invalidateFFTModels(); 137 delete m_peakCache;
138 delete m_wholeCache;
139 }
140
141 pair<ColourScaleType, double>
142 SpectrogramLayer::convertToColourScale(int value)
143 {
144 switch (value) {
145 case 0: return { ColourScaleType::Linear, 1.0 };
146 case 1: return { ColourScaleType::Meter, 1.0 };
147 case 2: return { ColourScaleType::Log, 2.0 }; // dB^2 (i.e. log of power)
148 case 3: return { ColourScaleType::Log, 1.0 }; // dB (of magnitude)
149 case 4: return { ColourScaleType::Phase, 1.0 };
150 default: return { ColourScaleType::Linear, 1.0 };
151 }
152 }
153
154 int
155 SpectrogramLayer::convertFromColourScale(ColourScaleType scale, double multiple)
156 {
157 switch (scale) {
158 case ColourScaleType::Linear: return 0;
159 case ColourScaleType::Meter: return 1;
160 case ColourScaleType::Log: return (multiple > 1.5 ? 2 : 3);
161 case ColourScaleType::Phase: return 4;
162 case ColourScaleType::PlusMinusOne:
163 case ColourScaleType::Absolute:
164 default: return 0;
165 }
166 }
167
168 std::pair<ColumnNormalization, bool>
169 SpectrogramLayer::convertToColumnNorm(int value)
170 {
171 switch (value) {
172 default:
173 case 0: return { ColumnNormalization::None, false };
174 case 1: return { ColumnNormalization::Max1, false };
175 case 2: return { ColumnNormalization::None, true }; // visible area
176 case 3: return { ColumnNormalization::Hybrid, false };
177 }
178 }
179
180 int
181 SpectrogramLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
182 {
183 if (visible) return 2;
184 switch (norm) {
185 case ColumnNormalization::None: return 0;
186 case ColumnNormalization::Max1: return 1;
187 case ColumnNormalization::Hybrid: return 3;
188
189 case ColumnNormalization::Sum1:
190 default: return 0;
191 }
127 } 192 }
128 193
129 void 194 void
130 SpectrogramLayer::setModel(const DenseTimeValueModel *model) 195 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
131 { 196 {
132 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl; 197 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl;
133 198
134 if (model == m_model) return; 199 if (model == m_model) return;
135 200
136 m_model = model; 201 m_model = model;
137 invalidateFFTModels(); 202
203 recreateFFTModel();
138 204
139 if (!m_model || !m_model->isOK()) return; 205 if (!m_model || !m_model->isOK()) return;
140 206
141 connectSignals(m_model); 207 connectSignals(m_model);
142 208
153 PropertyList list; 219 PropertyList list;
154 list.push_back("Colour"); 220 list.push_back("Colour");
155 list.push_back("Colour Scale"); 221 list.push_back("Colour Scale");
156 list.push_back("Window Size"); 222 list.push_back("Window Size");
157 list.push_back("Window Increment"); 223 list.push_back("Window Increment");
158 list.push_back("Normalize Columns"); 224 list.push_back("Normalization");
159 list.push_back("Normalize Visible Area");
160 list.push_back("Bin Display"); 225 list.push_back("Bin Display");
161 list.push_back("Threshold"); 226 list.push_back("Threshold");
162 list.push_back("Gain"); 227 list.push_back("Gain");
163 list.push_back("Colour Rotation"); 228 list.push_back("Colour Rotation");
164 // list.push_back("Min Frequency"); 229 // list.push_back("Min Frequency");
165 // list.push_back("Max Frequency"); 230 // list.push_back("Max Frequency");
166 list.push_back("Frequency Scale"); 231 list.push_back("Frequency Scale");
167 //// list.push_back("Zero Padding");
168 return list; 232 return list;
169 } 233 }
170 234
171 QString 235 QString
172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const 236 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
173 { 237 {
174 if (name == "Colour") return tr("Colour"); 238 if (name == "Colour") return tr("Colour");
175 if (name == "Colour Scale") return tr("Colour Scale"); 239 if (name == "Colour Scale") return tr("Colour Scale");
176 if (name == "Window Size") return tr("Window Size"); 240 if (name == "Window Size") return tr("Window Size");
177 if (name == "Window Increment") return tr("Window Overlap"); 241 if (name == "Window Increment") return tr("Window Overlap");
178 if (name == "Normalize Columns") return tr("Normalize Columns"); 242 if (name == "Normalization") return tr("Normalization");
179 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
180 if (name == "Bin Display") return tr("Bin Display"); 243 if (name == "Bin Display") return tr("Bin Display");
181 if (name == "Threshold") return tr("Threshold"); 244 if (name == "Threshold") return tr("Threshold");
182 if (name == "Gain") return tr("Gain"); 245 if (name == "Gain") return tr("Gain");
183 if (name == "Colour Rotation") return tr("Colour Rotation"); 246 if (name == "Colour Rotation") return tr("Colour Rotation");
184 if (name == "Min Frequency") return tr("Min Frequency"); 247 if (name == "Min Frequency") return tr("Min Frequency");
185 if (name == "Max Frequency") return tr("Max Frequency"); 248 if (name == "Max Frequency") return tr("Max Frequency");
186 if (name == "Frequency Scale") return tr("Frequency Scale"); 249 if (name == "Frequency Scale") return tr("Frequency Scale");
187 if (name == "Zero Padding") return tr("Smoothing");
188 return ""; 250 return "";
189 } 251 }
190 252
191 QString 253 QString
192 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const 254 SpectrogramLayer::getPropertyIconName(const PropertyName &) const
193 { 255 {
194 if (name == "Normalize Columns") return "normalise-columns";
195 if (name == "Normalize Visible Area") return "normalise";
196 return ""; 256 return "";
197 } 257 }
198 258
199 Layer::PropertyType 259 Layer::PropertyType
200 SpectrogramLayer::getPropertyType(const PropertyName &name) const 260 SpectrogramLayer::getPropertyType(const PropertyName &name) const
201 { 261 {
202 if (name == "Gain") return RangeProperty; 262 if (name == "Gain") return RangeProperty;
203 if (name == "Colour Rotation") return RangeProperty; 263 if (name == "Colour Rotation") return RangeProperty;
204 if (name == "Normalize Columns") return ToggleProperty;
205 if (name == "Normalize Visible Area") return ToggleProperty;
206 if (name == "Threshold") return RangeProperty; 264 if (name == "Threshold") return RangeProperty;
207 if (name == "Zero Padding") return ToggleProperty; 265 if (name == "Colour") return ColourMapProperty;
208 return ValueProperty; 266 return ValueProperty;
209 } 267 }
210 268
211 QString 269 QString
212 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 270 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
213 { 271 {
214 if (name == "Bin Display" || 272 if (name == "Bin Display" ||
215 name == "Frequency Scale") return tr("Bins"); 273 name == "Frequency Scale") return tr("Bins");
216 if (name == "Window Size" || 274 if (name == "Window Size" ||
217 name == "Window Increment" || 275 name == "Window Increment") return tr("Window");
218 name == "Zero Padding") return tr("Window");
219 if (name == "Colour" || 276 if (name == "Colour" ||
220 name == "Threshold" || 277 name == "Threshold" ||
221 name == "Colour Rotation") return tr("Colour"); 278 name == "Colour Rotation") return tr("Colour");
222 if (name == "Normalize Columns" || 279 if (name == "Normalization" ||
223 name == "Normalize Visible Area" ||
224 name == "Gain" || 280 name == "Gain" ||
225 name == "Colour Scale") return tr("Scale"); 281 name == "Colour Scale") return tr("Scale");
226 return QString(); 282 return QString();
227 } 283 }
228 284
250 if (val < *min) val = *min; 306 if (val < *min) val = *min;
251 if (val > *max) val = *max; 307 if (val > *max) val = *max;
252 308
253 } else if (name == "Threshold") { 309 } else if (name == "Threshold") {
254 310
255 *min = -50; 311 *min = -81;
256 *max = 0; 312 *max = -1;
257 313
258 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); 314 *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold)));
259 if (*deflt < *min) *deflt = *min; 315 if (*deflt < *min) *deflt = *min;
260 if (*deflt > *max) *deflt = *max; 316 if (*deflt > *max) *deflt = *max;
261 317
271 327
272 val = m_colourRotation; 328 val = m_colourRotation;
273 329
274 } else if (name == "Colour Scale") { 330 } else if (name == "Colour Scale") {
275 331
332 // linear, meter, db^2, db, phase
276 *min = 0; 333 *min = 0;
277 *max = 4; 334 *max = 4;
278 *deflt = int(dBColourScale); 335 *deflt = 2;
279 336
280 val = (int)m_colourScale; 337 val = convertFromColourScale(m_colourScale, m_colourScaleMultiple);
281 338
282 } else if (name == "Colour") { 339 } else if (name == "Colour") {
283 340
284 *min = 0; 341 *min = 0;
285 *max = ColourMapper::getColourMapCount() - 1; 342 *max = ColourMapper::getColourMapCount() - 1;
302 *min = 0; 359 *min = 0;
303 *max = 5; 360 *max = 5;
304 *deflt = 2; 361 *deflt = 2;
305 362
306 val = m_windowHopLevel; 363 val = m_windowHopLevel;
307
308 } else if (name == "Zero Padding") {
309
310 *min = 0;
311 *max = 1;
312 *deflt = 0;
313
314 val = m_zeroPadLevel > 0 ? 1 : 0;
315 364
316 } else if (name == "Min Frequency") { 365 } else if (name == "Min Frequency") {
317 366
318 *min = 0; 367 *min = 0;
319 *max = 9; 368 *max = 9;
353 402
354 } else if (name == "Frequency Scale") { 403 } else if (name == "Frequency Scale") {
355 404
356 *min = 0; 405 *min = 0;
357 *max = 1; 406 *max = 1;
358 *deflt = int(LinearFrequencyScale); 407 *deflt = int(BinScale::Linear);
359 val = (int)m_frequencyScale; 408 val = (int)m_binScale;
360 409
361 } else if (name == "Bin Display") { 410 } else if (name == "Bin Display") {
362 411
363 *min = 0; 412 *min = 0;
364 *max = 2; 413 *max = 2;
365 *deflt = int(AllBins); 414 *deflt = int(BinDisplay::AllBins);
366 val = (int)m_binDisplay; 415 val = (int)m_binDisplay;
367 416
368 } else if (name == "Normalize Columns") { 417 } else if (name == "Normalization") {
369 418
419 *min = 0;
420 *max = 3;
370 *deflt = 0; 421 *deflt = 0;
371 val = (m_normalizeColumns ? 1 : 0); 422
372 423 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
373 } else if (name == "Normalize Visible Area") {
374
375 *deflt = 0;
376 val = (m_normalizeVisibleArea ? 1 : 0);
377 424
378 } else { 425 } else {
379 val = Layer::getPropertyRangeAndValue(name, min, max, deflt); 426 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
380 } 427 }
381 428
396 case 1: return tr("Meter"); 443 case 1: return tr("Meter");
397 case 2: return tr("dBV^2"); 444 case 2: return tr("dBV^2");
398 case 3: return tr("dBV"); 445 case 3: return tr("dBV");
399 case 4: return tr("Phase"); 446 case 4: return tr("Phase");
400 } 447 }
448 }
449 if (name == "Normalization") {
450 switch(value) {
451 default:
452 case 0: return tr("None");
453 case 1: return tr("Col");
454 case 2: return tr("View");
455 case 3: return tr("Hybrid");
456 }
457 // return ""; // icon only
401 } 458 }
402 if (name == "Window Size") { 459 if (name == "Window Size") {
403 return QString("%1").arg(32 << value); 460 return QString("%1").arg(32 << value);
404 } 461 }
405 if (name == "Window Increment") { 462 if (name == "Window Increment") {
410 case 2: return tr("50 %"); 467 case 2: return tr("50 %");
411 case 3: return tr("75 %"); 468 case 3: return tr("75 %");
412 case 4: return tr("87.5 %"); 469 case 4: return tr("87.5 %");
413 case 5: return tr("93.75 %"); 470 case 5: return tr("93.75 %");
414 } 471 }
415 }
416 if (name == "Zero Padding") {
417 if (value == 0) return tr("None");
418 return QString("%1x").arg(value + 1);
419 } 472 }
420 if (name == "Min Frequency") { 473 if (name == "Min Frequency") {
421 switch (value) { 474 switch (value) {
422 default: 475 default:
423 case 0: return tr("No min"); 476 case 0: return tr("No min");
463 } 516 }
464 } 517 }
465 return tr("<unknown>"); 518 return tr("<unknown>");
466 } 519 }
467 520
521 QString
522 SpectrogramLayer::getPropertyValueIconName(const PropertyName &name,
523 int value) const
524 {
525 if (name == "Normalization") {
526 switch(value) {
527 default:
528 case 0: return "normalise-none";
529 case 1: return "normalise-columns";
530 case 2: return "normalise";
531 case 3: return "normalise-hybrid";
532 }
533 }
534 return "";
535 }
536
468 RangeMapper * 537 RangeMapper *
469 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const 538 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
470 { 539 {
471 if (name == "Gain") { 540 if (name == "Gain") {
472 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB")); 541 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
473 } 542 }
474 if (name == "Threshold") { 543 if (name == "Threshold") {
475 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB")); 544 return new LinearRangeMapper(-81, -1, -81, -1, tr("dB"), false,
545 { { -81, Strings::minus_infinity } });
476 } 546 }
477 return 0; 547 return 0;
478 } 548 }
479 549
480 void 550 void
481 SpectrogramLayer::setProperty(const PropertyName &name, int value) 551 SpectrogramLayer::setProperty(const PropertyName &name, int value)
482 { 552 {
483 if (name == "Gain") { 553 if (name == "Gain") {
484 setGain(float(pow(10, float(value)/20.0))); 554 setGain(float(pow(10, float(value)/20.0)));
485 } else if (name == "Threshold") { 555 } else if (name == "Threshold") {
486 if (value == -50) setThreshold(0.0); 556 if (value == -81) setThreshold(0.0);
487 else setThreshold(float(AudioLevel::dB_to_multiplier(value))); 557 else setThreshold(float(AudioLevel::dB_to_multiplier(value)));
488 } else if (name == "Colour Rotation") { 558 } else if (name == "Colour Rotation") {
489 setColourRotation(value); 559 setColourRotation(value);
490 } else if (name == "Colour") { 560 } else if (name == "Colour") {
491 setColourMap(value); 561 setColourMap(value);
492 } else if (name == "Window Size") { 562 } else if (name == "Window Size") {
493 setWindowSize(32 << value); 563 setWindowSize(32 << value);
494 } else if (name == "Window Increment") { 564 } else if (name == "Window Increment") {
495 setWindowHopLevel(value); 565 setWindowHopLevel(value);
496 } else if (name == "Zero Padding") {
497 setZeroPadLevel(value > 0.1 ? 3 : 0);
498 } else if (name == "Min Frequency") { 566 } else if (name == "Min Frequency") {
499 switch (value) { 567 switch (value) {
500 default: 568 default:
501 case 0: setMinFrequency(0); break; 569 case 0: setMinFrequency(0); break;
502 case 1: setMinFrequency(10); break; 570 case 1: setMinFrequency(10); break;
532 if (vs != m_lastEmittedZoomStep) { 600 if (vs != m_lastEmittedZoomStep) {
533 emit verticalZoomChanged(); 601 emit verticalZoomChanged();
534 m_lastEmittedZoomStep = vs; 602 m_lastEmittedZoomStep = vs;
535 } 603 }
536 } else if (name == "Colour Scale") { 604 } else if (name == "Colour Scale") {
605 setColourScaleMultiple(1.0);
537 switch (value) { 606 switch (value) {
538 default: 607 default:
539 case 0: setColourScale(LinearColourScale); break; 608 case 0: setColourScale(ColourScaleType::Linear); break;
540 case 1: setColourScale(MeterColourScale); break; 609 case 1: setColourScale(ColourScaleType::Meter); break;
541 case 2: setColourScale(dBSquaredColourScale); break; 610 case 2:
542 case 3: setColourScale(dBColourScale); break; 611 setColourScale(ColourScaleType::Log);
543 case 4: setColourScale(PhaseColourScale); break; 612 setColourScaleMultiple(2.0);
613 break;
614 case 3: setColourScale(ColourScaleType::Log); break;
615 case 4: setColourScale(ColourScaleType::Phase); break;
544 } 616 }
545 } else if (name == "Frequency Scale") { 617 } else if (name == "Frequency Scale") {
546 switch (value) { 618 switch (value) {
547 default: 619 default:
548 case 0: setFrequencyScale(LinearFrequencyScale); break; 620 case 0: setBinScale(BinScale::Linear); break;
549 case 1: setFrequencyScale(LogFrequencyScale); break; 621 case 1: setBinScale(BinScale::Log); break;
550 } 622 }
551 } else if (name == "Bin Display") { 623 } else if (name == "Bin Display") {
552 switch (value) { 624 switch (value) {
553 default: 625 default:
554 case 0: setBinDisplay(AllBins); break; 626 case 0: setBinDisplay(BinDisplay::AllBins); break;
555 case 1: setBinDisplay(PeakBins); break; 627 case 1: setBinDisplay(BinDisplay::PeakBins); break;
556 case 2: setBinDisplay(PeakFrequencies); break; 628 case 2: setBinDisplay(BinDisplay::PeakFrequencies); break;
557 } 629 }
558 } else if (name == "Normalize Columns") { 630 } else if (name == "Normalization") {
559 setNormalizeColumns(value ? true : false); 631 auto n = convertToColumnNorm(value);
560 } else if (name == "Normalize Visible Area") { 632 setNormalization(n.first);
561 setNormalizeVisibleArea(value ? true : false); 633 setNormalizeVisibleArea(n.second);
562 } 634 }
563 } 635 }
564 636
565 void 637 void
566 SpectrogramLayer::invalidateImageCaches() 638 SpectrogramLayer::invalidateRenderers()
567 { 639 {
568 for (ViewImageCache::iterator i = m_imageCaches.begin(); 640 #ifdef DEBUG_SPECTROGRAM
569 i != m_imageCaches.end(); ++i) { 641 cerr << "SpectrogramLayer::invalidateRenderers called" << endl;
570 i->second.validArea = QRect();
571 }
572 }
573
574 void
575 SpectrogramLayer::invalidateImageCaches(sv_frame_t startFrame, sv_frame_t endFrame)
576 {
577 for (ViewImageCache::iterator i = m_imageCaches.begin();
578 i != m_imageCaches.end(); ++i) {
579
580 //!!! when are views removed from the map? on setLayerDormant?
581 const View *v = i->first;
582
583 #ifdef DEBUG_SPECTROGRAM_REPAINT
584 SVDEBUG << "SpectrogramLayer::invalidateImageCaches("
585 << startFrame << ", " << endFrame << "): view range is "
586 << v->getStartFrame() << ", " << v->getEndFrame()
587 << endl;
588
589 cerr << "Valid area was: " << i->second.validArea.x() << ", "
590 << i->second.validArea.y() << " "
591 << i->second.validArea.width() << "x"
592 << i->second.validArea.height() << endl;
593 #endif 642 #endif
594 643
595 if (int(startFrame) > v->getStartFrame()) { 644 for (ViewRendererMap::iterator i = m_renderers.begin();
596 if (startFrame >= v->getEndFrame()) { 645 i != m_renderers.end(); ++i) {
597 #ifdef DEBUG_SPECTROGRAM_REPAINT 646 delete i->second;
598 cerr << "Modified start frame is off right of view" << endl; 647 }
599 #endif 648 m_renderers.clear();
600 return;
601 }
602 int x = v->getXForFrame(startFrame);
603 #ifdef DEBUG_SPECTROGRAM_REPAINT
604 SVDEBUG << "clipping from 0 to " << x-1 << endl;
605 #endif
606 if (x > 1) {
607 i->second.validArea &=
608 QRect(0, 0, x-1, v->height());
609 } else {
610 i->second.validArea = QRect();
611 }
612 } else {
613 if (int(endFrame) < v->getStartFrame()) {
614 #ifdef DEBUG_SPECTROGRAM_REPAINT
615 cerr << "Modified end frame is off left of view" << endl;
616 #endif
617 return;
618 }
619 int x = v->getXForFrame(endFrame);
620 #ifdef DEBUG_SPECTROGRAM_REPAINT
621 SVDEBUG << "clipping from " << x+1 << " to " << v->width()
622 << endl;
623 #endif
624 if (x < v->width()) {
625 i->second.validArea &=
626 QRect(x+1, 0, v->width()-(x+1), v->height());
627 } else {
628 i->second.validArea = QRect();
629 }
630 }
631
632 #ifdef DEBUG_SPECTROGRAM_REPAINT
633 cerr << "Valid area is now: " << i->second.validArea.x() << ", "
634 << i->second.validArea.y() << " "
635 << i->second.validArea.width() << "x"
636 << i->second.validArea.height() << endl;
637 #endif
638 }
639 } 649 }
640 650
641 void 651 void
642 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name) 652 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
643 { 653 {
646 if (name == "Window Type") { 656 if (name == "Window Type") {
647 setWindowType(Preferences::getInstance()->getWindowType()); 657 setWindowType(Preferences::getInstance()->getWindowType());
648 return; 658 return;
649 } 659 }
650 if (name == "Spectrogram Y Smoothing") { 660 if (name == "Spectrogram Y Smoothing") {
651 invalidateImageCaches(); 661 setWindowSize(m_windowSize);
662 invalidateRenderers();
652 invalidateMagnitudes(); 663 invalidateMagnitudes();
653 emit layerParametersChanged(); 664 emit layerParametersChanged();
654 } 665 }
655 if (name == "Spectrogram X Smoothing") { 666 if (name == "Spectrogram X Smoothing") {
656 invalidateImageCaches(); 667 invalidateRenderers();
657 invalidateMagnitudes(); 668 invalidateMagnitudes();
658 emit layerParametersChanged(); 669 emit layerParametersChanged();
659 } 670 }
660 if (name == "Tuning Frequency") { 671 if (name == "Tuning Frequency") {
661 emit layerParametersChanged(); 672 emit layerParametersChanged();
665 void 676 void
666 SpectrogramLayer::setChannel(int ch) 677 SpectrogramLayer::setChannel(int ch)
667 { 678 {
668 if (m_channel == ch) return; 679 if (m_channel == ch) return;
669 680
670 invalidateImageCaches(); 681 invalidateRenderers();
671 m_channel = ch; 682 m_channel = ch;
672 invalidateFFTModels(); 683 recreateFFTModel();
673 684
674 emit layerParametersChanged(); 685 emit layerParametersChanged();
675 } 686 }
676 687
677 int 688 int
678 SpectrogramLayer::getChannel() const 689 SpectrogramLayer::getChannel() const
679 { 690 {
680 return m_channel; 691 return m_channel;
681 } 692 }
682 693
694 int
695 SpectrogramLayer::getFFTOversampling() const
696 {
697 if (m_binDisplay != BinDisplay::AllBins) {
698 return 1;
699 }
700
701 Preferences::SpectrogramSmoothing smoothing =
702 Preferences::getInstance()->getSpectrogramSmoothing();
703
704 if (smoothing == Preferences::NoSpectrogramSmoothing ||
705 smoothing == Preferences::SpectrogramInterpolated) {
706 return 1;
707 }
708
709 return 4;
710 }
711
712 int
713 SpectrogramLayer::getFFTSize() const
714 {
715 return m_windowSize * getFFTOversampling();
716 }
717
683 void 718 void
684 SpectrogramLayer::setWindowSize(int ws) 719 SpectrogramLayer::setWindowSize(int ws)
685 { 720 {
686 if (m_windowSize == ws) return; 721 if (m_windowSize == ws) return;
687 722
688 invalidateImageCaches(); 723 invalidateRenderers();
689 724
690 m_windowSize = ws; 725 m_windowSize = ws;
691 m_fftSize = ws * (m_zeroPadLevel + 1); 726
692 727 recreateFFTModel();
693 invalidateFFTModels();
694 728
695 emit layerParametersChanged(); 729 emit layerParametersChanged();
696 } 730 }
697 731
698 int 732 int
704 void 738 void
705 SpectrogramLayer::setWindowHopLevel(int v) 739 SpectrogramLayer::setWindowHopLevel(int v)
706 { 740 {
707 if (m_windowHopLevel == v) return; 741 if (m_windowHopLevel == v) return;
708 742
709 invalidateImageCaches(); 743 invalidateRenderers();
710 744
711 m_windowHopLevel = v; 745 m_windowHopLevel = v;
712 746
713 invalidateFFTModels(); 747 recreateFFTModel();
714 748
715 emit layerParametersChanged(); 749 emit layerParametersChanged();
716
717 // fillCache();
718 } 750 }
719 751
720 int 752 int
721 SpectrogramLayer::getWindowHopLevel() const 753 SpectrogramLayer::getWindowHopLevel() const
722 { 754 {
723 return m_windowHopLevel; 755 return m_windowHopLevel;
724 } 756 }
725 757
726 void 758 void
727 SpectrogramLayer::setZeroPadLevel(int v)
728 {
729 if (m_zeroPadLevel == v) return;
730
731 invalidateImageCaches();
732
733 m_zeroPadLevel = v;
734 m_fftSize = m_windowSize * (v + 1);
735
736 invalidateFFTModels();
737
738 emit layerParametersChanged();
739 }
740
741 int
742 SpectrogramLayer::getZeroPadLevel() const
743 {
744 return m_zeroPadLevel;
745 }
746
747 void
748 SpectrogramLayer::setWindowType(WindowType w) 759 SpectrogramLayer::setWindowType(WindowType w)
749 { 760 {
750 if (m_windowType == w) return; 761 if (m_windowType == w) return;
751 762
752 invalidateImageCaches(); 763 invalidateRenderers();
753 764
754 m_windowType = w; 765 m_windowType = w;
755 766
756 invalidateFFTModels(); 767 recreateFFTModel();
757 768
758 emit layerParametersChanged(); 769 emit layerParametersChanged();
759 } 770 }
760 771
761 WindowType 772 WindowType
770 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " 781 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
771 // << m_gain << ")" << endl; 782 // << m_gain << ")" << endl;
772 783
773 if (m_gain == gain) return; 784 if (m_gain == gain) return;
774 785
775 invalidateImageCaches(); 786 invalidateRenderers();
776 787
777 m_gain = gain; 788 m_gain = gain;
778 789
779 emit layerParametersChanged(); 790 emit layerParametersChanged();
780 } 791 }
788 void 799 void
789 SpectrogramLayer::setThreshold(float threshold) 800 SpectrogramLayer::setThreshold(float threshold)
790 { 801 {
791 if (m_threshold == threshold) return; 802 if (m_threshold == threshold) return;
792 803
793 invalidateImageCaches(); 804 invalidateRenderers();
794 805
795 m_threshold = threshold; 806 m_threshold = threshold;
796 807
797 emit layerParametersChanged(); 808 emit layerParametersChanged();
798 } 809 }
808 { 819 {
809 if (m_minFrequency == mf) return; 820 if (m_minFrequency == mf) return;
810 821
811 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl; 822 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl;
812 823
813 invalidateImageCaches(); 824 invalidateRenderers();
814 invalidateMagnitudes(); 825 invalidateMagnitudes();
815 826
816 m_minFrequency = mf; 827 m_minFrequency = mf;
817 828
818 emit layerParametersChanged(); 829 emit layerParametersChanged();
829 { 840 {
830 if (m_maxFrequency == mf) return; 841 if (m_maxFrequency == mf) return;
831 842
832 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl; 843 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl;
833 844
834 invalidateImageCaches(); 845 invalidateRenderers();
835 invalidateMagnitudes(); 846 invalidateMagnitudes();
836 847
837 m_maxFrequency = mf; 848 m_maxFrequency = mf;
838 849
839 emit layerParametersChanged(); 850 emit layerParametersChanged();
846 } 857 }
847 858
848 void 859 void
849 SpectrogramLayer::setColourRotation(int r) 860 SpectrogramLayer::setColourRotation(int r)
850 { 861 {
851 invalidateImageCaches();
852
853 if (r < 0) r = 0; 862 if (r < 0) r = 0;
854 if (r > 256) r = 256; 863 if (r > 256) r = 256;
855 int distance = r - m_colourRotation; 864 int distance = r - m_colourRotation;
856 865
857 if (distance != 0) { 866 if (distance != 0) {
858 rotatePalette(-distance);
859 m_colourRotation = r; 867 m_colourRotation = r;
860 } 868 }
869
870 // Initially the idea with colour rotation was that we would just
871 // rotate the palette of an already-generated cache. That's not
872 // really practical now that cacheing is handled in a separate
873 // class in which the main cache no longer has a palette.
874 invalidateRenderers();
861 875
862 emit layerParametersChanged(); 876 emit layerParametersChanged();
863 } 877 }
864 878
865 void 879 void
866 SpectrogramLayer::setColourScale(ColourScale colourScale) 880 SpectrogramLayer::setColourScale(ColourScaleType colourScale)
867 { 881 {
868 if (m_colourScale == colourScale) return; 882 if (m_colourScale == colourScale) return;
869 883
870 invalidateImageCaches(); 884 invalidateRenderers();
871 885
872 m_colourScale = colourScale; 886 m_colourScale = colourScale;
873 887
874 emit layerParametersChanged(); 888 emit layerParametersChanged();
875 } 889 }
876 890
877 SpectrogramLayer::ColourScale 891 ColourScaleType
878 SpectrogramLayer::getColourScale() const 892 SpectrogramLayer::getColourScale() const
879 { 893 {
880 return m_colourScale; 894 return m_colourScale;
881 } 895 }
882 896
883 void 897 void
898 SpectrogramLayer::setColourScaleMultiple(double multiple)
899 {
900 if (m_colourScaleMultiple == multiple) return;
901
902 invalidateRenderers();
903
904 m_colourScaleMultiple = multiple;
905
906 emit layerParametersChanged();
907 }
908
909 double
910 SpectrogramLayer::getColourScaleMultiple() const
911 {
912 return m_colourScaleMultiple;
913 }
914
915 void
884 SpectrogramLayer::setColourMap(int map) 916 SpectrogramLayer::setColourMap(int map)
885 { 917 {
886 if (m_colourMap == map) return; 918 if (m_colourMap == map) return;
887 919
888 invalidateImageCaches(); 920 invalidateRenderers();
889 921
890 m_colourMap = map; 922 m_colourMap = map;
891 initialisePalette();
892 923
893 emit layerParametersChanged(); 924 emit layerParametersChanged();
894 } 925 }
895 926
896 int 927 int
898 { 929 {
899 return m_colourMap; 930 return m_colourMap;
900 } 931 }
901 932
902 void 933 void
903 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) 934 SpectrogramLayer::setBinScale(BinScale binScale)
904 { 935 {
905 if (m_frequencyScale == frequencyScale) return; 936 if (m_binScale == binScale) return;
906 937
907 invalidateImageCaches(); 938 invalidateRenderers();
908 m_frequencyScale = frequencyScale; 939 m_binScale = binScale;
909 940
910 emit layerParametersChanged(); 941 emit layerParametersChanged();
911 } 942 }
912 943
913 SpectrogramLayer::FrequencyScale 944 BinScale
914 SpectrogramLayer::getFrequencyScale() const 945 SpectrogramLayer::getBinScale() const
915 { 946 {
916 return m_frequencyScale; 947 return m_binScale;
917 } 948 }
918 949
919 void 950 void
920 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) 951 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
921 { 952 {
922 if (m_binDisplay == binDisplay) return; 953 if (m_binDisplay == binDisplay) return;
923 954
924 invalidateImageCaches(); 955 invalidateRenderers();
925 m_binDisplay = binDisplay; 956 m_binDisplay = binDisplay;
926 957
927 emit layerParametersChanged(); 958 emit layerParametersChanged();
928 } 959 }
929 960
930 SpectrogramLayer::BinDisplay 961 BinDisplay
931 SpectrogramLayer::getBinDisplay() const 962 SpectrogramLayer::getBinDisplay() const
932 { 963 {
933 return m_binDisplay; 964 return m_binDisplay;
934 } 965 }
935 966
936 void 967 void
937 SpectrogramLayer::setNormalizeColumns(bool n) 968 SpectrogramLayer::setNormalization(ColumnNormalization n)
938 { 969 {
939 if (m_normalizeColumns == n) return; 970 if (m_normalization == n) return;
940 971
941 invalidateImageCaches(); 972 invalidateRenderers();
942 invalidateMagnitudes(); 973 invalidateMagnitudes();
943 m_normalizeColumns = n; 974 m_normalization = n;
944 975
945 emit layerParametersChanged(); 976 emit layerParametersChanged();
946 } 977 }
947 978
948 bool 979 ColumnNormalization
949 SpectrogramLayer::getNormalizeColumns() const 980 SpectrogramLayer::getNormalization() const
950 { 981 {
951 return m_normalizeColumns; 982 return m_normalization;
952 }
953
954 void
955 SpectrogramLayer::setNormalizeHybrid(bool n)
956 {
957 if (m_normalizeHybrid == n) return;
958
959 invalidateImageCaches();
960 invalidateMagnitudes();
961 m_normalizeHybrid = n;
962
963 emit layerParametersChanged();
964 }
965
966 bool
967 SpectrogramLayer::getNormalizeHybrid() const
968 {
969 return m_normalizeHybrid;
970 } 983 }
971 984
972 void 985 void
973 SpectrogramLayer::setNormalizeVisibleArea(bool n) 986 SpectrogramLayer::setNormalizeVisibleArea(bool n)
974 { 987 {
975 SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n
976 << ") (from " << m_normalizeVisibleArea << ")" << endl;
977
978 if (m_normalizeVisibleArea == n) return; 988 if (m_normalizeVisibleArea == n) return;
979 989
980 invalidateImageCaches(); 990 invalidateRenderers();
981 invalidateMagnitudes(); 991 invalidateMagnitudes();
982 m_normalizeVisibleArea = n; 992 m_normalizeVisibleArea = n;
983 993
984 emit layerParametersChanged(); 994 emit layerParametersChanged();
985 } 995 }
986 996
987 bool 997 bool
988 SpectrogramLayer::getNormalizeVisibleArea() const 998 SpectrogramLayer::getNormalizeVisibleArea() const
989 { 999 {
990 return m_normalizeVisibleArea; 1000 return m_normalizeVisibleArea;
991 } 1001 }
992 1002
993 void 1003 void
994 SpectrogramLayer::setLayerDormant(const View *v, bool dormant) 1004 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
995 { 1005 {
996 if (dormant) { 1006 if (dormant) {
997 1007
998 #ifdef DEBUG_SPECTROGRAM_REPAINT 1008 #ifdef DEBUG_SPECTROGRAM_REPAINT
999 SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")" 1009 cerr << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
1000 << endl; 1010 << endl;
1001 #endif 1011 #endif
1002 1012
1003 if (isLayerDormant(v)) { 1013 if (isLayerDormant(v)) {
1004 return; 1014 return;
1005 } 1015 }
1006 1016
1007 Layer::setLayerDormant(v, true); 1017 Layer::setLayerDormant(v, true);
1008 1018
1009 invalidateImageCaches(); 1019 invalidateRenderers();
1010 m_imageCaches.erase(v);
1011
1012 if (m_fftModels.find(v) != m_fftModels.end()) {
1013
1014 if (m_sliceableModel == m_fftModels[v].first) {
1015 bool replaced = false;
1016 for (ViewFFTMap::iterator i = m_fftModels.begin();
1017 i != m_fftModels.end(); ++i) {
1018 if (i->second.first != m_sliceableModel) {
1019 emit sliceableModelReplaced(m_sliceableModel, i->second.first);
1020 replaced = true;
1021 break;
1022 }
1023 }
1024 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
1025 }
1026
1027 delete m_fftModels[v].first;
1028 m_fftModels.erase(v);
1029
1030 delete m_peakCaches[v];
1031 m_peakCaches.erase(v);
1032 }
1033 1020
1034 } else { 1021 } else {
1035 1022
1036 Layer::setLayerDormant(v, false); 1023 Layer::setLayerDormant(v, false);
1037 } 1024 }
1039 1026
1040 void 1027 void
1041 SpectrogramLayer::cacheInvalid() 1028 SpectrogramLayer::cacheInvalid()
1042 { 1029 {
1043 #ifdef DEBUG_SPECTROGRAM_REPAINT 1030 #ifdef DEBUG_SPECTROGRAM_REPAINT
1044 SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl; 1031 cerr << "SpectrogramLayer::cacheInvalid()" << endl;
1045 #endif 1032 #endif
1046 1033
1047 invalidateImageCaches(); 1034 invalidateRenderers();
1048 invalidateMagnitudes(); 1035 invalidateMagnitudes();
1049 } 1036 }
1050 1037
1051 void 1038 void
1052 SpectrogramLayer::cacheInvalid(sv_frame_t from, sv_frame_t to) 1039 SpectrogramLayer::cacheInvalid(
1053 {
1054 #ifdef DEBUG_SPECTROGRAM_REPAINT 1040 #ifdef DEBUG_SPECTROGRAM_REPAINT
1055 SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl; 1041 sv_frame_t from, sv_frame_t to
1042 #else
1043 sv_frame_t , sv_frame_t
1056 #endif 1044 #endif
1057 1045 )
1058 invalidateImageCaches(from, to); 1046 {
1047 #ifdef DEBUG_SPECTROGRAM_REPAINT
1048 cerr << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
1049 #endif
1050
1051 // We used to call invalidateMagnitudes(from, to) to invalidate
1052 // only those caches whose views contained some of the (from, to)
1053 // range. That's the right thing to do; it has been lost in
1054 // pulling out the image cache code, but it might not matter very
1055 // much, since the underlying models for spectrogram layers don't
1056 // change very often. Let's see.
1057 invalidateRenderers();
1059 invalidateMagnitudes(); 1058 invalidateMagnitudes();
1060 }
1061
1062 void
1063 SpectrogramLayer::fillTimerTimedOut()
1064 {
1065 if (!m_model) return;
1066
1067 bool allDone = true;
1068
1069 #ifdef DEBUG_SPECTROGRAM_REPAINT
1070 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl;
1071 #endif
1072
1073 for (ViewFFTMap::iterator i = m_fftModels.begin();
1074 i != m_fftModels.end(); ++i) {
1075
1076 const FFTModel *model = i->second.first;
1077 sv_frame_t lastFill = i->second.second;
1078
1079 if (model) {
1080
1081 sv_frame_t fill = model->getFillExtent();
1082
1083 #ifdef DEBUG_SPECTROGRAM_REPAINT
1084 SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl;
1085 #endif
1086
1087 if (fill >= lastFill) {
1088 if (fill >= m_model->getEndFrame() && lastFill > 0) {
1089 #ifdef DEBUG_SPECTROGRAM_REPAINT
1090 cerr << "complete!" << endl;
1091 #endif
1092 invalidateImageCaches();
1093 i->second.second = -1;
1094 emit modelChanged();
1095
1096 } else if (fill > lastFill) {
1097 #ifdef DEBUG_SPECTROGRAM_REPAINT
1098 cerr << "SpectrogramLayer: emitting modelChanged("
1099 << lastFill << "," << fill << ")" << endl;
1100 #endif
1101 invalidateImageCaches(lastFill, fill);
1102 i->second.second = fill;
1103 emit modelChangedWithin(lastFill, fill);
1104 }
1105 } else {
1106 #ifdef DEBUG_SPECTROGRAM_REPAINT
1107 cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
1108 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl;
1109 #endif
1110 invalidateImageCaches();
1111 i->second.second = fill;
1112 emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame());
1113 }
1114
1115 if (i->second.second >= 0) {
1116 allDone = false;
1117 }
1118 }
1119 }
1120
1121 if (allDone) {
1122 #ifdef DEBUG_SPECTROGRAM_REPAINT
1123 cerr << "SpectrogramLayer: all complete!" << endl;
1124 #endif
1125 delete m_updateTimer;
1126 m_updateTimer = 0;
1127 }
1128 } 1059 }
1129 1060
1130 bool 1061 bool
1131 SpectrogramLayer::hasLightBackground() const 1062 SpectrogramLayer::hasLightBackground() const
1132 { 1063 {
1133 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground(); 1064 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
1134 } 1065 }
1135 1066
1136 void
1137 SpectrogramLayer::initialisePalette()
1138 {
1139 int formerRotation = m_colourRotation;
1140
1141 if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
1142 m_palette.setColour(NO_VALUE, Qt::white);
1143 } else {
1144 m_palette.setColour(NO_VALUE, Qt::black);
1145 }
1146
1147 ColourMapper mapper(m_colourMap, 1.f, 255.f);
1148
1149 for (int pixel = 1; pixel < 256; ++pixel) {
1150 m_palette.setColour((unsigned char)pixel, mapper.map(pixel));
1151 }
1152
1153 m_crosshairColour = mapper.getContrastingColour();
1154
1155 m_colourRotation = 0;
1156 rotatePalette(m_colourRotation - formerRotation);
1157 m_colourRotation = formerRotation;
1158
1159 m_drawBuffer = QImage();
1160 }
1161
1162 void
1163 SpectrogramLayer::rotatePalette(int distance)
1164 {
1165 QColor newPixels[256];
1166
1167 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
1168
1169 for (int pixel = 1; pixel < 256; ++pixel) {
1170 int target = pixel + distance;
1171 while (target < 1) target += 255;
1172 while (target > 255) target -= 255;
1173 newPixels[target] = m_palette.getColour((unsigned char)pixel);
1174 }
1175
1176 for (int pixel = 0; pixel < 256; ++pixel) {
1177 m_palette.setColour((unsigned char)pixel, newPixels[pixel]);
1178 }
1179
1180 m_drawBuffer = QImage();
1181 }
1182
1183 unsigned char
1184 SpectrogramLayer::getDisplayValue(View *v, double input) const
1185 {
1186 int value;
1187
1188 double min = 0.0;
1189 double max = 1.0;
1190
1191 if (m_normalizeVisibleArea) {
1192 min = m_viewMags[v].getMin();
1193 max = m_viewMags[v].getMax();
1194 } else if (!m_normalizeColumns) {
1195 if (m_colourScale == LinearColourScale //||
1196 // m_colourScale == MeterColourScale) {
1197 ) {
1198 max = 0.1;
1199 }
1200 }
1201
1202 double thresh = -80.0;
1203
1204 if (max == 0.0) max = 1.0;
1205 if (max == min) min = max - 0.0001;
1206
1207 switch (m_colourScale) {
1208
1209 default:
1210 case LinearColourScale:
1211 value = int(((input - min) / (max - min)) * 255.0) + 1;
1212 break;
1213
1214 case MeterColourScale:
1215 value = AudioLevel::multiplier_to_preview
1216 ((input - min) / (max - min), 254) + 1;
1217 break;
1218
1219 case dBSquaredColourScale:
1220 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1221 if (input > 0.0) {
1222 input = 10.0 * log10(input);
1223 } else {
1224 input = thresh;
1225 }
1226 if (min > 0.0) {
1227 thresh = 10.0 * log10(min * min);
1228 if (thresh < -80.0) thresh = -80.0;
1229 }
1230 input = (input - thresh) / (-thresh);
1231 if (input < 0.0) input = 0.0;
1232 if (input > 1.0) input = 1.0;
1233 value = int(input * 255.0) + 1;
1234 break;
1235
1236 case dBColourScale:
1237 //!!! experiment with normalizing the visible area this way.
1238 //In any case, we need to have some indication of what the dB
1239 //scale is relative to.
1240 input = (input - min) / (max - min);
1241 if (input > 0.0) {
1242 input = 10.0 * log10(input);
1243 } else {
1244 input = thresh;
1245 }
1246 if (min > 0.0) {
1247 thresh = 10.0 * log10(min);
1248 if (thresh < -80.0) thresh = -80.0;
1249 }
1250 input = (input - thresh) / (-thresh);
1251 if (input < 0.0) input = 0.0;
1252 if (input > 1.0) input = 1.0;
1253 value = int(input * 255.0) + 1;
1254 break;
1255
1256 case PhaseColourScale:
1257 value = int((input * 127.0 / M_PI) + 128);
1258 break;
1259 }
1260
1261 if (value > UCHAR_MAX) value = UCHAR_MAX;
1262 if (value < 0) value = 0;
1263 return (unsigned char)value;
1264 }
1265
1266 double 1067 double
1267 SpectrogramLayer::getEffectiveMinFrequency() const 1068 SpectrogramLayer::getEffectiveMinFrequency() const
1268 { 1069 {
1269 sv_samplerate_t sr = m_model->getSampleRate(); 1070 sv_samplerate_t sr = m_model->getSampleRate();
1270 double minf = double(sr) / m_fftSize; 1071 double minf = double(sr) / getFFTSize();
1271 1072
1272 if (m_minFrequency > 0.0) { 1073 if (m_minFrequency > 0.0) {
1273 int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01); 1074 int minbin = int((double(m_minFrequency) * getFFTSize()) / sr + 0.01);
1274 if (minbin < 1) minbin = 1; 1075 if (minbin < 1) minbin = 1;
1275 minf = minbin * sr / m_fftSize; 1076 minf = minbin * sr / getFFTSize();
1276 } 1077 }
1277 1078
1278 return minf; 1079 return minf;
1279 } 1080 }
1280 1081
1283 { 1084 {
1284 sv_samplerate_t sr = m_model->getSampleRate(); 1085 sv_samplerate_t sr = m_model->getSampleRate();
1285 double maxf = double(sr) / 2; 1086 double maxf = double(sr) / 2;
1286 1087
1287 if (m_maxFrequency > 0.0) { 1088 if (m_maxFrequency > 0.0) {
1288 int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 1089 int maxbin = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
1289 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 1090 if (maxbin > getFFTSize() / 2) maxbin = getFFTSize() / 2;
1290 maxf = maxbin * sr / m_fftSize; 1091 maxf = maxbin * sr / getFFTSize();
1291 } 1092 }
1292 1093
1293 return maxf; 1094 return maxf;
1294 } 1095 }
1295 1096
1296 bool 1097 bool
1297 SpectrogramLayer::getYBinRange(View *v, int y, double &q0, double &q1) const 1098 SpectrogramLayer::getYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
1298 { 1099 {
1299 Profiler profiler("SpectrogramLayer::getYBinRange"); 1100 Profiler profiler("SpectrogramLayer::getYBinRange");
1300 1101 int h = v->getPaintHeight();
1301 int h = v->height();
1302 if (y < 0 || y >= h) return false; 1102 if (y < 0 || y >= h) return false;
1303 1103 q0 = getBinForY(v, y);
1104 q1 = getBinForY(v, y-1);
1105 return true;
1106 }
1107
1108 double
1109 SpectrogramLayer::getYForBin(const LayerGeometryProvider *v, double bin) const
1110 {
1111 double minf = getEffectiveMinFrequency();
1112 double maxf = getEffectiveMaxFrequency();
1113 bool logarithmic = (m_binScale == BinScale::Log);
1114 sv_samplerate_t sr = m_model->getSampleRate();
1115
1116 double freq = (bin * sr) / getFFTSize();
1117
1118 double y = v->getYForFrequency(freq, minf, maxf, logarithmic);
1119
1120 return y;
1121 }
1122
1123 double
1124 SpectrogramLayer::getBinForY(const LayerGeometryProvider *v, double y) const
1125 {
1304 sv_samplerate_t sr = m_model->getSampleRate(); 1126 sv_samplerate_t sr = m_model->getSampleRate();
1305 double minf = getEffectiveMinFrequency(); 1127 double minf = getEffectiveMinFrequency();
1306 double maxf = getEffectiveMaxFrequency(); 1128 double maxf = getEffectiveMaxFrequency();
1307 1129
1308 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1130 bool logarithmic = (m_binScale == BinScale::Log);
1309 1131
1310 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); 1132 double freq = v->getFrequencyForY(y, minf, maxf, logarithmic);
1311 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); 1133
1312 1134 // Now map on to ("proportion of") actual bins
1313 // Now map these on to ("proportions of") actual bins, using raw 1135 double bin = (freq * getFFTSize()) / sr;
1314 // FFT size (unsmoothed) 1136
1315 1137 return bin;
1316 q0 = (q0 * m_fftSize) / sr;
1317 q1 = (q1 * m_fftSize) / sr;
1318
1319 return true;
1320 } 1138 }
1321 1139
1322 bool 1140 bool
1323 SpectrogramLayer::getSmoothedYBinRange(View *v, int y, double &q0, double &q1) const 1141 SpectrogramLayer::getXBinRange(LayerGeometryProvider *v, int x, double &s0, double &s1) const
1324 {
1325 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
1326
1327 int h = v->height();
1328 if (y < 0 || y >= h) return false;
1329
1330 sv_samplerate_t sr = m_model->getSampleRate();
1331 double minf = getEffectiveMinFrequency();
1332 double maxf = getEffectiveMaxFrequency();
1333
1334 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1335
1336 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1337 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1338
1339 // Now map these on to ("proportions of") actual bins, using raw
1340 // FFT size (unsmoothed)
1341
1342 q0 = (q0 * getFFTSize(v)) / sr;
1343 q1 = (q1 * getFFTSize(v)) / sr;
1344
1345 return true;
1346 }
1347
1348 bool
1349 SpectrogramLayer::getXBinRange(View *v, int x, double &s0, double &s1) const
1350 { 1142 {
1351 sv_frame_t modelStart = m_model->getStartFrame(); 1143 sv_frame_t modelStart = m_model->getStartFrame();
1352 sv_frame_t modelEnd = m_model->getEndFrame(); 1144 sv_frame_t modelEnd = m_model->getEndFrame();
1353 1145
1354 // Each pixel column covers an exact range of sample frames: 1146 // Each pixel column covers an exact range of sample frames:
1368 1160
1369 return true; 1161 return true;
1370 } 1162 }
1371 1163
1372 bool 1164 bool
1373 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const 1165 SpectrogramLayer::getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &min, RealTime &max) const
1374 { 1166 {
1375 double s0 = 0, s1 = 0; 1167 double s0 = 0, s1 = 0;
1376 if (!getXBinRange(v, x, s0, s1)) return false; 1168 if (!getXBinRange(v, x, s0, s1)) return false;
1377 1169
1378 int s0i = int(s0 + 0.001); 1170 int s0i = int(s0 + 0.001);
1387 max = RealTime::frame2RealTime(w1, m_model->getSampleRate()); 1179 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
1388 return true; 1180 return true;
1389 } 1181 }
1390 1182
1391 bool 1183 bool
1392 SpectrogramLayer::getYBinSourceRange(View *v, int y, double &freqMin, double &freqMax) 1184 SpectrogramLayer::getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax)
1393 const 1185 const
1394 { 1186 {
1395 double q0 = 0, q1 = 0; 1187 double q0 = 0, q1 = 0;
1396 if (!getYBinRange(v, y, q0, q1)) return false; 1188 if (!getYBinRange(v, y, q0, q1)) return false;
1397 1189
1399 int q1i = int(q1); 1191 int q1i = int(q1);
1400 1192
1401 sv_samplerate_t sr = m_model->getSampleRate(); 1193 sv_samplerate_t sr = m_model->getSampleRate();
1402 1194
1403 for (int q = q0i; q <= q1i; ++q) { 1195 for (int q = q0i; q <= q1i; ++q) {
1404 if (q == q0i) freqMin = (sr * q) / m_fftSize; 1196 if (q == q0i) freqMin = (sr * q) / getFFTSize();
1405 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize; 1197 if (q == q1i) freqMax = (sr * (q+1)) / getFFTSize();
1406 } 1198 }
1407 return true; 1199 return true;
1408 } 1200 }
1409 1201
1410 bool 1202 bool
1411 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y, 1203 SpectrogramLayer::getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y,
1412 double &freqMin, double &freqMax, 1204 double &freqMin, double &freqMax,
1413 double &adjFreqMin, double &adjFreqMax) 1205 double &adjFreqMin, double &adjFreqMax)
1414 const 1206 const
1415 { 1207 {
1416 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1208 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1417 return false; 1209 return false;
1418 } 1210 }
1419 1211
1420 FFTModel *fft = getFFTModel(v); 1212 FFTModel *fft = getFFTModel();
1421 if (!fft) return false; 1213 if (!fft) return false;
1422 1214
1423 double s0 = 0, s1 = 0; 1215 double s0 = 0, s1 = 0;
1424 if (!getXBinRange(v, x, s0, s1)) return false; 1216 if (!getXBinRange(v, x, s0, s1)) return false;
1425 1217
1434 1226
1435 sv_samplerate_t sr = m_model->getSampleRate(); 1227 sv_samplerate_t sr = m_model->getSampleRate();
1436 1228
1437 bool haveAdj = false; 1229 bool haveAdj = false;
1438 1230
1439 bool peaksOnly = (m_binDisplay == PeakBins || 1231 bool peaksOnly = (m_binDisplay == BinDisplay::PeakBins ||
1440 m_binDisplay == PeakFrequencies); 1232 m_binDisplay == BinDisplay::PeakFrequencies);
1441 1233
1442 for (int q = q0i; q <= q1i; ++q) { 1234 for (int q = q0i; q <= q1i; ++q) {
1443 1235
1444 for (int s = s0i; s <= s1i; ++s) { 1236 for (int s = s0i; s <= s1i; ++s) {
1445
1446 if (!fft->isColumnAvailable(s)) continue;
1447 1237
1448 double binfreq = (double(sr) * q) / m_windowSize; 1238 double binfreq = (double(sr) * q) / m_windowSize;
1449 if (q == q0i) freqMin = binfreq; 1239 if (q == q0i) freqMin = binfreq;
1450 if (q == q1i) freqMax = binfreq; 1240 if (q == q1i) freqMax = binfreq;
1451 1241
1452 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; 1242 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1453 1243
1454 if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue; 1244 if (!fft->isOverThreshold
1245 (s, q, float(m_threshold * double(getFFTSize())/2.0))) {
1246 continue;
1247 }
1455 1248
1456 double freq = binfreq; 1249 double freq = binfreq;
1457 1250
1458 if (s < int(fft->getWidth()) - 1) { 1251 if (s < int(fft->getWidth()) - 1) {
1459 1252
1473 1266
1474 return haveAdj; 1267 return haveAdj;
1475 } 1268 }
1476 1269
1477 bool 1270 bool
1478 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y, 1271 SpectrogramLayer::getXYBinSourceRange(LayerGeometryProvider *v, int x, int y,
1479 double &min, double &max, 1272 double &min, double &max,
1480 double &phaseMin, double &phaseMax) const 1273 double &phaseMin, double &phaseMax) const
1481 { 1274 {
1482 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1275 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1483 return false; 1276 return false;
1495 int s0i = int(s0 + 0.001); 1288 int s0i = int(s0 + 0.001);
1496 int s1i = int(s1); 1289 int s1i = int(s1);
1497 1290
1498 bool rv = false; 1291 bool rv = false;
1499 1292
1500 int zp = getZeroPadLevel(v); 1293 FFTModel *fft = getFFTModel();
1501 q0i *= zp + 1;
1502 q1i *= zp + 1;
1503
1504 FFTModel *fft = getFFTModel(v);
1505 1294
1506 if (fft) { 1295 if (fft) {
1507 1296
1508 int cw = fft->getWidth(); 1297 int cw = fft->getWidth();
1509 int ch = fft->getHeight(); 1298 int ch = fft->getHeight();
1516 1305
1517 for (int q = q0i; q <= q1i; ++q) { 1306 for (int q = q0i; q <= q1i; ++q) {
1518 for (int s = s0i; s <= s1i; ++s) { 1307 for (int s = s0i; s <= s1i; ++s) {
1519 if (s >= 0 && q >= 0 && s < cw && q < ch) { 1308 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1520 1309
1521 if (!fft->isColumnAvailable(s)) continue;
1522
1523 double value; 1310 double value;
1524 1311
1525 value = fft->getPhaseAt(s, q); 1312 value = fft->getPhaseAt(s, q);
1526 if (!have || value < phaseMin) { phaseMin = value; } 1313 if (!have || value < phaseMin) { phaseMin = value; }
1527 if (!have || value > phaseMax) { phaseMax = value; } 1314 if (!have || value > phaseMax) { phaseMax = value; }
1528 1315
1529 value = fft->getMagnitudeAt(s, q) / (m_fftSize/2.0); 1316 value = fft->getMagnitudeAt(s, q) / (getFFTSize()/2.0);
1530 if (!have || value < min) { min = value; } 1317 if (!have || value < min) { min = value; }
1531 if (!have || value > max) { max = value; } 1318 if (!have || value > max) { max = value; }
1532 1319
1533 have = true; 1320 have = true;
1534 } 1321 }
1540 } 1327 }
1541 } 1328 }
1542 1329
1543 return rv; 1330 return rv;
1544 } 1331 }
1545 1332
1546 int 1333 void
1547 SpectrogramLayer::getZeroPadLevel(const View *v) const 1334 SpectrogramLayer::recreateFFTModel()
1548 { 1335 {
1549 //!!! tidy all this stuff 1336 #ifdef DEBUG_SPECTROGRAM
1550 1337 cerr << "SpectrogramLayer::recreateFFTModel called" << endl;
1551 if (m_binDisplay != AllBins) return 0; 1338 #endif
1552 1339
1553 Preferences::SpectrogramSmoothing smoothing = 1340 if (!m_model || !m_model->isOK()) {
1554 Preferences::getInstance()->getSpectrogramSmoothing(); 1341 emit sliceableModelReplaced(m_fftModel, 0);
1555 1342 delete m_fftModel;
1556 if (smoothing == Preferences::NoSpectrogramSmoothing || 1343 delete m_peakCache;
1557 smoothing == Preferences::SpectrogramInterpolated) return 0; 1344 delete m_wholeCache;
1558 1345 m_fftModel = 0;
1559 if (m_frequencyScale == LogFrequencyScale) return 3; 1346 m_peakCache = 0;
1560 1347 m_wholeCache = 0;
1561 sv_samplerate_t sr = m_model->getSampleRate(); 1348 return;
1562 1349 }
1563 int maxbin = m_fftSize / 2; 1350
1564 if (m_maxFrequency > 0) { 1351 FFTModel *oldModel = m_fftModel;
1565 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 1352
1566 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 1353 m_fftModel = new FFTModel(m_model,
1567 } 1354 m_channel,
1568 1355 m_windowType,
1569 int minbin = 1; 1356 m_windowSize,
1570 if (m_minFrequency > 0) { 1357 getWindowIncrement(),
1571 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1); 1358 getFFTSize());
1572 if (minbin < 1) minbin = 1; 1359
1573 if (minbin >= maxbin) minbin = maxbin - 1; 1360 delete m_peakCache;
1574 } 1361 m_peakCache = 0;
1575 1362
1576 double perPixel = 1363 delete m_wholeCache;
1577 double(v->height()) / 1364 m_wholeCache = 0;
1578 double((maxbin - minbin) / (m_zeroPadLevel + 1)); 1365
1579 1366 if (!m_fftModel->isOK()) {
1580 if (perPixel > 2.8) { 1367 QMessageBox::critical
1581 return 3; // 4x oversampling 1368 (0, tr("FFT cache failed"),
1582 } else if (perPixel > 1.5) { 1369 tr("Failed to create the FFT model for this spectrogram.\n"
1583 return 1; // 2x 1370 "There may be insufficient memory or disc space to continue."));
1371 delete m_fftModel;
1372 m_fftModel = 0;
1373 return;
1374 }
1375
1376 if (canStoreWholeCache()) { // i.e. if enough memory
1377 m_wholeCache = new Dense3DModelPeakCache(m_fftModel, 1);
1378 m_peakCache = new Dense3DModelPeakCache(m_wholeCache, m_peakCacheDivisor);
1584 } else { 1379 } else {
1585 return 0; // 1x 1380 m_peakCache = new Dense3DModelPeakCache(m_fftModel, m_peakCacheDivisor);
1586 } 1381 }
1587 } 1382
1588 1383 emit sliceableModelReplaced(oldModel, m_fftModel);
1589 int 1384
1590 SpectrogramLayer::getFFTSize(const View *v) const 1385 delete oldModel;
1591 { 1386 }
1592 return m_fftSize * (getZeroPadLevel(v) + 1); 1387
1593 } 1388 bool
1594 1389 SpectrogramLayer::canStoreWholeCache() const
1595 FFTModel * 1390 {
1596 SpectrogramLayer::getFFTModel(const View *v) const 1391 if (!m_fftModel) {
1597 { 1392 return false; // or true, doesn't really matter
1598 if (!m_model) return 0; 1393 }
1599 1394
1600 int fftSize = getFFTSize(v); 1395 size_t sz =
1601 1396 size_t(m_fftModel->getWidth()) *
1602 if (m_fftModels.find(v) != m_fftModels.end()) { 1397 size_t(m_fftModel->getHeight()) *
1603 if (m_fftModels[v].first == 0) { 1398 sizeof(float);
1604 #ifdef DEBUG_SPECTROGRAM_REPAINT 1399
1605 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl; 1400 try {
1606 #endif 1401 SVDEBUG << "Requesting advice from StorageAdviser on whether to create whole-model cache" << endl;
1607 return 0; 1402 StorageAdviser::Recommendation recommendation =
1403 StorageAdviser::recommend
1404 (StorageAdviser::Criteria(StorageAdviser::SpeedCritical |
1405 StorageAdviser::PrecisionCritical |
1406 StorageAdviser::FrequentLookupLikely),
1407 sz / 1024, sz / 1024);
1408 if ((recommendation & StorageAdviser::UseDisc) ||
1409 (recommendation & StorageAdviser::ConserveSpace)) {
1410 SVDEBUG << "Seems inadvisable to create whole-model cache" << endl;
1411 return false;
1412 } else {
1413 SVDEBUG << "Seems fine to create whole-model cache" << endl;
1414 return true;
1608 } 1415 }
1609 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) { 1416 } catch (const InsufficientDiscSpace &) {
1610 #ifdef DEBUG_SPECTROGRAM_REPAINT 1417 SVDEBUG << "Seems like a terrible idea to create whole-model cache" << endl;
1611 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl; 1418 return false;
1612 #endif 1419 }
1613 delete m_fftModels[v].first;
1614 m_fftModels.erase(v);
1615 delete m_peakCaches[v];
1616 m_peakCaches.erase(v);
1617 } else {
1618 #ifdef DEBUG_SPECTROGRAM_REPAINT
1619 SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl;
1620 #endif
1621 return m_fftModels[v].first;
1622 }
1623 }
1624
1625 if (m_fftModels.find(v) == m_fftModels.end()) {
1626
1627 FFTModel *model = new FFTModel(m_model,
1628 m_channel,
1629 m_windowType,
1630 m_windowSize,
1631 getWindowIncrement(),
1632 fftSize,
1633 true, // polar
1634 StorageAdviser::SpeedCritical,
1635 m_candidateFillStartFrame);
1636
1637 if (!model->isOK()) {
1638 QMessageBox::critical
1639 (0, tr("FFT cache failed"),
1640 tr("Failed to create the FFT model for this spectrogram.\n"
1641 "There may be insufficient memory or disc space to continue."));
1642 delete model;
1643 m_fftModels[v] = FFTFillPair(0, 0);
1644 return 0;
1645 }
1646
1647 if (!m_sliceableModel) {
1648 #ifdef DEBUG_SPECTROGRAM
1649 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl;
1650 #endif
1651 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1652 m_sliceableModel = model;
1653 }
1654
1655 m_fftModels[v] = FFTFillPair(model, 0);
1656
1657 model->resume();
1658
1659 delete m_updateTimer;
1660 m_updateTimer = new QTimer((SpectrogramLayer *)this);
1661 connect(m_updateTimer, SIGNAL(timeout()),
1662 this, SLOT(fillTimerTimedOut()));
1663 m_updateTimer->start(200);
1664 }
1665
1666 return m_fftModels[v].first;
1667 }
1668
1669 Dense3DModelPeakCache *
1670 SpectrogramLayer::getPeakCache(const View *v) const
1671 {
1672 if (!m_peakCaches[v]) {
1673 FFTModel *f = getFFTModel(v);
1674 if (!f) return 0;
1675 m_peakCaches[v] = new Dense3DModelPeakCache(f, 8);
1676 }
1677 return m_peakCaches[v];
1678 } 1420 }
1679 1421
1680 const Model * 1422 const Model *
1681 SpectrogramLayer::getSliceableModel() const 1423 SpectrogramLayer::getSliceableModel() const
1682 { 1424 {
1683 if (m_sliceableModel) return m_sliceableModel; 1425 return m_fftModel;
1684 if (m_fftModels.empty()) return 0;
1685 m_sliceableModel = m_fftModels.begin()->second.first;
1686 return m_sliceableModel;
1687 }
1688
1689 void
1690 SpectrogramLayer::invalidateFFTModels()
1691 {
1692 for (ViewFFTMap::iterator i = m_fftModels.begin();
1693 i != m_fftModels.end(); ++i) {
1694 delete i->second.first;
1695 }
1696 for (PeakCacheMap::iterator i = m_peakCaches.begin();
1697 i != m_peakCaches.end(); ++i) {
1698 delete i->second;
1699 }
1700
1701 m_fftModels.clear();
1702 m_peakCaches.clear();
1703
1704 if (m_sliceableModel) {
1705 cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl;
1706 emit sliceableModelReplaced(m_sliceableModel, 0);
1707 m_sliceableModel = 0;
1708 }
1709 } 1426 }
1710 1427
1711 void 1428 void
1712 SpectrogramLayer::invalidateMagnitudes() 1429 SpectrogramLayer::invalidateMagnitudes()
1713 { 1430 {
1431 #ifdef DEBUG_SPECTROGRAM
1432 cerr << "SpectrogramLayer::invalidateMagnitudes called" << endl;
1433 #endif
1714 m_viewMags.clear(); 1434 m_viewMags.clear();
1715 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin(); 1435 }
1716 i != m_columnMags.end(); ++i) { 1436
1717 *i = MagnitudeRange(); 1437 void
1718 } 1438 SpectrogramLayer::setSynchronousPainting(bool synchronous)
1719 } 1439 {
1720 1440 m_synchronous = synchronous;
1721 bool 1441 }
1722 SpectrogramLayer::updateViewMagnitudes(View *v) const 1442
1723 { 1443 Colour3DPlotRenderer *
1724 MagnitudeRange mag; 1444 SpectrogramLayer::getRenderer(LayerGeometryProvider *v) const
1725 1445 {
1726 int x0 = 0, x1 = v->width(); 1446 int viewId = v->getId();
1727 double s00 = 0, s01 = 0, s10 = 0, s11 = 0; 1447
1728 1448 if (m_renderers.find(viewId) == m_renderers.end()) {
1729 if (!getXBinRange(v, x0, s00, s01)) { 1449
1730 s00 = s01 = double(m_model->getStartFrame()) / getWindowIncrement(); 1450 Colour3DPlotRenderer::Sources sources;
1731 } 1451 sources.verticalBinLayer = this;
1732 1452 sources.fft = getFFTModel();
1733 if (!getXBinRange(v, x1, s10, s11)) { 1453 sources.source = sources.fft;
1734 s10 = s11 = double(m_model->getEndFrame()) / getWindowIncrement(); 1454 if (m_peakCache) sources.peakCaches.push_back(m_peakCache);
1735 } 1455 if (m_wholeCache) sources.peakCaches.push_back(m_wholeCache);
1736 1456
1737 int s0 = int(std::min(s00, s10) + 0.0001); 1457 ColourScale::Parameters cparams;
1738 int s1 = int(std::max(s01, s11) + 0.0001); 1458 cparams.colourMap = m_colourMap;
1739 1459 cparams.scaleType = m_colourScale;
1740 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl; 1460 cparams.multiple = m_colourScaleMultiple;
1741 1461
1742 if (int(m_columnMags.size()) <= s1) { 1462 if (m_colourScale != ColourScaleType::Phase) {
1743 m_columnMags.resize(s1 + 1); 1463 cparams.gain = m_gain;
1744 } 1464 cparams.threshold = m_threshold;
1745
1746 for (int s = s0; s <= s1; ++s) {
1747 if (m_columnMags[s].isSet()) {
1748 mag.sample(m_columnMags[s]);
1749 } 1465 }
1750 } 1466
1467 float minValue = 0.0f;
1468 float maxValue = 1.0f;
1469
1470 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) {
1471 minValue = m_viewMags[viewId].getMin();
1472 maxValue = m_viewMags[viewId].getMax();
1473 } else if (m_colourScale == ColourScaleType::Linear &&
1474 m_normalization == ColumnNormalization::None) {
1475 maxValue = 0.1f;
1476 }
1477
1478 if (maxValue <= minValue) {
1479 maxValue = minValue + 0.1f;
1480 }
1481 if (maxValue <= m_threshold) {
1482 maxValue = m_threshold + 0.1f;
1483 }
1484
1485 cparams.minValue = minValue;
1486 cparams.maxValue = maxValue;
1487
1488 m_lastRenderedMags[viewId] = MagnitudeRange(minValue, maxValue);
1489
1490 Colour3DPlotRenderer::Parameters params;
1491 params.colourScale = ColourScale(cparams);
1492 params.normalization = m_normalization;
1493 params.binDisplay = m_binDisplay;
1494 params.binScale = m_binScale;
1495 params.alwaysOpaque = true;
1496 params.invertVertical = false;
1497 params.scaleFactor = 1.0;
1498 params.colourRotation = m_colourRotation;
1499
1500 if (m_colourScale != ColourScaleType::Phase &&
1501 m_normalization != ColumnNormalization::Hybrid) {
1502 params.scaleFactor *= 2.f / float(getFFTSize());
1503 }
1504
1505 Preferences::SpectrogramSmoothing smoothing =
1506 Preferences::getInstance()->getSpectrogramSmoothing();
1507 params.interpolate =
1508 (smoothing == Preferences::SpectrogramInterpolated ||
1509 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated);
1510
1511 m_renderers[v->getId()] = new Colour3DPlotRenderer(sources, params);
1512 }
1513
1514 return m_renderers[v->getId()];
1515 }
1516
1517 void
1518 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1519 {
1520 Colour3DPlotRenderer *renderer = getRenderer(v);
1521
1522 Colour3DPlotRenderer::RenderResult result;
1523 MagnitudeRange magRange;
1524 int viewId = v->getId();
1525
1526 bool continuingPaint = !renderer->geometryChanged(v);
1527
1528 if (continuingPaint) {
1529 magRange = m_viewMags[viewId];
1530 }
1531
1532 if (m_synchronous) {
1533
1534 result = renderer->render(v, paint, rect);
1535
1536 } else {
1537
1538 result = renderer->renderTimeConstrained(v, paint, rect);
1751 1539
1752 #ifdef DEBUG_SPECTROGRAM_REPAINT 1540 #ifdef DEBUG_SPECTROGRAM_REPAINT
1753 SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols " 1541 cerr << "rect width from this paint: " << result.rendered.width()
1754 << s0 << " -> " << s1 << " inclusive" << endl; 1542 << ", mag range in this paint: " << result.range.getMin() << " -> "
1543 << result.range.getMax() << endl;
1755 #endif 1544 #endif
1756 1545
1757 if (!mag.isSet()) return false; 1546 QRect uncached = renderer->getLargestUncachedRect(v);
1758 if (mag == m_viewMags[v]) return false; 1547 if (uncached.width() > 0) {
1759 m_viewMags[v] = mag; 1548 v->updatePaintRect(uncached);
1760 return true; 1549 }
1761 } 1550 }
1762 1551
1763 void 1552 magRange.sample(result.range);
1764 SpectrogramLayer::setSynchronousPainting(bool synchronous) 1553
1765 { 1554 if (magRange.isSet()) {
1766 m_synchronous = synchronous; 1555 if (m_viewMags[viewId] != magRange) {
1767 } 1556 m_viewMags[viewId] = magRange;
1768 1557 #ifdef DEBUG_SPECTROGRAM_REPAINT
1769 void 1558 cerr << "mag range in this view has changed: "
1770 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const 1559 << magRange.getMin() << " -> " << magRange.getMax() << endl;
1771 { 1560 #endif
1772 // What a lovely, old-fashioned function this is. 1561 }
1773 // It's practically FORTRAN 77 in its clarity and linearity. 1562 }
1774 1563
1564 if (!continuingPaint && m_normalizeVisibleArea &&
1565 m_viewMags[viewId] != m_lastRenderedMags[viewId]) {
1566 #ifdef DEBUG_SPECTROGRAM_REPAINT
1567 cerr << "mag range has changed from last rendered range: re-rendering"
1568 << endl;
1569 #endif
1570 delete m_renderers[viewId];
1571 m_renderers.erase(viewId);
1572 v->updatePaintRect(v->getPaintRect());
1573 }
1574 }
1575
1576 void
1577 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1578 {
1775 Profiler profiler("SpectrogramLayer::paint", false); 1579 Profiler profiler("SpectrogramLayer::paint", false);
1776 1580
1777 #ifdef DEBUG_SPECTROGRAM_REPAINT 1581 #ifdef DEBUG_SPECTROGRAM_REPAINT
1778 SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl; 1582 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
1779 1583
1780 cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; 1584 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
1781 #endif 1585 #endif
1782
1783 sv_frame_t startFrame = v->getStartFrame();
1784 if (startFrame < 0) m_candidateFillStartFrame = 0;
1785 else m_candidateFillStartFrame = startFrame;
1786 1586
1787 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1587 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1788 return; 1588 return;
1789 } 1589 }
1790 1590
1791 if (isLayerDormant(v)) { 1591 if (isLayerDormant(v)) {
1792 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; 1592 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
1793 } 1593 }
1794 1594
1795 // Need to do this even if !isLayerDormant, as that could mean v 1595 paintWithRenderer(v, paint, rect);
1796 // is not in the dormancy map at all -- we need it to be present
1797 // and accountable for when determining whether we need the cache
1798 // in the cache-fill thread above.
1799 //!!! no inter use cache-fill thread
1800 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1801
1802 int fftSize = getFFTSize(v);
1803 /*
1804 FFTModel *fft = getFFTModel(v);
1805 if (!fft) {
1806 cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl;
1807 return;
1808 }
1809 */
1810 ImageCache &cache = m_imageCaches[v];
1811
1812 #ifdef DEBUG_SPECTROGRAM_REPAINT
1813 SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache.
1814
1815 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl;
1816 #endif
1817
1818 #ifdef DEBUG_SPECTROGRAM_REPAINT
1819 bool stillCacheing = (m_updateTimer != 0);
1820 SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl;
1821 #endif
1822
1823 int zoomLevel = v->getZoomLevel();
1824
1825 int x0 = 0;
1826 int x1 = v->width();
1827
1828 bool recreateWholeImageCache = true;
1829
1830 x0 = rect.left();
1831 x1 = rect.right() + 1;
1832 /*
1833 double xPixelRatio = double(fft->getResolution()) / double(zoomLevel);
1834 cerr << "xPixelRatio = " << xPixelRatio << endl;
1835 if (xPixelRatio < 1.f) xPixelRatio = 1.f;
1836 */
1837 if (cache.validArea.width() > 0) {
1838
1839 int cw = cache.image.width();
1840 int ch = cache.image.height();
1841
1842 if (int(cache.zoomLevel) == zoomLevel &&
1843 cw == v->width() &&
1844 ch == v->height()) {
1845
1846 if (v->getXForFrame(cache.startFrame) ==
1847 v->getXForFrame(startFrame) &&
1848 cache.validArea.x() <= x0 &&
1849 cache.validArea.x() + cache.validArea.width() >= x1) {
1850
1851 #ifdef DEBUG_SPECTROGRAM_REPAINT
1852 cerr << "SpectrogramLayer: image cache good" << endl;
1853 #endif
1854
1855 paint.drawImage(rect, cache.image, rect);
1856 //!!!
1857 // paint.drawImage(v->rect(), cache.image,
1858 // QRect(QPoint(0, 0), cache.image.size()));
1859
1860 illuminateLocalFeatures(v, paint);
1861 return;
1862
1863 } else {
1864
1865 #ifdef DEBUG_SPECTROGRAM_REPAINT
1866 cerr << "SpectrogramLayer: image cache partially OK" << endl;
1867 #endif
1868
1869 recreateWholeImageCache = false;
1870
1871 int dx = v->getXForFrame(cache.startFrame) -
1872 v->getXForFrame(startFrame);
1873
1874 #ifdef DEBUG_SPECTROGRAM_REPAINT
1875 cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl;
1876 #endif
1877
1878 if (dx != 0 &&
1879 dx > -cw &&
1880 dx < cw) {
1881
1882 int dxp = dx;
1883 if (dxp < 0) dxp = -dxp;
1884 size_t copy = (cw - dxp) * sizeof(QRgb);
1885 for (int y = 0; y < ch; ++y) {
1886 QRgb *line = (QRgb *)cache.image.scanLine(y);
1887 if (dx < 0) {
1888 memmove(line, line + dxp, copy);
1889 } else {
1890 memmove(line + dxp, line, copy);
1891 }
1892 }
1893
1894 int px = cache.validArea.x();
1895 int pw = cache.validArea.width();
1896
1897 if (dx < 0) {
1898 x0 = cw + dx;
1899 x1 = cw;
1900 px += dx;
1901 if (px < 0) {
1902 pw += px;
1903 px = 0;
1904 if (pw < 0) pw = 0;
1905 }
1906 } else {
1907 x0 = 0;
1908 x1 = dx;
1909 px += dx;
1910 if (px + pw > cw) {
1911 pw = int(cw) - px;
1912 if (pw < 0) pw = 0;
1913 }
1914 }
1915
1916 cache.validArea =
1917 QRect(px, cache.validArea.y(),
1918 pw, cache.validArea.height());
1919
1920 #ifdef DEBUG_SPECTROGRAM_REPAINT
1921 cerr << "valid area now "
1922 << px << "," << cache.validArea.y()
1923 << " " << pw << "x" << cache.validArea.height()
1924 << endl;
1925 #endif
1926 /*
1927 paint.drawImage(rect & cache.validArea,
1928 cache.image,
1929 rect & cache.validArea);
1930 */
1931 } else if (dx != 0) {
1932
1933 // we scrolled too far to be of use
1934
1935 #ifdef DEBUG_SPECTROGRAM_REPAINT
1936 cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl;
1937 #endif
1938
1939 cache.validArea = QRect();
1940 recreateWholeImageCache = true;
1941 }
1942 }
1943 } else {
1944 #ifdef DEBUG_SPECTROGRAM_REPAINT
1945 cerr << "SpectrogramLayer: image cache useless" << endl;
1946 if (int(cache.zoomLevel) != zoomLevel) {
1947 cerr << "(cache zoomLevel " << cache.zoomLevel
1948 << " != " << zoomLevel << ")" << endl;
1949 }
1950 if (cw != v->width()) {
1951 cerr << "(cache width " << cw
1952 << " != " << v->width();
1953 }
1954 if (ch != v->height()) {
1955 cerr << "(cache height " << ch
1956 << " != " << v->height();
1957 }
1958 #endif
1959 cache.validArea = QRect();
1960 // recreateWholeImageCache = true;
1961 }
1962 }
1963
1964 if (updateViewMagnitudes(v)) {
1965 #ifdef DEBUG_SPECTROGRAM_REPAINT
1966 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
1967 #endif
1968 if (m_normalizeVisibleArea) {
1969 cache.validArea = QRect();
1970 recreateWholeImageCache = true;
1971 }
1972 } else {
1973 #ifdef DEBUG_SPECTROGRAM_REPAINT
1974 cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
1975 #endif
1976 }
1977
1978 if (recreateWholeImageCache) {
1979 x0 = 0;
1980 x1 = v->width();
1981 }
1982
1983 struct timeval tv;
1984 (void)gettimeofday(&tv, 0);
1985 RealTime mainPaintStart = RealTime::fromTimeval(tv);
1986
1987 int paintBlockWidth = m_lastPaintBlockWidth;
1988
1989 if (m_synchronous) {
1990 if (paintBlockWidth < x1 - x0) {
1991 // always paint full width
1992 paintBlockWidth = x1 - x0;
1993 }
1994 } else {
1995 if (paintBlockWidth == 0) {
1996 paintBlockWidth = (300000 / zoomLevel);
1997 } else {
1998 RealTime lastTime = m_lastPaintTime;
1999 while (lastTime > RealTime::fromMilliseconds(200) &&
2000 paintBlockWidth > 50) {
2001 paintBlockWidth /= 2;
2002 lastTime = lastTime / 2;
2003 }
2004 while (lastTime < RealTime::fromMilliseconds(90) &&
2005 paintBlockWidth < 1500) {
2006 paintBlockWidth *= 2;
2007 lastTime = lastTime * 2;
2008 }
2009 }
2010
2011 if (paintBlockWidth < 20) paintBlockWidth = 20;
2012 }
2013
2014 #ifdef DEBUG_SPECTROGRAM_REPAINT
2015 cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl;
2016 #endif
2017
2018 // We always paint the full height when refreshing the cache.
2019 // Smaller heights can be used when painting direct from cache
2020 // (further up in this function), but we want to ensure the cache
2021 // is coherent without having to worry about vertical matching of
2022 // required and valid areas as well as horizontal.
2023
2024 int h = v->height();
2025
2026 if (cache.validArea.width() > 0) {
2027
2028 // If part of the cache is known to be valid, select a strip
2029 // immediately to left or right of the valid part
2030
2031 //!!! this really needs to be coordinated with the selection
2032 //!!! of m_drawBuffer boundaries in the bufferBinResolution
2033 //!!! case below
2034
2035 int vx0 = 0, vx1 = 0;
2036 vx0 = cache.validArea.x();
2037 vx1 = cache.validArea.x() + cache.validArea.width();
2038
2039 #ifdef DEBUG_SPECTROGRAM_REPAINT
2040 cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl;
2041 #endif
2042 if (x0 < vx0) {
2043 if (x0 + paintBlockWidth < vx0) {
2044 x0 = vx0 - paintBlockWidth;
2045 }
2046 x1 = vx0;
2047 } else if (x0 >= vx1) {
2048 x0 = vx1;
2049 if (x1 > x0 + paintBlockWidth) {
2050 x1 = x0 + paintBlockWidth;
2051 }
2052 } else {
2053 // x0 is within the valid area
2054 if (x1 > vx1) {
2055 x0 = vx1;
2056 if (x0 + paintBlockWidth < x1) {
2057 x1 = x0 + paintBlockWidth;
2058 }
2059 } else {
2060 x1 = x0; // it's all valid, paint nothing
2061 }
2062 }
2063
2064 cache.validArea = QRect
2065 (std::min(vx0, x0), cache.validArea.y(),
2066 std::max(vx1 - std::min(vx0, x0),
2067 x1 - std::min(vx0, x0)),
2068 cache.validArea.height());
2069
2070 #ifdef DEBUG_SPECTROGRAM_REPAINT
2071 cerr << "Valid area becomes " << cache.validArea.x()
2072 << ", " << cache.validArea.y() << ", "
2073 << cache.validArea.width() << "x"
2074 << cache.validArea.height() << endl;
2075 #endif
2076
2077 } else {
2078 if (x1 > x0 + paintBlockWidth) {
2079 int sfx = x1;
2080 if (startFrame < 0) sfx = v->getXForFrame(0);
2081 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
2082 x0 = sfx;
2083 x1 = x0 + paintBlockWidth;
2084 } else {
2085 int mid = (x1 + x0) / 2;
2086 x0 = mid - paintBlockWidth/2;
2087 x1 = x0 + paintBlockWidth;
2088 }
2089 }
2090 #ifdef DEBUG_SPECTROGRAM_REPAINT
2091 cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0)
2092 << "x" << h << endl;
2093 #endif
2094 cache.validArea = QRect(x0, 0, x1 - x0, h);
2095 }
2096
2097 /*
2098 if (xPixelRatio != 1.f) {
2099 x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001);
2100 x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001);
2101 }
2102 */
2103 int w = x1 - x0;
2104
2105 #ifdef DEBUG_SPECTROGRAM_REPAINT
2106 cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl;
2107 #endif
2108
2109 sv_samplerate_t sr = m_model->getSampleRate();
2110
2111 // Set minFreq and maxFreq to the frequency extents of the possibly
2112 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
2113 // to the actual scale frequency extents (presumably not zero padded).
2114
2115 // If we are zero padding, we want to use the zero-padded
2116 // equivalents of the bins that we would be using if not zero
2117 // padded, to avoid spaces at the top and bottom of the display.
2118
2119 // Note fftSize is the actual zero-padded fft size, m_fftSize the
2120 // nominal fft size.
2121
2122 int maxbin = m_fftSize / 2;
2123 if (m_maxFrequency > 0) {
2124 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
2125 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
2126 }
2127
2128 int minbin = 1;
2129 if (m_minFrequency > 0) {
2130 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
2131 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
2132 if (minbin < 1) minbin = 1;
2133 if (minbin >= maxbin) minbin = maxbin - 1;
2134 }
2135
2136 int zpl = getZeroPadLevel(v) + 1;
2137 minbin = minbin * zpl;
2138 maxbin = (maxbin + 1) * zpl - 1;
2139
2140 double minFreq = (double(minbin) * sr) / fftSize;
2141 double maxFreq = (double(maxbin) * sr) / fftSize;
2142
2143 double displayMinFreq = minFreq;
2144 double displayMaxFreq = maxFreq;
2145
2146 if (fftSize != m_fftSize) {
2147 displayMinFreq = getEffectiveMinFrequency();
2148 displayMaxFreq = getEffectiveMaxFrequency();
2149 }
2150
2151 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
2152
2153 int increment = getWindowIncrement();
2154
2155 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2156 /*
2157 double yforbin[maxbin - minbin + 1];
2158
2159 for (int q = minbin; q <= maxbin; ++q) {
2160 double f0 = (double(q) * sr) / fftSize;
2161 yforbin[q - minbin] =
2162 v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2163 logarithmic);
2164 }
2165 */
2166 MagnitudeRange overallMag = m_viewMags[v];
2167 bool overallMagChanged = false;
2168
2169 #ifdef DEBUG_SPECTROGRAM_REPAINT
2170 cerr << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
2171 #endif
2172
2173 if (w == 0) {
2174 SVDEBUG << "*** NOTE: w == 0" << endl;
2175 }
2176
2177 #ifdef DEBUG_SPECTROGRAM_REPAINT
2178 int pixels = 0;
2179 #endif
2180
2181 Profiler outerprof("SpectrogramLayer::paint: all cols");
2182
2183 // The draw buffer contains a fragment at either our pixel
2184 // resolution (if there is more than one time-bin per pixel) or
2185 // time-bin resolution (if a time-bin spans more than one pixel).
2186 // We need to ensure that it starts and ends at points where a
2187 // time-bin boundary occurs at an exact pixel boundary, and with a
2188 // certain amount of overlap across existing pixels so that we can
2189 // scale and draw from it without smoothing errors at the edges.
2190
2191 // If (getFrameForX(x) / increment) * increment ==
2192 // getFrameForX(x), then x is a time-bin boundary. We want two
2193 // such boundaries at either side of the draw buffer -- one which
2194 // we draw up to, and one which we subsequently crop at.
2195
2196 bool bufferBinResolution = false;
2197 if (increment > zoomLevel) bufferBinResolution = true;
2198
2199 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
2200 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
2201
2202 int bufwid;
2203
2204 if (bufferBinResolution) {
2205
2206 for (int x = x0; ; --x) {
2207 sv_frame_t f = v->getFrameForX(x);
2208 if ((f / increment) * increment == f) {
2209 if (leftCropFrame == -1) leftCropFrame = f;
2210 else if (x < x0 - 2) { leftBoundaryFrame = f; break; }
2211 }
2212 }
2213 for (int x = x0 + w; ; ++x) {
2214 sv_frame_t f = v->getFrameForX(x);
2215 if ((f / increment) * increment == f) {
2216 if (rightCropFrame == -1) rightCropFrame = f;
2217 else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; }
2218 }
2219 }
2220 #ifdef DEBUG_SPECTROGRAM_REPAINT
2221 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
2222 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
2223 #endif
2224
2225 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment);
2226
2227 } else {
2228
2229 bufwid = w;
2230 }
2231
2232 vector<int> binforx(bufwid);
2233 vector<double> binfory(h);
2234
2235 bool usePeaksCache = false;
2236
2237 if (bufferBinResolution) {
2238 for (int x = 0; x < bufwid; ++x) {
2239 binforx[x] = int(leftBoundaryFrame / increment) + x;
2240 // cerr << "binforx[" << x << "] = " << binforx[x] << endl;
2241 }
2242 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2243 } else {
2244 for (int x = 0; x < bufwid; ++x) {
2245 double s0 = 0, s1 = 0;
2246 if (getXBinRange(v, x + x0, s0, s1)) {
2247 binforx[x] = int(s0 + 0.0001);
2248 } else {
2249 binforx[x] = -1; //???
2250 }
2251 }
2252 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) {
2253 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2254 }
2255 usePeaksCache = (increment * 8) < zoomLevel;
2256 if (m_colourScale == PhaseColourScale) usePeaksCache = false;
2257 }
2258
2259 // No longer exists in Qt5: m_drawBuffer.setNumColors(256);
2260 for (int pixel = 0; pixel < 256; ++pixel) {
2261 m_drawBuffer.setColor((unsigned char)pixel,
2262 m_palette.getColour((unsigned char)pixel).rgb());
2263 }
2264
2265 m_drawBuffer.fill(0);
2266
2267 if (m_binDisplay != PeakFrequencies) {
2268
2269 for (int y = 0; y < h; ++y) {
2270 double q0 = 0, q1 = 0;
2271 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
2272 binfory[y] = -1;
2273 } else {
2274 binfory[y] = q0;
2275 // cerr << "binfory[" << y << "] = " << binfory[y] << endl;
2276 }
2277 }
2278
2279 paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache,
2280 overallMag, overallMagChanged);
2281
2282 } else {
2283
2284 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
2285 minbin, maxbin,
2286 displayMinFreq, displayMaxFreq,
2287 logarithmic,
2288 overallMag, overallMagChanged);
2289 }
2290
2291 /*
2292 for (int x = 0; x < w / xPixelRatio; ++x) {
2293
2294 Profiler innerprof("SpectrogramLayer::paint: 1 pixel column");
2295
2296 runOutOfData = !paintColumnValues(v, fft, x0, x,
2297 minbin, maxbin,
2298 displayMinFreq, displayMaxFreq,
2299 xPixelRatio,
2300 h, yforbin);
2301
2302 if (runOutOfData) {
2303 #ifdef DEBUG_SPECTROGRAM_REPAINT
2304 cerr << "Run out of data -- dropping out of loop" << endl;
2305 #endif
2306 break;
2307 }
2308 }
2309 */
2310 #ifdef DEBUG_SPECTROGRAM_REPAINT
2311 // cerr << pixels << " pixels drawn" << endl;
2312 #endif
2313
2314 if (overallMagChanged) {
2315 m_viewMags[v] = overallMag;
2316 #ifdef DEBUG_SPECTROGRAM_REPAINT
2317 cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl;
2318 #endif
2319 } else {
2320 #ifdef DEBUG_SPECTROGRAM_REPAINT
2321 cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
2322 #endif
2323 }
2324
2325 outerprof.end();
2326
2327 Profiler profiler2("SpectrogramLayer::paint: draw image");
2328
2329 if (recreateWholeImageCache) {
2330 #ifdef DEBUG_SPECTROGRAM_REPAINT
2331 SVDEBUG << "Recreating image cache: width = " << v->width()
2332 << ", height = " << h << endl;
2333 #endif
2334 cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied);
2335 }
2336
2337 if (w > 0) {
2338 #ifdef DEBUG_SPECTROGRAM_REPAINT
2339 SVDEBUG << "Painting " << w << "x" << h
2340 << " from draw buffer at " << 0 << "," << 0
2341 << " to " << w << "x" << h << " on cache at "
2342 << x0 << "," << 0 << endl;
2343 #endif
2344
2345 QPainter cachePainter(&cache.image);
2346
2347 if (bufferBinResolution) {
2348 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2349 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2350 #ifdef DEBUG_SPECTROGRAM_REPAINT
2351 SVDEBUG << "Rescaling image from " << bufwid
2352 << "x" << h << " to "
2353 << scaledRight-scaledLeft << "x" << h << endl;
2354 #endif
2355 Preferences::SpectrogramXSmoothing xsmoothing =
2356 Preferences::getInstance()->getSpectrogramXSmoothing();
2357 // SVDEBUG << "xsmoothing == " << xsmoothing << endl;
2358 QImage scaled = m_drawBuffer.scaled
2359 (scaledRight - scaledLeft, h,
2360 Qt::IgnoreAspectRatio,
2361 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2362 Qt::SmoothTransformation : Qt::FastTransformation));
2363 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2364 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2365 #ifdef DEBUG_SPECTROGRAM_REPAINT
2366 SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2367 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2368 #endif
2369 cachePainter.drawImage
2370 (QRect(scaledLeftCrop, 0,
2371 scaledRightCrop - scaledLeftCrop, h),
2372 scaled,
2373 QRect(scaledLeftCrop - scaledLeft, 0,
2374 scaledRightCrop - scaledLeftCrop, h));
2375 } else {
2376 cachePainter.drawImage(QRect(x0, 0, w, h),
2377 m_drawBuffer,
2378 QRect(0, 0, w, h));
2379 }
2380
2381 cachePainter.end();
2382 }
2383
2384 QRect pr = rect & cache.validArea;
2385
2386 #ifdef DEBUG_SPECTROGRAM_REPAINT
2387 SVDEBUG << "Painting " << pr.width() << "x" << pr.height()
2388 << " from cache at " << pr.x() << "," << pr.y()
2389 << " to window" << endl;
2390 #endif
2391
2392 paint.drawImage(pr.x(), pr.y(), cache.image,
2393 pr.x(), pr.y(), pr.width(), pr.height());
2394 //!!!
2395 // paint.drawImage(v->rect(), cache.image,
2396 // QRect(QPoint(0, 0), cache.image.size()));
2397
2398 cache.startFrame = startFrame;
2399 cache.zoomLevel = zoomLevel;
2400
2401 if (!m_synchronous) {
2402
2403 if (!m_normalizeVisibleArea || !overallMagChanged) {
2404
2405 if (cache.validArea.x() > 0) {
2406 #ifdef DEBUG_SPECTROGRAM_REPAINT
2407 SVDEBUG << "SpectrogramLayer::paint() updating left (0, "
2408 << cache.validArea.x() << ")" << endl;
2409 #endif
2410 v->update(0, 0, cache.validArea.x(), h);
2411 }
2412
2413 if (cache.validArea.x() + cache.validArea.width() <
2414 cache.image.width()) {
2415 #ifdef DEBUG_SPECTROGRAM_REPAINT
2416 SVDEBUG << "SpectrogramLayer::paint() updating right ("
2417 << cache.validArea.x() + cache.validArea.width()
2418 << ", "
2419 << cache.image.width() - (cache.validArea.x() +
2420 cache.validArea.width())
2421 << ")" << endl;
2422 #endif
2423 v->update(cache.validArea.x() + cache.validArea.width(),
2424 0,
2425 cache.image.width() - (cache.validArea.x() +
2426 cache.validArea.width()),
2427 h);
2428 }
2429 } else {
2430 // overallMagChanged
2431 cerr << "\noverallMagChanged - updating all\n" << endl;
2432 cache.validArea = QRect();
2433 v->update();
2434 }
2435 }
2436 1596
2437 illuminateLocalFeatures(v, paint); 1597 illuminateLocalFeatures(v, paint);
2438 1598 }
2439 #ifdef DEBUG_SPECTROGRAM_REPAINT 1599
2440 SVDEBUG << "SpectrogramLayer::paint() returning" << endl; 1600 void
2441 #endif 1601 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
2442
2443 if (!m_synchronous) {
2444 m_lastPaintBlockWidth = paintBlockWidth;
2445 (void)gettimeofday(&tv, 0);
2446 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
2447 }
2448
2449 //!!! if (fftSuspended) fft->resume();
2450 }
2451
2452 bool
2453 SpectrogramLayer::paintDrawBufferPeakFrequencies(View *v,
2454 int w,
2455 int h,
2456 const vector<int> &binforx,
2457 int minbin,
2458 int maxbin,
2459 double displayMinFreq,
2460 double displayMaxFreq,
2461 bool logarithmic,
2462 MagnitudeRange &overallMag,
2463 bool &overallMagChanged) const
2464 {
2465 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
2466
2467 #ifdef DEBUG_SPECTROGRAM_REPAINT
2468 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2469 #endif
2470 if (minbin < 0) minbin = 0;
2471 if (maxbin < 0) maxbin = minbin+1;
2472
2473 FFTModel *fft = getFFTModel(v);
2474 if (!fft) return false;
2475
2476 FFTModel::PeakSet peakfreqs;
2477
2478 int psx = -1;
2479
2480 #ifdef __GNUC__
2481 float values[maxbin - minbin + 1];
2482 #else
2483 float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2484 #endif
2485
2486 for (int x = 0; x < w; ++x) {
2487
2488 if (binforx[x] < 0) continue;
2489
2490 int sx0 = binforx[x];
2491 int sx1 = sx0;
2492 if (x+1 < w) sx1 = binforx[x+1];
2493 if (sx0 < 0) sx0 = sx1 - 1;
2494 if (sx0 < 0) continue;
2495 if (sx1 <= sx0) sx1 = sx0 + 1;
2496
2497 for (int sx = sx0; sx < sx1; ++sx) {
2498
2499 if (sx < 0 || sx >= int(fft->getWidth())) continue;
2500
2501 if (!m_synchronous) {
2502 if (!fft->isColumnAvailable(sx)) {
2503 #ifdef DEBUG_SPECTROGRAM_REPAINT
2504 cerr << "Met unavailable column at col " << sx << endl;
2505 #endif
2506 return false;
2507 }
2508 }
2509
2510 MagnitudeRange mag;
2511
2512 if (sx != psx) {
2513 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2514 minbin, maxbin - 1);
2515 if (m_colourScale == PhaseColourScale) {
2516 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2517 } else if (m_normalizeColumns) {
2518 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2519 } else if (m_normalizeHybrid) {
2520 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2521 double max = fft->getMaximumMagnitudeAt(sx);
2522 if (max > 0.f) {
2523 for (int i = minbin; i <= maxbin; ++i) {
2524 values[i - minbin] = float(values[i - minbin] * log10(max));
2525 }
2526 }
2527 } else {
2528 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2529 }
2530 psx = sx;
2531 }
2532
2533 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
2534 pi != peakfreqs.end(); ++pi) {
2535
2536 int bin = pi->first;
2537 double freq = pi->second;
2538
2539 if (bin < minbin) continue;
2540 if (bin > maxbin) break;
2541
2542 double value = values[bin - minbin];
2543
2544 if (m_colourScale != PhaseColourScale) {
2545 if (!m_normalizeColumns && !m_normalizeHybrid) {
2546 value /= (m_fftSize/2.0);
2547 }
2548 mag.sample(float(value));
2549 value *= m_gain;
2550 }
2551
2552 double y = v->getYForFrequency
2553 (freq, displayMinFreq, displayMaxFreq, logarithmic);
2554
2555 int iy = int(y + 0.5);
2556 if (iy < 0 || iy >= h) continue;
2557
2558 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value));
2559 }
2560
2561 if (mag.isSet()) {
2562 if (sx >= int(m_columnMags.size())) {
2563 #ifdef DEBUG_SPECTROGRAM
2564 cerr << "INTERNAL ERROR: " << sx << " >= "
2565 << m_columnMags.size()
2566 << " at SpectrogramLayer.cpp::paintDrawBuffer"
2567 << endl;
2568 #endif
2569 } else {
2570 m_columnMags[sx].sample(mag);
2571 if (overallMag.sample(mag)) overallMagChanged = true;
2572 }
2573 }
2574 }
2575 }
2576
2577 return true;
2578 }
2579
2580 bool
2581 SpectrogramLayer::paintDrawBuffer(View *v,
2582 int w,
2583 int h,
2584 const vector<int> &binforx,
2585 const vector<double> &binfory,
2586 bool usePeaksCache,
2587 MagnitudeRange &overallMag,
2588 bool &overallMagChanged) const
2589 {
2590 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2591
2592 int minbin = int(binfory[0] + 0.0001);
2593 int maxbin = int(binfory[h-1]);
2594
2595 #ifdef DEBUG_SPECTROGRAM_REPAINT
2596 cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2597 #endif
2598 if (minbin < 0) minbin = 0;
2599 if (maxbin < 0) maxbin = minbin+1;
2600
2601 DenseThreeDimensionalModel *sourceModel = 0;
2602 FFTModel *fft = 0;
2603 int divisor = 1;
2604 #ifdef DEBUG_SPECTROGRAM_REPAINT
2605 cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2606 #endif
2607 if (usePeaksCache) { //!!!
2608 sourceModel = getPeakCache(v);
2609 divisor = 8;//!!!
2610 minbin = 0;
2611 maxbin = sourceModel->getHeight();
2612 } else {
2613 sourceModel = fft = getFFTModel(v);
2614 }
2615
2616 if (!sourceModel) return false;
2617
2618 bool interpolate = false;
2619 Preferences::SpectrogramSmoothing smoothing =
2620 Preferences::getInstance()->getSpectrogramSmoothing();
2621 if (smoothing == Preferences::SpectrogramInterpolated ||
2622 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2623 if (m_binDisplay != PeakBins &&
2624 m_binDisplay != PeakFrequencies) {
2625 interpolate = true;
2626 }
2627 }
2628
2629 int psx = -1;
2630
2631 #ifdef __GNUC__
2632 float autoarray[maxbin - minbin + 1];
2633 float peaks[h];
2634 #else
2635 float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2636 float *peaks = (float *)alloca(h * sizeof(float));
2637 #endif
2638
2639 const float *values = autoarray;
2640 DenseThreeDimensionalModel::Column c;
2641
2642 for (int x = 0; x < w; ++x) {
2643
2644 if (binforx[x] < 0) continue;
2645
2646 // float columnGain = m_gain;
2647 float columnMax = 0.f;
2648
2649 int sx0 = binforx[x] / divisor;
2650 int sx1 = sx0;
2651 if (x+1 < w) sx1 = binforx[x+1] / divisor;
2652 if (sx0 < 0) sx0 = sx1 - 1;
2653 if (sx0 < 0) continue;
2654 if (sx1 <= sx0) sx1 = sx0 + 1;
2655
2656 for (int y = 0; y < h; ++y) peaks[y] = 0.f;
2657
2658 for (int sx = sx0; sx < sx1; ++sx) {
2659
2660 #ifdef DEBUG_SPECTROGRAM_REPAINT
2661 // cerr << "sx = " << sx << endl;
2662 #endif
2663
2664 if (sx < 0 || sx >= int(sourceModel->getWidth())) continue;
2665
2666 if (!m_synchronous) {
2667 if (!sourceModel->isColumnAvailable(sx)) {
2668 #ifdef DEBUG_SPECTROGRAM_REPAINT
2669 cerr << "Met unavailable column at col " << sx << endl;
2670 #endif
2671 return false;
2672 }
2673 }
2674
2675 MagnitudeRange mag;
2676
2677 if (sx != psx) {
2678 if (fft) {
2679 #ifdef DEBUG_SPECTROGRAM_REPAINT
2680 SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl;
2681 #endif
2682 if (m_colourScale == PhaseColourScale) {
2683 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2684 } else if (m_normalizeColumns) {
2685 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2686 } else if (m_normalizeHybrid) {
2687 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2688 double max = fft->getMaximumMagnitudeAt(sx);
2689 for (int i = minbin; i <= maxbin; ++i) {
2690 if (max > 0.0) {
2691 autoarray[i - minbin] = float(autoarray[i - minbin] * log10(max));
2692 }
2693 }
2694 } else {
2695 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2696 }
2697 } else {
2698 #ifdef DEBUG_SPECTROGRAM_REPAINT
2699 SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl;
2700 #endif
2701 c = sourceModel->getColumn(sx);
2702 if (m_normalizeColumns || m_normalizeHybrid) {
2703 for (int y = 0; y < h; ++y) {
2704 if (c[y] > columnMax) columnMax = c[y];
2705 }
2706 }
2707 values = c.constData() + minbin;
2708 }
2709 psx = sx;
2710 }
2711
2712 for (int y = 0; y < h; ++y) {
2713
2714 double sy0 = binfory[y];
2715 double sy1 = sy0 + 1;
2716 if (y+1 < h) sy1 = binfory[y+1];
2717
2718 double value = 0.0;
2719
2720 if (interpolate && fabs(sy1 - sy0) < 1.0) {
2721
2722 double centre = (sy0 + sy1) / 2;
2723 double dist = (centre - 0.5) - rint(centre - 0.5);
2724 int bin = int(centre);
2725 int other = (dist < 0 ? (bin-1) : (bin+1));
2726 if (bin < minbin) bin = minbin;
2727 if (bin > maxbin) bin = maxbin;
2728 if (other < minbin || other > maxbin) other = bin;
2729 double prop = 1.0 - fabs(dist);
2730
2731 double v0 = values[bin - minbin];
2732 double v1 = values[other - minbin];
2733 if (m_binDisplay == PeakBins) {
2734 if (bin == minbin || bin == maxbin ||
2735 v0 < values[bin-minbin-1] ||
2736 v0 < values[bin-minbin+1]) v0 = 0.0;
2737 if (other == minbin || other == maxbin ||
2738 v1 < values[other-minbin-1] ||
2739 v1 < values[other-minbin+1]) v1 = 0.0;
2740 }
2741 if (v0 == 0.0 && v1 == 0.0) continue;
2742 value = prop * v0 + (1.0 - prop) * v1;
2743
2744 if (m_colourScale != PhaseColourScale) {
2745 if (!m_normalizeColumns) {
2746 value /= (m_fftSize/2.0);
2747 }
2748 mag.sample(float(value));
2749 value *= m_gain;
2750 }
2751
2752 peaks[y] = float(value);
2753
2754 } else {
2755
2756 int by0 = int(sy0 + 0.0001);
2757 int by1 = int(sy1 + 0.0001);
2758 if (by1 < by0 + 1) by1 = by0 + 1;
2759
2760 for (int bin = by0; bin < by1; ++bin) {
2761
2762 value = values[bin - minbin];
2763 if (m_binDisplay == PeakBins) {
2764 if (bin == minbin || bin == maxbin ||
2765 value < values[bin-minbin-1] ||
2766 value < values[bin-minbin+1]) continue;
2767 }
2768
2769 if (m_colourScale != PhaseColourScale) {
2770 if (!m_normalizeColumns) {
2771 value /= (m_fftSize/2.0);
2772 }
2773 mag.sample(float(value));
2774 value *= m_gain;
2775 }
2776
2777 if (value > peaks[y]) {
2778 peaks[y] = float(value); //!!! not right for phase!
2779 }
2780 }
2781 }
2782 }
2783
2784 if (mag.isSet()) {
2785 if (sx >= int(m_columnMags.size())) {
2786 #ifdef DEBUG_SPECTROGRAM
2787 cerr << "INTERNAL ERROR: " << sx << " >= "
2788 << m_columnMags.size()
2789 << " at SpectrogramLayer.cpp::paintDrawBuffer"
2790 << endl;
2791 #endif
2792 } else {
2793 m_columnMags[sx].sample(mag);
2794 if (overallMag.sample(mag)) overallMagChanged = true;
2795 }
2796 }
2797 }
2798
2799 for (int y = 0; y < h; ++y) {
2800
2801 double peak = peaks[y];
2802
2803 if (m_colourScale != PhaseColourScale &&
2804 (m_normalizeColumns || m_normalizeHybrid) &&
2805 columnMax > 0.f) {
2806 peak /= columnMax;
2807 if (m_normalizeHybrid) {
2808 peak *= log10(columnMax);
2809 }
2810 }
2811
2812 unsigned char peakpix = getDisplayValue(v, peak);
2813
2814 m_drawBuffer.setPixel(x, h-y-1, peakpix);
2815 }
2816 }
2817
2818 return true;
2819 }
2820
2821 void
2822 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
2823 { 1602 {
2824 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures"); 1603 Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
2825 1604
2826 QPoint localPos; 1605 QPoint localPos;
2827 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) { 1606 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
2828 return; 1607 return;
2829 } 1608 }
2830 1609
2831 // cerr << "SpectrogramLayer: illuminateLocalFeatures(" 1610 #ifdef DEBUG_SPECTROGRAM_REPAINT
2832 // << localPos.x() << "," << localPos.y() << ")" << endl; 1611 cerr << "SpectrogramLayer: illuminateLocalFeatures("
1612 << localPos.x() << "," << localPos.y() << ")" << endl;
1613 #endif
2833 1614
2834 double s0, s1; 1615 double s0, s1;
2835 double f0, f1; 1616 double f0, f1;
2836 1617
2837 if (getXBinRange(v, localPos.x(), s0, s1) && 1618 if (getXBinRange(v, localPos.x(), s0, s1) &&
2844 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement()); 1625 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
2845 1626
2846 int y1 = int(getYForFrequency(v, f1)); 1627 int y1 = int(getYForFrequency(v, f1));
2847 int y0 = int(getYForFrequency(v, f0)); 1628 int y0 = int(getYForFrequency(v, f0));
2848 1629
2849 // cerr << "SpectrogramLayer: illuminate " 1630 #ifdef DEBUG_SPECTROGRAM_REPAINT
2850 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl; 1631 cerr << "SpectrogramLayer: illuminate "
1632 << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl;
1633 #endif
2851 1634
2852 paint.setPen(v->getForeground()); 1635 paint.setPen(v->getForeground());
2853 1636
2854 //!!! should we be using paintCrosshairs for this? 1637 //!!! should we be using paintCrosshairs for this?
2855 1638
2856 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); 1639 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
2857 } 1640 }
2858 } 1641 }
2859 1642
2860 double 1643 double
2861 SpectrogramLayer::getYForFrequency(const View *v, double frequency) const 1644 SpectrogramLayer::getYForFrequency(const LayerGeometryProvider *v, double frequency) const
2862 { 1645 {
2863 return v->getYForFrequency(frequency, 1646 return v->getYForFrequency(frequency,
2864 getEffectiveMinFrequency(), 1647 getEffectiveMinFrequency(),
2865 getEffectiveMaxFrequency(), 1648 getEffectiveMaxFrequency(),
2866 m_frequencyScale == LogFrequencyScale); 1649 m_binScale == BinScale::Log);
2867 } 1650 }
2868 1651
2869 double 1652 double
2870 SpectrogramLayer::getFrequencyForY(const View *v, int y) const 1653 SpectrogramLayer::getFrequencyForY(const LayerGeometryProvider *v, int y) const
2871 { 1654 {
2872 return v->getFrequencyForY(y, 1655 return v->getFrequencyForY(y,
2873 getEffectiveMinFrequency(), 1656 getEffectiveMinFrequency(),
2874 getEffectiveMaxFrequency(), 1657 getEffectiveMaxFrequency(),
2875 m_frequencyScale == LogFrequencyScale); 1658 m_binScale == BinScale::Log);
2876 } 1659 }
2877 1660
2878 int 1661 int
2879 SpectrogramLayer::getCompletion(View *v) const 1662 SpectrogramLayer::getCompletion(LayerGeometryProvider *) const
2880 { 1663 {
2881 if (m_updateTimer == 0) return 100; 1664 if (!m_fftModel) return 100;
2882 if (m_fftModels.find(v) == m_fftModels.end()) return 100; 1665 int completion = m_fftModel->getCompletion();
2883
2884 int completion = m_fftModels[v].first->getCompletion();
2885 #ifdef DEBUG_SPECTROGRAM_REPAINT 1666 #ifdef DEBUG_SPECTROGRAM_REPAINT
2886 SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl; 1667 cerr << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
2887 #endif 1668 #endif
2888 return completion; 1669 return completion;
2889 } 1670 }
2890 1671
2891 QString 1672 QString
2892 SpectrogramLayer::getError(View *v) const 1673 SpectrogramLayer::getError(LayerGeometryProvider *) const
2893 { 1674 {
2894 if (m_fftModels.find(v) == m_fftModels.end()) return ""; 1675 if (!m_fftModel) return "";
2895 return m_fftModels[v].first->getError(); 1676 return m_fftModel->getError();
2896 } 1677 }
2897 1678
2898 bool 1679 bool
2899 SpectrogramLayer::getValueExtents(double &min, double &max, 1680 SpectrogramLayer::getValueExtents(double &min, double &max,
2900 bool &logarithmic, QString &unit) const 1681 bool &logarithmic, QString &unit) const
2901 { 1682 {
2902 if (!m_model) return false; 1683 if (!m_model) return false;
2903 1684
2904 sv_samplerate_t sr = m_model->getSampleRate(); 1685 sv_samplerate_t sr = m_model->getSampleRate();
2905 min = double(sr) / m_fftSize; 1686 min = double(sr) / getFFTSize();
2906 max = double(sr) / 2; 1687 max = double(sr) / 2;
2907 1688
2908 logarithmic = (m_frequencyScale == LogFrequencyScale); 1689 logarithmic = (m_binScale == BinScale::Log);
2909 unit = "Hz"; 1690 unit = "Hz";
2910 return true; 1691 return true;
2911 } 1692 }
2912 1693
2913 bool 1694 bool
2933 int minf = int(lrint(min)); 1714 int minf = int(lrint(min));
2934 int maxf = int(lrint(max)); 1715 int maxf = int(lrint(max));
2935 1716
2936 if (m_minFrequency == minf && m_maxFrequency == maxf) return true; 1717 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
2937 1718
2938 invalidateImageCaches(); 1719 invalidateRenderers();
2939 invalidateMagnitudes(); 1720 invalidateMagnitudes();
2940 1721
2941 m_minFrequency = minf; 1722 m_minFrequency = minf;
2942 m_maxFrequency = maxf; 1723 m_maxFrequency = maxf;
2943 1724
2951 1732
2952 return true; 1733 return true;
2953 } 1734 }
2954 1735
2955 bool 1736 bool
2956 SpectrogramLayer::getYScaleValue(const View *v, int y, 1737 SpectrogramLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
2957 double &value, QString &unit) const 1738 double &value, QString &unit) const
2958 { 1739 {
2959 value = getFrequencyForY(v, y); 1740 value = getFrequencyForY(v, y);
2960 unit = "Hz"; 1741 unit = "Hz";
2961 return true; 1742 return true;
2962 } 1743 }
2963 1744
2964 bool 1745 bool
2965 SpectrogramLayer::snapToFeatureFrame(View *, 1746 SpectrogramLayer::snapToFeatureFrame(LayerGeometryProvider *,
2966 sv_frame_t &frame, 1747 sv_frame_t &frame,
2967 int &resolution, 1748 int &resolution,
2968 SnapType snap) const 1749 SnapType snap) const
2969 { 1750 {
2970 resolution = getWindowIncrement(); 1751 resolution = getWindowIncrement();
2983 1764
2984 return true; 1765 return true;
2985 } 1766 }
2986 1767
2987 void 1768 void
2988 SpectrogramLayer::measureDoubleClick(View *v, QMouseEvent *e) 1769 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e)
2989 { 1770 {
2990 ImageCache &cache = m_imageCaches[v]; 1771 const Colour3DPlotRenderer *renderer = getRenderer(v);
2991 1772 if (!renderer) return;
2992 cerr << "cache width: " << cache.image.width() << ", height: " 1773
2993 << cache.image.height() << endl; 1774 QRect rect = renderer->findSimilarRegionExtents(e->pos());
2994
2995 QImage image = cache.image;
2996
2997 ImageRegionFinder finder;
2998 QRect rect = finder.findRegionExtents(&image, e->pos());
2999 if (rect.isValid()) { 1775 if (rect.isValid()) {
3000 MeasureRect mr; 1776 MeasureRect mr;
3001 setMeasureRectFromPixrect(v, mr, rect); 1777 setMeasureRectFromPixrect(v, mr, rect);
3002 CommandHistory::getInstance()->addCommand 1778 CommandHistory::getInstance()->addCommand
3003 (new AddMeasurementRectCommand(this, mr)); 1779 (new AddMeasurementRectCommand(this, mr));
3004 } 1780 }
3005 } 1781 }
3006 1782
3007 bool 1783 bool
3008 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &paint, 1784 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
3009 QPoint cursorPos, 1785 QPoint cursorPos,
3010 std::vector<QRect> &extents) const 1786 vector<QRect> &extents) const
3011 { 1787 {
3012 QRect vertical(cursorPos.x() - 12, 0, 12, v->height()); 1788 QRect vertical(cursorPos.x() - 12, 0, 12, v->getPaintHeight());
3013 extents.push_back(vertical); 1789 extents.push_back(vertical);
3014 1790
3015 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1); 1791 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
3016 extents.push_back(horizontal); 1792 extents.push_back(horizontal);
3017 1793
3026 paint.fontMetrics().width("C#10+50c") + 2, 1802 paint.fontMetrics().width("C#10+50c") + 2,
3027 paint.fontMetrics().height()); 1803 paint.fontMetrics().height());
3028 extents.push_back(pitch); 1804 extents.push_back(pitch);
3029 1805
3030 QRect rt(cursorPos.x(), 1806 QRect rt(cursorPos.x(),
3031 v->height() - paint.fontMetrics().height() - 2, 1807 v->getPaintHeight() - paint.fontMetrics().height() - 2,
3032 paint.fontMetrics().width("1234.567 s"), 1808 paint.fontMetrics().width("1234.567 s"),
3033 paint.fontMetrics().height()); 1809 paint.fontMetrics().height());
3034 extents.push_back(rt); 1810 extents.push_back(rt);
3035 1811
3036 int w(paint.fontMetrics().width("1234567890") + 2); 1812 int w(paint.fontMetrics().width("1234567890") + 2);
3037 QRect frame(cursorPos.x() - w - 2, 1813 QRect frame(cursorPos.x() - w - 2,
3038 v->height() - paint.fontMetrics().height() - 2, 1814 v->getPaintHeight() - paint.fontMetrics().height() - 2,
3039 w, 1815 w,
3040 paint.fontMetrics().height()); 1816 paint.fontMetrics().height());
3041 extents.push_back(frame); 1817 extents.push_back(frame);
3042 1818
3043 return true; 1819 return true;
3044 } 1820 }
3045 1821
3046 void 1822 void
3047 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint, 1823 SpectrogramLayer::paintCrosshairs(LayerGeometryProvider *v, QPainter &paint,
3048 QPoint cursorPos) const 1824 QPoint cursorPos) const
3049 { 1825 {
3050 paint.save(); 1826 paint.save();
3051 1827
3052 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint); 1828 int sw = getVerticalScaleWidth(v, m_haveDetailedScale, paint);
3057 paint.setFont(fn); 1833 paint.setFont(fn);
3058 } 1834 }
3059 paint.setPen(m_crosshairColour); 1835 paint.setPen(m_crosshairColour);
3060 1836
3061 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y()); 1837 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
3062 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height()); 1838 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->getPaintHeight());
3063 1839
3064 double fundamental = getFrequencyForY(v, cursorPos.y()); 1840 double fundamental = getFrequencyForY(v, cursorPos.y());
3065 1841
3066 v->drawVisibleText(paint, 1842 PaintAssistant::drawVisibleText(v, paint,
3067 sw + 2, 1843 sw + 2,
3068 cursorPos.y() - 2, 1844 cursorPos.y() - 2,
3069 QString("%1 Hz").arg(fundamental), 1845 QString("%1 Hz").arg(fundamental),
3070 View::OutlinedText); 1846 PaintAssistant::OutlinedText);
3071 1847
3072 if (Pitch::isFrequencyInMidiRange(fundamental)) { 1848 if (Pitch::isFrequencyInMidiRange(fundamental)) {
3073 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); 1849 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
3074 v->drawVisibleText(paint, 1850 PaintAssistant::drawVisibleText(v, paint,
3075 sw + 2, 1851 sw + 2,
3076 cursorPos.y() + paint.fontMetrics().ascent() + 2, 1852 cursorPos.y() + paint.fontMetrics().ascent() + 2,
3077 pitchLabel, 1853 pitchLabel,
3078 View::OutlinedText); 1854 PaintAssistant::OutlinedText);
3079 } 1855 }
3080 1856
3081 sv_frame_t frame = v->getFrameForX(cursorPos.x()); 1857 sv_frame_t frame = v->getFrameForX(cursorPos.x());
3082 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); 1858 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
3083 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str()); 1859 QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
3084 QString frameLabel = QString("%1").arg(frame); 1860 QString frameLabel = QString("%1").arg(frame);
3085 v->drawVisibleText(paint, 1861 PaintAssistant::drawVisibleText(v, paint,
3086 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2, 1862 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
3087 v->height() - 2, 1863 v->getPaintHeight() - 2,
3088 frameLabel, 1864 frameLabel,
3089 View::OutlinedText); 1865 PaintAssistant::OutlinedText);
3090 v->drawVisibleText(paint, 1866 PaintAssistant::drawVisibleText(v, paint,
3091 cursorPos.x() + 2, 1867 cursorPos.x() + 2,
3092 v->height() - 2, 1868 v->getPaintHeight() - 2,
3093 rtLabel, 1869 rtLabel,
3094 View::OutlinedText); 1870 PaintAssistant::OutlinedText);
3095 1871
3096 int harmonic = 2; 1872 int harmonic = 2;
3097 1873
3098 while (harmonic < 100) { 1874 while (harmonic < 100) {
3099 1875
3100 int hy = int(lrint(getYForFrequency(v, fundamental * harmonic))); 1876 int hy = int(lrint(getYForFrequency(v, fundamental * harmonic)));
3101 if (hy < 0 || hy > v->height()) break; 1877 if (hy < 0 || hy > v->getPaintHeight()) break;
3102 1878
3103 int len = 7; 1879 int len = 7;
3104 1880
3105 if (harmonic % 2 == 0) { 1881 if (harmonic % 2 == 0) {
3106 if (harmonic % 4 == 0) { 1882 if (harmonic % 4 == 0) {
3120 1896
3121 paint.restore(); 1897 paint.restore();
3122 } 1898 }
3123 1899
3124 QString 1900 QString
3125 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const 1901 SpectrogramLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
3126 { 1902 {
3127 int x = pos.x(); 1903 int x = pos.x();
3128 int y = pos.y(); 1904 int y = pos.y();
3129 1905
3130 if (!m_model || !m_model->isOK()) return ""; 1906 if (!m_model || !m_model->isOK()) return "";
3145 haveValues = true; 1921 haveValues = true;
3146 } 1922 }
3147 1923
3148 QString adjFreqText = "", adjPitchText = ""; 1924 QString adjFreqText = "", adjPitchText = "";
3149 1925
3150 if (m_binDisplay == PeakFrequencies) { 1926 if (m_binDisplay == BinDisplay::PeakFrequencies) {
3151 1927
3152 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax, 1928 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
3153 adjFreqMin, adjFreqMax)) { 1929 adjFreqMin, adjFreqMax)) {
3154 return ""; 1930 return "";
3155 } 1931 }
3207 double dbMin = AudioLevel::multiplier_to_dB(magMin); 1983 double dbMin = AudioLevel::multiplier_to_dB(magMin);
3208 double dbMax = AudioLevel::multiplier_to_dB(magMax); 1984 double dbMax = AudioLevel::multiplier_to_dB(magMax);
3209 QString dbMinString; 1985 QString dbMinString;
3210 QString dbMaxString; 1986 QString dbMaxString;
3211 if (dbMin == AudioLevel::DB_FLOOR) { 1987 if (dbMin == AudioLevel::DB_FLOOR) {
3212 dbMinString = tr("-Inf"); 1988 dbMinString = Strings::minus_infinity;
3213 } else { 1989 } else {
3214 dbMinString = QString("%1").arg(lrint(dbMin)); 1990 dbMinString = QString("%1").arg(lrint(dbMin));
3215 } 1991 }
3216 if (dbMax == AudioLevel::DB_FLOOR) { 1992 if (dbMax == AudioLevel::DB_FLOOR) {
3217 dbMaxString = tr("-Inf"); 1993 dbMaxString = Strings::minus_infinity;
3218 } else { 1994 } else {
3219 dbMaxString = QString("%1").arg(lrint(dbMax)); 1995 dbMaxString = QString("%1").arg(lrint(dbMax));
3220 } 1996 }
3221 if (lrint(dbMin) != lrint(dbMax)) { 1997 if (lrint(dbMin) != lrint(dbMax)) {
3222 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString); 1998 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
3242 2018
3243 return cw; 2019 return cw;
3244 } 2020 }
3245 2021
3246 int 2022 int
3247 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const 2023 SpectrogramLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool detailed, QPainter &paint) const
3248 { 2024 {
3249 if (!m_model || !m_model->isOK()) return 0; 2025 if (!m_model || !m_model->isOK()) return 0;
3250 2026
3251 int cw = 0; 2027 int cw = 0;
3252 if (detailed) cw = getColourScaleWidth(paint); 2028 if (detailed) cw = getColourScaleWidth(paint);
3257 m_model->getSampleRate() / 2)); 2033 m_model->getSampleRate() / 2));
3258 2034
3259 int fw = paint.fontMetrics().width(tr("43Hz")); 2035 int fw = paint.fontMetrics().width(tr("43Hz"));
3260 if (tw < fw) tw = fw; 2036 if (tw < fw) tw = fw;
3261 2037
3262 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 2038 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
3263 2039
3264 return cw + tickw + tw + 13; 2040 return cw + tickw + tw + 13;
3265 } 2041 }
3266 2042
3267 void 2043 void
3268 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const 2044 SpectrogramLayer::paintVerticalScale(LayerGeometryProvider *v, bool detailed,
2045 QPainter &paint, QRect rect) const
3269 { 2046 {
3270 if (!m_model || !m_model->isOK()) { 2047 if (!m_model || !m_model->isOK()) {
3271 return; 2048 return;
3272 } 2049 }
3273 2050
3274 Profiler profiler("SpectrogramLayer::paintVerticalScale"); 2051 Profiler profiler("SpectrogramLayer::paintVerticalScale");
3275 2052
3276 //!!! cache this? 2053 //!!! cache this?
3277 2054
3278 int h = rect.height(), w = rect.width(); 2055 int h = rect.height(), w = rect.width();
3279 2056 int textHeight = paint.fontMetrics().height();
3280 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4); 2057
3281 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0); 2058 if (detailed && (h > textHeight * 3 + 10)) {
3282 2059 paintDetailedScale(v, paint, rect);
3283 int bins = m_fftSize / 2; 2060 }
2061 m_haveDetailedScale = detailed;
2062
2063 int tickw = (m_binScale == BinScale::Log ? 10 : 4);
2064 int pkw = (m_binScale == BinScale::Log ? 10 : 0);
2065
2066 int bins = getFFTSize() / 2;
3284 sv_samplerate_t sr = m_model->getSampleRate(); 2067 sv_samplerate_t sr = m_model->getSampleRate();
3285 2068
3286 if (m_maxFrequency > 0) { 2069 if (m_maxFrequency > 0) {
3287 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1); 2070 bins = int((double(m_maxFrequency) * getFFTSize()) / sr + 0.1);
3288 if (bins > m_fftSize / 2) bins = m_fftSize / 2; 2071 if (bins > getFFTSize() / 2) bins = getFFTSize() / 2;
3289 } 2072 }
3290 2073
3291 int cw = 0; 2074 int cw = 0;
3292
3293 if (detailed) cw = getColourScaleWidth(paint); 2075 if (detailed) cw = getColourScaleWidth(paint);
3294 int cbw = paint.fontMetrics().width("dB");
3295 2076
3296 int py = -1; 2077 int py = -1;
3297 int textHeight = paint.fontMetrics().height();
3298 int toff = -textHeight + paint.fontMetrics().ascent() + 2; 2078 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
3299 2079
3300 if (detailed && (h > textHeight * 3 + 10)) {
3301
3302 int topLines = 2;
3303 if (m_colourScale == PhaseColourScale) topLines = 1;
3304
3305 int ch = h - textHeight * (topLines + 1) - 8;
3306 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
3307 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3308
3309 QString top, bottom;
3310 double min = m_viewMags[v].getMin();
3311 double max = m_viewMags[v].getMax();
3312
3313 double dBmin = AudioLevel::multiplier_to_dB(min);
3314 double dBmax = AudioLevel::multiplier_to_dB(max);
3315
3316 if (dBmax < -60.f) dBmax = -60.f;
3317 else top = QString("%1").arg(lrint(dBmax));
3318
3319 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
3320 bottom = QString("%1").arg(lrint(dBmin));
3321
3322 //!!! & phase etc
3323
3324 if (m_colourScale != PhaseColourScale) {
3325 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
3326 2 + textHeight + toff, "dBFS");
3327 }
3328
3329 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
3330 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
3331 2 + textHeight * topLines + toff + textHeight/2, top);
3332
3333 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
3334 h + toff - 3 - textHeight/2, bottom);
3335
3336 paint.save();
3337 paint.setBrush(Qt::NoBrush);
3338
3339 int lasty = 0;
3340 int lastdb = 0;
3341
3342 for (int i = 0; i < ch; ++i) {
3343
3344 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
3345 int idb = int(dBval);
3346
3347 double value = AudioLevel::dB_to_multiplier(dBval);
3348 int colour = getDisplayValue(v, value * m_gain);
3349
3350 paint.setPen(m_palette.getColour((unsigned char)colour));
3351
3352 int y = textHeight * topLines + 4 + ch - i;
3353
3354 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
3355
3356 if (i == 0) {
3357 lasty = y;
3358 lastdb = idb;
3359 } else if (i < ch - paint.fontMetrics().ascent() &&
3360 idb != lastdb &&
3361 ((abs(y - lasty) > textHeight &&
3362 idb % 10 == 0) ||
3363 (abs(y - lasty) > paint.fontMetrics().ascent() &&
3364 idb % 5 == 0))) {
3365 paint.setPen(v->getBackground());
3366 QString text = QString("%1").arg(idb);
3367 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
3368 y + toff + textHeight/2, text);
3369 paint.setPen(v->getForeground());
3370 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
3371 lasty = y;
3372 lastdb = idb;
3373 }
3374 }
3375 paint.restore();
3376 }
3377
3378 paint.drawLine(cw + 7, 0, cw + 7, h); 2080 paint.drawLine(cw + 7, 0, cw + 7, h);
3379 2081
3380 int bin = -1; 2082 int bin = -1;
3381 2083
3382 for (int y = 0; y < v->height(); ++y) { 2084 for (int y = 0; y < v->getPaintHeight(); ++y) {
3383 2085
3384 double q0, q1; 2086 double q0, q1;
3385 if (!getYBinRange(v, v->height() - y, q0, q1)) continue; 2087 if (!getYBinRange(v, v->getPaintHeight() - y, q0, q1)) continue;
3386 2088
3387 int vy; 2089 int vy;
3388 2090
3389 if (int(q0) > bin) { 2091 if (int(q0) > bin) {
3390 vy = y; 2092 vy = y;
3391 bin = int(q0); 2093 bin = int(q0);
3392 } else { 2094 } else {
3393 continue; 2095 continue;
3394 } 2096 }
3395 2097
3396 int freq = int((sr * bin) / m_fftSize); 2098 int freq = int((sr * bin) / getFFTSize());
3397 2099
3398 if (py >= 0 && (vy - py) < textHeight - 1) { 2100 if (py >= 0 && (vy - py) < textHeight - 1) {
3399 if (m_frequencyScale == LinearFrequencyScale) { 2101 if (m_binScale == BinScale::Linear) {
3400 paint.drawLine(w - tickw, h - vy, w, h - vy); 2102 paint.drawLine(w - tickw, h - vy, w, h - vy);
3401 } 2103 }
3402 continue; 2104 continue;
3403 } 2105 }
3404 2106
3405 QString text = QString("%1").arg(freq); 2107 QString text = QString("%1").arg(freq);
3406 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC 2108 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
3407 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy); 2109 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
3408 2110
3409 if (h - vy - textHeight >= -2) { 2111 if (h - vy - textHeight >= -2) {
3410 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw); 2112 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw);
3411 paint.drawText(tx, h - vy + toff, text); 2113 paint.drawText(tx, h - vy + toff, text);
3412 } 2114 }
3413 2115
3414 py = vy; 2116 py = vy;
3415 } 2117 }
3416 2118
3417 if (m_frequencyScale == LogFrequencyScale) { 2119 if (m_binScale == BinScale::Log) {
3418 2120
3419 // piano keyboard 2121 // piano keyboard
3420 2122
3421 PianoScale().paintPianoVertical 2123 PianoScale().paintPianoVertical
3422 (v, paint, QRect(w - pkw - 1, 0, pkw, h), 2124 (v, paint, QRect(w - pkw - 1, 0, pkw, h),
3423 getEffectiveMinFrequency(), getEffectiveMaxFrequency()); 2125 getEffectiveMinFrequency(), getEffectiveMaxFrequency());
3424 } 2126 }
3425 2127
3426 m_haveDetailedScale = detailed; 2128 m_haveDetailedScale = detailed;
2129 }
2130
2131 void
2132 SpectrogramLayer::paintDetailedScale(LayerGeometryProvider *v,
2133 QPainter &paint, QRect rect) const
2134 {
2135 // The colour scale
2136
2137 if (m_colourScale == ColourScaleType::Phase) {
2138 paintDetailedScalePhase(v, paint, rect);
2139 return;
2140 }
2141
2142 int h = rect.height();
2143 int textHeight = paint.fontMetrics().height();
2144 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2145
2146 int cw = getColourScaleWidth(paint);
2147 int cbw = paint.fontMetrics().width("dB");
2148
2149 int topLines = 2;
2150
2151 int ch = h - textHeight * (topLines + 1) - 8;
2152 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
2153 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2154
2155 QString top, bottom;
2156 double min = m_viewMags[v->getId()].getMin();
2157 double max = m_viewMags[v->getId()].getMax();
2158
2159 if (min < m_threshold) min = m_threshold;
2160 if (max <= min) max = min + 0.1;
2161
2162 double dBmin = AudioLevel::multiplier_to_dB(min);
2163 double dBmax = AudioLevel::multiplier_to_dB(max);
2164
2165 #ifdef DEBUG_SPECTROGRAM_REPAINT
2166 cerr << "paintVerticalScale: for view id " << v->getId()
2167 << ": min = " << min << ", max = " << max
2168 << ", dBmin = " << dBmin << ", dBmax = " << dBmax << endl;
2169 #endif
2170
2171 if (dBmax < -60.f) dBmax = -60.f;
2172 else top = QString("%1").arg(lrint(dBmax));
2173
2174 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
2175 bottom = QString("%1").arg(lrint(dBmin));
2176
2177 #ifdef DEBUG_SPECTROGRAM_REPAINT
2178 cerr << "adjusted dB range to min = " << dBmin << ", max = " << dBmax
2179 << endl;
2180 #endif
2181
2182 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
2183 2 + textHeight + toff, "dBFS");
2184
2185 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2186 2 + textHeight * topLines + toff + textHeight/2, top);
2187
2188 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2189 h + toff - 3 - textHeight/2, bottom);
2190
2191 paint.save();
2192 paint.setBrush(Qt::NoBrush);
2193
2194 int lasty = 0;
2195 int lastdb = 0;
2196
2197 for (int i = 0; i < ch; ++i) {
2198
2199 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
2200 int idb = int(dBval);
2201
2202 double value = AudioLevel::dB_to_multiplier(dBval);
2203 paint.setPen(getRenderer(v)->getColour(value));
2204
2205 int y = textHeight * topLines + 4 + ch - i;
2206
2207 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2208
2209 if (i == 0) {
2210 lasty = y;
2211 lastdb = idb;
2212 } else if (i < ch - paint.fontMetrics().ascent() &&
2213 idb != lastdb &&
2214 ((abs(y - lasty) > textHeight &&
2215 idb % 10 == 0) ||
2216 (abs(y - lasty) > paint.fontMetrics().ascent() &&
2217 idb % 5 == 0))) {
2218 paint.setPen(v->getForeground());
2219 QString text = QString("%1").arg(idb);
2220 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
2221 y + toff + textHeight/2, text);
2222 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
2223 lasty = y;
2224 lastdb = idb;
2225 }
2226 }
2227 paint.restore();
2228 }
2229
2230 void
2231 SpectrogramLayer::paintDetailedScalePhase(LayerGeometryProvider *v,
2232 QPainter &paint, QRect rect) const
2233 {
2234 // The colour scale in phase mode
2235
2236 int h = rect.height();
2237 int textHeight = paint.fontMetrics().height();
2238 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2239
2240 int cw = getColourScaleWidth(paint);
2241
2242 // Phase is not measured in dB of course, but this places the
2243 // scale at the same position as in the magnitude spectrogram
2244 int cbw = paint.fontMetrics().width("dB");
2245
2246 int topLines = 1;
2247
2248 int ch = h - textHeight * (topLines + 1) - 8;
2249 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2250
2251 QString top = Strings::pi, bottom = Strings::minus_pi, middle = "0";
2252
2253 double min = -M_PI;
2254 double max = M_PI;
2255
2256 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2257 2 + textHeight * topLines + toff + textHeight/2, top);
2258
2259 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(middle),
2260 2 + textHeight * topLines + ch/2 + toff + textHeight/2, middle);
2261
2262 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2263 h + toff - 3 - textHeight/2, bottom);
2264
2265 paint.save();
2266 paint.setBrush(Qt::NoBrush);
2267
2268 for (int i = 0; i < ch; ++i) {
2269 double val = min + (((max - min) * i) / (ch - 1));
2270 paint.setPen(getRenderer(v)->getColour(val));
2271 int y = textHeight * topLines + 4 + ch - i;
2272 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2273 }
2274 paint.restore();
3427 } 2275 }
3428 2276
3429 class SpectrogramRangeMapper : public RangeMapper 2277 class SpectrogramRangeMapper : public RangeMapper
3430 { 2278 {
3431 public: 2279 public:
3488 { 2336 {
3489 if (!m_model) return 0; 2337 if (!m_model) return 0;
3490 2338
3491 sv_samplerate_t sr = m_model->getSampleRate(); 2339 sv_samplerate_t sr = m_model->getSampleRate();
3492 2340
3493 SpectrogramRangeMapper mapper(sr, m_fftSize); 2341 SpectrogramRangeMapper mapper(sr, getFFTSize());
3494 2342
3495 // int maxStep = mapper.getPositionForValue((double(sr) / m_fftSize) + 0.001); 2343 // int maxStep = mapper.getPositionForValue((double(sr) / getFFTSize()) + 0.001);
3496 int maxStep = mapper.getPositionForValue(0); 2344 int maxStep = mapper.getPositionForValue(0);
3497 int minStep = mapper.getPositionForValue(double(sr) / 2); 2345 int minStep = mapper.getPositionForValue(double(sr) / 2);
3498 2346
3499 int initialMax = m_initialMaxFrequency; 2347 int initialMax = m_initialMaxFrequency;
3500 if (initialMax == 0) initialMax = int(sr / 2); 2348 if (initialMax == 0) initialMax = int(sr / 2);
3512 if (!m_model) return 0; 2360 if (!m_model) return 0;
3513 2361
3514 double dmin, dmax; 2362 double dmin, dmax;
3515 getDisplayExtents(dmin, dmax); 2363 getDisplayExtents(dmin, dmax);
3516 2364
3517 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize); 2365 SpectrogramRangeMapper mapper(m_model->getSampleRate(), getFFTSize());
3518 int n = mapper.getPositionForValue(dmax - dmin); 2366 int n = mapper.getPositionForValue(dmax - dmin);
3519 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl; 2367 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
3520 return n; 2368 return n;
3521 } 2369 }
3522 2370
3529 // getDisplayExtents(dmin, dmax); 2377 // getDisplayExtents(dmin, dmax);
3530 2378
3531 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl; 2379 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl;
3532 2380
3533 sv_samplerate_t sr = m_model->getSampleRate(); 2381 sv_samplerate_t sr = m_model->getSampleRate();
3534 SpectrogramRangeMapper mapper(sr, m_fftSize); 2382 SpectrogramRangeMapper mapper(sr, getFFTSize());
3535 double newdist = mapper.getValueForPosition(step); 2383 double newdist = mapper.getValueForPosition(step);
3536 2384
3537 double newmin, newmax; 2385 double newmin, newmax;
3538 2386
3539 if (m_frequencyScale == LogFrequencyScale) { 2387 if (m_binScale == BinScale::Log) {
3540 2388
3541 // need to pick newmin and newmax such that 2389 // need to pick newmin and newmax such that
3542 // 2390 //
3543 // (log(newmin) + log(newmax)) / 2 == logmid 2391 // (log(newmin) + log(newmax)) / 2 == logmid
3544 // and 2392 // and
3590 2438
3591 RangeMapper * 2439 RangeMapper *
3592 SpectrogramLayer::getNewVerticalZoomRangeMapper() const 2440 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
3593 { 2441 {
3594 if (!m_model) return 0; 2442 if (!m_model) return 0;
3595 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize); 2443 return new SpectrogramRangeMapper(m_model->getSampleRate(), getFFTSize());
3596 } 2444 }
3597 2445
3598 void 2446 void
3599 SpectrogramLayer::updateMeasureRectYCoords(View *v, const MeasureRect &r) const 2447 SpectrogramLayer::updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const
3600 { 2448 {
3601 int y0 = 0; 2449 int y0 = 0;
3602 if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY)); 2450 if (r.startY > 0.0) y0 = int(getYForFrequency(v, r.startY));
3603 2451
3604 int y1 = y0; 2452 int y1 = y0;
3608 2456
3609 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0); 2457 r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
3610 } 2458 }
3611 2459
3612 void 2460 void
3613 SpectrogramLayer::setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const 2461 SpectrogramLayer::setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const
3614 { 2462 {
3615 if (start) { 2463 if (start) {
3616 r.startY = getFrequencyForY(v, y); 2464 r.startY = getFrequencyForY(v, y);
3617 r.endY = r.startY; 2465 r.endY = r.startY;
3618 } else { 2466 } else {
3646 "colourRotation=\"%5\" " 2494 "colourRotation=\"%5\" "
3647 "frequencyScale=\"%6\" " 2495 "frequencyScale=\"%6\" "
3648 "binDisplay=\"%7\" ") 2496 "binDisplay=\"%7\" ")
3649 .arg(m_minFrequency) 2497 .arg(m_minFrequency)
3650 .arg(m_maxFrequency) 2498 .arg(m_maxFrequency)
3651 .arg(m_colourScale) 2499 .arg(convertFromColourScale(m_colourScale, m_colourScaleMultiple))
3652 .arg(m_colourMap) 2500 .arg(m_colourMap)
3653 .arg(m_colourRotation) 2501 .arg(m_colourRotation)
3654 .arg(m_frequencyScale) 2502 .arg(int(m_binScale))
3655 .arg(m_binDisplay); 2503 .arg(int(m_binDisplay));
3656 2504
3657 s += QString("normalizeColumns=\"%1\" " 2505 // New-style normalization attributes, allowing for more types of
3658 "normalizeVisibleArea=\"%2\" " 2506 // normalization in future: write out the column normalization
3659 "normalizeHybrid=\"%3\" ") 2507 // type separately, and then whether we are normalizing visible
3660 .arg(m_normalizeColumns ? "true" : "false") 2508 // area as well afterwards
3661 .arg(m_normalizeVisibleArea ? "true" : "false") 2509
3662 .arg(m_normalizeHybrid ? "true" : "false"); 2510 s += QString("columnNormalization=\"%1\" ")
3663 2511 .arg(m_normalization == ColumnNormalization::Max1 ? "peak" :
2512 m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
2513
2514 // Old-style normalization attribute. We *don't* write out
2515 // normalizeHybrid here because the only release that would accept
2516 // it (Tony v1.0) has a totally different scale factor for
2517 // it. We'll just have to accept that session files from Tony
2518 // v2.0+ will look odd in Tony v1.0
2519
2520 s += QString("normalizeColumns=\"%1\" ")
2521 .arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false");
2522
2523 // And this applies to both old- and new-style attributes
2524
2525 s += QString("normalizeVisibleArea=\"%1\" ")
2526 .arg(m_normalizeVisibleArea ? "true" : "false");
2527
3664 Layer::toXml(stream, indent, extraAttributes + " " + s); 2528 Layer::toXml(stream, indent, extraAttributes + " " + s);
3665 } 2529 }
3666 2530
3667 void 2531 void
3668 SpectrogramLayer::setProperties(const QXmlAttributes &attributes) 2532 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
3705 if (ok) { 2569 if (ok) {
3706 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl; 2570 SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
3707 setMaxFrequency(maxFrequency); 2571 setMaxFrequency(maxFrequency);
3708 } 2572 }
3709 2573
3710 ColourScale colourScale = (ColourScale) 2574 auto colourScale = convertToColourScale
3711 attributes.value("colourScale").toInt(&ok); 2575 (attributes.value("colourScale").toInt(&ok));
3712 if (ok) setColourScale(colourScale); 2576 if (ok) {
2577 setColourScale(colourScale.first);
2578 setColourScaleMultiple(colourScale.second);
2579 }
3713 2580
3714 int colourMap = attributes.value("colourScheme").toInt(&ok); 2581 int colourMap = attributes.value("colourScheme").toInt(&ok);
3715 if (ok) setColourMap(colourMap); 2582 if (ok) setColourMap(colourMap);
3716 2583
3717 int colourRotation = attributes.value("colourRotation").toInt(&ok); 2584 int colourRotation = attributes.value("colourRotation").toInt(&ok);
3718 if (ok) setColourRotation(colourRotation); 2585 if (ok) setColourRotation(colourRotation);
3719 2586
3720 FrequencyScale frequencyScale = (FrequencyScale) 2587 BinScale binScale = (BinScale)
3721 attributes.value("frequencyScale").toInt(&ok); 2588 attributes.value("frequencyScale").toInt(&ok);
3722 if (ok) setFrequencyScale(frequencyScale); 2589 if (ok) setBinScale(binScale);
3723 2590
3724 BinDisplay binDisplay = (BinDisplay) 2591 BinDisplay binDisplay = (BinDisplay)
3725 attributes.value("binDisplay").toInt(&ok); 2592 attributes.value("binDisplay").toInt(&ok);
3726 if (ok) setBinDisplay(binDisplay); 2593 if (ok) setBinDisplay(binDisplay);
3727 2594
3728 bool normalizeColumns = 2595 bool haveNewStyleNormalization = false;
3729 (attributes.value("normalizeColumns").trimmed() == "true"); 2596
3730 setNormalizeColumns(normalizeColumns); 2597 QString columnNormalization = attributes.value("columnNormalization");
2598
2599 if (columnNormalization != "") {
2600
2601 haveNewStyleNormalization = true;
2602
2603 if (columnNormalization == "peak") {
2604 setNormalization(ColumnNormalization::Max1);
2605 } else if (columnNormalization == "hybrid") {
2606 setNormalization(ColumnNormalization::Hybrid);
2607 } else if (columnNormalization == "none") {
2608 setNormalization(ColumnNormalization::None);
2609 } else {
2610 cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
2611 << columnNormalization << "\"" << endl;
2612 }
2613 }
2614
2615 if (!haveNewStyleNormalization) {
2616
2617 bool normalizeColumns =
2618 (attributes.value("normalizeColumns").trimmed() == "true");
2619 if (normalizeColumns) {
2620 setNormalization(ColumnNormalization::Max1);
2621 }
2622
2623 bool normalizeHybrid =
2624 (attributes.value("normalizeHybrid").trimmed() == "true");
2625 if (normalizeHybrid) {
2626 setNormalization(ColumnNormalization::Hybrid);
2627 }
2628 }
3731 2629
3732 bool normalizeVisibleArea = 2630 bool normalizeVisibleArea =
3733 (attributes.value("normalizeVisibleArea").trimmed() == "true"); 2631 (attributes.value("normalizeVisibleArea").trimmed() == "true");
3734 setNormalizeVisibleArea(normalizeVisibleArea); 2632 setNormalizeVisibleArea(normalizeVisibleArea);
3735 2633
3736 bool normalizeHybrid = 2634 if (!haveNewStyleNormalization && m_normalization == ColumnNormalization::Hybrid) {
3737 (attributes.value("normalizeHybrid").trimmed() == "true"); 2635 // Tony v1.0 is (and hopefully will remain!) the only released
3738 setNormalizeHybrid(normalizeHybrid); 2636 // SV-a-like to use old-style attributes when saving sessions
3739 } 2637 // that ask for hybrid normalization. It saves them with the
3740 2638 // wrong gain factor, so hack in a fix for that here -- this
2639 // gives us backward but not forward compatibility.
2640 setGain(m_gain / float(getFFTSize() / 2));
2641 }
2642 }
2643