svgui  1.9
SpectrogramLayer.cpp
Go to the documentation of this file.
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-2009 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 "base/LogRange.h"
26 #include "widgets/CommandHistory.h"
27 #include "ColourMapper.h"
28 #include "ImageRegionFinder.h"
29 #include "data/model/Dense3DModelPeakCache.h"
30 #include "PianoScale.h"
31 
32 #include <QPainter>
33 #include <QImage>
34 #include <QPixmap>
35 #include <QRect>
36 #include <QTimer>
37 #include <QApplication>
38 #include <QMessageBox>
39 #include <QMouseEvent>
40 #include <QTextStream>
41 
42 #include <iostream>
43 
44 
45 
46 #include <cassert>
47 #include <cmath>
48 
49 #ifndef __GNUC__
50 #include <alloca.h>
51 #endif
52 
53 //#define DEBUG_SPECTROGRAM_REPAINT 1
54 
56  m_model(0),
57  m_channel(0),
58  m_windowSize(1024),
59  m_windowType(HanningWindow),
60  m_windowHopLevel(2),
61  m_zeroPadLevel(0),
62  m_fftSize(1024),
63  m_gain(1.0),
64  m_initialGain(1.0),
65  m_threshold(0.0),
66  m_initialThreshold(0.0),
67  m_colourRotation(0),
68  m_initialRotation(0),
69  m_minFrequency(10),
70  m_maxFrequency(8000),
71  m_initialMaxFrequency(8000),
72  m_colourScale(dBColourScale),
73  m_colourMap(0),
74  m_frequencyScale(LinearFrequencyScale),
75  m_binDisplay(AllBins),
76  m_normalizeColumns(false),
77  m_normalizeVisibleArea(false),
78  m_normalizeHybrid(false),
79  m_lastEmittedZoomStep(-1),
80  m_synchronous(false),
81  m_haveDetailedScale(false),
82  m_lastPaintBlockWidth(0),
83  m_updateTimer(0),
84  m_candidateFillStartFrame(0),
85  m_exiting(false),
86  m_sliceableModel(0)
87 {
88  if (config == FullRangeDb) {
90  setMaxFrequency(0);
91  } else if (config == MelodicRange) {
92  setWindowSize(8192);
94  m_initialMaxFrequency = 1500;
95  setMaxFrequency(1500);
96  setMinFrequency(40);
100 // setGain(20);
101  } else if (config == MelodicPeaks) {
102  setWindowSize(4096);
104  m_initialMaxFrequency = 2000;
105  setMaxFrequency(2000);
106  setMinFrequency(40);
110  setNormalizeColumns(true);
111  }
112 
113  Preferences *prefs = Preferences::getInstance();
114  connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
115  this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
116  setWindowType(prefs->getWindowType());
117 
119 }
120 
122 {
123  delete m_updateTimer;
124  m_updateTimer = 0;
125 
127 }
128 
129 void
130 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
131 {
132 // cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << endl;
133 
134  if (model == m_model) return;
135 
136  m_model = model;
138 
139  if (!m_model || !m_model->isOK()) return;
140 
142 
143  connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
144  connect(m_model, SIGNAL(modelChangedWithin(int, int)),
145  this, SLOT(cacheInvalid(int, int)));
146 
147  emit modelReplaced();
148 }
149 
150 Layer::PropertyList
152 {
153  PropertyList list;
154  list.push_back("Colour");
155  list.push_back("Colour Scale");
156  list.push_back("Window Size");
157  list.push_back("Window Increment");
158  list.push_back("Normalize Columns");
159  list.push_back("Normalize Visible Area");
160  list.push_back("Bin Display");
161  list.push_back("Threshold");
162  list.push_back("Gain");
163  list.push_back("Colour Rotation");
164 // list.push_back("Min Frequency");
165 // list.push_back("Max Frequency");
166  list.push_back("Frequency Scale");
168  return list;
169 }
170 
171 QString
172 SpectrogramLayer::getPropertyLabel(const PropertyName &name) const
173 {
174  if (name == "Colour") return tr("Colour");
175  if (name == "Colour Scale") return tr("Colour Scale");
176  if (name == "Window Size") return tr("Window Size");
177  if (name == "Window Increment") return tr("Window Overlap");
178  if (name == "Normalize Columns") return tr("Normalize Columns");
179  if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
180  if (name == "Bin Display") return tr("Bin Display");
181  if (name == "Threshold") return tr("Threshold");
182  if (name == "Gain") return tr("Gain");
183  if (name == "Colour Rotation") return tr("Colour Rotation");
184  if (name == "Min Frequency") return tr("Min Frequency");
185  if (name == "Max Frequency") return tr("Max Frequency");
186  if (name == "Frequency Scale") return tr("Frequency Scale");
187  if (name == "Zero Padding") return tr("Smoothing");
188  return "";
189 }
190 
191 QString
192 SpectrogramLayer::getPropertyIconName(const PropertyName &name) const
193 {
194  if (name == "Normalize Columns") return "normalise-columns";
195  if (name == "Normalize Visible Area") return "normalise";
196  return "";
197 }
198 
199 Layer::PropertyType
200 SpectrogramLayer::getPropertyType(const PropertyName &name) const
201 {
202  if (name == "Gain") return RangeProperty;
203  if (name == "Colour Rotation") return RangeProperty;
204  if (name == "Normalize Columns") return ToggleProperty;
205  if (name == "Normalize Visible Area") return ToggleProperty;
206  if (name == "Threshold") return RangeProperty;
207  if (name == "Zero Padding") return ToggleProperty;
208  return ValueProperty;
209 }
210 
211 QString
212 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
213 {
214  if (name == "Bin Display" ||
215  name == "Frequency Scale") return tr("Bins");
216  if (name == "Window Size" ||
217  name == "Window Increment" ||
218  name == "Zero Padding") return tr("Window");
219  if (name == "Colour" ||
220  name == "Threshold" ||
221  name == "Colour Rotation") return tr("Colour");
222  if (name == "Normalize Columns" ||
223  name == "Normalize Visible Area" ||
224  name == "Gain" ||
225  name == "Colour Scale") return tr("Scale");
226  return QString();
227 }
228 
229 int
231  int *min, int *max, int *deflt) const
232 {
233  int val = 0;
234 
235  int garbage0, garbage1, garbage2;
236  if (!min) min = &garbage0;
237  if (!max) max = &garbage1;
238  if (!deflt) deflt = &garbage2;
239 
240  if (name == "Gain") {
241 
242  *min = -50;
243  *max = 50;
244 
245  *deflt = lrintf(log10(m_initialGain) * 20.0);;
246  if (*deflt < *min) *deflt = *min;
247  if (*deflt > *max) *deflt = *max;
248 
249  val = lrintf(log10(m_gain) * 20.0);
250  if (val < *min) val = *min;
251  if (val > *max) val = *max;
252 
253  } else if (name == "Threshold") {
254 
255  *min = -50;
256  *max = 0;
257 
258  *deflt = lrintf(AudioLevel::multiplier_to_dB(m_initialThreshold));
259  if (*deflt < *min) *deflt = *min;
260  if (*deflt > *max) *deflt = *max;
261 
262  val = lrintf(AudioLevel::multiplier_to_dB(m_threshold));
263  if (val < *min) val = *min;
264  if (val > *max) val = *max;
265 
266  } else if (name == "Colour Rotation") {
267 
268  *min = 0;
269  *max = 256;
270  *deflt = m_initialRotation;
271 
272  val = m_colourRotation;
273 
274  } else if (name == "Colour Scale") {
275 
276  *min = 0;
277  *max = 4;
278  *deflt = int(dBColourScale);
279 
280  val = (int)m_colourScale;
281 
282  } else if (name == "Colour") {
283 
284  *min = 0;
285  *max = ColourMapper::getColourMapCount() - 1;
286  *deflt = 0;
287 
288  val = m_colourMap;
289 
290  } else if (name == "Window Size") {
291 
292  *min = 0;
293  *max = 10;
294  *deflt = 5;
295 
296  val = 0;
297  int ws = m_windowSize;
298  while (ws > 32) { ws >>= 1; val ++; }
299 
300  } else if (name == "Window Increment") {
301 
302  *min = 0;
303  *max = 5;
304  *deflt = 2;
305 
306  val = m_windowHopLevel;
307 
308  } else if (name == "Zero Padding") {
309 
310  *min = 0;
311  *max = 1;
312  *deflt = 0;
313 
314  val = m_zeroPadLevel > 0 ? 1 : 0;
315 
316  } else if (name == "Min Frequency") {
317 
318  *min = 0;
319  *max = 9;
320  *deflt = 1;
321 
322  switch (m_minFrequency) {
323  case 0: default: val = 0; break;
324  case 10: val = 1; break;
325  case 20: val = 2; break;
326  case 40: val = 3; break;
327  case 100: val = 4; break;
328  case 250: val = 5; break;
329  case 500: val = 6; break;
330  case 1000: val = 7; break;
331  case 4000: val = 8; break;
332  case 10000: val = 9; break;
333  }
334 
335  } else if (name == "Max Frequency") {
336 
337  *min = 0;
338  *max = 9;
339  *deflt = 6;
340 
341  switch (m_maxFrequency) {
342  case 500: val = 0; break;
343  case 1000: val = 1; break;
344  case 1500: val = 2; break;
345  case 2000: val = 3; break;
346  case 4000: val = 4; break;
347  case 6000: val = 5; break;
348  case 8000: val = 6; break;
349  case 12000: val = 7; break;
350  case 16000: val = 8; break;
351  default: val = 9; break;
352  }
353 
354  } else if (name == "Frequency Scale") {
355 
356  *min = 0;
357  *max = 1;
358  *deflt = int(LinearFrequencyScale);
359  val = (int)m_frequencyScale;
360 
361  } else if (name == "Bin Display") {
362 
363  *min = 0;
364  *max = 2;
365  *deflt = int(AllBins);
366  val = (int)m_binDisplay;
367 
368  } else if (name == "Normalize Columns") {
369 
370  *deflt = 0;
371  val = (m_normalizeColumns ? 1 : 0);
372 
373  } else if (name == "Normalize Visible Area") {
374 
375  *deflt = 0;
376  val = (m_normalizeVisibleArea ? 1 : 0);
377 
378  } else {
379  val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
380  }
381 
382  return val;
383 }
384 
385 QString
387  int value) const
388 {
389  if (name == "Colour") {
390  return ColourMapper::getColourMapName(value);
391  }
392  if (name == "Colour Scale") {
393  switch (value) {
394  default:
395  case 0: return tr("Linear");
396  case 1: return tr("Meter");
397  case 2: return tr("dBV^2");
398  case 3: return tr("dBV");
399  case 4: return tr("Phase");
400  }
401  }
402  if (name == "Window Size") {
403  return QString("%1").arg(32 << value);
404  }
405  if (name == "Window Increment") {
406  switch (value) {
407  default:
408  case 0: return tr("None");
409  case 1: return tr("25 %");
410  case 2: return tr("50 %");
411  case 3: return tr("75 %");
412  case 4: return tr("87.5 %");
413  case 5: return tr("93.75 %");
414  }
415  }
416  if (name == "Zero Padding") {
417  if (value == 0) return tr("None");
418  return QString("%1x").arg(value + 1);
419  }
420  if (name == "Min Frequency") {
421  switch (value) {
422  default:
423  case 0: return tr("No min");
424  case 1: return tr("10 Hz");
425  case 2: return tr("20 Hz");
426  case 3: return tr("40 Hz");
427  case 4: return tr("100 Hz");
428  case 5: return tr("250 Hz");
429  case 6: return tr("500 Hz");
430  case 7: return tr("1 KHz");
431  case 8: return tr("4 KHz");
432  case 9: return tr("10 KHz");
433  }
434  }
435  if (name == "Max Frequency") {
436  switch (value) {
437  default:
438  case 0: return tr("500 Hz");
439  case 1: return tr("1 KHz");
440  case 2: return tr("1.5 KHz");
441  case 3: return tr("2 KHz");
442  case 4: return tr("4 KHz");
443  case 5: return tr("6 KHz");
444  case 6: return tr("8 KHz");
445  case 7: return tr("12 KHz");
446  case 8: return tr("16 KHz");
447  case 9: return tr("No max");
448  }
449  }
450  if (name == "Frequency Scale") {
451  switch (value) {
452  default:
453  case 0: return tr("Linear");
454  case 1: return tr("Log");
455  }
456  }
457  if (name == "Bin Display") {
458  switch (value) {
459  default:
460  case 0: return tr("All Bins");
461  case 1: return tr("Peak Bins");
462  case 2: return tr("Frequencies");
463  }
464  }
465  return tr("<unknown>");
466 }
467 
468 RangeMapper *
469 SpectrogramLayer::getNewPropertyRangeMapper(const PropertyName &name) const
470 {
471  if (name == "Gain") {
472  return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
473  }
474  if (name == "Threshold") {
475  return new LinearRangeMapper(-50, 0, -50, 0, tr("dB"));
476  }
477  return 0;
478 }
479 
480 void
481 SpectrogramLayer::setProperty(const PropertyName &name, int value)
482 {
483  if (name == "Gain") {
484  setGain(pow(10, float(value)/20.0));
485  } else if (name == "Threshold") {
486  if (value == -50) setThreshold(0.0);
487  else setThreshold(AudioLevel::dB_to_multiplier(value));
488  } else if (name == "Colour Rotation") {
489  setColourRotation(value);
490  } else if (name == "Colour") {
491  setColourMap(value);
492  } else if (name == "Window Size") {
493  setWindowSize(32 << value);
494  } else if (name == "Window Increment") {
495  setWindowHopLevel(value);
496  } else if (name == "Zero Padding") {
497  setZeroPadLevel(value > 0.1 ? 3 : 0);
498  } else if (name == "Min Frequency") {
499  switch (value) {
500  default:
501  case 0: setMinFrequency(0); break;
502  case 1: setMinFrequency(10); break;
503  case 2: setMinFrequency(20); break;
504  case 3: setMinFrequency(40); break;
505  case 4: setMinFrequency(100); break;
506  case 5: setMinFrequency(250); break;
507  case 6: setMinFrequency(500); break;
508  case 7: setMinFrequency(1000); break;
509  case 8: setMinFrequency(4000); break;
510  case 9: setMinFrequency(10000); break;
511  }
512  int vs = getCurrentVerticalZoomStep();
513  if (vs != m_lastEmittedZoomStep) {
514  emit verticalZoomChanged();
516  }
517  } else if (name == "Max Frequency") {
518  switch (value) {
519  case 0: setMaxFrequency(500); break;
520  case 1: setMaxFrequency(1000); break;
521  case 2: setMaxFrequency(1500); break;
522  case 3: setMaxFrequency(2000); break;
523  case 4: setMaxFrequency(4000); break;
524  case 5: setMaxFrequency(6000); break;
525  case 6: setMaxFrequency(8000); break;
526  case 7: setMaxFrequency(12000); break;
527  case 8: setMaxFrequency(16000); break;
528  default:
529  case 9: setMaxFrequency(0); break;
530  }
531  int vs = getCurrentVerticalZoomStep();
532  if (vs != m_lastEmittedZoomStep) {
533  emit verticalZoomChanged();
535  }
536  } else if (name == "Colour Scale") {
537  switch (value) {
538  default:
539  case 0: setColourScale(LinearColourScale); break;
540  case 1: setColourScale(MeterColourScale); break;
541  case 2: setColourScale(dBSquaredColourScale); break;
542  case 3: setColourScale(dBColourScale); break;
543  case 4: setColourScale(PhaseColourScale); break;
544  }
545  } else if (name == "Frequency Scale") {
546  switch (value) {
547  default:
548  case 0: setFrequencyScale(LinearFrequencyScale); break;
549  case 1: setFrequencyScale(LogFrequencyScale); break;
550  }
551  } else if (name == "Bin Display") {
552  switch (value) {
553  default:
554  case 0: setBinDisplay(AllBins); break;
555  case 1: setBinDisplay(PeakBins); break;
556  case 2: setBinDisplay(PeakFrequencies); break;
557  }
558  } else if (name == "Normalize Columns") {
559  setNormalizeColumns(value ? true : false);
560  } else if (name == "Normalize Visible Area") {
561  setNormalizeVisibleArea(value ? true : false);
562  }
563 }
564 
565 void
567 {
568  for (ViewImageCache::iterator i = m_imageCaches.begin();
569  i != m_imageCaches.end(); ++i) {
570  i->second.validArea = QRect();
571  }
572 }
573 
574 void
575 SpectrogramLayer::invalidateImageCaches(int startFrame, int endFrame)
576 {
577  for (ViewImageCache::iterator i = m_imageCaches.begin();
578  i != m_imageCaches.end(); ++i) {
579 
581  const View *v = i->first;
582 
583 #ifdef DEBUG_SPECTROGRAM_REPAINT
584  SVDEBUG << "SpectrogramLayer::invalidateImageCaches("
585  << startFrame << ", " << endFrame << "): view range is "
586  << v->getStartFrame() << ", " << v->getEndFrame()
587  << endl;
588 
589  cerr << "Valid area was: " << i->second.validArea.x() << ", "
590  << i->second.validArea.y() << " "
591  << i->second.validArea.width() << "x"
592  << i->second.validArea.height() << endl;
593 #endif
594 
595  if (int(startFrame) > v->getStartFrame()) {
596  if (startFrame >= v->getEndFrame()) {
597 #ifdef DEBUG_SPECTROGRAM_REPAINT
598  cerr << "Modified start frame is off right of view" << endl;
599 #endif
600  return;
601  }
602  int x = v->getXForFrame(startFrame);
603 #ifdef DEBUG_SPECTROGRAM_REPAINT
604  SVDEBUG << "clipping from 0 to " << x-1 << endl;
605 #endif
606  if (x > 1) {
607  i->second.validArea &=
608  QRect(0, 0, x-1, v->height());
609  } else {
610  i->second.validArea = QRect();
611  }
612  } else {
613  if (int(endFrame) < v->getStartFrame()) {
614 #ifdef DEBUG_SPECTROGRAM_REPAINT
615  cerr << "Modified end frame is off left of view" << endl;
616 #endif
617  return;
618  }
619  int x = v->getXForFrame(endFrame);
620 #ifdef DEBUG_SPECTROGRAM_REPAINT
621  SVDEBUG << "clipping from " << x+1 << " to " << v->width()
622  << endl;
623 #endif
624  if (x < v->width()) {
625  i->second.validArea &=
626  QRect(x+1, 0, v->width()-(x+1), v->height());
627  } else {
628  i->second.validArea = QRect();
629  }
630  }
631 
632 #ifdef DEBUG_SPECTROGRAM_REPAINT
633  cerr << "Valid area is now: " << i->second.validArea.x() << ", "
634  << i->second.validArea.y() << " "
635  << i->second.validArea.width() << "x"
636  << i->second.validArea.height() << endl;
637 #endif
638  }
639 }
640 
641 void
642 SpectrogramLayer::preferenceChanged(PropertyContainer::PropertyName name)
643 {
644  SVDEBUG << "SpectrogramLayer::preferenceChanged(" << name << ")" << endl;
645 
646  if (name == "Window Type") {
647  setWindowType(Preferences::getInstance()->getWindowType());
648  return;
649  }
650  if (name == "Spectrogram Y Smoothing") {
653  emit layerParametersChanged();
654  }
655  if (name == "Spectrogram X Smoothing") {
658  emit layerParametersChanged();
659  }
660  if (name == "Tuning Frequency") {
661  emit layerParametersChanged();
662  }
663 }
664 
665 void
667 {
668  if (m_channel == ch) return;
669 
671  m_channel = ch;
673 
674  emit layerParametersChanged();
675 }
676 
677 int
679 {
680  return m_channel;
681 }
682 
683 void
685 {
686  if (m_windowSize == ws) return;
687 
689 
690  m_windowSize = ws;
691  m_fftSize = ws * (m_zeroPadLevel + 1);
692 
694 
695  emit layerParametersChanged();
696 }
697 
698 int
700 {
701  return m_windowSize;
702 }
703 
704 void
706 {
707  if (m_windowHopLevel == v) return;
708 
710 
711  m_windowHopLevel = v;
712 
714 
715  emit layerParametersChanged();
716 
717 // fillCache();
718 }
719 
720 int
722 {
723  return m_windowHopLevel;
724 }
725 
726 void
728 {
729  if (m_zeroPadLevel == v) return;
730 
732 
733  m_zeroPadLevel = v;
734  m_fftSize = m_windowSize * (v + 1);
735 
737 
738  emit layerParametersChanged();
739 }
740 
741 int
743 {
744  return m_zeroPadLevel;
745 }
746 
747 void
749 {
750  if (m_windowType == w) return;
751 
753 
754  m_windowType = w;
755 
757 
758  emit layerParametersChanged();
759 }
760 
761 WindowType
763 {
764  return m_windowType;
765 }
766 
767 void
769 {
770 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
771 // << m_gain << ")" << endl;
772 
773  if (m_gain == gain) return;
774 
776 
777  m_gain = gain;
778 
779  emit layerParametersChanged();
780 }
781 
782 float
784 {
785  return m_gain;
786 }
787 
788 void
790 {
791  if (m_threshold == threshold) return;
792 
794 
795  m_threshold = threshold;
796 
797  emit layerParametersChanged();
798 }
799 
800 float
802 {
803  return m_threshold;
804 }
805 
806 void
808 {
809  if (m_minFrequency == mf) return;
810 
811 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl;
812 
815 
816  m_minFrequency = mf;
817 
818  emit layerParametersChanged();
819 }
820 
821 int
823 {
824  return m_minFrequency;
825 }
826 
827 void
829 {
830  if (m_maxFrequency == mf) return;
831 
832 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl;
833 
836 
837  m_maxFrequency = mf;
838 
839  emit layerParametersChanged();
840 }
841 
842 int
844 {
845  return m_maxFrequency;
846 }
847 
848 void
850 {
852 
853  if (r < 0) r = 0;
854  if (r > 256) r = 256;
855  int distance = r - m_colourRotation;
856 
857  if (distance != 0) {
858  rotatePalette(-distance);
859  m_colourRotation = r;
860  }
861 
862  emit layerParametersChanged();
863 }
864 
865 void
867 {
868  if (m_colourScale == colourScale) return;
869 
871 
872  m_colourScale = colourScale;
873 
874  emit layerParametersChanged();
875 }
876 
879 {
880  return m_colourScale;
881 }
882 
883 void
885 {
886  if (m_colourMap == map) return;
887 
889 
890  m_colourMap = map;
892 
893  emit layerParametersChanged();
894 }
895 
896 int
898 {
899  return m_colourMap;
900 }
901 
902 void
904 {
905  if (m_frequencyScale == frequencyScale) return;
906 
908  m_frequencyScale = frequencyScale;
909 
910  emit layerParametersChanged();
911 }
912 
915 {
916  return m_frequencyScale;
917 }
918 
919 void
921 {
922  if (m_binDisplay == binDisplay) return;
923 
925  m_binDisplay = binDisplay;
926 
927  emit layerParametersChanged();
928 }
929 
932 {
933  return m_binDisplay;
934 }
935 
936 void
938 {
939  if (m_normalizeColumns == n) return;
940 
943  m_normalizeColumns = n;
944 
945  emit layerParametersChanged();
946 }
947 
948 bool
950 {
951  return m_normalizeColumns;
952 }
953 
954 void
956 {
957  if (m_normalizeHybrid == n) return;
958 
961  m_normalizeHybrid = n;
962 
963  emit layerParametersChanged();
964 }
965 
966 bool
968 {
969  return m_normalizeHybrid;
970 }
971 
972 void
974 {
975  SVDEBUG << "SpectrogramLayer::setNormalizeVisibleArea(" << n
976  << ") (from " << m_normalizeVisibleArea << ")" << endl;
977 
978  if (m_normalizeVisibleArea == n) return;
979 
983 
984  emit layerParametersChanged();
985 }
986 
987 bool
989 {
990  return m_normalizeVisibleArea;
991 }
992 
993 void
995 {
996  if (dormant) {
997 
998 #ifdef DEBUG_SPECTROGRAM_REPAINT
999  SVDEBUG << "SpectrogramLayer::setLayerDormant(" << dormant << ")"
1000  << endl;
1001 #endif
1002 
1003  if (isLayerDormant(v)) {
1004  return;
1005  }
1006 
1007  Layer::setLayerDormant(v, true);
1008 
1010  m_imageCaches.erase(v);
1011 
1012  if (m_fftModels.find(v) != m_fftModels.end()) {
1013 
1014  if (m_sliceableModel == m_fftModels[v].first) {
1015  bool replaced = false;
1016  for (ViewFFTMap::iterator i = m_fftModels.begin();
1017  i != m_fftModels.end(); ++i) {
1018  if (i->second.first != m_sliceableModel) {
1019  emit sliceableModelReplaced(m_sliceableModel, i->second.first);
1020  replaced = true;
1021  break;
1022  }
1023  }
1024  if (!replaced) emit sliceableModelReplaced(m_sliceableModel, 0);
1025  }
1026 
1027  delete m_fftModels[v].first;
1028  m_fftModels.erase(v);
1029 
1030  delete m_peakCaches[v];
1031  m_peakCaches.erase(v);
1032  }
1033 
1034  } else {
1035 
1036  Layer::setLayerDormant(v, false);
1037  }
1038 }
1039 
1040 void
1042 {
1043 #ifdef DEBUG_SPECTROGRAM_REPAINT
1044  SVDEBUG << "SpectrogramLayer::cacheInvalid()" << endl;
1045 #endif
1046 
1049 }
1050 
1051 void
1053 {
1054 #ifdef DEBUG_SPECTROGRAM_REPAINT
1055  SVDEBUG << "SpectrogramLayer::cacheInvalid(" << from << ", " << to << ")" << endl;
1056 #endif
1057 
1058  invalidateImageCaches(from, to);
1060 }
1061 
1062 void
1064 {
1065  if (!m_model) return;
1066 
1067  bool allDone = true;
1068 
1069 #ifdef DEBUG_SPECTROGRAM_REPAINT
1070  SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: have " << m_fftModels.size() << " FFT models associated with views" << endl;
1071 #endif
1072 
1073  for (ViewFFTMap::iterator i = m_fftModels.begin();
1074  i != m_fftModels.end(); ++i) {
1075 
1076  const FFTModel *model = i->second.first;
1077  int lastFill = i->second.second;
1078 
1079  if (model) {
1080 
1081  int fill = model->getFillExtent();
1082 
1083 #ifdef DEBUG_SPECTROGRAM_REPAINT
1084  SVDEBUG << "SpectrogramLayer::fillTimerTimedOut: extent for " << model << ": " << fill << ", last " << lastFill << ", total " << m_model->getEndFrame() << endl;
1085 #endif
1086 
1087  if (fill >= lastFill) {
1088  if (fill >= m_model->getEndFrame() && lastFill > 0) {
1089 #ifdef DEBUG_SPECTROGRAM_REPAINT
1090  cerr << "complete!" << endl;
1091 #endif
1093  i->second.second = -1;
1094  emit modelChanged();
1095 
1096  } else if (fill > lastFill) {
1097 #ifdef DEBUG_SPECTROGRAM_REPAINT
1098  cerr << "SpectrogramLayer: emitting modelChanged("
1099  << lastFill << "," << fill << ")" << endl;
1100 #endif
1101  invalidateImageCaches(lastFill, fill);
1102  i->second.second = fill;
1103  emit modelChangedWithin(lastFill, fill);
1104  }
1105  } else {
1106 #ifdef DEBUG_SPECTROGRAM_REPAINT
1107  cerr << "SpectrogramLayer: going backwards, emitting modelChanged("
1108  << m_model->getStartFrame() << "," << m_model->getEndFrame() << ")" << endl;
1109 #endif
1111  i->second.second = fill;
1112  emit modelChangedWithin(m_model->getStartFrame(), m_model->getEndFrame());
1113  }
1114 
1115  if (i->second.second >= 0) {
1116  allDone = false;
1117  }
1118  }
1119  }
1120 
1121  if (allDone) {
1122 #ifdef DEBUG_SPECTROGRAM_REPAINT
1123  cerr << "SpectrogramLayer: all complete!" << endl;
1124 #endif
1125  delete m_updateTimer;
1126  m_updateTimer = 0;
1127  }
1128 }
1129 
1130 bool
1132 {
1133  return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
1134 }
1135 
1136 void
1138 {
1139  int formerRotation = m_colourRotation;
1140 
1142  m_palette.setColour(NO_VALUE, Qt::white);
1143  } else {
1144  m_palette.setColour(NO_VALUE, Qt::black);
1145  }
1146 
1147  ColourMapper mapper(m_colourMap, 1.f, 255.f);
1148 
1149  for (int pixel = 1; pixel < 256; ++pixel) {
1150  m_palette.setColour(pixel, mapper.map(pixel));
1151  }
1152 
1154 
1155  m_colourRotation = 0;
1156  rotatePalette(m_colourRotation - formerRotation);
1157  m_colourRotation = formerRotation;
1158 
1159  m_drawBuffer = QImage();
1160 }
1161 
1162 void
1164 {
1165  QColor newPixels[256];
1166 
1167  newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
1168 
1169  for (int pixel = 1; pixel < 256; ++pixel) {
1170  int target = pixel + distance;
1171  while (target < 1) target += 255;
1172  while (target > 255) target -= 255;
1173  newPixels[target] = m_palette.getColour(pixel);
1174  }
1175 
1176  for (int pixel = 0; pixel < 256; ++pixel) {
1177  m_palette.setColour(pixel, newPixels[pixel]);
1178  }
1179 
1180  m_drawBuffer = QImage();
1181 }
1182 
1183 unsigned char
1185 {
1186  int value;
1187 
1188  float min = 0.f;
1189  float max = 1.f;
1190 
1191  if (m_normalizeVisibleArea) {
1192  min = m_viewMags[v].getMin();
1193  max = m_viewMags[v].getMax();
1194  } else if (!m_normalizeColumns) {
1195  if (m_colourScale == LinearColourScale //||
1196 // m_colourScale == MeterColourScale) {
1197  ) {
1198  max = 0.1f;
1199  }
1200  }
1201 
1202  float thresh = -80.f;
1203 
1204  if (max == 0.f) max = 1.f;
1205  if (max == min) min = max - 0.0001f;
1206 
1207  switch (m_colourScale) {
1208 
1209  default:
1210  case LinearColourScale:
1211  value = int(((input - min) / (max - min)) * 255.f) + 1;
1212  break;
1213 
1214  case MeterColourScale:
1215  value = AudioLevel::multiplier_to_preview
1216  ((input - min) / (max - min), 254) + 1;
1217  break;
1218 
1219  case dBSquaredColourScale:
1220  input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1221  if (input > 0.f) {
1222  input = 10.f * log10f(input);
1223  } else {
1224  input = thresh;
1225  }
1226  if (min > 0.f) {
1227  thresh = 10.f * log10f(min * min);
1228  if (thresh < -80.f) thresh = -80.f;
1229  }
1230  input = (input - thresh) / (-thresh);
1231  if (input < 0.f) input = 0.f;
1232  if (input > 1.f) input = 1.f;
1233  value = int(input * 255.f) + 1;
1234  break;
1235 
1236  case dBColourScale:
1238  //In any case, we need to have some indication of what the dB
1239  //scale is relative to.
1240  input = (input - min) / (max - min);
1241  if (input > 0.f) {
1242  input = 10.f * log10f(input);
1243  } else {
1244  input = thresh;
1245  }
1246  if (min > 0.f) {
1247  thresh = 10.f * log10f(min);
1248  if (thresh < -80.f) thresh = -80.f;
1249  }
1250  input = (input - thresh) / (-thresh);
1251  if (input < 0.f) input = 0.f;
1252  if (input > 1.f) input = 1.f;
1253  value = int(input * 255.f) + 1;
1254  break;
1255 
1256  case PhaseColourScale:
1257  value = int((input * 127.0 / M_PI) + 128);
1258  break;
1259  }
1260 
1261  if (value > UCHAR_MAX) value = UCHAR_MAX;
1262  if (value < 0) value = 0;
1263  return value;
1264 }
1265 
1266 float
1268 {
1269  int sr = m_model->getSampleRate();
1270  float minf = float(sr) / m_fftSize;
1271 
1272  if (m_minFrequency > 0.0) {
1273  int minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.01);
1274  if (minbin < 1) minbin = 1;
1275  minf = minbin * sr / m_fftSize;
1276  }
1277 
1278  return minf;
1279 }
1280 
1281 float
1283 {
1284  int sr = m_model->getSampleRate();
1285  float maxf = float(sr) / 2;
1286 
1287  if (m_maxFrequency > 0.0) {
1288  int maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1289  if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1290  maxf = maxbin * sr / m_fftSize;
1291  }
1292 
1293  return maxf;
1294 }
1295 
1296 bool
1297 SpectrogramLayer::getYBinRange(View *v, int y, float &q0, float &q1) const
1298 {
1299  Profiler profiler("SpectrogramLayer::getYBinRange");
1300 
1301  int h = v->height();
1302  if (y < 0 || y >= h) return false;
1303 
1304  int sr = m_model->getSampleRate();
1305  float minf = getEffectiveMinFrequency();
1306  float maxf = getEffectiveMaxFrequency();
1307 
1308  bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1309 
1310  q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1311  q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1312 
1313  // Now map these on to ("proportions of") actual bins, using raw
1314  // FFT size (unsmoothed)
1315 
1316  q0 = (q0 * m_fftSize) / sr;
1317  q1 = (q1 * m_fftSize) / sr;
1318 
1319  return true;
1320 }
1321 
1322 bool
1323 SpectrogramLayer::getSmoothedYBinRange(View *v, int y, float &q0, float &q1) const
1324 {
1325  Profiler profiler("SpectrogramLayer::getSmoothedYBinRange");
1326 
1327  int h = v->height();
1328  if (y < 0 || y >= h) return false;
1329 
1330  int sr = m_model->getSampleRate();
1331  float minf = getEffectiveMinFrequency();
1332  float maxf = getEffectiveMaxFrequency();
1333 
1334  bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1335 
1336  q0 = v->getFrequencyForY(y, minf, maxf, logarithmic);
1337  q1 = v->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1338 
1339  // Now map these on to ("proportions of") actual bins, using raw
1340  // FFT size (unsmoothed)
1341 
1342  q0 = (q0 * getFFTSize(v)) / sr;
1343  q1 = (q1 * getFFTSize(v)) / sr;
1344 
1345  return true;
1346 }
1347 
1348 bool
1349 SpectrogramLayer::getXBinRange(View *v, int x, float &s0, float &s1) const
1350 {
1351  int modelStart = m_model->getStartFrame();
1352  int modelEnd = m_model->getEndFrame();
1353 
1354  // Each pixel column covers an exact range of sample frames:
1355  int f0 = v->getFrameForX(x) - modelStart;
1356  int f1 = v->getFrameForX(x + 1) - modelStart - 1;
1357 
1358  if (f1 < int(modelStart) || f0 > int(modelEnd)) {
1359  return false;
1360  }
1361 
1362  // And that range may be drawn from a possibly non-integral
1363  // range of spectrogram windows:
1364 
1365  int windowIncrement = getWindowIncrement();
1366  s0 = float(f0) / windowIncrement;
1367  s1 = float(f1) / windowIncrement;
1368 
1369  return true;
1370 }
1371 
1372 bool
1373 SpectrogramLayer::getXBinSourceRange(View *v, int x, RealTime &min, RealTime &max) const
1374 {
1375  float s0 = 0, s1 = 0;
1376  if (!getXBinRange(v, x, s0, s1)) return false;
1377 
1378  int s0i = int(s0 + 0.001);
1379  int s1i = int(s1);
1380 
1381  int windowIncrement = getWindowIncrement();
1382  int w0 = s0i * windowIncrement - (m_windowSize - windowIncrement)/2;
1383  int w1 = s1i * windowIncrement + windowIncrement +
1384  (m_windowSize - windowIncrement)/2 - 1;
1385 
1386  min = RealTime::frame2RealTime(w0, m_model->getSampleRate());
1387  max = RealTime::frame2RealTime(w1, m_model->getSampleRate());
1388  return true;
1389 }
1390 
1391 bool
1392 SpectrogramLayer::getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax)
1393 const
1394 {
1395  float q0 = 0, q1 = 0;
1396  if (!getYBinRange(v, y, q0, q1)) return false;
1397 
1398  int q0i = int(q0 + 0.001);
1399  int q1i = int(q1);
1400 
1401  int sr = m_model->getSampleRate();
1402 
1403  for (int q = q0i; q <= q1i; ++q) {
1404  if (q == q0i) freqMin = (sr * q) / m_fftSize;
1405  if (q == q1i) freqMax = (sr * (q+1)) / m_fftSize;
1406  }
1407  return true;
1408 }
1409 
1410 bool
1412  float &freqMin, float &freqMax,
1413  float &adjFreqMin, float &adjFreqMax)
1414 const
1415 {
1416  if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1417  return false;
1418  }
1419 
1420  FFTModel *fft = getFFTModel(v);
1421  if (!fft) return false;
1422 
1423  float s0 = 0, s1 = 0;
1424  if (!getXBinRange(v, x, s0, s1)) return false;
1425 
1426  float q0 = 0, q1 = 0;
1427  if (!getYBinRange(v, y, q0, q1)) return false;
1428 
1429  int s0i = int(s0 + 0.001);
1430  int s1i = int(s1);
1431 
1432  int q0i = int(q0 + 0.001);
1433  int q1i = int(q1);
1434 
1435  int sr = m_model->getSampleRate();
1436 
1437  bool haveAdj = false;
1438 
1439  bool peaksOnly = (m_binDisplay == PeakBins ||
1441 
1442  for (int q = q0i; q <= q1i; ++q) {
1443 
1444  for (int s = s0i; s <= s1i; ++s) {
1445 
1446  if (!fft->isColumnAvailable(s)) continue;
1447 
1448  float binfreq = (float(sr) * q) / m_windowSize;
1449  if (q == q0i) freqMin = binfreq;
1450  if (q == q1i) freqMax = binfreq;
1451 
1452  if (peaksOnly && !fft->isLocalPeak(s, q)) continue;
1453 
1454  if (!fft->isOverThreshold(s, q, m_threshold * (m_fftSize/2))) continue;
1455 
1456  float freq = binfreq;
1457 
1458  if (s < int(fft->getWidth()) - 1) {
1459 
1460  fft->estimateStableFrequency(s, q, freq);
1461 
1462  if (!haveAdj || freq < adjFreqMin) adjFreqMin = freq;
1463  if (!haveAdj || freq > adjFreqMax) adjFreqMax = freq;
1464 
1465  haveAdj = true;
1466  }
1467  }
1468  }
1469 
1470  if (!haveAdj) {
1471  adjFreqMin = adjFreqMax = 0.0;
1472  }
1473 
1474  return haveAdj;
1475 }
1476 
1477 bool
1479  float &min, float &max,
1480  float &phaseMin, float &phaseMax) const
1481 {
1482  if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1483  return false;
1484  }
1485 
1486  float q0 = 0, q1 = 0;
1487  if (!getYBinRange(v, y, q0, q1)) return false;
1488 
1489  float s0 = 0, s1 = 0;
1490  if (!getXBinRange(v, x, s0, s1)) return false;
1491 
1492  int q0i = int(q0 + 0.001);
1493  int q1i = int(q1);
1494 
1495  int s0i = int(s0 + 0.001);
1496  int s1i = int(s1);
1497 
1498  bool rv = false;
1499 
1500  int zp = getZeroPadLevel(v);
1501  q0i *= zp + 1;
1502  q1i *= zp + 1;
1503 
1504  FFTModel *fft = getFFTModel(v);
1505 
1506  if (fft) {
1507 
1508  int cw = fft->getWidth();
1509  int ch = fft->getHeight();
1510 
1511  min = 0.0;
1512  max = 0.0;
1513  phaseMin = 0.0;
1514  phaseMax = 0.0;
1515  bool have = false;
1516 
1517  for (int q = q0i; q <= q1i; ++q) {
1518  for (int s = s0i; s <= s1i; ++s) {
1519  if (s >= 0 && q >= 0 && s < cw && q < ch) {
1520 
1521  if (!fft->isColumnAvailable(s)) continue;
1522 
1523  float value;
1524 
1525  value = fft->getPhaseAt(s, q);
1526  if (!have || value < phaseMin) { phaseMin = value; }
1527  if (!have || value > phaseMax) { phaseMax = value; }
1528 
1529  value = fft->getMagnitudeAt(s, q) / (m_fftSize/2);
1530  if (!have || value < min) { min = value; }
1531  if (!have || value > max) { max = value; }
1532 
1533  have = true;
1534  }
1535  }
1536  }
1537 
1538  if (have) {
1539  rv = true;
1540  }
1541  }
1542 
1543  return rv;
1544 }
1545 
1546 int
1548 {
1550 
1551  if (m_binDisplay != AllBins) return 0;
1552 
1553  Preferences::SpectrogramSmoothing smoothing =
1554  Preferences::getInstance()->getSpectrogramSmoothing();
1555 
1556  if (smoothing == Preferences::NoSpectrogramSmoothing ||
1557  smoothing == Preferences::SpectrogramInterpolated) return 0;
1558 
1559  if (m_frequencyScale == LogFrequencyScale) return 3;
1560 
1561  int sr = m_model->getSampleRate();
1562 
1563  int maxbin = m_fftSize / 2;
1564  if (m_maxFrequency > 0) {
1565  maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
1566  if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
1567  }
1568 
1569  int minbin = 1;
1570  if (m_minFrequency > 0) {
1571  minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.1);
1572  if (minbin < 1) minbin = 1;
1573  if (minbin >= maxbin) minbin = maxbin - 1;
1574  }
1575 
1576  float perPixel =
1577  float(v->height()) /
1578  float((maxbin - minbin) / (m_zeroPadLevel + 1));
1579 
1580  if (perPixel > 2.8) {
1581  return 3; // 4x oversampling
1582  } else if (perPixel > 1.5) {
1583  return 1; // 2x
1584  } else {
1585  return 0; // 1x
1586  }
1587 }
1588 
1589 int
1591 {
1592  return m_fftSize * (getZeroPadLevel(v) + 1);
1593 }
1594 
1595 FFTModel *
1597 {
1598  if (!m_model) return 0;
1599 
1600  int fftSize = getFFTSize(v);
1601 
1602  if (m_fftModels.find(v) != m_fftModels.end()) {
1603  if (m_fftModels[v].first == 0) {
1604 #ifdef DEBUG_SPECTROGRAM_REPAINT
1605  SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found null model" << endl;
1606 #endif
1607  return 0;
1608  }
1609  if (m_fftModels[v].first->getHeight() != fftSize / 2 + 1) {
1610 #ifdef DEBUG_SPECTROGRAM_REPAINT
1611  SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a model with the wrong height (" << m_fftModels[v].first->getHeight() << ", wanted " << (fftSize / 2 + 1) << ")" << endl;
1612 #endif
1613  delete m_fftModels[v].first;
1614  m_fftModels.erase(v);
1615  delete m_peakCaches[v];
1616  m_peakCaches.erase(v);
1617  } else {
1618 #ifdef DEBUG_SPECTROGRAM_REPAINT
1619  SVDEBUG << "SpectrogramLayer::getFFTModel(" << v << "): Found a good model of height " << m_fftModels[v].first->getHeight() << endl;
1620 #endif
1621  return m_fftModels[v].first;
1622  }
1623  }
1624 
1625  if (m_fftModels.find(v) == m_fftModels.end()) {
1626 
1627  FFTModel *model = new FFTModel(m_model,
1628  m_channel,
1629  m_windowType,
1630  m_windowSize,
1632  fftSize,
1633  true, // polar
1634  StorageAdviser::SpeedCritical,
1636 
1637  if (!model->isOK()) {
1638  QMessageBox::critical
1639  (0, tr("FFT cache failed"),
1640  tr("Failed to create the FFT model for this spectrogram.\n"
1641  "There may be insufficient memory or disc space to continue."));
1642  delete model;
1643  m_fftModels[v] = FFTFillPair(0, 0);
1644  return 0;
1645  }
1646 
1647  if (!m_sliceableModel) {
1648 #ifdef DEBUG_SPECTROGRAM
1649  cerr << "SpectrogramLayer: emitting sliceableModelReplaced(0, " << model << ")" << endl;
1650 #endif
1651  ((SpectrogramLayer *)this)->sliceableModelReplaced(0, model);
1652  m_sliceableModel = model;
1653  }
1654 
1655  m_fftModels[v] = FFTFillPair(model, 0);
1656 
1657  model->resume();
1658 
1659  delete m_updateTimer;
1660  m_updateTimer = new QTimer((SpectrogramLayer *)this);
1661  connect(m_updateTimer, SIGNAL(timeout()),
1662  this, SLOT(fillTimerTimedOut()));
1663  m_updateTimer->start(200);
1664  }
1665 
1666  return m_fftModels[v].first;
1667 }
1668 
1669 Dense3DModelPeakCache *
1671 {
1672  if (!m_peakCaches[v]) {
1673  FFTModel *f = getFFTModel(v);
1674  if (!f) return 0;
1675  m_peakCaches[v] = new Dense3DModelPeakCache(f, 8);
1676  }
1677  return m_peakCaches[v];
1678 }
1679 
1680 const Model *
1682 {
1683  if (m_sliceableModel) return m_sliceableModel;
1684  if (m_fftModels.empty()) return 0;
1685  m_sliceableModel = m_fftModels.begin()->second.first;
1686  return m_sliceableModel;
1687 }
1688 
1689 void
1691 {
1692  for (ViewFFTMap::iterator i = m_fftModels.begin();
1693  i != m_fftModels.end(); ++i) {
1694  delete i->second.first;
1695  }
1696  for (PeakCacheMap::iterator i = m_peakCaches.begin();
1697  i != m_peakCaches.end(); ++i) {
1698  delete i->second;
1699  }
1700 
1701  m_fftModels.clear();
1702  m_peakCaches.clear();
1703 
1704  if (m_sliceableModel) {
1705  cerr << "SpectrogramLayer: emitting sliceableModelReplaced(" << m_sliceableModel << ", 0)" << endl;
1707  m_sliceableModel = 0;
1708  }
1709 }
1710 
1711 void
1713 {
1714  m_viewMags.clear();
1715  for (std::vector<MagnitudeRange>::iterator i = m_columnMags.begin();
1716  i != m_columnMags.end(); ++i) {
1717  *i = MagnitudeRange();
1718  }
1719 }
1720 
1721 bool
1723 {
1724  MagnitudeRange mag;
1725 
1726  int x0 = 0, x1 = v->width();
1727  float s00 = 0, s01 = 0, s10 = 0, s11 = 0;
1728 
1729  if (!getXBinRange(v, x0, s00, s01)) {
1730  s00 = s01 = m_model->getStartFrame() / getWindowIncrement();
1731  }
1732 
1733  if (!getXBinRange(v, x1, s10, s11)) {
1734  s10 = s11 = m_model->getEndFrame() / getWindowIncrement();
1735  }
1736 
1737  int s0 = int(std::min(s00, s10) + 0.0001);
1738  int s1 = int(std::max(s01, s11) + 0.0001);
1739 
1740 // SVDEBUG << "SpectrogramLayer::updateViewMagnitudes: x0 = " << x0 << ", x1 = " << x1 << ", s00 = " << s00 << ", s11 = " << s11 << " s0 = " << s0 << ", s1 = " << s1 << endl;
1741 
1742  if (int(m_columnMags.size()) <= s1) {
1743  m_columnMags.resize(s1 + 1);
1744  }
1745 
1746  for (int s = s0; s <= s1; ++s) {
1747  if (m_columnMags[s].isSet()) {
1748  mag.sample(m_columnMags[s]);
1749  }
1750  }
1751 
1752 #ifdef DEBUG_SPECTROGRAM_REPAINT
1753  SVDEBUG << "SpectrogramLayer::updateViewMagnitudes returning from cols "
1754  << s0 << " -> " << s1 << " inclusive" << endl;
1755 #endif
1756 
1757  if (!mag.isSet()) return false;
1758  if (mag == m_viewMags[v]) return false;
1759  m_viewMags[v] = mag;
1760  return true;
1761 }
1762 
1763 void
1765 {
1766  m_synchronous = synchronous;
1767 }
1768 
1769 void
1770 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
1771 {
1772  // What a lovely, old-fashioned function this is.
1773  // It's practically FORTRAN 77 in its clarity and linearity.
1774 
1775  Profiler profiler("SpectrogramLayer::paint", false);
1776 
1777 #ifdef DEBUG_SPECTROGRAM_REPAINT
1778  SVDEBUG << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << endl;
1779 
1780  cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
1781 #endif
1782 
1783  int startFrame = v->getStartFrame();
1784  if (startFrame < 0) m_candidateFillStartFrame = 0;
1785  else m_candidateFillStartFrame = startFrame;
1786 
1787  if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1788  return;
1789  }
1790 
1791  if (isLayerDormant(v)) {
1792  SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
1793  }
1794 
1795  // Need to do this even if !isLayerDormant, as that could mean v
1796  // is not in the dormancy map at all -- we need it to be present
1797  // and accountable for when determining whether we need the cache
1798  // in the cache-fill thread above.
1800  const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1801 
1802  int fftSize = getFFTSize(v);
1803 /*
1804  FFTModel *fft = getFFTModel(v);
1805  if (!fft) {
1806  cerr << "ERROR: SpectrogramLayer::paint(): No FFT model, returning" << endl;
1807  return;
1808  }
1809 */
1810  ImageCache &cache = m_imageCaches[v];
1811 
1812 #ifdef DEBUG_SPECTROGRAM_REPAINT
1813  SVDEBUG << "SpectrogramLayer::paint(): image cache valid area " << cache.
1814 
1815 validArea.x() << ", " << cache.validArea.y() << ", " << cache.validArea.width() << "x" << cache.validArea.height() << endl;
1816 #endif
1817 
1818 #ifdef DEBUG_SPECTROGRAM_REPAINT
1819  bool stillCacheing = (m_updateTimer != 0);
1820  SVDEBUG << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << endl;
1821 #endif
1822 
1823  int zoomLevel = v->getZoomLevel();
1824 
1825  int x0 = 0;
1826  int x1 = v->width();
1827 
1828  bool recreateWholeImageCache = true;
1829 
1830  x0 = rect.left();
1831  x1 = rect.right() + 1;
1832 /*
1833  float xPixelRatio = float(fft->getResolution()) / float(zoomLevel);
1834  cerr << "xPixelRatio = " << xPixelRatio << endl;
1835  if (xPixelRatio < 1.f) xPixelRatio = 1.f;
1836 */
1837  if (cache.validArea.width() > 0) {
1838 
1839  int cw = cache.image.width();
1840  int ch = cache.image.height();
1841 
1842  if (int(cache.zoomLevel) == zoomLevel &&
1843  cw == v->width() &&
1844  ch == v->height()) {
1845 
1846  if (v->getXForFrame(cache.startFrame) ==
1847  v->getXForFrame(startFrame) &&
1848  cache.validArea.x() <= x0 &&
1849  cache.validArea.x() + cache.validArea.width() >= x1) {
1850 
1851 #ifdef DEBUG_SPECTROGRAM_REPAINT
1852  cerr << "SpectrogramLayer: image cache good" << endl;
1853 #endif
1854 
1855  paint.drawImage(rect, cache.image, rect);
1857 // paint.drawImage(v->rect(), cache.image,
1858 // QRect(QPoint(0, 0), cache.image.size()));
1859 
1861  return;
1862 
1863  } else {
1864 
1865 #ifdef DEBUG_SPECTROGRAM_REPAINT
1866  cerr << "SpectrogramLayer: image cache partially OK" << endl;
1867 #endif
1868 
1869  recreateWholeImageCache = false;
1870 
1871  int dx = v->getXForFrame(cache.startFrame) -
1872  v->getXForFrame(startFrame);
1873 
1874 #ifdef DEBUG_SPECTROGRAM_REPAINT
1875  cerr << "SpectrogramLayer: dx = " << dx << " (image cache " << cw << "x" << ch << ")" << endl;
1876 #endif
1877 
1878  if (dx != 0 &&
1879  dx > -cw &&
1880  dx < cw) {
1881 
1882  int dxp = dx;
1883  if (dxp < 0) dxp = -dxp;
1884  int copy = (cw - dxp) * sizeof(QRgb);
1885  for (int y = 0; y < ch; ++y) {
1886  QRgb *line = (QRgb *)cache.image.scanLine(y);
1887  if (dx < 0) {
1888  memmove(line, line + dxp, copy);
1889  } else {
1890  memmove(line + dxp, line, copy);
1891  }
1892  }
1893 
1894  int px = cache.validArea.x();
1895  int pw = cache.validArea.width();
1896 
1897  if (dx < 0) {
1898  x0 = cw + dx;
1899  x1 = cw;
1900  px += dx;
1901  if (px < 0) {
1902  pw += px;
1903  px = 0;
1904  if (pw < 0) pw = 0;
1905  }
1906  } else {
1907  x0 = 0;
1908  x1 = dx;
1909  px += dx;
1910  if (px + pw > cw) {
1911  pw = int(cw) - px;
1912  if (pw < 0) pw = 0;
1913  }
1914  }
1915 
1916  cache.validArea =
1917  QRect(px, cache.validArea.y(),
1918  pw, cache.validArea.height());
1919 
1920 #ifdef DEBUG_SPECTROGRAM_REPAINT
1921  cerr << "valid area now "
1922  << px << "," << cache.validArea.y()
1923  << " " << pw << "x" << cache.validArea.height()
1924  << endl;
1925 #endif
1926 /*
1927  paint.drawImage(rect & cache.validArea,
1928  cache.image,
1929  rect & cache.validArea);
1930 */
1931  } else if (dx != 0) {
1932 
1933  // we scrolled too far to be of use
1934 
1935 #ifdef DEBUG_SPECTROGRAM_REPAINT
1936  cerr << "dx == " << dx << ": scrolled too far for cache to be useful" << endl;
1937 #endif
1938 
1939  cache.validArea = QRect();
1940  recreateWholeImageCache = true;
1941  }
1942  }
1943  } else {
1944 #ifdef DEBUG_SPECTROGRAM_REPAINT
1945  cerr << "SpectrogramLayer: image cache useless" << endl;
1946  if (int(cache.zoomLevel) != zoomLevel) {
1947  cerr << "(cache zoomLevel " << cache.zoomLevel
1948  << " != " << zoomLevel << ")" << endl;
1949  }
1950  if (cw != v->width()) {
1951  cerr << "(cache width " << cw
1952  << " != " << v->width();
1953  }
1954  if (ch != v->height()) {
1955  cerr << "(cache height " << ch
1956  << " != " << v->height();
1957  }
1958 #endif
1959  cache.validArea = QRect();
1960 // recreateWholeImageCache = true;
1961  }
1962  }
1963 
1964  if (updateViewMagnitudes(v)) {
1965 #ifdef DEBUG_SPECTROGRAM_REPAINT
1966  cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
1967 #endif
1968  if (m_normalizeVisibleArea) {
1969  cache.validArea = QRect();
1970  recreateWholeImageCache = true;
1971  }
1972  } else {
1973 #ifdef DEBUG_SPECTROGRAM_REPAINT
1974  cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
1975 #endif
1976  }
1977 
1978  if (recreateWholeImageCache) {
1979  x0 = 0;
1980  x1 = v->width();
1981  }
1982 
1983  struct timeval tv;
1984  (void)gettimeofday(&tv, 0);
1985  RealTime mainPaintStart = RealTime::fromTimeval(tv);
1986 
1987  int paintBlockWidth = m_lastPaintBlockWidth;
1988 
1989  if (m_synchronous) {
1990  if (paintBlockWidth < x1 - x0) {
1991  // always paint full width
1992  paintBlockWidth = x1 - x0;
1993  }
1994  } else {
1995  if (paintBlockWidth == 0) {
1996  paintBlockWidth = (300000 / zoomLevel);
1997  } else {
1998  RealTime lastTime = m_lastPaintTime;
1999  while (lastTime > RealTime::fromMilliseconds(200) &&
2000  paintBlockWidth > 50) {
2001  paintBlockWidth /= 2;
2002  lastTime = lastTime / 2;
2003  }
2004  while (lastTime < RealTime::fromMilliseconds(90) &&
2005  paintBlockWidth < 1500) {
2006  paintBlockWidth *= 2;
2007  lastTime = lastTime * 2;
2008  }
2009  }
2010 
2011  if (paintBlockWidth < 20) paintBlockWidth = 20;
2012  }
2013 
2014 #ifdef DEBUG_SPECTROGRAM_REPAINT
2015  cerr << "[" << this << "]: last paint width: " << m_lastPaintBlockWidth << ", last paint time: " << m_lastPaintTime << ", new paint width: " << paintBlockWidth << endl;
2016 #endif
2017 
2018  // We always paint the full height when refreshing the cache.
2019  // Smaller heights can be used when painting direct from cache
2020  // (further up in this function), but we want to ensure the cache
2021  // is coherent without having to worry about vertical matching of
2022  // required and valid areas as well as horizontal.
2023 
2024  int h = v->height();
2025 
2026  if (cache.validArea.width() > 0) {
2027 
2028  // If part of the cache is known to be valid, select a strip
2029  // immediately to left or right of the valid part
2030 
2034 
2035  int vx0 = 0, vx1 = 0;
2036  vx0 = cache.validArea.x();
2037  vx1 = cache.validArea.x() + cache.validArea.width();
2038 
2039 #ifdef DEBUG_SPECTROGRAM_REPAINT
2040  cerr << "x0 " << x0 << ", x1 " << x1 << ", vx0 " << vx0 << ", vx1 " << vx1 << ", paintBlockWidth " << paintBlockWidth << endl;
2041 #endif
2042  if (x0 < vx0) {
2043  if (x0 + paintBlockWidth < vx0) {
2044  x0 = vx0 - paintBlockWidth;
2045  }
2046  x1 = vx0;
2047  } else if (x0 >= vx1) {
2048  x0 = vx1;
2049  if (x1 > x0 + paintBlockWidth) {
2050  x1 = x0 + paintBlockWidth;
2051  }
2052  } else {
2053  // x0 is within the valid area
2054  if (x1 > vx1) {
2055  x0 = vx1;
2056  if (x0 + paintBlockWidth < x1) {
2057  x1 = x0 + paintBlockWidth;
2058  }
2059  } else {
2060  x1 = x0; // it's all valid, paint nothing
2061  }
2062  }
2063 
2064  cache.validArea = QRect
2065  (std::min(vx0, x0), cache.validArea.y(),
2066  std::max(vx1 - std::min(vx0, x0),
2067  x1 - std::min(vx0, x0)),
2068  cache.validArea.height());
2069 
2070 #ifdef DEBUG_SPECTROGRAM_REPAINT
2071  cerr << "Valid area becomes " << cache.validArea.x()
2072  << ", " << cache.validArea.y() << ", "
2073  << cache.validArea.width() << "x"
2074  << cache.validArea.height() << endl;
2075 #endif
2076 
2077  } else {
2078  if (x1 > x0 + paintBlockWidth) {
2079  int sfx = x1;
2080  if (startFrame < 0) sfx = v->getXForFrame(0);
2081  if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
2082  x0 = sfx;
2083  x1 = x0 + paintBlockWidth;
2084  } else {
2085  int mid = (x1 + x0) / 2;
2086  x0 = mid - paintBlockWidth/2;
2087  x1 = x0 + paintBlockWidth;
2088  }
2089  }
2090 #ifdef DEBUG_SPECTROGRAM_REPAINT
2091  cerr << "Valid area becomes " << x0 << ", 0, " << (x1-x0)
2092  << "x" << h << endl;
2093 #endif
2094  cache.validArea = QRect(x0, 0, x1 - x0, h);
2095  }
2096 
2097 /*
2098  if (xPixelRatio != 1.f) {
2099  x0 = int((int(x0 / xPixelRatio) - 4) * xPixelRatio + 0.0001);
2100  x1 = int((int(x1 / xPixelRatio) + 4) * xPixelRatio + 0.0001);
2101  }
2102 */
2103  int w = x1 - x0;
2104 
2105 #ifdef DEBUG_SPECTROGRAM_REPAINT
2106  cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << endl;
2107 #endif
2108 
2109  int sr = m_model->getSampleRate();
2110 
2111  // Set minFreq and maxFreq to the frequency extents of the possibly
2112  // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
2113  // to the actual scale frequency extents (presumably not zero padded).
2114 
2115  // If we are zero padding, we want to use the zero-padded
2116  // equivalents of the bins that we would be using if not zero
2117  // padded, to avoid spaces at the top and bottom of the display.
2118 
2119  // Note fftSize is the actual zero-padded fft size, m_fftSize the
2120  // nominal fft size.
2121 
2122  int maxbin = m_fftSize / 2;
2123  if (m_maxFrequency > 0) {
2124  maxbin = int((double(m_maxFrequency) * m_fftSize) / sr + 0.001);
2125  if (maxbin > m_fftSize / 2) maxbin = m_fftSize / 2;
2126  }
2127 
2128  int minbin = 1;
2129  if (m_minFrequency > 0) {
2130  minbin = int((double(m_minFrequency) * m_fftSize) / sr + 0.001);
2131 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
2132  if (minbin < 1) minbin = 1;
2133  if (minbin >= maxbin) minbin = maxbin - 1;
2134  }
2135 
2136  int zpl = getZeroPadLevel(v) + 1;
2137  minbin = minbin * zpl;
2138  maxbin = (maxbin + 1) * zpl - 1;
2139 
2140  float minFreq = (float(minbin) * sr) / fftSize;
2141  float maxFreq = (float(maxbin) * sr) / fftSize;
2142 
2143  float displayMinFreq = minFreq;
2144  float displayMaxFreq = maxFreq;
2145 
2146  if (fftSize != m_fftSize) {
2147  displayMinFreq = getEffectiveMinFrequency();
2148  displayMaxFreq = getEffectiveMaxFrequency();
2149  }
2150 
2151 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
2152 
2153  int increment = getWindowIncrement();
2154 
2155  bool logarithmic = (m_frequencyScale == LogFrequencyScale);
2156 /*
2157  float yforbin[maxbin - minbin + 1];
2158 
2159  for (int q = minbin; q <= maxbin; ++q) {
2160  float f0 = (float(q) * sr) / fftSize;
2161  yforbin[q - minbin] =
2162  v->getYForFrequency(f0, displayMinFreq, displayMaxFreq,
2163  logarithmic);
2164  }
2165 */
2166  MagnitudeRange overallMag = m_viewMags[v];
2167  bool overallMagChanged = false;
2168 
2169 #ifdef DEBUG_SPECTROGRAM_REPAINT
2170  cerr << ((float(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
2171 #endif
2172 
2173  if (w == 0) {
2174  SVDEBUG << "*** NOTE: w == 0" << endl;
2175  }
2176 
2177 #ifdef DEBUG_SPECTROGRAM_REPAINT
2178  int pixels = 0;
2179 #endif
2180 
2181  Profiler outerprof("SpectrogramLayer::paint: all cols");
2182 
2183  // The draw buffer contains a fragment at either our pixel
2184  // resolution (if there is more than one time-bin per pixel) or
2185  // time-bin resolution (if a time-bin spans more than one pixel).
2186  // We need to ensure that it starts and ends at points where a
2187  // time-bin boundary occurs at an exact pixel boundary, and with a
2188  // certain amount of overlap across existing pixels so that we can
2189  // scale and draw from it without smoothing errors at the edges.
2190 
2191  // If (getFrameForX(x) / increment) * increment ==
2192  // getFrameForX(x), then x is a time-bin boundary. We want two
2193  // such boundaries at either side of the draw buffer -- one which
2194  // we draw up to, and one which we subsequently crop at.
2195 
2196  bool bufferBinResolution = false;
2197  if (increment > zoomLevel) bufferBinResolution = true;
2198 
2199  int leftBoundaryFrame = -1, leftCropFrame = -1;
2200  int rightBoundaryFrame = -1, rightCropFrame = -1;
2201 
2202  int bufwid;
2203 
2204  if (bufferBinResolution) {
2205 
2206  for (int x = x0; ; --x) {
2207  int f = v->getFrameForX(x);
2208  if ((f / increment) * increment == f) {
2209  if (leftCropFrame == -1) leftCropFrame = f;
2210  else if (x < x0 - 2) { leftBoundaryFrame = f; break; }
2211  }
2212  }
2213  for (int x = x0 + w; ; ++x) {
2214  int f = v->getFrameForX(x);
2215  if ((f / increment) * increment == f) {
2216  if (rightCropFrame == -1) rightCropFrame = f;
2217  else if (x > x0 + w + 2) { rightBoundaryFrame = f; break; }
2218  }
2219  }
2220 #ifdef DEBUG_SPECTROGRAM_REPAINT
2221  cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
2222  cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
2223 #endif
2224 
2225  bufwid = (rightBoundaryFrame - leftBoundaryFrame) / increment;
2226 
2227  } else {
2228 
2229  bufwid = w;
2230  }
2231 
2232 #ifdef __GNUC__
2233  int binforx[bufwid];
2234  float binfory[h];
2235 #else
2236  int *binforx = (int *)alloca(bufwid * sizeof(int));
2237  float *binfory = (float *)alloca(h * sizeof(float));
2238 #endif
2239 
2240  bool usePeaksCache = false;
2241 
2242  if (bufferBinResolution) {
2243  for (int x = 0; x < bufwid; ++x) {
2244  binforx[x] = (leftBoundaryFrame / increment) + x;
2245 // cerr << "binforx[" << x << "] = " << binforx[x] << endl;
2246  }
2247  m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2248  } else {
2249  for (int x = 0; x < bufwid; ++x) {
2250  float s0 = 0, s1 = 0;
2251  if (getXBinRange(v, x + x0, s0, s1)) {
2252  binforx[x] = int(s0 + 0.0001);
2253  } else {
2254  binforx[x] = -1; //???
2255  }
2256  }
2257  if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() < h) {
2258  m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
2259  }
2260  usePeaksCache = (increment * 8) < zoomLevel;
2261  if (m_colourScale == PhaseColourScale) usePeaksCache = false;
2262  }
2263 
2264 // No longer exists in Qt5: m_drawBuffer.setNumColors(256);
2265  for (int pixel = 0; pixel < 256; ++pixel) {
2266  m_drawBuffer.setColor(pixel, m_palette.getColour(pixel).rgb());
2267  }
2268 
2269  m_drawBuffer.fill(0);
2270 
2271  if (m_binDisplay != PeakFrequencies) {
2272 
2273  for (int y = 0; y < h; ++y) {
2274  float q0 = 0, q1 = 0;
2275  if (!getSmoothedYBinRange(v, h-y-1, q0, q1)) {
2276  binfory[y] = -1;
2277  } else {
2278  binfory[y] = q0;
2279 // cerr << "binfory[" << y << "] = " << binfory[y] << endl;
2280  }
2281  }
2282 
2283  paintDrawBuffer(v, bufwid, h, binforx, binfory, usePeaksCache,
2284  overallMag, overallMagChanged);
2285 
2286  } else {
2287 
2288  paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
2289  minbin, maxbin,
2290  displayMinFreq, displayMaxFreq,
2291  logarithmic,
2292  overallMag, overallMagChanged);
2293  }
2294 
2295 /*
2296  for (int x = 0; x < w / xPixelRatio; ++x) {
2297 
2298  Profiler innerprof("SpectrogramLayer::paint: 1 pixel column");
2299 
2300  runOutOfData = !paintColumnValues(v, fft, x0, x,
2301  minbin, maxbin,
2302  displayMinFreq, displayMaxFreq,
2303  xPixelRatio,
2304  h, yforbin);
2305 
2306  if (runOutOfData) {
2307 #ifdef DEBUG_SPECTROGRAM_REPAINT
2308  cerr << "Run out of data -- dropping out of loop" << endl;
2309 #endif
2310  break;
2311  }
2312  }
2313 */
2314 #ifdef DEBUG_SPECTROGRAM_REPAINT
2315 // cerr << pixels << " pixels drawn" << endl;
2316 #endif
2317 
2318  if (overallMagChanged) {
2319  m_viewMags[v] = overallMag;
2320 #ifdef DEBUG_SPECTROGRAM_REPAINT
2321  cerr << "Overall mag is now [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "] - will be updating" << endl;
2322 #endif
2323  } else {
2324 #ifdef DEBUG_SPECTROGRAM_REPAINT
2325  cerr << "Overall mag unchanged at [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << endl;
2326 #endif
2327  }
2328 
2329  outerprof.end();
2330 
2331  Profiler profiler2("SpectrogramLayer::paint: draw image");
2332 
2333  if (recreateWholeImageCache) {
2334 #ifdef DEBUG_SPECTROGRAM_REPAINT
2335  SVDEBUG << "Recreating image cache: width = " << v->width()
2336  << ", height = " << h << endl;
2337 #endif
2338  cache.image = QImage(v->width(), h, QImage::Format_ARGB32_Premultiplied);
2339  }
2340 
2341  if (w > 0) {
2342 #ifdef DEBUG_SPECTROGRAM_REPAINT
2343  SVDEBUG << "Painting " << w << "x" << h
2344  << " from draw buffer at " << 0 << "," << 0
2345  << " to " << w << "x" << h << " on cache at "
2346  << x0 << "," << 0 << endl;
2347 #endif
2348 
2349  QPainter cachePainter(&cache.image);
2350 
2351  if (bufferBinResolution) {
2352  int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2353  int scaledRight = v->getXForFrame(rightBoundaryFrame);
2354 #ifdef DEBUG_SPECTROGRAM_REPAINT
2355  SVDEBUG << "Rescaling image from " << bufwid
2356  << "x" << h << " to "
2357  << scaledRight-scaledLeft << "x" << h << endl;
2358 #endif
2359  Preferences::SpectrogramXSmoothing xsmoothing =
2360  Preferences::getInstance()->getSpectrogramXSmoothing();
2361 // SVDEBUG << "xsmoothing == " << xsmoothing << endl;
2362  QImage scaled = m_drawBuffer.scaled
2363  (scaledRight - scaledLeft, h,
2364  Qt::IgnoreAspectRatio,
2365  ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2366  Qt::SmoothTransformation : Qt::FastTransformation));
2367  int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2368  int scaledRightCrop = v->getXForFrame(rightCropFrame);
2369 #ifdef DEBUG_SPECTROGRAM_REPAINT
2370  SVDEBUG << "Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2371  << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2372 #endif
2373  cachePainter.drawImage
2374  (QRect(scaledLeftCrop, 0,
2375  scaledRightCrop - scaledLeftCrop, h),
2376  scaled,
2377  QRect(scaledLeftCrop - scaledLeft, 0,
2378  scaledRightCrop - scaledLeftCrop, h));
2379  } else {
2380  cachePainter.drawImage(QRect(x0, 0, w, h),
2381  m_drawBuffer,
2382  QRect(0, 0, w, h));
2383  }
2384 
2385  cachePainter.end();
2386  }
2387 
2388  QRect pr = rect & cache.validArea;
2389 
2390 #ifdef DEBUG_SPECTROGRAM_REPAINT
2391  SVDEBUG << "Painting " << pr.width() << "x" << pr.height()
2392  << " from cache at " << pr.x() << "," << pr.y()
2393  << " to window" << endl;
2394 #endif
2395 
2396  paint.drawImage(pr.x(), pr.y(), cache.image,
2397  pr.x(), pr.y(), pr.width(), pr.height());
2399 // paint.drawImage(v->rect(), cache.image,
2400 // QRect(QPoint(0, 0), cache.image.size()));
2401 
2402  cache.startFrame = startFrame;
2403  cache.zoomLevel = zoomLevel;
2404 
2405  if (!m_synchronous) {
2406 
2407  if (!m_normalizeVisibleArea || !overallMagChanged) {
2408 
2409  if (cache.validArea.x() > 0) {
2410 #ifdef DEBUG_SPECTROGRAM_REPAINT
2411  SVDEBUG << "SpectrogramLayer::paint() updating left (0, "
2412  << cache.validArea.x() << ")" << endl;
2413 #endif
2414  v->update(0, 0, cache.validArea.x(), h);
2415  }
2416 
2417  if (cache.validArea.x() + cache.validArea.width() <
2418  cache.image.width()) {
2419 #ifdef DEBUG_SPECTROGRAM_REPAINT
2420  SVDEBUG << "SpectrogramLayer::paint() updating right ("
2421  << cache.validArea.x() + cache.validArea.width()
2422  << ", "
2423  << cache.image.width() - (cache.validArea.x() +
2424  cache.validArea.width())
2425  << ")" << endl;
2426 #endif
2427  v->update(cache.validArea.x() + cache.validArea.width(),
2428  0,
2429  cache.image.width() - (cache.validArea.x() +
2430  cache.validArea.width()),
2431  h);
2432  }
2433  } else {
2434  // overallMagChanged
2435  cerr << "\noverallMagChanged - updating all\n" << endl;
2436  cache.validArea = QRect();
2437  v->update();
2438  }
2439  }
2440 
2442 
2443 #ifdef DEBUG_SPECTROGRAM_REPAINT
2444  SVDEBUG << "SpectrogramLayer::paint() returning" << endl;
2445 #endif
2446 
2447  if (!m_synchronous) {
2448  m_lastPaintBlockWidth = paintBlockWidth;
2449  (void)gettimeofday(&tv, 0);
2450  m_lastPaintTime = RealTime::fromTimeval(tv) - mainPaintStart;
2451  }
2452 
2454 }
2455 
2456 bool
2458  int w,
2459  int h,
2460  int *binforx,
2461  int minbin,
2462  int maxbin,
2463  float displayMinFreq,
2464  float displayMaxFreq,
2465  bool logarithmic,
2466  MagnitudeRange &overallMag,
2467  bool &overallMagChanged) const
2468 {
2469  Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
2470 
2471 #ifdef DEBUG_SPECTROGRAM_REPAINT
2472  cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2473 #endif
2474  if (minbin < 0) minbin = 0;
2475  if (maxbin < 0) maxbin = minbin+1;
2476 
2477  FFTModel *fft = getFFTModel(v);
2478  if (!fft) return false;
2479 
2480  FFTModel::PeakSet peakfreqs;
2481 
2482  int psx = -1;
2483 
2484 #ifdef __GNUC__
2485  float values[maxbin - minbin + 1];
2486 #else
2487  float *values = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2488 #endif
2489 
2490  for (int x = 0; x < w; ++x) {
2491 
2492  if (binforx[x] < 0) continue;
2493 
2494  int sx0 = binforx[x];
2495  int sx1 = sx0;
2496  if (x+1 < w) sx1 = binforx[x+1];
2497  if (sx0 < 0) sx0 = sx1 - 1;
2498  if (sx0 < 0) continue;
2499  if (sx1 <= sx0) sx1 = sx0 + 1;
2500 
2501  for (int sx = sx0; sx < sx1; ++sx) {
2502 
2503  if (sx < 0 || sx >= int(fft->getWidth())) continue;
2504 
2505  if (!m_synchronous) {
2506  if (!fft->isColumnAvailable(sx)) {
2507 #ifdef DEBUG_SPECTROGRAM_REPAINT
2508  cerr << "Met unavailable column at col " << sx << endl;
2509 #endif
2510  return false;
2511  }
2512  }
2513 
2514  MagnitudeRange mag;
2515 
2516  if (sx != psx) {
2517  peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2518  minbin, maxbin - 1);
2520  fft->getPhasesAt(sx, values, minbin, maxbin - minbin + 1);
2521  } else if (m_normalizeColumns) {
2522  fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2523  } else if (m_normalizeHybrid) {
2524  fft->getNormalizedMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2525  float max = fft->getMaximumMagnitudeAt(sx);
2526  if (max > 0.f) {
2527  for (int i = minbin; i <= maxbin; ++i) {
2528  values[i - minbin] *= log10(max);
2529  }
2530  }
2531  } else {
2532  fft->getMagnitudesAt(sx, values, minbin, maxbin - minbin + 1);
2533  }
2534  psx = sx;
2535  }
2536 
2537  for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
2538  pi != peakfreqs.end(); ++pi) {
2539 
2540  int bin = pi->first;
2541  int freq = pi->second;
2542 
2543  if (bin < minbin) continue;
2544  if (bin > maxbin) break;
2545 
2546  float value = values[bin - minbin];
2547 
2550  value /= (m_fftSize/2.f);
2551  }
2552  mag.sample(value);
2553  value *= m_gain;
2554  }
2555 
2556  float y = v->getYForFrequency
2557  (freq, displayMinFreq, displayMaxFreq, logarithmic);
2558 
2559  int iy = int(y + 0.5);
2560  if (iy < 0 || iy >= h) continue;
2561 
2562  m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value));
2563  }
2564 
2565  if (mag.isSet()) {
2566  if (sx >= int(m_columnMags.size())) {
2567 #ifdef DEBUG_SPECTROGRAM
2568  cerr << "INTERNAL ERROR: " << sx << " >= "
2569  << m_columnMags.size()
2570  << " at SpectrogramLayer.cpp::paintDrawBuffer"
2571  << endl;
2572 #endif
2573  } else {
2574  m_columnMags[sx].sample(mag);
2575  if (overallMag.sample(mag)) overallMagChanged = true;
2576  }
2577  }
2578  }
2579  }
2580 
2581  return true;
2582 }
2583 
2584 bool
2586  int w,
2587  int h,
2588  int *binforx,
2589  float *binfory,
2590  bool usePeaksCache,
2591  MagnitudeRange &overallMag,
2592  bool &overallMagChanged) const
2593 {
2594  Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2595 
2596  int minbin = int(binfory[0] + 0.0001);
2597  int maxbin = binfory[h-1];
2598 
2599 #ifdef DEBUG_SPECTROGRAM_REPAINT
2600  cerr << "minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2601 #endif
2602  if (minbin < 0) minbin = 0;
2603  if (maxbin < 0) maxbin = minbin+1;
2604 
2605  DenseThreeDimensionalModel *sourceModel = 0;
2606  FFTModel *fft = 0;
2607  int divisor = 1;
2608 #ifdef DEBUG_SPECTROGRAM_REPAINT
2609  cerr << "Note: bin display = " << m_binDisplay << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2610 #endif
2611  if (usePeaksCache) {
2612  sourceModel = getPeakCache(v);
2613  divisor = 8;
2614  minbin = 0;
2615  maxbin = sourceModel->getHeight();
2616  } else {
2617  sourceModel = fft = getFFTModel(v);
2618  }
2619 
2620  if (!sourceModel) return false;
2621 
2622  bool interpolate = false;
2623  Preferences::SpectrogramSmoothing smoothing =
2624  Preferences::getInstance()->getSpectrogramSmoothing();
2625  if (smoothing == Preferences::SpectrogramInterpolated ||
2626  smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2627  if (m_binDisplay != PeakBins &&
2629  interpolate = true;
2630  }
2631  }
2632 
2633  int psx = -1;
2634 
2635 #ifdef __GNUC__
2636  float autoarray[maxbin - minbin + 1];
2637  float peaks[h];
2638 #else
2639  float *autoarray = (float *)alloca((maxbin - minbin + 1) * sizeof(float));
2640  float *peaks = (float *)alloca(h * sizeof(float));
2641 #endif
2642 
2643  const float *values = autoarray;
2644  DenseThreeDimensionalModel::Column c;
2645 
2646  for (int x = 0; x < w; ++x) {
2647 
2648  if (binforx[x] < 0) continue;
2649 
2650 // float columnGain = m_gain;
2651  float columnMax = 0.f;
2652 
2653  int sx0 = binforx[x] / divisor;
2654  int sx1 = sx0;
2655  if (x+1 < w) sx1 = binforx[x+1] / divisor;
2656  if (sx0 < 0) sx0 = sx1 - 1;
2657  if (sx0 < 0) continue;
2658  if (sx1 <= sx0) sx1 = sx0 + 1;
2659 
2660  for (int y = 0; y < h; ++y) peaks[y] = 0.f;
2661 
2662  for (int sx = sx0; sx < sx1; ++sx) {
2663 
2664 #ifdef DEBUG_SPECTROGRAM_REPAINT
2665 // cerr << "sx = " << sx << endl;
2666 #endif
2667 
2668  if (sx < 0 || sx >= int(sourceModel->getWidth())) continue;
2669 
2670  if (!m_synchronous) {
2671  if (!sourceModel->isColumnAvailable(sx)) {
2672 #ifdef DEBUG_SPECTROGRAM_REPAINT
2673  cerr << "Met unavailable column at col " << sx << endl;
2674 #endif
2675  return false;
2676  }
2677  }
2678 
2679  MagnitudeRange mag;
2680 
2681  if (sx != psx) {
2682  if (fft) {
2683 #ifdef DEBUG_SPECTROGRAM_REPAINT
2684  SVDEBUG << "Retrieving column " << sx << " from fft directly" << endl;
2685 #endif
2687  fft->getPhasesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2688  } else if (m_normalizeColumns) {
2689  fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2690  } else if (m_normalizeHybrid) {
2691  fft->getNormalizedMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2692  float max = fft->getMaximumMagnitudeAt(sx);
2693  for (int i = minbin; i <= maxbin; ++i) {
2694  if (max > 0.f) {
2695  autoarray[i - minbin] *= log10(max);
2696  }
2697  }
2698  } else {
2699  fft->getMagnitudesAt(sx, autoarray, minbin, maxbin - minbin + 1);
2700  }
2701  } else {
2702 #ifdef DEBUG_SPECTROGRAM_REPAINT
2703  SVDEBUG << "Retrieving column " << sx << " from peaks cache" << endl;
2704 #endif
2705  c = sourceModel->getColumn(sx);
2707  for (int y = 0; y < h; ++y) {
2708  if (c[y] > columnMax) columnMax = c[y];
2709  }
2710  }
2711  values = c.constData() + minbin;
2712  }
2713  psx = sx;
2714  }
2715 
2716  for (int y = 0; y < h; ++y) {
2717 
2718  float sy0 = binfory[y];
2719  float sy1 = sy0 + 1;
2720  if (y+1 < h) sy1 = binfory[y+1];
2721 
2722  float value = 0.f;
2723 
2724  if (interpolate && fabsf(sy1 - sy0) < 1.f) {
2725 
2726  float centre = (sy0 + sy1) / 2;
2727  float dist = (centre - 0.5) - lrintf(centre - 0.5);
2728  int bin = int(centre);
2729  int other = (dist < 0 ? (bin-1) : (bin+1));
2730  if (bin < minbin) bin = minbin;
2731  if (bin > maxbin) bin = maxbin;
2732  if (other < minbin || other > maxbin) other = bin;
2733  float prop = 1.f - fabsf(dist);
2734 
2735  float v0 = values[bin - minbin];
2736  float v1 = values[other - minbin];
2737  if (m_binDisplay == PeakBins) {
2738  if (bin == minbin || bin == maxbin ||
2739  v0 < values[bin-minbin-1] ||
2740  v0 < values[bin-minbin+1]) v0 = 0.f;
2741  if (other == minbin || other == maxbin ||
2742  v1 < values[other-minbin-1] ||
2743  v1 < values[other-minbin+1]) v1 = 0.f;
2744  }
2745  if (v0 == 0.f && v1 == 0.f) continue;
2746  value = prop * v0 + (1.f - prop) * v1;
2747 
2749  if (!m_normalizeColumns) {
2750  value /= (m_fftSize/2.f);
2751  }
2752  mag.sample(value);
2753  value *= m_gain;
2754  }
2755 
2756  peaks[y] = value;
2757 
2758  } else {
2759 
2760  int by0 = int(sy0 + 0.0001);
2761  int by1 = int(sy1 + 0.0001);
2762  if (by1 < by0 + 1) by1 = by0 + 1;
2763 
2764  for (int bin = by0; bin < by1; ++bin) {
2765 
2766  value = values[bin - minbin];
2767  if (m_binDisplay == PeakBins) {
2768  if (bin == minbin || bin == maxbin ||
2769  value < values[bin-minbin-1] ||
2770  value < values[bin-minbin+1]) continue;
2771  }
2772 
2774  if (!m_normalizeColumns) {
2775  value /= (m_fftSize/2.f);
2776  }
2777  mag.sample(value);
2778  value *= m_gain;
2779  }
2780 
2781  if (value > peaks[y]) peaks[y] = value;
2782  }
2783  }
2784  }
2785 
2786  if (mag.isSet()) {
2787  if (sx >= int(m_columnMags.size())) {
2788 #ifdef DEBUG_SPECTROGRAM
2789  cerr << "INTERNAL ERROR: " << sx << " >= "
2790  << m_columnMags.size()
2791  << " at SpectrogramLayer.cpp::paintDrawBuffer"
2792  << endl;
2793 #endif
2794  } else {
2795  m_columnMags[sx].sample(mag);
2796  if (overallMag.sample(mag)) overallMagChanged = true;
2797  }
2798  }
2799  }
2800 
2801  for (int y = 0; y < h; ++y) {
2802 
2803  float peak = peaks[y];
2804 
2807  columnMax > 0.f) {
2808  peak /= columnMax;
2809  if (m_normalizeHybrid) {
2810  peak *= log10(columnMax);
2811  }
2812  }
2813 
2814  unsigned char peakpix = getDisplayValue(v, peak);
2815 
2816  m_drawBuffer.setPixel(x, h-y-1, peakpix);
2817  }
2818  }
2819 
2820  return true;
2821 }
2822 
2823 void
2825 {
2826  Profiler profiler("SpectrogramLayer::illuminateLocalFeatures");
2827 
2828  QPoint localPos;
2829  if (!v->shouldIlluminateLocalFeatures(this, localPos) || !m_model) {
2830  return;
2831  }
2832 
2833 // cerr << "SpectrogramLayer: illuminateLocalFeatures("
2834 // << localPos.x() << "," << localPos.y() << ")" << endl;
2835 
2836  float s0, s1;
2837  float f0, f1;
2838 
2839  if (getXBinRange(v, localPos.x(), s0, s1) &&
2840  getYBinSourceRange(v, localPos.y(), f0, f1)) {
2841 
2842  int s0i = int(s0 + 0.001);
2843  int s1i = int(s1);
2844 
2845  int x0 = v->getXForFrame(s0i * getWindowIncrement());
2846  int x1 = v->getXForFrame((s1i + 1) * getWindowIncrement());
2847 
2848  int y1 = int(getYForFrequency(v, f1));
2849  int y0 = int(getYForFrequency(v, f0));
2850 
2851 // cerr << "SpectrogramLayer: illuminate "
2852 // << x0 << "," << y1 << " -> " << x1 << "," << y0 << endl;
2853 
2854  paint.setPen(v->getForeground());
2855 
2857 
2858  paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
2859  }
2860 }
2861 
2862 float
2863 SpectrogramLayer::getYForFrequency(const View *v, float frequency) const
2864 {
2865  return v->getYForFrequency(frequency,
2869 }
2870 
2871 float
2873 {
2874  return v->getFrequencyForY(y,
2878 }
2879 
2880 int
2882 {
2883  if (m_updateTimer == 0) return 100;
2884  if (m_fftModels.find(v) == m_fftModels.end()) return 100;
2885 
2886  int completion = m_fftModels[v].first->getCompletion();
2887 #ifdef DEBUG_SPECTROGRAM_REPAINT
2888  SVDEBUG << "SpectrogramLayer::getCompletion: completion = " << completion << endl;
2889 #endif
2890  return completion;
2891 }
2892 
2893 QString
2895 {
2896  if (m_fftModels.find(v) == m_fftModels.end()) return "";
2897  return m_fftModels[v].first->getError();
2898 }
2899 
2900 bool
2901 SpectrogramLayer::getValueExtents(float &min, float &max,
2902  bool &logarithmic, QString &unit) const
2903 {
2904  if (!m_model) return false;
2905 
2906  int sr = m_model->getSampleRate();
2907  min = float(sr) / m_fftSize;
2908  max = float(sr) / 2;
2909 
2910  logarithmic = (m_frequencyScale == LogFrequencyScale);
2911  unit = "Hz";
2912  return true;
2913 }
2914 
2915 bool
2916 SpectrogramLayer::getDisplayExtents(float &min, float &max) const
2917 {
2918  min = getEffectiveMinFrequency();
2919  max = getEffectiveMaxFrequency();
2920 
2921 // SVDEBUG << "SpectrogramLayer::getDisplayExtents: " << min << "->" << max << endl;
2922  return true;
2923 }
2924 
2925 bool
2927 {
2928  if (!m_model) return false;
2929 
2930 // SVDEBUG << "SpectrogramLayer::setDisplayExtents: " << min << "->" << max << endl;
2931 
2932  if (min < 0) min = 0;
2933  if (max > m_model->getSampleRate()/2.f) max = m_model->getSampleRate()/2.f;
2934 
2935  int minf = lrintf(min);
2936  int maxf = lrintf(max);
2937 
2938  if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
2939 
2942 
2943  m_minFrequency = minf;
2944  m_maxFrequency = maxf;
2945 
2946  emit layerParametersChanged();
2947 
2948  int vs = getCurrentVerticalZoomStep();
2949  if (vs != m_lastEmittedZoomStep) {
2950  emit verticalZoomChanged();
2951  m_lastEmittedZoomStep = vs;
2952  }
2953 
2954  return true;
2955 }
2956 
2957 bool
2959  float &value, QString &unit) const
2960 {
2961  value = getFrequencyForY(v, y);
2962  unit = "Hz";
2963  return true;
2964 }
2965 
2966 bool
2968  int &resolution,
2969  SnapType snap) const
2970 {
2971  resolution = getWindowIncrement();
2972  int left = (frame / resolution) * resolution;
2973  int right = left + resolution;
2974 
2975  switch (snap) {
2976  case SnapLeft: frame = left; break;
2977  case SnapRight: frame = right; break;
2978  case SnapNearest:
2979  case SnapNeighbouring:
2980  if (frame - left > right - frame) frame = right;
2981  else frame = left;
2982  break;
2983  }
2984 
2985  return true;
2986 }
2987 
2988 void
2990 {
2991  ImageCache &cache = m_imageCaches[v];
2992 
2993  cerr << "cache width: " << cache.image.width() << ", height: "
2994  << cache.image.height() << endl;
2995 
2996  QImage image = cache.image;
2997 
2998  ImageRegionFinder finder;
2999  QRect rect = finder.findRegionExtents(&image, e->pos());
3000  if (rect.isValid()) {
3001  MeasureRect mr;
3002  setMeasureRectFromPixrect(v, mr, rect);
3004  (new AddMeasurementRectCommand(this, mr));
3005  }
3006 }
3007 
3008 bool
3010  QPoint cursorPos,
3011  std::vector<QRect> &extents) const
3012 {
3013  QRect vertical(cursorPos.x() - 12, 0, 12, v->height());
3014  extents.push_back(vertical);
3015 
3016  QRect horizontal(0, cursorPos.y(), cursorPos.x(), 1);
3017  extents.push_back(horizontal);
3018 
3020 
3021  QRect freq(sw, cursorPos.y() - paint.fontMetrics().ascent() - 2,
3022  paint.fontMetrics().width("123456 Hz") + 2,
3023  paint.fontMetrics().height());
3024  extents.push_back(freq);
3025 
3026  QRect pitch(sw, cursorPos.y() + 2,
3027  paint.fontMetrics().width("C#10+50c") + 2,
3028  paint.fontMetrics().height());
3029  extents.push_back(pitch);
3030 
3031  QRect rt(cursorPos.x(),
3032  v->height() - paint.fontMetrics().height() - 2,
3033  paint.fontMetrics().width("1234.567 s"),
3034  paint.fontMetrics().height());
3035  extents.push_back(rt);
3036 
3037  int w(paint.fontMetrics().width("1234567890") + 2);
3038  QRect frame(cursorPos.x() - w - 2,
3039  v->height() - paint.fontMetrics().height() - 2,
3040  w,
3041  paint.fontMetrics().height());
3042  extents.push_back(frame);
3043 
3044  return true;
3045 }
3046 
3047 void
3049  QPoint cursorPos) const
3050 {
3051  paint.save();
3052 
3054 
3055  QFont fn = paint.font();
3056  if (fn.pointSize() > 8) {
3057  fn.setPointSize(fn.pointSize() - 1);
3058  paint.setFont(fn);
3059  }
3060  paint.setPen(m_crosshairColour);
3061 
3062  paint.drawLine(0, cursorPos.y(), cursorPos.x() - 1, cursorPos.y());
3063  paint.drawLine(cursorPos.x(), 0, cursorPos.x(), v->height());
3064 
3065  float fundamental = getFrequencyForY(v, cursorPos.y());
3066 
3068  sw + 2,
3069  cursorPos.y() - 2,
3070  QString("%1 Hz").arg(fundamental),
3072 
3073  if (Pitch::isFrequencyInMidiRange(fundamental)) {
3074  QString pitchLabel = Pitch::getPitchLabelForFrequency(fundamental);
3076  sw + 2,
3077  cursorPos.y() + paint.fontMetrics().ascent() + 2,
3078  pitchLabel,
3080  }
3081 
3082  int frame = v->getFrameForX(cursorPos.x());
3083  RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
3084  QString rtLabel = QString("%1 s").arg(rt.toText(true).c_str());
3085  QString frameLabel = QString("%1").arg(frame);
3087  cursorPos.x() - paint.fontMetrics().width(frameLabel) - 2,
3088  v->height() - 2,
3089  frameLabel,
3092  cursorPos.x() + 2,
3093  v->height() - 2,
3094  rtLabel,
3096 
3097  int harmonic = 2;
3098 
3099  while (harmonic < 100) {
3100 
3101  float hy = lrintf(getYForFrequency(v, fundamental * harmonic));
3102  if (hy < 0 || hy > v->height()) break;
3103 
3104  int len = 7;
3105 
3106  if (harmonic % 2 == 0) {
3107  if (harmonic % 4 == 0) {
3108  len = 12;
3109  } else {
3110  len = 10;
3111  }
3112  }
3113 
3114  paint.drawLine(cursorPos.x() - len,
3115  int(hy),
3116  cursorPos.x(),
3117  int(hy));
3118 
3119  ++harmonic;
3120  }
3121 
3122  paint.restore();
3123 }
3124 
3125 QString
3127 {
3128  int x = pos.x();
3129  int y = pos.y();
3130 
3131  if (!m_model || !m_model->isOK()) return "";
3132 
3133  float magMin = 0, magMax = 0;
3134  float phaseMin = 0, phaseMax = 0;
3135  float freqMin = 0, freqMax = 0;
3136  float adjFreqMin = 0, adjFreqMax = 0;
3137  QString pitchMin, pitchMax;
3138  RealTime rtMin, rtMax;
3139 
3140  bool haveValues = false;
3141 
3142  if (!getXBinSourceRange(v, x, rtMin, rtMax)) {
3143  return "";
3144  }
3145  if (getXYBinSourceRange(v, x, y, magMin, magMax, phaseMin, phaseMax)) {
3146  haveValues = true;
3147  }
3148 
3149  QString adjFreqText = "", adjPitchText = "";
3150 
3151  if (m_binDisplay == PeakFrequencies) {
3152 
3153  if (!getAdjustedYBinSourceRange(v, x, y, freqMin, freqMax,
3154  adjFreqMin, adjFreqMax)) {
3155  return "";
3156  }
3157 
3158  if (adjFreqMin != adjFreqMax) {
3159  adjFreqText = tr("Peak Frequency:\t%1 - %2 Hz\n")
3160  .arg(adjFreqMin).arg(adjFreqMax);
3161  } else {
3162  adjFreqText = tr("Peak Frequency:\t%1 Hz\n")
3163  .arg(adjFreqMin);
3164  }
3165 
3166  QString pmin = Pitch::getPitchLabelForFrequency(adjFreqMin);
3167  QString pmax = Pitch::getPitchLabelForFrequency(adjFreqMax);
3168 
3169  if (pmin != pmax) {
3170  adjPitchText = tr("Peak Pitch:\t%3 - %4\n").arg(pmin).arg(pmax);
3171  } else {
3172  adjPitchText = tr("Peak Pitch:\t%2\n").arg(pmin);
3173  }
3174 
3175  } else {
3176 
3177  if (!getYBinSourceRange(v, y, freqMin, freqMax)) return "";
3178  }
3179 
3180  QString text;
3181 
3182  if (rtMin != rtMax) {
3183  text += tr("Time:\t%1 - %2\n")
3184  .arg(rtMin.toText(true).c_str())
3185  .arg(rtMax.toText(true).c_str());
3186  } else {
3187  text += tr("Time:\t%1\n")
3188  .arg(rtMin.toText(true).c_str());
3189  }
3190 
3191  if (freqMin != freqMax) {
3192  text += tr("%1Bin Frequency:\t%2 - %3 Hz\n%4Bin Pitch:\t%5 - %6\n")
3193  .arg(adjFreqText)
3194  .arg(freqMin)
3195  .arg(freqMax)
3196  .arg(adjPitchText)
3197  .arg(Pitch::getPitchLabelForFrequency(freqMin))
3198  .arg(Pitch::getPitchLabelForFrequency(freqMax));
3199  } else {
3200  text += tr("%1Bin Frequency:\t%2 Hz\n%3Bin Pitch:\t%4\n")
3201  .arg(adjFreqText)
3202  .arg(freqMin)
3203  .arg(adjPitchText)
3204  .arg(Pitch::getPitchLabelForFrequency(freqMin));
3205  }
3206 
3207  if (haveValues) {
3208  float dbMin = AudioLevel::multiplier_to_dB(magMin);
3209  float dbMax = AudioLevel::multiplier_to_dB(magMax);
3210  QString dbMinString;
3211  QString dbMaxString;
3212  if (dbMin == AudioLevel::DB_FLOOR) {
3213  dbMinString = tr("-Inf");
3214  } else {
3215  dbMinString = QString("%1").arg(lrintf(dbMin));
3216  }
3217  if (dbMax == AudioLevel::DB_FLOOR) {
3218  dbMaxString = tr("-Inf");
3219  } else {
3220  dbMaxString = QString("%1").arg(lrintf(dbMax));
3221  }
3222  if (lrintf(dbMin) != lrintf(dbMax)) {
3223  text += tr("dB:\t%1 - %2").arg(dbMinString).arg(dbMaxString);
3224  } else {
3225  text += tr("dB:\t%1").arg(dbMinString);
3226  }
3227  if (phaseMin != phaseMax) {
3228  text += tr("\nPhase:\t%1 - %2").arg(phaseMin).arg(phaseMax);
3229  } else {
3230  text += tr("\nPhase:\t%1").arg(phaseMin);
3231  }
3232  }
3233 
3234  return text;
3235 }
3236 
3237 int
3239 {
3240  int cw;
3241 
3242  cw = paint.fontMetrics().width("-80dB");
3243 
3244  return cw;
3245 }
3246 
3247 int
3248 SpectrogramLayer::getVerticalScaleWidth(View *, bool detailed, QPainter &paint) const
3249 {
3250  if (!m_model || !m_model->isOK()) return 0;
3251 
3252  int cw = 0;
3253  if (detailed) cw = getColourScaleWidth(paint);
3254 
3255  int tw = paint.fontMetrics().width(QString("%1")
3256  .arg(m_maxFrequency > 0 ?
3257  m_maxFrequency - 1 :
3258  m_model->getSampleRate() / 2));
3259 
3260  int fw = paint.fontMetrics().width(tr("43Hz"));
3261  if (tw < fw) tw = fw;
3262 
3263  int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
3264 
3265  return cw + tickw + tw + 13;
3266 }
3267 
3268 void
3269 SpectrogramLayer::paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
3270 {
3271  if (!m_model || !m_model->isOK()) {
3272  return;
3273  }
3274 
3275  Profiler profiler("SpectrogramLayer::paintVerticalScale");
3276 
3278 
3279  int h = rect.height(), w = rect.width();
3280 
3281  int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
3282  int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
3283 
3284  int bins = m_fftSize / 2;
3285  int sr = m_model->getSampleRate();
3286 
3287  if (m_maxFrequency > 0) {
3288  bins = int((double(m_maxFrequency) * m_fftSize) / sr + 0.1);
3289  if (bins > m_fftSize / 2) bins = m_fftSize / 2;
3290  }
3291 
3292  int cw = 0;
3293 
3294  if (detailed) cw = getColourScaleWidth(paint);
3295  int cbw = paint.fontMetrics().width("dB");
3296 
3297  int py = -1;
3298  int textHeight = paint.fontMetrics().height();
3299  int toff = -textHeight + paint.fontMetrics().ascent() + 2;
3300 
3301  if (detailed && (h > textHeight * 3 + 10)) {
3302 
3303  int topLines = 2;
3304  if (m_colourScale == PhaseColourScale) topLines = 1;
3305 
3306  int ch = h - textHeight * (topLines + 1) - 8;
3307 // paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
3308  paint.drawRect(4 + cw - cbw, textHeight * topLines + 4, cbw - 1, ch + 1);
3309 
3310  QString top, bottom;
3311  float min = m_viewMags[v].getMin();
3312  float max = m_viewMags[v].getMax();
3313 
3314  float dBmin = AudioLevel::multiplier_to_dB(min);
3315  float dBmax = AudioLevel::multiplier_to_dB(max);
3316 
3317  if (dBmax < -60.f) dBmax = -60.f;
3318  else top = QString("%1").arg(lrintf(dBmax));
3319 
3320  if (dBmin < dBmax - 60.f) dBmin = dBmax - 60.f;
3321  bottom = QString("%1").arg(lrintf(dBmin));
3322 
3324 
3326  paint.drawText((cw + 6 - paint.fontMetrics().width("dBFS")) / 2,
3327  2 + textHeight + toff, "dBFS");
3328  }
3329 
3330 // paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
3331  paint.drawText(3 + cw - cbw - paint.fontMetrics().width(top),
3332  2 + textHeight * topLines + toff + textHeight/2, top);
3333 
3334  paint.drawText(3 + cw - cbw - paint.fontMetrics().width(bottom),
3335  h + toff - 3 - textHeight/2, bottom);
3336 
3337  paint.save();
3338  paint.setBrush(Qt::NoBrush);
3339 
3340  int lasty = 0;
3341  int lastdb = 0;
3342 
3343  for (int i = 0; i < ch; ++i) {
3344 
3345  float dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
3346  int idb = int(dBval);
3347 
3348  float value = AudioLevel::dB_to_multiplier(dBval);
3349  int colour = getDisplayValue(v, value * m_gain);
3350 
3351  paint.setPen(m_palette.getColour(colour));
3352 
3353  int y = textHeight * topLines + 4 + ch - i;
3354 
3355  paint.drawLine(5 + cw - cbw, y, cw + 2, y);
3356 
3357  if (i == 0) {
3358  lasty = y;
3359  lastdb = idb;
3360  } else if (i < ch - paint.fontMetrics().ascent() &&
3361  idb != lastdb &&
3362  ((abs(y - lasty) > textHeight &&
3363  idb % 10 == 0) ||
3364  (abs(y - lasty) > paint.fontMetrics().ascent() &&
3365  idb % 5 == 0))) {
3366  paint.setPen(v->getBackground());
3367  QString text = QString("%1").arg(idb);
3368  paint.drawText(3 + cw - cbw - paint.fontMetrics().width(text),
3369  y + toff + textHeight/2, text);
3370  paint.setPen(v->getForeground());
3371  paint.drawLine(5 + cw - cbw, y, 8 + cw - cbw, y);
3372  lasty = y;
3373  lastdb = idb;
3374  }
3375  }
3376  paint.restore();
3377  }
3378 
3379  paint.drawLine(cw + 7, 0, cw + 7, h);
3380 
3381  int bin = -1;
3382 
3383  for (int y = 0; y < v->height(); ++y) {
3384 
3385  float q0, q1;
3386  if (!getYBinRange(v, v->height() - y, q0, q1)) continue;
3387 
3388  int vy;
3389 
3390  if (int(q0) > bin) {
3391  vy = y;
3392  bin = int(q0);
3393  } else {
3394  continue;
3395  }
3396 
3397  int freq = (sr * bin) / m_fftSize;
3398 
3399  if (py >= 0 && (vy - py) < textHeight - 1) {
3401  paint.drawLine(w - tickw, h - vy, w, h - vy);
3402  }
3403  continue;
3404  }
3405 
3406  QString text = QString("%1").arg(freq);
3407  if (bin == 1) text = tr("%1Hz").arg(freq); // bin 0 is DC
3408  paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
3409 
3410  if (h - vy - textHeight >= -2) {
3411  int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
3412  paint.drawText(tx, h - vy + toff, text);
3413  }
3414 
3415  py = vy;
3416  }
3417 
3419 
3420  // piano keyboard
3421 
3423  (v, paint, QRect(w - pkw - 1, 0, pkw, h),
3425  }
3426 
3427  m_haveDetailedScale = detailed;
3428 }
3429 
3430 class SpectrogramRangeMapper : public RangeMapper
3431 {
3432 public:
3433  SpectrogramRangeMapper(int sr, int /* fftsize */) :
3434  m_dist(float(sr) / 2),
3435  m_s2(sqrtf(sqrtf(2))) { }
3437 
3438  virtual int getPositionForValue(float value) const {
3439 
3440  float dist = m_dist;
3441 
3442  int n = 0;
3443 
3444  while (dist > (value + 0.00001) && dist > 0.1f) {
3445  dist /= m_s2;
3446  ++n;
3447  }
3448 
3449  return n;
3450  }
3451 
3452  virtual int getPositionForValueUnclamped(float value) const {
3453  // We don't really support this
3454  return getPositionForValue(value);
3455  }
3456 
3457  virtual float getValueForPosition(int position) const {
3458 
3459  // Vertical zoom step 0 shows the entire range from DC ->
3460  // Nyquist frequency. Step 1 shows 2^(1/4) of the range of
3461  // step 0, and so on until the visible range is smaller than
3462  // the frequency step between bins at the current fft size.
3463 
3464  float dist = m_dist;
3465 
3466  int n = 0;
3467  while (n < position) {
3468  dist /= m_s2;
3469  ++n;
3470  }
3471 
3472  return dist;
3473  }
3474 
3475  virtual float getValueForPositionUnclamped(int position) const {
3476  // We don't really support this
3477  return getValueForPosition(position);
3478  }
3479 
3480  virtual QString getUnit() const { return "Hz"; }
3481 
3482 protected:
3483  float m_dist;
3484  float m_s2;
3485 };
3486 
3487 int
3489 {
3490  if (!m_model) return 0;
3491 
3492  int sr = m_model->getSampleRate();
3493 
3494  SpectrogramRangeMapper mapper(sr, m_fftSize);
3495 
3496 // int maxStep = mapper.getPositionForValue((float(sr) / m_fftSize) + 0.001);
3497  int maxStep = mapper.getPositionForValue(0);
3498  int minStep = mapper.getPositionForValue(float(sr) / 2);
3499 
3500  int initialMax = m_initialMaxFrequency;
3501  if (initialMax == 0) initialMax = sr / 2;
3502 
3503  defaultStep = mapper.getPositionForValue(initialMax) - minStep;
3504 
3505 // SVDEBUG << "SpectrogramLayer::getVerticalZoomSteps: " << maxStep - minStep << " (" << maxStep <<"-" << minStep << "), default is " << defaultStep << " (from initial max freq " << initialMax << ")" << endl;
3506 
3507  return maxStep - minStep;
3508 }
3509 
3510 int
3512 {
3513  if (!m_model) return 0;
3514 
3515  float dmin, dmax;
3516  getDisplayExtents(dmin, dmax);
3517 
3518  SpectrogramRangeMapper mapper(m_model->getSampleRate(), m_fftSize);
3519  int n = mapper.getPositionForValue(dmax - dmin);
3520 // SVDEBUG << "SpectrogramLayer::getCurrentVerticalZoomStep: " << n << endl;
3521  return n;
3522 }
3523 
3524 void
3526 {
3527  if (!m_model) return;
3528 
3529  float dmin = m_minFrequency, dmax = m_maxFrequency;
3530 // getDisplayExtents(dmin, dmax);
3531 
3532 // cerr << "current range " << dmin << " -> " << dmax << ", range " << dmax-dmin << ", mid " << (dmax + dmin)/2 << endl;
3533 
3534  int sr = m_model->getSampleRate();
3535  SpectrogramRangeMapper mapper(sr, m_fftSize);
3536  float newdist = mapper.getValueForPosition(step);
3537 
3538  float newmin, newmax;
3539 
3541 
3542  // need to pick newmin and newmax such that
3543  //
3544  // (log(newmin) + log(newmax)) / 2 == logmid
3545  // and
3546  // newmax - newmin = newdist
3547  //
3548  // so log(newmax - newdist) + log(newmax) == 2logmid
3549  // log(newmax(newmax - newdist)) == 2logmid
3550  // newmax.newmax - newmax.newdist == exp(2logmid)
3551  // newmax^2 + (-newdist)newmax + -exp(2logmid) == 0
3552  // quadratic with a = 1, b = -newdist, c = -exp(2logmid), all known
3553  //
3554  // positive root
3555  // newmax = (newdist + sqrt(newdist^2 + 4exp(2logmid))) / 2
3556  //
3557  // but logmid = (log(dmin) + log(dmax)) / 2
3558  // so exp(2logmid) = exp(log(dmin) + log(dmax))
3559  // = exp(log(dmin.dmax))
3560  // = dmin.dmax
3561  // so newmax = (newdist + sqrtf(newdist^2 + 4dmin.dmax)) / 2
3562 
3563  newmax = (newdist + sqrtf(newdist*newdist + 4*dmin*dmax)) / 2;
3564  newmin = newmax - newdist;
3565 
3566 // cerr << "newmin = " << newmin << ", newmax = " << newmax << endl;
3567 
3568  } else {
3569  float dmid = (dmax + dmin) / 2;
3570  newmin = dmid - newdist / 2;
3571  newmax = dmid + newdist / 2;
3572  }
3573 
3574  float mmin, mmax;
3575  mmin = 0;
3576  mmax = float(sr) / 2;
3577 
3578  if (newmin < mmin) {
3579  newmax += (mmin - newmin);
3580  newmin = mmin;
3581  }
3582  if (newmax > mmax) {
3583  newmax = mmax;
3584  }
3585 
3586 // SVDEBUG << "SpectrogramLayer::setVerticalZoomStep: " << step << ": " << newmin << " -> " << newmax << " (range " << newdist << ")" << endl;
3587 
3588  setMinFrequency(lrintf(newmin));
3589  setMaxFrequency(lrintf(newmax));
3590 }
3591 
3592 RangeMapper *
3594 {
3595  if (!m_model) return 0;
3596  return new SpectrogramRangeMapper(m_model->getSampleRate(), m_fftSize);
3597 }
3598 
3599 void
3601 {
3602  int y0 = 0;
3603  if (r.startY > 0.0) y0 = getYForFrequency(v, r.startY);
3604 
3605  int y1 = y0;
3606  if (r.endY > 0.0) y1 = getYForFrequency(v, r.endY);
3607 
3608 // SVDEBUG << "SpectrogramLayer::updateMeasureRectYCoords: start " << r.startY << " -> " << y0 << ", end " << r.endY << " -> " << y1 << endl;
3609 
3610  r.pixrect = QRect(r.pixrect.x(), y0, r.pixrect.width(), y1 - y0);
3611 }
3612 
3613 void
3615 {
3616  if (start) {
3617  r.startY = getFrequencyForY(v, y);
3618  r.endY = r.startY;
3619  } else {
3620  r.endY = getFrequencyForY(v, y);
3621  }
3622 // SVDEBUG << "SpectrogramLayer::setMeasureRectYCoord: start " << r.startY << " <- " << y << ", end " << r.endY << " <- " << y << endl;
3623 
3624 }
3625 
3626 void
3627 SpectrogramLayer::toXml(QTextStream &stream,
3628  QString indent, QString extraAttributes) const
3629 {
3630  QString s;
3631 
3632  s += QString("channel=\"%1\" "
3633  "windowSize=\"%2\" "
3634  "windowHopLevel=\"%3\" "
3635  "gain=\"%4\" "
3636  "threshold=\"%5\" ")
3637  .arg(m_channel)
3638  .arg(m_windowSize)
3639  .arg(m_windowHopLevel)
3640  .arg(m_gain)
3641  .arg(m_threshold);
3642 
3643  s += QString("minFrequency=\"%1\" "
3644  "maxFrequency=\"%2\" "
3645  "colourScale=\"%3\" "
3646  "colourScheme=\"%4\" "
3647  "colourRotation=\"%5\" "
3648  "frequencyScale=\"%6\" "
3649  "binDisplay=\"%7\" ")
3650  .arg(m_minFrequency)
3651  .arg(m_maxFrequency)
3652  .arg(m_colourScale)
3653  .arg(m_colourMap)
3654  .arg(m_colourRotation)
3655  .arg(m_frequencyScale)
3656  .arg(m_binDisplay);
3657 
3658  s += QString("normalizeColumns=\"%1\" "
3659  "normalizeVisibleArea=\"%2\" "
3660  "normalizeHybrid=\"%3\" ")
3661  .arg(m_normalizeColumns ? "true" : "false")
3662  .arg(m_normalizeVisibleArea ? "true" : "false")
3663  .arg(m_normalizeHybrid ? "true" : "false");
3664 
3665  Layer::toXml(stream, indent, extraAttributes + " " + s);
3666 }
3667 
3668 void
3669 SpectrogramLayer::setProperties(const QXmlAttributes &attributes)
3670 {
3671  bool ok = false;
3672 
3673  int channel = attributes.value("channel").toInt(&ok);
3674  if (ok) setChannel(channel);
3675 
3676  int windowSize = attributes.value("windowSize").toUInt(&ok);
3677  if (ok) setWindowSize(windowSize);
3678 
3679  int windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
3680  if (ok) setWindowHopLevel(windowHopLevel);
3681  else {
3682  int windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
3683  // a percentage value
3684  if (ok) {
3685  if (windowOverlap == 0) setWindowHopLevel(0);
3686  else if (windowOverlap == 25) setWindowHopLevel(1);
3687  else if (windowOverlap == 50) setWindowHopLevel(2);
3688  else if (windowOverlap == 75) setWindowHopLevel(3);
3689  else if (windowOverlap == 90) setWindowHopLevel(4);
3690  }
3691  }
3692 
3693  float gain = attributes.value("gain").toFloat(&ok);
3694  if (ok) setGain(gain);
3695 
3696  float threshold = attributes.value("threshold").toFloat(&ok);
3697  if (ok) setThreshold(threshold);
3698 
3699  int minFrequency = attributes.value("minFrequency").toUInt(&ok);
3700  if (ok) {
3701  SVDEBUG << "SpectrogramLayer::setProperties: setting min freq to " << minFrequency << endl;
3702  setMinFrequency(minFrequency);
3703  }
3704 
3705  int maxFrequency = attributes.value("maxFrequency").toUInt(&ok);
3706  if (ok) {
3707  SVDEBUG << "SpectrogramLayer::setProperties: setting max freq to " << maxFrequency << endl;
3708  setMaxFrequency(maxFrequency);
3709  }
3710 
3711  ColourScale colourScale = (ColourScale)
3712  attributes.value("colourScale").toInt(&ok);
3713  if (ok) setColourScale(colourScale);
3714 
3715  int colourMap = attributes.value("colourScheme").toInt(&ok);
3716  if (ok) setColourMap(colourMap);
3717 
3718  int colourRotation = attributes.value("colourRotation").toInt(&ok);
3719  if (ok) setColourRotation(colourRotation);
3720 
3721  FrequencyScale frequencyScale = (FrequencyScale)
3722  attributes.value("frequencyScale").toInt(&ok);
3723  if (ok) setFrequencyScale(frequencyScale);
3724 
3725  BinDisplay binDisplay = (BinDisplay)
3726  attributes.value("binDisplay").toInt(&ok);
3727  if (ok) setBinDisplay(binDisplay);
3728 
3729  bool normalizeColumns =
3730  (attributes.value("normalizeColumns").trimmed() == "true");
3731  setNormalizeColumns(normalizeColumns);
3732 
3733  bool normalizeVisibleArea =
3734  (attributes.value("normalizeVisibleArea").trimmed() == "true");
3735  setNormalizeVisibleArea(normalizeVisibleArea);
3736 
3737  bool normalizeHybrid =
3738  (attributes.value("normalizeHybrid").trimmed() == "true");
3739  setNormalizeHybrid(normalizeHybrid);
3740 }
3741 
void paintPianoVertical(View *v, QPainter &paint, QRect rect, float minf, float maxf)
Definition: PianoScale.cpp:27
void setChannel(int)
Specify the channel to use from the source model.
ColourScale m_colourScale
virtual bool snapToFeatureFrame(View *v, int &frame, int &resolution, SnapType snap) const
Adjust the given frame to snap to the nearest feature, if possible.
int getFrameForX(int x) const
Return the closest frame to the given pixel x-coordinate.
Definition: View.cpp:363
void setColourRotation(int)
Specify the colourmap rotation for the colour scale.
void modelChangedWithin(int startFrame, int endFrame)
bool getNormalizeHybrid() const
virtual void measureDoubleClick(View *, QMouseEvent *)
FFTModel * getFFTModel(const View *v) const
virtual bool hasLightBackground() const
bool paintDrawBuffer(View *v, int w, int h, int *binforx, float *binfory, bool usePeaksCache, MagnitudeRange &overallMag, bool &overallMagChanged) const
SpectrogramLayer(Configuration=FullRangeDb)
virtual const Model * getSliceableModel() const
virtual void setVerticalZoomStep(int)
Set the vertical zoom step.
void illuminateLocalFeatures(View *v, QPainter &painter) const
PeakCacheMap m_peakCaches
void connectSignals(const Model *)
Definition: Layer.cpp:49
SpectrogramLayer represents waveform data (obtained from a DenseTimeValueModel) in spectrogram form.
void setColourScale(ColourScale)
Specify the scale for sample levels.
bool getYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const
virtual void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const
Convert the layer's data (though not those of the model it refers to) into XML for file output.
virtual QColor getForeground() const
Definition: View.cpp:513
virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
float getYForFrequency(float frequency, float minFreq, float maxFreq, bool logarithmic) const
Return the pixel y-coordinate corresponding to a given frequency, if the frequency range is as specif...
Definition: View.cpp:377
virtual void paint(View *v, QPainter &paint, QRect rect) const
Paint the given rectangle of this layer onto the given view using the given painter,...
virtual void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const
Convert the layer's data (though not those of the model it refers to) into XML for file output.
Definition: Layer.cpp:592
WindowType getWindowType() const
int getFFTSize(const View *v) const
bool getNormalizeVisibleArea() const
void modelReplaced()
virtual QString getUnit() const
virtual float getValueForPosition(int position) const
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
ColourScale getColourScale() const
Dense3DModelPeakCache * getPeakCache(const View *v) const
virtual QString getPropertyGroupName(const PropertyName &) const
void addCommand(Command *command)
Add a command to the command history.
virtual int getCurrentVerticalZoomStep() const
Get the current vertical zoom step.
virtual void setProperty(const PropertyName &, int value)
void setThreshold(float threshold)
Set the threshold for sample values to qualify for being shown in the FFT, in voltage units.
bool getXBinRange(View *v, int x, float &windowMin, float &windowMax) const
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const
Definition: View.h:260
static int getColourMapCount()
unsigned char getDisplayValue(View *v, float input) const
WindowType m_windowType
virtual PropertyList getProperties() const
void setWindowHopLevel(int level)
const DenseTimeValueModel * m_model
virtual void setLayerDormant(const View *v, bool dormant)
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
Definition: Layer.cpp:118
int getZoomLevel() const
Return the zoom level, i.e.
Definition: View.cpp:443
float getGain() const
void setProperties(const QXmlAttributes &attributes)
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
void setColourMap(int map)
virtual bool getYScaleValue(const View *, int, float &, QString &) const
Return the value and unit at the given y coordinate in the given view.
bool getAdjustedYBinSourceRange(View *v, int x, int y, float &freqMin, float &freqMax, float &adjFreqMin, float &adjFreqMax) const
virtual bool setDisplayExtents(float min, float max)
Set the displayed minimum and maximum values for the y axis to the given range, if supported.
bool getXYBinSourceRange(View *v, int x, int y, float &min, float &max, float &phaseMin, float &phaseMax) const
virtual void setMeasureRectFromPixrect(View *v, MeasureRect &r, QRect pixrect) const
Definition: Layer.cpp:529
int getZeroPadLevel() const
virtual void setLayerDormant(const View *v, bool dormant)
Indicate that a layer is not currently visible in the given view and is not expected to become visibl...
std::pair< FFTModel *, int > FFTFillPair
bool getSmoothedYBinRange(View *v, int y, float &freqBinMin, float &freqBinMax) const
int getStartFrame() const
Retrieve the first visible sample frame on the widget.
Definition: View.cpp:302
virtual int getPositionForValue(float value) const
virtual int getVerticalZoomSteps(int &defaultStep) const
Get the number of vertical zoom steps available for this layer.
virtual QString getFeatureDescription(View *v, QPoint &) const
float getEffectiveMaxFrequency() const
virtual PropertyType getPropertyType(const PropertyName &) const
void layerParametersChanged()
void setFrequencyScale(FrequencyScale)
Specify the scale for the y axis.
float getThreshold() const
virtual void setMeasureRectYCoord(View *v, MeasureRect &r, bool start, int y) const
void verticalZoomChanged()
int getColourScaleWidth(QPainter &) const
int getMinFrequency() const
BinDisplay m_binDisplay
void sliceableModelReplaced(const Model *modelToBeReplaced, const Model *replacement)
int getEndFrame() const
Retrieve the last visible sample frame on the widget.
Definition: View.cpp:308
FrequencyScale getFrequencyScale() const
SnapType
Definition: Layer.h:157
void preferenceChanged(PropertyContainer::PropertyName name)
virtual int getPositionForValueUnclamped(float value) const
A class for mapping intensity values onto various colour maps.
Definition: ColourMapper.h:27
bool updateViewMagnitudes(View *v) const
QColor map(float value) const
static CommandHistory * getInstance()
ViewFFTMap m_fftModels
virtual QString getError(View *v) const
Return an error string if any errors have occurred while loading or processing data for the given vie...
void setZeroPadLevel(int level)
ImageCache covers the area of the view, at view resolution.
QColor getContrastingColour() const
virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const
QRect findRegionExtents(QImage *image, QPoint origin) const
bool getXBinSourceRange(View *v, int x, RealTime &timeMin, RealTime &timeMax) const
virtual void paintCrosshairs(View *, QPainter &, QPoint) const
QImage m_drawBuffer
When painting, we draw directly onto the draw buffer and then copy this to the part of the image cach...
void setNormalizeHybrid(bool n)
Normalize each column to its maximum value, and then scale by the log of the (absolute) maximum value...
int getWindowSize() const
virtual bool getDisplayExtents(float &min, float &max) const
Return the minimum and maximum values within the displayed range for the y axis, if only a subset of ...
virtual QString getPropertyIconName(const PropertyName &) const
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
BinDisplay getBinDisplay() const
float getEffectiveMinFrequency() const
void modelChanged()
bool getYBinSourceRange(View *v, int y, float &freqMin, float &freqMax) const
std::vector< MagnitudeRange > m_columnMags
virtual QString getPropertyLabel(const PropertyName &) const
void setNormalizeVisibleArea(bool n)
Normalize each value against the maximum in the visible region.
int getWindowHopLevel() const
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
Definition: View.h:50
virtual void copy(View *, Selection, Clipboard &)
Definition: Layer.h:261
bool hasLightBackground() const
double startY
Definition: Layer.h:551
virtual float getValueForPositionUnclamped(int position) const
float getFrequencyForY(const View *v, int y) const
void setGain(float gain)
Set the gain multiplier for sample values in this view.
virtual void drawVisibleText(QPainter &p, int x, int y, QString text, TextStyle style) const
Definition: View.cpp:787
int getWindowIncrement() const
int getMaxFrequency() const
bool paintDrawBufferPeakFrequencies(View *v, int w, int h, int *binforx, int minbin, int maxbin, float displayMinFreq, float displayMaxFreq, bool logarithmic, MagnitudeRange &overallMag, bool &overallMagChanged) const
virtual void updateMeasureRectYCoords(View *v, const MeasureRect &r) const
virtual void setSynchronousPainting(bool synchronous)
Enable or disable synchronous painting.
float getYForFrequency(const View *v, float frequency) const
ViewImageCache m_imageCaches
FrequencyScale m_frequencyScale
float getFrequencyForY(int y, float minFreq, float maxFreq, bool logarithmic) const
Return the closest frequency to the given pixel y-coordinate, if the frequency range is as specified.
Definition: View.cpp:411
virtual bool getCrosshairExtents(View *, QPainter &, QPoint cursorPos, std::vector< QRect > &extents) const
QColor getColour(unsigned char index) const
virtual QColor getBackground() const
Definition: View.cpp:493
void setModel(const DenseTimeValueModel *model)
void setColour(unsigned char index, QColor colour)
void setWindowType(WindowType type)
virtual RangeMapper * getNewVerticalZoomRangeMapper() const
Create and return a range mapper for vertical zoom step values.
void setNormalizeColumns(bool n)
Normalize each column to its maximum value, independent of its neighbours.
bool getNormalizeColumns() const
virtual int getCompletion(View *v) const
Return the proportion of background work complete in drawing this view, as a percentage – in most cas...
void setBinDisplay(BinDisplay)
Specify the processing of frequency bins for the y axis.
virtual bool isLayerDormant(const View *v) const
Return whether the layer is dormant (i.e.
Definition: Layer.cpp:126
virtual RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const
int getColourMap() const
int getXForFrame(int frame) const
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative).
Definition: View.cpp:357
void rotatePalette(int distance)
virtual bool getValueExtents(float &min, float &max, bool &logarithmic, QString &unit) const
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
static QString getColourMapName(int n)