comparison layer/SpectrogramLayer.cpp @ 1148:c0d841cb8ab9 tony-2.0-integration

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