comparison layer/SpectrumLayer.cpp @ 1324:13d9b422f7fe zoom

Merge from default branch
author Chris Cannam
date Mon, 17 Sep 2018 13:51:31 +0100
parents fc9d9f1103fa
children d79e21855aef
comparison
equal deleted inserted replaced
1183:57d192e26331 1324:13d9b422f7fe
23 #include "base/Pitch.h" 23 #include "base/Pitch.h"
24 #include "base/Strings.h" 24 #include "base/Strings.h"
25 25
26 #include "ColourMapper.h" 26 #include "ColourMapper.h"
27 #include "PaintAssistant.h" 27 #include "PaintAssistant.h"
28 #include "PianoScale.h"
29 #include "HorizontalFrequencyScale.h"
28 30
29 #include <QPainter> 31 #include <QPainter>
30 #include <QTextStream> 32 #include <QTextStream>
31 33
32 34
164 166
165 QString 167 QString
166 SpectrumLayer::getPropertyGroupName(const PropertyName &name) const 168 SpectrumLayer::getPropertyGroupName(const PropertyName &name) const
167 { 169 {
168 if (name == "Window Size" || 170 if (name == "Window Size" ||
169 name == "Window Increment") return tr("Window"); 171 name == "Window Increment") return tr("Window");
170 if (name == "Show Peak Frequencies") return tr("Bins"); 172 if (name == "Show Peak Frequencies") return tr("Bins");
171 return SliceLayer::getPropertyGroupName(name); 173 return SliceLayer::getPropertyGroupName(name);
172 } 174 }
173 175
174 int 176 int
182 if (!max) max = &garbage1; 184 if (!max) max = &garbage1;
183 if (!deflt) deflt = &garbage2; 185 if (!deflt) deflt = &garbage2;
184 186
185 if (name == "Window Size") { 187 if (name == "Window Size") {
186 188
187 *min = 0; 189 *min = 0;
188 *max = 15; 190 *max = 15;
189 *deflt = 5; 191 *deflt = 5;
190 192
191 val = 0; 193 val = 0;
192 int ws = m_windowSize; 194 int ws = m_windowSize;
193 while (ws > 32) { ws >>= 1; val ++; } 195 while (ws > 32) { ws >>= 1; val ++; }
194 196
195 } else if (name == "Window Increment") { 197 } else if (name == "Window Increment") {
196 198
197 *min = 0; 199 *min = 0;
198 *max = 5; 200 *max = 5;
199 *deflt = 2; 201 *deflt = 2;
200 202
201 val = m_windowHopLevel; 203 val = m_windowHopLevel;
202 204
203 } else if (name == "Show Peak Frequencies") { 205 } else if (name == "Show Peak Frequencies") {
204 206
205 return m_showPeaks ? 1 : 0; 207 return m_showPeaks ? 1 : 0;
212 return val; 214 return val;
213 } 215 }
214 216
215 QString 217 QString
216 SpectrumLayer::getPropertyValueLabel(const PropertyName &name, 218 SpectrumLayer::getPropertyValueLabel(const PropertyName &name,
217 int value) const 219 int value) const
218 { 220 {
219 if (name == "Window Size") { 221 if (name == "Window Size") {
220 return QString("%1").arg(32 << value); 222 return QString("%1").arg(32 << value);
221 } 223 }
222 if (name == "Window Increment") { 224 if (name == "Window Increment") {
223 switch (value) { 225 switch (value) {
224 default: 226 default:
225 case 0: return tr("None"); 227 case 0: return tr("None");
226 case 1: return tr("25 %"); 228 case 1: return tr("25 %");
227 case 2: return tr("50 %"); 229 case 2: return tr("50 %");
228 case 3: return tr("75 %"); 230 case 3: return tr("75 %");
229 case 4: return tr("87.5 %"); 231 case 4: return tr("87.5 %");
230 case 5: return tr("93.75 %"); 232 case 5: return tr("93.75 %");
231 } 233 }
232 } 234 }
233 return SliceLayer::getPropertyValueLabel(name, value); 235 return SliceLayer::getPropertyValueLabel(name, value);
234 } 236 }
235 237
236 RangeMapper * 238 RangeMapper *
241 243
242 void 244 void
243 SpectrumLayer::setProperty(const PropertyName &name, int value) 245 SpectrumLayer::setProperty(const PropertyName &name, int value)
244 { 246 {
245 if (name == "Window Size") { 247 if (name == "Window Size") {
246 setWindowSize(32 << value); 248 setWindowSize(32 << value);
247 } else if (name == "Window Increment") { 249 } else if (name == "Window Increment") {
248 setWindowHopLevel(value); 250 setWindowHopLevel(value);
249 } else if (name == "Show Peak Frequencies") { 251 } else if (name == "Show Peak Frequencies") {
250 setShowPeaks(value ? true : false); 252 setShowPeaks(value ? true : false);
251 } else { 253 } else {
295 setWindowType(Preferences::getInstance()->getWindowType()); 297 setWindowType(Preferences::getInstance()->getWindowType());
296 return; 298 return;
297 } 299 }
298 } 300 }
299 301
300 bool
301 SpectrumLayer::getValueExtents(double &, double &, bool &, QString &) const
302 {
303 return false;
304 }
305
306 double 302 double
307 SpectrumLayer::getXForBin(int bin, int totalBins, double w) const 303 SpectrumLayer::getFrequencyForX(const LayerGeometryProvider *v, double x) const
308 { 304 {
309 if (!m_sliceableModel) return SliceLayer::getXForBin(bin, totalBins, w); 305 if (!m_sliceableModel) return 0;
310 306 double bin = getBinForX(v, x);
311 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); 307 // we assume the frequency of a bin corresponds to the centre of
312 double binfreq = (sampleRate * bin) / (totalBins * 2); 308 // its visual range
313 309 bin -= 0.5;
314 return getXForFrequency(binfreq, w); 310 return (m_sliceableModel->getSampleRate() * bin) /
315 } 311 (m_sliceableModel->getHeight() * 2);
316
317 int
318 SpectrumLayer::getBinForX(double x, int totalBins, double w) const
319 {
320 if (!m_sliceableModel) return SliceLayer::getBinForX(x, totalBins, w);
321
322 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate();
323 double binfreq = getFrequencyForX(x, w);
324
325 return int((binfreq * totalBins * 2) / sampleRate);
326 } 312 }
327 313
328 double 314 double
329 SpectrumLayer::getFrequencyForX(double x, double w) const 315 SpectrumLayer::getXForFrequency(const LayerGeometryProvider *v, double freq) const
330 { 316 {
331 double freq = 0;
332 if (!m_sliceableModel) return 0; 317 if (!m_sliceableModel) return 0;
333 318 double bin = (freq * m_sliceableModel->getHeight() * 2) /
334 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate(); 319 m_sliceableModel->getSampleRate();
335 320 // we want the centre of the bin range
336 double maxfreq = double(sampleRate) / 2; 321 bin += 0.5;
337 322 return getXForBin(v, bin);
338 switch (m_binScale) {
339
340 case LinearBins:
341 freq = ((x * maxfreq) / w);
342 break;
343
344 case LogBins:
345 freq = pow(10.0, (x * log10(maxfreq)) / w);
346 break;
347
348 case InvertedLogBins:
349 freq = maxfreq - pow(10.0, ((w - x) * log10(maxfreq)) / w);
350 break;
351 }
352
353 return freq;
354 }
355
356 double
357 SpectrumLayer::getXForFrequency(double freq, double w) const
358 {
359 double x = 0;
360 if (!m_sliceableModel) return x;
361
362 sv_samplerate_t sampleRate = m_sliceableModel->getSampleRate();
363
364 double maxfreq = double(sampleRate) / 2;
365
366 switch (m_binScale) {
367
368 case LinearBins:
369 x = (freq * w) / maxfreq;
370 break;
371
372 case LogBins:
373 x = (log10(freq) * w) / log10(maxfreq);
374 break;
375
376 case InvertedLogBins:
377 if (maxfreq == freq) x = w;
378 else x = w - (log10(maxfreq - freq) * w) / log10(maxfreq);
379 break;
380 }
381
382 return x;
383 } 323 }
384 324
385 bool 325 bool
386 SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, 326 SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x,
387 double &value, QString &unit) const 327 double &value, QString &unit) const
388 { 328 {
389 if (m_xorigins.find(v) == m_xorigins.end()) return false; 329 value = getFrequencyForX(v, x);
390 int xorigin = m_xorigins.find(v)->second;
391 value = getFrequencyForX(x - xorigin, v->getPaintWidth() - xorigin - 1);
392 unit = "Hz"; 330 unit = "Hz";
393 return true; 331 return true;
394 } 332 }
395 333
396 bool 334 bool
397 SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y, 335 SpectrumLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
398 double &value, QString &unit) const 336 double &value, QString &unit) const
399 { 337 {
400 value = getValueForY(y, v); 338 value = getValueForY(v, y);
401 339
402 if (m_energyScale == dBScale || m_energyScale == MeterScale) { 340 if (m_energyScale == dBScale || m_energyScale == MeterScale) {
403 341
404 if (value > 0.0) { 342 if (value > 0.0) {
405 value = 10.0 * log10(value); 343 value = 10.0 * log10(value);
481 } 419 }
482 420
483 ColourMapper mapper(m_colourMap, 0, 1); 421 ColourMapper mapper(m_colourMap, 0, 1);
484 paint.setPen(mapper.getContrastingColour()); 422 paint.setPen(mapper.getContrastingColour());
485 423
486 int xorigin = m_xorigins[v]; 424 int xorigin = m_xorigins[v->getId()];
487 int w = v->getPaintWidth() - xorigin - 1;
488
489 paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y()); 425 paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y());
490 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight()); 426 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight());
491 427
492 double fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); 428 double fundamental = getFrequencyForX(v, cursorPos.x());
493 429
494 int hoffset = 2; 430 int hoffset = 2;
495 if (m_binScale == LogBins) hoffset = 13; 431 if (m_binScale == LogBins) hoffset = 13;
496 432
497 PaintAssistant::drawVisibleText(v, paint, 433 PaintAssistant::drawVisibleText(v, paint,
498 cursorPos.x() + 2, 434 cursorPos.x() + 2,
499 v->getPaintHeight() - 2 - hoffset, 435 v->getPaintHeight() - 2 - hoffset,
500 QString("%1 Hz").arg(fundamental), 436 QString("%1 Hz").arg(fundamental),
501 PaintAssistant::OutlinedText); 437 PaintAssistant::OutlinedText);
502 438
503 if (Pitch::isFrequencyInMidiRange(fundamental)) { 439 if (Pitch::isFrequencyInMidiRange(fundamental)) {
504 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); 440 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
505 PaintAssistant::drawVisibleText(v, paint, 441 PaintAssistant::drawVisibleText(v, paint,
506 cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2, 442 cursorPos.x() -
507 v->getPaintHeight() - 2 - hoffset, 443 paint.fontMetrics().width(pitchLabel) - 2,
508 pitchLabel, 444 v->getPaintHeight() - 2 - hoffset,
509 PaintAssistant::OutlinedText); 445 pitchLabel,
510 } 446 PaintAssistant::OutlinedText);
511 447 }
512 double value = getValueForY(cursorPos.y(), v); 448
449 double value = getValueForY(v, cursorPos.y());
513 double thresh = m_threshold; 450 double thresh = m_threshold;
514 double db = thresh; 451 double db = thresh;
515 if (value > 0.0) db = 10.0 * log10(value); 452 if (value > 0.0) db = 10.0 * log10(value);
516 if (db < thresh) db = thresh; 453 if (db < thresh) db = thresh;
517 454
529 466
530 int harmonic = 2; 467 int harmonic = 2;
531 468
532 while (harmonic < 100) { 469 while (harmonic < 100) {
533 470
534 int hx = int(lrint(getXForFrequency(fundamental * harmonic, w))); 471 int hx = int(lrint(getXForFrequency(v, fundamental * harmonic)));
535 hx += xorigin;
536 472
537 if (hx < xorigin || hx > v->getPaintWidth()) break; 473 if (hx < xorigin || hx > v->getPaintWidth()) break;
538 474
539 int len = 7; 475 int len = 7;
540 476
566 QString genericDesc = SliceLayer::getFeatureDescriptionAux 502 QString genericDesc = SliceLayer::getFeatureDescriptionAux
567 (v, p, false, minbin, maxbin, range); 503 (v, p, false, minbin, maxbin, range);
568 504
569 if (genericDesc == "") return ""; 505 if (genericDesc == "") return "";
570 506
571 double minvalue = 0.f; 507 int i0 = minbin - m_minbin;
572 if (minbin < int(m_values.size())) minvalue = m_values[minbin]; 508 int i1 = maxbin - m_minbin;
573
574 double maxvalue = minvalue;
575 if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin];
576 509
510 float minvalue = 0.0;
511 if (in_range_for(m_values, i0)) minvalue = m_values[i0];
512
513 float maxvalue = minvalue;
514 if (in_range_for(m_values, i1)) maxvalue = m_values[i1];
515
577 if (minvalue > maxvalue) std::swap(minvalue, maxvalue); 516 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
578 517
579 QString binstr; 518 QString binstr;
580 QString hzstr; 519 QString hzstr;
581 int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) / 520 int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) /
582 m_windowSize)); 521 m_windowSize));
583 int maxfreq = int(lrint((std::max(maxbin, minbin+1) 522 int maxfreq = int(lrint((std::max(maxbin, minbin)
584 * m_sliceableModel->getSampleRate()) / 523 * m_sliceableModel->getSampleRate()) /
585 m_windowSize)); 524 m_windowSize));
586 525
587 if (maxbin != minbin) { 526 if (maxbin != minbin) {
588 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); 527 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
664 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); 603 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel));
665 604
666 double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj 605 double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj
667 606
668 int xorigin = getVerticalScaleWidth(v, false, paint) + 1; 607 int xorigin = getVerticalScaleWidth(v, false, paint) + 1;
669 int w = v->getPaintWidth() - xorigin - 1; 608 int scaleHeight = getHorizontalScaleHeight(v, paint);
670
671 int pkh = 0;
672 //!!! if (m_binScale == LogBins) {
673 pkh = 10;
674 //!!! }
675
676 paint.save();
677 609
678 if (fft && m_showPeaks) { 610 if (fft && m_showPeaks) {
679 611
680 // draw peak lines 612 // draw peak lines
681
682 // SVDEBUG << "Showing peaks..." << endl;
683 613
684 int col = int(v->getCentreFrame() / fft->getResolution()); 614 int col = int(v->getCentreFrame() / fft->getResolution());
685 615
686 paint.save(); 616 paint.save();
687 paint.setRenderHint(QPainter::Antialiasing, false); 617 paint.setRenderHint(QPainter::Antialiasing, false);
688 paint.setPen(QColor(160, 160, 160)); //!!! 618
689 619 ColourMapper mapper =
620 hasLightBackground() ?
621 ColourMapper(ColourMapper::BlackOnWhite, 0, 1) :
622 ColourMapper(ColourMapper::WhiteOnBlack, 0, 1);
623
690 int peakminbin = 0; 624 int peakminbin = 0;
691 int peakmaxbin = fft->getHeight() - 1; 625 int peakmaxbin = fft->getHeight() - 1;
692 double peakmaxfreq = Pitch::getFrequencyForPitch(128); 626 double peakmaxfreq = Pitch::getFrequencyForPitch(128);
693 peakmaxbin = int(((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate())); 627 peakmaxbin = int(((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate()));
694 628
695 FFTModel::PeakSet peaks = fft->getPeakFrequencies 629 FFTModel::PeakSet peaks = fft->getPeakFrequencies
696 (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin); 630 (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin);
697 631
698 ColourMapper mapper(ColourMapper::BlackOnWhite, 0, 1);
699
700 BiasCurve curve; 632 BiasCurve curve;
701 getBiasCurve(curve); 633 getBiasCurve(curve);
702 int cs = int(curve.size()); 634 int cs = int(curve.size());
703 635
704 std::vector<double> values; 636 std::vector<double> values;
718 650
719 if (!fft->isOverThreshold(col, bin, float(thresh))) continue; 651 if (!fft->isOverThreshold(col, bin, float(thresh))) continue;
720 652
721 double freq = i->second; 653 double freq = i->second;
722 654
723 int x = int(lrint(getXForFrequency(freq, w))); 655 int x = int(lrint(getXForFrequency(v, freq)));
724 656
725 double norm = 0.f; 657 double norm = 0.f;
726 (void)getYForValue(values[bin], v, norm); // don't need return value, need norm 658 (void)getYForValue(v, values[bin], norm); // don't need return value, need norm
727 659
728 paint.setPen(mapper.map(norm)); 660 paint.setPen(mapper.map(norm));
729 paint.drawLine(xorigin + x, 0, xorigin + x, v->getPaintHeight() - pkh - 1); 661 paint.drawLine(x, 0, x, v->getPaintHeight() - scaleHeight - 1);
730 } 662 }
731 663
732 paint.restore(); 664 paint.restore();
733 } 665 }
734 666
667 paint.save();
668
735 SliceLayer::paint(v, paint, rect); 669 SliceLayer::paint(v, paint, rect);
736 670
671 paintHorizontalScale(v, paint, xorigin);
672
673 paint.restore();
674 }
675
676 int
677 SpectrumLayer::getHorizontalScaleHeight(LayerGeometryProvider *v,
678 QPainter &paint) const
679 {
680 int pkh = int(paint.fontMetrics().height() * 0.7 + 0.5);
681 if (pkh < 10) pkh = 10;
682
683 int scaleh = HorizontalFrequencyScale().getHeight(v, paint);
684
685 return pkh + scaleh;
686 }
687
688 void
689 SpectrumLayer::paintHorizontalScale(LayerGeometryProvider *v,
690 QPainter &paint,
691 int xorigin) const
692 {
737 //!!! All of this stuff relating to depicting frequencies 693 //!!! All of this stuff relating to depicting frequencies
738 //(keyboard, crosshairs etc) should be applicable to any slice 694 // (keyboard, crosshairs etc) should be applicable to any slice
739 //layer whose model has a vertical scale unit of Hz. However, the 695 // layer whose model has a vertical scale unit of Hz. However,
740 //dense 3d model at the moment doesn't record its vertical scale 696 // the dense 3d model at the moment doesn't record its vertical
741 //unit -- we need to fix that and hoist this code as appropriate. 697 // scale unit -- we need to fix that and hoist this code as
742 //Same really goes for any code in SpectrogramLayer that could be 698 // appropriate. Same really goes for any code in SpectrogramLayer
743 //relevant to Colour3DPlotLayer with unit Hz, but that's a bigger 699 // that could be relevant to Colour3DPlotLayer with unit Hz, but
744 //proposition. 700 // that's a bigger proposition.
745 701
746 // if (m_binScale == LogBins) { 702 if (!v->getViewManager()->shouldShowHorizontalValueScale()) {
747 703 return;
748 // int pkh = 10; 704 }
749 int h = v->getPaintHeight(); 705
750 706 int totalScaleHeight = getHorizontalScaleHeight(v, paint); // inc piano
751 // piano keyboard 707 int freqScaleHeight = HorizontalFrequencyScale().getHeight(v, paint);
752 //!!! should be in a new paintHorizontalScale()? 708 int paintHeight = v->getPaintHeight();
753 // nice to have a piano keyboard class, of course 709 int paintWidth = v->getPaintWidth();
754 710
755 paint.drawLine(xorigin, h - pkh - 1, w + xorigin, h - pkh - 1); 711 PianoScale().paintPianoHorizontal
756 712 (v, this, paint,
757 int px = xorigin, ppx = xorigin; 713 QRect(xorigin, paintHeight - totalScaleHeight - 1,
758 paint.setBrush(paint.pen().color()); 714 paintWidth - 1, totalScaleHeight - freqScaleHeight));
759 715
760 for (int i = 0; i < 128; ++i) { 716 int scaleLeft = int(getXForBin(v, 1));
761 717
762 double f = Pitch::getFrequencyForPitch(i); 718 paint.drawLine(int(getXForBin(v, 0)), paintHeight - freqScaleHeight,
763 int x = int(lrint(getXForFrequency(f, w))); 719 scaleLeft, paintHeight - freqScaleHeight);
764 720
765 x += xorigin; 721 QString hz = tr("Hz");
766 722 int hzw = paint.fontMetrics().width(hz);
767 if (i == 0) { 723 if (scaleLeft > hzw + 5) {
768 px = ppx = x; 724 paint.drawText
769 } 725 (scaleLeft - hzw - 5,
770 if (i == 1) { 726 paintHeight - freqScaleHeight + paint.fontMetrics().ascent() + 5,
771 ppx = px - (x - px); 727 hz);
772 } 728 }
773 729
774 if (x < xorigin) { 730 HorizontalFrequencyScale().paintScale
775 ppx = px; 731 (v, this, paint,
776 px = x; 732 QRect(scaleLeft, paintHeight - freqScaleHeight,
777 continue; 733 paintWidth, totalScaleHeight),
778 } 734 m_binScale == LogBins);
779
780 if (x > w) {
781 break;
782 }
783
784 int n = (i % 12);
785
786 if (n == 1) {
787 // C# -- fill the C from here
788 QColor col = Qt::gray;
789 if (i == 61) { // filling middle C
790 col = Qt::blue;
791 col = col.light(150);
792 }
793 if (x - ppx > 2) {
794 paint.fillRect((px + ppx) / 2 + 1,
795 h - pkh,
796 x - (px + ppx) / 2 - 1,
797 pkh,
798 col);
799 }
800 }
801
802 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
803 // black notes
804 paint.drawLine(x, h - pkh, x, h);
805 int rw = int(lrint(double(x - px) / 4) * 2);
806 if (rw < 2) rw = 2;
807 paint.drawRect(x - rw/2, h - pkh, rw, pkh/2);
808 } else if (n == 0 || n == 5) {
809 // C, F
810 if (px < w) {
811 paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h);
812 }
813 }
814
815 ppx = px;
816 px = x;
817 }
818 // }
819
820 paint.restore();
821 } 735 }
822 736
823 void 737 void
824 SpectrumLayer::getBiasCurve(BiasCurve &curve) const 738 SpectrumLayer::getBiasCurve(BiasCurve &curve) const
825 { 739 {