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 "base/Strings.h"
23 
24 #include "ColourDatabase.h"
25 #include "PaintAssistant.h"
26 
27 #include "data/model/WaveformOversampler.h"
28 
29 #include <QPainter>
30 #include <QPainterPath>
31 #include <QPixmap>
32 #include <QTextStream>
33 
34 #include <iostream>
35 #include <cmath>
36 
37 //#define DEBUG_WAVEFORM_PAINT 1
38 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1
39 
40 using std::vector;
41 
42 double
44 
47  m_gain(1.0f),
48  m_autoNormalize(false),
49  m_showMeans(true),
50  m_channelMode(SeparateChannels),
51  m_channel(-1),
52  m_channelCount(0),
53  m_scale(LinearScale),
54  m_middleLineHeight(0.5),
55  m_aggressive(false),
56  m_cache(nullptr),
57  m_cacheValid(false)
58 {
59 }
60 
62 {
63  delete m_cache;
64 }
65 
66 const ZoomConstraint *
68 {
69  auto model = ModelById::get(m_model);
70  if (model) return model->getZoomConstraint();
71  else return nullptr;
72 }
73 
74 void
75 WaveformLayer::setModel(ModelId modelId)
76 {
77  auto oldModel = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
78  auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId);
79 
80  if (!modelId.isNone() && !newModel) {
81  throw std::logic_error("Not a RangeSummarisableTimeValueModel");
82  }
83 
84  if (m_model == modelId) return;
85  m_model = modelId;
86 
87  // NB newModel may legitimately be null
88 
89  m_cacheValid = false;
90 
91  bool channelsChanged = false;
92  if (m_channel == -1) {
93  if (!oldModel) {
94  if (newModel) {
95  channelsChanged = true;
96  }
97  } else {
98  if (newModel &&
99  oldModel->getChannelCount() != newModel->getChannelCount()) {
100  channelsChanged = true;
101  }
102  }
103  }
104 
105  if (newModel) {
106  m_channelCount = newModel->getChannelCount();
108  }
109 
110  emit modelReplaced();
111 
112  if (channelsChanged) emit layerParametersChanged();
113 }
114 
115 Layer::PropertyList
117 {
118  PropertyList list = SingleColourLayer::getProperties();
119  list.push_back("Scale");
120  list.push_back("Gain");
121  list.push_back("Normalize Visible Area");
122  if (m_channelCount > 1 && m_channel == -1) {
123  list.push_back("Channels");
124  }
125 
126  return list;
127 }
128 
129 QString
130 WaveformLayer::getPropertyLabel(const PropertyName &name) const
131 {
132  if (name == "Scale") return tr("Scale");
133  if (name == "Gain") return tr("Gain");
134  if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
135  if (name == "Channels") return tr("Channels");
137 }
138 
139 QString
140 WaveformLayer::getPropertyIconName(const PropertyName &name) const
141 {
142  if (name == "Normalize Visible Area") return "normalise";
143  return "";
144 }
145 
146 Layer::PropertyType
147 WaveformLayer::getPropertyType(const PropertyName &name) const
148 {
149  if (name == "Gain") return RangeProperty;
150  if (name == "Normalize Visible Area") return ToggleProperty;
151  if (name == "Channels") return ValueProperty;
152  if (name == "Scale") return ValueProperty;
154 }
155 
156 QString
157 WaveformLayer::getPropertyGroupName(const PropertyName &name) const
158 {
159  if (name == "Gain" ||
160  name == "Normalize Visible Area" ||
161  name == "Scale") return tr("Scale");
162  return QString();
163 }
164 
165 int
167  int *min, int *max, int *deflt) const
168 {
169  int val = 0;
170 
171  int garbage0, garbage1, garbage2;
172  if (!min) min = &garbage0;
173  if (!max) max = &garbage1;
174  if (!deflt) deflt = &garbage2;
175 
176  if (name == "Gain") {
177 
178  *min = -50;
179  *max = 50;
180  *deflt = 0;
181 
182  val = int(lrint(log10(m_gain) * 20.0));
183  if (val < *min) val = *min;
184  if (val > *max) val = *max;
185 
186  } else if (name == "Normalize Visible Area") {
187 
188  val = (m_autoNormalize ? 1 : 0);
189  *deflt = 0;
190 
191  } else if (name == "Channels") {
192 
193  *min = 0;
194  *max = 2;
195  *deflt = 0;
196  if (m_channelMode == MixChannels) val = 1;
197  else if (m_channelMode == MergeChannels) val = 2;
198  else val = 0;
199 
200  } else if (name == "Scale") {
201 
202  *min = 0;
203  *max = 2;
204  *deflt = 0;
205 
206  val = (int)m_scale;
207 
208  } else {
209  val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt);
210  }
211 
212  return val;
213 }
214 
215 QString
216 WaveformLayer::getPropertyValueLabel(const PropertyName &name,
217  int value) const
218 {
219  if (name == "Scale") {
220  switch (value) {
221  default:
222  case 0: return tr("Linear");
223  case 1: return tr("Meter");
224  case 2: return tr("dB");
225  }
226  }
227  if (name == "Channels") {
228  switch (value) {
229  default:
230  case 0: return tr("Separate");
231  case 1: return tr("Mean");
232  case 2: return tr("Butterfly");
233  }
234  }
235  return SingleColourLayer::getPropertyValueLabel(name, value);
236 }
237 
238 RangeMapper *
239 WaveformLayer::getNewPropertyRangeMapper(const PropertyName &name) const
240 {
241  if (name == "Gain") {
242  return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
243  }
244  return nullptr;
245 }
246 
247 void
248 WaveformLayer::setProperty(const PropertyName &name, int value)
249 {
250  if (name == "Gain") {
251  setGain(float(pow(10, float(value)/20.0)));
252  } else if (name == "Normalize Visible Area") {
253  setAutoNormalize(value ? true : false);
254  } else if (name == "Channels") {
255  if (value == 1) setChannelMode(MixChannels);
256  else if (value == 2) setChannelMode(MergeChannels);
258  } else if (name == "Scale") {
259  switch (value) {
260  default:
261  case 0: setScale(LinearScale); break;
262  case 1: setScale(MeterScale); break;
263  case 2: setScale(dBScale); break;
264  }
265  } else {
266  SingleColourLayer::setProperty(name, value);
267  }
268 }
269 
270 void
272 {
273  if (m_gain == gain) return;
274  m_gain = gain;
275  m_cacheValid = false;
276  emit layerParametersChanged();
277  emit verticalZoomChanged();
278 }
279 
280 void
282 {
283  if (m_autoNormalize == autoNormalize) return;
284  m_autoNormalize = autoNormalize;
285  m_cacheValid = false;
286  emit layerParametersChanged();
287 }
288 
289 void
291 {
292  if (m_showMeans == showMeans) return;
293  m_showMeans = showMeans;
294  m_cacheValid = false;
295  emit layerParametersChanged();
296 }
297 
298 void
300 {
301  if (m_channelMode == channelMode) return;
302  m_channelMode = channelMode;
303  m_cacheValid = false;
304  emit layerParametersChanged();
305 }
306 
307 void
309 {
310 // SVDEBUG << "WaveformLayer::setChannel(" << channel << ")" << endl;
311 
312  if (m_channel == channel) return;
313  m_channel = channel;
314  m_cacheValid = false;
315  emit layerParametersChanged();
316 }
317 
318 void
320 {
321  if (m_scale == scale) return;
322  m_scale = scale;
323  m_cacheValid = false;
324  emit layerParametersChanged();
325 }
326 
327 void
329 {
330  if (m_middleLineHeight == height) return;
331  m_middleLineHeight = height;
332  m_cacheValid = false;
333  emit layerParametersChanged();
334 }
335 
336 void
338 {
339  if (m_aggressive == aggressive) return;
340  m_aggressive = aggressive;
341  m_cacheValid = false;
342  emit layerParametersChanged();
343 }
344 
345 int
347 {
348  int completion = 100;
349  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
350  if (!model || !model->isOK()) return completion;
351  if (model->isReady(&completion)) return 100;
352  return completion;
353 }
354 
355 bool
356 WaveformLayer::getValueExtents(double &min, double &max,
357  bool &log, QString &unit) const
358 {
359  // This function serves two purposes. It's used to gather min and
360  // max values for a given unit, for cases where there are
361  // auto-align layers out there that aren't providing extents of
362  // their own and that have no specific other layer with display
363  // extents to align to. It's also used to determine whether a
364  // layer might be capable of drawing a scale for itself.
365  //
366  // This makes our situation a bit tricky. There's no point in
367  // returning extents that anyone else might try to align to unless
368  // we have a scale that they can actually calculate with, which is
369  // only the case for certain linear/log arrangements (see
370  // getDisplayExtents - we can test this case by checking whether
371  // getDisplayExtents returns successfully).
372  //
373  // However, there is a point in returning something that indicates
374  // our own capacity to draw a scale. If we don't do that, then we
375  // won't get a scale at all if e.g. we have a time-instant layer
376  // on top (or something else that doesn't care about the y axis).
377  //
378  // Our "solution" to this is to always return true and our
379  // extents, but with an empty unit unless we have the sort of nice
380  // linear/log scale that others can actually align to.
381  //
382  // It might be better to respond to capability requests - can draw
383  // scale, care about scale, can align unit X etc.
384 
385  if (getDisplayExtents(min, max)) {
386  unit = "V";
387  log = (m_scale == dBScale);
388  } else {
389  max = 1.0;
390  min = -1.0;
391  log = false;
392  unit = "";
393  }
394 
395  return true;
396 }
397 
398 bool
399 WaveformLayer::getDisplayExtents(double &min, double &max) const
400 {
401  // If we have a single channel visible and either linear or log
402  // (dB) scale, then we have a continuous scale that runs from -1
403  // to 1 or -dBMin to 0 and we can offer it as an alignment target
404  // for other layers with the same unit. We can also do this in
405  // butterfly mode, but only with linear scale. Otherwise no.
406 
407  if (m_scale == MeterScale) {
408  return false;
409  }
410 
411  if (m_channelCount > 1) {
413  return false;
414  }
416  return false;
417  }
418  }
419 
420  if (m_scale == LinearScale) {
421  max = 1.0;
422  min = -1.0;
423  return true;
424  }
425 
426  if (m_scale == dBScale) {
427  max = 1.0;
428  min = AudioLevel::dB_to_multiplier(m_dBMin);
429  return true;
430  }
431 
432  return false;
433 }
434 
435 double
436 WaveformLayer::dBscale(double sample, int m) const
437 {
438  if (sample < 0.0) return dBscale(-sample, m);
439  double dB = AudioLevel::multiplier_to_dB(sample);
440  if (dB < m_dBMin) return 0;
441  if (dB > 0.0) return m;
442  return ((dB - m_dBMin) * m) / (-m_dBMin);
443 }
444 
445 int
447  bool &merging, bool &mixing)
448  const
449 {
450  int channels = m_channelCount;
451  if (channels == 0) return 0;
452 
453  int rawChannels = channels;
454 
455  if (m_channel == -1) {
456  min = 0;
457  if (m_channelMode == MergeChannels ||
459  max = 0;
460  channels = 1;
461  } else {
462  max = channels - 1;
463  }
464  } else {
465  min = m_channel;
466  max = m_channel;
467  rawChannels = 1;
468  channels = 1;
469  }
470 
471  // "Merging" -> "butterfly mode" - use +ve side of "waveform" for
472  // channel 0 and -ve side for channel 1. If we only have one
473  // channel, we still do this but just duplicate channel 0 onto
474  // channel 1 - this is the only way to get a classic-looking
475  // waveform with meter or db scale from a single-channel file,
476  // although it isn't currently exposed in the SV UI
477  merging = (m_channelMode == MergeChannels);
478 
479  // "Mixing" -> produce a single waveform from the mean of the
480  // channels. Unlike merging, this really does only make sense if
481  // we have >1 channel.
482  mixing = (m_channelMode == MixChannels && rawChannels > 1);
483 
484 // SVDEBUG << "WaveformLayer::getChannelArrangement: min " << min << ", max " << max << ", merging " << merging << ", channels " << channels << endl;
485 
486  return channels;
487 }
488 
489 bool
491 {
492  return !m_autoNormalize;
493 }
494 
495 static float meterdbs[] = { -40, -30, -20, -15, -10,
496  -5, -3, -2, -1, -0.5, 0 };
497 
498 bool
500  int x, int modelZoomLevel,
501  sv_frame_t &f0, sv_frame_t &f1) const
502 {
503  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
504  if (!model) return false;
505 
506  sv_frame_t viewFrame = v->getFrameForX(x);
507  if (viewFrame < 0) {
508  f0 = 0;
509  f1 = 0;
510  return false;
511  }
512 
513  f0 = viewFrame;
514  f0 = f0 / modelZoomLevel;
515  f0 = f0 * modelZoomLevel;
516 
517  if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
518  f1 = f0 + 1;
519  } else {
520  viewFrame = v->getFrameForX(x + 1);
521  f1 = viewFrame;
522  f1 = f1 / modelZoomLevel;
523  f1 = f1 * modelZoomLevel;
524  }
525 
526  return (f0 < model->getEndFrame());
527 }
528 
529 float
531 {
532  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
533  if (!model) return 0.f;
534 
535  sv_frame_t startFrame = v->getStartFrame();
536  sv_frame_t endFrame = v->getEndFrame();
537 
538  sv_frame_t modelStart = model->getStartFrame();
539  sv_frame_t modelEnd = model->getEndFrame();
540 
541  sv_frame_t rangeStart, rangeEnd;
542 
543  if (startFrame < modelStart) rangeStart = modelStart;
544  else rangeStart = startFrame;
545 
546  if (endFrame < 0) rangeEnd = 0;
547  else if (endFrame > modelEnd) rangeEnd = modelEnd;
548  else rangeEnd = endFrame;
549 
550  if (rangeEnd < rangeStart) rangeEnd = rangeStart;
551 
552  RangeSummarisableTimeValueModel::Range range =
553  model->getSummary(channel, rangeStart, rangeEnd - rangeStart);
554 
555  int minChannel = 0, maxChannel = 0;
556  bool mergingChannels = false, mixingChannels = false;
557 
558  (void)getChannelArrangement(minChannel, maxChannel,
559  mergingChannels, mixingChannels);
560 
561  if (mergingChannels || mixingChannels) {
562  if (m_channelCount > 1) {
563  RangeSummarisableTimeValueModel::Range otherRange =
564  model->getSummary(1, rangeStart, rangeEnd - rangeStart);
565  range.setMax(std::max(range.max(), otherRange.max()));
566  range.setMin(std::min(range.min(), otherRange.min()));
567  range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
568  }
569  }
570 
571  return float(1.0 / std::max(fabs(range.max()), fabs(range.min())));
572 }
573 
574 void
575 WaveformLayer::paint(LayerGeometryProvider *v, QPainter &viewPainter, QRect rect) const
576 {
577  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
578  if (!model || !model->isOK()) {
579  return;
580  }
581 
582  ZoomLevel zoomLevel = v->getZoomLevel();
583 
584 #ifdef DEBUG_WAVEFORM_PAINT
585  Profiler profiler("WaveformLayer::paint", true);
586  SVCERR << "WaveformLayer::paint (" << rect.x() << "," << rect.y()
587  << ") [" << rect.width() << "x" << rect.height() << "]: zoom " << zoomLevel << endl;
588 #endif
589 
590  int channels = 0, minChannel = 0, maxChannel = 0;
591  bool mergingChannels = false, mixingChannels = false;
592 
593  channels = getChannelArrangement(minChannel, maxChannel,
594  mergingChannels, mixingChannels);
595  if (channels == 0) return;
596 
597  int w = v->getPaintWidth();
598  int h = v->getPaintHeight();
599 
600  QPainter *paint;
601 
602  if (m_aggressive) {
603 
604 #ifdef DEBUG_WAVEFORM_PAINT
605  SVCERR << "WaveformLayer::paint: aggressive is true" << endl;
606 #endif
607 
608  using namespace std::rel_ops;
609 
610  if (m_cacheValid && (zoomLevel != m_cacheZoomLevel)) {
611  m_cacheValid = false;
612  }
613 
614  if (!m_cache || m_cache->width() != w || m_cache->height() != h) {
615 #ifdef DEBUG_WAVEFORM_PAINT
616  if (m_cache) {
617  SVCERR << "WaveformLayer::paint: cache size " << m_cache->width() << "x" << m_cache->height() << " differs from view size " << w << "x" << h << ": regenerating aggressive cache" << endl;
618  }
619 #endif
620  delete m_cache;
621  m_cache = new QPixmap(w, h);
622  m_cacheValid = false;
623  }
624 
625  if (m_cacheValid) {
626  viewPainter.drawPixmap(rect, *m_cache, rect);
627  return;
628  }
629 
630  paint = new QPainter(m_cache);
631 
632  paint->setPen(Qt::NoPen);
633  paint->setBrush(getBackgroundQColor(v));
634  paint->drawRect(rect);
635 
636  paint->setPen(getForegroundQColor(v));
637  paint->setBrush(Qt::NoBrush);
638 
639  } else {
640  paint = &viewPainter;
641  }
642 
643  paint->setRenderHint(QPainter::Antialiasing, true);
644 
645  if (m_middleLineHeight != 0.5) {
646  paint->save();
647  double space = m_middleLineHeight * 2;
648  if (space > 1.0) space = 2.0 - space;
649  double yt = h * (m_middleLineHeight - space/2);
650  paint->translate(QPointF(0, yt));
651  paint->scale(1.0, space);
652  }
653 
654  int x0 = 0, x1 = w - 1;
655 
656  x0 = rect.left();
657  x1 = rect.right();
658 
659  if (x0 > 0) {
660  rect.adjust(-1, 0, 0, 0);
661  x0 = rect.left();
662  }
663 
664  if (x1 < w) {
665  rect.adjust(0, 0, 1, 0);
666  x1 = rect.right();
667  }
668 
669  // Our zoom level may differ from that at which the underlying
670  // model has its blocks.
671 
672  // Each pixel within our visible range must always draw from
673  // exactly the same set of underlying audio frames, no matter what
674  // the range being drawn is. And that set of underlying frames
675  // must remain the same when we scroll one or more pixels left or
676  // right.
677 
678  int desiredBlockSize = 1;
679  if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
680  desiredBlockSize = zoomLevel.level;
681  }
682  int blockSize = model->getSummaryBlockSize(desiredBlockSize);
683 
684  sv_frame_t frame0;
685  sv_frame_t frame1;
686  sv_frame_t spare;
687 
688  getSourceFramesForX(v, x0, blockSize, frame0, spare);
689  getSourceFramesForX(v, x1, blockSize, spare, frame1);
690 
691 #ifdef DEBUG_WAVEFORM_PAINT
692  SVCERR << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << blockSize << ")" << endl;
693 #endif
694 
695  m_effectiveGains.clear();
696  while ((int)m_effectiveGains.size() <= maxChannel) {
697  m_effectiveGains.push_back(m_gain);
698  }
699  if (m_autoNormalize) {
700  for (int ch = minChannel; ch <= maxChannel; ++ch) {
701  m_effectiveGains[ch] = getNormalizeGain(v, ch);
702  }
703  }
704 
705  RangeVec ranges;
706 
707  if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
708  getSummaryRanges(minChannel, maxChannel,
709  mixingChannels || mergingChannels,
710  frame0, frame1,
711  blockSize, ranges);
712  } else {
713  getOversampledRanges(minChannel, maxChannel,
714  mixingChannels || mergingChannels,
715  frame0, frame1,
716  v->getZoomLevel().level, ranges);
717  }
718 
719  if (!ranges.empty()) {
720  for (int ch = minChannel; ch <= maxChannel; ++ch) {
721  paintChannel(v, paint, rect, ch, ranges, blockSize,
722  frame0, frame1);
723  }
724  }
725 
726  if (m_middleLineHeight != 0.5) {
727  paint->restore();
728  }
729 
730  if (m_aggressive) {
731  if (model->isReady() && rect == v->getPaintRect()) {
732  m_cacheValid = true;
733  m_cacheZoomLevel = zoomLevel;
734  }
735  paint->end();
736  delete paint;
737  viewPainter.drawPixmap(rect, *m_cache, rect);
738  }
739 }
740 
741 void
742 WaveformLayer::getSummaryRanges(int minChannel, int maxChannel,
743  bool mixingOrMerging,
744  sv_frame_t frame0, sv_frame_t frame1,
745  int blockSize, RangeVec &ranges)
746  const
747 {
748  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
749  if (!model) return;
750 
751  for (int ch = minChannel; ch <= maxChannel; ++ch) {
752  ranges.push_back({});
753  model->getSummaries(ch, frame0, frame1 - frame0,
754  ranges[ch - minChannel], blockSize);
755 #ifdef DEBUG_WAVEFORM_PAINT
756  SVCERR << "channel " << ch << ": " << ranges[ch - minChannel].size() << " ranges from " << frame0 << " to " << frame1 << " at zoom level " << blockSize << endl;
757 #endif
758  }
759 
760  if (mixingOrMerging) {
761  if (minChannel != 0 || maxChannel != 0) {
762  throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
763  } else if (m_channelCount > 1) {
764  ranges.push_back({});
765  model->getSummaries
766  (1, frame0, frame1 - frame0, ranges[1], blockSize);
767  } else {
768  ranges.push_back(ranges[0]);
769  }
770  }
771 }
772 
773 void
774 WaveformLayer::getOversampledRanges(int minChannel, int maxChannel,
775  bool mixingOrMerging,
776  sv_frame_t frame0, sv_frame_t frame1,
777  int oversampleBy, RangeVec &ranges)
778  const
779 {
780  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
781  if (!model) return;
782 
783  if (mixingOrMerging) {
784  if (minChannel != 0 || maxChannel != 0) {
785  throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
786  }
787  if (m_channelCount > 1) {
788  // call back on self for the individual channels with
789  // mixingOrMerging false
791  (0, 1, false, frame0, frame1, oversampleBy, ranges);
792  return;
793  } else {
794  // call back on self for a single channel, then duplicate
796  (0, 0, false, frame0, frame1, oversampleBy, ranges);
797  ranges.push_back(ranges[0]);
798  return;
799  }
800  }
801 
802  // These frame values, tail length, etc variables are at the model
803  // sample rate, not the oversampled rate
804 
805  sv_frame_t tail = 16;
806  sv_frame_t startFrame = model->getStartFrame();
807  sv_frame_t endFrame = model->getEndFrame();
808 
809  sv_frame_t rf0 = frame0 - tail;
810  if (rf0 < startFrame) {
811  rf0 = 0;
812  }
813 
814  sv_frame_t rf1 = frame1 + tail;
815  if (rf1 >= endFrame) {
816  rf1 = endFrame - 1;
817  }
818  if (rf1 <= rf0) {
819  SVCERR << "WARNING: getOversampledRanges: rf1 (" << rf1 << ") <= rf0 ("
820  << rf0 << ")" << endl;
821  return;
822  }
823 
824  for (int ch = minChannel; ch <= maxChannel; ++ch) {
825  floatvec_t oversampled = WaveformOversampler::getOversampledData
826  (*model, ch, frame0, frame1 - frame0, oversampleBy);
827  RangeSummarisableTimeValueModel::RangeBlock rr;
828  for (float v: oversampled) {
829  RangeSummarisableTimeValueModel::Range r;
830  r.sample(v);
831  rr.push_back(r);
832  }
833  ranges.push_back(rr);
834 
835 #ifdef DEBUG_WAVEFORM_PAINT
836  SVCERR << "getOversampledRanges: " << frame0 << " -> " << frame1
837  << " (" << frame1 - frame0 << "-frame range) at ratio "
838  << oversampleBy << " with tail " << tail
839  << " -> got " << oversampled.size()
840  << " oversampled values for channel " << ch
841  << ", from which returning " << rr.size() << " ranges" << endl;
842 #endif
843  }
844 
845  return;
846 }
847 
848 void
850  QPainter *paint,
851  QRect rect, int ch,
852  const RangeVec &ranges,
853  int blockSize,
854  sv_frame_t frame0,
855  sv_frame_t frame1)
856  const
857 {
858  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
859  if (!model) return;
860 
861  int x0 = rect.left();
862  int y0 = rect.top();
863 
864  int x1 = rect.right();
865  int y1 = rect.bottom();
866 
867  int h = v->getPaintHeight();
868 
869  int channels = 0, minChannel = 0, maxChannel = 0;
870  bool mergingChannels = false, mixingChannels = false;
871 
872  channels = getChannelArrangement(minChannel, maxChannel,
873  mergingChannels, mixingChannels);
874  if (channels == 0) return;
875 
876  QColor baseColour = getBaseQColor();
877  QColor midColour = baseColour;
878 
879  if (midColour == Qt::black) {
880  midColour = Qt::gray;
881  } else if (v->hasLightBackground()) {
882  midColour = midColour.lighter(150);
883  } else {
884  midColour = midColour.lighter(50);
885  }
886 
887  double gain = m_effectiveGains[ch];
888 
889  int m = (h / channels) / 2;
890  int my = m + (((ch - minChannel) * h) / channels);
891 
892 #ifdef DEBUG_WAVEFORM_PAINT
893  SVCERR << "ch = " << ch << ", channels = " << channels << ", m = " << m << ", my = " << my << ", h = " << h << endl;
894 #endif
895 
896  if (my - m > y1 || my + m < y0) return;
897 
898  if ((m_scale == dBScale || m_scale == MeterScale) &&
900  m = (h / channels);
901  my = m + (((ch - minChannel) * h) / channels);
902  }
903 
904  // Horizontal axis along middle
905  paint->setPen(QPen(midColour, 0));
906  paint->drawLine(QPointF(x0, my + 0.5), QPointF(x1, my + 0.5));
907 
908  paintChannelScaleGuides(v, paint, rect, ch);
909 
910  int rangeix = ch - minChannel;
911 
912 #ifdef DEBUG_WAVEFORM_PAINT
913  SVCERR << "paint channel " << ch << ": frame0 = " << frame0 << ", frame1 = " << frame1 << ", blockSize = " << blockSize << ", have " << ranges.size() << " range blocks of which ours is index " << rangeix << " with " << ranges[rangeix].size() << " ranges in it" << endl;
914 #else
915  (void)frame1; // not actually used
916 #endif
917 
918  QPainterPath waveformPath;
919  QPainterPath meanPath;
920  QPainterPath clipPath;
921  vector<QPointF> individualSamplePoints;
922 
923  bool firstPoint = true;
924  double prevRangeBottom = 0, prevRangeTop = 0;
925 
926  for (int x = x0; x <= x1; ++x) {
927 
928  sv_frame_t f0, f1;
929  sv_frame_t i0, i1;
930 
931  bool showIndividualSample = false;
932 
933  if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
934  if (!getSourceFramesForX(v, x, blockSize, f0, f1)) {
935  continue;
936  }
937  f1 = f1 - 1;
938  i0 = (f0 - frame0) / blockSize;
939  i1 = (f1 - frame0) / blockSize;
940  } else {
941  int oversampleBy = v->getZoomLevel().level;
942  f0 = f1 = v->getFrameForX(x);
943  int xf0 = v->getXForFrame(f0);
944  showIndividualSample = (x == xf0);
945  i0 = i1 = (f0 - frame0) * oversampleBy + (x - xf0);
946  }
947 
948  if (f0 < frame0) {
949  // Not an error, this simply occurs when painting the
950  // start of a signal in PixelsPerFrame zone
951  continue;
952  }
953 
954 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
955  SVCERR << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << endl;
956 #endif
957 
958  if (i1 > i0 + 1) {
959  SVCERR << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << v->getZoomLevel() << ", model zoom = " << blockSize << ")" << endl;
960  }
961 
962  const auto &r = ranges[rangeix];
963  RangeSummarisableTimeValueModel::Range range;
964 
965  if (in_range_for(r, i0)) {
966 
967  range = r[i0];
968 
969  if (i1 > i0 && in_range_for(r, i1)) {
970  range.setMax(std::max(range.max(), r[i1].max()));
971  range.setMin(std::min(range.min(), r[i1].min()));
972  range.setAbsmean((range.absmean() + r[i1].absmean()) / 2);
973  }
974 
975  } else {
976 #ifdef DEBUG_WAVEFORM_PAINT
977  SVCERR << "No (or not enough) ranges for index i0 = " << i0 << " (there are " << r.size() << " range(s))" << endl;
978 #endif
979  continue;
980  }
981 
982  double rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0;
983 
984  if (mergingChannels && ranges.size() > 1) {
985 
986  const auto &other = ranges[1];
987 
988  if (in_range_for(other, i0)) {
989 
990  range.setMax(fabsf(range.max()));
991  range.setMin(-fabsf(other[i0].max()));
992  range.setAbsmean
993  ((range.absmean() + other[i0].absmean()) / 2);
994 
995  if (i1 > i0 && in_range_for(other, i1)) {
996  // let's not concern ourselves about the mean
997  range.setMin(std::min(range.min(),
998  -fabsf(other[i1].max())));
999  }
1000  }
1001 
1002  } else if (mixingChannels && ranges.size() > 1) {
1003 
1004  const auto &other = ranges[1];
1005 
1006  if (in_range_for(other, i0)) {
1007 
1008  range.setMax((range.max() + other[i0].max()) / 2);
1009  range.setMin((range.min() + other[i0].min()) / 2);
1010  range.setAbsmean((range.absmean() + other[i0].absmean()) / 2);
1011  }
1012  }
1013 
1014  switch (m_scale) {
1015 
1016  case LinearScale:
1017  rangeBottom = range.min() * gain * m;
1018  rangeTop = range.max() * gain * m;
1019  meanBottom = range.absmean() * gain * (-m);
1020  meanTop = range.absmean() * gain * m;
1021  break;
1022 
1023  case dBScale:
1024  if (!mergingChannels) {
1025  double db0 = dBscale(range.min() * gain, m);
1026  double db1 = dBscale(range.max() * gain, m);
1027  rangeTop = std::max(db0, db1);
1028  meanTop = std::min(db0, db1);
1029  if (mixingChannels) rangeBottom = meanTop;
1030  else rangeBottom = dBscale(range.absmean() * gain, m);
1031  meanBottom = rangeBottom;
1032  } else {
1033  rangeBottom = -dBscale(range.min() * gain, m);
1034  rangeTop = dBscale(range.max() * gain, m);
1035  meanBottom = -dBscale(range.absmean() * gain, m);
1036  meanTop = dBscale(range.absmean() * gain, m);
1037  }
1038  break;
1039 
1040  case MeterScale:
1041  if (!mergingChannels) {
1042  double r0 = std::abs(AudioLevel::multiplier_to_preview
1043  (range.min() * gain, m));
1044  double r1 = std::abs(AudioLevel::multiplier_to_preview
1045  (range.max() * gain, m));
1046  rangeTop = std::max(r0, r1);
1047  meanTop = std::min(r0, r1);
1048  if (mixingChannels) rangeBottom = meanTop;
1049  else rangeBottom = AudioLevel::multiplier_to_preview
1050  (range.absmean() * gain, m);
1051  meanBottom = rangeBottom;
1052  } else {
1053  rangeBottom = -AudioLevel::multiplier_to_preview
1054  (range.min() * gain, m);
1055  rangeTop = AudioLevel::multiplier_to_preview
1056  (range.max() * gain, m);
1057  meanBottom = -AudioLevel::multiplier_to_preview
1058  (range.absmean() * gain, m);
1059  meanTop = AudioLevel::multiplier_to_preview
1060  (range.absmean() * gain, m);
1061  }
1062  break;
1063  }
1064 
1065  rangeBottom = my - rangeBottom;
1066  rangeTop = my - rangeTop;
1067  meanBottom = my - meanBottom;
1068  meanTop = my - meanTop;
1069 
1070  bool clipped = false;
1071 
1072  if (rangeTop < my - m) { rangeTop = my - m; }
1073  if (rangeTop > my + m) { rangeTop = my + m; }
1074  if (rangeBottom < my - m) { rangeBottom = my - m; }
1075  if (rangeBottom > my + m) { rangeBottom = my + m; }
1076 
1077  if (range.max() <= -1.0 || range.max() >= 1.0) {
1078  clipped = true;
1079  }
1080 
1081  bool drawMean = m_showMeans;
1082 
1083  meanTop = meanTop - 0.5;
1084  meanBottom = meanBottom + 0.5;
1085 
1086  if (meanTop <= rangeTop + 1.0) {
1087  meanTop = rangeTop + 1.0;
1088  }
1089  if (meanBottom >= rangeBottom - 1.0 && m_scale == LinearScale) {
1090  meanBottom = rangeBottom - 1.0;
1091  }
1092  if (meanTop > meanBottom - 1.0) {
1093  drawMean = false;
1094  }
1095 
1096 #ifdef DEBUG_WAVEFORM_PAINT_BY_PIXEL
1097  SVCERR << "range " << rangeBottom << " -> " << rangeTop << ", means " << meanBottom << " -> " << meanTop << ", raw range " << range.min() << " -> " << range.max() << endl;
1098 #endif
1099 
1100  double rangeMiddle = (rangeTop + rangeBottom) / 2.0;
1101  bool trivialRange = (fabs(rangeTop - rangeBottom) < 1.0);
1102  double px = x + 0.5;
1103 
1104  if (showIndividualSample) {
1105  individualSamplePoints.push_back(QPointF(px, rangeTop));
1106  if (!trivialRange) {
1107  // common e.g. in "butterfly" merging mode
1108  individualSamplePoints.push_back(QPointF(px, rangeBottom));
1109  }
1110  }
1111 
1112  bool contiguous = true;
1113  if (rangeTop > prevRangeBottom + 0.5 ||
1114  rangeBottom < prevRangeTop - 0.5) {
1115  contiguous = false;
1116  }
1117 
1118  if (firstPoint || (contiguous && !trivialRange)) {
1119  waveformPath.moveTo(QPointF(px, rangeTop));
1120  waveformPath.lineTo(QPointF(px, rangeBottom));
1121  waveformPath.moveTo(QPointF(px, rangeMiddle));
1122  } else {
1123  waveformPath.lineTo(QPointF(px, rangeMiddle));
1124  if (!trivialRange) {
1125  waveformPath.lineTo(QPointF(px, rangeTop));
1126  waveformPath.lineTo(QPointF(px, rangeBottom));
1127  waveformPath.lineTo(QPointF(px, rangeMiddle));
1128  }
1129  }
1130 
1131  firstPoint = false;
1132  prevRangeTop = rangeTop;
1133  prevRangeBottom = rangeBottom;
1134 
1135  if (drawMean) {
1136  meanPath.moveTo(QPointF(px, meanBottom));
1137  meanPath.lineTo(QPointF(px, meanTop));
1138  }
1139 
1140  if (clipped) {
1141  if (trivialRange) {
1142  clipPath.moveTo(QPointF(px, rangeMiddle));
1143  clipPath.lineTo(QPointF(px+1, rangeMiddle));
1144  } else {
1145  clipPath.moveTo(QPointF(px, rangeBottom));
1146  clipPath.lineTo(QPointF(px, rangeTop));
1147  }
1148  }
1149  }
1150 
1151  double penWidth = 1.0;
1152  if (v->getZoomLevel().zone == ZoomLevel::FramesPerPixel) {
1153  penWidth = 0.0;
1154  }
1155 
1156  if (model->isReady()) {
1157  paint->setPen(QPen(baseColour, penWidth));
1158  } else {
1159  paint->setPen(QPen(midColour, penWidth));
1160  }
1161  paint->drawPath(waveformPath);
1162 
1163  if (!clipPath.isEmpty()) {
1164  paint->save();
1165  paint->setPen(QPen(ColourDatabase::getInstance()->
1166  getContrastingColour(m_colour), penWidth));
1167  paint->drawPath(clipPath);
1168  paint->restore();
1169  }
1170 
1171  if (!meanPath.isEmpty()) {
1172  paint->save();
1173  paint->setPen(QPen(midColour, penWidth));
1174  paint->drawPath(meanPath);
1175  paint->restore();
1176  }
1177 
1178  if (!individualSamplePoints.empty()) {
1179  double sz = v->scaleSize(2.0);
1180  if (v->getZoomLevel().zone == ZoomLevel::PixelsPerFrame) {
1181  if (v->getZoomLevel().level < 10) {
1182  sz = v->scaleSize(1.2);
1183  }
1184  }
1185  paint->save();
1186  paint->setPen(QPen(baseColour, penWidth));
1187  for (QPointF p: individualSamplePoints) {
1188  paint->drawRect(QRectF(p.x() - sz/2, p.y() - sz/2, sz, sz));
1189  }
1190  paint->restore();
1191  }
1192 }
1193 
1194 void
1196  QPainter *paint,
1197  QRect rect,
1198  int ch) const
1199 {
1200  int x0 = rect.left();
1201  int x1 = rect.right();
1202 
1203  int n = 10;
1204  int py = -1;
1205 
1206  double gain = m_effectiveGains[ch];
1207 
1208  if (v->hasLightBackground() &&
1209  v->getViewManager() &&
1211 
1212  paint->setPen(QColor(240, 240, 240));
1213 
1214  for (int i = 1; i < n; ++i) {
1215 
1216  double val = 0.0, nval = 0.0;
1217 
1218  switch (m_scale) {
1219 
1220  case LinearScale:
1221  val = (i * gain) / n;
1222  if (i > 0) nval = -val;
1223  break;
1224 
1225  case MeterScale:
1226  val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
1227  break;
1228 
1229  case dBScale:
1230  val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
1231  break;
1232  }
1233 
1234  if (val < -1.0 || val > 1.0) continue;
1235 
1236  int y = getYForValue(v, val, ch);
1237 
1238  if (py >= 0 && abs(y - py) < 10) continue;
1239  else py = y;
1240 
1241  int ny = y;
1242  if (nval != 0.0) {
1243  ny = getYForValue(v, nval, ch);
1244  }
1245 
1246  paint->drawLine(x0, y, x1, y);
1247  if (ny != y) {
1248  paint->drawLine(x0, ny, x1, ny);
1249  }
1250  }
1251  }
1252 }
1253 
1254 QString
1256 {
1257  int x = pos.x();
1258 
1259  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
1260  if (!model || !model->isOK()) return "";
1261 
1262  ZoomLevel zoomLevel = v->getZoomLevel();
1263 
1264  int desiredBlockSize = 1;
1265  if (zoomLevel.zone == ZoomLevel::FramesPerPixel) {
1266  desiredBlockSize = zoomLevel.level;
1267  }
1268 
1269  int blockSize = model->getSummaryBlockSize(desiredBlockSize);
1270 
1271  sv_frame_t f0, f1;
1272  if (!getSourceFramesForX(v, x, blockSize, f0, f1)) return "";
1273 
1274  QString text;
1275 
1276  RealTime rt0 = RealTime::frame2RealTime(f0, model->getSampleRate());
1277  RealTime rt1 = RealTime::frame2RealTime(f1, model->getSampleRate());
1278 
1279  if (f1 != f0 + 1 && (rt0.sec != rt1.sec || rt0.msec() != rt1.msec())) {
1280  text += tr("Time:\t%1 - %2")
1281  .arg(rt0.toText(true).c_str())
1282  .arg(rt1.toText(true).c_str());
1283  } else {
1284  text += tr("Time:\t%1")
1285  .arg(rt0.toText(true).c_str());
1286  }
1287 
1288  int channels = 0, minChannel = 0, maxChannel = 0;
1289  bool mergingChannels = false, mixingChannels = false;
1290 
1291  channels = getChannelArrangement(minChannel, maxChannel,
1292  mergingChannels, mixingChannels);
1293  if (channels == 0) return "";
1294 
1295  for (int ch = minChannel; ch <= maxChannel; ++ch) {
1296 
1297  RangeSummarisableTimeValueModel::RangeBlock ranges;
1298  model->getSummaries(ch, f0, f1 - f0, ranges, blockSize);
1299 
1300  if (ranges.empty()) continue;
1301 
1302  RangeSummarisableTimeValueModel::Range range = ranges[0];
1303 
1304  QString label = tr("Level:");
1305  if (minChannel != maxChannel) {
1306  if (ch == 0) label = tr("Left:");
1307  else if (ch == 1) label = tr("Right:");
1308  else label = tr("Channel %1").arg(ch + 1);
1309  }
1310 
1311  bool singleValue = false;
1312  double min, max;
1313 
1314  if (fabs(range.min()) < 0.01) {
1315  min = range.min();
1316  max = range.max();
1317  singleValue = (min == max);
1318  } else {
1319  int imin = int(lrint(range.min() * 10000));
1320  int imax = int(lrint(range.max() * 10000));
1321  singleValue = (imin == imax);
1322  min = double(imin)/10000;
1323  max = double(imax)/10000;
1324  }
1325 
1326  int db = int(AudioLevel::multiplier_to_dB(std::max(fabsf(range.min()),
1327  fabsf(range.max())))
1328  * 100);
1329 
1330  if (!singleValue) {
1331  text += tr("\n%1\t%2 - %3 (%4 dB peak)")
1332  .arg(label).arg(min).arg(max).arg(double(db)/100);
1333  } else {
1334  text += tr("\n%1\t%2 (%3 dB peak)")
1335  .arg(label).arg(min).arg(double(db)/100);
1336  }
1337  }
1338 
1339  return text;
1340 }
1341 
1342 int
1343 WaveformLayer::getYForValue(const LayerGeometryProvider *v, double value, int channel) const
1344 {
1345  int channels = 0, minChannel = 0, maxChannel = 0;
1346  bool mergingChannels = false, mixingChannels = false;
1347 
1348  channels = getChannelArrangement(minChannel, maxChannel,
1349  mergingChannels, mixingChannels);
1350  if (channels == 0) return 0;
1351  if (maxChannel < minChannel || channel < minChannel) return 0;
1352 
1353  int h = v->getPaintHeight();
1354  int m = (h / channels) / 2;
1355 
1356  if ((m_scale == dBScale || m_scale == MeterScale) &&
1358  m = (h / channels);
1359  }
1360 
1361  int my = m + (((channel - minChannel) * h) / channels);
1362 
1363  int vy = 0;
1364 
1365  switch (m_scale) {
1366 
1367  case LinearScale:
1368  vy = int(m * value);
1369  break;
1370 
1371  case MeterScale:
1372  vy = AudioLevel::multiplier_to_preview(value, m);
1373  break;
1374 
1375  case dBScale:
1376  vy = int(dBscale(value, m));
1377  break;
1378  }
1379 
1380 // SVCERR << "mergingChannels= " << mergingChannels << ", channel = " << channel << ", value = " << value << ", vy = " << vy << endl;
1381 
1382  return my - vy;
1383 }
1384 
1385 double
1386 WaveformLayer::getValueForY(const LayerGeometryProvider *v, int y, int &channel) const
1387 {
1388  int channels = 0, minChannel = 0, maxChannel = 0;
1389  bool mergingChannels = false, mixingChannels = false;
1390 
1391  channels = getChannelArrangement(minChannel, maxChannel,
1392  mergingChannels, mixingChannels);
1393  if (channels == 0) return 0;
1394  if (maxChannel < minChannel) return 0;
1395 
1396  int h = v->getPaintHeight();
1397  int m = (h / channels) / 2;
1398 
1399  if ((m_scale == dBScale || m_scale == MeterScale) &&
1401  m = (h / channels);
1402  }
1403 
1404  channel = (y * channels) / h + minChannel;
1405 
1406  int my = m + (((channel - minChannel) * h) / channels);
1407 
1408  int vy = my - y;
1409  double value = 0;
1410  double thresh = m_dBMin;
1411 
1412  switch (m_scale) {
1413 
1414  case LinearScale:
1415  value = double(vy) / m;
1416  break;
1417 
1418  case MeterScale:
1419  value = AudioLevel::preview_to_multiplier(vy, m);
1420  break;
1421 
1422  case dBScale:
1423  value = (-thresh * double(vy)) / m + thresh;
1424  value = AudioLevel::dB_to_multiplier(value);
1425  break;
1426  }
1427 
1428  return value / m_gain;
1429 }
1430 
1431 bool
1433  double &value, QString &unit) const
1434 {
1435  int channel;
1436 
1437  value = getValueForY(v, y, channel);
1438 
1439  if (m_scale == dBScale || m_scale == MeterScale) {
1440 
1441  double thresh = m_dBMin;
1442 
1443  if (value > 0.0) {
1444  value = 10.0 * log10(value);
1445  if (value < thresh) value = thresh;
1446  } else value = thresh;
1447 
1448  unit = "dBV";
1449 
1450  } else {
1451  unit = "V";
1452  }
1453 
1454  return true;
1455 }
1456 
1457 bool
1459  double &diff, QString &unit) const
1460 {
1461  int c0, c1;
1462  double v0 = getValueForY(v, y0, c0);
1463  double v1 = getValueForY(v, y1, c1);
1464 
1465  if (c0 != c1) {
1466  // different channels, not comparable
1467  diff = 0.0;
1468  unit = "";
1469  return false;
1470  }
1471 
1472  if (m_scale == dBScale || m_scale == MeterScale) {
1473 
1474  double thresh = m_dBMin;
1475 
1476  if (v1 == v0) diff = thresh;
1477  else {
1478  if (v1 > v0) diff = v0 / v1;
1479  else diff = v1 / v0;
1480 
1481  diff = 10.0 * log10(diff);
1482  if (diff < thresh) diff = thresh;
1483  }
1484 
1485  unit = "dBV";
1486 
1487  } else {
1488  diff = fabs(v1 - v0);
1489  unit = "V";
1490  }
1491 
1492  return true;
1493 }
1494 
1495 int
1497 {
1498  // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
1499  // replacement (horizontalAdvance) was only added in Qt 5.11
1500  // which is too new for us
1501 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1502 
1503  if (m_scale == LinearScale) {
1504  return paint.fontMetrics().width("0.0") + 13;
1505  } else {
1506  return std::max(paint.fontMetrics().width(tr("0dB")),
1507  paint.fontMetrics().width(Strings::minus_infinity)) + 13;
1508  }
1509 }
1510 
1511 void
1512 WaveformLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
1513 {
1514  auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
1515  if (!model || !model->isOK()) {
1516  return;
1517  }
1518 
1519  int channels = 0, minChannel = 0, maxChannel = 0;
1520  bool mergingChannels = false, mixingChannels = false;
1521 
1522  channels = getChannelArrangement(minChannel, maxChannel,
1523  mergingChannels, mixingChannels);
1524  if (channels == 0) return;
1525 
1526  int h = rect.height(), w = rect.width();
1527  int textHeight = paint.fontMetrics().height();
1528  int toff = -textHeight/2 + paint.fontMetrics().ascent() + 1;
1529 
1530  double gain = m_gain;
1531 
1532  for (int ch = minChannel; ch <= maxChannel; ++ch) {
1533 
1534  int lastLabelledY = -1;
1535 
1536  if (ch < (int)m_effectiveGains.size()) gain = m_effectiveGains[ch];
1537 
1538  int n = 10;
1539 
1540  for (int i = 0; i <= n; ++i) {
1541 
1542  double val = 0.0, nval = 0.0;
1543  QString text = "";
1544 
1545  switch (m_scale) {
1546 
1547  case LinearScale:
1548  val = (i * gain) / n;
1549  text = QString("%1").arg(double(i) / n);
1550  if (i == 0) text = "0.0";
1551  else {
1552  nval = -val;
1553  if (i == n) text = "1.0";
1554  }
1555  break;
1556 
1557  case MeterScale:
1558  val = AudioLevel::dB_to_multiplier(meterdbs[i]) * gain;
1559  text = QString("%1").arg(meterdbs[i]);
1560  if (i == n) text = tr("0dB");
1561  if (i == 0) {
1562  text = Strings::minus_infinity;
1563  val = 0.0;
1564  }
1565  break;
1566 
1567  case dBScale:
1568  val = AudioLevel::dB_to_multiplier(-(10*n) + i * 10) * gain;
1569  text = QString("%1").arg(-(10*n) + i * 10);
1570  if (i == n) text = tr("0dB");
1571  if (i == 0) {
1572  text = Strings::minus_infinity;
1573  val = 0.0;
1574  }
1575  break;
1576  }
1577 
1578  if (val < -1.0 || val > 1.0) continue;
1579 
1580  int y = getYForValue(v, val, ch);
1581 
1582  int ny = y;
1583  if (nval != 0.0) {
1584  ny = getYForValue(v, nval, ch);
1585  }
1586 
1587  bool spaceForLabel = (i == 0 ||
1588  abs(y - lastLabelledY) >= textHeight - 1);
1589 
1590  if (spaceForLabel) {
1591 
1592  int tx = 3;
1593  if (m_scale != LinearScale) {
1594  tx = w - 10 - paint.fontMetrics().width(text);
1595  }
1596 
1597  int ty = y;
1598  if (ty < paint.fontMetrics().ascent()) {
1599  ty = paint.fontMetrics().ascent();
1600  } else if (ty > h - paint.fontMetrics().descent()) {
1601  ty = h - paint.fontMetrics().descent();
1602  } else {
1603  ty += toff;
1604  }
1605  paint.drawText(tx, ty, text);
1606 
1607  lastLabelledY = ty - toff;
1608 
1609  if (ny != y) {
1610  ty = ny;
1611  if (ty < paint.fontMetrics().ascent()) {
1612  ty = paint.fontMetrics().ascent();
1613  } else if (ty > h - paint.fontMetrics().descent()) {
1614  ty = h - paint.fontMetrics().descent();
1615  } else {
1616  ty += toff;
1617  }
1618  paint.drawText(tx, ty, text);
1619  }
1620 
1621  paint.drawLine(w - 7, y, w, y);
1622  if (ny != y) paint.drawLine(w - 7, ny, w, ny);
1623 
1624  } else {
1625 
1626  paint.drawLine(w - 4, y, w, y);
1627  if (ny != y) paint.drawLine(w - 4, ny, w, ny);
1628  }
1629  }
1630  }
1631 }
1632 
1633 void
1634 WaveformLayer::toXml(QTextStream &stream,
1635  QString indent, QString extraAttributes) const
1636 {
1637  QString s;
1638 
1639  QString colourName, colourSpec, darkbg;
1641  (m_colour, colourName, colourSpec, darkbg);
1642 
1643  s += QString("gain=\"%1\" "
1644  "showMeans=\"%2\" "
1645  "greyscale=\"%3\" "
1646  "channelMode=\"%4\" "
1647  "channel=\"%5\" "
1648  "scale=\"%6\" "
1649  "middleLineHeight=\"%7\" "
1650  "aggressive=\"%8\" "
1651  "autoNormalize=\"%9\"")
1652  .arg(m_gain)
1653  .arg(m_showMeans)
1654  .arg(true) // Option removed, but effectively always on, so
1655  // retained in the session file for compatibility
1656  .arg(m_channelMode)
1657  .arg(m_channel)
1658  .arg(m_scale)
1659  .arg(m_middleLineHeight)
1660  .arg(m_aggressive)
1661  .arg(m_autoNormalize);
1662 
1663  SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s);
1664 }
1665 
1666 void
1667 WaveformLayer::setProperties(const QXmlAttributes &attributes)
1668 {
1669  bool ok = false;
1670 
1672 
1673  float gain = attributes.value("gain").toFloat(&ok);
1674  if (ok) setGain(gain);
1675 
1676  bool showMeans = (attributes.value("showMeans") == "1" ||
1677  attributes.value("showMeans") == "true");
1678  setShowMeans(showMeans);
1679 
1680  ChannelMode channelMode = (ChannelMode)
1681  attributes.value("channelMode").toInt(&ok);
1682  if (ok) setChannelMode(channelMode);
1683 
1684  int channel = attributes.value("channel").toInt(&ok);
1685  if (ok) setChannel(channel);
1686 
1687  Scale scale = (Scale)attributes.value("scale").toInt(&ok);
1688  if (ok) setScale(scale);
1689 
1690  float middleLineHeight = attributes.value("middleLineHeight").toFloat(&ok);
1691  if (ok) setMiddleLineHeight(middleLineHeight);
1692 
1693  bool aggressive = (attributes.value("aggressive") == "1" ||
1694  attributes.value("aggressive") == "true");
1695  setAggressiveCacheing(aggressive);
1696 
1697  bool autoNormalize = (attributes.value("autoNormalize") == "1" ||
1698  attributes.value("autoNormalize") == "true");
1699  setAutoNormalize(autoNormalize);
1700 }
1701 
1702 int
1704 {
1705  defaultStep = 50;
1706  return 100;
1707 }
1708 
1709 int
1711 {
1712  int val = int(lrint(log10(m_gain) * 20.0) + 50);
1713  if (val < 0) val = 0;
1714  if (val > 100) val = 100;
1715  return val;
1716 }
1717 
1718 void
1720 {
1721  setGain(powf(10, float(step - 50) / 20.f));
1722 }
1723 
PropertyType getPropertyType(const PropertyName &) const override
const ZoomConstraint * getZoomConstraint() const override
Return a zoom constraint object defining the supported zoom levels for this layer.
bool getDisplayExtents(double &min, double &max) const override
Return the minimum and maximum values within the visible area for the y axis of this layer...
bool isLayerScrollable(const LayerGeometryProvider *) const override
This should return true if the layer can safely be scrolled automatically by a given view (simply cop...
void paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const override
Paint the given rectangle of this layer onto the given view using the given painter, superimposing it on top of any existing material in that view.
PropertyList getProperties() const override
void setChannel(int)
Specify the channel to use from the source model.
bool getYScaleValue(const LayerGeometryProvider *v, int y, double &value, QString &unit) const override
Return the value and unit at the given y coordinate in the given view.
virtual ZoomLevel getZoomLevel() const =0
Return the zoom level, i.e.
int getChannelArrangement(int &min, int &max, bool &merging, bool &mixing) const
Return value is number of channels displayed.
double m_middleLineHeight
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
void setMiddleLineHeight(double)
Specify the height of the middle of the waveform track or tracks within the layer, from 0.0 to 1.0.
void modelReplaced()
void setModel(ModelId model)
int getYForValue(const LayerGeometryProvider *v, double value, int channel) const
void setAutoNormalize(bool)
Toggle automatic normalization of the currently visible waveform.
int getVerticalZoomSteps(int &defaultStep) const override
Get the number of vertical zoom steps available for this layer.
virtual QColor getForegroundQColor(LayerGeometryProvider *v) const
ChannelMode m_channelMode
std::vector< float > m_effectiveGains
QString getPropertyLabel(const PropertyName &) const override
std::vector< RangeSummarisableTimeValueModel::RangeBlock > RangeVec
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
PropertyList getProperties() const override
static float meterdbs[]
void setScale(Scale)
Specify the vertical scale for sample levels.
QString getFeatureDescription(LayerGeometryProvider *v, QPoint &) const override
void getOversampledRanges(int minChannel, int maxChannel, bool mixingOrMerging, sv_frame_t f0, sv_frame_t f1, int oversampleBy, RangeVec &ranges) const
bool getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1, double &diff, QString &unit) const override
Return the difference between the values at the given y coordinates in the given view, and the unit of the difference.
virtual double scaleSize(double size) const =0
virtual QColor getBaseQColor() const
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
QString getPropertyIconName(const PropertyName &) const override
void paintVerticalScale(LayerGeometryProvider *v, bool detailed, QPainter &paint, QRect rect) const override
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
void setVerticalZoomStep(int) override
Set the vertical zoom step.
void setProperty(const PropertyName &, int value) override
void setGain(float gain)
Set the gain multiplier for sample values in this view.
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
bool getSourceFramesForX(LayerGeometryProvider *v, int x, int modelZoomLevel, sv_frame_t &f0, sv_frame_t &f1) const
int getCompletion(LayerGeometryProvider *) const override
Return the proportion of background work complete in drawing this view, as a percentage – in most ca...
QString getPropertyValueLabel(const PropertyName &, int value) const override
virtual QRect getPaintRect() const =0
To be called from a layer, to obtain the extent of the surface that the layer is currently painting t...
void paintChannelScaleGuides(LayerGeometryProvider *, QPainter *paint, QRect rect, int channel) const
void layerParametersChanged()
virtual sv_frame_t getStartFrame() const =0
Retrieve the first visible sample frame on the widget.
void verticalZoomChanged()
RangeMapper * getNewPropertyRangeMapper(const PropertyName &) const override
void getStringValues(int index, QString &colourName, QString &colourSpec, QString &darkbg) const
int getCurrentVerticalZoomStep() const override
Get the current vertical zoom step.
int getPropertyRangeAndValue(const PropertyName &, int *min, int *max, int *deflt) const override
float getNormalizeGain(LayerGeometryProvider *v, int channel) const
void connectSignals(ModelId)
Definition: Layer.cpp:49
virtual int getPaintHeight() const
QString getPropertyGroupName(const PropertyName &) const override
double dBscale(double sample, int m) const
PropertyType getPropertyType(const PropertyName &) const override
static double m_dBMin
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
int getVerticalScaleWidth(LayerGeometryProvider *v, bool detailed, QPainter &) const override
void paintChannel(LayerGeometryProvider *, QPainter *paint, QRect rect, int channel, const RangeVec &ranges, int blockSize, sv_frame_t frame0, sv_frame_t frame1) const
virtual ViewManager * getViewManager() const =0
bool shouldShowScaleGuides() const
Definition: ViewManager.h:234
void getSummaryRanges(int minChannel, int maxChannel, bool mixingOrMerging, sv_frame_t f0, sv_frame_t f1, int blockSize, RangeVec &ranges) const
QString getPropertyValueLabel(const PropertyName &, int value) const override
virtual QColor getBackgroundQColor(LayerGeometryProvider *v) const
void setShowMeans(bool)
Set whether to display mean values as a lighter-coloured area beneath the peaks.
void setProperties(const QXmlAttributes &attributes) override
Set the particular properties of a layer (those specific to the subclass) from a set of XML attribute...
QPixmap * m_cache
ZoomLevel m_cacheZoomLevel
virtual sv_frame_t getEndFrame() const =0
Retrieve the last visible sample frame on the widget.
void setAggressiveCacheing(bool)
Enable or disable aggressive pixmap cacheing.
virtual bool hasLightBackground() const =0
void setProperty(const PropertyName &, int value) override
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
virtual int getPaintWidth() const
bool getValueExtents(double &min, double &max, bool &log, QString &unit) const override
Return the minimum and maximum values for the y axis of the model in this layer, as well as whether t...
QString getPropertyLabel(const PropertyName &) const override
void setChannelMode(ChannelMode)
Specify whether multi-channel audio data should be displayed with a separate axis per channel (Separa...
static ColourDatabase * getInstance()
double getValueForY(const LayerGeometryProvider *v, int y, int &channel) const