comparison layer/SpectrogramLayer.cpp @ 1086:163cb9b98104 spectrogram-minor-refactor

Simplify the oversampling/zero-padding logic. FFT model selection no longer depends on the view.
author Chris Cannam
date Fri, 01 Jul 2016 18:30:42 +0100
parents 179ea8a2f650
children 6d990a24ac1b
comparison
equal deleted inserted replaced
1085:179ea8a2f650 1086:163cb9b98104
59 m_model(0), 59 m_model(0),
60 m_channel(0), 60 m_channel(0),
61 m_windowSize(1024), 61 m_windowSize(1024),
62 m_windowType(HanningWindow), 62 m_windowType(HanningWindow),
63 m_windowHopLevel(2), 63 m_windowHopLevel(2),
64 m_zeroPadLevel(0),
65 m_fftSize(1024), 64 m_fftSize(1024),
66 m_gain(1.0), 65 m_gain(1.0),
67 m_initialGain(1.0), 66 m_initialGain(1.0),
68 m_threshold(0.0), 67 m_threshold(0.0),
69 m_initialThreshold(0.0), 68 m_initialThreshold(0.0),
169 list.push_back("Gain"); 168 list.push_back("Gain");
170 list.push_back("Colour Rotation"); 169 list.push_back("Colour Rotation");
171 // list.push_back("Min Frequency"); 170 // list.push_back("Min Frequency");
172 // list.push_back("Max Frequency"); 171 // list.push_back("Max Frequency");
173 list.push_back("Frequency Scale"); 172 list.push_back("Frequency Scale");
174 //// list.push_back("Zero Padding");
175 return list; 173 return list;
176 } 174 }
177 175
178 QString 176 QString
179 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const 177 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
188 if (name == "Gain") return tr("Gain"); 186 if (name == "Gain") return tr("Gain");
189 if (name == "Colour Rotation") return tr("Colour Rotation"); 187 if (name == "Colour Rotation") return tr("Colour Rotation");
190 if (name == "Min Frequency") return tr("Min Frequency"); 188 if (name == "Min Frequency") return tr("Min Frequency");
191 if (name == "Max Frequency") return tr("Max Frequency"); 189 if (name == "Max Frequency") return tr("Max Frequency");
192 if (name == "Frequency Scale") return tr("Frequency Scale"); 190 if (name == "Frequency Scale") return tr("Frequency Scale");
193 if (name == "Zero Padding") return tr("Smoothing");
194 return ""; 191 return "";
195 } 192 }
196 193
197 QString 194 QString
198 SpectrogramLayer::getPropertyIconName(const PropertyName &) const 195 SpectrogramLayer::getPropertyIconName(const PropertyName &) const
204 SpectrogramLayer::getPropertyType(const PropertyName &name) const 201 SpectrogramLayer::getPropertyType(const PropertyName &name) const
205 { 202 {
206 if (name == "Gain") return RangeProperty; 203 if (name == "Gain") return RangeProperty;
207 if (name == "Colour Rotation") return RangeProperty; 204 if (name == "Colour Rotation") return RangeProperty;
208 if (name == "Threshold") return RangeProperty; 205 if (name == "Threshold") return RangeProperty;
209 if (name == "Zero Padding") return ToggleProperty;
210 return ValueProperty; 206 return ValueProperty;
211 } 207 }
212 208
213 QString 209 QString
214 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 210 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
215 { 211 {
216 if (name == "Bin Display" || 212 if (name == "Bin Display" ||
217 name == "Frequency Scale") return tr("Bins"); 213 name == "Frequency Scale") return tr("Bins");
218 if (name == "Window Size" || 214 if (name == "Window Size" ||
219 name == "Window Increment" || 215 name == "Window Increment") return tr("Window");
220 name == "Zero Padding") return tr("Window");
221 if (name == "Colour" || 216 if (name == "Colour" ||
222 name == "Threshold" || 217 name == "Threshold" ||
223 name == "Colour Rotation") return tr("Colour"); 218 name == "Colour Rotation") return tr("Colour");
224 if (name == "Normalization" || 219 if (name == "Normalization" ||
225 name == "Gain" || 220 name == "Gain" ||
303 *min = 0; 298 *min = 0;
304 *max = 5; 299 *max = 5;
305 *deflt = 2; 300 *deflt = 2;
306 301
307 val = m_windowHopLevel; 302 val = m_windowHopLevel;
308
309 } else if (name == "Zero Padding") {
310
311 *min = 0;
312 *max = 1;
313 *deflt = 0;
314
315 val = m_zeroPadLevel > 0 ? 1 : 0;
316 303
317 } else if (name == "Min Frequency") { 304 } else if (name == "Min Frequency") {
318 305
319 *min = 0; 306 *min = 0;
320 *max = 9; 307 *max = 9;
411 case 2: return tr("50 %"); 398 case 2: return tr("50 %");
412 case 3: return tr("75 %"); 399 case 3: return tr("75 %");
413 case 4: return tr("87.5 %"); 400 case 4: return tr("87.5 %");
414 case 5: return tr("93.75 %"); 401 case 5: return tr("93.75 %");
415 } 402 }
416 }
417 if (name == "Zero Padding") {
418 if (value == 0) return tr("None");
419 return QString("%1x").arg(value + 1);
420 } 403 }
421 if (name == "Min Frequency") { 404 if (name == "Min Frequency") {
422 switch (value) { 405 switch (value) {
423 default: 406 default:
424 case 0: return tr("No min"); 407 case 0: return tr("No min");
508 setColourMap(value); 491 setColourMap(value);
509 } else if (name == "Window Size") { 492 } else if (name == "Window Size") {
510 setWindowSize(32 << value); 493 setWindowSize(32 << value);
511 } else if (name == "Window Increment") { 494 } else if (name == "Window Increment") {
512 setWindowHopLevel(value); 495 setWindowHopLevel(value);
513 } else if (name == "Zero Padding") {
514 setZeroPadLevel(value > 0.1 ? 3 : 0);
515 } else if (name == "Min Frequency") { 496 } else if (name == "Min Frequency") {
516 switch (value) { 497 switch (value) {
517 default: 498 default:
518 case 0: setMinFrequency(0); break; 499 case 0: setMinFrequency(0); break;
519 case 1: setMinFrequency(10); break; 500 case 1: setMinFrequency(10); break;
603 if (name == "Window Type") { 584 if (name == "Window Type") {
604 setWindowType(Preferences::getInstance()->getWindowType()); 585 setWindowType(Preferences::getInstance()->getWindowType());
605 return; 586 return;
606 } 587 }
607 if (name == "Spectrogram Y Smoothing") { 588 if (name == "Spectrogram Y Smoothing") {
589 setWindowSize(m_windowSize);
608 invalidateImageCaches(); 590 invalidateImageCaches();
609 invalidateMagnitudes(); 591 invalidateMagnitudes();
610 emit layerParametersChanged(); 592 emit layerParametersChanged();
611 } 593 }
612 if (name == "Spectrogram X Smoothing") { 594 if (name == "Spectrogram X Smoothing") {
635 SpectrogramLayer::getChannel() const 617 SpectrogramLayer::getChannel() const
636 { 618 {
637 return m_channel; 619 return m_channel;
638 } 620 }
639 621
622 int
623 SpectrogramLayer::getFFTOversampling() const
624 {
625 if (m_binDisplay != AllBins) {
626 return 1;
627 }
628
629 Preferences::SpectrogramSmoothing smoothing =
630 Preferences::getInstance()->getSpectrogramSmoothing();
631
632 if (smoothing == Preferences::NoSpectrogramSmoothing ||
633 smoothing == Preferences::SpectrogramInterpolated) {
634 return 1;
635 }
636
637 return 4;
638 }
639
640 void 640 void
641 SpectrogramLayer::setWindowSize(int ws) 641 SpectrogramLayer::setWindowSize(int ws)
642 { 642 {
643 if (m_windowSize == ws) return; 643 int fftSize = ws * getFFTOversampling();
644
645 if (m_windowSize == ws && m_fftSize == fftSize) return;
644 646
645 invalidateImageCaches(); 647 invalidateImageCaches();
646 648
647 m_windowSize = ws; 649 m_windowSize = ws;
648 m_fftSize = ws * (m_zeroPadLevel + 1); 650 m_fftSize = fftSize;
649 651
650 invalidateFFTModels(); 652 invalidateFFTModels();
651 653
652 emit layerParametersChanged(); 654 emit layerParametersChanged();
653 } 655 }
676 678
677 int 679 int
678 SpectrogramLayer::getWindowHopLevel() const 680 SpectrogramLayer::getWindowHopLevel() const
679 { 681 {
680 return m_windowHopLevel; 682 return m_windowHopLevel;
681 }
682
683 void
684 SpectrogramLayer::setZeroPadLevel(int v)
685 {
686 if (m_zeroPadLevel == v) return;
687
688 invalidateImageCaches();
689
690 m_zeroPadLevel = v;
691 m_fftSize = m_windowSize * (v + 1);
692
693 invalidateFFTModels();
694
695 emit layerParametersChanged();
696 }
697
698 int
699 SpectrogramLayer::getZeroPadLevel() const
700 {
701 return m_zeroPadLevel;
702 } 683 }
703 684
704 void 685 void
705 SpectrogramLayer::setWindowType(WindowType w) 686 SpectrogramLayer::setWindowType(WindowType w)
706 { 687 {
1173 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1154 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1174 1155
1175 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic); 1156 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1176 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic); 1157 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1177 1158
1178 // Now map these on to ("proportions of") actual bins, using raw 1159 // Now map these on to ("proportions of") actual bins
1179 // FFT size (unsmoothed)
1180 1160
1181 q0 = (q0 * m_fftSize) / sr; 1161 q0 = (q0 * m_fftSize) / sr;
1182 q1 = (q1 * m_fftSize) / sr; 1162 q1 = (q1 * m_fftSize) / sr;
1183
1184 return true;
1185 }
1186
1187 bool
1188 SpectrogramLayer::getSmoothedYBinRange(LayerGeometryProvider *v, int y, double &q0, double &q1) const
1189 {
1190 Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
1191
1192 int h = v->getPaintHeight();
1193 if (y < 0 || y >= h) return false;
1194
1195 sv_samplerate_t sr = m_model->getSampleRate();
1196 double minf = getEffectiveMinFrequency();
1197 double maxf = getEffectiveMaxFrequency();
1198
1199 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1200
1201 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1202 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1203
1204 // Now map these on to ("proportions of") actual bins, using raw
1205 // FFT size (unsmoothed)
1206
1207 q0 = (q0 * getFFTSize(v)) / sr;
1208 q1 = (q1 * getFFTSize(v)) / sr;
1209 1163
1210 return true; 1164 return true;
1211 } 1165 }
1212 1166
1213 double 1167 double
1231 1185
1232 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1186 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1233 1187
1234 double q = v->getFrequencyForY(y, minf, maxf, logarithmic); 1188 double q = v->getFrequencyForY(y, minf, maxf, logarithmic);
1235 1189
1236 // Now map on to ("proportions of") actual bins, using raw FFT 1190 // Now map on to ("proportions of") actual bins
1237 // size (unsmoothed)
1238 1191
1239 q = (q * getFFTSize(v)) / sr; 1192 q = (q * getFFTSize(v)) / sr;
1240 1193
1241 return q; 1194 return q;
1242 } 1195 }
1343 if (q == q0i) freqMin = binfreq; 1296 if (q == q0i) freqMin = binfreq;
1344 if (q == q1i) freqMax = binfreq; 1297 if (q == q1i) freqMax = binfreq;
1345 1298
1346 if (peaksOnly && !fft->isLocalPeak(s, q)) continue; 1299 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1347 1300
1348 if (!fft->isOverThreshold(s, q, float(m_threshold * double(m_fftSize)/2.0))) continue; 1301 if (!fft->isOverThreshold
1302 (s, q, float(m_threshold * double(m_fftSize)/2.0))) {
1303 continue;
1304 }
1349 1305
1350 double freq = binfreq; 1306 double freq = binfreq;
1351 1307
1352 if (s < int(fft->getWidth()) - 1) { 1308 if (s < int(fft->getWidth()) - 1) {
1353 1309
1388 1344
1389 int s0i = int(s0 + 0.001); 1345 int s0i = int(s0 + 0.001);
1390 int s1i = int(s1); 1346 int s1i = int(s1);
1391 1347
1392 bool rv = false; 1348 bool rv = false;
1393
1394 int zp = getZeroPadLevel(v);
1395 q0i *= zp + 1;
1396 q1i *= zp + 1;
1397 1349
1398 FFTModel *fft = getFFTModel(v); 1350 FFTModel *fft = getFFTModel(v);
1399 1351
1400 if (fft) { 1352 if (fft) {
1401 1353
1434 1386
1435 return rv; 1387 return rv;
1436 } 1388 }
1437 1389
1438 int 1390 int
1439 SpectrogramLayer::getZeroPadLevel(const LayerGeometryProvider *v) const 1391 SpectrogramLayer::getFFTSize(const LayerGeometryProvider *) const
1440 { 1392 {
1441 //!!! tidy all this stuff 1393 //!!!
1442 1394 return m_fftSize;
1443 if (m_binDisplay != AllBins) return 0;
1444
1445 Preferences::SpectrogramSmoothing smoothing =
1446 Preferences::getInstance()->getSpectrogramSmoothing();
1447
1448 if (smoothing == Preferences::NoSpectrogramSmoothing ||
1449 smoothing == Preferences::SpectrogramInterpolated) return 0;
1450
1451 if (m_frequencyScale == LogFrequencyScale) return 3;
1452
1453 sv_samplerate_t sr = m_model->getSampleRate();
1454
1455 int maxbin = m_fftSize / 2;
1456 if (m_maxFrequency > 0) {
1457 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1458 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1459 }
1460
1461 int minbin = 1;
1462 if (m_minFrequency > 0) {
1463 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
1464 if (minbin < 1) minbin = 1;
1465 if (minbin >= maxbin) minbin = maxbin - 1;
1466 }
1467
1468 double perPixel =
1469 double(v->getPaintHeight()) /
1470 double((maxbin - minbin) / (m_zeroPadLevel + 1));
1471
1472 if (perPixel > 2.8) {
1473 return 3; // 4x oversampling
1474 } else if (perPixel > 1.5) {
1475 return 1; // 2x
1476 } else {
1477 return 0; // 1x
1478 }
1479 }
1480
1481 int
1482 SpectrogramLayer::getFFTSize(const LayerGeometryProvider *v) const
1483 {
1484 return m_fftSize * (getZeroPadLevel(v) + 1);
1485 } 1395 }
1486 1396
1487 FFTModel * 1397 FFTModel *
1488 SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const 1398 SpectrogramLayer::getFFTModel(const LayerGeometryProvider *v) const
1489 { 1399 {
1832 1742
1833 // Set minFreq and maxFreq to the frequency extents of the possibly 1743 // Set minFreq and maxFreq to the frequency extents of the possibly
1834 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq 1744 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
1835 // to the actual scale frequency extents (presumably not zero padded). 1745 // to the actual scale frequency extents (presumably not zero padded).
1836 1746
1837 // If we are zero padding, we want to use the zero-padded 1747 // If we are zero padding (i.e. oversampling) we want to use the
1838 // equivalents of the bins that we would be using if not zero 1748 // zero-padded equivalents of the bins that we would be using if
1839 // padded, to avoid spaces at the top and bottom of the display. 1749 // not zero padded, to avoid spaces at the top and bottom of the
1840 1750 // display.
1841 // Note fftSize is the actual zero-padded fft size, m_fftSize the
1842 // nominal fft size.
1843 1751
1844 int maxbin = m_fftSize / 2; 1752 int maxbin = m_fftSize / 2;
1845 if (m_maxFrequency > 0) { 1753 if (m_maxFrequency > 0) {
1846 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001); 1754 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
1847 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2; 1755 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1853 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl; 1761 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
1854 if (minbin < 1) minbin = 1; 1762 if (minbin < 1) minbin = 1;
1855 if (minbin >= maxbin) minbin = maxbin - 1; 1763 if (minbin >= maxbin) minbin = maxbin - 1;
1856 } 1764 }
1857 1765
1858 int zpl = getZeroPadLevel(v) + 1; 1766 int over = getFFTOversampling();
1859 minbin = minbin * zpl; 1767 minbin = minbin * over;
1860 maxbin = (maxbin + 1) * zpl - 1; 1768 maxbin = (maxbin + 1) * over - 1;
1861 1769
1862 double minFreq = (double(minbin) * sr) / fftSize; 1770 double minFreq = (double(minbin) * sr) / fftSize;
1863 double maxFreq = (double(maxbin) * sr) / fftSize; 1771 double maxFreq = (double(maxbin) * sr) / fftSize;
1864 1772
1865 double displayMinFreq = minFreq; 1773 double displayMinFreq = minFreq;
2006 1914
2007 if (m_binDisplay != PeakFrequencies) { 1915 if (m_binDisplay != PeakFrequencies) {
2008 1916
2009 for (int y = 0; y < h; ++y) { 1917 for (int y = 0; y < h; ++y) {
2010 double q0 = 0, q1 = 0; 1918 double q0 = 0, q1 = 0;
2011 if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) { 1919 if (!getYBinRange(v, h-y-1, q0, q1)) {
2012 binfory[y] = -1; 1920 binfory[y] = -1;
2013 } else { 1921 } else {
2014 binfory[y] = q0; 1922 binfory[y] = q0;
2015 } 1923 }
2016 } 1924 }