19 #include "base/Profiler.h" 20 #include "base/AudioLevel.h" 21 #include "base/Window.h" 22 #include "base/Pitch.h" 23 #include "base/Preferences.h" 24 #include "base/RangeMapper.h" 25 #include "base/LogRange.h" 26 #include "base/ColumnOp.h" 27 #include "base/Strings.h" 28 #include "base/StorageAdviser.h" 29 #include "base/Exceptions.h" 31 #include "data/model/Dense3DModelPeakCache.h" 43 #include <QApplication> 44 #include <QMessageBox> 45 #include <QMouseEvent> 46 #include <QTextStream> 62 m_windowType(HanningWindow),
68 m_initialThreshold(1.0e-8f),
73 m_initialMaxFrequency(8000),
74 m_verticallyFixed(false),
76 m_colourScaleMultiple(1.0),
78 m_colourInverted(false),
81 m_normalization(ColumnNormalization::None),
82 m_normalizeVisibleArea(false),
83 m_lastEmittedZoomStep(-1),
85 m_haveDetailedScale(false),
89 QString colourConfigName =
"spectrogram-colour";
104 colourConfigName =
"spectrogram-melodic-colour";
117 colourConfigName =
"spectrogram-melodic-colour";
122 settings.beginGroup(
"Preferences");
123 setColourMap(settings.value(colourConfigName, colourConfigDefault).toInt());
126 Preferences *prefs = Preferences::getInstance();
127 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
174 ModelId exporter = ModelById::add
175 (std::make_shared<Colour3DPlotExporter>(sources, params));
189 ModelById::getAs<Colour3DPlotExporter>(exporterId)) {
190 exporter->discardSources();
192 ModelById::release(exporterId);
201 pair<ColourScaleType, double>
228 std::pair<ColumnNormalization, bool>
233 case 0:
return { ColumnNormalization::None,
false };
234 case 1:
return { ColumnNormalization::Max1,
false };
235 case 2:
return { ColumnNormalization::None,
true };
236 case 3:
return { ColumnNormalization::Hybrid,
false };
243 if (visible)
return 2;
245 case ColumnNormalization::None:
return 0;
246 case ColumnNormalization::Max1:
return 1;
247 case ColumnNormalization::Hybrid:
return 3;
249 case ColumnNormalization::Sum1:
250 case ColumnNormalization::Range01:
258 auto newModel = ModelById::getAs<DenseTimeValueModel>(modelId);
259 if (!modelId.isNone() && !newModel) {
260 throw std::logic_error(
"Not a DenseTimeValueModel");
263 if (modelId ==
m_model)
return;
271 connect(newModel.get(),
274 connect(newModel.get(),
276 this, SLOT(
cacheInvalid(ModelId, sv_frame_t, sv_frame_t)));
286 list.push_back(
"Colour");
287 list.push_back(
"Colour Scale");
288 list.push_back(
"Window Size");
289 list.push_back(
"Window Increment");
290 list.push_back(
"Oversampling");
291 list.push_back(
"Normalization");
292 list.push_back(
"Bin Display");
293 list.push_back(
"Threshold");
294 list.push_back(
"Gain");
295 list.push_back(
"Colour Rotation");
298 list.push_back(
"Frequency Scale");
305 if (name ==
"Colour")
return tr(
"Colour");
306 if (name ==
"Colour Scale")
return tr(
"Colour Scale");
307 if (name ==
"Window Size")
return tr(
"Window Size");
308 if (name ==
"Window Increment")
return tr(
"Window Overlap");
309 if (name ==
"Oversampling")
return tr(
"Oversampling");
310 if (name ==
"Normalization")
return tr(
"Normalization");
311 if (name ==
"Bin Display")
return tr(
"Bin Display");
312 if (name ==
"Threshold")
return tr(
"Threshold");
313 if (name ==
"Gain")
return tr(
"Gain");
314 if (name ==
"Colour Rotation")
return tr(
"Colour Rotation");
315 if (name ==
"Min Frequency")
return tr(
"Min Frequency");
316 if (name ==
"Max Frequency")
return tr(
"Max Frequency");
317 if (name ==
"Frequency Scale")
return tr(
"Frequency Scale");
330 if (name ==
"Gain")
return RangeProperty;
331 if (name ==
"Colour Rotation")
return RangeProperty;
332 if (name ==
"Threshold")
return RangeProperty;
333 if (name ==
"Colour")
return ColourMapProperty;
334 return ValueProperty;
340 if (name ==
"Bin Display" ||
341 name ==
"Frequency Scale")
return tr(
"Bins");
342 if (name ==
"Window Size" ||
343 name ==
"Window Increment" ||
344 name ==
"Oversampling")
return tr(
"Window");
345 if (name ==
"Colour" ||
346 name ==
"Threshold" ||
347 name ==
"Colour Rotation")
return tr(
"Colour");
348 if (name ==
"Normalization" ||
350 name ==
"Colour Scale")
return tr(
"Scale");
356 int *min,
int *max,
int *deflt)
const 360 int garbage0, garbage1, garbage2;
361 if (!min) min = &garbage0;
362 if (!max) max = &garbage1;
363 if (!deflt) deflt = &garbage2;
365 if (name ==
"Gain") {
371 if (*deflt < *min) *deflt = *min;
372 if (*deflt > *max) *deflt = *max;
374 val = int(lrint(log10(
m_gain) * 20.0));
375 if (val < *min) val = *min;
376 if (val > *max) val = *max;
378 }
else if (name ==
"Threshold") {
384 if (*deflt < *min) *deflt = *min;
385 if (*deflt > *max) *deflt = *max;
387 val = int(lrint(AudioLevel::multiplier_to_dB(
m_threshold)));
388 if (val < *min) val = *min;
389 if (val > *max) val = *max;
391 }
else if (name ==
"Colour Rotation") {
399 }
else if (name ==
"Colour Scale") {
408 }
else if (name ==
"Colour") {
416 }
else if (name ==
"Window Size") {
424 while (ws > 32) { ws >>= 1; val ++; }
426 }
else if (name ==
"Window Increment") {
434 }
else if (name ==
"Oversampling") {
442 while (ov > 1) { ov >>= 1; val ++; }
444 }
else if (name ==
"Min Frequency") {
451 case 0:
default: val = 0;
break;
452 case 10: val = 1;
break;
453 case 20: val = 2;
break;
454 case 40: val = 3;
break;
455 case 100: val = 4;
break;
456 case 250: val = 5;
break;
457 case 500: val = 6;
break;
458 case 1000: val = 7;
break;
459 case 4000: val = 8;
break;
460 case 10000: val = 9;
break;
463 }
else if (name ==
"Max Frequency") {
470 case 500: val = 0;
break;
471 case 1000: val = 1;
break;
472 case 1500: val = 2;
break;
473 case 2000: val = 3;
break;
474 case 4000: val = 4;
break;
475 case 6000: val = 5;
break;
476 case 8000: val = 6;
break;
477 case 12000: val = 7;
break;
478 case 16000: val = 8;
break;
479 default: val = 9;
break;
482 }
else if (name ==
"Frequency Scale") {
489 }
else if (name ==
"Bin Display") {
496 }
else if (name ==
"Normalization") {
505 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
515 if (name ==
"Colour") {
518 if (name ==
"Colour Scale") {
521 case 0:
return tr(
"Linear");
522 case 1:
return tr(
"Meter");
523 case 2:
return tr(
"dBV^2");
524 case 3:
return tr(
"dBV");
525 case 4:
return tr(
"Phase");
528 if (name ==
"Normalization") {
531 case 0:
return tr(
"None");
532 case 1:
return tr(
"Col");
533 case 2:
return tr(
"View");
534 case 3:
return tr(
"Hybrid");
538 if (name ==
"Window Size") {
539 return QString(
"%1").arg(32 << value);
541 if (name ==
"Window Increment") {
544 case 0:
return tr(
"None");
545 case 1:
return tr(
"25 %");
546 case 2:
return tr(
"50 %");
547 case 3:
return tr(
"75 %");
548 case 4:
return tr(
"87.5 %");
549 case 5:
return tr(
"93.75 %");
552 if (name ==
"Oversampling") {
555 case 0:
return tr(
"1x");
556 case 1:
return tr(
"2x");
557 case 2:
return tr(
"4x");
558 case 3:
return tr(
"8x");
561 if (name ==
"Min Frequency") {
564 case 0:
return tr(
"No min");
565 case 1:
return tr(
"10 Hz");
566 case 2:
return tr(
"20 Hz");
567 case 3:
return tr(
"40 Hz");
568 case 4:
return tr(
"100 Hz");
569 case 5:
return tr(
"250 Hz");
570 case 6:
return tr(
"500 Hz");
571 case 7:
return tr(
"1 KHz");
572 case 8:
return tr(
"4 KHz");
573 case 9:
return tr(
"10 KHz");
576 if (name ==
"Max Frequency") {
579 case 0:
return tr(
"500 Hz");
580 case 1:
return tr(
"1 KHz");
581 case 2:
return tr(
"1.5 KHz");
582 case 3:
return tr(
"2 KHz");
583 case 4:
return tr(
"4 KHz");
584 case 5:
return tr(
"6 KHz");
585 case 6:
return tr(
"8 KHz");
586 case 7:
return tr(
"12 KHz");
587 case 8:
return tr(
"16 KHz");
588 case 9:
return tr(
"No max");
591 if (name ==
"Frequency Scale") {
594 case 0:
return tr(
"Linear");
595 case 1:
return tr(
"Log");
598 if (name ==
"Bin Display") {
601 case 0:
return tr(
"All Bins");
602 case 1:
return tr(
"Peak Bins");
603 case 2:
return tr(
"Frequencies");
606 return tr(
"<unknown>");
613 if (name ==
"Normalization") {
616 case 0:
return "normalise-none";
617 case 1:
return "normalise-columns";
618 case 2:
return "normalise";
619 case 3:
return "normalise-hybrid";
628 if (name ==
"Gain") {
629 return new LinearRangeMapper(-50, 50, -25, 25, tr(
"dB"));
631 if (name ==
"Threshold") {
632 return new LinearRangeMapper(-81, -1, -81, -1, tr(
"dB"),
false,
633 { { -81, Strings::minus_infinity } });
641 if (name ==
"Gain") {
642 setGain(
float(pow(10,
float(value)/20.0)));
643 }
else if (name ==
"Threshold") {
645 else setThreshold(
float(AudioLevel::dB_to_multiplier(value)));
646 }
else if (name ==
"Colour Rotation") {
648 }
else if (name ==
"Colour") {
650 }
else if (name ==
"Window Size") {
652 }
else if (name ==
"Window Increment") {
654 }
else if (name ==
"Oversampling") {
656 }
else if (name ==
"Min Frequency") {
675 }
else if (name ==
"Max Frequency") {
694 }
else if (name ==
"Colour Scale") {
707 }
else if (name ==
"Frequency Scale") {
713 }
else if (name ==
"Bin Display") {
720 }
else if (name ==
"Normalization") {
730 #ifdef DEBUG_SPECTROGRAM 731 cerr <<
"SpectrogramLayer::invalidateRenderers called" << endl;
734 for (ViewRendererMap::iterator i =
m_renderers.begin();
744 SVDEBUG <<
"SpectrogramLayer::preferenceChanged(" << name <<
")" << endl;
746 if (name ==
"Window Type") {
750 if (name ==
"Spectrogram Y Smoothing") {
755 if (name ==
"Spectrogram X Smoothing") {
760 if (name ==
"Tuning Frequency") {
863 if (
m_gain == gain)
return;
902 throw std::logic_error(
"setMinFrequency called with value differing from the default, on SpectrogramLayer with verticallyFixed true");
927 throw std::logic_error(
"setMaxFrequency called with value differing from the default, on SpectrogramLayer with verticallyFixed true");
950 if (r > 256) r = 256;
954 m_colourRotation = r;
1095 #ifdef DEBUG_SPECTROGRAM_REPAINT 1096 cerr <<
"SpectrogramLayer::setLayerDormant(" << dormant <<
")" 1126 #ifdef DEBUG_SPECTROGRAM_REPAINT 1127 cerr <<
"SpectrogramLayer::cacheInvalid()" << endl;
1137 #ifdef DEBUG_SPECTROGRAM_REPAINT
1138 sv_frame_t from, sv_frame_t to
1140 sv_frame_t , sv_frame_t
1144 #ifdef DEBUG_SPECTROGRAM_REPAINT 1145 cerr <<
"SpectrogramLayer::cacheInvalid(" << from <<
", " << to <<
")" << endl;
1168 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1169 if (!model)
return 0.0;
1171 sv_samplerate_t sr = model->getSampleRate();
1176 if (minbin < 1) minbin = 1;
1186 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1187 if (!model)
return 0.0;
1189 sv_samplerate_t sr = model->getSampleRate();
1190 double maxf = double(sr) / 2;
1204 Profiler profiler(
"SpectrogramLayer::getYBinRange");
1206 if (y < 0 || y >= h)
return false;
1215 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1216 if (!model)
return 0.0;
1221 sv_samplerate_t sr = model->getSampleRate();
1233 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1234 if (!model)
return 0.0;
1236 sv_samplerate_t sr = model->getSampleRate();
1253 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1254 if (!model)
return false;
1256 sv_frame_t modelStart = model->getStartFrame();
1257 sv_frame_t modelEnd = model->getEndFrame();
1261 sv_frame_t f1 = v->
getFrameForX(x + 1) - modelStart - 1;
1263 if (f1 <
int(modelStart) || f0 >
int(modelEnd)) {
1271 s0 = double(f0) / windowIncrement;
1272 s1 = double(f1) / windowIncrement;
1280 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1281 if (!model)
return false;
1283 double s0 = 0, s1 = 0;
1286 int s0i = int(s0 + 0.001);
1290 int w0 = s0i * windowIncrement - (
m_windowSize - windowIncrement)/2;
1291 int w1 = s1i * windowIncrement + windowIncrement +
1294 min = RealTime::frame2RealTime(w0, model->getSampleRate());
1295 max = RealTime::frame2RealTime(w1, model->getSampleRate());
1303 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1304 if (!model)
return false;
1306 double q0 = 0, q1 = 0;
1309 int q0i = int(q0 + 0.001);
1312 sv_samplerate_t sr = model->getSampleRate();
1314 for (
int q = q0i; q <= q1i; ++q) {
1315 if (q == q0i) freqMin = (sr * q) /
getFFTSize();
1316 if (q == q1i) freqMax = (sr * (q+1)) /
getFFTSize();
1323 double &freqMin,
double &freqMax,
1324 double &adjFreqMin,
double &adjFreqMax)
1327 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1328 if (!model || !model->isOK() || !model->isReady()) {
1332 auto fft = ModelById::getAs<FFTModel>(
m_fftModel);
1333 if (!fft)
return false;
1335 double s0 = 0, s1 = 0;
1338 double q0 = 0, q1 = 0;
1341 int s0i = int(s0 + 0.001);
1344 int q0i = int(q0 + 0.001);
1347 sv_samplerate_t sr = model->getSampleRate();
1349 bool haveAdj =
false;
1354 for (
int q = q0i; q <= q1i; ++q) {
1356 for (
int s = s0i; s <= s1i; ++s) {
1358 double binfreq = (double(sr) * q) /
getFFTSize();
1359 if (q == q0i) freqMin = binfreq;
1360 if (q == q1i) freqMax = binfreq;
1362 if (peaksOnly && !fft->isLocalPeak(s, q))
continue;
1364 if (!fft->isOverThreshold
1369 double freq = binfreq;
1371 if (s <
int(fft->getWidth()) - 1) {
1373 fft->estimateStableFrequency(s, q, freq);
1375 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
1376 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
1384 adjFreqMin = adjFreqMax = 0.0;
1392 double &min,
double &max,
1393 double &phaseMin,
double &phaseMax)
const 1395 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1396 if (!model || !model->isOK() || !model->isReady()) {
1400 double q0 = 0, q1 = 0;
1403 double s0 = 0, s1 = 0;
1406 int q0i = int(q0 + 0.001);
1409 int s0i = int(s0 + 0.001);
1414 auto fft = ModelById::getAs<FFTModel>(
m_fftModel);
1418 int cw = fft->getWidth();
1419 int ch = fft->getHeight();
1427 for (
int q = q0i; q <= q1i; ++q) {
1428 for (
int s = s0i; s <= s1i; ++s) {
1429 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1433 value = fft->getPhaseAt(s, q);
1434 if (!have || value < phaseMin) { phaseMin = value; }
1435 if (!have || value > phaseMax) { phaseMax = value; }
1437 value = fft->getMagnitudeAt(s, q) / (
getFFTSize()/2.0);
1438 if (!have || value < min) { min = value; }
1439 if (!have || value > max) { max = value; }
1457 SVDEBUG <<
"SpectrogramLayer::recreateFFTModel called" << endl;
1460 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1461 if (!model || !model->isOK()) {
1469 auto newFFTModel = std::make_shared<FFTModel>(
m_model,
1476 if (!newFFTModel->isOK()) {
1477 QMessageBox::critical
1478 (
nullptr, tr(
"FFT cache failed"),
1479 tr(
"Failed to create the FFT model for this spectrogram.\n" 1480 "There may be insufficient memory or disc space to continue."));
1490 bool createWholeCache =
false;
1493 if (createWholeCache) {
1495 auto whole = std::make_shared<Dense3DModelPeakCache>(
m_fftModel, 1);
1498 auto peaks = std::make_shared<Dense3DModelPeakCache>(
m_fftModel,
1504 auto peaks = std::make_shared<Dense3DModelPeakCache>(
m_fftModel,
1512 bool *createWholeCache)
const 1514 *suggestedPeakDivisor = 8;
1515 *createWholeCache =
false;
1517 auto fftModel = ModelById::getAs<FFTModel>(
m_fftModel);
1518 if (!fftModel)
return;
1521 size_t(fftModel->getWidth()) *
1522 size_t(fftModel->getHeight()) *
1526 SVDEBUG <<
"Requesting advice from StorageAdviser on whether to create whole-model cache" << endl;
1532 StorageAdviser::Recommendation recommendation =
1533 StorageAdviser::recommend
1534 (StorageAdviser::Criteria(StorageAdviser::SpeedCritical |
1535 StorageAdviser::PrecisionCritical |
1536 StorageAdviser::FrequentLookupLikely),
1537 (sz / 8) / 1024, sz / 1024);
1538 if (recommendation & StorageAdviser::UseDisc) {
1539 SVDEBUG <<
"Seems inadvisable to create whole-model cache" << endl;
1540 }
else if (recommendation & StorageAdviser::ConserveSpace) {
1541 SVDEBUG <<
"Seems inadvisable to create whole-model cache but acceptable to use the slightly higher-resolution peak cache" << endl;
1542 *suggestedPeakDivisor = 4;
1544 SVDEBUG <<
"Seems fine to create whole-model cache" << endl;
1545 *createWholeCache =
true;
1547 }
catch (
const InsufficientDiscSpace &) {
1548 SVDEBUG <<
"Seems like a terrible idea to create whole-model cache" << endl;
1561 #ifdef DEBUG_SPECTROGRAM 1562 cerr <<
"SpectrogramLayer::invalidateMagnitudes called" << endl;
1576 int viewId = v->
getId();
1597 double minValue = 0.0f;
1598 double maxValue = 1.0f;
1608 if (maxValue <= minValue) {
1609 maxValue = minValue + 0.1f;
1636 Preferences::SpectrogramSmoothing smoothing =
1637 Preferences::getInstance()->getSpectrogramSmoothing();
1639 (smoothing != Preferences::NoSpectrogramSmoothing);
1657 MagnitudeRange magRange;
1658 int viewId = v->
getId();
1662 if (continuingPaint) {
1668 result = renderer->
render(v, paint, rect);
1674 #ifdef DEBUG_SPECTROGRAM_REPAINT 1675 cerr <<
"rect width from this paint: " << result.
rendered.width()
1676 <<
", mag range in this paint: " << result.
range.getMin() <<
" -> " 1677 << result.
range.getMax() << endl;
1681 if (uncached.width() > 0) {
1686 magRange.sample(result.
range);
1688 if (magRange.isSet()) {
1691 #ifdef DEBUG_SPECTROGRAM_REPAINT 1692 cerr <<
"mag range in this view has changed: " 1693 << magRange.getMin() <<
" -> " << magRange.getMax() << endl;
1700 #ifdef DEBUG_SPECTROGRAM_REPAINT 1701 cerr <<
"mag range has changed from last rendered range: re-rendering" 1713 Profiler profiler(
"SpectrogramLayer::paint",
false);
1715 #ifdef DEBUG_SPECTROGRAM_REPAINT 1716 cerr <<
"SpectrogramLayer::paint() entering: m_model is " <<
m_model <<
", zoom level is " << v->
getZoomLevel() << endl;
1718 cerr <<
"SpectrogramLayer::paint(): rect is " << rect.x() <<
"," << rect.y() <<
" " << rect.width() <<
"x" << rect.height() << endl;
1721 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1722 if (!model || !model->isOK() || !model->isReady()) {
1734 Profiler profiler(
"SpectrogramLayer::illuminateLocalFeatures");
1736 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1743 #ifdef DEBUG_SPECTROGRAM_REPAINT 1744 cerr <<
"SpectrogramLayer: illuminateLocalFeatures(" 1745 << localPos.x() <<
"," << localPos.y() <<
")" << endl;
1754 int s0i = int(s0 + 0.001);
1763 #ifdef DEBUG_SPECTROGRAM_REPAINT 1764 cerr <<
"SpectrogramLayer: illuminate " 1765 << x0 <<
"," << y1 <<
" -> " << x1 <<
"," << y0 << endl;
1772 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
1797 auto fftModel = ModelById::getAs<FFTModel>(
m_fftModel);
1798 if (!fftModel)
return 100;
1799 int completion = fftModel->getCompletion();
1800 #ifdef DEBUG_SPECTROGRAM_REPAINT 1801 cerr <<
"SpectrogramLayer::getCompletion: completion = " << completion << endl;
1809 auto fftModel = ModelById::getAs<FFTModel>(
m_fftModel);
1810 if (!fftModel)
return "";
1811 return fftModel->getError();
1816 bool &logarithmic, QString &unit)
const 1818 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1819 if (!model)
return false;
1821 sv_samplerate_t sr = model->getSampleRate();
1823 max = double(sr) / 2;
1843 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1844 if (!model)
return false;
1848 if (min < 0) min = 0;
1849 if (max > model->getSampleRate()/2.0) max = model->getSampleRate()/2.0;
1851 int minf = int(lrint(min));
1852 int maxf = int(lrint(max));
1861 throw std::logic_error(
"setDisplayExtents called with values differing from the defaults, on SpectrogramLayer with verticallyFixed true");
1880 double &value, QString &unit)
const 1894 sv_frame_t left = (frame / resolution) * resolution;
1895 sv_frame_t right = left + resolution;
1898 case SnapLeft: frame = left;
break;
1901 if (frame - left > right - frame) frame = right;
1913 if (!renderer)
return;
1916 if (rect.isValid()) {
1927 vector<QRect> &extents)
const 1932 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 1935 extents.push_back(vertical);
1937 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
1938 extents.push_back(horizontal);
1942 QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
1943 paint.fontMetrics().width(
"123456 Hz") + 2,
1944 paint.fontMetrics().height());
1945 extents.push_back(freq);
1947 QRect pitch(sw, cursorPos.y() + 2,
1948 paint.fontMetrics().width(
"C#10+50c") + 2,
1949 paint.fontMetrics().height());
1950 extents.push_back(pitch);
1952 QRect rt(cursorPos.x(),
1954 paint.fontMetrics().width(
"1234.567 s"),
1955 paint.fontMetrics().height());
1956 extents.push_back(rt);
1958 int w(paint.fontMetrics().width(
"1234567890") + 2);
1959 QRect frame(cursorPos.x() - w - 2,
1962 paint.fontMetrics().height());
1963 extents.push_back(frame);
1970 QPoint cursorPos)
const 1972 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
1979 QFont fn = paint.font();
1980 if (fn.pointSize() > 8) {
1981 fn.setPointSize(fn.pointSize() - 1);
1986 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
1987 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->
getPaintHeight());
1995 QString(
"%1 Hz").arg(fundamental),
1998 if (Pitch::isFrequencyInMidiRange(fundamental)) {
1999 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
2003 cursorPos.y() + paint.fontMetrics().ascent() + 2,
2009 RealTime rt = RealTime::frame2RealTime(frame, model->getSampleRate());
2010 QString rtLabel = QString(
"%1 s").arg(rt.toText(
true).c_str());
2011 QString frameLabel = QString(
"%1").arg(frame);
2014 cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
2027 while (harmonic < 100) {
2034 if (harmonic % 2 == 0) {
2035 if (harmonic % 4 == 0) {
2042 paint.drawLine(cursorPos.x() - len,
2059 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2060 if (!model || !model->isOK())
return "";
2062 double magMin = 0, magMax = 0;
2063 double phaseMin = 0, phaseMax = 0;
2064 double freqMin = 0, freqMax = 0;
2065 double adjFreqMin = 0, adjFreqMax = 0;
2066 QString pitchMin, pitchMax;
2067 RealTime rtMin, rtMax;
2069 bool haveValues =
false;
2078 QString adjFreqText =
"", adjPitchText =
"";
2083 adjFreqMin, adjFreqMax)) {
2087 if (adjFreqMin != adjFreqMax) {
2088 adjFreqText = tr(
"Peak Frequency:\t%1 - %2 Hz\n")
2089 .arg(adjFreqMin).arg(adjFreqMax);
2091 adjFreqText = tr(
"Peak Frequency:\t%1 Hz\n")
2095 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
2096 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
2099 adjPitchText = tr(
"Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
2101 adjPitchText = tr(
"Peak Pitch:\t%2\n").arg(pmin);
2111 if (rtMin != rtMax) {
2112 text += tr(
"Time:\t%1 - %2\n")
2113 .arg(rtMin.toText(
true).c_str())
2114 .arg(rtMax.toText(
true).c_str());
2116 text += tr(
"Time:\t%1\n")
2117 .arg(rtMin.toText(
true).c_str());
2120 if (freqMin != freqMax) {
2121 text += tr(
"%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
2126 .arg(Pitch::getPitchLabelForFrequency(freqMin))
2127 .arg(Pitch::getPitchLabelForFrequency(freqMax));
2129 text += tr(
"%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
2133 .arg(Pitch::getPitchLabelForFrequency(freqMin));
2137 double dbMin = AudioLevel::multiplier_to_dB(magMin);
2138 double dbMax = AudioLevel::multiplier_to_dB(magMax);
2139 QString dbMinString;
2140 QString dbMaxString;
2141 if (dbMin == AudioLevel::DB_FLOOR) {
2142 dbMinString = Strings::minus_infinity;
2144 dbMinString = QString(
"%1").arg(lrint(dbMin));
2146 if (dbMax == AudioLevel::DB_FLOOR) {
2147 dbMaxString = Strings::minus_infinity;
2149 dbMaxString = QString(
"%1").arg(lrint(dbMax));
2151 if (lrint(dbMin) != lrint(dbMax)) {
2152 text += tr(
"dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
2154 text += tr(
"dB:\t%1").arg(dbMinString);
2156 if (phaseMin != phaseMax) {
2157 text += tr(
"\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
2159 text += tr(
"\nPhase:\t%1").arg(phaseMin);
2171 cw = paint.fontMetrics().width(
"-80dB");
2179 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2180 if (!model || !model->isOK())
return 0;
2185 int tw = paint.fontMetrics().width(QString(
"%1")
2188 model->getSampleRate() / 2));
2190 int fw = paint.fontMetrics().width(tr(
"43Hz"));
2191 if (tw < fw) tw = fw;
2195 return cw + tickw + tw + 13;
2200 QPainter &
paint, QRect rect)
const 2202 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2203 if (!model || !model->isOK()) {
2207 Profiler profiler(
"SpectrogramLayer::paintVerticalScale");
2211 int h = rect.height(), w = rect.width();
2212 int textHeight = paint.fontMetrics().height();
2214 if (detailed && (h > textHeight * 3 + 10)) {
2223 sv_samplerate_t sr = model->getSampleRate();
2234 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2236 paint.drawLine(cw + 7, 0, cw + 7, h);
2247 if (
int(q0) > bin) {
2256 if (py >= 0 && (vy - py) < textHeight - 1) {
2258 paint.drawLine(w - tickw, h - vy, w, h - vy);
2263 QString text = QString(
"%1").arg(freq);
2264 if (bin == 1) text = tr(
"%1Hz").arg(freq);
2265 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
2267 if (h - vy - textHeight >= -2) {
2268 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw);
2269 paint.drawText(tx, h - vy + toff, text);
2280 (v, paint, QRect(w - pkw - 1, 0, pkw, h),
2289 QPainter &
paint, QRect rect)
const 2298 int h = rect.height();
2299 int textHeight = paint.fontMetrics().height();
2300 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2303 int cbw = paint.fontMetrics().width(
"dB");
2307 int ch = h - textHeight * (topLines + 1) - 8;
2309 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2311 QString top, bottom;
2316 if (max <= min) max = min + 0.1;
2318 double dBmin = AudioLevel::multiplier_to_dB(min);
2319 double dBmax = AudioLevel::multiplier_to_dB(max);
2321 #ifdef DEBUG_SPECTROGRAM_REPAINT 2322 cerr <<
"paintVerticalScale: for view id " << v->
getId()
2323 <<
": min = " << min <<
", max = " << max
2324 <<
", dBmin = " << dBmin <<
", dBmax = " << dBmax << endl;
2327 if (dBmax < -60.f) dBmax = -60.f;
2328 else top = QString(
"%1").arg(lrint(dBmax));
2330 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
2331 bottom = QString(
"%1").arg(lrint(dBmin));
2333 #ifdef DEBUG_SPECTROGRAM_REPAINT 2334 cerr <<
"adjusted dB range to min = " << dBmin <<
", max = " << dBmax
2338 paint.drawText((cw + 6 - paint.fontMetrics().width(
"dBFS")) / 2,
2339 2 + textHeight + toff,
"dBFS");
2341 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2342 2 + textHeight * topLines + toff + textHeight/2, top);
2344 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2345 h + toff - 3 - textHeight/2, bottom);
2348 paint.setBrush(Qt::NoBrush);
2353 for (
int i = 0; i < ch; ++i) {
2355 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
2356 int idb = int(dBval);
2358 double value = AudioLevel::dB_to_multiplier(dBval);
2361 int y = textHeight * topLines + 4 + ch - i;
2363 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2368 }
else if (i < ch - paint.fontMetrics().ascent() &&
2370 ((abs(y - lasty) > textHeight &&
2372 (abs(y - lasty) > paint.fontMetrics().ascent() &&
2375 QString text = QString(
"%1").arg(idb);
2376 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
2377 y + toff + textHeight/2, text);
2378 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
2388 QPainter &
paint, QRect rect)
const 2392 int h = rect.height();
2393 int textHeight = paint.fontMetrics().height();
2394 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2400 int cbw = paint.fontMetrics().width(
"dB");
2404 int ch = h - textHeight * (topLines + 1) - 8;
2405 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2407 QString top = Strings::pi, bottom = Strings::minus_pi, middle =
"0";
2412 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2413 2 + textHeight * topLines + toff + textHeight/2, top);
2415 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(middle),
2416 2 + textHeight * topLines + ch/2 + toff + textHeight/2, middle);
2418 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2419 h + toff - 3 - textHeight/2, bottom);
2422 paint.setBrush(Qt::NoBrush);
2424 for (
int i = 0; i < ch; ++i) {
2425 double val = min + (((max - min) * i) / (ch - 1));
2427 int y = textHeight * topLines + 4 + ch - i;
2428 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2438 m_s2(sqrt(sqrt(2))) { }
2443 double dist = m_dist;
2447 while (dist > (value + 0.00001) && dist > 0.1) {
2457 return getPositionForValue(value);
2467 double dist = m_dist;
2470 while (n < position) {
2480 return getValueForPosition(position);
2493 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2494 if (!model)
return 0;
2496 sv_samplerate_t sr = model->getSampleRate();
2505 if (initialMax == 0) initialMax = int(sr / 2);
2511 return maxStep - minStep;
2517 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2518 if (!model)
return 0;
2532 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2540 sv_samplerate_t sr = model->getSampleRate();
2544 double newmin, newmax;
2569 newmax = (newdist + sqrt(newdist*newdist + 4*dmin*dmax)) / 2;
2570 newmin = newmax - newdist;
2575 double dmid = (dmax + dmin) / 2;
2576 newmin = dmid - newdist / 2;
2577 newmax = dmid + newdist / 2;
2582 mmax = double(sr) / 2;
2584 if (newmin < mmin) {
2585 newmax += (mmin - newmin);
2588 if (newmax > mmax) {
2601 auto model = ModelById::getAs<DenseTimeValueModel>(
m_model);
2602 if (!model)
return nullptr;
2635 QString indent, QString extraAttributes)
const 2639 s += QString(
"channel=\"%1\" " 2640 "windowSize=\"%2\" " 2641 "windowHopLevel=\"%3\" " 2642 "oversampling=\"%4\" " 2644 "threshold=\"%6\" ")
2652 s += QString(
"minFrequency=\"%1\" " 2653 "maxFrequency=\"%2\" " 2654 "colourScale=\"%3\" " 2655 "colourRotation=\"%4\" " 2656 "frequencyScale=\"%5\" " 2657 "binDisplay=\"%6\" ")
2668 s += QString(
"colourMap=\"%1\" ")
2673 s += QString(
"colourScheme=\"%1\" ")
2681 s += QString(
"columnNormalization=\"%1\" ")
2691 s += QString(
"normalizeColumns=\"%1\" ")
2692 .arg(
m_normalization == ColumnNormalization::Max1 ?
"true" :
"false");
2696 s += QString(
"normalizeVisibleArea=\"%1\" ")
2699 Layer::toXml(stream, indent, extraAttributes +
" " + s);
2707 int channel = attributes.value(
"channel").toInt(&ok);
2710 int windowSize = attributes.value(
"windowSize").toUInt(&ok);
2713 int windowHopLevel = attributes.value(
"windowHopLevel").toUInt(&ok);
2716 int windowOverlap = attributes.value(
"windowOverlap").toUInt(&ok);
2727 int oversampling = attributes.value(
"oversampling").toUInt(&ok);
2730 float gain = attributes.value(
"gain").toFloat(&ok);
2733 float threshold = attributes.value(
"threshold").toFloat(&ok);
2736 int minFrequency = attributes.value(
"minFrequency").toUInt(&ok);
2738 SVDEBUG <<
"SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
2742 int maxFrequency = attributes.value(
"maxFrequency").toUInt(&ok);
2744 SVDEBUG <<
"SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
2749 (attributes.value(
"colourScale").toInt(&ok));
2755 QString colourMapId = attributes.value(
"colourMap");
2757 if (colourMap >= 0) {
2760 colourMap = attributes.value(
"colourScheme").toInt(&ok);
2766 int colourRotation = attributes.value(
"colourRotation").toInt(&ok);
2770 attributes.value(
"frequencyScale").toInt(&ok);
2774 attributes.value(
"binDisplay").toInt(&ok);
2777 bool haveNewStyleNormalization =
false;
2779 QString columnNormalization = attributes.value(
"columnNormalization");
2781 if (columnNormalization !=
"") {
2783 haveNewStyleNormalization =
true;
2785 if (columnNormalization ==
"peak") {
2787 }
else if (columnNormalization ==
"hybrid") {
2789 }
else if (columnNormalization ==
"none") {
2792 SVCERR <<
"NOTE: Unknown or unsupported columnNormalization attribute \"" 2793 << columnNormalization <<
"\"" << endl;
2797 if (!haveNewStyleNormalization) {
2799 bool normalizeColumns =
2800 (attributes.value(
"normalizeColumns").trimmed() ==
"true");
2801 if (normalizeColumns) {
2805 bool normalizeHybrid =
2806 (attributes.value(
"normalizeHybrid").trimmed() ==
"true");
2807 if (normalizeHybrid) {
2812 bool normalizeVisibleArea =
2813 (attributes.value(
"normalizeVisibleArea").trimmed() ==
"true");
2816 if (!haveNewStyleNormalization &&
m_normalization == ColumnNormalization::Hybrid) {
QString getPropertyValueLabel(const PropertyName &, int value) const override
void setChannel(int)
Specify the channel to use from the source model.
void setColourRotation(int)
Specify the colourmap rotation for the colour scale.
double threshold
Threshold below which every value is mapped to background pixel 0.
virtual bool isLayerDormant(const LayerGeometryProvider *v) const
Return whether the layer is dormant (i.e.
RangeMapper * getNewVerticalZoomRangeMapper() const override
Create and return a range mapper for vertical zoom step values.
void measureDoubleClick(LayerGeometryProvider *, QMouseEvent *) override
int getMaxFrequency() const
bool getYScaleValue(const LayerGeometryProvider *, int, double &, QString &) const override
Return the value and unit at the given y coordinate in the given view.
void setNormalizeVisibleArea(bool)
Specify whether to normalize the visible area.
bool isLayerScrollable(const LayerGeometryProvider *) const override
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
void cacheInvalid(ModelId)
bool setDisplayExtents(double min, double max) override
Set the displayed minimum and maximum values for the y axis to the given range, if supported...
SpectrogramLayer(Configuration=FullRangeDb)
Construct a SpectrogramLayer with default parameters appropriate for the given configuration.
ColumnNormalization normalization
Type of column normalization.
virtual QColor getForeground() const =0
const LayerGeometryProvider * provider
ColourScale colourScale
A complete ColourScale object by value, used for colour map conversion.
PropertyList getProperties() const override
double getYForBin(const LayerGeometryProvider *, double bin) const override
!! VerticalBinLayer methods. Note overlap with get*BinRange()
double getFrequencyForY(const LayerGeometryProvider *v, int y) const
void checkCacheSpace(int *suggestedPeakDivisor, bool *createWholeCache) const
int m_lastEmittedZoomStep
int getWindowSize() const
int getWindowIncrement() const
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Convert the layer's data (though not those of the model it refers to) into XML for file output...
static std::pair< ColourScaleType, double > convertToColourScale(int value)
void illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &painter) const
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
double getValueForPositionUnclamped(int position) const override
virtual ZoomLevel getZoomLevel() const =0
Return the zoom level, i.e.
RenderResult renderTimeConstrained(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Render the requested area using the given painter, obtaining geometry (e.g.
void updateMeasureRectYCoords(LayerGeometryProvider *v, const MeasureRect &r) const override
ViewMagMap m_lastRenderedMags
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
void setBinScale(BinScale)
Specify the scale for the y axis.
bool getXBinSourceRange(LayerGeometryProvider *v, int x, RealTime &timeMin, RealTime &timeMax) const
void paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
double getBinForY(const LayerGeometryProvider *, double y) const override
Return the bin number, possibly fractional, at the given y coordinate.
int getColourScaleWidth(QPainter &) const
void setMaxFrequency(int)
int getOversampling() const
double scaleFactor
Initial scale factor (e.g.
virtual double getFrequencyForY(double y, double minFreq, double maxFreq, bool logarithmic) const =0
Return the closest frequency to the given (maybe fractional) pixel y-coordinate, if the frequency ran...
Map values within a range onto a set of colours, with a given distribution (linear, log etc) and optional colourmap rotation.
ViewRendererMap m_renderers
bool getYBinRange(LayerGeometryProvider *v, int y, double &freqBinMin, double &freqBinMax) const
void setColourScaleMultiple(double)
Specify multiple factor for colour scale.
void addCommand(Command *command)
Add a command to the command history.
bool getXYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &min, double &max, double &phaseMin, double &phaseMax) const
bool m_normalizeVisibleArea
void setThreshold(float threshold)
Set the threshold for sample values to qualify for being shown in the FFT, in voltage units...
void setNormalization(ColumnNormalization)
Specify the normalization mode for individual columns.
void setModel(ModelId model)
virtual void setLayerDormant(const LayerGeometryProvider *v, bool dormant)
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
BinDisplay binDisplay
Selection of bins to include in the export.
int getMinFrequency() const
void invalidateMagnitudes()
double gain
Gain that is applied before thresholding, in the display, matching the ColourScale object parameters...
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
bool geometryChanged(const LayerGeometryProvider *v)
Return true if the provider's geometry differs from the cache, or if we are not using a cache...
void setProperty(const PropertyName &, int value) override
double maxValue
Maximum value in source range.
int colourMap
A colour map index as used by ColourMapper.
static int getColourMapCount()
Return the number of known colour maps.
void setVerticallyFixed()
Mark the spectrogram layer as having a fixed range in the vertical axis.
void setWindowHopLevel(int level)
static std::pair< ColumnNormalization, bool > convertToColumnNorm(int value)
double getEffectiveMinFrequency() const
void setColourMap(int map)
Specify the colour map.
static int getBackwardCompatibilityColourMap(int n)
Older versions of colour-handling code save and reload colour maps by numerical index and can't prope...
bool snapToFeatureFrame(LayerGeometryProvider *v, sv_frame_t &frame, int &resolution, SnapType snap, int ycoord) const override
Adjust the given frame to snap to the nearest feature, if possible.
void modelChanged(ModelId)
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
static QString getColourMapLabel(int n)
Return a human-readable label for the colour map with the given index.
int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const override
double getValueForPosition(int position) const override
void setVerticalZoomStep(int) override
Set the vertical zoom step.
ColourScaleType getColourScale() const
int colourRotation
Colourmap rotation, in the range 0-255.
bool interpolate
Whether to apply smoothing when rendering cells at more than one pixel per cell.
double gain
Gain to apply before thresholding, mapping, and clamping.
QRect rendered
The rect that was actually rendered.
double minValue
Minimum value in source range.
void setMeasureRectYCoord(LayerGeometryProvider *v, MeasureRect &r, bool start, int y) const override
void setOversampling(int oversampling)
double threshold
Threshold below which every value is mapped to background pixel 0 in the display, matching the Colour...
ModelId getExportModel(LayerGeometryProvider *) const override
Return the ID of a model representing the contents of this layer in a form suitable for export to a t...
bool hasLightBackground() const
Return true if the colour map is intended to be placed over a light background, false otherwise...
virtual double getYForFrequency(double frequency, double minFreq, double maxFreq, bool logarithmic) const =0
Return the (maybe fractional) pixel y-coordinate corresponding to a given frequency, if the frequency range is as specified.
virtual QRect getPaintRect() const =0
To be called from a layer, to obtain the extent of the surface that the layer is currently painting t...
bool hasLightBackground() const override
static int getColourMapById(QString id)
Return the index for the colour map with the given machine-readable id string, or -1 if the id is not...
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
double getYForFrequency(const LayerGeometryProvider *v, double frequency) const
void layerParametersChanged()
int getWindowHopLevel() const
void setSynchronousPainting(bool synchronous) override
Enable or disable synchronous painting.
void verticalZoomChanged()
ColumnNormalization m_normalization
RenderResult render(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Render the requested area using the given painter, obtaining geometry (e.g.
void paintDetailedScale(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
QString getError(LayerGeometryProvider *v) const override
Return an error string if any errors have occurred while loading or processing data for the given vie...
void paintPianoVertical(LayerGeometryProvider *v, QPainter &paint, QRect rect, double minf, double maxf)
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
bool getValueExtents(double &min, double &max, bool &logarithmic, QString &unit) const override
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
void preferenceChanged(PropertyContainer::PropertyName name)
QString getUnit() const override
std::vector< ModelId > m_exporters
A class for mapping intensity values onto various colour maps.
bool alwaysOpaque
Whether cells should always be opaque.
static CommandHistory * getInstance()
void connectSignals(ModelId)
virtual int getPaintHeight() const
int m_initialMaxFrequency
int getPositionForValue(double value) const override
bool getYBinSourceRange(LayerGeometryProvider *v, int y, double &freqMin, double &freqMax) const
void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const override
ColourScaleType m_colourScale
static int convertFromColourScale(ColourScaleType type, double multiple)
ColourScaleType scaleType
Distribution for the scale.
RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const override
SpectrogramRangeMapper(sv_samplerate_t sr, int)
double scaleFactor
Initial scale factor (e.g.
void paintDetailedScalePhase(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
QString getPropertyIconName(const PropertyName &) const override
ColumnNormalization normalization
Type of column normalization.
QString getPropertyGroupName(const PropertyName &) const override
bool invertVertical
Whether to render the whole caboodle upside-down.
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
BinDisplay getBinDisplay() const
void paintCrosshairs(LayerGeometryProvider *, QPainter &, QPoint) const override
double getEffectiveMaxFrequency() const
virtual void updatePaintRect(QRect r)=0
Colour3DPlotRenderer * getRenderer(LayerGeometryProvider *) const
BinScale getBinScale() const
QString getPropertyValueIconName(const PropertyName &, int value) const override
ColumnNormalization getNormalization() const
void setLayerDormant(const LayerGeometryProvider *v, bool dormant) override
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
void invalidateRenderers()
QRect getLargestUncachedRect(const LayerGeometryProvider *v)
Return the area of the largest rectangle within the entire area of the cache that is unavailable in t...
void setGain(float gain)
Set the gain multiplier for sample values in this view.
virtual void setMeasureRectFromPixrect(LayerGeometryProvider *v, MeasureRect &r, QRect pixrect) const
virtual int getId() const =0
Retrieve the id of this object.
const VerticalBinLayer * verticalBinLayer
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
void setColourScale(ColourScaleType)
Specify the scale for sample levels.
static QString getColourMapId(int n)
Return a machine-readable id string for the colour map with the given index.
QString getPropertyLabel(const PropertyName &) const override
int getCompletion(LayerGeometryProvider *v) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
MagnitudeRange range
The magnitude range of the data in the rendered area, after initial scaling (parameters.scaleFactor) and normalisation, for use in displaying colour scale etc.
int getPositionForValueUnclamped(double value) const override
void modelChangedWithin(ModelId, sv_frame_t startFrame, sv_frame_t endFrame)
ModelId getSliceableModel() const override
void setMinFrequency(int)
QColor getContrastingColour() const
Return a colour that contrasts somewhat with the colours in the map, so as to be used for cursors etc...
double m_colourScaleMultiple
~SpectrogramRangeMapper() override
const VerticalBinLayer * verticalBinLayer
float getThreshold() const
double getColourScaleMultiple() const
bool getAdjustedYBinSourceRange(LayerGeometryProvider *v, int x, int y, double &freqMin, double &freqMax, double &adjFreqMin, double &adjFreqMax) const
void setWindowType(WindowType type)
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
bool getXBinRange(LayerGeometryProvider *v, int x, double &windowMin, double &windowMax) const
std::vector< ModelId > peakCaches
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
double multiple
Multiple to apply after thresholding and mapping.
BinScale binScale
Scale for vertical bin spacing (linear or logarithmic).
bool getCrosshairExtents(LayerGeometryProvider *, QPainter &, QPoint cursorPos, std::vector< QRect > &extents) const override
BinDisplay binDisplay
Selection of bins to display.
void deleteDerivedModels()
bool getNormalizeVisibleArea() const
void setBinDisplay(BinDisplay)
Specify the processing of frequency bins for the y axis.
WindowType getWindowType() const
QRect findSimilarRegionExtents(QPoint point) const
Return the enclosing rectangle for the region of similar colour to the given point within the cache...
static int convertFromColumnNorm(ColumnNormalization norm, bool visible)
PropertyType getPropertyType(const PropertyName &) const override