comparison layer/WaveformLayer.cpp @ 1542:10fe8124dc17

Overhaul the way WaveformLayer reports value and display extents. The previous logic has been nonsense for a very long time, excused only by the fact that it was very seldom relevant (only really if another layer with unit V came along that wanted to auto-align with it). Partial fix for #1954 Peculiar alignment for Amplitude Follower y-scale in Auto-Align mode
author Chris Cannam
date Wed, 16 Oct 2019 12:23:36 +0100
parents 1ccb64bfb22b
children 2d4107270015
comparison
equal deleted inserted replaced
1541:b4b5b8dd5fe1 1542:10fe8124dc17
36 //#define DEBUG_WAVEFORM_PAINT 1 36 //#define DEBUG_WAVEFORM_PAINT 1
37 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1 37 //#define DEBUG_WAVEFORM_PAINT_BY_PIXEL 1
38 38
39 using std::vector; 39 using std::vector;
40 40
41 double
42 WaveformLayer::m_dBMin = -50.0;
41 43
42 WaveformLayer::WaveformLayer() : 44 WaveformLayer::WaveformLayer() :
43 SingleColourLayer(), 45 SingleColourLayer(),
44 m_gain(1.0f), 46 m_gain(1.0f),
45 m_autoNormalize(false), 47 m_autoNormalize(false),
46 m_showMeans(true), 48 m_showMeans(true),
47 m_channelMode(SeparateChannels), 49 m_channelMode(SeparateChannels),
48 m_channel(-1), 50 m_channel(-1),
51 m_channelCount(0),
49 m_scale(LinearScale), 52 m_scale(LinearScale),
50 m_middleLineHeight(0.5), 53 m_middleLineHeight(0.5),
51 m_aggressive(false), 54 m_aggressive(false),
52 m_cache(nullptr), 55 m_cache(nullptr),
53 m_cacheValid(false) 56 m_cacheValid(false)
74 auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId); 77 auto newModel = ModelById::getAs<RangeSummarisableTimeValueModel>(modelId);
75 78
76 if (!modelId.isNone() && !newModel) { 79 if (!modelId.isNone() && !newModel) {
77 throw std::logic_error("Not a RangeSummarisableTimeValueModel"); 80 throw std::logic_error("Not a RangeSummarisableTimeValueModel");
78 } 81 }
79 82
80 if (m_model == modelId) return; 83 if (m_model == modelId) return;
81 m_model = modelId; 84 m_model = modelId;
82 85
86 // NB newModel may legitimately be null
87
83 m_cacheValid = false; 88 m_cacheValid = false;
84 89
85 bool channelsChanged = false; 90 bool channelsChanged = false;
86 if (m_channel == -1) { 91 if (m_channel == -1) {
87 if (!oldModel) { 92 if (!oldModel) {
95 } 100 }
96 } 101 }
97 } 102 }
98 103
99 if (newModel) { 104 if (newModel) {
105 m_channelCount = newModel->getChannelCount();
100 connectSignals(m_model); 106 connectSignals(m_model);
101 } 107 }
102 108
103 emit modelReplaced(); 109 emit modelReplaced();
104 110
110 { 116 {
111 PropertyList list = SingleColourLayer::getProperties(); 117 PropertyList list = SingleColourLayer::getProperties();
112 list.push_back("Scale"); 118 list.push_back("Scale");
113 list.push_back("Gain"); 119 list.push_back("Gain");
114 list.push_back("Normalize Visible Area"); 120 list.push_back("Normalize Visible Area");
115 121 if (m_channelCount > 1 && m_channel == -1) {
116 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model);
117 if (model && model->getChannelCount() > 1 && m_channel == -1) {
118 list.push_back("Channels"); 122 list.push_back("Channels");
119 } 123 }
120 124
121 return list; 125 return list;
122 } 126 }
347 return completion; 351 return completion;
348 } 352 }
349 353
350 bool 354 bool
351 WaveformLayer::getValueExtents(double &min, double &max, 355 WaveformLayer::getValueExtents(double &min, double &max,
352 bool &, QString &unit) const 356 bool &log, QString &unit) const
353 { 357 {
358 unit = "V";
359
360 // There is no point in returning extents here unless we have a
361 // scale that anyone else can actually calculate with, which is
362 // only the case if getDisplayExtents is returning successfully
363
364 if (getDisplayExtents(min, max)) {
365 log = (m_scale == dBScale);
366 return true;
367 } else {
368 return false;
369 }
370 }
371
372 bool
373 WaveformLayer::getDisplayExtents(double &min, double &max) const
374 {
375 // If we have a single channel visible and either linear or log
376 // (dB) scale, then we have a continuous scale that runs from -1
377 // to 1 or -dBMin to 0 and we can offer it as an alignment target
378 // for other layers with the same unit. We can also do this in
379 // butterfly mode, but only with linear scale. Otherwise no.
380
381 if (m_scale == MeterScale) {
382 return false;
383 }
384
385 if (m_channelCount > 1) {
386 if (m_channelMode == SeparateChannels) {
387 return false;
388 }
389 if (m_channelMode == MergeChannels && m_scale != LinearScale) {
390 return false;
391 }
392 }
393
354 if (m_scale == LinearScale) { 394 if (m_scale == LinearScale) {
355 min = 0.0;
356 max = 1.0; 395 max = 1.0;
357 unit = "V"; 396 min = -1.0;
358 } else if (m_scale == MeterScale) { 397 return true;
359 return false; //!!! 398 }
360 } else { 399
361 min = AudioLevel::multiplier_to_dB(0.0); 400 if (m_scale == dBScale) {
362 max = AudioLevel::multiplier_to_dB(1.0); 401 max = 1.0;
363 unit = "dB"; 402 min = AudioLevel::dB_to_multiplier(m_dBMin);
364 } 403 return true;
365 return true; 404 }
405
406 return false;
366 } 407 }
367 408
368 double 409 double
369 WaveformLayer::dBscale(double sample, int m) const 410 WaveformLayer::dBscale(double sample, int m) const
370 { 411 {
371 if (sample < 0.0) return dBscale(-sample, m); 412 if (sample < 0.0) return dBscale(-sample, m);
372 double dB = AudioLevel::multiplier_to_dB(sample); 413 double dB = AudioLevel::multiplier_to_dB(sample);
373 if (dB < -50.0) return 0; 414 if (dB < m_dBMin) return 0;
374 if (dB > 0.0) return m; 415 if (dB > 0.0) return m;
375 return ((dB + 50.0) * m) / 50.0; 416 return ((dB - m_dBMin) * m) / (-m_dBMin);
376 } 417 }
377 418
378 int 419 int
379 WaveformLayer::getChannelArrangement(int &min, int &max, 420 WaveformLayer::getChannelArrangement(int &min, int &max,
380 bool &merging, bool &mixing) 421 bool &merging, bool &mixing)
381 const 422 const
382 { 423 {
383 auto model = ModelById::getAs<RangeSummarisableTimeValueModel>(m_model); 424 int channels = m_channelCount;
384 if (!model || !model->isOK()) return 0;
385
386 int channels = model->getChannelCount();
387 if (channels == 0) return 0; 425 if (channels == 0) return 0;
388 426
389 int rawChannels = channels; 427 int rawChannels = channels;
390 428
391 if (m_channel == -1) { 429 if (m_channel == -1) {
493 531
494 (void)getChannelArrangement(minChannel, maxChannel, 532 (void)getChannelArrangement(minChannel, maxChannel,
495 mergingChannels, mixingChannels); 533 mergingChannels, mixingChannels);
496 534
497 if (mergingChannels || mixingChannels) { 535 if (mergingChannels || mixingChannels) {
498 if (model->getChannelCount() > 1) { 536 if (m_channelCount > 1) {
499 RangeSummarisableTimeValueModel::Range otherRange = 537 RangeSummarisableTimeValueModel::Range otherRange =
500 model->getSummary(1, rangeStart, rangeEnd - rangeStart); 538 model->getSummary(1, rangeStart, rangeEnd - rangeStart);
501 range.setMax(std::max(range.max(), otherRange.max())); 539 range.setMax(std::max(range.max(), otherRange.max()));
502 range.setMin(std::min(range.min(), otherRange.min())); 540 range.setMin(std::min(range.min(), otherRange.min()));
503 range.setAbsmean(std::min(range.absmean(), otherRange.absmean())); 541 range.setAbsmean(std::min(range.absmean(), otherRange.absmean()));
694 } 732 }
695 733
696 if (mixingOrMerging) { 734 if (mixingOrMerging) {
697 if (minChannel != 0 || maxChannel != 0) { 735 if (minChannel != 0 || maxChannel != 0) {
698 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels"); 736 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
699 } else if (model->getChannelCount() > 1) { 737 } else if (m_channelCount > 1) {
700 ranges.push_back({}); 738 ranges.push_back({});
701 model->getSummaries 739 model->getSummaries
702 (1, frame0, frame1 - frame0, ranges[1], blockSize); 740 (1, frame0, frame1 - frame0, ranges[1], blockSize);
703 } else { 741 } else {
704 ranges.push_back(ranges[0]); 742 ranges.push_back(ranges[0]);
718 756
719 if (mixingOrMerging) { 757 if (mixingOrMerging) {
720 if (minChannel != 0 || maxChannel != 0) { 758 if (minChannel != 0 || maxChannel != 0) {
721 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels"); 759 throw std::logic_error("Internal error: min & max channels should be 0 when merging or mixing all channels");
722 } 760 }
723 if (model->getChannelCount() > 1) { 761 if (m_channelCount > 1) {
724 // call back on self for the individual channels with 762 // call back on self for the individual channels with
725 // mixingOrMerging false 763 // mixingOrMerging false
726 getOversampledRanges 764 getOversampledRanges
727 (0, 1, false, frame0, frame1, oversampleBy, ranges); 765 (0, 1, false, frame0, frame1, oversampleBy, ranges);
728 return; 766 return;
1341 1379
1342 int my = m + (((channel - minChannel) * h) / channels); 1380 int my = m + (((channel - minChannel) * h) / channels);
1343 1381
1344 int vy = my - y; 1382 int vy = my - y;
1345 double value = 0; 1383 double value = 0;
1346 double thresh = -50.f; 1384 double thresh = m_dBMin;
1347 1385
1348 switch (m_scale) { 1386 switch (m_scale) {
1349 1387
1350 case LinearScale: 1388 case LinearScale:
1351 value = double(vy) / m; 1389 value = double(vy) / m;
1372 1410
1373 value = getValueForY(v, y, channel); 1411 value = getValueForY(v, y, channel);
1374 1412
1375 if (m_scale == dBScale || m_scale == MeterScale) { 1413 if (m_scale == dBScale || m_scale == MeterScale) {
1376 1414
1377 double thresh = -50.f; 1415 double thresh = m_dBMin;
1378 1416
1379 if (value > 0.0) { 1417 if (value > 0.0) {
1380 value = 10.0 * log10(value); 1418 value = 10.0 * log10(value);
1381 if (value < thresh) value = thresh; 1419 if (value < thresh) value = thresh;
1382 } else value = thresh; 1420 } else value = thresh;
1405 return false; 1443 return false;
1406 } 1444 }
1407 1445
1408 if (m_scale == dBScale || m_scale == MeterScale) { 1446 if (m_scale == dBScale || m_scale == MeterScale) {
1409 1447
1410 double thresh = -50.0; 1448 double thresh = m_dBMin;
1411 1449
1412 if (v1 == v0) diff = thresh; 1450 if (v1 == v0) diff = thresh;
1413 else { 1451 else {
1414 if (v1 > v0) diff = v0 / v1; 1452 if (v1 > v0) diff = v0 / v1;
1415 else diff = v1 / v0; 1453 else diff = v1 / v0;