comparison layer/SpectrumLayer.cpp @ 1395:32bbb86094c3

Merge from branch spectrogramparam
author Chris Cannam
date Wed, 14 Nov 2018 14:23:17 +0000
parents 4a36f6130056
children 2e316a724336
comparison
equal deleted inserted replaced
1380:78eecb19e688 1395:32bbb86094c3
37 m_channel(-1), 37 m_channel(-1),
38 m_channelSet(false), 38 m_channelSet(false),
39 m_windowSize(4096), 39 m_windowSize(4096),
40 m_windowType(HanningWindow), 40 m_windowType(HanningWindow),
41 m_windowHopLevel(3), 41 m_windowHopLevel(3),
42 m_oversampling(1),
42 m_showPeaks(false), 43 m_showPeaks(false),
43 m_newFFTNeeded(true) 44 m_newFFTNeeded(true)
44 { 45 {
45 Preferences *prefs = Preferences::getInstance(); 46 Preferences *prefs = Preferences::getInstance();
46 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 47 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
110 111
111 if (!m_originModel) { 112 if (!m_originModel) {
112 return; 113 return;
113 } 114 }
114 115
116 int fftSize = getFFTSize();
117
115 FFTModel *newFFT = new FFTModel(m_originModel, 118 FFTModel *newFFT = new FFTModel(m_originModel,
116 m_channel, 119 m_channel,
117 m_windowType, 120 m_windowType,
118 m_windowSize, 121 m_windowSize,
119 getWindowIncrement(), 122 getWindowIncrement(),
120 m_windowSize); 123 fftSize);
121 124
122 setSliceableModel(newFFT); 125 setSliceableModel(newFFT);
123 126
124 m_biasCurve.clear(); 127 m_biasCurve.clear();
125 for (int i = 0; i < m_windowSize; ++i) { 128 for (int i = 0; i < fftSize; ++i) {
126 m_biasCurve.push_back(1.f / (float(m_windowSize)/2.f)); 129 m_biasCurve.push_back(1.f / (float(fftSize)/2.f));
127 } 130 }
128 131
129 m_newFFTNeeded = false; 132 m_newFFTNeeded = false;
130 } 133 }
131 134
133 SpectrumLayer::getProperties() const 136 SpectrumLayer::getProperties() const
134 { 137 {
135 PropertyList list = SliceLayer::getProperties(); 138 PropertyList list = SliceLayer::getProperties();
136 list.push_back("Window Size"); 139 list.push_back("Window Size");
137 list.push_back("Window Increment"); 140 list.push_back("Window Increment");
141 list.push_back("Oversampling");
138 list.push_back("Show Peak Frequencies"); 142 list.push_back("Show Peak Frequencies");
139 return list; 143 return list;
140 } 144 }
141 145
142 QString 146 QString
143 SpectrumLayer::getPropertyLabel(const PropertyName &name) const 147 SpectrumLayer::getPropertyLabel(const PropertyName &name) const
144 { 148 {
145 if (name == "Window Size") return tr("Window Size"); 149 if (name == "Window Size") return tr("Window Size");
146 if (name == "Window Increment") return tr("Window Overlap"); 150 if (name == "Window Increment") return tr("Window Overlap");
151 if (name == "Oversampling") return tr("Oversampling");
147 if (name == "Show Peak Frequencies") return tr("Show Peak Frequencies"); 152 if (name == "Show Peak Frequencies") return tr("Show Peak Frequencies");
148 return SliceLayer::getPropertyLabel(name); 153 return SliceLayer::getPropertyLabel(name);
149 } 154 }
150 155
151 QString 156 QString
158 Layer::PropertyType 163 Layer::PropertyType
159 SpectrumLayer::getPropertyType(const PropertyName &name) const 164 SpectrumLayer::getPropertyType(const PropertyName &name) const
160 { 165 {
161 if (name == "Window Size") return ValueProperty; 166 if (name == "Window Size") return ValueProperty;
162 if (name == "Window Increment") return ValueProperty; 167 if (name == "Window Increment") return ValueProperty;
168 if (name == "Oversampling") return ValueProperty;
163 if (name == "Show Peak Frequencies") return ToggleProperty; 169 if (name == "Show Peak Frequencies") return ToggleProperty;
164 return SliceLayer::getPropertyType(name); 170 return SliceLayer::getPropertyType(name);
165 } 171 }
166 172
167 QString 173 QString
168 SpectrumLayer::getPropertyGroupName(const PropertyName &name) const 174 SpectrumLayer::getPropertyGroupName(const PropertyName &name) const
169 { 175 {
170 if (name == "Window Size" || 176 if (name == "Window Size" ||
171 name == "Window Increment") return tr("Window"); 177 name == "Window Increment" ||
178 name == "Oversampling") return tr("Window");
172 if (name == "Show Peak Frequencies") return tr("Bins"); 179 if (name == "Show Peak Frequencies") return tr("Bins");
173 return SliceLayer::getPropertyGroupName(name); 180 return SliceLayer::getPropertyGroupName(name);
174 } 181 }
175 182
176 int 183 int
200 *max = 5; 207 *max = 5;
201 *deflt = 2; 208 *deflt = 2;
202 209
203 val = m_windowHopLevel; 210 val = m_windowHopLevel;
204 211
212 } else if (name == "Oversampling") {
213
214 *min = 0;
215 *max = 3;
216 *deflt = 0;
217
218 val = 0;
219 int ov = m_oversampling;
220 while (ov > 1) { ov >>= 1; val ++; }
221
205 } else if (name == "Show Peak Frequencies") { 222 } else if (name == "Show Peak Frequencies") {
206 223
207 return m_showPeaks ? 1 : 0; 224 return m_showPeaks ? 1 : 0;
208 225
209 } else { 226 } else {
230 case 3: return tr("75 %"); 247 case 3: return tr("75 %");
231 case 4: return tr("87.5 %"); 248 case 4: return tr("87.5 %");
232 case 5: return tr("93.75 %"); 249 case 5: return tr("93.75 %");
233 } 250 }
234 } 251 }
252 if (name == "Oversampling") {
253 switch (value) {
254 default:
255 case 0: return tr("1x");
256 case 1: return tr("2x");
257 case 2: return tr("4x");
258 case 3: return tr("8x");
259 }
260 }
235 return SliceLayer::getPropertyValueLabel(name, value); 261 return SliceLayer::getPropertyValueLabel(name, value);
236 } 262 }
237 263
238 RangeMapper * 264 RangeMapper *
239 SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const 265 SpectrumLayer::getNewPropertyRangeMapper(const PropertyName &name) const
246 { 272 {
247 if (name == "Window Size") { 273 if (name == "Window Size") {
248 setWindowSize(32 << value); 274 setWindowSize(32 << value);
249 } else if (name == "Window Increment") { 275 } else if (name == "Window Increment") {
250 setWindowHopLevel(value); 276 setWindowHopLevel(value);
277 } else if (name == "Oversampling") {
278 setOversampling(1 << value);
251 } else if (name == "Show Peak Frequencies") { 279 } else if (name == "Show Peak Frequencies") {
252 setShowPeaks(value ? true : false); 280 setShowPeaks(value ? true : false);
253 } else { 281 } else {
254 SliceLayer::setProperty(name, value); 282 SliceLayer::setProperty(name, value);
255 } 283 }
257 285
258 void 286 void
259 SpectrumLayer::setWindowSize(int ws) 287 SpectrumLayer::setWindowSize(int ws)
260 { 288 {
261 if (m_windowSize == ws) return; 289 if (m_windowSize == ws) return;
290
291 SVDEBUG << "setWindowSize: from " << m_windowSize
292 << " to " << ws << ": updating min and max bins from "
293 << m_minbin << " and " << m_maxbin << " to ";
294
295 m_minbin = int(round((double(m_minbin) / m_windowSize) * ws));
296 m_maxbin = int(round((double(m_maxbin) / m_windowSize) * ws));
297
298 SVDEBUG << m_minbin << " and " << m_maxbin << endl;
299
262 m_windowSize = ws; 300 m_windowSize = ws;
263 m_newFFTNeeded = true; 301 m_newFFTNeeded = true;
264 emit layerParametersChanged(); 302 emit layerParametersChanged();
265 } 303 }
266 304
281 m_newFFTNeeded = true; 319 m_newFFTNeeded = true;
282 emit layerParametersChanged(); 320 emit layerParametersChanged();
283 } 321 }
284 322
285 void 323 void
324 SpectrumLayer::setOversampling(int oversampling)
325 {
326 if (m_oversampling == oversampling) return;
327
328 SVDEBUG << "setOversampling: from " << m_oversampling
329 << " to " << oversampling << ": updating min and max bins from "
330 << m_minbin << " and " << m_maxbin << " to ";
331
332 m_minbin = int(round((double(m_minbin) / m_oversampling) * oversampling));
333 m_maxbin = int(round((double(m_maxbin) / m_oversampling) * oversampling));
334
335 SVDEBUG << m_minbin << " and " << m_maxbin << endl;
336
337 m_oversampling = oversampling;
338 m_newFFTNeeded = true;
339
340 emit layerParametersChanged();
341 }
342
343 int
344 SpectrumLayer::getOversampling() const
345 {
346 return m_oversampling;
347 }
348
349 void
286 SpectrumLayer::setShowPeaks(bool show) 350 SpectrumLayer::setShowPeaks(bool show)
287 { 351 {
288 if (m_showPeaks == show) return; 352 if (m_showPeaks == show) return;
289 m_showPeaks = show; 353 m_showPeaks = show;
290 emit layerParametersChanged(); 354 emit layerParametersChanged();
292 356
293 void 357 void
294 SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name) 358 SpectrumLayer::preferenceChanged(PropertyContainer::PropertyName name)
295 { 359 {
296 if (name == "Window Type") { 360 if (name == "Window Type") {
297 setWindowType(Preferences::getInstance()->getWindowType()); 361 auto type = Preferences::getInstance()->getWindowType();
362 SVDEBUG << "SpectrumLayer::preferenceChanged: Window type changed to "
363 << type << endl;
364 setWindowType(type);
298 return; 365 return;
299 } 366 }
367 }
368
369 double
370 SpectrumLayer::getBinForFrequency(double freq) const
371 {
372 if (!m_sliceableModel) return 0;
373 double bin = (freq * getFFTSize()) / m_sliceableModel->getSampleRate();
374 // we assume the frequency of a bin corresponds to the centre of
375 // its visual range
376 bin += 0.5;
377 return bin;
378 }
379
380 double
381 SpectrumLayer::getBinForX(const LayerGeometryProvider *v, double x) const
382 {
383 if (!m_sliceableModel) return 0;
384 double bin = getBinForFrequency(getFrequencyForX(v, x));
385 return bin;
300 } 386 }
301 387
302 double 388 double
303 SpectrumLayer::getFrequencyForX(const LayerGeometryProvider *v, double x) const 389 SpectrumLayer::getFrequencyForX(const LayerGeometryProvider *v, double x) const
304 { 390 {
305 if (!m_sliceableModel) return 0; 391 if (!m_sliceableModel) return 0;
306 double bin = getBinForX(v, x); 392
393 double fmin = getFrequencyForBin(m_minbin);
394
395 if (m_binScale == LogBins && m_minbin == 0) {
396 // Avoid too much space going to the first bin, but do so in a
397 // way that usually avoids us shifting left/right as the
398 // window size or oversampling ratio change - i.e. base this
399 // on frequency rather than bin number unless we have a lot of
400 // very low-resolution content
401 fmin = getFrequencyForBin(0.8);
402 if (fmin > 6.0) fmin = 6.0;
403 }
404
405 double fmax = getFrequencyForBin(m_maxbin);
406
407 double freq = getScalePointForX(v, x, fmin, fmax);
408 return freq;
409 }
410
411 double
412 SpectrumLayer::getFrequencyForBin(double bin) const
413 {
414 if (!m_sliceableModel) return 0;
307 // we assume the frequency of a bin corresponds to the centre of 415 // we assume the frequency of a bin corresponds to the centre of
308 // its visual range 416 // its visual range
309 bin -= 0.5; 417 bin -= 0.5;
310 return (m_sliceableModel->getSampleRate() * bin) / 418 double freq = (bin * m_sliceableModel->getSampleRate()) / getFFTSize();
311 (m_sliceableModel->getHeight() * 2); 419 return freq;
420 }
421
422 double
423 SpectrumLayer::getXForBin(const LayerGeometryProvider *v, double bin) const
424 {
425 if (!m_sliceableModel) return 0;
426 double x = getXForFrequency(v, getFrequencyForBin(bin));
427 return x;
312 } 428 }
313 429
314 double 430 double
315 SpectrumLayer::getXForFrequency(const LayerGeometryProvider *v, double freq) const 431 SpectrumLayer::getXForFrequency(const LayerGeometryProvider *v, double freq) const
316 { 432 {
317 if (!m_sliceableModel) return 0; 433 if (!m_sliceableModel) return 0;
318 double bin = (freq * m_sliceableModel->getHeight() * 2) / 434
319 m_sliceableModel->getSampleRate(); 435 double fmin = getFrequencyForBin(m_minbin);
320 // we want the centre of the bin range 436 if (m_binScale == LogBins && m_minbin == 0) {
321 bin += 0.5; 437 // See comment in getFrequencyForX above
322 return getXForBin(v, bin); 438 fmin = getFrequencyForBin(0.8);
439 if (fmin > 6.0) fmin = 6.0;
440 }
441
442 double fmax = getFrequencyForBin(m_maxbin);
443
444 double x = getXForScalePoint(v, freq, fmin, fmax);
445 return x;
323 } 446 }
324 447
325 bool 448 bool
326 SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x, 449 SpectrumLayer::getXScaleValue(const LayerGeometryProvider *v, int x,
327 double &value, QString &unit) const 450 double &value, QString &unit) const
425 paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y()); 548 paint.drawLine(xorigin, cursorPos.y(), v->getPaintWidth(), cursorPos.y());
426 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight()); 549 paint.drawLine(cursorPos.x(), cursorPos.y(), cursorPos.x(), v->getPaintHeight());
427 550
428 double fundamental = getFrequencyForX(v, cursorPos.x()); 551 double fundamental = getFrequencyForX(v, cursorPos.x());
429 552
430 int hoffset = 2; 553 int hoffset = getHorizontalScaleHeight(v, paint) +
431 if (m_binScale == LogBins) hoffset = 13; 554 2 * paint.fontMetrics().height();
432 555
433 PaintAssistant::drawVisibleText(v, paint, 556 PaintAssistant::drawVisibleText(v, paint,
434 cursorPos.x() + 2, 557 cursorPos.x() + 2,
435 v->getPaintHeight() - 2 - hoffset, 558 v->getPaintHeight() - 2 - hoffset,
436 QString("%1 Hz").arg(fundamental), 559 tr("%1 Hz").arg(fundamental),
437 PaintAssistant::OutlinedText); 560 PaintAssistant::OutlinedText);
438 561
439 if (Pitch::isFrequencyInMidiRange(fundamental)) { 562 if (Pitch::isFrequencyInMidiRange(fundamental)) {
440 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental); 563 QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
441 PaintAssistant::drawVisibleText(v, paint, 564 PaintAssistant::drawVisibleText(v, paint,
445 pitchLabel, 568 pitchLabel,
446 PaintAssistant::OutlinedText); 569 PaintAssistant::OutlinedText);
447 } 570 }
448 571
449 double value = getValueForY(v, cursorPos.y()); 572 double value = getValueForY(v, cursorPos.y());
450 double thresh = m_threshold;
451 double db = thresh;
452 if (value > 0.0) db = 10.0 * log10(value);
453 if (db < thresh) db = thresh;
454 573
455 PaintAssistant::drawVisibleText(v, paint, 574 PaintAssistant::drawVisibleText(v, paint,
456 xorigin + 2, 575 xorigin + 2,
457 cursorPos.y() - 2, 576 cursorPos.y() - 2,
458 QString("%1 V").arg(value), 577 QString("%1 V").arg(value),
459 PaintAssistant::OutlinedText); 578 PaintAssistant::OutlinedText);
460 579
461 PaintAssistant::drawVisibleText(v, paint, 580 if (value > m_threshold) {
462 xorigin + 2, 581 double db = 10.0 * log10(value);
463 cursorPos.y() + 2 + paint.fontMetrics().ascent(), 582 PaintAssistant::drawVisibleText(v, paint,
464 QString("%1 dBV").arg(db), 583 xorigin + 2,
465 PaintAssistant::OutlinedText); 584 cursorPos.y() + 2 +
585 paint.fontMetrics().ascent(),
586 QString("%1 dBV").arg(db),
587 PaintAssistant::OutlinedText);
588 }
466 589
467 int harmonic = 2; 590 int harmonic = 2;
468 591
469 while (harmonic < 100) { 592 while (harmonic < 100) {
470 593
516 if (minvalue > maxvalue) std::swap(minvalue, maxvalue); 639 if (minvalue > maxvalue) std::swap(minvalue, maxvalue);
517 640
518 QString binstr; 641 QString binstr;
519 QString hzstr; 642 QString hzstr;
520 int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) / 643 int minfreq = int(lrint((minbin * m_sliceableModel->getSampleRate()) /
521 m_windowSize)); 644 getFFTSize()));
522 int maxfreq = int(lrint((std::max(maxbin, minbin) 645 int maxfreq = int(lrint((std::max(maxbin, minbin)
523 * m_sliceableModel->getSampleRate()) / 646 * m_sliceableModel->getSampleRate()) /
524 m_windowSize)); 647 getFFTSize()));
525 648
526 if (maxbin != minbin) { 649 if (maxbin != minbin) {
527 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); 650 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1);
528 } else { 651 } else {
529 binstr = QString("%1").arg(minbin+1); 652 binstr = QString("%1").arg(minbin+1);
600 } 723 }
601 724
602 FFTModel *fft = dynamic_cast<FFTModel *> 725 FFTModel *fft = dynamic_cast<FFTModel *>
603 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel)); 726 (const_cast<DenseThreeDimensionalModel *>(m_sliceableModel));
604 727
605 double thresh = (pow(10, -6) / m_gain) * (m_windowSize / 2.0); // -60dB adj 728 double thresh = (pow(10, -6) / m_gain) * (getFFTSize() / 2.0); // -60dB adj
606 729
607 int xorigin = getVerticalScaleWidth(v, false, paint) + 1; 730 int xorigin = getVerticalScaleWidth(v, false, paint) + 1;
608 int scaleHeight = getHorizontalScaleHeight(v, paint); 731 int scaleHeight = getHorizontalScaleHeight(v, paint);
732
733 QPoint localPos;
734 bool shouldIlluminate = v->shouldIlluminateLocalFeatures(this, localPos);
735
736 // cerr << "shouldIlluminate = " << shouldIlluminate << ", localPos = " << localPos.x() << "," << localPos.y() << endl;
609 737
610 if (fft && m_showPeaks) { 738 if (fft && m_showPeaks) {
611 739
612 // draw peak lines 740 // draw peak lines
613 741
622 ColourMapper(ColourMapper::WhiteOnBlack, m_colourInverted, 0, 1); 750 ColourMapper(ColourMapper::WhiteOnBlack, m_colourInverted, 0, 1);
623 751
624 int peakminbin = 0; 752 int peakminbin = 0;
625 int peakmaxbin = fft->getHeight() - 1; 753 int peakmaxbin = fft->getHeight() - 1;
626 double peakmaxfreq = Pitch::getFrequencyForPitch(128); 754 double peakmaxfreq = Pitch::getFrequencyForPitch(128);
627 peakmaxbin = int(((peakmaxfreq * fft->getHeight() * 2) / fft->getSampleRate())); 755 peakmaxbin = int(((peakmaxfreq * fft->getHeight() * 2) /
756 fft->getSampleRate()));
628 757
629 FFTModel::PeakSet peaks = fft->getPeakFrequencies 758 FFTModel::PeakSet peaks = fft->getPeakFrequencies
630 (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin); 759 (FFTModel::MajorPitchAdaptivePeaks, col, peakminbin, peakmaxbin);
631 760
632 BiasCurve curve; 761 BiasCurve curve;
633 getBiasCurve(curve); 762 getBiasCurve(curve);
634 int cs = int(curve.size()); 763 int cs = int(curve.size());
635 764
636 std::vector<double> values; 765 int px = -1;
766
767 int fuzz = ViewManager::scalePixelSize(3);
768 bool illuminatedSomething = false;
637 769
638 for (int bin = 0; bin < fft->getHeight(); ++bin) {
639 double value = m_sliceableModel->getValueAt(col, bin);
640 if (bin < cs) value *= curve[bin];
641 values.push_back(value);
642 }
643
644 for (FFTModel::PeakSet::iterator i = peaks.begin(); 770 for (FFTModel::PeakSet::iterator i = peaks.begin();
645 i != peaks.end(); ++i) { 771 i != peaks.end(); ++i) {
646 772
773 double freq = i->second;
774 int x = int(lrint(getXForFrequency(v, freq)));
775 if (x == px) {
776 continue;
777 }
778
647 int bin = i->first; 779 int bin = i->first;
648 780
649 // cerr << "bin = " << bin << ", thresh = " << thresh << ", value = " << fft->getMagnitudeAt(col, bin) << endl; 781 // cerr << "bin = " << bin << ", thresh = " << thresh << ", value = " << fft->getMagnitudeAt(col, bin) << endl;
650 782
651 if (!fft->isOverThreshold(col, bin, float(thresh))) continue; 783 double value = fft->getValueAt(col, bin);
784 if (value < thresh) continue;
785 if (bin < cs) value *= curve[bin];
652 786
653 double freq = i->second;
654
655 int x = int(lrint(getXForFrequency(v, freq)));
656
657 double norm = 0.f; 787 double norm = 0.f;
658 (void)getYForValue(v, values[bin], norm); // don't need return value, need norm 788 // we need the norm here for colour map; the y coord is
659 789 // only used to pick a label height if illuminating the
660 paint.setPen(mapper.map(norm)); 790 // local point
791 double y = getYForValue(v, value, norm);
792
793 QColor colour = mapper.map(norm);
794
795 paint.setPen(QPen(colour, 1));
661 paint.drawLine(x, 0, x, v->getPaintHeight() - scaleHeight - 1); 796 paint.drawLine(x, 0, x, v->getPaintHeight() - scaleHeight - 1);
797
798 bool illuminateThis = false;
799 if (shouldIlluminate && !illuminatedSomething &&
800 std::abs(localPos.x() - x) <= fuzz) {
801 illuminateThis = true;
802 }
803
804 if (illuminateThis) {
805 int labelY = v->getPaintHeight() -
806 getHorizontalScaleHeight(v, paint) -
807 paint.fontMetrics().height() * 3;
808 QString text = tr("%1 Hz").arg(freq);
809 int lw = paint.fontMetrics().width(text);
810 int gap = ViewManager::scalePixelSize(3);
811 double half = double(gap)/2.0;
812 int labelX = x - lw - gap;
813 if (labelX < getVerticalScaleWidth(v, false, paint)) {
814 labelX = x + gap;
815 }
816 PaintAssistant::drawVisibleText
817 (v, paint, labelX, labelY,
818 text, PaintAssistant::OutlinedText);
819 if (Pitch::isFrequencyInMidiRange(freq)) {
820 QString pitchLabel = Pitch::getPitchLabelForFrequency(freq);
821 PaintAssistant::drawVisibleText
822 (v, paint,
823 labelX, labelY + paint.fontMetrics().ascent() + gap,
824 pitchLabel, PaintAssistant::OutlinedText);
825 }
826 paint.fillRect(QRectF(x - half, labelY + gap, gap, gap),
827 colour);
828 illuminatedSomething = true;
829 }
830
831 px = x;
662 } 832 }
663 833
664 paint.restore(); 834 paint.restore();
665 } 835 }
666 836
744 SpectrumLayer::toXml(QTextStream &stream, 914 SpectrumLayer::toXml(QTextStream &stream,
745 QString indent, QString extraAttributes) const 915 QString indent, QString extraAttributes) const
746 { 916 {
747 QString s = QString("windowSize=\"%1\" " 917 QString s = QString("windowSize=\"%1\" "
748 "windowHopLevel=\"%2\" " 918 "windowHopLevel=\"%2\" "
749 "showPeaks=\"%3\" ") 919 "oversampling=\"%3\" "
920 "showPeaks=\"%4\" ")
750 .arg(m_windowSize) 921 .arg(m_windowSize)
751 .arg(m_windowHopLevel) 922 .arg(m_windowHopLevel)
923 .arg(m_oversampling)
752 .arg(m_showPeaks ? "true" : "false"); 924 .arg(m_showPeaks ? "true" : "false");
753 925
754 SliceLayer::toXml(stream, indent, extraAttributes + " " + s); 926 SliceLayer::toXml(stream, indent, extraAttributes + " " + s);
755 } 927 }
756 928
765 if (ok) setWindowSize(windowSize); 937 if (ok) setWindowSize(windowSize);
766 938
767 int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok); 939 int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
768 if (ok) setWindowHopLevel(windowHopLevel); 940 if (ok) setWindowHopLevel(windowHopLevel);
769 941
942 int oversampling = attributes.value("oversampling").toUInt(&ok);
943 if (ok) setOversampling(oversampling);
944
770 bool showPeaks = (attributes.value("showPeaks").trimmed() == "true"); 945 bool showPeaks = (attributes.value("showPeaks").trimmed() == "true");
771 setShowPeaks(showPeaks); 946 setShowPeaks(showPeaks);
772 } 947 }
773 948
774 949