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