Mercurial > hg > svgui
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 { |