Mercurial > hg > svgui
comparison layer/WaveformLayer.cpp @ 365:26ce2fb7bcbf
* Ensure waveforms are strictly correct even when using a non-power-of-two
non-power-of-sqrt-two block size with cacheing off and painting only small
areas at a time
| author | Chris Cannam |
|---|---|
| date | Thu, 07 Feb 2008 15:25:05 +0000 |
| parents | a9dfa2d6d5ac |
| children | e1a9e478b7f2 |
comparison
equal
deleted
inserted
replaced
| 364:f1e6204c1f17 | 365:26ce2fb7bcbf |
|---|---|
| 394 } | 394 } |
| 395 | 395 |
| 396 static float meterdbs[] = { -40, -30, -20, -15, -10, | 396 static float meterdbs[] = { -40, -30, -20, -15, -10, |
| 397 -5, -3, -2, -1, -0.5, 0 }; | 397 -5, -3, -2, -1, -0.5, 0 }; |
| 398 | 398 |
| 399 bool | |
| 400 WaveformLayer::getSourceFramesForX(View *v, int x, size_t modelZoomLevel, | |
| 401 size_t &f0, size_t &f1) const | |
| 402 { | |
| 403 long viewFrame = v->getFrameForX(x); | |
| 404 if (viewFrame < 0) { | |
| 405 f0 = 0; | |
| 406 f1 = 0; | |
| 407 return false; | |
| 408 } | |
| 409 | |
| 410 f0 = viewFrame; | |
| 411 | |
| 412 f0 = f0 / modelZoomLevel; | |
| 413 f0 = f0 * modelZoomLevel; | |
| 414 | |
| 415 viewFrame = v->getFrameForX(x + 1); | |
| 416 | |
| 417 f1 = viewFrame; | |
| 418 f1 = f1 / modelZoomLevel; | |
| 419 f1 = f1 * modelZoomLevel; | |
| 420 | |
| 421 return (f0 < m_model->getEndFrame()); | |
| 422 } | |
| 423 | |
| 424 float | |
| 425 WaveformLayer::getNormalizeGain(View *v, int channel) const | |
| 426 { | |
| 427 long startFrame = v->getStartFrame(); | |
| 428 long endFrame = v->getEndFrame(); | |
| 429 | |
| 430 // Although a long for purposes of comparison against the view | |
| 431 // start and end frames, these are known to be non-negative | |
| 432 long modelStart = long(m_model->getStartFrame()); | |
| 433 long modelEnd = long(m_model->getEndFrame()); | |
| 434 | |
| 435 size_t rangeStart, rangeEnd; | |
| 436 | |
| 437 if (startFrame < modelStart) rangeStart = modelStart; | |
| 438 else rangeStart = startFrame; | |
| 439 | |
| 440 if (endFrame < 0) rangeEnd = 0; | |
| 441 else if (endFrame > modelEnd) rangeEnd = modelEnd; | |
| 442 else rangeEnd = endFrame; | |
| 443 | |
| 444 if (rangeEnd < rangeStart) rangeEnd = rangeStart; | |
| 445 | |
| 446 RangeSummarisableTimeValueModel::Range range = | |
| 447 m_model->getSummary(channel, rangeStart, rangeEnd - rangeStart); | |
| 448 | |
| 449 size_t minChannel = 0, maxChannel = 0; | |
| 450 bool mergingChannels = false, mixingChannels = false; | |
| 451 | |
| 452 getChannelArrangement(minChannel, maxChannel, | |
| 453 mergingChannels, mixingChannels); | |
| 454 | |
| 455 if (mergingChannels || mixingChannels) { | |
| 456 RangeSummarisableTimeValueModel::Range otherRange = | |
| 457 m_model->getSummary(1, rangeStart, rangeEnd - rangeStart); | |
| 458 range.max = std::max(range.max, otherRange.max); | |
| 459 range.min = std::min(range.min, otherRange.min); | |
| 460 range.absmean = std::min(range.absmean, otherRange.absmean); | |
| 461 } | |
| 462 | |
| 463 return 1.0 / std::max(fabsf(range.max), fabsf(range.min)); | |
| 464 } | |
| 465 | |
| 399 void | 466 void |
| 400 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const | 467 WaveformLayer::paint(View *v, QPainter &viewPainter, QRect rect) const |
| 401 { | 468 { |
| 402 if (!m_model || !m_model->isOK()) { | 469 if (!m_model || !m_model->isOK()) { |
| 403 return; | 470 return; |
| 404 } | 471 } |
| 405 | 472 |
| 406 long startFrame = v->getStartFrame(); | |
| 407 long endFrame = v->getEndFrame(); | |
| 408 int zoomLevel = v->getZoomLevel(); | 473 int zoomLevel = v->getZoomLevel(); |
| 409 | 474 |
| 410 #ifdef DEBUG_WAVEFORM_PAINT | 475 #ifdef DEBUG_WAVEFORM_PAINT |
| 411 Profiler profiler("WaveformLayer::paint", true); | 476 Profiler profiler("WaveformLayer::paint", true); |
| 412 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y() | 477 std::cerr << "WaveformLayer::paint (" << rect.x() << "," << rect.y() |
| 476 y1 = rect.bottom(); | 541 y1 = rect.bottom(); |
| 477 | 542 |
| 478 if (x0 > 0) --x0; | 543 if (x0 > 0) --x0; |
| 479 if (x1 < v->width()) ++x1; | 544 if (x1 < v->width()) ++x1; |
| 480 | 545 |
| 481 long frame0 = v->getFrameForX(x0); | 546 // Our zoom level may differ from that at which the underlying |
| 482 long frame1 = v->getFrameForX(x1 + 1); | 547 // model has its blocks. |
| 483 | 548 |
| 549 // Each pixel within our visible range must always draw from | |
| 550 // exactly the same set of underlying audio frames, no matter what | |
| 551 // the range being drawn is. And that set of underlying frames | |
| 552 // must remain the same when we scroll one or more pixels left or | |
| 553 // right. | |
| 554 | |
| 555 size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); | |
| 556 | |
| 557 size_t frame0; | |
| 558 size_t frame1; | |
| 559 size_t spare; | |
| 560 | |
| 561 getSourceFramesForX(v, x0, modelZoomLevel, frame0, spare); | |
| 562 getSourceFramesForX(v, x1, modelZoomLevel, spare, frame1); | |
| 563 | |
| 484 #ifdef DEBUG_WAVEFORM_PAINT | 564 #ifdef DEBUG_WAVEFORM_PAINT |
| 485 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << ")" << std::endl; | 565 std::cerr << "Painting waveform from " << frame0 << " to " << frame1 << " (" << (x1-x0+1) << " pixels at zoom " << zoomLevel << " and model zoom " << modelZoomLevel << ")" << std::endl; |
| 486 #endif | 566 #endif |
| 487 | 567 |
| 488 RangeSummarisableTimeValueModel::RangeBlock *ranges = | 568 RangeSummarisableTimeValueModel::RangeBlock *ranges = |
| 489 new RangeSummarisableTimeValueModel::RangeBlock; | 569 new RangeSummarisableTimeValueModel::RangeBlock; |
| 490 | 570 |
| 505 | 585 |
| 506 while (m_effectiveGains.size() <= maxChannel) { | 586 while (m_effectiveGains.size() <= maxChannel) { |
| 507 m_effectiveGains.push_back(m_gain); | 587 m_effectiveGains.push_back(m_gain); |
| 508 } | 588 } |
| 509 | 589 |
| 510 // Although a long for purposes of comparison against the view | |
| 511 // start and end frames, these are known to be non-negative | |
| 512 long modelStart = long(m_model->getStartFrame()); | |
| 513 long modelEnd = long(m_model->getEndFrame()); | |
| 514 | |
| 515 #ifdef DEBUG_WAVEFORM_PAINT | |
| 516 std::cerr << "Model start = " << modelStart << ", end = " << modelEnd << std::endl; | |
| 517 #endif | |
| 518 | |
| 519 for (size_t ch = minChannel; ch <= maxChannel; ++ch) { | 590 for (size_t ch = minChannel; ch <= maxChannel; ++ch) { |
| 520 | 591 |
| 521 int prevRangeBottom = -1, prevRangeTop = -1; | 592 int prevRangeBottom = -1, prevRangeTop = -1; |
| 522 QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; | 593 QColor prevRangeBottomColour = baseColour, prevRangeTopColour = baseColour; |
| 523 size_t rangeStart, rangeEnd; | |
| 524 | 594 |
| 525 m_effectiveGains[ch] = m_gain; | 595 m_effectiveGains[ch] = m_gain; |
| 526 | 596 |
| 527 if (m_autoNormalize) { | 597 if (m_autoNormalize) { |
| 528 | 598 m_effectiveGains[ch] = getNormalizeGain(v, ch); |
| 529 if (startFrame < modelStart) rangeStart = modelStart; | |
| 530 else rangeStart = startFrame; | |
| 531 | |
| 532 if (endFrame < 0) rangeEnd = 0; | |
| 533 else if (endFrame > modelEnd) rangeEnd = modelEnd; | |
| 534 else rangeEnd = endFrame; | |
| 535 | |
| 536 if (rangeEnd < rangeStart) rangeEnd = rangeStart; | |
| 537 | |
| 538 RangeSummarisableTimeValueModel::Range range = | |
| 539 m_model->getSummary(ch, rangeStart, rangeEnd - rangeStart); | |
| 540 if (mergingChannels || mixingChannels) { | |
| 541 RangeSummarisableTimeValueModel::Range otherRange = | |
| 542 m_model->getSummary(1, rangeStart, rangeEnd - rangeStart); | |
| 543 range.max = std::max(range.max, otherRange.max); | |
| 544 range.min = std::min(range.min, otherRange.min); | |
| 545 range.absmean = std::min(range.absmean, otherRange.absmean); | |
| 546 } | |
| 547 m_effectiveGains[ch] = 1.0 / std::max(fabsf(range.max), | |
| 548 fabsf(range.min)); | |
| 549 } | 599 } |
| 550 | 600 |
| 551 float gain = m_effectiveGains[ch]; | 601 float gain = m_effectiveGains[ch]; |
| 552 | 602 |
| 553 int m = (h / channels) / 2; | 603 int m = (h / channels) / 2; |
| 611 if (ny != y) { | 661 if (ny != y) { |
| 612 paint->drawLine(x0, ny, x1, ny); | 662 paint->drawLine(x0, ny, x1, ny); |
| 613 } | 663 } |
| 614 } | 664 } |
| 615 } | 665 } |
| 616 | 666 |
| 617 if (frame1 < modelStart) continue; | 667 m_model->getSummaries(ch, frame0, frame1 - frame0, |
| 618 | 668 *ranges, modelZoomLevel); |
| 619 size_t modelZoomLevel = zoomLevel; | 669 |
| 620 | 670 #ifdef DEBUG_WAVEFORM_PAINT |
| 621 if (frame0 < modelStart) rangeStart = modelStart; | 671 std::cerr << ranges->size() << " ranges from " << frame0 << " to " << frame1 << std::endl; |
| 622 else rangeStart = frame0; | 672 #endif |
| 623 | 673 |
| 624 if (frame1 < 0) rangeEnd = 0; | |
| 625 else if (frame1 > modelEnd) rangeEnd = modelEnd; | |
| 626 else rangeEnd = frame1; | |
| 627 | |
| 628 if (rangeEnd < rangeStart) rangeEnd = rangeStart; | |
| 629 | |
| 630 m_model->getSummaries | |
| 631 (ch, rangeStart, rangeEnd - rangeStart, *ranges, modelZoomLevel); | |
| 632 | |
| 633 // std::cerr << ranges->size() << " ranges" << std::endl; | |
| 634 | |
| 635 if (mergingChannels || mixingChannels) { | 674 if (mergingChannels || mixingChannels) { |
| 636 if (m_model->getChannelCount() > 1) { | 675 if (m_model->getChannelCount() > 1) { |
| 637 if (!otherChannelRanges) { | 676 if (!otherChannelRanges) { |
| 638 otherChannelRanges = | 677 otherChannelRanges = |
| 639 new RangeSummarisableTimeValueModel::RangeBlock; | 678 new RangeSummarisableTimeValueModel::RangeBlock; |
| 640 } | 679 } |
| 641 m_model->getSummaries | 680 m_model->getSummaries |
| 642 (1, rangeStart, rangeEnd - rangeStart, *otherChannelRanges, | 681 (1, frame0, frame1 - frame0, *otherChannelRanges, |
| 643 modelZoomLevel); | 682 modelZoomLevel); |
| 644 } else { | 683 } else { |
| 645 if (otherChannelRanges != ranges) delete otherChannelRanges; | 684 if (otherChannelRanges != ranges) delete otherChannelRanges; |
| 646 otherChannelRanges = ranges; | 685 otherChannelRanges = ranges; |
| 647 } | 686 } |
| 648 } | 687 } |
| 649 | 688 |
| 650 for (int x = x0; x <= x1; ++x) { | 689 for (int x = x0; x <= x1; ++x) { |
| 651 | 690 |
| 652 range = RangeSummarisableTimeValueModel::Range(); | 691 range = RangeSummarisableTimeValueModel::Range(); |
| 653 size_t index = x - x0; | 692 |
| 654 size_t maxIndex = index; | 693 size_t f0, f1; |
| 655 | 694 if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) continue; |
| 656 if (frame0 < modelStart) { | 695 f1 = f1 - 1; |
| 657 if (index < size_t((modelStart - frame0) / zoomLevel)) { | 696 |
| 658 continue; | 697 if (f0 < frame0) { |
| 659 } else { | 698 std::cerr << "ERROR: WaveformLayer::paint: pixel " << x << " has f0 = " << f0 << " which is less than range frame0 " << frame0 << " for x0 = " << x0 << std::endl; |
| 660 index -= ((modelStart - frame0) / zoomLevel); | 699 continue; |
| 661 maxIndex = index; | |
| 662 } | |
| 663 } | 700 } |
| 664 | 701 |
| 665 if (int(modelZoomLevel) != zoomLevel) { | 702 size_t i0 = (f0 - frame0) / modelZoomLevel; |
| 666 | 703 size_t i1 = (f1 - frame0) / modelZoomLevel; |
| 667 std::cerr << "WaveformLayer::paint: zoom level " << zoomLevel << " differs from model zoom level " << modelZoomLevel << std::endl; | 704 |
| 668 std::cerr << "index from " << index; | 705 #ifdef DEBUG_WAVEFORM_PAINT |
| 669 | 706 std::cerr << "WaveformLayer::paint: pixel " << x << ": i0 " << i0 << " (f " << f0 << "), i1 " << i1 << " (f " << f1 << ")" << std::endl; |
| 670 index = size_t((double(index) * zoomLevel) / modelZoomLevel); | 707 #endif |
| 671 | 708 |
| 672 std::cerr << " to " << index << std::endl; | 709 if (i1 > i0 + 1) { |
| 673 | 710 std::cerr << "WaveformLayer::paint: ERROR: i1 " << i1 << " > i0 " << i0 << " plus one (zoom = " << zoomLevel << ", model zoom = " << modelZoomLevel << ")" << std::endl; |
| 674 if (int(modelZoomLevel) < zoomLevel) { | 711 } |
| 675 // Peaks may be missed! The model should avoid | 712 |
| 676 // this by rounding zoom levels up rather than | 713 if (ranges && i0 < ranges->size()) { |
| 677 // down, but we'd better cope in case it doesn't | 714 |
| 678 maxIndex = index; | 715 range = (*ranges)[i0]; |
| 679 } else { | 716 |
| 680 maxIndex = size_t((double(index + 1) * zoomLevel) | 717 if (i1 > i0 && i1 < ranges->size()) { |
| 681 / modelZoomLevel) - 1; | 718 range.max = std::max(range.max, (*ranges)[i1].max); |
| 682 } | 719 range.min = std::min(range.min, (*ranges)[i1].min); |
| 683 | 720 range.absmean = (range.absmean + (*ranges)[i1].absmean) / 2; |
| 684 std::cerr << "maxIndex = " << maxIndex << std::endl; | |
| 685 } | |
| 686 | |
| 687 if (ranges && index < ranges->size()) { | |
| 688 | |
| 689 range = (*ranges)[index]; | |
| 690 | |
| 691 if (maxIndex > index && maxIndex < ranges->size()) { | |
| 692 range.max = std::max(range.max, (*ranges)[maxIndex].max); | |
| 693 range.min = std::min(range.min, (*ranges)[maxIndex].min); | |
| 694 range.absmean = (range.absmean + | |
| 695 (*ranges)[maxIndex].absmean) / 2; | |
| 696 } | 721 } |
| 697 | 722 |
| 698 } else { | 723 } else { |
| 699 continue; | 724 continue; |
| 700 } | 725 } |
| 701 | 726 |
| 702 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0; | 727 int rangeBottom = 0, rangeTop = 0, meanBottom = 0, meanTop = 0; |
| 703 | 728 |
| 704 if (mergingChannels) { | 729 if (mergingChannels) { |
| 705 | 730 |
| 706 if (otherChannelRanges && index < otherChannelRanges->size()) { | 731 if (otherChannelRanges && i0 < otherChannelRanges->size()) { |
| 707 | 732 |
| 708 range.max = fabsf(range.max); | 733 range.max = fabsf(range.max); |
| 709 range.min = -fabsf((*otherChannelRanges)[index].max); | 734 range.min = -fabsf((*otherChannelRanges)[i0].max); |
| 710 range.absmean = (range.absmean + | 735 range.absmean = (range.absmean + |
| 711 (*otherChannelRanges)[index].absmean) / 2; | 736 (*otherChannelRanges)[i0].absmean) / 2; |
| 712 | 737 |
| 713 if (maxIndex > index && maxIndex < otherChannelRanges->size()) { | 738 if (i1 > i0 && i1 < otherChannelRanges->size()) { |
| 714 // let's not concern ourselves about the mean | 739 // let's not concern ourselves about the mean |
| 715 range.min = std::min | 740 range.min = std::min |
| 716 (range.min, | 741 (range.min, |
| 717 -fabsf((*otherChannelRanges)[maxIndex].max)); | 742 -fabsf((*otherChannelRanges)[i1].max)); |
| 718 } | 743 } |
| 719 } | 744 } |
| 720 | 745 |
| 721 } else if (mixingChannels) { | 746 } else if (mixingChannels) { |
| 722 | 747 |
| 723 if (otherChannelRanges && index < otherChannelRanges->size()) { | 748 if (otherChannelRanges && i0 < otherChannelRanges->size()) { |
| 724 | 749 |
| 725 range.max = (range.max + (*otherChannelRanges)[index].max) / 2; | 750 range.max = (range.max + (*otherChannelRanges)[i0].max) / 2; |
| 726 range.min = (range.min + (*otherChannelRanges)[index].min) / 2; | 751 range.min = (range.min + (*otherChannelRanges)[i0].min) / 2; |
| 727 range.absmean = (range.absmean + (*otherChannelRanges)[index].absmean) / 2; | 752 range.absmean = (range.absmean + (*otherChannelRanges)[i0].absmean) / 2; |
| 728 } | 753 } |
| 729 } | 754 } |
| 730 | 755 |
| 731 int greyLevels = 1; | 756 int greyLevels = 1; |
| 732 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4; | 757 if (m_greyscale && (m_scale == LinearScale)) greyLevels = 4; |
| 895 { | 920 { |
| 896 int x = pos.x(); | 921 int x = pos.x(); |
| 897 | 922 |
| 898 if (!m_model || !m_model->isOK()) return ""; | 923 if (!m_model || !m_model->isOK()) return ""; |
| 899 | 924 |
| 900 long f0 = v->getFrameForX(x); | 925 int zoomLevel = v->getZoomLevel(); |
| 901 long f1 = v->getFrameForX(x + 1); | 926 |
| 902 | 927 size_t modelZoomLevel = m_model->getSummaryBlockSize(zoomLevel); |
| 903 if (f0 < 0) f0 = 0; | 928 |
| 904 if (f1 <= f0) return ""; | 929 size_t f0, f1; |
| 930 if (!getSourceFramesForX(v, x, modelZoomLevel, f0, f1)) return ""; | |
| 905 | 931 |
| 906 QString text; | 932 QString text; |
| 907 | 933 |
| 908 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate()); | 934 RealTime rt0 = RealTime::frame2RealTime(f0, m_model->getSampleRate()); |
| 909 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate()); | 935 RealTime rt1 = RealTime::frame2RealTime(f1, m_model->getSampleRate()); |
