comparison layer/SpectrogramLayer.cpp @ 0:fc9323a41f5a

start base : Sonic Visualiser sv1-1.0rc1
author lbajardsilogic
date Fri, 11 May 2007 09:08:14 +0000
parents
children d8e6709e9075
comparison
equal deleted inserted replaced
-1:000000000000 0:fc9323a41f5a
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "SpectrogramLayer.h"
17
18 #include "view/View.h"
19 #include "base/Profiler.h"
20 #include "base/AudioLevel.h"
21 #include "base/Window.h"
22 #include "base/Pitch.h"
23 #include "base/Preferences.h"
24 #include "base/RangeMapper.h"
25 #include "ColourMapper.h"
26
27 #include <QPainter>
28 #include <QImage>
29 #include <QPixmap>
30 #include <QRect>
31 #include <QTimer>
32 #include <QApplication>
33 #include <QMessageBox>
34
35 #include <iostream>
36
37 #include <cassert>
38 #include <cmath>
39
40 //#define DEBUG_SPECTROGRAM_REPAINT 1
41
42 SpectrogramLayer::SpectrogramLayer(Configuration config) :
43 m_model(0),
44 m_channel(0),
45 m_windowSize(1024),
46 m_windowType(HanningWindow),
47 m_windowHopLevel(2),
48 m_zeroPadLevel(0),
49 m_fftSize(1024),
50 m_gain(1.0),
51 m_initialGain(1.0),
52 m_threshold(0.0),
53 m_initialThreshold(0.0),
54 m_colourRotation(0),
55 m_initialRotation(0),
56 m_minFrequency(10),
57 m_maxFrequency(8000),
58 m_initialMaxFrequency(8000),
59 m_colourScale(dBColourScale),
60 m_colourMap(0),
61 m_frequencyScale(LinearFrequencyScale),
62 m_binDisplay(AllBins),
63 m_normalizeColumns(false),
64 m_normalizeVisibleArea(false),
65 m_lastEmittedZoomStep(-1),
66 m_lastPaintBlockWidth(0),
67 m_updateTimer(0),
68 m_candidateFillStartFrame(0),
69 m_exiting(false),
70 m_sliceableModel(0)
71 {
72 if (config == FullRangeDb) {
73 m_initialMaxFrequency = 0;
74 setMaxFrequency(0);
75 } else if (config == MelodicRange) {
76 setWindowSize(8192);
77 setWindowHopLevel(4);
78 m_initialMaxFrequency = 1500;
79 setMaxFrequency(1500);
80 setMinFrequency(40);
81 setColourScale(LinearColourScale);
82 setColourMap(ColourMapper::Sunset);
83 setFrequencyScale(LogFrequencyScale);
84 // setGain(20);
85 } else if (config == MelodicPeaks) {
86 setWindowSize(4096);
87 setWindowHopLevel(5);
88 m_initialMaxFrequency = 2000;
89 setMaxFrequency(2000);
90 setMinFrequency(40);
91 setFrequencyScale(LogFrequencyScale);
92 setColourScale(LinearColourScale);
93 setBinDisplay(PeakFrequencies);
94 setNormalizeColumns(true);
95 }
96
97 Preferences *prefs = Preferences::getInstance();
98 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
99 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
100 setWindowType(prefs->getWindowType());
101
102 initialisePalette();
103 }
104
105 SpectrogramLayer::~SpectrogramLayer()
106 {
107 delete m_updateTimer;
108 m_updateTimer = 0;
109
110 invalidateFFTModels();
111 }
112
113 void
114 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
115 {
116 // std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl;
117
118 if (model == m_model) return;
119
120 m_model = model;
121 invalidateFFTModels();
122
123 if (!m_model || !m_model->isOK()) return;
124
125 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
126 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
127 this, SIGNAL(modelChanged(size_t, size_t)));
128
129 connect(m_model, SIGNAL(completionChanged()),
130 this, SIGNAL(modelCompletionChanged()));
131
132 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
133 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
134 this, SLOT(cacheInvalid(size_t, size_t)));
135
136 emit modelReplaced();
137 }
138
139 Layer::PropertyList
140 SpectrogramLayer::getProperties() const
141 {
142 PropertyList list;
143 list.push_back("Colour");
144 list.push_back("Colour Scale");
145 list.push_back("Window Size");
146 list.push_back("Window Increment");
147 list.push_back("Normalize Columns");
148 list.push_back("Normalize Visible Area");
149 list.push_back("Bin Display");
150 list.push_back("Threshold");
151 list.push_back("Gain");
152 list.push_back("Colour Rotation");
153 // list.push_back("Min Frequency");
154 // list.push_back("Max Frequency");
155 list.push_back("Frequency Scale");
156 //// list.push_back("Zero Padding");
157 return list;
158 }
159
160 QString
161 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
162 {
163 if (name == "Colour") return tr("Colour");
164 if (name == "Colour Scale") return tr("Colour Scale");
165 if (name == "Window Size") return tr("Window Size");
166 if (name == "Window Increment") return tr("Window Overlap");
167 if (name == "Normalize Columns") return tr("Normalize Columns");
168 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
169 if (name == "Bin Display") return tr("Bin Display");
170 if (name == "Threshold") return tr("Threshold");
171 if (name == "Gain") return tr("Gain");
172 if (name == "Colour Rotation") return tr("Colour Rotation");
173 if (name == "Min Frequency") return tr("Min Frequency");
174 if (name == "Max Frequency") return tr("Max Frequency");
175 if (name == "Frequency Scale") return tr("Frequency Scale");
176 if (name == "Zero Padding") return tr("Smoothing");
177 return "";
178 }
179
180 Layer::PropertyType
181 SpectrogramLayer::getPropertyType(const PropertyName &name) const
182 {
183 if (name == "Gain") return RangeProperty;
184 if (name == "Colour Rotation") return RangeProperty;
185 if (name == "Normalize Columns") return ToggleProperty;
186 if (name == "Normalize Visible Area") return ToggleProperty;
187 if (name == "Threshold") return RangeProperty;
188 if (name == "Zero Padding") return ToggleProperty;
189 return ValueProperty;
190 }
191
192 QString
193 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
194 {
195 if (name == "Bin Display" ||
196 name == "Frequency Scale") return tr("Bins");
197 if (name == "Window Size" ||
198 name == "Window Increment" ||
199 name == "Zero Padding") return tr("Window");
200 if (name == "Colour" ||
201 name == "Threshold" ||
202 name == "Colour Rotation") return tr("Colour");
203 if (name == "Normalize Columns" ||
204 name == "Normalize Visible Area" ||
205 name == "Gain" ||
206 name == "Colour Scale") return tr("Scale");
207 return QString();
208 }
209
210 int
211 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name,
212 int *min, int *max, int *deflt) const
213 {
214 int val = 0;
215
216 int garbage0, garbage1, garbage2;
217 if (!min) min = &garbage0;
218 if (!max) max = &garbage1;
219 if (!deflt) deflt = &garbage2;
220
221 if (name == "Gain") {
222
223 *min = -50;
224 *max = 50;
225
226 *deflt = lrintf(log10(m_initialGain) * 20.0);;
227 if (*deflt < *min) *deflt = *min;
228 if (*deflt > *max) *deflt = *max;
229
230 val = lrintf(log10(m_gain) * 20.0);
231 if (val < *min) val = *min;
232 if (val > *max) val = *max;
233
234 } else if (name == "Threshold") {
235
236 *min = -50;
237 *max = 0;
238
239 *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold));
240 if (*deflt < *min) *deflt = *min;
241 if (*deflt > *max) *deflt = *max;
242
243 val = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
244 if (val < *min) val = *min;
245 if (val > *max) val = *max;
246
247 } else if (name == "Colour Rotation") {
248
249 *min = 0;
250 *max = 256;
251 *deflt = m_initialRotation;
252
253 val = m_colourRotation;
254
255 } else if (name == "Colour Scale") {
256
257 *min = 0;
258 *max = 4;
259 *deflt = int(dBColourScale);
260
261 val = (int)m_colourScale;
262
263 } else if (name == "Colour") {
264
265 *min = 0;
266 *max = ColourMapper::getColourMapCount() - 1;
267 *deflt = 0;
268
269 val = m_colourMap;
270
271 } else if (name == "Window Size") {
272
273 *min = 0;
274 *max = 10;
275 *deflt = 5;
276
277 val = 0;
278 int ws = m_windowSize;
279 while (ws > 32) { ws >>= 1; val ++; }
280
281 } else if (name == "Window Increment") {
282
283 *min = 0;
284 *max = 5;
285 *deflt = 2;
286
287 val = m_windowHopLevel;
288
289 } else if (name == "Zero Padding") {
290
291 *min = 0;
292 *max = 1;
293 *deflt = 0;
294
295 val = m_zeroPadLevel > 0 ? 1 : 0;
296
297 } else if (name == "Min Frequency") {
298
299 *min = 0;
300 *max = 9;
301 *deflt = 1;
302
303 switch (m_minFrequency) {
304 case 0: default: val = 0; break;
305 case 10: val = 1; break;
306 case 20: val = 2; break;
307 case 40: val = 3; break;
308 case 100: val = 4; break;
309 case 250: val = 5; break;
310 case 500: val = 6; break;
311 case 1000: val = 7; break;
312 case 4000: val = 8; break;
313 case 10000: val = 9; break;
314 }
315
316 } else if (name == "Max Frequency") {
317
318 *min = 0;
319 *max = 9;
320 *deflt = 6;
321
322 switch (m_maxFrequency) {
323 case 500: val = 0; break;
324 case 1000: val = 1; break;
325 case 1500: val = 2; break;
326 case 2000: val = 3; break;
327 case 4000: val = 4; break;
328 case 6000: val = 5; break;
329 case 8000: val = 6; break;
330 case 12000: val = 7; break;
331 case 16000: val = 8; break;
332 default: val = 9; break;
333 }
334
335 } else if (name == "Frequency Scale") {
336
337 *min = 0;
338 *max = 1;
339 *deflt = int(LinearFrequencyScale);
340 val = (int)m_frequencyScale;
341
342 } else if (name == "Bin Display") {
343
344 *min = 0;
345 *max = 2;
346 *deflt = int(AllBins);
347 val = (int)m_binDisplay;
348
349 } else if (name == "Normalize Columns") {
350
351 *deflt = 0;
352 val = (m_normalizeColumns ? 1 : 0);
353
354 } else if (name == "Normalize Visible Area") {
355
356 *deflt = 0;
357 val = (m_normalizeVisibleArea ? 1 : 0);
358
359 } else {
360 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
361 }
362
363 return val;
364 }
365
366 QString
367 SpectrogramLayer::getPropertyValueLabel(const PropertyName &name,
368 int value) const
369 {
370 if (name == "Colour") {
371 return ColourMapper::getColourMapName(value);
372 }
373 if (name == "Colour Scale") {
374 switch (value) {
375 default:
376 case 0: return tr("Linear");
377 case 1: return tr("Meter");
378 case 2: return tr("dBV^2");
379 case 3: return tr("dBV");
380 case 4: return tr("Phase");
381 }
382 }
383 if (name == "Window Size") {
384 return QString("%1").arg(32 << value);
385 }
386 if (name == "Window Increment") {
387 switch (value) {
388 default:
389 case 0: return tr("None");
390 case 1: return tr("25 %");
391 case 2: return tr("50 %");
392 case 3: return tr("75 %");
393 case 4: return tr("87.5 %");
394 case 5: return tr("93.75 %");
395 }
396 }
397 if (name == "Zero Padding") {
398 if (value == 0) return tr("None");
399 return QString("%1x").arg(value + 1);
400 }
401 if (name == "Min Frequency") {
402 switch (value) {
403 default:
404 case 0: return tr("No min");
405 case 1: return tr("10 Hz");
406 case 2: return tr("20 Hz");
407 case 3: return tr("40 Hz");
408 case 4: return tr("100 Hz");
409 case 5: return tr("250 Hz");
410 case 6: return tr("500 Hz");
411 case 7: return tr("1 KHz");
412 case 8: return tr("4 KHz");
413 case 9: return tr("10 KHz");
414 }
415 }
416 if (name == "Max Frequency") {
417 switch (value) {
418 default:
419 case 0: return tr("500 Hz");
420 case 1: return tr("1 KHz");
421 case 2: return tr("1.5 KHz");
422 case 3: return tr("2 KHz");
423 case 4: return tr("4 KHz");
424 case 5: return tr("6 KHz");
425 case 6: return tr("8 KHz");
426 case 7: return tr("12 KHz");
427 case 8: return tr("16 KHz");
428 case 9: return tr("No max");
429 }
430 }
431 if (name == "Frequency Scale") {
432 switch (value) {
433 default:
434 case 0: return tr("Linear");
435 case 1: return tr("Log");
436 }
437 }
438 if (name == "Bin Display") {
439 switch (value) {
440 default:
441 case 0: return tr("All Bins");
442 case 1: return tr("Peak Bins");
443 case 2: return tr("Frequencies");
444 }
445 }
446 return tr("<unknown>");
447 }
448
449 RangeMapper *
450 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
451 {
452 if (name == "Gain") {
453 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
454 }
455 if (name == "Threshold") {
456 return new LinearRangeMapper(-50, 0, -50, 0, tr("dB"));
457 }
458 return 0;
459 }
460
461 void
462 SpectrogramLayer::setProperty(const PropertyName &name, int value)
463 {
464 if (name == "Gain") {
465 setGain(pow(10, float(value)/20.0));
466 } else if (name == "Threshold") {
467 if (value == -50) setThreshold(0.0);
468 else setThreshold(AudioLevel::dB_to_multiplier(value));
469 } else if (name == "Colour Rotation") {
470 setColourRotation(value);
471 } else if (name == "Colour") {
472 setColourMap(value);
473 } else if (name == "Window Size") {
474 setWindowSize(32 << value);
475 } else if (name == "Window Increment") {
476 setWindowHopLevel(value);
477 } else if (name == "Zero Padding") {
478 setZeroPadLevel(value > 0.1 ? 3 : 0);
479 } else if (name == "Min Frequency") {
480 switch (value) {
481 default:
482 case 0: setMinFrequency(0); break;
483 case 1: setMinFrequency(10); break;
484 case 2: setMinFrequency(20); break;
485 case 3: setMinFrequency(40); break;
486 case 4: setMinFrequency(100); break;
487 case 5: setMinFrequency(250); break;
488 case 6: setMinFrequency(500); break;
489 case 7: setMinFrequency(1000); break;
490 case 8: setMinFrequency(4000); break;
491 case 9: setMinFrequency(10000); break;
492 }
493 int vs = getCurrentVerticalZoomStep();
494 if (vs != m_lastEmittedZoomStep) {
495 emit verticalZoomChanged();
496 m_lastEmittedZoomStep = vs;
497 }
498 } else if (name == "Max Frequency") {
499 switch (value) {
500 case 0: setMaxFrequency(500); break;
501 case 1: setMaxFrequency(1000); break;
502 case 2: setMaxFrequency(1500); break;
503 case 3: setMaxFrequency(2000); break;
504 case 4: setMaxFrequency(4000); break;
505 case 5: setMaxFrequency(6000); break;
506 case 6: setMaxFrequency(8000); break;
507 case 7: setMaxFrequency(12000); break;
508 case 8: setMaxFrequency(16000); break;
509 default:
510 case 9: setMaxFrequency(0); break;
511 }
512 int vs = getCurrentVerticalZoomStep();
513 if (vs != m_lastEmittedZoomStep) {
514 emit verticalZoomChanged();
515 m_lastEmittedZoomStep = vs;
516 }
517 } else if (name == "Colour Scale") {
518 switch (value) {
519 default:
520 case 0: setColourScale(LinearColourScale); break;
521 case 1: setColourScale(MeterColourScale); break;
522 case 2: setColourScale(dBSquaredColourScale); break;
523 case 3: setColourScale(dBColourScale); break;
524 case 4: setColourScale(PhaseColourScale); break;
525 }
526 } else if (name == "Frequency Scale") {
527 switch (value) {
528 default:
529 case 0: setFrequencyScale(LinearFrequencyScale); break;
530 case 1: setFrequencyScale(LogFrequencyScale); break;
531 }
532 } else if (name == "Bin Display") {
533 switch (value) {
534 default:
535 case 0: setBinDisplay(AllBins); break;
536 case 1: setBinDisplay(PeakBins); break;
537 case 2: setBinDisplay(PeakFrequencies); break;
538 }
539 } else if (name == "Normalize Columns") {
540 setNormalizeColumns(value ? true : false);
541 } else if (name == "Normalize Visible Area") {
542 setNormalizeVisibleArea(value ? true : false);
543 }
544 }
545
546 void
547 SpectrogramLayer::invalidatePixmapCaches()
548 {
549 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
550 i != m_pixmapCaches.end(); ++i) {
551 i->second.validArea = QRect();
552 }
553 }
554
555 void
556 SpectrogramLayer::invalidatePixmapCaches(size_t startFrame, size_t endFrame)
557 {
558 for (ViewPixmapCache::iterator i = m_pixmapCaches.begin();
559 i != m_pixmapCaches.end(); ++i) {
560
561 //!!! when are views removed from the map? on setLayerDormant?
562 const View *v = i->first;
563
564 if (startFrame < v->getEndFrame() && int(endFrame) >= v->getStartFrame()) {
565 i->second.validArea = QRect();
566 }
567 }
568 }
569
570 void
571 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
572 {
573 std::cerr << "SpectrogramLayer::preferenceChanged(" << name.toStdString() << ")" << std::endl;
574
575 if (name == "Window Type") {
576 setWindowType(Preferences::getInstance()->getWindowType());
577 return;
578 }
579 if (name == "Spectrogram Smoothing") {
580 invalidatePixmapCaches();
581 invalidateMagnitudes();
582 emit layerParametersChanged();
583 }
584 if (name == "Tuning Frequency") {
585 emit layerParametersChanged();
586 }
587 }
588
589 void
590 SpectrogramLayer::setChannel(int ch)
591 {
592 if (m_channel == ch) return;
593
594 invalidatePixmapCaches();
595 m_channel = ch;
596 invalidateFFTModels();
597
598 emit layerParametersChanged();
599 }
600
601 int
602 SpectrogramLayer::getChannel() const
603 {
604 return m_channel;
605 }
606
607 void
608 SpectrogramLayer::setWindowSize(size_t ws)
609 {
610 if (m_windowSize == ws) return;
611
612 invalidatePixmapCaches();
613
614 m_windowSize = ws;
615 m_fftSize = ws * (m_zeroPadLevel + 1);
616
617 invalidateFFTModels();
618
619 emit layerParametersChanged();
620 }
621
622 size_t
623 SpectrogramLayer::getWindowSize() const
624 {
625 return m_windowSize;
626 }
627
628 void
629 SpectrogramLayer::setWindowHopLevel(size_t v)
630 {
631 if (m_windowHopLevel == v) return;
632
633 invalidatePixmapCaches();
634
635 m_windowHopLevel = v;
636
637 invalidateFFTModels();
638
639 emit layerParametersChanged();
640
641 // fillCache();
642 }
643
644 size_t
645 SpectrogramLayer::getWindowHopLevel() const
646 {
647 return m_windowHopLevel;
648 }
649
650 void
651 SpectrogramLayer::setZeroPadLevel(size_t v)
652 {
653 if (m_zeroPadLevel == v) return;
654
655 invalidatePixmapCaches();
656
657 m_zeroPadLevel = v;
658 m_fftSize = m_windowSize * (v + 1);
659
660 invalidateFFTModels();
661
662 emit layerParametersChanged();
663 }
664
665 size_t
666 SpectrogramLayer::getZeroPadLevel() const
667 {
668 return m_zeroPadLevel;
669 }
670
671 void
672 SpectrogramLayer::setWindowType(WindowType w)
673 {
674 if (m_windowType == w) return;
675
676 invalidatePixmapCaches();
677
678 m_windowType = w;
679
680 invalidateFFTModels();
681
682 emit layerParametersChanged();
683 }
684
685 WindowType
686 SpectrogramLayer::getWindowType() const
687 {
688 return m_windowType;
689 }
690
691 void
692 SpectrogramLayer::setGain(float gain)
693 {
694 // std::cerr << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
695 // << m_gain << ")" << std::endl;
696
697 if (m_gain == gain) return;
698
699 invalidatePixmapCaches();
700
701 m_gain = gain;
702
703 emit layerParametersChanged();
704 }
705
706 float
707 SpectrogramLayer::getGain() const
708 {
709 return m_gain;
710 }
711
712 void
713 SpectrogramLayer::setThreshold(float threshold)
714 {
715 if (m_threshold == threshold) return;
716
717 invalidatePixmapCaches();
718
719 m_threshold = threshold;
720
721 emit layerParametersChanged();
722 }
723
724 float
725 SpectrogramLayer::getThreshold() const
726 {
727 return m_threshold;
728 }
729
730 void
731 SpectrogramLayer::setMinFrequency(size_t mf)
732 {
733 if (m_minFrequency == mf) return;
734
735 // std::cerr << "SpectrogramLayer::setMinFrequency: " << mf << std::endl;
736
737 invalidatePixmapCaches();
738 invalidateMagnitudes();
739
740 m_minFrequency = mf;
741
742 emit layerParametersChanged();
743 }
744
745 size_t
746 SpectrogramLayer::getMinFrequency() const
747 {
748 return m_minFrequency;
749 }
750
751 void
752 SpectrogramLayer::setMaxFrequency(size_t mf)
753 {
754 if (m_maxFrequency == mf) return;
755
756 // std::cerr << "SpectrogramLayer::setMaxFrequency: " << mf << std::endl;
757
758 invalidatePixmapCaches();
759 invalidateMagnitudes();
760
761 m_maxFrequency = mf;
762
763 emit layerParametersChanged();
764 }
765
766 size_t
767 SpectrogramLayer::getMaxFrequency() const
768 {
769 return m_maxFrequency;
770 }
771
772 void
773 SpectrogramLayer::setColourRotation(int r)
774 {
775 invalidatePixmapCaches();
776
777 if (r < 0) r = 0;
778 if (r > 256) r = 256;
779 int distance = r - m_colourRotation;
780
781 if (distance != 0) {
782 rotatePalette(-distance);
783 m_colourRotation = r;
784 }
785
786 emit layerParametersChanged();
787 }
788
789 void
790 SpectrogramLayer::setColourScale(ColourScale colourScale)
791 {
792 if (m_colourScale == colourScale) return;
793
794 invalidatePixmapCaches();
795
796 m_colourScale = colourScale;
797
798 emit layerParametersChanged();
799 }
800
801 SpectrogramLayer::ColourScale
802 SpectrogramLayer::getColourScale() const
803 {
804 return m_colourScale;
805 }
806
807 void
808 SpectrogramLayer::setColourMap(int map)
809 {
810 if (m_colourMap == map) return;
811
812 invalidatePixmapCaches();
813
814 m_colourMap = map;
815 initialisePalette();
816
817 emit layerParametersChanged();
818 }
819
820 int
821 SpectrogramLayer::getColourMap() const
822 {
823 return m_colourMap;
824 }
825
826 void
827 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale)
828 {
829 if (m_frequencyScale == frequencyScale) return;
830
831 invalidatePixmapCaches();
832 m_frequencyScale = frequencyScale;
833
834 emit layerParametersChanged();
835 }
836
837 SpectrogramLayer::FrequencyScale
838 SpectrogramLayer::getFrequencyScale() const
839 {
840 return m_frequencyScale;
841 }
842
843 void
844 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
845 {
846 if (m_binDisplay == binDisplay) return;
847
848 invalidatePixmapCaches();
849 m_binDisplay = binDisplay;
850
851 emit layerParametersChanged();
852 }
853
854 SpectrogramLayer::BinDisplay
855 SpectrogramLayer::getBinDisplay() const
856 {
857 return m_binDisplay;
858 }
859
860 void
861 SpectrogramLayer::setNormalizeColumns(bool n)
862 {
863 if (m_normalizeColumns == n) return;
864
865 invalidatePixmapCaches();
866 invalidateMagnitudes();
867 m_normalizeColumns = n;
868
869 emit layerParametersChanged();
870 }
871
872 bool
873 SpectrogramLayer::getNormalizeColumns() const
874 {
875 return m_normalizeColumns;
876 }
877
878 void
879 SpectrogramLayer::setNormalizeVisibleArea(bool n)
880 {
881 if (m_normalizeVisibleArea == n) return;
882
883 invalidatePixmapCaches();
884 invalidateMagnitudes();
885 m_normalizeVisibleArea = n;
886
887 emit layerParametersChanged();
888 }
889
890 bool
891 SpectrogramLayer::getNormalizeVisibleArea() const
892 {
893 return m_normalizeVisibleArea;
894 }
895
896 void
897 SpectrogramLayer::setLayerDormant(const View *v, bool dormant)
898 {
899 if (dormant) {
900
901 if (isLayerDormant(v)) {
902 return;
903 }
904
905 Layer::setLayerDormant(v, true);
906
907 invalidatePixmapCaches();
908 m_pixmapCaches.erase(v);
909
910 if (m_fftModels.find(v) != m_fftModels.end()) {
911
912 if (m_sliceableModel == m_fftModels[v].first) {
913 bool replaced = false;
914 for (ViewFFTMap::iterator i = m_fftModels.begin();
915 i != m_fftModels.end(); ++i) {
916 if (i->second.first != m_sliceableModel) {
917 emit sliceableModelReplaced(m_sliceableModel, i->second.first);
918 replaced = true;
919 break;
920 }
921 }
922 if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
923 }
924
925 delete m_fftModels[v].first;
926 m_fftModels.erase(v);
927 }
928
929 } else {
930
931 Layer::setLayerDormant(v, false);
932 }
933 }
934
935 void
936 SpectrogramLayer::cacheInvalid()
937 {
938 invalidatePixmapCaches();
939 invalidateMagnitudes();
940 }
941
942 void
943 SpectrogramLayer::cacheInvalid(size_t, size_t)
944 {
945 // for now (or forever?)
946 cacheInvalid();
947 }
948
949 void
950 SpectrogramLayer::fillTimerTimedOut()
951 {
952 if (!m_model) return;
953
954 bool allDone = true;
955
956 #ifdef DEBUG_SPECTROGRAM_REPAINT
957 std::cerr << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << std::endl;
958 #endif
959
960 for (ViewFFTMap::iterator i = m_fftModels.begin();
961 i != m_fftModels.end(); ++i) {
962
963 const FFTModel *model = i->second.first;
964 size_t lastFill = i->second.second;
965
966 if (model) {
967
968 size_t fill = model->getFillExtent();
969
970 #ifdef DEBUG_SPECTROGRAM_REPAINT
971 std::cerr << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << std::endl;
972 #endif
973
974 if (fill >= lastFill) {
975 if (fill >= m_model->getEndFrame() && lastFill > 0) {
976 #ifdef DEBUG_SPECTROGRAM_REPAINT
977 std::cerr << "complete!" << std::endl;
978 #endif
979 invalidatePixmapCaches();
980 i->second.second = -1;
981 emit modelChanged();
982
983 } else if (fill > lastFill) {
984 #ifdef DEBUG_SPECTROGRAM_REPAINT
985 std::cerr << "SpectrogramLayer: emitting modelChanged("
986 << lastFill << "," << fill << ")" << std::endl;
987 #endif
988 invalidatePixmapCaches(lastFill, fill);
989 i->second.second = fill;
990 emit modelChanged(lastFill, fill);
991 }
992 } else {
993 #ifdef DEBUG_SPECTROGRAM_REPAINT
994 std::cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
995 << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << std::endl;
996 #endif
997 invalidatePixmapCaches();
998 i->second.second = fill;
999 emit modelChanged(m_model->getStartFrame(), m_model->getEndFrame());
1000 }
1001
1002 if (i->second.second >= 0) {
1003 allDone = false;
1004 }
1005 }
1006 }
1007
1008 if (allDone) {
1009 #ifdef DEBUG_SPECTROGRAM_REPAINT
1010 std::cerr << "SpectrogramLayer: all complete!" << std::endl;
1011 #endif
1012 delete m_updateTimer;
1013 m_updateTimer = 0;
1014 }
1015 }
1016
1017 bool
1018 SpectrogramLayer::hasLightBackground() const
1019 {
1020 return (m_colourMap == (int)ColourMapper::BlackOnWhite);
1021 }
1022
1023 void
1024 SpectrogramLayer::initialisePalette()
1025 {
1026 int formerRotation = m_colourRotation;
1027
1028 if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
1029 m_palette.setColour(NO_VALUE, Qt::white);
1030 } else {
1031 m_palette.setColour(NO_VALUE, Qt::black);
1032 }
1033
1034 ColourMapper mapper(m_colourMap, 1.f, 255.f);
1035
1036 for (int pixel = 1; pixel < 256; ++pixel) {
1037 m_palette.setColour(pixel, mapper.map(pixel));
1038 }
1039
1040 m_crosshairColour = mapper.getContrastingColour();
1041
1042 m_colourRotation = 0;
1043 rotatePalette(m_colourRotation - formerRotation);
1044 m_colourRotation = formerRotation;
1045 }
1046
1047 void
1048 SpectrogramLayer::rotatePalette(int distance)
1049 {
1050 QColor newPixels[256];
1051
1052 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
1053
1054 for (int pixel = 1; pixel < 256; ++pixel) {
1055 int target = pixel + distance;
1056 while (target < 1) target += 255;
1057 while (target > 255) target -= 255;
1058 newPixels[target] = m_palette.getColour(pixel);
1059 }
1060
1061 for (int pixel = 0; pixel < 256; ++pixel) {
1062 m_palette.setColour(pixel, newPixels[pixel]);
1063 }
1064 }
1065
1066 float
1067 SpectrogramLayer::calculateFrequency(size_t bin,
1068 size_t windowSize,
1069 size_t windowIncrement,
1070 size_t sampleRate,
1071 float oldPhase,
1072 float newPhase,
1073 bool &steadyState)
1074 {
1075 // At frequency f, phase shift of 2pi (one cycle) happens in 1/f sec.
1076 // At hopsize h and sample rate sr, one hop happens in h/sr sec.
1077 // At window size w, for bin b, f is b*sr/w.
1078 // thus 2pi phase shift happens in w/(b*sr) sec.
1079 // We need to know what phase shift we expect from h/sr sec.
1080 // -> 2pi * ((h/sr) / (w/(b*sr)))
1081 // = 2pi * ((h * b * sr) / (w * sr))
1082 // = 2pi * (h * b) / w.
1083
1084 float frequency = (float(bin) * sampleRate) / windowSize;
1085
1086 float expectedPhase =
1087 oldPhase + (2.0 * M_PI * bin * windowIncrement) / windowSize;
1088
1089 float phaseError = princargf(newPhase - expectedPhase);
1090
1091 if (fabsf(phaseError) < (1.1f * (windowIncrement * M_PI) / windowSize)) {
1092
1093 // The new frequency estimate based on the phase error
1094 // resulting from assuming the "native" frequency of this bin
1095
1096 float newFrequency =
1097 (sampleRate * (expectedPhase + phaseError - oldPhase)) /
1098 (2 * M_PI * windowIncrement);
1099
1100 steadyState = true;
1101 return newFrequency;
1102 }
1103
1104 steadyState = false;
1105 return frequency;
1106 }
1107
1108 unsigned char
1109 SpectrogramLayer::getDisplayValue(View *v, float input) const
1110 {
1111 int value;
1112
1113 float min = 0.f;
1114 float max = 1.f;
1115
1116 if (m_normalizeVisibleArea) {
1117 min = m_viewMags[v].getMin();
1118 max = m_viewMags[v].getMax();
1119 } else if (!m_normalizeColumns) {
1120 if (m_colourScale == LinearColourScale //||
1121 // m_colourScale == MeterColourScale) {
1122 ) {
1123 max = 0.1f;
1124 }
1125 }
1126
1127 float thresh = -80.f;
1128
1129 if (max == 0.f) max = 1.f;
1130 if (max == min) min = max - 0.0001f;
1131
1132 switch (m_colourScale) {
1133
1134 default:
1135 case LinearColourScale:
1136 value = int(((input - min) / (max - min)) * 255.f) + 1;
1137 break;
1138
1139 case MeterColourScale:
1140 value = AudioLevel::multiplier_to_preview
1141 ((input - min) / (max - min), 254) + 1;
1142 break;
1143
1144 case dBSquaredColourScale:
1145 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1146 if (input > 0.f) {
1147 input = 10.f * log10f(input);
1148 } else {
1149 input = thresh;
1150 }
1151 if (min > 0.f) {
1152 thresh = 10.f * log10f(min * min);
1153 if (thresh < -80.f) thresh = -80.f;
1154 }
1155 input = (input - thresh) / (-thresh);
1156 if (input < 0.f) input = 0.f;
1157 if (input > 1.f) input = 1.f;
1158 value = int(input * 255.f) + 1;
1159 break;
1160
1161 case dBColourScale:
1162 //!!! experiment with normalizing the visible area this way.
1163 //In any case, we need to have some indication of what the dB
1164 //scale is relative to.
1165 input = (input - min) / (max - min);
1166 if (input > 0.f) {
1167 input = 10.f * log10f(input);
1168 } else {
1169 input = thresh;
1170 }
1171 if (min > 0.f) {
1172 thresh = 10.f * log10f(min);
1173 if (thresh < -80.f) thresh = -80.f;
1174 }
1175 input = (input - thresh) / (-thresh);
1176 if (input < 0.f) input = 0.f;
1177 if (input > 1.f) input = 1.f;
1178 value = int(input * 255.f) + 1;
1179 break;
1180
1181 case PhaseColourScale:
1182 value = int((input * 127.0 / M_PI) + 128);
1183 break;
1184 }
1185
1186 if (value > UCHAR_MAX) value = UCHAR_MAX;
1187 if (value < 0) value = 0;
1188 return value;
1189 }
1190
1191 float
1192 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
1193 {
1194 //!!! unused
1195
1196 int value = uc;
1197 float input;
1198
1199 //!!! incorrect for normalizing visible area (and also out of date)
1200
1201 switch (m_colourScale) {
1202
1203 default:
1204 case LinearColourScale:
1205 input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50);
1206 break;
1207
1208 case MeterColourScale:
1209 input = AudioLevel::preview_to_multiplier(value - 1, 255)
1210 / (m_normalizeColumns ? 1.0 : 50.0);
1211 break;
1212
1213 case dBSquaredColourScale:
1214 input = float(value - 1) / 255.0;
1215 input = (input * 80.0) - 80.0;
1216 input = powf(10.0, input) / 20.0;
1217 value = int(input);
1218 break;
1219
1220 case dBColourScale:
1221 input = float(value - 1) / 255.0;
1222 input = (input * 80.0) - 80.0;
1223 input = powf(10.0, input) / 20.0;
1224 value = int(input);
1225 break;
1226
1227 case PhaseColourScale:
1228 input = float(value - 128) * M_PI / 127.0;
1229 break;
1230 }
1231
1232 return input;
1233 }
1234
1235 float
1236 SpectrogramLayer::getEffectiveMinFrequency() const
1237 {
1238 int sr = m_model->getSampleRate();
1239 float minf = float(sr) / m_fftSize;
1240
1241 if (m_minFrequency > 0.0) {
1242 size_t minbin = size_t((double(m_minFrequency) * m_fftSize) / sr + 0.01);
1243 if (minbin < 1) minbin = 1;
1244 minf = minbin * sr / m_fftSize;
1245 }
1246
1247 return minf;
1248 }
1249
1250 float
1251 SpectrogramLayer::getEffectiveMaxFrequency() const
1252 {
1253 int sr = m_model->getSampleRate();
1254 float maxf = float(sr) / 2;
1255
1256 if (m_maxFrequency > 0.0) {
1257 size_t maxbin = size_t((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1258 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1259 maxf = maxbin * sr / m_fftSize;
1260 }
1261
1262 return maxf;
1263 }
1264
1265 bool
1266 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const
1267 {
1268 int h = v->height();
1269 if (y < 0 || y >= h) return false;
1270
1271 int sr = m_model->getSampleRate();
1272 float minf = getEffectiveMinFrequency();
1273 float maxf = getEffectiveMaxFrequency();
1274
1275 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1276
1277 //!!! wrong for smoothing -- wrong fft size for fft model
1278
1279 q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1280 q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1281
1282 // Now map these on to actual bins
1283
1284 int b0 = int((q0 * m_fftSize) / sr);
1285 int b1 = int((q1 * m_fftSize) / sr);
1286
1287 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
1288 q0 = b0;
1289 q1 = b1;
1290
1291 // q0 = (b0 * sr) / m_fftSize;
1292 // q1 = (b1 * sr) / m_fftSize;
1293
1294 return true;
1295 }
1296
1297 bool
1298 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
1299 {
1300 size_t modelStart = m_model->getStartFrame();
1301 size_t modelEnd = m_model->getEndFrame();
1302
1303 // Each pixel column covers an exact range of sample frames:
1304 int f0 = v->getFrameForX(x) - modelStart;
1305 int f1 = v->getFrameForX(x + 1) - modelStart - 1;
1306
1307 if (f1 < int(modelStart) || f0 > int(modelEnd)) {
1308 return false;
1309 }
1310
1311 // And that range may be drawn from a possibly non-integral
1312 // range of spectrogram windows:
1313
1314 size_t windowIncrement = getWindowIncrement();
1315 s0 = float(f0) / windowIncrement;
1316 s1 = float(f1) / windowIncrement;
1317
1318 return true;
1319 }
1320
1321 bool
1322 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
1323 {
1324 float s0 = 0, s1 = 0;
1325 if (!getXBinRange(v, x, s0, s1)) return false;
1326
1327 int s0i = int(s0 + 0.001);
1328 int s1i = int(s1);
1329
1330 int windowIncrement = getWindowIncrement();
1331 int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
1332 int w1 = s1i * windowIncrement + windowIncrement +
1333 (m_windowSize - windowIncrement)/2 - 1;
1334
1335 min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
1336 max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
1337 return true;
1338 }
1339
1340 bool
1341 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
1342 const
1343 {
1344 float q0 = 0, q1 = 0;
1345 if (!getYBinRange(v, y, q0, q1)) return false;
1346
1347 int q0i = int(q0 + 0.001);
1348 int q1i = int(q1);
1349
1350 int sr = m_model->getSampleRate();
1351
1352 for (int q = q0i; q <= q1i; ++q) {
1353 if (q == q0i) freqMin = (sr * q) / m_fftSize;
1354 if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize;
1355 }
1356 return true;
1357 }
1358
1359 bool
1360 SpectrogramLayer::getAdjustedYBinSourceRange(View *v, int x, int y,
1361 float &freqMin, float &freqMax,
1362 float &adjFreqMin, float &adjFreqMax)
1363 const
1364 {
1365 FFTModel *fft = getFFTModel(v);
1366 if (!fft) return false;
1367
1368 float s0 = 0, s1 = 0;
1369 if (!getXBinRange(v, x, s0, s1)) return false;
1370
1371 float q0 = 0, q1 = 0;
1372 if (!getYBinRange(v, y, q0, q1)) return false;
1373
1374 int s0i = int(s0 + 0.001);
1375 int s1i = int(s1);
1376
1377 int q0i = int(q0 + 0.001);
1378 int q1i = int(q1);
1379
1380 int sr = m_model->getSampleRate();
1381
1382 size_t windowSize = m_windowSize;
1383 size_t windowIncrement = getWindowIncrement();
1384
1385 bool haveAdj = false;
1386
1387 bool peaksOnly = (m_binDisplay == PeakBins ||
1388 m_binDisplay == PeakFrequencies);
1389
1390 for (int q = q0i; q <= q1i; ++q) {
1391
1392 for (int s = s0i; s <= s1i; ++s) {
1393
1394 if (!fft->isColumnAvailable(s)) continue;
1395
1396 float binfreq = (sr * q) / m_windowSize;
1397 if (q == q0i) freqMin = binfreq;
1398 if (q == q1i) freqMax = binfreq;
1399
1400 if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1401
1402 if (!fft->isOverThreshold(s, q, m_threshold)) continue;
1403
1404 float freq = binfreq;
1405 bool steady = false;
1406
1407 if (s < int(fft->getWidth()) - 1) {
1408
1409 freq = calculateFrequency(q,
1410 windowSize,
1411 windowIncrement,
1412 sr,
1413 fft->getPhaseAt(s, q),
1414 fft->getPhaseAt(s+1, q),
1415 steady);
1416
1417 if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
1418 if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
1419
1420 haveAdj = true;
1421 }
1422 }
1423 }
1424
1425 if (!haveAdj) {
1426 adjFreqMin = adjFreqMax = 0.0;
1427 }
1428
1429 return haveAdj;
1430 }
1431
1432 bool
1433 SpectrogramLayer::getXYBinSourceRange(View *v, int x, int y,
1434 float &min, float &max,
1435 float &phaseMin, float &phaseMax) const
1436 {
1437 float q0 = 0, q1 = 0;
1438 if (!getYBinRange(v, y, q0, q1)) return false;
1439
1440 float s0 = 0, s1 = 0;
1441 if (!getXBinRange(v, x, s0, s1)) return false;
1442
1443 int q0i = int(q0 + 0.001);
1444 int q1i = int(q1);
1445
1446 int s0i = int(s0 + 0.001);
1447 int s1i = int(s1);
1448
1449 bool rv = false;
1450
1451 size_t zp = getZeroPadLevel(v);
1452 q0i *= zp + 1;
1453 q1i *= zp + 1;
1454
1455 FFTModel *fft = getFFTModel(v);
1456
1457 if (fft) {
1458
1459 int cw = fft->getWidth();
1460 int ch = fft->getHeight();
1461
1462 min = 0.0;
1463 max = 0.0;
1464 phaseMin = 0.0;
1465 phaseMax = 0.0;
1466 bool have = false;
1467
1468 for (int q = q0i; q <= q1i; ++q) {
1469 for (int s = s0i; s <= s1i; ++s) {
1470 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1471
1472 if (!fft->isColumnAvailable(s)) continue;
1473
1474 float value;
1475
1476 value = fft->getPhaseAt(s, q);
1477 if (!have || value < phaseMin) { phaseMin = value; }
1478 if (!have || value > phaseMax) { phaseMax = value; }
1479
1480 value = fft->getMagnitudeAt(s, q);
1481 if (!have || value < min) { min = value; }
1482 if (!have || value > max) { max = value; }
1483
1484 have = true;
1485 }
1486 }
1487 }
1488
1489 if (have) {
1490 rv = true;
1491 }
1492 }
1493
1494 return rv;
1495 }
1496
1497 size_t
1498 SpectrogramLayer::getZeroPadLevel(const View *v) const
1499 {
1500 //!!! tidy all this stuff
1501
1502 if (m_binDisplay != AllBins) return 0;
1503
1504 Preferences::SpectrogramSmoothing smoothing =
1505 Preferences::getInstance()->getSpectrogramSmoothing();
1506
1507 if (smoothing == Preferences::NoSpectrogramSmoothing ||
1508 smoothing == Preferences::SpectrogramInterpolated) return 0;
1509
1510 if (m_frequencyScale == LogFrequencyScale) return 3;
1511
1512 int sr = m_model->getSampleRate();
1513
1514 size_t maxbin = m_fftSize / 2;
1515 if (m_maxFrequency > 0) {
1516 maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1517 if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1518 }
1519
1520 size_t minbin = 1;
1521 if (m_minFrequency > 0) {
1522 minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
1523 if (minbin < 1) minbin = 1;
1524 if (minbin >= maxbin) minbin = maxbin - 1;
1525 }
1526
1527 float perPixel =
1528 float(v->height()) /
1529 float((maxbin - minbin) / (m_zeroPadLevel + 1));
1530
1531 if (perPixel > 2.8) {
1532 return 3; // 4x oversampling
1533 } else if (perPixel > 1.5) {
1534 return 1; // 2x
1535 } else {
1536 return 0; // 1x
1537 }
1538 }
1539
1540 size_t
1541 SpectrogramLayer::getFFTSize(const View *v) const
1542 {
1543 return m_fftSize * (getZeroPadLevel(v) + 1);
1544 }
1545
1546 FFTModel *
1547 SpectrogramLayer::getFFTModel(const View *v) const
1548 {
1549 if (!m_model) return 0;
1550
1551 size_t fftSize = getFFTSize(v);
1552
1553 if (m_fftModels.find(v) != m_fftModels.end()) {
1554 if (m_fftModels[v].first == 0) {
1555 #ifdef DEBUG_SPECTROGRAM_REPAINT
1556 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << std::endl;
1557 #endif
1558 return 0;
1559 }
1560 if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
1561 #ifdef DEBUG_SPECTROGRAM_REPAINT
1562 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << std::endl;
1563 #endif
1564 delete m_fftModels[v].first;
1565 m_fftModels.erase(v);
1566 } else {
1567 #ifdef DEBUG_SPECTROGRAM_REPAINT
1568 std::cerr << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << std::endl;
1569 #endif
1570 return m_fftModels[v].first;
1571 }
1572 }
1573
1574 if (m_fftModels.find(v) == m_fftModels.end()) {
1575
1576 FFTModel *model = new FFTModel(m_model,
1577 m_channel,
1578 m_windowType,
1579 m_windowSize,
1580 getWindowIncrement(),
1581 fftSize,
1582 true,
1583 m_candidateFillStartFrame);
1584
1585 if (!model->isOK()) {
1586 QMessageBox::critical
1587 (0, tr("FFT cache failed"),
1588 tr("Failed to create the FFT model for this spectrogram.\n"
1589 "There may be insufficient memory or disc space to continue."));
1590 delete model;
1591 m_fftModels[v] = FFTFillPair(0, 0);
1592 return 0;
1593 }
1594
1595 if (!m_sliceableModel) {
1596 #ifdef DEBUG_SPECTROGRAM
1597 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << std::endl;
1598 #endif
1599 ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1600 m_sliceableModel = model;
1601 }
1602
1603 m_fftModels[v] = FFTFillPair(model, 0);
1604
1605 model->resume();
1606
1607 delete m_updateTimer;
1608 m_updateTimer = new QTimer((SpectrogramLayer *)this);
1609 connect(m_updateTimer, SIGNAL(timeout()),
1610 this, SLOT(fillTimerTimedOut()));
1611 m_updateTimer->start(200);
1612 }
1613
1614 return m_fftModels[v].first;
1615 }
1616
1617 const Model *
1618 SpectrogramLayer::getSliceableModel() const
1619 {
1620 if (m_sliceableModel) return m_sliceableModel;
1621 if (m_fftModels.empty()) return 0;
1622 m_sliceableModel = m_fftModels.begin()->second.first;
1623 return m_sliceableModel;
1624 }
1625
1626 void
1627 SpectrogramLayer::invalidateFFTModels()
1628 {
1629 for (ViewFFTMap::iterator i = m_fftModels.begin();
1630 i != m_fftModels.end(); ++i) {
1631 delete i->second.first;
1632 }
1633
1634 m_fftModels.clear();
1635
1636 if (m_sliceableModel) {
1637 std::cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << std::endl;
1638 emit sliceableModelReplaced(m_sliceableModel, 0);
1639 m_sliceableModel = 0;
1640 }
1641 }
1642
1643 void
1644 SpectrogramLayer::invalidateMagnitudes()
1645 {
1646 m_viewMags.clear();
1647 for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin();
1648 i != m_columnMags.end(); ++i) {
1649 *i = MagnitudeRange();
1650 }
1651 }
1652
1653 bool
1654 SpectrogramLayer::updateViewMagnitudes(View *v) const
1655 {
1656 MagnitudeRange mag;
1657
1658 int x0 = 0, x1 = v->width();
1659 float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
1660
1661 if (!getXBinRange(v, x0, s00, s01)) {
1662 s00 = s01 = m_model->getStartFrame() / getWindowIncrement();
1663 }
1664
1665 if (!getXBinRange(v, x1, s10, s11)) {
1666 s10 = s11 = m_model->getEndFrame() / getWindowIncrement();
1667 }
1668
1669 int s0 = int(min(s00, s10) + 0.0001);
1670 int s1 = int(max(s01, s11) + 0.0001);
1671
1672 // std::cerr << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << std::endl;
1673
1674 if (int(m_columnMags.size()) <= s1) {
1675 m_columnMags.resize(s1 + 1);
1676 }
1677
1678 for (int s = s0; s <= s1; ++s) {
1679 if (m_columnMags[s].isSet()) {
1680 mag.sample(m_columnMags[s]);
1681 }
1682 }
1683
1684 #ifdef DEBUG_SPECTROGRAM_REPAINT
1685 std::cerr << "SpectrogramLayer::updateViewMagnitudes returning from cols "
1686 << s0 << " -> " << s1 << " inclusive" << std::endl;
1687 #endif
1688
1689 if (!mag.isSet()) return false;
1690 if (mag == m_viewMags[v]) return false;
1691 m_viewMags[v] = mag;
1692 return true;
1693 }
1694
1695 void
1696 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
1697 {
1698 Profiler profiler("SpectrogramLayer::paint", true);
1699 #ifdef DEBUG_SPECTROGRAM_REPAINT
1700 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
1701
1702 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
1703 #endif
1704
1705 long startFrame = v->getStartFrame();
1706 if (startFrame < 0) m_candidateFillStartFrame = 0;
1707 else m_candidateFillStartFrame = startFrame;
1708
1709 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1710 return;
1711 }
1712
1713 if (isLayerDormant(v)) {
1714 std::cerr << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << std::endl;
1715 }
1716
1717 // Need to do this even if !isLayerDormant, as that could mean v
1718 // is not in the dormancy map at all -- we need it to be present
1719 // and accountable for when determining whether we need the cache
1720 // in the cache-fill thread above.
1721 //!!! no longer use cache-fill thread
1722 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1723
1724 size_t fftSize = getFFTSize(v);
1725 FFTModel *fft = getFFTModel(v);
1726 if (!fft) {
1727 std::cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << std::endl;
1728 return;
1729 }
1730
1731 PixmapCache &cache = m_pixmapCaches[v];
1732
1733 #ifdef DEBUG_SPECTROGRAM_REPAINT
1734 std::cerr << "SpectrogramLayer::paint(): pixmap cache valid area " << cache.validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << std::endl;
1735 #endif
1736
1737 #ifdef DEBUG_SPECTROGRAM_REPAINT
1738 bool stillCacheing = (m_updateTimer != 0);
1739 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
1740 #endif
1741
1742 int zoomLevel = v->getZoomLevel();
1743
1744 int x0 = 0;
1745 int x1 = v->width();
1746
1747 bool recreateWholePixmapCache = true;
1748
1749 x0 = rect.left();
1750 x1 = rect.right() + 1;
1751
1752 if (cache.validArea.width() > 0) {
1753
1754 if (int(cache.zoomLevel) == zoomLevel &&
1755 cache.pixmap.width() == v->width() &&
1756 cache.pixmap.height() == v->height()) {
1757
1758 if (v->getXForFrame(cache.startFrame) ==
1759 v->getXForFrame(startFrame) &&
1760 cache.validArea.x() <= x0 &&
1761 cache.validArea.x() + cache.validArea.width() >= x1) {
1762
1763 #ifdef DEBUG_SPECTROGRAM_REPAINT
1764 std::cerr << "SpectrogramLayer: pixmap cache good" << std::endl;
1765 #endif
1766
1767 paint.drawPixmap(rect, cache.pixmap, rect);
1768 illuminateLocalFeatures(v, paint);
1769 return;
1770
1771 } else {
1772
1773 #ifdef DEBUG_SPECTROGRAM_REPAINT
1774 std::cerr << "SpectrogramLayer: pixmap cache partially OK" << std::endl;
1775 #endif
1776
1777 recreateWholePixmapCache = false;
1778
1779 int dx = v->getXForFrame(cache.startFrame) -
1780 v->getXForFrame(startFrame);
1781
1782 #ifdef DEBUG_SPECTROGRAM_REPAINT
1783 std::cerr << "SpectrogramLayer: dx = " << dx << " (pixmap cache " << cache.pixmap.width() << "x" << cache.pixmap.height() << ")" << std::endl;
1784 #endif
1785
1786 if (dx != 0 &&
1787 dx > -cache.pixmap.width() &&
1788 dx < cache.pixmap.width()) {
1789
1790 #if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
1791 // Copying a pixmap to itself doesn't work
1792 // properly on Windows or Mac (it only works when
1793 // moving in one direction).
1794
1795 //!!! Need a utility function for this
1796
1797 static QPixmap *tmpPixmap = 0;
1798 if (!tmpPixmap ||
1799 tmpPixmap->width() != cache.pixmap.width() ||
1800 tmpPixmap->height() != cache.pixmap.height()) {
1801 delete tmpPixmap;
1802 tmpPixmap = new QPixmap(cache.pixmap.width(),
1803 cache.pixmap.height());
1804 }
1805 QPainter cachePainter;
1806 cachePainter.begin(tmpPixmap);
1807 cachePainter.drawPixmap(0, 0, cache.pixmap);
1808 cachePainter.end();
1809 cachePainter.begin(&cache.pixmap);
1810 cachePainter.drawPixmap(dx, 0, *tmpPixmap);
1811 cachePainter.end();
1812 #else
1813 QPainter cachePainter(&cache.pixmap);
1814 cachePainter.drawPixmap(dx, 0, cache.pixmap);
1815 cachePainter.end();
1816 #endif
1817
1818 int px = cache.validArea.x();
1819 int pw = cache.validArea.width();
1820
1821 if (dx < 0) {
1822 x0 = cache.pixmap.width() + dx;
1823 x1 = cache.pixmap.width();
1824 px += dx;
1825 if (px < 0) {
1826 pw += px;
1827 px = 0;
1828 if (pw < 0) pw = 0;
1829 }
1830 } else {
1831 x0 = 0;
1832 x1 = dx;
1833 px += dx;
1834 if (px + pw > cache.pixmap.width()) {
1835 pw = int(cache.pixmap.width()) - px;
1836 if (pw < 0) pw = 0;
1837 }
1838 }
1839
1840 cache.validArea =
1841 QRect(px, cache.validArea.y(),
1842 pw, cache.validArea.height());
1843
1844 paint.drawPixmap(rect & cache.validArea,
1845 cache.pixmap,
1846 rect & cache.validArea);
1847 }
1848 }
1849 } else {
1850 #ifdef DEBUG_SPECTROGRAM_REPAINT
1851 std::cerr << "SpectrogramLayer: pixmap cache useless" << std::endl;
1852 if (int(cache.zoomLevel) != zoomLevel) {
1853 std::cerr << "(cache zoomLevel " << cache.zoomLevel
1854 << " != " << zoomLevel << ")" << std::endl;
1855 }
1856 if (cache.pixmap.width() != v->width()) {
1857 std::cerr << "(cache width " << cache.pixmap.width()
1858 << " != " << v->width();
1859 }
1860 if (cache.pixmap.height() != v->height()) {
1861 std::cerr << "(cache height " << cache.pixmap.height()
1862 << " != " << v->height();
1863 }
1864 #endif
1865 cache.validArea = QRect();
1866 }
1867 }
1868
1869 if (updateViewMagnitudes(v)) {
1870 #ifdef DEBUG_SPECTROGRAM_REPAINT
1871 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1872 #endif
1873 recreateWholePixmapCache = true;
1874 } else {
1875 #ifdef DEBUG_SPECTROGRAM_REPAINT
1876 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1877 #endif
1878 }
1879
1880 if (recreateWholePixmapCache) {
1881 x0 = 0;
1882 x1 = v->width();
1883 }
1884
1885 struct timeval tv;
1886 (void)gettimeofday(&tv, 0);
1887 RealTime mainPaintStart = RealTime::fromTimeval(tv);
1888
1889 int paintBlockWidth = m_lastPaintBlockWidth;
1890
1891 if (paintBlockWidth == 0) {
1892 paintBlockWidth = (300000 / zoomLevel);
1893 } else {
1894 RealTime lastTime = m_lastPaintTime;
1895 while (lastTime > RealTime::fromMilliseconds(200) &&
1896 paintBlockWidth > 50) {
1897 paintBlockWidth /= 2;
1898 lastTime = lastTime / 2;
1899 }
1900 while (lastTime < RealTime::fromMilliseconds(90) &&
1901 paintBlockWidth < 1500) {
1902 paintBlockWidth *= 2;
1903 lastTime = lastTime * 2;
1904 }
1905 }
1906
1907 if (paintBlockWidth < 20) paintBlockWidth = 20;
1908
1909 #ifdef DEBUG_SPECTROGRAM_REPAINT
1910 std::cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << std::endl;
1911 #endif
1912
1913 // We always paint the full height when refreshing the cache.
1914 // Smaller heights can be used when painting direct from cache
1915 // (further up in this function), but we want to ensure the cache
1916 // is coherent without having to worry about vertical matching of
1917 // required and valid areas as well as horizontal.
1918
1919 int h = v->height();
1920
1921 if (cache.validArea.width() > 0) {
1922
1923 int vx0 = 0, vx1 = 0;
1924 vx0 = cache.validArea.x();
1925 vx1 = cache.validArea.x() + cache.validArea.width();
1926
1927 #ifdef DEBUG_SPECTROGRAM_REPAINT
1928 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << std::endl;
1929 #endif
1930 if (x0 < vx0) {
1931 if (x0 + paintBlockWidth < vx0) {
1932 x0 = vx0 - paintBlockWidth;
1933 } else {
1934 x0 = 0;
1935 }
1936 } else if (x0 > vx1) {
1937 x0 = vx1;
1938 }
1939
1940 if (x1 < vx0) {
1941 x1 = vx0;
1942 } else if (x1 > vx1) {
1943 if (vx1 + paintBlockWidth < x1) {
1944 x1 = vx1 + paintBlockWidth;
1945 } else {
1946 x1 = v->width();
1947 }
1948 }
1949
1950 cache.validArea = QRect
1951 (min(vx0, x0), cache.validArea.y(),
1952 max(vx1 - min(vx0, x0),
1953 x1 - min(vx0, x0)),
1954 cache.validArea.height());
1955
1956 } else {
1957 if (x1 > x0 + paintBlockWidth) {
1958 int sfx = x1;
1959 if (startFrame < 0) sfx = v->getXForFrame(0);
1960 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
1961 x0 = sfx;
1962 x1 = x0 + paintBlockWidth;
1963 } else {
1964 int mid = (x1 + x0) / 2;
1965 x0 = mid - paintBlockWidth/2;
1966 x1 = x0 + paintBlockWidth;
1967 }
1968 }
1969 cache.validArea = QRect(x0, 0, x1 - x0, h);
1970 }
1971
1972 int w = x1 - x0;
1973
1974 #ifdef DEBUG_SPECTROGRAM_REPAINT
1975 std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl;
1976 #endif
1977
1978 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
1979 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
1980 }
1981
1982 m_drawBuffer.fill(m_palette.getColour(0).rgb());
1983
1984 int sr = m_model->getSampleRate();
1985
1986 // Set minFreq and maxFreq to the frequency extents of the possibly
1987 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
1988 // to the actual scale frequency extents (presumably not zero padded).
1989
1990 size_t maxbin = fftSize / 2;
1991 if (m_maxFrequency > 0) {
1992 maxbin = int((double(m_maxFrequency) * fftSize) / sr + 0.1);
1993 if (maxbin > fftSize / 2) maxbin = fftSize / 2;
1994 }
1995
1996 size_t minbin = 1;
1997 if (m_minFrequency > 0) {
1998 minbin = int((double(m_minFrequency) * fftSize) / sr + 0.1);
1999 if (minbin < 1) minbin = 1;
2000 if (minbin >= maxbin) minbin = maxbin - 1;
2001 }
2002
2003 float minFreq = (float(minbin) * sr) / fftSize;
2004 float maxFreq = (float(maxbin) * sr) / fftSize;
2005
2006 float displayMinFreq = minFreq;
2007 float displayMaxFreq = maxFreq;
2008
2009 if (fftSize != m_fftSize) {
2010 displayMinFreq = getEffectiveMinFrequency();
2011 displayMaxFreq = getEffectiveMaxFrequency();
2012 }
2013
2014 /*float ymag[h];
2015 float ydiv[h];
2016 float yval[maxbin + 1]; //!!! cache this?*/
2017 float *ymag = (float*) malloc(h*sizeof(float));
2018 float *ydiv = (float*) malloc(h*sizeof(float));
2019 float *yval = (float*) malloc((maxbin + 1)*sizeof(float));
2020
2021 size_t increment = getWindowIncrement();
2022
2023 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2024
2025 for (size_t q = minbin; q <= maxbin; ++q) {
2026 float f0 = (float(q) * sr) / fftSize;
2027 yval[q] = v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2028 logarithmic);
2029 // std::cerr << "min: " << minFreq << ", max: " << maxFreq << ", yval[" << q << "]: " << yval[q] << std::endl;
2030 }
2031
2032 MagnitudeRange overallMag = m_viewMags[v];
2033 bool overallMagChanged = false;
2034
2035 bool fftSuspended = false;
2036
2037 bool interpolate = false;
2038 Preferences::SpectrogramSmoothing smoothing =
2039 Preferences::getInstance()->getSpectrogramSmoothing();
2040 if (smoothing == Preferences::SpectrogramInterpolated ||
2041 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2042 if (m_binDisplay != PeakBins &&
2043 m_binDisplay != PeakFrequencies) {
2044 interpolate = true;
2045 }
2046 }
2047
2048 #ifdef DEBUG_SPECTROGRAM_REPAINT
2049 std::cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << std::endl;
2050 #endif
2051
2052 bool runOutOfData = false;
2053
2054 for (int x = 0; x < w; ++x) {
2055
2056 if (runOutOfData) break;
2057
2058 for (int y = 0; y < h; ++y) {
2059 ymag[y] = 0.f;
2060 ydiv[y] = 0.f;
2061 }
2062
2063 float s0 = 0, s1 = 0;
2064
2065 if (!getXBinRange(v, x0 + x, s0, s1)) {
2066 assert(x <= m_drawBuffer.width());
2067 continue;
2068 }
2069
2070 int s0i = int(s0 + 0.001);
2071 int s1i = int(s1);
2072
2073 if (s1i >= int(fft->getWidth())) {
2074 if (s0i >= int(fft->getWidth())) {
2075 continue;
2076 } else {
2077 s1i = s0i;
2078 }
2079 }
2080
2081 for (int s = s0i; s <= s1i; ++s) {
2082
2083 if (!fft->isColumnAvailable(s)) {
2084 #ifdef DEBUG_SPECTROGRAM_REPAINT
2085 std::cerr << "Met unavailable column at col " << s << std::endl;
2086 #endif
2087 // continue;
2088 runOutOfData = true;
2089 break;
2090 }
2091
2092 if (!fftSuspended) {
2093 fft->suspendWrites();
2094 fftSuspended = true;
2095 }
2096
2097 MagnitudeRange mag;
2098
2099 for (size_t q = minbin; q < maxbin; ++q) {
2100
2101 float y0 = yval[q + 1];
2102 float y1 = yval[q];
2103
2104 if (m_binDisplay == PeakBins ||
2105 m_binDisplay == PeakFrequencies) {
2106 if (!fft->isLocalPeak(s, q)) continue;
2107 }
2108
2109 if (m_threshold != 0.f &&
2110 !fft->isOverThreshold(s, q, m_threshold)) {
2111 continue;
2112 }
2113
2114 float sprop = 1.0;
2115 if (s == s0i) sprop *= (s + 1) - s0;
2116 if (s == s1i) sprop *= s1 - s;
2117
2118 if (m_binDisplay == PeakFrequencies &&
2119 s < int(fft->getWidth()) - 1) {
2120
2121 bool steady = false;
2122 float f = calculateFrequency(q,
2123 m_windowSize,
2124 increment,
2125 sr,
2126 fft->getPhaseAt(s, q),
2127 fft->getPhaseAt(s+1, q),
2128 steady);
2129
2130 y0 = y1 = v->getYForFrequency
2131 (f, displayMinFreq, displayMaxFreq, logarithmic);
2132 }
2133
2134 int y0i = int(y0 + 0.001);
2135 int y1i = int(y1);
2136
2137 float value;
2138
2139 if (m_colourScale == PhaseColourScale) {
2140 value = fft->getPhaseAt(s, q);
2141 } else if (m_normalizeColumns) {
2142 value = fft->getNormalizedMagnitudeAt(s, q);
2143 mag.sample(value);
2144 value *= m_gain;
2145 } else {
2146 value = fft->getMagnitudeAt(s, q);
2147 mag.sample(value);
2148 value *= m_gain;
2149 }
2150
2151 if (interpolate) {
2152
2153 int ypi = y0i;
2154 if (q < maxbin - 1) ypi = int(yval[q + 2]);
2155
2156 for (int y = ypi; y <= y1i; ++y) {
2157
2158 if (y < 0 || y >= h) continue;
2159
2160 float yprop = sprop;
2161 float iprop = yprop;
2162
2163 if (ypi < y0i && y <= y0i) {
2164
2165 float half = float(y0i - ypi) / 2;
2166 float dist = y - (ypi + half);
2167
2168 if (dist >= 0) {
2169 iprop = (iprop * dist) / half;
2170 ymag[y] += iprop * value;
2171 }
2172 } else {
2173 if (y1i > y0i) {
2174
2175 float half = float(y1i - y0i) / 2;
2176 float dist = y - (y0i + half);
2177
2178 if (dist >= 0) {
2179 iprop = (iprop * (half - dist)) / half;
2180 }
2181 }
2182
2183 ymag[y] += iprop * value;
2184 ydiv[y] += yprop;
2185 }
2186 }
2187
2188 } else {
2189
2190 for (int y = y0i; y <= y1i; ++y) {
2191
2192 if (y < 0 || y >= h) continue;
2193
2194 float yprop = sprop;
2195 if (y == y0i) yprop *= (y + 1) - y0;
2196 if (y == y1i) yprop *= y1 - y;
2197
2198 for (int y = y0i; y <= y1i; ++y) {
2199
2200 if (y < 0 || y >= h) continue;
2201
2202 float yprop = sprop;
2203 if (y == y0i) yprop *= (y + 1) - y0;
2204 if (y == y1i) yprop *= y1 - y;
2205 ymag[y] += yprop * value;
2206 ydiv[y] += yprop;
2207 }
2208 }
2209 }
2210 }
2211
2212 if (mag.isSet()) {
2213
2214 if (s >= int(m_columnMags.size())) {
2215 std::cerr << "INTERNAL ERROR: " << s << " >= "
2216 << m_columnMags.size() << " at SpectrogramLayer.cpp:2087" << std::endl;
2217 }
2218
2219 m_columnMags[s].sample(mag);
2220
2221 if (overallMag.sample(mag)) {
2222 //!!! scaling would change here
2223 overallMagChanged = true;
2224 #ifdef DEBUG_SPECTROGRAM_REPAINT
2225 std::cerr << "Overall mag changed (again?) at column " << s << ", to [" << overallMag.getMin() << "->" << overallMag.getMax() << "]" << std::endl;
2226 #endif
2227 }
2228 }
2229 }
2230
2231 for (int y = 0; y < h; ++y) {
2232
2233 if (ydiv[y] > 0.0) {
2234
2235 unsigned char pixel = 0;
2236
2237 float avg = ymag[y] / ydiv[y];
2238 pixel = getDisplayValue(v, avg);
2239
2240 assert(x <= m_drawBuffer.width());
2241 QColor c = m_palette.getColour(pixel);
2242 m_drawBuffer.setPixel(x, y,
2243 qRgb(c.red(), c.green(), c.blue()));
2244 }
2245 }
2246 }
2247
2248 if (overallMagChanged) {
2249 m_viewMags[v] = overallMag;
2250 #ifdef DEBUG_SPECTROGRAM_REPAINT
2251 std::cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << std::endl;
2252 #endif
2253 } else {
2254 #ifdef DEBUG_SPECTROGRAM_REPAINT
2255 std::cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
2256 #endif
2257 }
2258
2259 Profiler profiler2("SpectrogramLayer::paint: draw image", true);
2260
2261 #ifdef DEBUG_SPECTROGRAM_REPAINT
2262 std::cerr << "Painting " << w << "x" << rect.height()
2263 << " from draw buffer at " << 0 << "," << rect.y()
2264 << " to window at " << x0 << "," << rect.y() << std::endl;
2265 #endif
2266
2267 paint.drawImage(x0, rect.y(), m_drawBuffer, 0, rect.y(), w, rect.height());
2268
2269 if (recreateWholePixmapCache) {
2270 cache.pixmap = QPixmap(v->width(), h);
2271 }
2272
2273 #ifdef DEBUG_SPECTROGRAM_REPAINT
2274 std::cerr << "Painting " << w << "x" << h
2275 << " from draw buffer at " << 0 << "," << 0
2276 << " to cache at " << x0 << "," << 0 << std::endl;
2277 #endif
2278
2279 QPainter cachePainter(&cache.pixmap);
2280 cachePainter.drawImage(x0, 0, m_drawBuffer, 0, 0, w, h);
2281 cachePainter.end();
2282
2283 if (!m_normalizeVisibleArea || !overallMagChanged) {
2284
2285 cache.startFrame = startFrame;
2286 cache.zoomLevel = zoomLevel;
2287
2288 if (cache.validArea.x() > 0) {
2289 #ifdef DEBUG_SPECTROGRAM_REPAINT
2290 std::cerr << "SpectrogramLayer::paint() updating left (0, "
2291 << cache.validArea.x() << ")" << std::endl;
2292 #endif
2293 v->update(0, 0, cache.validArea.x(), h);
2294 }
2295
2296 if (cache.validArea.x() + cache.validArea.width() <
2297 cache.pixmap.width()) {
2298 #ifdef DEBUG_SPECTROGRAM_REPAINT
2299 std::cerr << "SpectrogramLayer::paint() updating right ("
2300 << cache.validArea.x() + cache.validArea.width()
2301 << ", "
2302 << cache.pixmap.width() - (cache.validArea.x() +
2303 cache.validArea.width())
2304 << ")" << std::endl;
2305 #endif
2306 v->update(cache.validArea.x() + cache.validArea.width(),
2307 0,
2308 cache.pixmap.width() - (cache.validArea.x() +
2309 cache.validArea.width()),
2310 h);
2311 }
2312 } else {
2313 // overallMagChanged
2314 cache.validArea = QRect();
2315 v->update();
2316 }
2317
2318 illuminateLocalFeatures(v, paint);
2319
2320 #ifdef DEBUG_SPECTROGRAM_REPAINT
2321 std::cerr << "SpectrogramLayer::paint() returning" << std::endl;
2322 #endif
2323
2324 m_lastPaintBlockWidth = paintBlockWidth;
2325 (void)gettimeofday(&tv, 0);
2326 m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
2327
2328 if (fftSuspended) fft->resume();
2329 }
2330
2331 void
2332 SpectrogramLayer::illuminateLocalFeatures(View *v, QPainter &paint) const
2333 {
2334 QPoint localPos;
2335 if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
2336 return;
2337 }
2338
2339 // std::cerr << "SpectrogramLayer: illuminateLocalFeatures("
2340 // << localPos.x() << "," << localPos.y() << ")" << std::endl;
2341
2342 float s0, s1;
2343 float f0, f1;
2344
2345 if (getXBinRange(v, localPos.x(), s0, s1) &&
2346 getYBinSourceRange(v, localPos.y(), f0, f1)) {
2347
2348 int s0i = int(s0 + 0.001);
2349 int s1i = int(s1);
2350
2351 int x0 = v->getXForFrame(s0i * getWindowIncrement());
2352 int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
2353
2354 int y1 = int(getYForFrequency(v, f1));
2355 int y0 = int(getYForFrequency(v, f0));
2356
2357 // std::cerr << "SpectrogramLayer: illuminate "
2358 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl;
2359
2360 paint.setPen(Qt::white);
2361
2362 //!!! should we be using paintCrosshairs for this?
2363
2364 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
2365 }
2366 }
2367
2368 float
2369 SpectrogramLayer::getYForFrequency(View *v, float frequency) const
2370 {
2371 return v->getYForFrequency(frequency,
2372 getEffectiveMinFrequency(),
2373 getEffectiveMaxFrequency(),
2374 m_frequencyScale == LogFrequencyScale);
2375 }
2376
2377 float
2378 SpectrogramLayer::getFrequencyForY(View *v, int y) const
2379 {
2380 return v->getFrequencyForY(y,
2381 getEffectiveMinFrequency(),
2382 getEffectiveMaxFrequency(),
2383 m_frequencyScale == LogFrequencyScale);
2384 }
2385
2386 int
2387 SpectrogramLayer::getCompletion(View *v) const
2388 {
2389 if (m_updateTimer == 0) return 100;
2390 if (m_fftModels.find(v) == m_fftModels.end()) return 100;
2391
2392 size_t completion = m_fftModels[v].first->getCompletion();
2393 #ifdef DEBUG_SPECTROGRAM_REPAINT
2394 std::cerr << "SpectrogramLayer::getCompletion: completion = " << completion << std::endl;
2395 #endif
2396 return completion;
2397 }
2398
2399 bool
2400 SpectrogramLayer::getValueExtents(float &min, float &max,
2401 bool &logarithmic, QString &unit) const
2402 {
2403 if (!m_model) return false;
2404
2405 int sr = m_model->getSampleRate();
2406 min = float(sr) / m_fftSize;
2407 max = float(sr) / 2;
2408
2409 logarithmic = (m_frequencyScale == LogFrequencyScale);
2410 unit = "Hz";
2411 return true;
2412 }
2413
2414 bool
2415 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
2416 {
2417 min = getEffectiveMinFrequency();
2418 max = getEffectiveMaxFrequency();
2419 // std::cerr << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << std::endl;
2420 return true;
2421 }
2422
2423 bool
2424 SpectrogramLayer::setDisplayExtents(float min, float max)
2425 {
2426 if (!m_model) return false;
2427
2428 std::cerr << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << std::endl;
2429
2430 if (min < 0) min = 0;
2431 if (max > m_model->getSampleRate()/2) max = m_model->getSampleRate()/2;
2432
2433 size_t minf = lrintf(min);
2434 size_t maxf = lrintf(max);
2435
2436 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
2437
2438 invalidatePixmapCaches();
2439 invalidateMagnitudes();
2440
2441 m_minFrequency = minf;
2442 m_maxFrequency = maxf;
2443
2444 emit layerParametersChanged();
2445
2446 int vs = getCurrentVerticalZoomStep();
2447 if (vs != m_lastEmittedZoomStep) {
2448 emit verticalZoomChanged();
2449 m_lastEmittedZoomStep = vs;
2450 }
2451
2452 return true;
2453 }
2454
2455 bool
2456 SpectrogramLayer::snapToFeatureFrame(View *, int &frame,
2457 size_t &resolution,
2458 SnapType snap) const
2459 {
2460 resolution = getWindowIncrement();
2461 int left = (frame / resolution) * resolution;
2462 int right = left + resolution;
2463
2464 switch (snap) {
2465 case SnapLeft: frame = left; break;
2466 case SnapRight: frame = right; break;
2467 case SnapNearest:
2468 case SnapNeighbouring:
2469 if (frame - left > right - frame) frame = right;
2470 else frame = left;
2471 break;
2472 }
2473
2474 return true;
2475 }
2476
2477 bool
2478 SpectrogramLayer::getCrosshairExtents(View *v, QPainter &,
2479 QPoint cursorPos,
2480 std::vector<QRect> &extents) const
2481 {
2482 QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
2483 extents.push_back(vertical);
2484
2485 QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
2486 extents.push_back(horizontal);
2487
2488 return true;
2489 }
2490
2491 void
2492 SpectrogramLayer::paintCrosshairs(View *v, QPainter &paint,
2493 QPoint cursorPos) const
2494 {
2495 paint.save();
2496 paint.setPen(m_crosshairColour);
2497
2498 paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
2499 paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
2500
2501 float fundamental = getFrequencyForY(v, cursorPos.y());
2502
2503 int harmonic = 2;
2504
2505 while (harmonic < 100) {
2506
2507 float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
2508 if (hy < 0 || hy > v->height()) break;
2509
2510 int len = 7;
2511
2512 if (harmonic % 2 == 0) {
2513 if (harmonic % 4 == 0) {
2514 len = 12;
2515 } else {
2516 len = 10;
2517 }
2518 }
2519
2520 paint.drawLine(cursorPos.x() - len,
2521 int(hy),
2522 cursorPos.x(),
2523 int(hy));
2524
2525 ++harmonic;
2526 }
2527
2528 paint.restore();
2529 }
2530
2531 QString
2532 SpectrogramLayer::getFeatureDescription(View *v, QPoint &pos) const
2533 {
2534 int x = pos.x();
2535 int y = pos.y();
2536
2537 if (!m_model || !m_model->isOK()) return "";
2538
2539 float magMin = 0, magMax = 0;
2540 float phaseMin = 0, phaseMax = 0;
2541 float freqMin = 0, freqMax = 0;
2542 float adjFreqMin = 0, adjFreqMax = 0;
2543 QString pitchMin, pitchMax;
2544 RealTime rtMin, rtMax;
2545
2546 bool haveValues = false;
2547
2548 if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
2549 return "";
2550 }
2551 if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
2552 haveValues = true;
2553 }
2554
2555 QString adjFreqText = "", adjPitchText = "";
2556
2557 if (m_binDisplay == PeakFrequencies) {
2558
2559 if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
2560 adjFreqMin, adjFreqMax)) {
2561 return "";
2562 }
2563
2564 if (adjFreqMin != adjFreqMax) {
2565 adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
2566 .arg(adjFreqMin).arg(adjFreqMax);
2567 } else {
2568 adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
2569 .arg(adjFreqMin);
2570 }
2571
2572 QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
2573 QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
2574
2575 if (pmin != pmax) {
2576 adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
2577 } else {
2578 adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
2579 }
2580
2581 } else {
2582
2583 if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
2584 }
2585
2586 QString text;
2587
2588 if (rtMin != rtMax) {
2589 text += tr("Time:\t%1 - %2\n")
2590 .arg(rtMin.toText(true).c_str())
2591 .arg(rtMax.toText(true).c_str());
2592 } else {
2593 text += tr("Time:\t%1\n")
2594 .arg(rtMin.toText(true).c_str());
2595 }
2596
2597 if (freqMin != freqMax) {
2598 text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
2599 .arg(adjFreqText)
2600 .arg(freqMin)
2601 .arg(freqMax)
2602 .arg(adjPitchText)
2603 .arg(Pitch::getPitchLabelForFrequency(freqMin))
2604 .arg(Pitch::getPitchLabelForFrequency(freqMax));
2605 } else {
2606 text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
2607 .arg(adjFreqText)
2608 .arg(freqMin)
2609 .arg(adjPitchText)
2610 .arg(Pitch::getPitchLabelForFrequency(freqMin));
2611 }
2612
2613 if (haveValues) {
2614 float dbMin = AudioLevel::multiplier_to_dB(magMin);
2615 float dbMax = AudioLevel::multiplier_to_dB(magMax);
2616 QString dbMinString;
2617 QString dbMaxString;
2618 if (dbMin == AudioLevel::DB_FLOOR) {
2619 dbMinString = tr("-Inf");
2620 } else {
2621 dbMinString = QString("%1").arg(lrintf(dbMin));
2622 }
2623 if (dbMax == AudioLevel::DB_FLOOR) {
2624 dbMaxString = tr("-Inf");
2625 } else {
2626 dbMaxString = QString("%1").arg(lrintf(dbMax));
2627 }
2628 if (lrintf(dbMin) != lrintf(dbMax)) {
2629 text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
2630 } else {
2631 text += tr("dB:\t%1").arg(dbMinString);
2632 }
2633 if (phaseMin != phaseMax) {
2634 text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
2635 } else {
2636 text += tr("\nPhase:\t%1").arg(phaseMin);
2637 }
2638 }
2639
2640 return text;
2641 }
2642
2643 int
2644 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
2645 {
2646 int cw;
2647
2648 cw = paint.fontMetrics().width("-80dB");
2649
2650 return cw;
2651 }
2652
2653 int
2654 SpectrogramLayer::getVerticalScaleWidth(View *, QPainter &paint) const
2655 {
2656 if (!m_model || !m_model->isOK()) return 0;
2657
2658 int cw = getColourScaleWidth(paint);
2659
2660 int tw = paint.fontMetrics().width(QString("%1")
2661 .arg(m_maxFrequency > 0 ?
2662 m_maxFrequency - 1 :
2663 m_model->getSampleRate() / 2));
2664
2665 int fw = paint.fontMetrics().width(tr("43Hz"));
2666 if (tw < fw) tw = fw;
2667
2668 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
2669
2670 return cw + tickw + tw + 13;
2671 }
2672
2673 void
2674 SpectrogramLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
2675 {
2676 if (!m_model || !m_model->isOK()) {
2677 return;
2678 }
2679
2680 Profiler profiler("SpectrogramLayer::paintVerticalScale", true);
2681
2682 //!!! cache this?
2683
2684 int h = rect.height(), w = rect.width();
2685
2686 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
2687 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
2688
2689 size_t bins = m_fftSize / 2;
2690 int sr = m_model->getSampleRate();
2691
2692 if (m_maxFrequency > 0) {
2693 bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
2694 if (bins > m_fftSize / 2) bins = m_fftSize / 2;
2695 }
2696
2697 int cw = getColourScaleWidth(paint);
2698 int cbw = paint.fontMetrics().width("dB");
2699
2700 int py = -1;
2701 int textHeight = paint.fontMetrics().height();
2702 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2703
2704 if (h > textHeight * 3 + 10) {
2705
2706 int topLines = 2;
2707 if (m_colourScale == PhaseColourScale) topLines = 1;
2708
2709 int ch = h - textHeight * (topLines + 1) - 8;
2710 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
2711 paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
2712
2713 QString top, bottom;
2714 float min = m_viewMags[v].getMin();
2715 float max = m_viewMags[v].getMax();
2716
2717 float dBmin = AudioLevel::multiplier_to_dB(min);
2718 float dBmax = AudioLevel::multiplier_to_dB(max);
2719
2720 if (dBmax < -60.f) dBmax = -60.f;
2721 else top = QString("%1").arg(lrintf(dBmax));
2722
2723 if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
2724 bottom = QString("%1").arg(lrintf(dBmin));
2725
2726 //!!! & phase etc
2727
2728 if (m_colourScale != PhaseColourScale) {
2729 paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
2730 2 + textHeight + toff, "dBFS");
2731 }
2732
2733 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
2734 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
2735 2 + textHeight * topLines + toff + textHeight/2, top);
2736
2737 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
2738 h + toff - 3 - textHeight/2, bottom);
2739
2740 paint.save();
2741 paint.setBrush(Qt::NoBrush);
2742
2743 int lasty = 0;
2744 int lastdb = 0;
2745
2746 for (int i = 0; i < ch; ++i) {
2747
2748 float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
2749 int idb = int(dBval);
2750
2751 float value = AudioLevel::dB_to_multiplier(dBval);
2752 int colour = getDisplayValue(v, value * m_gain);
2753
2754 paint.setPen(m_palette.getColour(colour));
2755
2756 int y = textHeight * topLines + 4 + ch - i;
2757
2758 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
2759
2760 if (i == 0) {
2761 lasty = y;
2762 lastdb = idb;
2763 } else if (i < ch - paint.fontMetrics().ascent() &&
2764 idb != lastdb &&
2765 ((abs(y - lasty) > textHeight &&
2766 idb % 10 == 0) ||
2767 (abs(y - lasty) > paint.fontMetrics().ascent() &&
2768 idb % 5 == 0))) {
2769 paint.setPen(Qt::black);
2770 QString text = QString("%1").arg(idb);
2771 paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
2772 y + toff + textHeight/2, text);
2773 paint.setPen(Qt::white);
2774 paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
2775 lasty = y;
2776 lastdb = idb;
2777 }
2778 }
2779 paint.restore();
2780 }
2781
2782 paint.drawLine(cw + 7, 0, cw + 7, h);
2783
2784 int bin = -1;
2785
2786 for (int y = 0; y < v->height(); ++y) {
2787
2788 float q0, q1;
2789 if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
2790
2791 int vy;
2792
2793 if (int(q0) > bin) {
2794 vy = y;
2795 bin = int(q0);
2796 } else {
2797 continue;
2798 }
2799
2800 int freq = (sr * bin) / m_fftSize;
2801
2802 if (py >= 0 && (vy - py) < textHeight - 1) {
2803 if (m_frequencyScale == LinearFrequencyScale) {
2804 paint.drawLine(w - tickw, h - vy, w, h - vy);
2805 }
2806 continue;
2807 }
2808
2809 QString text = QString("%1").arg(freq);
2810 if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
2811 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
2812
2813 if (h - vy - textHeight >= -2) {
2814 int tx = w - 3 - paint.fontMetrics().width(text) - max(tickw, pkw);
2815 paint.drawText(tx, h - vy + toff, text);
2816 }
2817
2818 py = vy;
2819 }
2820
2821 if (m_frequencyScale == LogFrequencyScale) {
2822
2823 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
2824
2825 float minf = getEffectiveMinFrequency();
2826 float maxf = getEffectiveMaxFrequency();
2827
2828 int py = h, ppy = h;
2829 paint.setBrush(paint.pen().color());
2830
2831 for (int i = 0; i < 128; ++i) {
2832
2833 float f = Pitch::getFrequencyForPitch(i);
2834 int y = lrintf(v->getYForFrequency(f, minf, maxf, true));
2835
2836 if (y < -2) break;
2837 if (y > h + 2) {
2838 continue;
2839 }
2840
2841 int n = (i % 12);
2842
2843 if (n == 1) {
2844 // C# -- fill the C from here
2845 if (ppy - y > 2) {
2846 paint.fillRect(w - pkw,
2847 // y - (py - y) / 2 - (py - y) / 4,
2848 y,
2849 pkw,
2850 (py + ppy) / 2 - y,
2851 // py - y + 1,
2852 Qt::gray);
2853 }
2854 }
2855
2856 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
2857 // black notes
2858 paint.drawLine(w - pkw, y, w, y);
2859 int rh = ((py - y) / 4) * 2;
2860 if (rh < 2) rh = 2;
2861 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, rh);
2862 } else if (n == 0 || n == 5) {
2863 // C, F
2864 if (py < h) {
2865 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
2866 }
2867 }
2868
2869 ppy = py;
2870 py = y;
2871 }
2872 }
2873 }
2874
2875 class SpectrogramRangeMapper : public RangeMapper
2876 {
2877 public:
2878 SpectrogramRangeMapper(int sr, int /* fftsize */) :
2879 m_dist(float(sr) / 2),
2880 m_s2(sqrtf(sqrtf(2))) { }
2881 ~SpectrogramRangeMapper() { }
2882
2883 virtual int getPositionForValue(float value) const {
2884
2885 float dist = m_dist;
2886
2887 int n = 0;
2888
2889 while (dist > (value + 0.00001) && dist > 0.1f) {
2890 dist /= m_s2;
2891 ++n;
2892 }
2893
2894 return n;
2895 }
2896
2897 virtual float getValueForPosition(int position) const {
2898
2899 // Vertical zoom step 0 shows the entire range from DC ->
2900 // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
2901 // step 0, and so on until the visible range is smaller than
2902 // the frequency step between bins at the current fft size.
2903
2904 float dist = m_dist;
2905
2906 int n = 0;
2907 while (n < position) {
2908 dist /= m_s2;
2909 ++n;
2910 }
2911
2912 return dist;
2913 }
2914
2915 virtual QString getUnit() const { return "Hz"; }
2916
2917 protected:
2918 float m_dist;
2919 float m_s2;
2920 };
2921
2922 int
2923 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
2924 {
2925 if (!m_model) return 0;
2926
2927 int sr = m_model->getSampleRate();
2928
2929 SpectrogramRangeMapper mapper(sr, m_fftSize);
2930
2931 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
2932 int maxStep = mapper.getPositionForValue(0);
2933 int minStep = mapper.getPositionForValue(float(sr) / 2);
2934
2935 defaultStep = mapper.getPositionForValue(m_initialMaxFrequency) - minStep;
2936
2937 // std::cerr << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << m_initialMaxFrequency << ")" << std::endl;
2938
2939 return maxStep - minStep;
2940 }
2941
2942 int
2943 SpectrogramLayer::getCurrentVerticalZoomStep() const
2944 {
2945 if (!m_model) return 0;
2946
2947 float dmin, dmax;
2948 getDisplayExtents(dmin, dmax);
2949
2950 SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
2951 int n = mapper.getPositionForValue(dmax - dmin);
2952 // std::cerr << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << std::endl;
2953 return n;
2954 }
2955
2956 void
2957 SpectrogramLayer::setVerticalZoomStep(int step)
2958 {
2959 //!!! does not do the right thing for log scale
2960
2961 if (!m_model) return;
2962
2963 float dmin, dmax;
2964 getDisplayExtents(dmin, dmax);
2965
2966 int sr = m_model->getSampleRate();
2967 SpectrogramRangeMapper mapper(sr, m_fftSize);
2968 float ddist = mapper.getValueForPosition(step);
2969
2970 float dmid = (dmax + dmin) / 2;
2971 float newmin = dmid - ddist / 2;
2972 float newmax = dmid + ddist / 2;
2973
2974 float mmin, mmax;
2975 mmin = 0;
2976 mmax = float(sr) / 2;
2977
2978 if (newmin < mmin) {
2979 newmax += (mmin - newmin);
2980 newmin = mmin;
2981 }
2982 if (newmax > mmax) {
2983 newmax = mmax;
2984 }
2985
2986 // std::cerr << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << ddist << ")" << std::endl;
2987
2988 setMinFrequency(int(newmin));
2989 setMaxFrequency(int(newmax));
2990 }
2991
2992 RangeMapper *
2993 SpectrogramLayer::getNewVerticalZoomRangeMapper() const
2994 {
2995 if (!m_model) return 0;
2996 return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
2997 }
2998
2999 QString
3000 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
3001 {
3002 QString s;
3003
3004 s += QString("channel=\"%1\" "
3005 "windowSize=\"%2\" "
3006 "windowHopLevel=\"%3\" "
3007 "gain=\"%4\" "
3008 "threshold=\"%5\" ")
3009 .arg(m_channel)
3010 .arg(m_windowSize)
3011 .arg(m_windowHopLevel)
3012 .arg(m_gain)
3013 .arg(m_threshold);
3014
3015 s += QString("minFrequency=\"%1\" "
3016 "maxFrequency=\"%2\" "
3017 "colourScale=\"%3\" "
3018 "colourScheme=\"%4\" "
3019 "colourRotation=\"%5\" "
3020 "frequencyScale=\"%6\" "
3021 "binDisplay=\"%7\" "
3022 "normalizeColumns=\"%8\" "
3023 "normalizeVisibleArea=\"%9\"")
3024 .arg(m_minFrequency)
3025 .arg(m_maxFrequency)
3026 .arg(m_colourScale)
3027 .arg(m_colourMap)
3028 .arg(m_colourRotation)
3029 .arg(m_frequencyScale)
3030 .arg(m_binDisplay)
3031 .arg(m_normalizeColumns ? "true" : "false")
3032 .arg(m_normalizeVisibleArea ? "true" : "false");
3033
3034 return Layer::toXmlString(indent, extraAttributes + " " + s);
3035 }
3036
3037 void
3038 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
3039 {
3040 bool ok = false;
3041
3042 int channel = attributes.value("channel").toInt(&ok);
3043 if (ok) setChannel(channel);
3044
3045 size_t windowSize = attributes.value("windowSize").toUInt(&ok);
3046 if (ok) setWindowSize(windowSize);
3047
3048 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
3049 if (ok) setWindowHopLevel(windowHopLevel);
3050 else {
3051 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
3052 // a percentage value
3053 if (ok) {
3054 if (windowOverlap == 0) setWindowHopLevel(0);
3055 else if (windowOverlap == 25) setWindowHopLevel(1);
3056 else if (windowOverlap == 50) setWindowHopLevel(2);
3057 else if (windowOverlap == 75) setWindowHopLevel(3);
3058 else if (windowOverlap == 90) setWindowHopLevel(4);
3059 }
3060 }
3061
3062 float gain = attributes.value("gain").toFloat(&ok);
3063 if (ok) setGain(gain);
3064
3065 float threshold = attributes.value("threshold").toFloat(&ok);
3066 if (ok) setThreshold(threshold);
3067
3068 size_t minFrequency = attributes.value("minFrequency").toUInt(&ok);
3069 if (ok) {
3070 std::cerr << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << std::endl;
3071 setMinFrequency(minFrequency);
3072 }
3073
3074 size_t maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
3075 if (ok) {
3076 std::cerr << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << std::endl;
3077 setMaxFrequency(maxFrequency);
3078 }
3079
3080 ColourScale colourScale = (ColourScale)
3081 attributes.value("colourScale").toInt(&ok);
3082 if (ok) setColourScale(colourScale);
3083
3084 int colourMap = attributes.value("colourScheme").toInt(&ok);
3085 if (ok) setColourMap(colourMap);
3086
3087 int colourRotation = attributes.value("colourRotation").toInt(&ok);
3088 if (ok) setColourRotation(colourRotation);
3089
3090 FrequencyScale frequencyScale = (FrequencyScale)
3091 attributes.value("frequencyScale").toInt(&ok);
3092 if (ok) setFrequencyScale(frequencyScale);
3093
3094 BinDisplay binDisplay = (BinDisplay)
3095 attributes.value("binDisplay").toInt(&ok);
3096 if (ok) setBinDisplay(binDisplay);
3097
3098 bool normalizeColumns =
3099 (attributes.value("normalizeColumns").trimmed() == "true");
3100 setNormalizeColumns(normalizeColumns);
3101
3102 bool normalizeVisibleArea =
3103 (attributes.value("normalizeVisibleArea").trimmed() == "true");
3104 setNormalizeVisibleArea(normalizeVisibleArea);
3105 }
3106