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