00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "SpectrumLayer.h"
00017
00018 #include "data/model/FFTModel.h"
00019 #include "view/View.h"
00020 #include "base/AudioLevel.h"
00021 #include "base/Preferences.h"
00022 #include "base/RangeMapper.h"
00023 #include "base/Pitch.h"
00024 #include "base/ColourMapper.h"
00025
00026 #include <QPainter>
00027 #include <QTextStream>
00028
00029
00030 SpectrumLayer::SpectrumLayer() :
00031 m_originModel(0),
00032 m_channel(-1),
00033 m_channelSet(false),
00034 m_windowSize(4096),
00035 m_windowType(HanningWindow),
00036 m_windowHopLevel(3),
00037 m_showPeaks(false),
00038 m_newFFTNeeded(true)
00039 {
00040 Preferences *prefs = Preferences::getInstance();
00041 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
00042 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
00043 setWindowType(prefs->getWindowType());
00044
00045 setBinScale(LogBins);
00046 }
00047
00048 SpectrumLayer::~SpectrumLayer()
00049 {
00050 Model *m = const_cast<Model *>
00051 (static_cast<const Model *>(m_sliceableModel));
00052 m->aboutToDelete();
00053 m_sliceableModel = 0;
00054 delete m;
00055 }
00056
00057 void
00058 SpectrumLayer::setModel(DenseTimeValueModel *model)
00059 {
00060 std::cerr << "SpectrumLayer::setModel(" << model << ") from " << m_originModel << std::endl;
00061
00062 if (m_originModel == model) return;
00063
00064 m_originModel = model;
00065
00066 if (m_sliceableModel) {
00067 Model *m = const_cast<Model *>
00068 (static_cast<const Model *>(m_sliceableModel));
00069 m->aboutToDelete();
00070 setSliceableModel(0);
00071 delete m;
00072 }
00073
00074 m_newFFTNeeded = true;
00075
00076 emit layerParametersChanged();
00077 }
00078
00079 void
00080 SpectrumLayer::setChannel(int channel)
00081 {
00082 std::cerr << "SpectrumLayer::setChannel(" << channel << ") from " << m_channel << std::endl;
00083
00084 m_channelSet = true;
00085
00086 if (m_channel == channel) return;
00087
00088 m_channel = channel;
00089
00090 m_newFFTNeeded = true;
00091
00092 emit layerParametersChanged();
00093 }
00094
00095 void
00096 SpectrumLayer::setupFFT()
00097 {
00098 if (m_sliceableModel) {
00099 Model *m = const_cast<Model *>
00100 (static_cast<const Model *>(m_sliceableModel));
00101 m->aboutToDelete();
00102 setSliceableModel(0);
00103 delete m;
00104 }
00105
00106 if (!m_originModel) {
00107 return;
00108 }
00109
00110 FFTModel *newFFT = new FFTModel(m_originModel,
00111 m_channel,
00112 m_windowType,
00113 m_windowSize,
00114 getWindowIncrement(),
00115 m_windowSize,
00116 false,
00117 StorageAdviser::Criteria
00118 (StorageAdviser::SpeedCritical |
00119 StorageAdviser::FrequentLookupLikely));
00120
00121 setSliceableModel(newFFT);
00122
00123 m_biasCurve.clear();
00124 for (size_t i = 0; i < m_windowSize; ++i) {
00125 m_biasCurve.push_back(1.f / (float(m_windowSize)/2.f));
00126 }
00127
00128 newFFT->resume();
00129
00130 m_newFFTNeeded = false;
00131 }
00132
00133 Layer::PropertyList
00134 SpectrumLayer::getProperties() const
00135 {
00136 PropertyList list = SliceLayer::getProperties();
00137 list.push_back("Window Size");
00138 list.push_back("Window Increment");
00139 list.push_back("Show Peak Frequencies");
00140 return list;
00141 }
00142
00143 QString
00144 SpectrumLayer::getPropertyLabel(const PropertyName &name) const
00145 {
00146 if (name == "Window Size") return tr("Window Size");
00147 if (name == "Window Increment") return tr("Window Overlap");
00148 if (name == "Show Peak Frequencies") return tr("Show Peak Frequencies");
00149 return SliceLayer::getPropertyLabel(name);
00150 }
00151
00152 QString
00153 SpectrumLayer::getPropertyIconName(const PropertyName &name) const
00154 {
00155 if (name == "Show Peak Frequencies") return "show-peaks";
00156 return SliceLayer::getPropertyIconName(name);
00157 }
00158
00159 Layer::PropertyType
00160 SpectrumLayer::getPropertyType(const PropertyName &name) const
00161 {
00162 if (name == "Window Size") return ValueProperty;
00163 if (name == "Window Increment") return ValueProperty;
00164 if (name == "Show Peak Frequencies") return ToggleProperty;
00165 return SliceLayer::getPropertyType(name);
00166 }
00167
00168 QString
00169 SpectrumLayer::getPropertyGroupName(const PropertyName &name) const
00170 {
00171 if (name == "Window Size" ||
00172 name == "Window Increment") return tr("Window");
00173 if (name == "Show Peak Frequencies") return tr("Plot Type");
00174 return SliceLayer::getPropertyGroupName(name);
00175 }
00176
00177 int
00178 SpectrumLayer::getPropertyRangeAndValue(const PropertyName &name,
00179 int *min, int *max, int *deflt) const
00180 {
00181 int val = 0;
00182
00183 int garbage0, garbage1, garbage2;
00184 if (!min) min = &garbage0;
00185 if (!max) max = &garbage1;
00186 if (!deflt) deflt = &garbage2;
00187
00188 if (name == "Window Size") {
00189
00190 *min = 0;
00191 *max = 15;
00192 *deflt = 5;
00193
00194 val = 0;
00195 int ws = m_windowSize;
00196 while (ws > 32) { ws >>= 1; val ++; }
00197
00198 } else if (name == "Window Increment") {
00199
00200 *min = 0;
00201 *max = 5;
00202 *deflt = 2;
00203
00204 val = m_windowHopLevel;
00205
00206 } else if (name == "Show Peak Frequencies") {
00207
00208 return m_showPeaks ? 1 : 0;
00209
00210 } else {
00211
00212 val = SliceLayer::getPropertyRangeAndValue(name, min, max, deflt);
00213 }
00214
00215 return val;
00216 }
00217
00218 QString
00219 SpectrumLayer::getPropertyValueLabel(const PropertyName &name,
00220 int value) const
00221 {
00222 if (name == "Window Size") {
00223 return QString("%1").arg(32 << value);
00224 }
00225 if (name == "Window Increment") {
00226 switch (value) {
00227 default:
00228 case 0: return tr("None");
00229 case 1: return tr("25 %");
00230 case 2: return tr("50 %");
00231 case 3: return tr("75 %");
00232 case 4: return tr("87.5 %");
00233 case 5: return tr("93.75 %");
00234 }
00235 }
00236 return SliceLayer::getPropertyValueLabel(name, value);
00237 }
00238
00239 RangeMapper *
00240 SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const
00241 {
00242 return SliceLayer::getNewPropertyRangeMapper(name);
00243 }
00244
00245 void
00246 SpectrumLayer::setProperty(const PropertyName &name, int value)
00247 {
00248 if (name == "Window Size") {
00249 setWindowSize(32 << value);
00250 } else if (name == "Window Increment") {
00251 setWindowHopLevel(value);
00252 } else if (name == "Show Peak Frequencies") {
00253 setShowPeaks(value ? true : false);
00254 } else {
00255 SliceLayer::setProperty(name, value);
00256 }
00257 }
00258
00259 void
00260 SpectrumLayer::setWindowSize(size_t ws)
00261 {
00262 if (m_windowSize == ws) return;
00263 m_windowSize = ws;
00264 m_newFFTNeeded = true;
00265 emit layerParametersChanged();
00266 }
00267
00268 void
00269 SpectrumLayer::setWindowHopLevel(size_t v)
00270 {
00271 if (m_windowHopLevel == v) return;
00272 m_windowHopLevel = v;
00273 m_newFFTNeeded = true;
00274 emit layerParametersChanged();
00275 }
00276
00277 void
00278 SpectrumLayer::setWindowType(WindowType w)
00279 {
00280 if (m_windowType == w) return;
00281 m_windowType = w;
00282 m_newFFTNeeded = true;
00283 emit layerParametersChanged();
00284 }
00285
00286 void
00287 SpectrumLayer::setShowPeaks(bool show)
00288 {
00289 if (m_showPeaks == show) return;
00290 m_showPeaks = show;
00291 emit layerParametersChanged();
00292 }
00293
00294 void
00295 SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name)
00296 {
00297 if (name == "Window Type") {
00298 setWindowType(Preferences::getInstance()->getWindowType());
00299 return;
00300 }
00301 }
00302
00303 bool
00304 SpectrumLayer::getValueExtents(float &, float &, bool &, QString &) const
00305 {
00306 return false;
00307 }
00308
00309 float
00310 SpectrumLayer::getXForBin(int bin, int totalBins, float w) const
00311 {
00312 if (!m_sliceableModel) return SliceLayer::getXForBin(bin, totalBins, w);
00313
00314 float sampleRate = m_sliceableModel->getSampleRate();
00315 float binfreq = (sampleRate * bin) / (totalBins * 2);
00316
00317 return getXForFrequency(binfreq, w);
00318 }
00319
00320 int
00321 SpectrumLayer::getBinForX(float x, int totalBins, float w) const
00322 {
00323 if (!m_sliceableModel) return SliceLayer::getBinForX(x, totalBins, w);
00324
00325 float sampleRate = m_sliceableModel->getSampleRate();
00326 float binfreq = getFrequencyForX(x, w);
00327
00328 return int((binfreq * totalBins * 2) / sampleRate);
00329 }
00330
00331 float
00332 SpectrumLayer::getFrequencyForX(float x, float w) const
00333 {
00334 float freq = 0;
00335 if (!m_sliceableModel) return 0;
00336
00337 int sampleRate = m_sliceableModel->getSampleRate();
00338
00339 float maxfreq = float(sampleRate) / 2;
00340
00341 switch (m_binScale) {
00342
00343 case LinearBins:
00344 freq = ((x * maxfreq) / w);
00345 break;
00346
00347 case LogBins:
00348 freq = powf(10.f, (x * log10f(maxfreq)) / w);
00349 break;
00350
00351 case InvertedLogBins:
00352 freq = maxfreq - powf(10.f, ((w - x) * log10f(maxfreq)) / w);
00353 break;
00354 }
00355
00356 return freq;
00357 }
00358
00359 float
00360 SpectrumLayer::getXForFrequency(float freq, float w) const
00361 {
00362 float x = 0;
00363 if (!m_sliceableModel) return x;
00364
00365 int sampleRate = m_sliceableModel->getSampleRate();
00366
00367 float maxfreq = float(sampleRate) / 2;
00368
00369 switch (m_binScale) {
00370
00371 case LinearBins:
00372 x = (freq * w) / maxfreq;
00373 break;
00374
00375 case LogBins:
00376 x = (log10f(freq) * w) / log10f(maxfreq);
00377 break;
00378
00379 case InvertedLogBins:
00380 if (maxfreq == freq) x = w;
00381 else x = w - (log10f(maxfreq - freq) * w) / log10f(maxfreq);
00382 break;
00383 }
00384
00385 return x;
00386 }
00387
00388 bool
00389 SpectrumLayer::getXScaleValue(const View *v, int x,
00390 float &value, QString &unit) const
00391 {
00392 if (m_xorigins.find(v) == m_xorigins.end()) return false;
00393 int xorigin = m_xorigins.find(v)->second;
00394 value = getFrequencyForX(x - xorigin, v->width() - xorigin - 1);
00395 unit = "Hz";
00396 return true;
00397 }
00398
00399 bool
00400 SpectrumLayer::getYScaleValue(const View *v, int y,
00401 float &value, QString &unit) const
00402 {
00403 value = getValueForY(y, v);
00404
00405 if (m_energyScale == dBScale || m_energyScale == MeterScale) {
00406
00407 if (value > 0.f) {
00408 value = 10.f * log10f(value);
00409 if (value < m_threshold) value = m_threshold;
00410 } else value = m_threshold;
00411
00412 unit = "dBV";
00413
00414 } else {
00415 unit = "V";
00416 }
00417
00418 return true;
00419 }
00420
00421 bool
00422 SpectrumLayer::getYScaleDifference(const View *v, int y0, int y1,
00423 float &diff, QString &unit) const
00424 {
00425 bool rv = SliceLayer::getYScaleDifference(v, y0, y1, diff, unit);
00426 if (rv && (unit == "dBV")) unit = "dB";
00427 return rv;
00428 }
00429
00430
00431 bool
00432 SpectrumLayer::getCrosshairExtents(View *v, QPainter &paint,
00433 QPoint cursorPos,
00434 std::vector<QRect> &extents) const
00435 {
00436 QRect vertical(cursorPos.x(), cursorPos.y(), 1, v->height() - cursorPos.y());
00437 extents.push_back(vertical);
00438
00439 QRect horizontal(0, cursorPos.y(), v->width(), 12);
00440 extents.push_back(horizontal);
00441
00442 int hoffset = 2;
00443 if (m_binScale == LogBins) hoffset = 13;
00444
00445 int sw = getVerticalScaleWidth(v, paint);
00446
00447 QRect value(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
00448 paint.fontMetrics().width("0.0000001 V") + 2,
00449 paint.fontMetrics().height());
00450 extents.push_back(value);
00451
00452 QRect log(sw, cursorPos.y() + 2,
00453 paint.fontMetrics().width("-80.000 dBV") + 2,
00454 paint.fontMetrics().height());
00455 extents.push_back(log);
00456
00457 QRect freq(cursorPos.x(),
00458 v->height() - paint.fontMetrics().height() - hoffset,
00459 paint.fontMetrics().width("123456 Hz") + 2,
00460 paint.fontMetrics().height());
00461 extents.push_back(freq);
00462
00463 int w(paint.fontMetrics().width("C#10+50c") + 2);
00464 QRect pitch(cursorPos.x() - w,
00465 v->height() - paint.fontMetrics().height() - hoffset,
00466 w,
00467 paint.fontMetrics().height());
00468 extents.push_back(pitch);
00469
00470 return true;
00471 }
00472
00473 void
00474 SpectrumLayer::paintCrosshairs(View *v, QPainter &paint,
00475 QPoint cursorPos) const
00476 {
00477 if (!m_sliceableModel) return;
00478
00479 paint.save();
00480 QFont fn = paint.font();
00481 if (fn.pointSize() > 8) {
00482 fn.setPointSize(fn.pointSize() - 1);
00483 paint.setFont(fn);
00484 }
00485
00486 ColourMapper mapper(m_colourMap, 0, 1);
00487 paint.setPen(mapper.getContrastingColour());
00488
00489 int xorigin = m_xorigins[v];
00490 int w = v->width() - xorigin - 1;
00491
00492 paint.drawLine(xorigin, cursorPos.y(), v->width(), cursorPos.y());
00493 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->height());
00494
00495 float fundamental = getFrequencyForX(cursorPos.x() - xorigin, w);
00496
00497 int hoffset = 2;
00498 if (m_binScale == LogBins) hoffset = 13;
00499
00500 v->drawVisibleText(paint,
00501 cursorPos.x() + 2,
00502 v->height() - 2 - hoffset,
00503 QString("%1 Hz").arg(fundamental),
00504 View::OutlinedText);
00505
00506 if (Pitch::isFrequencyInMidiRange(fundamental)) {
00507 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
00508 v->drawVisibleText(paint,
00509 cursorPos.x() - paint.fontMetrics().width(pitchLabel) - 2,
00510 v->height() - 2 - hoffset,
00511 pitchLabel,
00512 View::OutlinedText);
00513 }
00514
00515 float value = getValueForY(cursorPos.y(), v);
00516 float thresh = m_threshold;
00517 float db = thresh;
00518 if (value > 0.f) db = 10.f * log10f(value);
00519 if (db < thresh) db = thresh;
00520
00521 v->drawVisibleText(paint,
00522 xorigin + 2,
00523 cursorPos.y() - 2,
00524 QString("%1 V").arg(value),
00525 View::OutlinedText);
00526
00527 v->drawVisibleText(paint,
00528 xorigin + 2,
00529 cursorPos.y() + 2 + paint.fontMetrics().ascent(),
00530 QString("%1 dBV").arg(db),
00531 View::OutlinedText);
00532
00533 int harmonic = 2;
00534
00535 while (harmonic < 100) {
00536
00537 float hx = lrintf(getXForFrequency(fundamental * harmonic, w));
00538 hx += xorigin;
00539
00540 if (hx < xorigin || hx > v->width()) break;
00541
00542 int len = 7;
00543
00544 if (harmonic % 2 == 0) {
00545 if (harmonic % 4 == 0) {
00546 len = 12;
00547 } else {
00548 len = 10;
00549 }
00550 }
00551
00552 paint.drawLine(int(hx),
00553 cursorPos.y(),
00554 int(hx),
00555 cursorPos.y() + len);
00556
00557 ++harmonic;
00558 }
00559
00560 paint.restore();
00561 }
00562
00563 QString
00564 SpectrumLayer::getFeatureDescription(View *v, QPoint &p) const
00565 {
00566 if (!m_sliceableModel) return "";
00567
00568 int minbin = 0, maxbin = 0, range = 0;
00569 QString genericDesc = SliceLayer::getFeatureDescription
00570 (v, p, false, minbin, maxbin, range);
00571
00572 if (genericDesc == "") return "";
00573
00574 float minvalue = 0.f;
00575 if (minbin < int(m_values.size())) minvalue = m_values[minbin];
00576
00577 float maxvalue = minvalue;
00578 if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin];
00579
00580 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
00581
00582 QString binstr;
00583 QString hzstr;
00584 int minfreq = lrintf((minbin * m_sliceableModel->getSampleRate()) /
00585 m_windowSize);
00586 int maxfreq = lrintf((std::max(maxbin, minbin+1)
00587 * m_sliceableModel->getSampleRate()) /
00588 m_windowSize);
00589
00590 if (maxbin != minbin) {
00591 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
00592 } else {
00593 binstr = QString("%1").arg(minbin+1);
00594 }
00595 if (minfreq != maxfreq) {
00596 hzstr = tr("%1 - %2 Hz").arg(minfreq).arg(maxfreq);
00597 } else {
00598 hzstr = tr("%1 Hz").arg(minfreq);
00599 }
00600
00601 QString valuestr;
00602 if (maxvalue != minvalue) {
00603 valuestr = tr("%1 - %2").arg(minvalue).arg(maxvalue);
00604 } else {
00605 valuestr = QString("%1").arg(minvalue);
00606 }
00607
00608 QString dbstr;
00609 float mindb = AudioLevel::multiplier_to_dB(minvalue);
00610 float maxdb = AudioLevel::multiplier_to_dB(maxvalue);
00611 QString mindbstr;
00612 QString maxdbstr;
00613 if (mindb == AudioLevel::DB_FLOOR) {
00614 mindbstr = tr("-Inf");
00615 } else {
00616 mindbstr = QString("%1").arg(lrintf(mindb));
00617 }
00618 if (maxdb == AudioLevel::DB_FLOOR) {
00619 maxdbstr = tr("-Inf");
00620 } else {
00621 maxdbstr = QString("%1").arg(lrintf(maxdb));
00622 }
00623 if (lrintf(mindb) != lrintf(maxdb)) {
00624 dbstr = tr("%1 - %2").arg(mindbstr).arg(maxdbstr);
00625 } else {
00626 dbstr = tr("%1").arg(mindbstr);
00627 }
00628
00629 QString description;
00630
00631 if (range > int(m_sliceableModel->getResolution())) {
00632 description = tr("%1\nBin:\t%2 (%3)\n%4 value:\t%5\ndB:\t%6")
00633 .arg(genericDesc)
00634 .arg(binstr)
00635 .arg(hzstr)
00636 .arg(m_samplingMode == NearestSample ? tr("First") :
00637 m_samplingMode == SampleMean ? tr("Mean") : tr("Peak"))
00638 .arg(valuestr)
00639 .arg(dbstr);
00640 } else {
00641 description = tr("%1\nBin:\t%2 (%3)\nValue:\t%4\ndB:\t%5")
00642 .arg(genericDesc)
00643 .arg(binstr)
00644 .arg(hzstr)
00645 .arg(valuestr)
00646 .arg(dbstr);
00647 }
00648
00649 return description;
00650 }
00651
00652 void
00653 SpectrumLayer::paint(View *v, QPainter &paint, QRect rect) const
00654 {
00655 if (!m_originModel || !m_originModel->isOK() ||
00656 !m_originModel->isReady()) {
00657 std::cerr << "SpectrumLayer::paint: no origin model, or origin model not OK or not ready" << std::endl;
00658 return;
00659 }
00660
00661 if (m_newFFTNeeded) {
00662 std::cerr << "SpectrumLayer::paint: new FFT needed, calling setupFFT" << std::endl;
00663 const_cast<SpectrumLayer *>(this)->setupFFT();
00664 }
00665
00666 FFTModel *fft = dynamic_cast<FFTModel *>
00667 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel));
00668
00669 float thresh = (powf(10, -6) / m_gain) * (m_windowSize / 2.f);
00670
00671 int xorigin = getVerticalScaleWidth(v, paint) + 1;
00672 int w = v->width() - xorigin - 1;
00673
00674 int pkh = 0;
00676 pkh = 10;
00678
00679 paint.save();
00680
00681 if (fft && m_showPeaks) {
00682
00683
00684
00685 size_t col = v->getCentreFrame() / fft->getResolution();
00686
00687 paint.save();
00688 paint.setRenderHint(QPainter::Antialiasing, false);
00689 paint.setPen(QColor(160, 160, 160));
00690
00691 int peakminbin = 0;
00692 int peakmaxbin = fft->getHeight() - 1;
00693 float peakmaxfreq = Pitch::getFrequencyForPitch(128);
00694 peakmaxbin = ((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate());
00695
00696 FFTModel::PeakSet peaks = fft->getPeakFrequencies
00697 (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin);
00698
00699 ColourMapper mapper(ColourMapper::BlackOnWhite, 0, 1);
00700
00701 BiasCurve curve;
00702 getBiasCurve(curve);
00703 size_t cs = curve.size();
00704
00705 std::vector<float> values;
00706
00707 for (size_t bin = 0; bin < fft->getHeight(); ++bin) {
00708 float value = m_sliceableModel->getValueAt(col, bin);
00709 if (bin < cs) value *= curve[bin];
00710 values.push_back(value);
00711 }
00712
00713 for (FFTModel::PeakSet::iterator i = peaks.begin();
00714 i != peaks.end(); ++i) {
00715
00716 size_t bin = i->first;
00717
00718
00719
00720 if (!fft->isOverThreshold(col, bin, thresh)) continue;
00721
00722 float freq = i->second;
00723
00724 int x = lrintf(getXForFrequency(freq, w));
00725
00726 float norm = 0.f;
00727 float y = getYForValue(values[bin], v, norm);
00728
00729 paint.setPen(mapper.map(norm));
00730 paint.drawLine(xorigin + x, 0, xorigin + x, v->height() - pkh - 1);
00731 }
00732
00733 paint.restore();
00734 }
00735
00736 SliceLayer::paint(v, paint, rect);
00737
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750 int h = v->height();
00751
00752
00754
00755
00756 paint.drawLine(xorigin, h - pkh - 1, w + xorigin, h - pkh - 1);
00757
00758 int px = xorigin, ppx = xorigin;
00759 paint.setBrush(paint.pen().color());
00760
00761 for (int i = 0; i < 128; ++i) {
00762
00763 float f = Pitch::getFrequencyForPitch(i);
00764 int x = lrintf(getXForFrequency(f, w));
00765
00766 x += xorigin;
00767
00768 if (i == 0) {
00769 px = ppx = x;
00770 }
00771 if (i == 1) {
00772 ppx = px - (x - px);
00773 }
00774
00775 if (x < xorigin) {
00776 ppx = px;
00777 px = x;
00778 continue;
00779 }
00780
00781 if (x > w) {
00782 break;
00783 }
00784
00785 int n = (i % 12);
00786
00787 if (n == 1) {
00788
00789 QColor col = Qt::gray;
00790 if (i == 61) {
00791 col = Qt::blue;
00792 col = col.light(150);
00793 }
00794 if (x - ppx > 2) {
00795 paint.fillRect((px + ppx) / 2 + 1,
00796 h - pkh,
00797 x - (px + ppx) / 2 - 1,
00798 pkh,
00799 col);
00800 }
00801 }
00802
00803 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
00804
00805 paint.drawLine(x, h - pkh, x, h);
00806 int rw = lrintf(float(x - px) / 4) * 2;
00807 if (rw < 2) rw = 2;
00808 paint.drawRect(x - rw/2, h - pkh, rw, pkh/2);
00809 } else if (n == 0 || n == 5) {
00810
00811 if (px < w) {
00812 paint.drawLine((x + px) / 2, h - pkh, (x + px) / 2, h);
00813 }
00814 }
00815
00816 ppx = px;
00817 px = x;
00818 }
00819
00820
00821 paint.restore();
00822 }
00823
00824 void
00825 SpectrumLayer::getBiasCurve(BiasCurve &curve) const
00826 {
00827 curve = m_biasCurve;
00828 }
00829
00830 void
00831 SpectrumLayer::toXml(QTextStream &stream,
00832 QString indent, QString extraAttributes) const
00833 {
00834 QString s = QString("windowSize=\"%1\" "
00835 "windowHopLevel=\"%2\"")
00836 .arg(m_windowSize)
00837 .arg(m_windowHopLevel);
00838
00839 SliceLayer::toXml(stream, indent, extraAttributes + " " + s);
00840 }
00841
00842 void
00843 SpectrumLayer::setProperties(const QXmlAttributes &attributes)
00844 {
00845 SliceLayer::setProperties(attributes);
00846
00847 bool ok = false;
00848
00849 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
00850 if (ok) setWindowSize(windowSize);
00851
00852 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
00853 if (ok) setWindowHopLevel(windowHopLevel);
00854 }
00855
00856