Mercurial > hg > svgui
comparison layer/SpectrumLayer.cpp @ 280:3c402c6052f6
* Pull peak-picker out of SpectrumLayer and into FFTModel; use combined
peak-picker and frequency estimator for SpectrogramLayer (makes the
peak frequency spectrogram a bit quicker)
* Add more information to spectrum and spectrogram crosshairs
author | Chris Cannam |
---|---|
date | Wed, 04 Jul 2007 15:29:16 +0000 |
parents | a078aa2932cc |
children | 4edaff85875d |
comparison
equal
deleted
inserted
replaced
279:47fe0352861e | 280:3c402c6052f6 |
---|---|
1 | |
2 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ |
3 | 2 |
4 /* | 3 /* |
5 Sonic Visualiser | 4 Sonic Visualiser |
6 An audio file viewer and annotation editor. | 5 An audio file viewer and annotation editor. |
292 | 291 |
293 float | 292 float |
294 SpectrumLayer::getFrequencyForX(float x, float w) const | 293 SpectrumLayer::getFrequencyForX(float x, float w) const |
295 { | 294 { |
296 float freq = 0; | 295 float freq = 0; |
296 if (!m_sliceableModel) return 0; | |
297 | 297 |
298 int sampleRate = m_sliceableModel->getSampleRate(); | 298 int sampleRate = m_sliceableModel->getSampleRate(); |
299 | 299 |
300 float maxfreq = float(sampleRate) / 2; | 300 float maxfreq = float(sampleRate) / 2; |
301 | 301 |
319 | 319 |
320 float | 320 float |
321 SpectrumLayer::getXForFrequency(float freq, float w) const | 321 SpectrumLayer::getXForFrequency(float freq, float w) const |
322 { | 322 { |
323 float x = 0; | 323 float x = 0; |
324 if (!m_sliceableModel) return x; | |
324 | 325 |
325 int sampleRate = m_sliceableModel->getSampleRate(); | 326 int sampleRate = m_sliceableModel->getSampleRate(); |
326 | 327 |
327 float maxfreq = float(sampleRate) / 2; | 328 float maxfreq = float(sampleRate) / 2; |
328 | 329 |
399 extents.push_back(vertical); | 400 extents.push_back(vertical); |
400 | 401 |
401 QRect horizontal(0, cursorPos.y(), v->width(), 12); | 402 QRect horizontal(0, cursorPos.y(), v->width(), 12); |
402 extents.push_back(horizontal); | 403 extents.push_back(horizontal); |
403 | 404 |
404 int hoffset = 1; | 405 int hoffset = 2; |
405 if (m_binScale == LogBins) hoffset = 12; | 406 if (m_binScale == LogBins) hoffset = 13; |
406 | 407 |
407 QRect label(cursorPos.x(), | 408 int sw = getVerticalScaleWidth(v, paint); |
408 v->height() - paint.fontMetrics().height() - hoffset, | 409 |
409 paint.fontMetrics().width("123456 Hz") + 2, | 410 QRect value(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2, |
411 paint.fontMetrics().width("0.0000001 V") + 2, | |
410 paint.fontMetrics().height()); | 412 paint.fontMetrics().height()); |
411 extents.push_back(label); | 413 extents.push_back(value); |
414 | |
415 QRect log(sw, cursorPos.y() + 2, | |
416 paint.fontMetrics().width("-80.000 dBV") + 2, | |
417 paint.fontMetrics().height()); | |
418 extents.push_back(log); | |
419 | |
420 QRect freq(cursorPos.x(), | |
421 v->height() - paint.fontMetrics().height() - hoffset, | |
422 paint.fontMetrics().width("123456 Hz") + 2, | |
423 paint.fontMetrics().height()); | |
424 extents.push_back(freq); | |
412 | 425 |
413 int w(paint.fontMetrics().width("C#10+50c") + 2); | 426 int w(paint.fontMetrics().width("C#10+50c") + 2); |
414 QRect pitch(cursorPos.x() - w, | 427 QRect pitch(cursorPos.x() - w, |
415 v->height() - paint.fontMetrics().height() - hoffset, | 428 v->height() - paint.fontMetrics().height() - hoffset, |
416 w, | 429 w, |
422 | 435 |
423 void | 436 void |
424 SpectrumLayer::paintCrosshairs(View *v, QPainter &paint, | 437 SpectrumLayer::paintCrosshairs(View *v, QPainter &paint, |
425 QPoint cursorPos) const | 438 QPoint cursorPos) const |
426 { | 439 { |
440 if (!m_sliceableModel) return; | |
441 | |
427 paint.save(); | 442 paint.save(); |
428 | 443 |
429 ColourMapper mapper(m_colourMap, 0, 1); | 444 ColourMapper mapper(m_colourMap, 0, 1); |
430 paint.setPen(mapper.getContrastingColour()); | 445 paint.setPen(mapper.getContrastingColour()); |
431 | 446 |
435 paint.drawLine(xorigin, cursorPos.y(), v->width(), cursorPos.y()); | 450 paint.drawLine(xorigin, cursorPos.y(), v->width(), cursorPos.y()); |
436 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->height()); | 451 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->height()); |
437 | 452 |
438 float fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); | 453 float fundamental = getFrequencyForX(cursorPos.x() - xorigin, w); |
439 | 454 |
440 int hoffset = 1; | 455 int hoffset = 2; |
441 if (m_binScale == LogBins) hoffset = 12; | 456 if (m_binScale == LogBins) hoffset = 13; |
442 | 457 |
443 v->drawVisibleText(paint, | 458 v->drawVisibleText(paint, |
444 cursorPos.x() + 2, | 459 cursorPos.x() + 2, |
445 v->height() - 2 - hoffset, | 460 v->height() - 2 - hoffset, |
446 QString("%1 Hz").arg(fundamental), | 461 QString("%1 Hz").arg(fundamental), |
453 v->height() - 2 - hoffset, | 468 v->height() - 2 - hoffset, |
454 pitchLabel, | 469 pitchLabel, |
455 View::OutlinedText); | 470 View::OutlinedText); |
456 } | 471 } |
457 | 472 |
473 float value = getValueForY(cursorPos.y(), v); | |
474 float thresh = -80.f; | |
475 float db = thresh; | |
476 if (value > 0.f) db = 10.f * log10f(value); | |
477 if (db < thresh) db = thresh; | |
478 | |
479 v->drawVisibleText(paint, | |
480 xorigin + 2, | |
481 cursorPos.y() - 2, | |
482 QString("%1 V").arg(value), | |
483 View::OutlinedText); | |
484 | |
485 v->drawVisibleText(paint, | |
486 xorigin + 2, | |
487 cursorPos.y() + 2 + paint.fontMetrics().ascent(), | |
488 QString("%1 dBV").arg(db), | |
489 View::OutlinedText); | |
490 | |
458 int harmonic = 2; | 491 int harmonic = 2; |
459 | 492 |
460 while (harmonic < 100) { | 493 while (harmonic < 100) { |
461 | 494 |
462 float hx = lrintf(getXForFrequency(fundamental * harmonic, w)); | 495 float hx = lrintf(getXForFrequency(fundamental * harmonic, w)); |
586 } | 619 } |
587 | 620 |
588 FFTModel *fft = dynamic_cast<FFTModel *> | 621 FFTModel *fft = dynamic_cast<FFTModel *> |
589 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); | 622 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); |
590 | 623 |
591 float thresh = powf(10, -8) / m_gain; // -80dB | 624 float thresh = (powf(10, -6) / m_gain) * (m_windowSize / 2.f); // -60dB adj |
592 | 625 |
593 int xorigin = getVerticalScaleWidth(v, paint) + 1; | 626 int xorigin = getVerticalScaleWidth(v, paint) + 1; |
594 int w = v->width() - xorigin - 1; | 627 int w = v->width() - xorigin - 1; |
595 | 628 |
596 int pkh = 0; | 629 int pkh = 0; |
605 | 638 |
606 paint.save(); | 639 paint.save(); |
607 paint.setRenderHint(QPainter::Antialiasing, false); | 640 paint.setRenderHint(QPainter::Antialiasing, false); |
608 paint.setPen(QColor(160, 160, 160)); //!!! | 641 paint.setPen(QColor(160, 160, 160)); //!!! |
609 | 642 |
610 ColourMapper mapper(m_colourMap, 0, 1); | 643 FFTModel::PeakSet peaks = fft->getPeakFrequencies |
644 (FFTModel::MajorPitchAdaptivePeaks, col); | |
645 | |
646 ColourMapper mapper(ColourMapper::BlackOnWhite, 0, 1); | |
611 | 647 |
612 BiasCurve curve; | 648 BiasCurve curve; |
613 getBiasCurve(curve); | 649 getBiasCurve(curve); |
614 size_t cs = curve.size(); | 650 size_t cs = curve.size(); |
651 | |
652 std::vector<float> values; | |
615 | 653 |
616 for (size_t bin = 0; bin < fft->getHeight(); ++bin) { | 654 for (size_t bin = 0; bin < fft->getHeight(); ++bin) { |
655 float value = m_sliceableModel->getValueAt(col, bin); | |
656 if (bin < cs) value *= curve[bin]; | |
657 values.push_back(value); | |
658 } | |
659 | |
660 for (FFTModel::PeakSet::iterator i = peaks.begin(); | |
661 i != peaks.end(); ++i) { | |
662 | |
663 size_t bin = i->first; | |
617 | 664 |
618 if (!fft->isLocalPeak(col, bin)) continue; | 665 // std::cerr << "bin = " << bin << ", thresh = " << thresh << ", value = " << fft->getMagnitudeAt(col, bin) << std::endl; |
666 | |
619 if (!fft->isOverThreshold(col, bin, thresh)) continue; | 667 if (!fft->isOverThreshold(col, bin, thresh)) continue; |
620 | 668 |
621 float freq = 0; | 669 float freq = i->second; |
622 bool haveFreq = fft->estimateStableFrequency(col, bin, freq); | 670 |
623 if (!haveFreq) continue; | |
624 | |
625 int x = lrintf(getXForFrequency(freq, w)); | 671 int x = lrintf(getXForFrequency(freq, w)); |
626 | 672 |
627 float value = m_sliceableModel->getValueAt(col, bin); | |
628 if (bin < cs) value *= curve[bin]; | |
629 float norm = 0.f; | 673 float norm = 0.f; |
630 float y = getYForValue(value, v, norm); // don't need y, need norm | 674 float y = getYForValue(values[bin], v, norm); // don't need y, need norm |
631 | 675 |
632 paint.setPen(mapper.map(norm)); | 676 paint.setPen(mapper.map(norm)); |
633 paint.drawLine(xorigin + x, 0, xorigin + x, v->height() - pkh - 1); | 677 paint.drawLine(xorigin + x, 0, xorigin + x, v->height() - pkh - 1); |
634 } | 678 } |
635 | 679 |