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; |
