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" 29 #include "data/model/Dense3DModelPeakCache.h" 37 #include <QApplication> 38 #include <QMessageBox> 39 #include <QMouseEvent> 40 #include <QTextStream> 59 m_windowType(HanningWindow),
66 m_initialThreshold(0.0),
71 m_initialMaxFrequency(8000),
72 m_colourScale(dBColourScale),
74 m_frequencyScale(LinearFrequencyScale),
75 m_binDisplay(AllBins),
76 m_normalizeColumns(false),
77 m_normalizeVisibleArea(false),
78 m_normalizeHybrid(false),
79 m_lastEmittedZoomStep(-1),
81 m_haveDetailedScale(false),
82 m_lastPaintBlockWidth(0),
84 m_candidateFillStartFrame(0),
113 Preferences *prefs = Preferences::getInstance();
114 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
154 list.push_back(
"Colour");
155 list.push_back(
"Colour Scale");
156 list.push_back(
"Window Size");
157 list.push_back(
"Window Increment");
158 list.push_back(
"Normalize Columns");
159 list.push_back(
"Normalize Visible Area");
160 list.push_back(
"Bin Display");
161 list.push_back(
"Threshold");
162 list.push_back(
"Gain");
163 list.push_back(
"Colour Rotation");
166 list.push_back(
"Frequency Scale");
174 if (name ==
"Colour")
return tr(
"Colour");
175 if (name ==
"Colour Scale")
return tr(
"Colour Scale");
176 if (name ==
"Window Size")
return tr(
"Window Size");
177 if (name ==
"Window Increment")
return tr(
"Window Overlap");
178 if (name ==
"Normalize Columns")
return tr(
"Normalize Columns");
179 if (name ==
"Normalize Visible Area")
return tr(
"Normalize Visible Area");
180 if (name ==
"Bin Display")
return tr(
"Bin Display");
181 if (name ==
"Threshold")
return tr(
"Threshold");
182 if (name ==
"Gain")
return tr(
"Gain");
183 if (name ==
"Colour Rotation")
return tr(
"Colour Rotation");
184 if (name ==
"Min Frequency")
return tr(
"Min Frequency");
185 if (name ==
"Max Frequency")
return tr(
"Max Frequency");
186 if (name ==
"Frequency Scale")
return tr(
"Frequency Scale");
187 if (name ==
"Zero Padding")
return tr(
"Smoothing");
194 if (name ==
"Normalize Columns")
return "normalise-columns";
195 if (name ==
"Normalize Visible Area")
return "normalise";
202 if (name ==
"Gain")
return RangeProperty;
203 if (name ==
"Colour Rotation")
return RangeProperty;
204 if (name ==
"Normalize Columns")
return ToggleProperty;
205 if (name ==
"Normalize Visible Area")
return ToggleProperty;
206 if (name ==
"Threshold")
return RangeProperty;
207 if (name ==
"Zero Padding")
return ToggleProperty;
208 return ValueProperty;
214 if (name ==
"Bin Display" ||
215 name ==
"Frequency Scale")
return tr(
"Bins");
216 if (name ==
"Window Size" ||
217 name ==
"Window Increment" ||
218 name ==
"Zero Padding")
return tr(
"Window");
219 if (name ==
"Colour" ||
220 name ==
"Threshold" ||
221 name ==
"Colour Rotation")
return tr(
"Colour");
222 if (name ==
"Normalize Columns" ||
223 name ==
"Normalize Visible Area" ||
225 name ==
"Colour Scale")
return tr(
"Scale");
231 int *min,
int *max,
int *deflt)
const 235 int garbage0, garbage1, garbage2;
236 if (!min) min = &garbage0;
237 if (!max) max = &garbage1;
238 if (!deflt) deflt = &garbage2;
240 if (name ==
"Gain") {
246 if (*deflt < *min) *deflt = *min;
247 if (*deflt > *max) *deflt = *max;
249 val = lrintf(log10(
m_gain) * 20.0);
250 if (val < *min) val = *min;
251 if (val > *max) val = *max;
253 }
else if (name ==
"Threshold") {
259 if (*deflt < *min) *deflt = *min;
260 if (*deflt > *max) *deflt = *max;
262 val = lrintf(AudioLevel::multiplier_to_dB(
m_threshold));
263 if (val < *min) val = *min;
264 if (val > *max) val = *max;
266 }
else if (name ==
"Colour Rotation") {
274 }
else if (name ==
"Colour Scale") {
282 }
else if (name ==
"Colour") {
290 }
else if (name ==
"Window Size") {
298 while (ws > 32) { ws >>= 1; val ++; }
300 }
else if (name ==
"Window Increment") {
308 }
else if (name ==
"Zero Padding") {
316 }
else if (name ==
"Min Frequency") {
323 case 0:
default: val = 0;
break;
324 case 10: val = 1;
break;
325 case 20: val = 2;
break;
326 case 40: val = 3;
break;
327 case 100: val = 4;
break;
328 case 250: val = 5;
break;
329 case 500: val = 6;
break;
330 case 1000: val = 7;
break;
331 case 4000: val = 8;
break;
332 case 10000: val = 9;
break;
335 }
else if (name ==
"Max Frequency") {
342 case 500: val = 0;
break;
343 case 1000: val = 1;
break;
344 case 1500: val = 2;
break;
345 case 2000: val = 3;
break;
346 case 4000: val = 4;
break;
347 case 6000: val = 5;
break;
348 case 8000: val = 6;
break;
349 case 12000: val = 7;
break;
350 case 16000: val = 8;
break;
351 default: val = 9;
break;
354 }
else if (name ==
"Frequency Scale") {
361 }
else if (name ==
"Bin Display") {
368 }
else if (name ==
"Normalize Columns") {
373 }
else if (name ==
"Normalize Visible Area") {
379 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
389 if (name ==
"Colour") {
392 if (name ==
"Colour Scale") {
395 case 0:
return tr(
"Linear");
396 case 1:
return tr(
"Meter");
397 case 2:
return tr(
"dBV^2");
398 case 3:
return tr(
"dBV");
399 case 4:
return tr(
"Phase");
402 if (name ==
"Window Size") {
403 return QString(
"%1").arg(32 << value);
405 if (name ==
"Window Increment") {
408 case 0:
return tr(
"None");
409 case 1:
return tr(
"25 %");
410 case 2:
return tr(
"50 %");
411 case 3:
return tr(
"75 %");
412 case 4:
return tr(
"87.5 %");
413 case 5:
return tr(
"93.75 %");
416 if (name ==
"Zero Padding") {
417 if (value == 0)
return tr(
"None");
418 return QString(
"%1x").arg(value + 1);
420 if (name ==
"Min Frequency") {
423 case 0:
return tr(
"No min");
424 case 1:
return tr(
"10 Hz");
425 case 2:
return tr(
"20 Hz");
426 case 3:
return tr(
"40 Hz");
427 case 4:
return tr(
"100 Hz");
428 case 5:
return tr(
"250 Hz");
429 case 6:
return tr(
"500 Hz");
430 case 7:
return tr(
"1 KHz");
431 case 8:
return tr(
"4 KHz");
432 case 9:
return tr(
"10 KHz");
435 if (name ==
"Max Frequency") {
438 case 0:
return tr(
"500 Hz");
439 case 1:
return tr(
"1 KHz");
440 case 2:
return tr(
"1.5 KHz");
441 case 3:
return tr(
"2 KHz");
442 case 4:
return tr(
"4 KHz");
443 case 5:
return tr(
"6 KHz");
444 case 6:
return tr(
"8 KHz");
445 case 7:
return tr(
"12 KHz");
446 case 8:
return tr(
"16 KHz");
447 case 9:
return tr(
"No max");
450 if (name ==
"Frequency Scale") {
453 case 0:
return tr(
"Linear");
454 case 1:
return tr(
"Log");
457 if (name ==
"Bin Display") {
460 case 0:
return tr(
"All Bins");
461 case 1:
return tr(
"Peak Bins");
462 case 2:
return tr(
"Frequencies");
465 return tr(
"<unknown>");
471 if (name ==
"Gain") {
472 return new LinearRangeMapper(-50, 50, -25, 25, tr(
"dB"));
474 if (name ==
"Threshold") {
475 return new LinearRangeMapper(-50, 0, -50, 0, tr(
"dB"));
483 if (name ==
"Gain") {
484 setGain(pow(10,
float(value)/20.0));
485 }
else if (name ==
"Threshold") {
488 }
else if (name ==
"Colour Rotation") {
490 }
else if (name ==
"Colour") {
492 }
else if (name ==
"Window Size") {
494 }
else if (name ==
"Window Increment") {
496 }
else if (name ==
"Zero Padding") {
498 }
else if (name ==
"Min Frequency") {
517 }
else if (name ==
"Max Frequency") {
536 }
else if (name ==
"Colour Scale") {
545 }
else if (name ==
"Frequency Scale") {
551 }
else if (name ==
"Bin Display") {
558 }
else if (name ==
"Normalize Columns") {
560 }
else if (name ==
"Normalize Visible Area") {
570 i->second.validArea = QRect();
581 const View *v = i->first;
583 #ifdef DEBUG_SPECTROGRAM_REPAINT 584 SVDEBUG <<
"SpectrogramLayer::invalidateImageCaches(" 585 << startFrame <<
", " << endFrame <<
"): view range is " 589 cerr <<
"Valid area was: " << i->second.validArea.x() <<
", " 590 << i->second.validArea.y() <<
" " 591 << i->second.validArea.width() <<
"x" 592 << i->second.validArea.height() << endl;
597 #ifdef DEBUG_SPECTROGRAM_REPAINT 598 cerr <<
"Modified start frame is off right of view" << endl;
603 #ifdef DEBUG_SPECTROGRAM_REPAINT 604 SVDEBUG <<
"clipping from 0 to " << x-1 << endl;
607 i->second.validArea &=
608 QRect(0, 0, x-1, v->height());
610 i->second.validArea = QRect();
614 #ifdef DEBUG_SPECTROGRAM_REPAINT 615 cerr <<
"Modified end frame is off left of view" << endl;
620 #ifdef DEBUG_SPECTROGRAM_REPAINT 621 SVDEBUG <<
"clipping from " << x+1 <<
" to " << v->width()
624 if (x < v->width()) {
625 i->second.validArea &=
626 QRect(x+1, 0, v->width()-(x+1), v->height());
628 i->second.validArea = QRect();
632 #ifdef DEBUG_SPECTROGRAM_REPAINT 633 cerr <<
"Valid area is now: " << i->second.validArea.x() <<
", " 634 << i->second.validArea.y() <<
" " 635 << i->second.validArea.width() <<
"x" 636 << i->second.validArea.height() << endl;
644 SVDEBUG <<
"SpectrogramLayer::preferenceChanged(" << name <<
")" << endl;
646 if (name ==
"Window Type") {
650 if (name ==
"Spectrogram Y Smoothing") {
655 if (name ==
"Spectrogram X Smoothing") {
660 if (name ==
"Tuning Frequency") {
773 if (
m_gain == gain)
return;
854 if (r > 256) r = 256;
975 SVDEBUG <<
"SpectrogramLayer::setNormalizeVisibleArea(" << n
998 #ifdef DEBUG_SPECTROGRAM_REPAINT 999 SVDEBUG <<
"SpectrogramLayer::setLayerDormant(" << dormant <<
")" 1015 bool replaced =
false;
1016 for (ViewFFTMap::iterator i =
m_fftModels.begin();
1043 #ifdef DEBUG_SPECTROGRAM_REPAINT 1044 SVDEBUG <<
"SpectrogramLayer::cacheInvalid()" << endl;
1054 #ifdef DEBUG_SPECTROGRAM_REPAINT 1055 SVDEBUG <<
"SpectrogramLayer::cacheInvalid(" << from <<
", " << to <<
")" << endl;
1067 bool allDone =
true;
1069 #ifdef DEBUG_SPECTROGRAM_REPAINT 1070 SVDEBUG <<
"SpectrogramLayer::fillTimerTimedOut: have " <<
m_fftModels.size() <<
" FFT models associated with views" << endl;
1073 for (ViewFFTMap::iterator i =
m_fftModels.begin();
1076 const FFTModel *model = i->second.first;
1077 int lastFill = i->second.second;
1081 int fill = model->getFillExtent();
1083 #ifdef DEBUG_SPECTROGRAM_REPAINT 1084 SVDEBUG <<
"SpectrogramLayer::fillTimerTimedOut: extent for " << model <<
": " << fill <<
", last " << lastFill <<
", total " <<
m_model->getEndFrame() << endl;
1087 if (fill >= lastFill) {
1088 if (fill >=
m_model->getEndFrame() && lastFill > 0) {
1089 #ifdef DEBUG_SPECTROGRAM_REPAINT 1090 cerr <<
"complete!" << endl;
1093 i->second.second = -1;
1096 }
else if (fill > lastFill) {
1097 #ifdef DEBUG_SPECTROGRAM_REPAINT 1098 cerr <<
"SpectrogramLayer: emitting modelChanged(" 1099 << lastFill <<
"," << fill <<
")" << endl;
1102 i->second.second = fill;
1106 #ifdef DEBUG_SPECTROGRAM_REPAINT 1107 cerr <<
"SpectrogramLayer: going backwards, emitting modelChanged(" 1108 <<
m_model->getStartFrame() <<
"," <<
m_model->getEndFrame() <<
")" << endl;
1111 i->second.second = fill;
1115 if (i->second.second >= 0) {
1122 #ifdef DEBUG_SPECTROGRAM_REPAINT 1123 cerr <<
"SpectrogramLayer: all complete!" << endl;
1149 for (
int pixel = 1; pixel < 256; ++pixel) {
1165 QColor newPixels[256];
1169 for (
int pixel = 1; pixel < 256; ++pixel) {
1170 int target = pixel + distance;
1171 while (target < 1) target += 255;
1172 while (target > 255) target -= 255;
1176 for (
int pixel = 0; pixel < 256; ++pixel) {
1202 float thresh = -80.f;
1204 if (max == 0.f) max = 1.f;
1205 if (max == min) min = max - 0.0001f;
1211 value = int(((input - min) / (max - min)) * 255.f) + 1;
1215 value = AudioLevel::multiplier_to_preview
1216 ((input - min) / (max - min), 254) + 1;
1220 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1222 input = 10.f * log10f(input);
1227 thresh = 10.f * log10f(min * min);
1228 if (thresh < -80.f) thresh = -80.f;
1230 input = (input - thresh) / (-thresh);
1231 if (input < 0.f) input = 0.f;
1232 if (input > 1.f) input = 1.f;
1233 value = int(input * 255.f) + 1;
1240 input = (input - min) / (max - min);
1242 input = 10.f * log10f(input);
1247 thresh = 10.f * log10f(min);
1248 if (thresh < -80.f) thresh = -80.f;
1250 input = (input - thresh) / (-thresh);
1251 if (input < 0.f) input = 0.f;
1252 if (input > 1.f) input = 1.f;
1253 value = int(input * 255.f) + 1;
1257 value = int((input * 127.0 / M_PI) + 128);
1261 if (value > UCHAR_MAX) value = UCHAR_MAX;
1262 if (value < 0) value = 0;
1269 int sr =
m_model->getSampleRate();
1274 if (minbin < 1) minbin = 1;
1284 int sr =
m_model->getSampleRate();
1285 float maxf = float(sr) / 2;
1299 Profiler profiler(
"SpectrogramLayer::getYBinRange");
1301 int h = v->height();
1302 if (y < 0 || y >= h)
return false;
1304 int sr =
m_model->getSampleRate();
1325 Profiler profiler(
"SpectrogramLayer::getSmoothedYBinRange");
1327 int h = v->height();
1328 if (y < 0 || y >= h)
return false;
1330 int sr =
m_model->getSampleRate();
1351 int modelStart =
m_model->getStartFrame();
1352 int modelEnd =
m_model->getEndFrame();
1358 if (f1 <
int(modelStart) || f0 >
int(modelEnd)) {
1366 s0 = float(f0) / windowIncrement;
1367 s1 = float(f1) / windowIncrement;
1375 float s0 = 0, s1 = 0;
1378 int s0i = int(s0 + 0.001);
1382 int w0 = s0i * windowIncrement - (
m_windowSize - windowIncrement)/2;
1383 int w1 = s1i * windowIncrement + windowIncrement +
1386 min = RealTime::frame2RealTime(w0,
m_model->getSampleRate());
1387 max = RealTime::frame2RealTime(w1,
m_model->getSampleRate());
1395 float q0 = 0, q1 = 0;
1398 int q0i = int(q0 + 0.001);
1401 int sr =
m_model->getSampleRate();
1403 for (
int q = q0i; q <= q1i; ++q) {
1404 if (q == q0i) freqMin = (sr * q) /
m_fftSize;
1405 if (q == q1i) freqMax = (sr * (q+1)) /
m_fftSize;
1412 float &freqMin,
float &freqMax,
1413 float &adjFreqMin,
float &adjFreqMax)
1421 if (!fft)
return false;
1423 float s0 = 0, s1 = 0;
1426 float q0 = 0, q1 = 0;
1429 int s0i = int(s0 + 0.001);
1432 int q0i = int(q0 + 0.001);
1435 int sr =
m_model->getSampleRate();
1437 bool haveAdj =
false;
1442 for (
int q = q0i; q <= q1i; ++q) {
1444 for (
int s = s0i; s <= s1i; ++s) {
1446 if (!fft->isColumnAvailable(s))
continue;
1449 if (q == q0i) freqMin = binfreq;
1450 if (q == q1i) freqMax = binfreq;
1452 if (peaksOnly && !fft->isLocalPeak(s, q))
continue;
1456 float freq = binfreq;
1458 if (s <
int(fft->getWidth()) - 1) {
1460 fft->estimateStableFrequency(s, q, freq);
1462 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
1463 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
1471 adjFreqMin = adjFreqMax = 0.0;
1479 float &min,
float &max,
1480 float &phaseMin,
float &phaseMax)
const 1486 float q0 = 0, q1 = 0;
1489 float s0 = 0, s1 = 0;
1492 int q0i = int(q0 + 0.001);
1495 int s0i = int(s0 + 0.001);
1508 int cw = fft->getWidth();
1509 int ch = fft->getHeight();
1517 for (
int q = q0i; q <= q1i; ++q) {
1518 for (
int s = s0i; s <= s1i; ++s) {
1519 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1521 if (!fft->isColumnAvailable(s))
continue;
1525 value = fft->getPhaseAt(s, q);
1526 if (!have || value < phaseMin) { phaseMin = value; }
1527 if (!have || value > phaseMax) { phaseMax = value; }
1529 value = fft->getMagnitudeAt(s, q) / (
m_fftSize/2);
1530 if (!have || value < min) { min = value; }
1531 if (!have || value > max) { max = value; }
1553 Preferences::SpectrogramSmoothing smoothing =
1554 Preferences::getInstance()->getSpectrogramSmoothing();
1556 if (smoothing == Preferences::NoSpectrogramSmoothing ||
1557 smoothing == Preferences::SpectrogramInterpolated)
return 0;
1561 int sr =
m_model->getSampleRate();
1572 if (minbin < 1) minbin = 1;
1573 if (minbin >= maxbin) minbin = maxbin - 1;
1577 float(v->height()) /
1580 if (perPixel > 2.8) {
1582 }
else if (perPixel > 1.5) {
1604 #ifdef DEBUG_SPECTROGRAM_REPAINT 1605 SVDEBUG <<
"SpectrogramLayer::getFFTModel(" << v <<
"): Found null model" << endl;
1609 if (
m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
1610 #ifdef DEBUG_SPECTROGRAM_REPAINT 1611 SVDEBUG <<
"SpectrogramLayer::getFFTModel(" << v <<
"): Found a model with the wrong height (" <<
m_fftModels[v].first->getHeight() <<
", wanted " << (fftSize / 2 + 1) <<
")" << endl;
1618 #ifdef DEBUG_SPECTROGRAM_REPAINT 1619 SVDEBUG <<
"SpectrogramLayer::getFFTModel(" << v <<
"): Found a good model of height " <<
m_fftModels[v].first->getHeight() << endl;
1627 FFTModel *model =
new FFTModel(
m_model,
1634 StorageAdviser::SpeedCritical,
1637 if (!model->isOK()) {
1638 QMessageBox::critical
1639 (0, tr(
"FFT cache failed"),
1640 tr(
"Failed to create the FFT model for this spectrogram.\n" 1641 "There may be insufficient memory or disc space to continue."));
1648 #ifdef DEBUG_SPECTROGRAM 1649 cerr <<
"SpectrogramLayer: emitting sliceableModelReplaced(0, " << model <<
")" << endl;
1669 Dense3DModelPeakCache *
1692 for (ViewFFTMap::iterator i =
m_fftModels.begin();
1694 delete i->second.first;
1705 cerr <<
"SpectrogramLayer: emitting sliceableModelReplaced(" <<
m_sliceableModel <<
", 0)" << endl;
1715 for (std::vector<MagnitudeRange>::iterator i =
m_columnMags.begin();
1726 int x0 = 0, x1 = v->width();
1727 float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
1737 int s0 = int(std::min(s00, s10) + 0.0001);
1738 int s1 = int(std::max(s01, s11) + 0.0001);
1746 for (
int s = s0; s <= s1; ++s) {
1752 #ifdef DEBUG_SPECTROGRAM_REPAINT 1753 SVDEBUG <<
"SpectrogramLayer::updateViewMagnitudes returning from cols " 1754 << s0 <<
" -> " << s1 <<
" inclusive" << endl;
1757 if (!mag.
isSet())
return false;
1775 Profiler profiler(
"SpectrogramLayer::paint",
false);
1777 #ifdef DEBUG_SPECTROGRAM_REPAINT 1780 cerr <<
"rect is " << rect.x() <<
"," << rect.y() <<
" " << rect.width() <<
"x" << rect.height() << endl;
1792 SVDEBUG <<
"SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
1800 const_cast<SpectrogramLayer *>(
this)->Layer::setLayerDormant(v,
false);
1812 #ifdef DEBUG_SPECTROGRAM_REPAINT 1813 SVDEBUG <<
"SpectrogramLayer::paint(): image cache valid area " << cache.
1818 #ifdef DEBUG_SPECTROGRAM_REPAINT 1820 SVDEBUG <<
"SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl;
1826 int x1 = v->width();
1828 bool recreateWholeImageCache =
true;
1831 x1 = rect.right() + 1;
1839 int cw = cache.
image.width();
1840 int ch = cache.
image.height();
1842 if (
int(cache.
zoomLevel) == zoomLevel &&
1844 ch == v->height()) {
1851 #ifdef DEBUG_SPECTROGRAM_REPAINT 1852 cerr <<
"SpectrogramLayer: image cache good" << endl;
1865 #ifdef DEBUG_SPECTROGRAM_REPAINT 1866 cerr <<
"SpectrogramLayer: image cache partially OK" << endl;
1869 recreateWholeImageCache =
false;
1874 #ifdef DEBUG_SPECTROGRAM_REPAINT 1875 cerr <<
"SpectrogramLayer: dx = " << dx <<
" (image cache " << cw <<
"x" << ch <<
")" << endl;
1883 if (dxp < 0) dxp = -dxp;
1884 int copy = (cw - dxp) *
sizeof(QRgb);
1885 for (
int y = 0; y < ch; ++y) {
1886 QRgb *line = (QRgb *)cache.
image.scanLine(y);
1888 memmove(line, line + dxp,
copy);
1890 memmove(line + dxp, line,
copy);
1920 #ifdef DEBUG_SPECTROGRAM_REPAINT 1921 cerr <<
"valid area now " 1923 <<
" " << pw <<
"x" << cache.
validArea.height()
1931 }
else if (dx != 0) {
1935 #ifdef DEBUG_SPECTROGRAM_REPAINT 1936 cerr <<
"dx == " << dx <<
": scrolled too far for cache to be useful" << endl;
1940 recreateWholeImageCache =
true;
1944 #ifdef DEBUG_SPECTROGRAM_REPAINT 1945 cerr <<
"SpectrogramLayer: image cache useless" << endl;
1946 if (
int(cache.
zoomLevel) != zoomLevel) {
1947 cerr <<
"(cache zoomLevel " << cache.
zoomLevel 1948 <<
" != " << zoomLevel <<
")" << endl;
1950 if (cw != v->width()) {
1951 cerr <<
"(cache width " << cw
1952 <<
" != " << v->width();
1954 if (ch != v->height()) {
1955 cerr <<
"(cache height " << ch
1956 <<
" != " << v->height();
1965 #ifdef DEBUG_SPECTROGRAM_REPAINT 1966 cerr <<
"SpectrogramLayer: magnitude range changed to [" <<
m_viewMags[v].getMin() <<
"->" <<
m_viewMags[v].getMax() <<
"]" << endl;
1970 recreateWholeImageCache =
true;
1973 #ifdef DEBUG_SPECTROGRAM_REPAINT 1974 cerr <<
"No change in magnitude range [" <<
m_viewMags[v].getMin() <<
"->" <<
m_viewMags[v].getMax() <<
"]" << endl;
1978 if (recreateWholeImageCache) {
1984 (void)gettimeofday(&tv, 0);
1985 RealTime mainPaintStart = RealTime::fromTimeval(tv);
1990 if (paintBlockWidth < x1 - x0) {
1992 paintBlockWidth = x1 - x0;
1995 if (paintBlockWidth == 0) {
1996 paintBlockWidth = (300000 / zoomLevel);
1999 while (lastTime > RealTime::fromMilliseconds(200) &&
2000 paintBlockWidth > 50) {
2001 paintBlockWidth /= 2;
2002 lastTime = lastTime / 2;
2004 while (lastTime < RealTime::fromMilliseconds(90) &&
2005 paintBlockWidth < 1500) {
2006 paintBlockWidth *= 2;
2007 lastTime = lastTime * 2;
2011 if (paintBlockWidth < 20) paintBlockWidth = 20;
2014 #ifdef DEBUG_SPECTROGRAM_REPAINT 2024 int h = v->height();
2035 int vx0 = 0, vx1 = 0;
2039 #ifdef DEBUG_SPECTROGRAM_REPAINT 2040 cerr <<
"x0 " << x0 <<
", x1 " << x1 <<
", vx0 " << vx0 <<
", vx1 " << vx1 <<
", paintBlockWidth " << paintBlockWidth << endl;
2043 if (x0 + paintBlockWidth < vx0) {
2044 x0 = vx0 - paintBlockWidth;
2047 }
else if (x0 >= vx1) {
2049 if (x1 > x0 + paintBlockWidth) {
2050 x1 = x0 + paintBlockWidth;
2056 if (x0 + paintBlockWidth < x1) {
2057 x1 = x0 + paintBlockWidth;
2065 (std::min(vx0, x0), cache.
validArea.y(),
2066 std::max(vx1 - std::min(vx0, x0),
2067 x1 - std::min(vx0, x0)),
2070 #ifdef DEBUG_SPECTROGRAM_REPAINT 2071 cerr <<
"Valid area becomes " << cache.
validArea.x()
2078 if (x1 > x0 + paintBlockWidth) {
2081 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
2083 x1 = x0 + paintBlockWidth;
2085 int mid = (x1 + x0) / 2;
2086 x0 = mid - paintBlockWidth/2;
2087 x1 = x0 + paintBlockWidth;
2090 #ifdef DEBUG_SPECTROGRAM_REPAINT 2091 cerr <<
"Valid area becomes " << x0 <<
", 0, " << (x1-x0)
2092 <<
"x" << h << endl;
2094 cache.
validArea = QRect(x0, 0, x1 - x0, h);
2105 #ifdef DEBUG_SPECTROGRAM_REPAINT 2106 cerr <<
"x0 " << x0 <<
", x1 " << x1 <<
", w " << w <<
", h " << h << endl;
2109 int sr =
m_model->getSampleRate();
2132 if (minbin < 1) minbin = 1;
2133 if (minbin >= maxbin) minbin = maxbin - 1;
2137 minbin = minbin * zpl;
2138 maxbin = (maxbin + 1) * zpl - 1;
2140 float minFreq = (float(minbin) * sr) / fftSize;
2141 float maxFreq = (float(maxbin) * sr) / fftSize;
2143 float displayMinFreq = minFreq;
2144 float displayMaxFreq = maxFreq;
2167 bool overallMagChanged =
false;
2169 #ifdef DEBUG_SPECTROGRAM_REPAINT 2174 SVDEBUG <<
"*** NOTE: w == 0" << endl;
2177 #ifdef DEBUG_SPECTROGRAM_REPAINT 2181 Profiler outerprof(
"SpectrogramLayer::paint: all cols");
2196 bool bufferBinResolution =
false;
2197 if (increment > zoomLevel) bufferBinResolution =
true;
2199 int leftBoundaryFrame = -1, leftCropFrame = -1;
2200 int rightBoundaryFrame = -1, rightCropFrame = -1;
2204 if (bufferBinResolution) {
2206 for (
int x = x0; ; --x) {
2208 if ((f / increment) * increment == f) {
2209 if (leftCropFrame == -1) leftCropFrame = f;
2210 else if (x < x0 - 2) { leftBoundaryFrame = f;
break; }
2213 for (
int x = x0 + w; ; ++x) {
2215 if ((f / increment) * increment == f) {
2216 if (rightCropFrame == -1) rightCropFrame = f;
2217 else if (x > x0 + w + 2) { rightBoundaryFrame = f;
break; }
2220 #ifdef DEBUG_SPECTROGRAM_REPAINT 2221 cerr <<
"Left: crop: " << leftCropFrame <<
" (bin " << leftCropFrame/increment <<
"); boundary: " << leftBoundaryFrame <<
" (bin " << leftBoundaryFrame/increment <<
")" << endl;
2222 cerr <<
"Right: crop: " << rightCropFrame <<
" (bin " << rightCropFrame/increment <<
"); boundary: " << rightBoundaryFrame <<
" (bin " << rightBoundaryFrame/increment <<
")" << endl;
2225 bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment;
2233 int binforx[bufwid];
2236 int *binforx = (
int *)alloca(bufwid *
sizeof(
int));
2237 float *binfory = (
float *)alloca(h *
sizeof(
float));
2240 bool usePeaksCache =
false;
2242 if (bufferBinResolution) {
2243 for (
int x = 0; x < bufwid; ++x) {
2244 binforx[x] = (leftBoundaryFrame / increment) + x;
2247 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2249 for (
int x = 0; x < bufwid; ++x) {
2250 float s0 = 0, s1 = 0;
2252 binforx[x] = int(s0 + 0.0001);
2258 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2260 usePeaksCache = (increment * 8) < zoomLevel;
2265 for (
int pixel = 0; pixel < 256; ++pixel) {
2273 for (
int y = 0; y < h; ++y) {
2274 float q0 = 0, q1 = 0;
2284 overallMag, overallMagChanged);
2290 displayMinFreq, displayMaxFreq,
2292 overallMag, overallMagChanged);
2314 #ifdef DEBUG_SPECTROGRAM_REPAINT 2318 if (overallMagChanged) {
2320 #ifdef DEBUG_SPECTROGRAM_REPAINT 2321 cerr <<
"Overall mag is now [" <<
m_viewMags[v].getMin() <<
"->" <<
m_viewMags[v].getMax() <<
"] - will be updating" << endl;
2324 #ifdef DEBUG_SPECTROGRAM_REPAINT 2325 cerr <<
"Overall mag unchanged at [" <<
m_viewMags[v].getMin() <<
"->" <<
m_viewMags[v].getMax() <<
"]" << endl;
2331 Profiler profiler2(
"SpectrogramLayer::paint: draw image");
2333 if (recreateWholeImageCache) {
2334 #ifdef DEBUG_SPECTROGRAM_REPAINT 2335 SVDEBUG <<
"Recreating image cache: width = " << v->width()
2336 <<
", height = " << h << endl;
2338 cache.
image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied);
2342 #ifdef DEBUG_SPECTROGRAM_REPAINT 2343 SVDEBUG <<
"Painting " << w <<
"x" << h
2344 <<
" from draw buffer at " << 0 <<
"," << 0
2345 <<
" to " << w <<
"x" << h <<
" on cache at " 2346 << x0 <<
"," << 0 << endl;
2349 QPainter cachePainter(&cache.
image);
2351 if (bufferBinResolution) {
2354 #ifdef DEBUG_SPECTROGRAM_REPAINT 2355 SVDEBUG <<
"Rescaling image from " << bufwid
2356 <<
"x" << h <<
" to " 2357 << scaledRight-scaledLeft <<
"x" << h << endl;
2359 Preferences::SpectrogramXSmoothing xsmoothing =
2360 Preferences::getInstance()->getSpectrogramXSmoothing();
2363 (scaledRight - scaledLeft, h,
2364 Qt::IgnoreAspectRatio,
2365 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2366 Qt::SmoothTransformation : Qt::FastTransformation));
2369 #ifdef DEBUG_SPECTROGRAM_REPAINT 2370 SVDEBUG <<
"Drawing image region of width " << scaledRightCrop - scaledLeftCrop <<
" to " 2371 << scaledLeftCrop <<
" from " << scaledLeftCrop - scaledLeft << endl;
2373 cachePainter.drawImage
2374 (QRect(scaledLeftCrop, 0,
2375 scaledRightCrop - scaledLeftCrop, h),
2377 QRect(scaledLeftCrop - scaledLeft, 0,
2378 scaledRightCrop - scaledLeftCrop, h));
2380 cachePainter.drawImage(QRect(x0, 0, w, h),
2390 #ifdef DEBUG_SPECTROGRAM_REPAINT 2391 SVDEBUG <<
"Painting " << pr.width() <<
"x" << pr.height()
2392 <<
" from cache at " << pr.x() <<
"," << pr.y()
2393 <<
" to window" << endl;
2397 pr.x(), pr.y(), pr.width(), pr.height());
2410 #ifdef DEBUG_SPECTROGRAM_REPAINT 2411 SVDEBUG <<
"SpectrogramLayer::paint() updating left (0, " 2414 v->update(0, 0, cache.
validArea.x(), h);
2418 cache.
image.width()) {
2419 #ifdef DEBUG_SPECTROGRAM_REPAINT 2420 SVDEBUG <<
"SpectrogramLayer::paint() updating right (" 2435 cerr <<
"\noverallMagChanged - updating all\n" << endl;
2443 #ifdef DEBUG_SPECTROGRAM_REPAINT 2444 SVDEBUG <<
"SpectrogramLayer::paint() returning" << endl;
2449 (void)gettimeofday(&tv, 0);
2463 float displayMinFreq,
2464 float displayMaxFreq,
2467 bool &overallMagChanged)
const 2469 Profiler profiler(
"SpectrogramLayer::paintDrawBufferPeakFrequencies");
2471 #ifdef DEBUG_SPECTROGRAM_REPAINT 2472 cerr <<
"minbin " << minbin <<
", maxbin " << maxbin <<
"; w " << w <<
", h " << h << endl;
2474 if (minbin < 0) minbin = 0;
2475 if (maxbin < 0) maxbin = minbin+1;
2478 if (!fft)
return false;
2480 FFTModel::PeakSet peakfreqs;
2485 float values[maxbin - minbin + 1];
2487 float *values = (
float *)alloca((maxbin - minbin + 1) *
sizeof(float));
2490 for (
int x = 0; x < w; ++x) {
2492 if (binforx[x] < 0)
continue;
2494 int sx0 = binforx[x];
2496 if (x+1 < w) sx1 = binforx[x+1];
2497 if (sx0 < 0) sx0 = sx1 - 1;
2498 if (sx0 < 0)
continue;
2499 if (sx1 <= sx0) sx1 = sx0 + 1;
2501 for (
int sx = sx0; sx < sx1; ++sx) {
2503 if (sx < 0 || sx >=
int(fft->getWidth()))
continue;
2506 if (!fft->isColumnAvailable(sx)) {
2507 #ifdef DEBUG_SPECTROGRAM_REPAINT 2508 cerr <<
"Met unavailable column at col " << sx << endl;
2517 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2518 minbin, maxbin - 1);
2520 fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2522 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2524 fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2525 float max = fft->getMaximumMagnitudeAt(sx);
2527 for (
int i = minbin; i <= maxbin; ++i) {
2528 values[i - minbin] *= log10(max);
2532 fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2537 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
2538 pi != peakfreqs.end(); ++pi) {
2540 int bin = pi->first;
2541 int freq = pi->second;
2543 if (bin < minbin)
continue;
2544 if (bin > maxbin)
break;
2546 float value = values[bin - minbin];
2557 (freq, displayMinFreq, displayMaxFreq, logarithmic);
2559 int iy = int(y + 0.5);
2560 if (iy < 0 || iy >= h)
continue;
2567 #ifdef DEBUG_SPECTROGRAM 2568 cerr <<
"INTERNAL ERROR: " << sx <<
" >= " 2570 <<
" at SpectrogramLayer.cpp::paintDrawBuffer" 2575 if (overallMag.
sample(mag)) overallMagChanged =
true;
2592 bool &overallMagChanged)
const 2594 Profiler profiler(
"SpectrogramLayer::paintDrawBuffer");
2596 int minbin = int(binfory[0] + 0.0001);
2597 int maxbin = binfory[h-1];
2599 #ifdef DEBUG_SPECTROGRAM_REPAINT 2600 cerr <<
"minbin " << minbin <<
", maxbin " << maxbin <<
"; w " << w <<
", h " << h << endl;
2602 if (minbin < 0) minbin = 0;
2603 if (maxbin < 0) maxbin = minbin+1;
2605 DenseThreeDimensionalModel *sourceModel = 0;
2608 #ifdef DEBUG_SPECTROGRAM_REPAINT 2609 cerr <<
"Note: bin display = " <<
m_binDisplay <<
", w = " << w <<
", binforx[" << w-1 <<
"] = " << binforx[w-1] <<
", binforx[0] = " << binforx[0] << endl;
2611 if (usePeaksCache) {
2615 maxbin = sourceModel->getHeight();
2620 if (!sourceModel)
return false;
2622 bool interpolate =
false;
2623 Preferences::SpectrogramSmoothing smoothing =
2624 Preferences::getInstance()->getSpectrogramSmoothing();
2625 if (smoothing == Preferences::SpectrogramInterpolated ||
2626 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2636 float autoarray[maxbin - minbin + 1];
2639 float *autoarray = (
float *)alloca((maxbin - minbin + 1) *
sizeof(float));
2640 float *peaks = (
float *)alloca(h *
sizeof(
float));
2643 const float *values = autoarray;
2644 DenseThreeDimensionalModel::Column c;
2646 for (
int x = 0; x < w; ++x) {
2648 if (binforx[x] < 0)
continue;
2651 float columnMax = 0.f;
2653 int sx0 = binforx[x] / divisor;
2655 if (x+1 < w) sx1 = binforx[x+1] / divisor;
2656 if (sx0 < 0) sx0 = sx1 - 1;
2657 if (sx0 < 0)
continue;
2658 if (sx1 <= sx0) sx1 = sx0 + 1;
2660 for (
int y = 0; y < h; ++y) peaks[y] = 0.f;
2662 for (
int sx = sx0; sx < sx1; ++sx) {
2664 #ifdef DEBUG_SPECTROGRAM_REPAINT 2668 if (sx < 0 || sx >=
int(sourceModel->getWidth()))
continue;
2671 if (!sourceModel->isColumnAvailable(sx)) {
2672 #ifdef DEBUG_SPECTROGRAM_REPAINT 2673 cerr <<
"Met unavailable column at col " << sx << endl;
2683 #ifdef DEBUG_SPECTROGRAM_REPAINT 2684 SVDEBUG <<
"Retrieving column " << sx <<
" from fft directly" << endl;
2687 fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2689 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2691 fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2692 float max = fft->getMaximumMagnitudeAt(sx);
2693 for (
int i = minbin; i <= maxbin; ++i) {
2695 autoarray[i - minbin] *= log10(max);
2699 fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2702 #ifdef DEBUG_SPECTROGRAM_REPAINT 2703 SVDEBUG <<
"Retrieving column " << sx <<
" from peaks cache" << endl;
2705 c = sourceModel->getColumn(sx);
2707 for (
int y = 0; y < h; ++y) {
2708 if (c[y] > columnMax) columnMax = c[y];
2711 values = c.constData() + minbin;
2716 for (
int y = 0; y < h; ++y) {
2718 float sy0 = binfory[y];
2719 float sy1 = sy0 + 1;
2720 if (y+1 < h) sy1 = binfory[y+1];
2724 if (interpolate && fabsf(sy1 - sy0) < 1.f) {
2726 float centre = (sy0 + sy1) / 2;
2727 float dist = (centre - 0.5) - lrintf(centre - 0.5);
2728 int bin = int(centre);
2729 int other = (dist < 0 ? (bin-1) : (bin+1));
2730 if (bin < minbin) bin = minbin;
2731 if (bin > maxbin) bin = maxbin;
2732 if (other < minbin || other > maxbin) other = bin;
2733 float prop = 1.f - fabsf(dist);
2735 float v0 = values[bin - minbin];
2736 float v1 = values[other - minbin];
2738 if (bin == minbin || bin == maxbin ||
2739 v0 < values[bin-minbin-1] ||
2740 v0 < values[bin-minbin+1]) v0 = 0.f;
2741 if (other == minbin || other == maxbin ||
2742 v1 < values[other-minbin-1] ||
2743 v1 < values[other-minbin+1]) v1 = 0.f;
2745 if (v0 == 0.f && v1 == 0.f)
continue;
2746 value = prop * v0 + (1.f - prop) * v1;
2760 int by0 = int(sy0 + 0.0001);
2761 int by1 = int(sy1 + 0.0001);
2762 if (by1 < by0 + 1) by1 = by0 + 1;
2764 for (
int bin = by0; bin < by1; ++bin) {
2766 value = values[bin - minbin];
2768 if (bin == minbin || bin == maxbin ||
2769 value < values[bin-minbin-1] ||
2770 value < values[bin-minbin+1])
continue;
2781 if (value > peaks[y]) peaks[y] = value;
2788 #ifdef DEBUG_SPECTROGRAM 2789 cerr <<
"INTERNAL ERROR: " << sx <<
" >= " 2791 <<
" at SpectrogramLayer.cpp::paintDrawBuffer" 2796 if (overallMag.
sample(mag)) overallMagChanged =
true;
2801 for (
int y = 0; y < h; ++y) {
2803 float peak = peaks[y];
2810 peak *= log10(columnMax);
2826 Profiler profiler(
"SpectrogramLayer::illuminateLocalFeatures");
2842 int s0i = int(s0 + 0.001);
2858 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
2886 int completion =
m_fftModels[v].first->getCompletion();
2887 #ifdef DEBUG_SPECTROGRAM_REPAINT 2888 SVDEBUG <<
"SpectrogramLayer::getCompletion: completion = " << completion << endl;
2902 bool &logarithmic, QString &unit)
const 2906 int sr =
m_model->getSampleRate();
2908 max = float(sr) / 2;
2932 if (min < 0) min = 0;
2933 if (max >
m_model->getSampleRate()/2.f) max =
m_model->getSampleRate()/2.f;
2935 int minf = lrintf(min);
2936 int maxf = lrintf(max);
2959 float &value, QString &unit)
const 2972 int left = (frame / resolution) * resolution;
2973 int right = left + resolution;
2976 case SnapLeft: frame = left;
break;
2980 if (frame - left > right - frame) frame = right;
2993 cerr <<
"cache width: " << cache.
image.width() <<
", height: " 2994 << cache.
image.height() << endl;
2996 QImage image = cache.
image;
3000 if (rect.isValid()) {
3011 std::vector<QRect> &extents)
const 3013 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
3014 extents.push_back(vertical);
3016 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
3017 extents.push_back(horizontal);
3021 QRect freq(sw, cursorPos.y() -
paint.fontMetrics().ascent() - 2,
3022 paint.fontMetrics().width(
"123456 Hz") + 2,
3023 paint.fontMetrics().height());
3024 extents.push_back(freq);
3026 QRect pitch(sw, cursorPos.y() + 2,
3027 paint.fontMetrics().width(
"C#10+50c") + 2,
3028 paint.fontMetrics().height());
3029 extents.push_back(pitch);
3031 QRect rt(cursorPos.x(),
3032 v->height() -
paint.fontMetrics().height() - 2,
3033 paint.fontMetrics().width(
"1234.567 s"),
3034 paint.fontMetrics().height());
3035 extents.push_back(rt);
3037 int w(
paint.fontMetrics().width(
"1234567890") + 2);
3038 QRect frame(cursorPos.x() - w - 2,
3039 v->height() -
paint.fontMetrics().height() - 2,
3041 paint.fontMetrics().height());
3042 extents.push_back(frame);
3049 QPoint cursorPos)
const 3055 QFont fn =
paint.font();
3056 if (fn.pointSize() > 8) {
3057 fn.setPointSize(fn.pointSize() - 1);
3062 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
3063 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
3070 QString(
"%1 Hz").arg(fundamental),
3073 if (Pitch::isFrequencyInMidiRange(fundamental)) {
3074 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
3077 cursorPos.y() +
paint.fontMetrics().ascent() + 2,
3083 RealTime rt = RealTime::frame2RealTime(frame,
m_model->getSampleRate());
3084 QString rtLabel = QString(
"%1 s").arg(rt.toText(
true).c_str());
3085 QString frameLabel = QString(
"%1").arg(frame);
3087 cursorPos.x() -
paint.fontMetrics().width(frameLabel) - 2,
3099 while (harmonic < 100) {
3102 if (hy < 0 || hy > v->height())
break;
3106 if (harmonic % 2 == 0) {
3107 if (harmonic % 4 == 0) {
3114 paint.drawLine(cursorPos.x() - len,
3133 float magMin = 0, magMax = 0;
3134 float phaseMin = 0, phaseMax = 0;
3135 float freqMin = 0, freqMax = 0;
3136 float adjFreqMin = 0, adjFreqMax = 0;
3137 QString pitchMin, pitchMax;
3138 RealTime rtMin, rtMax;
3140 bool haveValues =
false;
3149 QString adjFreqText =
"", adjPitchText =
"";
3154 adjFreqMin, adjFreqMax)) {
3158 if (adjFreqMin != adjFreqMax) {
3159 adjFreqText = tr(
"Peak Frequency:\t%1 - %2 Hz\n")
3160 .arg(adjFreqMin).arg(adjFreqMax);
3162 adjFreqText = tr(
"Peak Frequency:\t%1 Hz\n")
3166 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
3167 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
3170 adjPitchText = tr(
"Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
3172 adjPitchText = tr(
"Peak Pitch:\t%2\n").arg(pmin);
3182 if (rtMin != rtMax) {
3183 text += tr(
"Time:\t%1 - %2\n")
3184 .arg(rtMin.toText(
true).c_str())
3185 .arg(rtMax.toText(
true).c_str());
3187 text += tr(
"Time:\t%1\n")
3188 .arg(rtMin.toText(
true).c_str());
3191 if (freqMin != freqMax) {
3192 text += tr(
"%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
3197 .arg(Pitch::getPitchLabelForFrequency(freqMin))
3198 .arg(Pitch::getPitchLabelForFrequency(freqMax));
3200 text += tr(
"%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
3204 .arg(Pitch::getPitchLabelForFrequency(freqMin));
3208 float dbMin = AudioLevel::multiplier_to_dB(magMin);
3209 float dbMax = AudioLevel::multiplier_to_dB(magMax);
3210 QString dbMinString;
3211 QString dbMaxString;
3212 if (dbMin == AudioLevel::DB_FLOOR) {
3213 dbMinString = tr(
"-Inf");
3215 dbMinString = QString(
"%1").arg(lrintf(dbMin));
3217 if (dbMax == AudioLevel::DB_FLOOR) {
3218 dbMaxString = tr(
"-Inf");
3220 dbMaxString = QString(
"%1").arg(lrintf(dbMax));
3222 if (lrintf(dbMin) != lrintf(dbMax)) {
3223 text += tr(
"dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
3225 text += tr(
"dB:\t%1").arg(dbMinString);
3227 if (phaseMin != phaseMax) {
3228 text += tr(
"\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
3230 text += tr(
"\nPhase:\t%1").arg(phaseMin);
3242 cw =
paint.fontMetrics().width(
"-80dB");
3255 int tw =
paint.fontMetrics().width(QString(
"%1")
3258 m_model->getSampleRate() / 2));
3260 int fw =
paint.fontMetrics().width(tr(
"43Hz"));
3261 if (tw < fw) tw = fw;
3265 return cw + tickw + tw + 13;
3275 Profiler profiler(
"SpectrogramLayer::paintVerticalScale");
3279 int h = rect.height(), w = rect.width();
3285 int sr =
m_model->getSampleRate();
3295 int cbw =
paint.fontMetrics().width(
"dB");
3298 int textHeight =
paint.fontMetrics().height();
3299 int toff = -textHeight +
paint.fontMetrics().ascent() + 2;
3301 if (detailed && (h > textHeight * 3 + 10)) {
3306 int ch = h - textHeight * (topLines + 1) - 8;
3308 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3310 QString top, bottom;
3314 float dBmin = AudioLevel::multiplier_to_dB(min);
3315 float dBmax = AudioLevel::multiplier_to_dB(max);
3317 if (dBmax < -60.f) dBmax = -60.f;
3318 else top = QString(
"%1").arg(lrintf(dBmax));
3320 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
3321 bottom = QString(
"%1").arg(lrintf(dBmin));
3326 paint.drawText((cw + 6 -
paint.fontMetrics().width(
"dBFS")) / 2,
3327 2 + textHeight + toff,
"dBFS");
3331 paint.drawText(3 + cw - cbw -
paint.fontMetrics().width(top),
3332 2 + textHeight * topLines + toff + textHeight/2, top);
3334 paint.drawText(3 + cw - cbw -
paint.fontMetrics().width(bottom),
3335 h + toff - 3 - textHeight/2, bottom);
3338 paint.setBrush(Qt::NoBrush);
3343 for (
int i = 0; i < ch; ++i) {
3345 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
3346 int idb = int(dBval);
3348 float value = AudioLevel::dB_to_multiplier(dBval);
3353 int y = textHeight * topLines + 4 + ch - i;
3355 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
3360 }
else if (i < ch -
paint.fontMetrics().ascent() &&
3362 ((abs(y - lasty) > textHeight &&
3364 (abs(y - lasty) >
paint.fontMetrics().ascent() &&
3367 QString text = QString(
"%1").arg(idb);
3368 paint.drawText(3 + cw - cbw -
paint.fontMetrics().width(text),
3369 y + toff + textHeight/2, text);
3371 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
3379 paint.drawLine(cw + 7, 0, cw + 7, h);
3383 for (
int y = 0; y < v->height(); ++y) {
3386 if (!
getYBinRange(v, v->height() - y, q0, q1))
continue;
3390 if (
int(q0) > bin) {
3399 if (py >= 0 && (vy - py) < textHeight - 1) {
3401 paint.drawLine(w - tickw, h - vy, w, h - vy);
3406 QString text = QString(
"%1").arg(freq);
3407 if (bin == 1) text = tr(
"%1Hz").arg(freq);
3408 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
3410 if (h - vy - textHeight >= -2) {
3411 int tx = w - 3 -
paint.fontMetrics().width(text) - std::max(tickw, pkw);
3412 paint.drawText(tx, h - vy + toff, text);
3423 (v,
paint, QRect(w - pkw - 1, 0, pkw, h),
3435 m_s2(sqrtf(sqrtf(2))) { }
3444 while (dist > (value + 0.00001) && dist > 0.1f) {
3467 while (n < position) {
3492 int sr =
m_model->getSampleRate();
3501 if (initialMax == 0) initialMax = sr / 2;
3507 return maxStep - minStep;
3534 int sr =
m_model->getSampleRate();
3538 float newmin, newmax;
3563 newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
3564 newmin = newmax - newdist;
3569 float dmid = (dmax + dmin) / 2;
3570 newmin = dmid - newdist / 2;
3571 newmax = dmid + newdist / 2;
3576 mmax = float(sr) / 2;
3578 if (newmin < mmin) {
3579 newmax += (mmin - newmin);
3582 if (newmax > mmax) {
3628 QString indent, QString extraAttributes)
const 3632 s += QString(
"channel=\"%1\" " 3633 "windowSize=\"%2\" " 3634 "windowHopLevel=\"%3\" " 3636 "threshold=\"%5\" ")
3643 s += QString(
"minFrequency=\"%1\" " 3644 "maxFrequency=\"%2\" " 3645 "colourScale=\"%3\" " 3646 "colourScheme=\"%4\" " 3647 "colourRotation=\"%5\" " 3648 "frequencyScale=\"%6\" " 3649 "binDisplay=\"%7\" ")
3658 s += QString(
"normalizeColumns=\"%1\" " 3659 "normalizeVisibleArea=\"%2\" " 3660 "normalizeHybrid=\"%3\" ")
3665 Layer::toXml(stream, indent, extraAttributes +
" " + s);
3673 int channel = attributes.value(
"channel").toInt(&ok);
3676 int windowSize = attributes.value(
"windowSize").toUInt(&ok);
3679 int windowHopLevel = attributes.value(
"windowHopLevel").toUInt(&ok);
3682 int windowOverlap = attributes.value(
"windowOverlap").toUInt(&ok);
3693 float gain = attributes.value(
"gain").toFloat(&ok);
3696 float threshold = attributes.value(
"threshold").toFloat(&ok);
3699 int minFrequency = attributes.value(
"minFrequency").toUInt(&ok);
3701 SVDEBUG <<
"SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
3705 int maxFrequency = attributes.value(
"maxFrequency").toUInt(&ok);
3707 SVDEBUG <<
"SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
3712 attributes.value(
"colourScale").toInt(&ok);
3715 int colourMap = attributes.value(
"colourScheme").toInt(&ok);
3718 int colourRotation = attributes.value(
"colourRotation").toInt(&ok);
3722 attributes.value(
"frequencyScale").toInt(&ok);
3726 attributes.value(
"binDisplay").toInt(&ok);
3729 bool normalizeColumns =
3730 (attributes.value(
"normalizeColumns").trimmed() ==
"true");
3733 bool normalizeVisibleArea =
3734 (attributes.value(
"normalizeVisibleArea").trimmed() ==
"true");
3737 bool normalizeHybrid =
3738 (attributes.value(
"normalizeHybrid").trimmed() ==
"true");
void paintPianoVertical(View *v, QPainter &paint, QRect rect, float minf, float maxf)
void setChannel(int)
Specify the channel to use from the source model.
ColourScale m_colourScale
virtual bool snapToFeatureFrame(View *v, int &frame, int &resolution, SnapType snap) const
Adjust the given frame to snap to the nearest feature, if possible.
int getFrameForX(int x) const
Return the closest frame to the given pixel x-coordinate.
void setColourRotation(int)
Specify the colourmap rotation for the colour scale.
void modelChangedWithin(int startFrame, int endFrame)
bool getNormalizeHybrid() const
int m_lastPaintBlockWidth
virtual void measureDoubleClick(View *, QMouseEvent *)
FFTModel * getFFTModel(const View *v) const
virtual bool hasLightBackground() const
void invalidateFFTModels()
bool paintDrawBuffer(View *v, int w, int h, int *binforx, float *binfory, bool usePeaksCache, MagnitudeRange &overallMag, bool &overallMagChanged) const
SpectrogramLayer(Configuration=FullRangeDb)
virtual const Model * getSliceableModel() const
void invalidateImageCaches()
virtual void setVerticalZoomStep(int)
Set the vertical zoom step.
void illuminateLocalFeatures(View *v, QPainter &painter) const
int m_lastEmittedZoomStep
PeakCacheMap m_peakCaches
void connectSignals(const Model *)
SpectrogramLayer represents waveform data (obtained from a DenseTimeValueModel) in spectrogram form.
void setColourScale(ColourScale)
Specify the scale for sample levels.
bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const
virtual void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const
Convert the layer's data (though not those of the model it refers to) into XML for file output.
virtual QColor getForeground() const
virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
float getYForFrequency(float frequency, float minFreq, float maxFreq, bool logarithmic) const
Return the pixel y-coordinate corresponding to a given frequency, if the frequency range is as specif...
virtual void paint(View *v, QPainter &paint, QRect rect) const
Paint the given rectangle of this layer onto the given view using the given painter,...
virtual void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const
Convert the layer's data (though not those of the model it refers to) into XML for file output.
WindowType getWindowType() const
int getFFTSize(const View *v) const
bool getNormalizeVisibleArea() const
virtual QString getUnit() const
virtual float getValueForPosition(int position) const
void setMaxFrequency(int)
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
ColourScale getColourScale() const
Dense3DModelPeakCache * getPeakCache(const View *v) const
virtual QString getPropertyGroupName(const PropertyName &) const
void addCommand(Command *command)
Add a command to the command history.
virtual int getCurrentVerticalZoomStep() const
Get the current vertical zoom step.
virtual void setProperty(const PropertyName &, int value)
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 invalidateMagnitudes()
bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const
static int getColourMapCount()
unsigned char getDisplayValue(View *v, float input) const
virtual PropertyList getProperties() const
void setWindowHopLevel(int level)
const DenseTimeValueModel * m_model
virtual void setLayerDormant(const View *v, bool dormant)
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
int getZoomLevel() const
Return the zoom level, i.e.
void setProperties(const QXmlAttributes &attributes)
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void setColourMap(int map)
virtual bool getYScaleValue(const View *, int, float &, QString &) const
Return the value and unit at the given y coordinate in the given view.
bool getAdjustedYBinSourceRange(View *v, int x, int y, float &freqMin, float &freqMax, float &adjFreqMin, float &adjFreqMax) const
virtual bool setDisplayExtents(float min, float max)
Set the displayed minimum and maximum values for the y axis to the given range, if supported.
bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max, float &phaseMin, float &phaseMax) const
virtual void setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const
int getZeroPadLevel() const
virtual void setLayerDormant(const View *v, bool dormant)
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
std::pair< FFTModel *, int > FFTFillPair
~SpectrogramRangeMapper()
bool getSmoothedYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const
int getStartFrame() const
Retrieve the first visible sample frame on the widget.
virtual int getPositionForValue(float value) const
virtual int getVerticalZoomSteps(int &defaultStep) const
Get the number of vertical zoom steps available for this layer.
virtual QString getFeatureDescription(View *v, QPoint &) const
float getEffectiveMaxFrequency() const
virtual PropertyType getPropertyType(const PropertyName &) const
void layerParametersChanged()
void setFrequencyScale(FrequencyScale)
Specify the scale for the y axis.
float getThreshold() const
virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
void verticalZoomChanged()
int getColourScaleWidth(QPainter &) const
int getMinFrequency() const
void sliceableModelReplaced(const Model *modelToBeReplaced, const Model *replacement)
int getEndFrame() const
Retrieve the last visible sample frame on the widget.
FrequencyScale getFrequencyScale() const
void preferenceChanged(PropertyContainer::PropertyName name)
virtual int getPositionForValueUnclamped(float value) const
A class for mapping intensity values onto various colour maps.
bool updateViewMagnitudes(View *v) const
QColor map(float value) const
static CommandHistory * getInstance()
virtual QString getError(View *v) const
Return an error string if any errors have occurred while loading or processing data for the given vie...
int m_initialMaxFrequency
void setZeroPadLevel(int level)
ImageCache covers the area of the view, at view resolution.
QColor getContrastingColour() const
virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const
QRect findRegionExtents(QImage *image, QPoint origin) const
bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const
virtual void paintCrosshairs(View *, QPainter &, QPoint) const
QImage m_drawBuffer
When painting, we draw directly onto the draw buffer and then copy this to the part of the image cach...
void setNormalizeHybrid(bool n)
Normalize each column to its maximum value, and then scale by the log of the (absolute) maximum value...
int getWindowSize() const
virtual bool getDisplayExtents(float &min, float &max) const
Return the minimum and maximum values within the displayed range for the y axis, if only a subset of ...
virtual QString getPropertyIconName(const PropertyName &) const
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
BinDisplay getBinDisplay() const
float getEffectiveMinFrequency() const
bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const
std::vector< MagnitudeRange > m_columnMags
virtual QString getPropertyLabel(const PropertyName &) const
void setNormalizeVisibleArea(bool n)
Normalize each value against the maximum in the visible region.
int getWindowHopLevel() const
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
virtual void copy(View *, Selection, Clipboard &)
int m_candidateFillStartFrame
bool hasLightBackground() const
virtual float getValueForPositionUnclamped(int position) const
float getFrequencyForY(const View *v, int y) const
void setGain(float gain)
Set the gain multiplier for sample values in this view.
virtual void drawVisibleText(QPainter &p, int x, int y, QString text, TextStyle style) const
int getWindowIncrement() const
int getMaxFrequency() const
bool paintDrawBufferPeakFrequencies(View *v, int w, int h, int *binforx, int minbin, int maxbin, float displayMinFreq, float displayMaxFreq, bool logarithmic, MagnitudeRange &overallMag, bool &overallMagChanged) const
virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const
virtual void setSynchronousPainting(bool synchronous)
Enable or disable synchronous painting.
float getYForFrequency(const View *v, float frequency) const
ViewImageCache m_imageCaches
FrequencyScale m_frequencyScale
void setMinFrequency(int)
float getFrequencyForY(int y, float minFreq, float maxFreq, bool logarithmic) const
Return the closest frequency to the given pixel y-coordinate, if the frequency range is as specified.
virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos, std::vector< QRect > &extents) const
SpectrogramRangeMapper(int sr, int)
QColor getColour(unsigned char index) const
virtual QColor getBackground() const
void setModel(const DenseTimeValueModel *model)
void setColour(unsigned char index, QColor colour)
void setWindowType(WindowType type)
virtual RangeMapper * getNewVerticalZoomRangeMapper() const
Create and return a range mapper for vertical zoom step values.
void setNormalizeColumns(bool n)
Normalize each column to its maximum value, independent of its neighbours.
bool getNormalizeColumns() const
virtual int getCompletion(View *v) const
Return the proportion of background work complete in drawing this view, as a percentage – in most cas...
void setBinDisplay(BinDisplay)
Specify the processing of frequency bins for the y axis.
virtual bool isLayerDormant(const View *v) const
Return whether the layer is dormant (i.e.
virtual RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const
int getXForFrame(int frame) const
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative).
void rotatePalette(int distance)
virtual bool getValueExtents(float &min, float &max, bool &logarithmic, QString &unit) const
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
static QString getColourMapName(int n)