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