Mercurial > hg > svgui
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; |