svgui  1.9
WaveformLayer.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 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 "WaveformLayer.h"
17 
18 #include "base/AudioLevel.h"
19 #include "view/View.h"
20 #include "base/Profiler.h"
21 #include "base/RangeMapper.h"
22 #include "ColourDatabase.h"
23 
24 #include <QPainter>
25 #include <QPixmap>
26 #include <QTextStream>
27 
28 #include <iostream>
29 #include <cmath>
30 
31 //#define DEBUG_WAVEFORM_PAINT 1
32 
33 
34 
35 
38  m_model(0),
39  m_gain(1.0f),
40  m_autoNormalize(false),
41  m_showMeans(true),
42  m_greyscale(true),
43  m_channelMode(SeparateChannels),
44  m_channel(-1),
45  m_scale(LinearScale),
46  m_middleLineHeight(0.5),
47  m_aggressive(false),
48  m_cache(0),
49  m_cacheValid(false),
50  m_cacheZoomLevel(0)
51 {
52 
53 }
54 
56 {
57  delete m_cache;
58 }
59 
60 void
61 WaveformLayer::setModel(const RangeSummarisableTimeValueModel *model)
62 {
63  bool channelsChanged = false;
64  if (m_channel == -1) {
65  if (!m_model) {
66  if (model) {
67  channelsChanged = true;
68  }
69  } else {
70  if (model &&
71  m_model->getChannelCount() != model->getChannelCount()) {
72  channelsChanged = true;
73  }
74  }
75  }
76 
77  m_model = model;
78  m_cacheValid = false;
79  if (!m_model || !m_model->isOK()) return;
80 
82 
83  emit modelReplaced();
84 
85  if (channelsChanged) emit layerParametersChanged();
86 }
87 
88 Layer::PropertyList
90 {
91  PropertyList list = SingleColourLayer::getProperties();
92  list.push_back("Scale");
93  list.push_back("Gain");
94  list.push_back("Normalize Visible Area");
95 
96  if (m_model && m_model->getChannelCount() > 1 && m_channel == -1) {
97  list.push_back("Channels");
98  }
99 
100  return list;
101 }
102 
103 QString
104 WaveformLayer::getPropertyLabel(const PropertyName &name) const
105 {
106  if (name == "Scale") return tr("Scale");
107  if (name == "Gain") return tr("Gain");
108  if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
109  if (name == "Channels") return tr("Channels");
111 }
112 
113 QString
114 WaveformLayer::getPropertyIconName(const PropertyName &name) const
115 {
116  if (name == "Normalize Visible Area") return "normalise";
117  return "";
118 }
119 
120 Layer::PropertyType
121 WaveformLayer::getPropertyType(const PropertyName &name) const
122 {
123  if (name == "Gain") return RangeProperty;
124  if (name == "Normalize Visible Area") return ToggleProperty;
125  if (name == "Channels") return ValueProperty;
126  if (name == "Scale") return ValueProperty;
128 }
129 
130 QString
131 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
132 {
133  if (name == "Gain" ||
134  name == "Normalize Visible Area" ||
135  name == "Scale") return tr("Scale");
136  return QString();
137 }
138 
139 int
141  int *min, int *max, int *deflt) const
142 {
143  int val = 0;
144 
145  int garbage0, garbage1, garbage2;
146  if (!min) min = &garbage0;
147  if (!max) max = &garbage1;
148  if (!deflt) deflt = &garbage2;
149 
150  if (name == "Gain") {
151 
152  *min = -50;
153  *max = 50;
154  *deflt = 0;
155 
156  val = lrint(log10(m_gain) * 20.0);
157  if (val < *min) val = *min;
158  if (val > *max) val = *max;
159 
160  } else if (name == "Normalize Visible Area") {
161 
162  val = (m_autoNormalize ? 1 : 0);
163  *deflt = 0;
164 
165  } else if (name == "Channels") {
166 
167  *min = 0;
168  *max = 2;
169  *deflt = 0;
170  if (m_channelMode == MixChannels) val = 1;
171  else if (m_channelMode == MergeChannels) val = 2;
172  else val = 0;
173 
174  } else if (name == "Scale") {
175 
176  *min = 0;
177  *max = 2;
178  *deflt = 0;
179 
180  val = (int)m_scale;
181 
182  } else {
183  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
184  }
185 
186  return val;
187 }
188 
189 QString
190 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
191  int value) const
192 {
193  if (name == "Scale") {
194  switch (value) {
195  default:
196  case 0: return tr("Linear");
197  case 1: return tr("Meter");
198  case 2: return tr("dB");
199  }
200  }
201  if (name == "Channels") {
202  switch (value) {
203  default:
204  case 0: return tr("Separate");
205  case 1: return tr("Mean");
206  case 2: return tr("Butterfly");
207  }
208  }
209  return SingleColourLayer::getPropertyValueLabel(name, value);
210 }
211 
212 RangeMapper *
213 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
214 {
215  if (name == "Gain") {
216  return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
217  }
218  return 0;
219 }
220 
221 void
222 WaveformLayer::setProperty(const PropertyName &name, int value)
223 {
224  if (name == "Gain") {
225  setGain(pow(10, float(value)/20.0));
226  } else if (name == "Normalize Visible Area") {
227  setAutoNormalize(value ? true : false);
228  } else if (name == "Channels") {
229  if (value == 1) setChannelMode(MixChannels);
230  else if (value == 2) setChannelMode(MergeChannels);
232  } else if (name == "Scale") {
233  switch (value) {
234  default:
235  case 0: setScale(LinearScale); break;
236  case 1: setScale(MeterScale); break;
237  case 2: setScale(dBScale); break;
238  }
239  } else {
240  SingleColourLayer::setProperty(name, value);
241  }
242 }
243 
244 void
246 {
247  if (m_gain == gain) return;
248  m_gain = gain;
249  m_cacheValid = false;
250  emit layerParametersChanged();
251  emit verticalZoomChanged();
252 }
253 
254 void
256 {
257  if (m_autoNormalize == autoNormalize) return;
258  m_autoNormalize = autoNormalize;
259  m_cacheValid = false;
260  emit layerParametersChanged();
261 }
262 
263 void
265 {
266  if (m_showMeans == showMeans) return;
267  m_showMeans = showMeans;
268  m_cacheValid = false;
269  emit layerParametersChanged();
270 }
271 
272 void
274 {
275  if (m_greyscale == useGreyscale) return;
276  m_greyscale = useGreyscale;
277  m_cacheValid = false;
278  emit layerParametersChanged();
279 }
280 
281 void
283 {
284  if (m_channelMode == channelMode) return;
285  m_channelMode = channelMode;
286  m_cacheValid = false;
287  emit layerParametersChanged();
288 }
289 
290 void
292 {
293 // SVDEBUG << "WaveformLayer::setChannel(" << channel << ")" << endl;
294 
295  if (m_channel == channel) return;
296  m_channel = channel;
297  m_cacheValid = false;
298  emit layerParametersChanged();
299 }
300 
301 void
303 {
304  if (m_scale == scale) return;
305  m_scale = scale;
306  m_cacheValid = false;
307  emit layerParametersChanged();
308 }
309 
310 void
312 {
313  if (m_middleLineHeight == height) return;
314  m_middleLineHeight = height;
315  m_cacheValid = false;
316  emit layerParametersChanged();
317 }
318 
319 void
321 {
322  if (m_aggressive == aggressive) return;
323  m_aggressive = aggressive;
324  m_cacheValid = false;
325  emit layerParametersChanged();
326 }
327 
328 int
330 {
331  int completion = 100;
332  if (!m_model || !m_model->isOK()) return completion;
333  if (m_model->isReady(&completion)) return 100;
334  return completion;
335 }
336 
337 bool
338 WaveformLayer::getValueExtents(float &min, float &max,
339  bool &, QString &unit) const
340 {
341  if (m_scale == LinearScale) {
342  min = 0.0;
343  max = 1.0;
344  unit = "V";
345  } else if (m_scale == MeterScale) {
346  return false;
347  } else {
348  min = AudioLevel::multiplier_to_dB(0.0);
349  max = AudioLevel::multiplier_to_dB(1.0);
350  unit = "dB";
351  }
352  return true;
353 }
354 
355 int
356 WaveformLayer::dBscale(float sample, int m) const
357 {
358  if (sample < 0.0) return dBscale(-sample, m);
359  float dB = AudioLevel::multiplier_to_dB(sample);
360  if (dB < -50.0) return 0;
361  if (dB > 0.0) return m;
362  return int(((dB + 50.0) * m) / 50.0 + 0.1);
363 }
364 
365 int
367  bool &merging, bool &mixing)
368  const
369 {
370  if (!m_model || !m_model->isOK()) return 0;
371 
372  int channels = m_model->getChannelCount();
373  if (channels == 0) return 0;
374 
375  int rawChannels = channels;
376 
377  if (m_channel == -1) {
378  min = 0;
379  if (m_channelMode == MergeChannels ||
381  max = 0;
382  channels = 1;
383  } else {
384  max = channels - 1;
385  }
386  } else {
387  min = m_channel;
388  max = m_channel;
389  rawChannels = 1;
390  channels = 1;
391  }
392 
393  merging = (m_channelMode == MergeChannels && rawChannels > 1);
394  mixing = (m_channelMode == MixChannels && rawChannels > 1);
395 
396 // SVDEBUG << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << endl;
397 
398  return channels;
399 }
400 
401 bool
403 {
404  return !m_autoNormalize;
405 }
406 
407 static float meterdbs[] = { -40, -30, -20, -15, -10,
408  -5, -3, -2, -1, -0.5, 0 };
409 
410 bool
411 WaveformLayer::getSourceFramesForX(View *v, int x, int modelZoomLevel,
412  int &f0, int &f1) const
413 {
414  int viewFrame = v->getFrameForX(x);
415  if (viewFrame < 0) {
416  f0 = 0;
417  f1 = 0;
418  return false;
419  }
420 
421  f0 = viewFrame;
422 
423  f0 = f0 / modelZoomLevel;
424  f0 = f0 * modelZoomLevel;
425 
426  viewFrame = v->getFrameForX(x + 1);
427 
428  f1 = viewFrame;
429  f1 = f1 / modelZoomLevel;
430  f1 = f1 * modelZoomLevel;
431 
432  return (f0 < m_model->getEndFrame());
433 }
434 
435 float
436 WaveformLayer::getNormalizeGain(View *v, int channel) const
437 {
438  int startFrame = v->getStartFrame();
439  int endFrame = v->getEndFrame();
440 
441  int modelStart = m_model->getStartFrame();
442  int modelEnd = m_model->getEndFrame();
443 
444  int rangeStart, rangeEnd;
445 
446  if (startFrame < modelStart) rangeStart = modelStart;
447  else rangeStart = startFrame;
448 
449  if (endFrame < 0) rangeEnd = 0;
450  else if (endFrame > modelEnd) rangeEnd = modelEnd;
451  else rangeEnd = endFrame;
452 
453  if (rangeEnd < rangeStart) rangeEnd = rangeStart;
454 
455  RangeSummarisableTimeValueModel::Range range =
456  m_model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
457 
458  int minChannel = 0, maxChannel = 0;
459  bool mergingChannels = false, mixingChannels = false;
460 
461  (void)getChannelArrangement(minChannel, maxChannel,
462  mergingChannels, mixingChannels);
463 
464  if (mergingChannels || mixingChannels) {
465  RangeSummarisableTimeValueModel::Range otherRange =
466  m_model->getSummary(1, rangeStart, rangeEnd - rangeStart);
467  range.setMax(std::max(range.max(), otherRange.max()));
468  range.setMin(std::min(range.min(), otherRange.min()));
469  range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
470  }
471 
472  return 1.0 / std::max(fabsf(range.max()), fabsf(range.min()));
473 }
474 
475 void
476 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const
477 {
478  if (!m_model || !m_model->isOK()) {
479  return;
480  }
481 
482  int zoomLevel = v->getZoomLevel();
483 
484 #ifdef DEBUG_WAVEFORM_PAINT
485  Profiler profiler("WaveformLayer::paint", true);
486  cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
487  << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl;
488 #endif
489 
490  int channels = 0, minChannel = 0, maxChannel = 0;
491  bool mergingChannels = false, mixingChannels = false;
492 
493  channels = getChannelArrangement(minChannel, maxChannel,
494  mergingChannels, mixingChannels);
495  if (channels == 0) return;
496 
497  int w = v->width();
498  int h = v->height();
499 
500  bool ready = m_model->isReady();
501  QPainter *paint;
502 
503  if (m_aggressive) {
504 
505 #ifdef DEBUG_WAVEFORM_PAINT
506  cerr << "WaveformLayer::paint: aggressive is true" << endl;
507 #endif
508 
509  if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
510  m_cacheValid = false;
511  }
512 
513  if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
514 #ifdef DEBUG_WAVEFORM_PAINT
515  if (m_cache) {
516  cerr << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << endl;
517  }
518 #endif
519  delete m_cache;
520  m_cache = new QPixmap(w, h);
521  m_cacheValid = false;
522  }
523 
524  if (m_cacheValid) {
525  viewPainter.drawPixmap(rect, *m_cache, rect);
526  return;
527  }
528 
529  paint = new QPainter(m_cache);
530 
531  paint->setPen(Qt::NoPen);
532  paint->setBrush(getBackgroundQColor(v));
533  paint->drawRect(rect);
534 
535  paint->setPen(getForegroundQColor(v));
536  paint->setBrush(Qt::NoBrush);
537 
538  } else {
539  paint = &viewPainter;
540  }
541 
542  paint->setRenderHint(QPainter::Antialiasing, false);
543 
544  if (m_middleLineHeight != 0.5) {
545  paint->save();
546  float space = m_middleLineHeight * 2;
547  if (space > 1.0) space = 2.0 - space;
548  float yt = h * (m_middleLineHeight - space/2);
549  paint->translate(QPointF(0, yt));
550  paint->scale(1.0, space);
551  }
552 
553  int x0 = 0, x1 = w - 1;
554  int y0 = 0, y1 = h - 1;
555 
556  x0 = rect.left();
557  x1 = rect.right();
558  y0 = rect.top();
559  y1 = rect.bottom();
560 
561  if (x0 > 0) --x0;
562  if (x1 < v->width()) ++x1;
563 
564  // Our zoom level may differ from that at which the underlying
565  // model has its blocks.
566 
567  // Each pixel within our visible range must always draw from
568  // exactly the same set of underlying audio frames, no matter what
569  // the range being drawn is. And that set of underlying frames
570  // must remain the same when we scroll one or more pixels left or
571  // right.
572 
573  int modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel);
574 
575  int frame0;
576  int frame1;
577  int spare;
578 
579  getSourceFramesForX(v, x0, modelZoomLevel, frame0, spare);
580  getSourceFramesForX(v, x1, modelZoomLevel, spare, frame1);
581 
582 #ifdef DEBUG_WAVEFORM_PAINT
583  cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << modelZoomLevel << ")" << endl;
584 #endif
585 
586  RangeSummarisableTimeValueModel::RangeBlock *ranges =
587  new RangeSummarisableTimeValueModel::RangeBlock;
588 
589  RangeSummarisableTimeValueModel::RangeBlock *otherChannelRanges = 0;
590  RangeSummarisableTimeValueModel::Range range;
591 
592  QColor baseColour = getBaseQColor();
593  std::vector<QColor> greys = getPartialShades(v);
594 
595  QColor midColour = baseColour;
596  if (midColour == Qt::black) {
597  midColour = Qt::gray;
598  } else if (v->hasLightBackground()) {
599  midColour = midColour.light(150);
600  } else {
601  midColour = midColour.light(50);
602  }
603 
604  while ((int)m_effectiveGains.size() <= maxChannel) {
605  m_effectiveGains.push_back(m_gain);
606  }
607 
608  for (int ch = minChannel; ch <= maxChannel; ++ch) {
609 
610  int prevRangeBottom = -1, prevRangeTop = -1;
611  QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour;
612 
613  m_effectiveGains[ch] = m_gain;
614 
615  if (m_autoNormalize) {
616  m_effectiveGains[ch] = getNormalizeGain(v, ch);
617  }
618 
619  float gain = m_effectiveGains[ch];
620 
621  int m = (h / channels) / 2;
622  int my = m + (((ch - minChannel) * h) / channels);
623 
624 #ifdef DEBUG_WAVEFORM_PAINT
625  cerr << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
626 #endif
627 
628  if (my - m > y1 || my + m < y0) continue;
629 
630  if ((m_scale == dBScale || m_scale == MeterScale) &&
632  m = (h / channels);
633  my = m + (((ch - minChannel) * h) / channels);
634  }
635 
636  paint->setPen(greys[1]);
637  paint->drawLine(x0, my, x1, my);
638 
639  int n = 10;
640  int py = -1;
641 
642  if (v->hasLightBackground() &&
643  v->getViewManager() &&
645 
646  paint->setPen(QColor(240, 240, 240));
647 
648  for (int i = 1; i < n; ++i) {
649 
650  float val = 0.0, nval = 0.0;
651 
652  switch (m_scale) {
653 
654  case LinearScale:
655  val = (i * gain) / n;
656  if (i > 0) nval = -val;
657  break;
658 
659  case MeterScale:
660  val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
661  break;
662 
663  case dBScale:
664  val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
665  break;
666  }
667 
668  if (val < -1.0 || val > 1.0) continue;
669 
670  int y = getYForValue(v, val, ch);
671 
672  if (py >= 0 && abs(y - py) < 10) continue;
673  else py = y;
674 
675  int ny = y;
676  if (nval != 0.0) {
677  ny = getYForValue(v, nval, ch);
678  }
679 
680  paint->drawLine(x0, y, x1, y);
681  if (ny != y) {
682  paint->drawLine(x0, ny, x1, ny);
683  }
684  }
685  }
686 
687  m_model->getSummaries(ch, frame0, frame1 - frame0,
688  *ranges, modelZoomLevel);
689 
690 #ifdef DEBUG_WAVEFORM_PAINT
691  cerr << "channel " << ch << ": " << ranges->size() << " ranges from " << frame0 << " to " << frame1 << " at zoom level " << modelZoomLevel << endl;
692 #endif
693 
694  if (mergingChannels || mixingChannels) {
695  if (m_model->getChannelCount() > 1) {
696  if (!otherChannelRanges) {
697  otherChannelRanges =
698  new RangeSummarisableTimeValueModel::RangeBlock;
699  }
700  m_model->getSummaries
701  (1, frame0, frame1 - frame0, *otherChannelRanges,
702  modelZoomLevel);
703  } else {
704  if (otherChannelRanges != ranges) delete otherChannelRanges;
705  otherChannelRanges = ranges;
706  }
707  }
708 
709  for (int x = x0; x <= x1; ++x) {
710 
711  range = RangeSummarisableTimeValueModel::Range();
712 
713  int f0, f1;
714  if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue;
715  f1 = f1 - 1;
716 
717  if (f0 < frame0) {
718  cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << endl;
719  continue;
720  }
721 
722  int i0 = (f0 - frame0) / modelZoomLevel;
723  int i1 = (f1 - frame0) / modelZoomLevel;
724 
725 #ifdef DEBUG_WAVEFORM_PAINT
726  cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
727 #endif
728 
729  if (i1 > i0 + 1) {
730  cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << endl;
731  }
732 
733  if (ranges && i0 < (int)ranges->size()) {
734 
735  range = (*ranges)[i0];
736 
737  if (i1 > i0 && i1 < (int)ranges->size()) {
738  range.setMax(std::max(range.max(), (*ranges)[i1].max()));
739  range.setMin(std::min(range.min(), (*ranges)[i1].min()));
740  range.setAbsmean((range.absmean() + (*ranges)[i1].absmean()) / 2);
741  }
742 
743  } else {
744 #ifdef DEBUG_WAVEFORM_PAINT
745  cerr << "No (or not enough) ranges for i0 = " << i0 << endl;
746 #endif
747  continue;
748  }
749 
750  int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
751 
752  if (mergingChannels) {
753 
754  if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
755 
756  range.setMax(fabsf(range.max()));
757  range.setMin(-fabsf((*otherChannelRanges)[i0].max()));
758  range.setAbsmean
759  ((range.absmean() +
760  (*otherChannelRanges)[i0].absmean()) / 2);
761 
762  if (i1 > i0 && i1 < (int)otherChannelRanges->size()) {
763  // let's not concern ourselves about the mean
764  range.setMin
765  (std::min
766  (range.min(),
767  -fabsf((*otherChannelRanges)[i1].max())));
768  }
769  }
770 
771  } else if (mixingChannels) {
772 
773  if (otherChannelRanges && i0 < (int)otherChannelRanges->size()) {
774 
775  range.setMax((range.max() + (*otherChannelRanges)[i0].max()) / 2);
776  range.setMin((range.min() + (*otherChannelRanges)[i0].min()) / 2);
777  range.setAbsmean((range.absmean() + (*otherChannelRanges)[i0].absmean()) / 2);
778  }
779  }
780 
781  int greyLevels = 1;
782  if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4;
783 
784  switch (m_scale) {
785 
786  case LinearScale:
787  rangeBottom = int( m * greyLevels * range.min() * gain);
788  rangeTop = int( m * greyLevels * range.max() * gain);
789  meanBottom = int(-m * range.absmean() * gain);
790  meanTop = int( m * range.absmean() * gain);
791  break;
792 
793  case dBScale:
794  if (!mergingChannels) {
795  int db0 = dBscale(range.min() * gain, m);
796  int db1 = dBscale(range.max() * gain, m);
797  rangeTop = std::max(db0, db1);
798  meanTop = std::min(db0, db1);
799  if (mixingChannels) rangeBottom = meanTop;
800  else rangeBottom = dBscale(range.absmean() * gain, m);
801  meanBottom = rangeBottom;
802  } else {
803  rangeBottom = -dBscale(range.min() * gain, m * greyLevels);
804  rangeTop = dBscale(range.max() * gain, m * greyLevels);
805  meanBottom = -dBscale(range.absmean() * gain, m);
806  meanTop = dBscale(range.absmean() * gain, m);
807  }
808  break;
809 
810  case MeterScale:
811  if (!mergingChannels) {
812  int r0 = abs(AudioLevel::multiplier_to_preview(range.min() * gain, m));
813  int r1 = abs(AudioLevel::multiplier_to_preview(range.max() * gain, m));
814  rangeTop = std::max(r0, r1);
815  meanTop = std::min(r0, r1);
816  if (mixingChannels) rangeBottom = meanTop;
817  else rangeBottom = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
818  meanBottom = rangeBottom;
819  } else {
820  rangeBottom = -AudioLevel::multiplier_to_preview(range.min() * gain, m * greyLevels);
821  rangeTop = AudioLevel::multiplier_to_preview(range.max() * gain, m * greyLevels);
822  meanBottom = -AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
823  meanTop = AudioLevel::multiplier_to_preview(range.absmean() * gain, m);
824  }
825  break;
826  }
827 
828  rangeBottom = my * greyLevels - rangeBottom;
829  rangeTop = my * greyLevels - rangeTop;
830  meanBottom = my - meanBottom;
831  meanTop = my - meanTop;
832 
833  int topFill = (rangeTop % greyLevels);
834  if (topFill > 0) topFill = greyLevels - topFill;
835 
836  int bottomFill = (rangeBottom % greyLevels);
837 
838  rangeTop = rangeTop / greyLevels;
839  rangeBottom = rangeBottom / greyLevels;
840 
841  bool clipped = false;
842 
843  if (rangeTop < my - m) { rangeTop = my - m; }
844  if (rangeTop > my + m) { rangeTop = my + m; }
845  if (rangeBottom < my - m) { rangeBottom = my - m; }
846  if (rangeBottom > my + m) { rangeBottom = my + m; }
847 
848  if (range.max() <= -1.0 ||
849  range.max() >= 1.0) clipped = true;
850 
851  if (meanBottom > rangeBottom) meanBottom = rangeBottom;
852  if (meanTop < rangeTop) meanTop = rangeTop;
853 
854  bool drawMean = m_showMeans;
855  if (meanTop == rangeTop) {
856  if (meanTop < meanBottom) ++meanTop;
857  else drawMean = false;
858  }
859  if (meanBottom == rangeBottom && m_scale == LinearScale) {
860  if (meanBottom > meanTop) --meanBottom;
861  else drawMean = false;
862  }
863 
864  if (x != x0 && prevRangeBottom != -1) {
865  if (prevRangeBottom > rangeBottom + 1 &&
866  prevRangeTop > rangeBottom + 1) {
867 // paint->setPen(midColour);
868  paint->setPen(baseColour);
869  paint->drawLine(x-1, prevRangeTop, x, rangeBottom + 1);
870  paint->setPen(prevRangeTopColour);
871  paint->drawPoint(x-1, prevRangeTop);
872  } else if (prevRangeBottom < rangeTop - 1 &&
873  prevRangeTop < rangeTop - 1) {
874 // paint->setPen(midColour);
875  paint->setPen(baseColour);
876  paint->drawLine(x-1, prevRangeBottom, x, rangeTop - 1);
877  paint->setPen(prevRangeBottomColour);
878  paint->drawPoint(x-1, prevRangeBottom);
879  }
880  }
881 
882  if (ready) {
883  if (clipped ) {
886  paint->setPen(Qt::red);
887  } else {
888  paint->setPen(baseColour);
889  }
890  } else {
891  paint->setPen(midColour);
892  }
893 
894 #ifdef DEBUG_WAVEFORM_PAINT
895  cerr << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
896 #endif
897 
898  if (rangeTop == rangeBottom) {
899  paint->drawPoint(x, rangeTop);
900  } else {
901  paint->drawLine(x, rangeBottom, x, rangeTop);
902  }
903 
904  prevRangeTopColour = baseColour;
905  prevRangeBottomColour = baseColour;
906 
907  if (m_greyscale && (m_scale == LinearScale) && ready) {
908  if (!clipped) {
909  if (rangeTop < rangeBottom) {
910  if (topFill > 0 &&
911  (!drawMean || (rangeTop < meanTop - 1))) {
912  paint->setPen(greys[topFill - 1]);
913  paint->drawPoint(x, rangeTop);
914  prevRangeTopColour = greys[topFill - 1];
915  }
916  if (bottomFill > 0 &&
917  (!drawMean || (rangeBottom > meanBottom + 1))) {
918  paint->setPen(greys[bottomFill - 1]);
919  paint->drawPoint(x, rangeBottom);
920  prevRangeBottomColour = greys[bottomFill - 1];
921  }
922  }
923  }
924  }
925 
926  if (drawMean) {
927  paint->setPen(midColour);
928  paint->drawLine(x, meanBottom, x, meanTop);
929  }
930 
931  prevRangeBottom = rangeBottom;
932  prevRangeTop = rangeTop;
933  }
934  }
935 
936  if (m_middleLineHeight != 0.5) {
937  paint->restore();
938  }
939 
940  if (m_aggressive) {
941 
942  if (ready && rect == v->rect()) {
943  m_cacheValid = true;
944  m_cacheZoomLevel = zoomLevel;
945  }
946  paint->end();
947  delete paint;
948  viewPainter.drawPixmap(rect, *m_cache, rect);
949  }
950 
951  if (otherChannelRanges != ranges) delete otherChannelRanges;
952  delete ranges;
953 }
954 
955 QString
957 {
958  int x = pos.x();
959 
960  if (!m_model || !m_model->isOK()) return "";
961 
962  int zoomLevel = v->getZoomLevel();
963 
964  int modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel);
965 
966  int f0, f1;
967  if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return "";
968 
969  QString text;
970 
971  RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate());
972  RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate());
973 
974  if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
975  text += tr("Time:\t%1 - %2")
976  .arg(rt0.toText(true).c_str())
977  .arg(rt1.toText(true).c_str());
978  } else {
979  text += tr("Time:\t%1")
980  .arg(rt0.toText(true).c_str());
981  }
982 
983  int channels = 0, minChannel = 0, maxChannel = 0;
984  bool mergingChannels = false, mixingChannels = false;
985 
986  channels = getChannelArrangement(minChannel, maxChannel,
987  mergingChannels, mixingChannels);
988  if (channels == 0) return "";
989 
990  for (int ch = minChannel; ch <= maxChannel; ++ch) {
991 
992  int blockSize = v->getZoomLevel();
993  RangeSummarisableTimeValueModel::RangeBlock ranges;
994  m_model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
995 
996  if (ranges.empty()) continue;
997 
998  RangeSummarisableTimeValueModel::Range range = ranges[0];
999 
1000  QString label = tr("Level:");
1001  if (minChannel != maxChannel) {
1002  if (ch == 0) label = tr("Left:");
1003  else if (ch == 1) label = tr("Right:");
1004  else label = tr("Channel %1").arg(ch + 1);
1005  }
1006 
1007  bool singleValue = false;
1008  float min, max;
1009 
1010  if (fabs(range.min()) < 0.01) {
1011  min = range.min();
1012  max = range.max();
1013  singleValue = (min == max);
1014  } else {
1015  int imin = lrint(range.min() * 10000);
1016  int imax = lrint(range.max() * 10000);
1017  singleValue = (imin == imax);
1018  min = float(imin)/10000;
1019  max = float(imax)/10000;
1020  }
1021 
1022  int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
1023  fabsf(range.max())))
1024  * 100);
1025 
1026  if (!singleValue) {
1027  text += tr("\n%1\t%2 - %3 (%4 dB peak)")
1028  .arg(label).arg(min).arg(max).arg(float(db)/100);
1029  } else {
1030  text += tr("\n%1\t%2 (%3 dB peak)")
1031  .arg(label).arg(min).arg(float(db)/100);
1032  }
1033  }
1034 
1035  return text;
1036 }
1037 
1038 int
1039 WaveformLayer::getYForValue(const View *v, float value, int channel) const
1040 {
1041  int channels = 0, minChannel = 0, maxChannel = 0;
1042  bool mergingChannels = false, mixingChannels = false;
1043 
1044  channels = getChannelArrangement(minChannel, maxChannel,
1045  mergingChannels, mixingChannels);
1046  if (channels == 0) return 0;
1047  if (maxChannel < minChannel || channel < minChannel) return 0;
1048 
1049  int h = v->height();
1050  int m = (h / channels) / 2;
1051 
1052  if ((m_scale == dBScale || m_scale == MeterScale) &&
1054  m = (h / channels);
1055  }
1056 
1057  int my = m + (((channel - minChannel) * h) / channels);
1058 
1059  int vy = 0;
1060 
1061  switch (m_scale) {
1062 
1063  case LinearScale:
1064  vy = int(m * value);
1065  break;
1066 
1067  case MeterScale:
1068  vy = AudioLevel::multiplier_to_preview(value, m);
1069  break;
1070 
1071  case dBScale:
1072  vy = dBscale(value, m);
1073  break;
1074  }
1075 
1076 // cerr << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
1077 
1078  return my - vy;
1079 }
1080 
1081 float
1082 WaveformLayer::getValueForY(const View *v, int y, int &channel) const
1083 {
1084  int channels = 0, minChannel = 0, maxChannel = 0;
1085  bool mergingChannels = false, mixingChannels = false;
1086 
1087  channels = getChannelArrangement(minChannel, maxChannel,
1088  mergingChannels, mixingChannels);
1089  if (channels == 0) return 0;
1090  if (maxChannel < minChannel) return 0;
1091 
1092  int h = v->height();
1093  int m = (h / channels) / 2;
1094 
1095  if ((m_scale == dBScale || m_scale == MeterScale) &&
1097  m = (h / channels);
1098  }
1099 
1100  channel = (y * channels) / h + minChannel;
1101 
1102  int my = m + (((channel - minChannel) * h) / channels);
1103 
1104  int vy = my - y;
1105  float value = 0;
1106  float thresh = -50.f;
1107 
1108  switch (m_scale) {
1109 
1110  case LinearScale:
1111  value = float(vy) / m;
1112  break;
1113 
1114  case MeterScale:
1115  value = AudioLevel::preview_to_multiplier(vy, m);
1116  break;
1117 
1118  case dBScale:
1119  value = (-thresh * float(vy)) / m + thresh;
1120  value = AudioLevel::dB_to_multiplier(value);
1121  break;
1122  }
1123 
1124  return value / m_gain;
1125 }
1126 
1127 bool
1129  float &value, QString &unit) const
1130 {
1131  int channel;
1132 
1133  value = getValueForY(v, y, channel);
1134 
1135  if (m_scale == dBScale || m_scale == MeterScale) {
1136 
1137  float thresh = -50.f;
1138 
1139  if (value > 0.f) {
1140  value = 10.f * log10f(value);
1141  if (value < thresh) value = thresh;
1142  } else value = thresh;
1143 
1144  unit = "dBV";
1145 
1146  } else {
1147  unit = "V";
1148  }
1149 
1150  return true;
1151 }
1152 
1153 bool
1154 WaveformLayer::getYScaleDifference(const View *v, int y0, int y1,
1155  float &diff, QString &unit) const
1156 {
1157  int c0, c1;
1158  float v0 = getValueForY(v, y0, c0);
1159  float v1 = getValueForY(v, y1, c1);
1160 
1161  if (c0 != c1) {
1162  // different channels, not comparable
1163  diff = 0.f;
1164  unit = "";
1165  return false;
1166  }
1167 
1168  if (m_scale == dBScale || m_scale == MeterScale) {
1169 
1170  float thresh = -50.f;
1171 
1172  if (v1 == v0) diff = thresh;
1173  else {
1174  if (v1 > v0) diff = v0 / v1;
1175  else diff = v1 / v0;
1176 
1177  diff = 10.f * log10f(diff);
1178  if (diff < thresh) diff = thresh;
1179  }
1180 
1181  unit = "dBV";
1182 
1183  } else {
1184  diff = fabsf(v1 - v0);
1185  unit = "V";
1186  }
1187 
1188  return true;
1189 }
1190 
1191 int
1192 WaveformLayer::getVerticalScaleWidth(View *, bool, QPainter &paint) const
1193 {
1194  if (m_scale == LinearScale) {
1195  return paint.fontMetrics().width("0.0") + 13;
1196  } else {
1197  return std::max(paint.fontMetrics().width(tr("0dB")),
1198  paint.fontMetrics().width(tr("-Inf"))) + 13;
1199  }
1200 }
1201 
1202 void
1203 WaveformLayer::paintVerticalScale(View *v, bool, QPainter &paint, QRect rect) const
1204 {
1205  if (!m_model || !m_model->isOK()) {
1206  return;
1207  }
1208 
1209  int channels = 0, minChannel = 0, maxChannel = 0;
1210  bool mergingChannels = false, mixingChannels = false;
1211 
1212  channels = getChannelArrangement(minChannel, maxChannel,
1213  mergingChannels, mixingChannels);
1214  if (channels == 0) return;
1215 
1216  int h = rect.height(), w = rect.width();
1217  int textHeight = paint.fontMetrics().height();
1218  int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
1219 
1220  float gain = m_gain;
1221 
1222  for (int ch = minChannel; ch <= maxChannel; ++ch) {
1223 
1224  int lastLabelledY = -1;
1225 
1226  if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
1227 
1228  int n = 10;
1229 
1230  for (int i = 0; i <= n; ++i) {
1231 
1232  float val = 0.0, nval = 0.0;
1233  QString text = "";
1234 
1235  switch (m_scale) {
1236 
1237  case LinearScale:
1238  val = (i * gain) / n;
1239  text = QString("%1").arg(float(i) / n);
1240  if (i == 0) text = "0.0";
1241  else {
1242  nval = -val;
1243  if (i == n) text = "1.0";
1244  }
1245  break;
1246 
1247  case MeterScale:
1248  val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
1249  text = QString("%1").arg(meterdbs[i]);
1250  if (i == n) text = tr("0dB");
1251  if (i == 0) {
1252  text = tr("-Inf");
1253  val = 0.0;
1254  }
1255  break;
1256 
1257  case dBScale:
1258  val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
1259  text = QString("%1").arg(-(10*n) + i * 10);
1260  if (i == n) text = tr("0dB");
1261  if (i == 0) {
1262  text = tr("-Inf");
1263  val = 0.0;
1264  }
1265  break;
1266  }
1267 
1268  if (val < -1.0 || val > 1.0) continue;
1269 
1270  int y = getYForValue(v, val, ch);
1271 
1272  int ny = y;
1273  if (nval != 0.0) {
1274  ny = getYForValue(v, nval, ch);
1275  }
1276 
1277  bool spaceForLabel = (i == 0 ||
1278  abs(y - lastLabelledY) >= textHeight - 1);
1279 
1280  if (spaceForLabel) {
1281 
1282  int tx = 3;
1283  if (m_scale != LinearScale) {
1284  tx = w - 10 - paint.fontMetrics().width(text);
1285  }
1286 
1287  int ty = y;
1288  if (ty < paint.fontMetrics().ascent()) {
1289  ty = paint.fontMetrics().ascent();
1290  } else if (ty > h - paint.fontMetrics().descent()) {
1291  ty = h - paint.fontMetrics().descent();
1292  } else {
1293  ty += toff;
1294  }
1295  paint.drawText(tx, ty, text);
1296 
1297  lastLabelledY = ty - toff;
1298 
1299  if (ny != y) {
1300  ty = ny;
1301  if (ty < paint.fontMetrics().ascent()) {
1302  ty = paint.fontMetrics().ascent();
1303  } else if (ty > h - paint.fontMetrics().descent()) {
1304  ty = h - paint.fontMetrics().descent();
1305  } else {
1306  ty += toff;
1307  }
1308  paint.drawText(tx, ty, text);
1309  }
1310 
1311  paint.drawLine(w - 7, y, w, y);
1312  if (ny != y) paint.drawLine(w - 7, ny, w, ny);
1313 
1314  } else {
1315 
1316  paint.drawLine(w - 4, y, w, y);
1317  if (ny != y) paint.drawLine(w - 4, ny, w, ny);
1318  }
1319  }
1320  }
1321 }
1322 
1323 void
1324 WaveformLayer::toXml(QTextStream &stream,
1325  QString indent, QString extraAttributes) const
1326 {
1327  QString s;
1328 
1329  QString colourName, colourSpec, darkbg;
1331  (m_colour, colourName, colourSpec, darkbg);
1332 
1333  s += QString("gain=\"%1\" "
1334  "showMeans=\"%2\" "
1335  "greyscale=\"%3\" "
1336  "channelMode=\"%4\" "
1337  "channel=\"%5\" "
1338  "scale=\"%6\" "
1339  "middleLineHeight=\"%7\" "
1340  "aggressive=\"%8\" "
1341  "autoNormalize=\"%9\"")
1342  .arg(m_gain)
1343  .arg(m_showMeans)
1344  .arg(m_greyscale)
1345  .arg(m_channelMode)
1346  .arg(m_channel)
1347  .arg(m_scale)
1348  .arg(m_middleLineHeight)
1349  .arg(m_aggressive)
1350  .arg(m_autoNormalize);
1351 
1352  SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
1353 }
1354 
1355 void
1356 WaveformLayer::setProperties(const QXmlAttributes &attributes)
1357 {
1358  bool ok = false;
1359 
1361 
1362  float gain = attributes.value("gain").toFloat(&ok);
1363  if (ok) setGain(gain);
1364 
1365  bool showMeans = (attributes.value("showMeans") == "1" ||
1366  attributes.value("showMeans") == "true");
1367  setShowMeans(showMeans);
1368 
1369  bool greyscale = (attributes.value("greyscale") == "1" ||
1370  attributes.value("greyscale") == "true");
1371  setUseGreyscale(greyscale);
1372 
1373  ChannelMode channelMode = (ChannelMode)
1374  attributes.value("channelMode").toInt(&ok);
1375  if (ok) setChannelMode(channelMode);
1376 
1377  int channel = attributes.value("channel").toInt(&ok);
1378  if (ok) setChannel(channel);
1379 
1380  Scale scale = (Scale)attributes.value("scale").toInt(&ok);
1381  if (ok) setScale(scale);
1382 
1383  float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
1384  if (ok) setMiddleLineHeight(middleLineHeight);
1385 
1386  bool aggressive = (attributes.value("aggressive") == "1" ||
1387  attributes.value("aggressive") == "true");
1388  setUseGreyscale(aggressive);
1389 
1390  bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
1391  attributes.value("autoNormalize") == "true");
1392  setAutoNormalize(autoNormalize);
1393 }
1394 
1395 int
1397 {
1398  defaultStep = 50;
1399  return 100;
1400 }
1401 
1402 int
1404 {
1405  int val = lrint(log10(m_gain) * 20.0) + 50;
1406  if (val < 0) val = 0;
1407  if (val > 100) val = 100;
1408  return val;
1409 }
1410 
1411 void
1413 {
1414  setGain(pow(10, float(step - 50) / 20.0));
1415 }
1416 
virtual void setVerticalZoomStep(int)
Set the vertical zoom step.
void setModel(const RangeSummarisableTimeValueModel *model)
int getFrameForX(int x) const
Return the closest frame to the given pixel x-coordinate.
Definition: View.cpp:363
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
virtual QString getPropertyGroupName(const PropertyName &) const
virtual RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const
void connectSignals(const Model *)
Definition: Layer.cpp:49
virtual PropertyType getPropertyType(const PropertyName &) const
void setChannel(int)
Specify the channel to use from the source model.
bool getSourceFramesForX(View *v, int x, int modelZoomLevel, int &f0, int &f1) const
int getYForValue(const View *v, float value, int channel) const
virtual int getVerticalScaleWidth(View *v, bool detailed, QPainter &) const
void modelReplaced()
void getStringValues(int index, QString &colourName, QString &colourSpec, QString &darkbg) const
void setAutoNormalize(bool)
Toggle automatic normalization of the currently visible waveform.
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,...
void setMiddleLineHeight(float height)
Specify the height of the middle of the waveform track or tracks within the layer,...
ChannelMode m_channelMode
std::vector< float > m_effectiveGains
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 ViewManager * getViewManager() const
Definition: View.h:233
virtual QString getFeatureDescription(View *v, QPoint &) const
virtual int getVerticalZoomSteps(int &defaultStep) const
Get the number of vertical zoom steps available for this layer.
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 bool hasLightBackground() const
Definition: View.cpp:463
static float meterdbs[]
void setScale(Scale)
Specify the vertical scale for sample levels.
int getZoomLevel() const
Return the zoom level, i.e.
Definition: View.cpp:443
virtual QColor getForegroundQColor(View *v) const
virtual QColor getBaseQColor() const
virtual int getCurrentVerticalZoomStep() const
Get the current vertical zoom step.
bool shouldShowScaleGuides() const
Definition: ViewManager.h:217
std::vector< QColor > getPartialShades(View *v) const
virtual int getCompletion(View *) const
Return the proportion of background work complete in drawing this view, as a percentage – in most cas...
void setGain(float gain)
Set the gain multiplier for sample values in this view.
int getChannelArrangement(int &min, int &max, bool &merging, bool &mixing) const
Return value is number of channels displayed.
virtual QString getPropertyLabel(const PropertyName &) const
int getStartFrame() const
Retrieve the first visible sample frame on the widget.
Definition: View.cpp:302
virtual QColor getBackgroundQColor(View *v) const
void layerParametersChanged()
float getValueForY(const View *v, int y, int &channel) const
const RangeSummarisableTimeValueModel * m_model
void verticalZoomChanged()
virtual void setProperties(const QXmlAttributes &attributes)
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
int getEndFrame() const
Retrieve the last visible sample frame on the widget.
Definition: View.cpp:308
virtual int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const
virtual bool getYScaleDifference(const View *v, int y0, int y1, float &diff, QString &unit) const
Return the difference between the values at the given y coordinates in the given view,...
virtual void paintVerticalScale(View *v, bool detailed, QPainter &paint, QRect rect) const
virtual bool isLayerScrollable(const View *) const
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
virtual bool getValueExtents(float &min, float &max, bool &log, QString &unit) const
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
virtual void setProperty(const PropertyName &, int value)
virtual void setProperties(const QXmlAttributes &attributes)
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
View is the base class of widgets that display one or more overlaid views of data against a horizonta...
Definition: View.h:50
virtual PropertyList getProperties() const
virtual QString getPropertyValueLabel(const PropertyName &, int value) const
virtual QString getPropertyLabel(const PropertyName &) const
float m_middleLineHeight
void setShowMeans(bool)
Set whether to display mean values as a lighter-coloured area beneath the peaks.
QPixmap * m_cache
virtual QString getPropertyIconName(const PropertyName &) const
int dBscale(float sample, int m) const
virtual PropertyList getProperties() const
void setAggressiveCacheing(bool)
Enable or disable aggressive pixmap cacheing.
void setUseGreyscale(bool)
Set whether to use shades of grey (or of the base colour) to provide additional perceived vertical re...
virtual PropertyType getPropertyType(const PropertyName &) const
void setChannelMode(ChannelMode)
Specify whether multi-channel audio data should be displayed with a separate axis per channel (Separa...
static ColourDatabase * getInstance()
virtual void setProperty(const PropertyName &, int value)
float getNormalizeGain(View *v, int channel) const
virtual bool getYScaleValue(const View *v, int y, float &value, QString &unit) const
Return the value and unit at the given y coordinate in the given view.