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
43 WaveformLayer::m_dBMin = -50.0;
44
WaveformLayer()45 WaveformLayer::WaveformLayer() :
46 SingleColourLayer(),
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
~WaveformLayer()61 WaveformLayer::~WaveformLayer()
62 {
63 delete m_cache;
64 }
65
66 const ZoomConstraint *
getZoomConstraint() const67 WaveformLayer::getZoomConstraint() const
68 {
69 auto model = ModelById::get(m_model);
70 if (model) return model->getZoomConstraint();
71 else return nullptr;
72 }
73
74 void
setModel(ModelId modelId)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();
107 connectSignals(m_model);
108 }
109
110 emit modelReplaced();
111
112 if (channelsChanged) emit layerParametersChanged();
113 }
114
115 Layer::PropertyList
getProperties() const116 WaveformLayer::getProperties() const
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
getPropertyLabel(const PropertyName & name) const130 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");
136 return SingleColourLayer::getPropertyLabel(name);
137 }
138
139 QString
getPropertyIconName(const PropertyName & name) const140 WaveformLayer::getPropertyIconName(const PropertyName &name) const
141 {
142 if (name == "Normalize Visible Area") return "normalise";
143 return "";
144 }
145
146 Layer::PropertyType
getPropertyType(const PropertyName & name) const147 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;
153 return SingleColourLayer::getPropertyType(name);
154 }
155
156 QString
getPropertyGroupName(const PropertyName & name) const157 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
getPropertyRangeAndValue(const PropertyName & name,int * min,int * max,int * deflt) const166 WaveformLayer::getPropertyRangeAndValue(const PropertyName &name,
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
getPropertyValueLabel(const PropertyName & name,int value) const216 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 *
getNewPropertyRangeMapper(const PropertyName & name) const239 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
setProperty(const PropertyName & name,int value)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);
257 else setChannelMode(SeparateChannels);
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
setGain(float gain)271 WaveformLayer::setGain(float gain)
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
setAutoNormalize(bool autoNormalize)281 WaveformLayer::setAutoNormalize(bool autoNormalize)
282 {
283 if (m_autoNormalize == autoNormalize) return;
284 m_autoNormalize = autoNormalize;
285 m_cacheValid = false;
286 emit layerParametersChanged();
287 }
288
289 void
setShowMeans(bool showMeans)290 WaveformLayer::setShowMeans(bool showMeans)
291 {
292 if (m_showMeans == showMeans) return;
293 m_showMeans = showMeans;
294 m_cacheValid = false;
295 emit layerParametersChanged();
296 }
297
298 void
setChannelMode(ChannelMode channelMode)299 WaveformLayer::setChannelMode(ChannelMode channelMode)
300 {
301 if (m_channelMode == channelMode) return;
302 m_channelMode = channelMode;
303 m_cacheValid = false;
304 emit layerParametersChanged();
305 }
306
307 void
setChannel(int channel)308 WaveformLayer::setChannel(int channel)
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
setScale(Scale scale)319 WaveformLayer::setScale(Scale scale)
320 {
321 if (m_scale == scale) return;
322 m_scale = scale;
323 m_cacheValid = false;
324 emit layerParametersChanged();
325 }
326
327 void
setMiddleLineHeight(double height)328 WaveformLayer::setMiddleLineHeight(double height)
329 {
330 if (m_middleLineHeight == height) return;
331 m_middleLineHeight = height;
332 m_cacheValid = false;
333 emit layerParametersChanged();
334 }
335
336 void
setAggressiveCacheing(bool aggressive)337 WaveformLayer::setAggressiveCacheing(bool aggressive)
338 {
339 if (m_aggressive == aggressive) return;
340 m_aggressive = aggressive;
341 m_cacheValid = false;
342 emit layerParametersChanged();
343 }
344
345 int
getCompletion(LayerGeometryProvider *) const346 WaveformLayer::getCompletion(LayerGeometryProvider *) const
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
getValueExtents(double & min,double & max,bool & log,QString & unit) const356 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
getDisplayExtents(double & min,double & max) const399 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) {
412 if (m_channelMode == SeparateChannels) {
413 return false;
414 }
415 if (m_channelMode == MergeChannels && m_scale != LinearScale) {
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
dBscale(double sample,int m) const436 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
getChannelArrangement(int & min,int & max,bool & merging,bool & mixing) const446 WaveformLayer::getChannelArrangement(int &min, int &max,
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 ||
458 m_channelMode == MixChannels) {
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
isLayerScrollable(const LayerGeometryProvider *) const490 WaveformLayer::isLayerScrollable(const LayerGeometryProvider *) const
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
getSourceFramesForX(LayerGeometryProvider * v,int x,int modelZoomLevel,sv_frame_t & f0,sv_frame_t & f1) const499 WaveformLayer::getSourceFramesForX(LayerGeometryProvider *v,
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
getNormalizeGain(LayerGeometryProvider * v,int channel) const530 WaveformLayer::getNormalizeGain(LayerGeometryProvider *v, int channel) const
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
paint(LayerGeometryProvider * v,QPainter & viewPainter,QRect rect) const575 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
getSummaryRanges(int minChannel,int maxChannel,bool mixingOrMerging,sv_frame_t frame0,sv_frame_t frame1,int blockSize,RangeVec & ranges) const742 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
getOversampledRanges(int minChannel,int maxChannel,bool mixingOrMerging,sv_frame_t frame0,sv_frame_t frame1,int oversampleBy,RangeVec & ranges) const774 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
790 getOversampledRanges
791 (0, 1, false, frame0, frame1, oversampleBy, ranges);
792 return;
793 } else {
794 // call back on self for a single channel, then duplicate
795 getOversampledRanges
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
paintChannel(LayerGeometryProvider * v,QPainter * paint,QRect rect,int ch,const RangeVec & ranges,int blockSize,sv_frame_t frame0,sv_frame_t frame1) const849 WaveformLayer::paintChannel(LayerGeometryProvider *v,
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) &&
899 m_channelMode != MergeChannels) {
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
paintChannelScaleGuides(LayerGeometryProvider * v,QPainter * paint,QRect rect,int ch) const1195 WaveformLayer::paintChannelScaleGuides(LayerGeometryProvider *v,
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() &&
1210 v->getViewManager()->shouldShowScaleGuides()) {
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
getFeatureDescription(LayerGeometryProvider * v,QPoint & pos) const1255 WaveformLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
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
getYForValue(const LayerGeometryProvider * v,double value,int channel) const1343 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) &&
1357 m_channelMode != MergeChannels) {
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
getValueForY(const LayerGeometryProvider * v,int y,int & channel) const1386 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) &&
1400 m_channelMode != MergeChannels) {
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
getYScaleValue(const LayerGeometryProvider * v,int y,double & value,QString & unit) const1432 WaveformLayer::getYScaleValue(const LayerGeometryProvider *v, int y,
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
getYScaleDifference(const LayerGeometryProvider * v,int y0,int y1,double & diff,QString & unit) const1458 WaveformLayer::getYScaleDifference(const LayerGeometryProvider *v, int y0, int y1,
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
getVerticalScaleWidth(LayerGeometryProvider *,bool,QPainter & paint) const1496 WaveformLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
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
paintVerticalScale(LayerGeometryProvider * v,bool,QPainter & paint,QRect rect) const1512 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
toXml(QTextStream & stream,QString indent,QString extraAttributes) const1634 WaveformLayer::toXml(QTextStream &stream,
1635 QString indent, QString extraAttributes) const
1636 {
1637 QString s;
1638
1639 QString colourName, colourSpec, darkbg;
1640 ColourDatabase::getInstance()->getStringValues
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
setProperties(const QXmlAttributes & attributes)1667 WaveformLayer::setProperties(const QXmlAttributes &attributes)
1668 {
1669 bool ok = false;
1670
1671 SingleColourLayer::setProperties(attributes);
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
getVerticalZoomSteps(int & defaultStep) const1703 WaveformLayer::getVerticalZoomSteps(int &defaultStep) const
1704 {
1705 defaultStep = 50;
1706 return 100;
1707 }
1708
1709 int
getCurrentVerticalZoomStep() const1710 WaveformLayer::getCurrentVerticalZoomStep() const
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
setVerticalZoomStep(int step)1719 WaveformLayer::setVerticalZoomStep(int step)
1720 {
1721 setGain(powf(10, float(step - 50) / 20.f));
1722 }
1723
1724