comparison layer/SpectrogramLayer.cpp @ 1106:8abdefce36a6 spectrogram-minor-refactor

Remove all of the "old" paint logic from SpectrogramLayer, even where it hasn't been completely replaced yet
author Chris Cannam
date Thu, 14 Jul 2016 17:03:40 +0100
parents ea5ae9dd10ba
children d578b685d912
comparison
equal deleted inserted replaced
1105:ea5ae9dd10ba 1106:8abdefce36a6
125 125
126 Preferences *prefs = Preferences::getInstance(); 126 Preferences *prefs = Preferences::getInstance();
127 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), 127 connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
128 this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); 128 this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
129 setWindowType(prefs->getWindowType()); 129 setWindowType(prefs->getWindowType());
130
131 initialisePalette();
132 } 130 }
133 131
134 SpectrogramLayer::~SpectrogramLayer() 132 SpectrogramLayer::~SpectrogramLayer()
135 { 133 {
136 invalidateImageCaches(); 134 invalidateRenderers();
137 invalidateFFTModel(); 135 invalidateFFTModel();
138 } 136 }
139 137
140 ColourScaleType 138 ColourScaleType
141 SpectrogramLayer::convertToColourScale(int value) 139 SpectrogramLayer::convertToColourScale(int value)
619 setNormalizeVisibleArea(n.second); 617 setNormalizeVisibleArea(n.second);
620 } 618 }
621 } 619 }
622 620
623 void 621 void
624 SpectrogramLayer::invalidateImageCaches() 622 SpectrogramLayer::invalidateRenderers()
625 { 623 {
626 #ifdef DEBUG_SPECTROGRAM 624 #ifdef DEBUG_SPECTROGRAM
627 cerr << "SpectrogramLayer::invalidateImageCaches called" << endl; 625 cerr << "SpectrogramLayer::invalidateRenderers called" << endl;
628 #endif 626 #endif
629 for (ViewImageCache::iterator i = m_imageCaches.begin(); 627
630 i != m_imageCaches.end(); ++i) {
631 i->second.invalidate();
632 }
633
634 //!!!
635 for (ViewRendererMap::iterator i = m_renderers.begin(); 628 for (ViewRendererMap::iterator i = m_renderers.begin();
636 i != m_renderers.end(); ++i) { 629 i != m_renderers.end(); ++i) {
637 delete i->second; 630 delete i->second;
638 } 631 }
639 m_renderers.clear(); 632 m_renderers.clear();
648 setWindowType(Preferences::getInstance()->getWindowType()); 641 setWindowType(Preferences::getInstance()->getWindowType());
649 return; 642 return;
650 } 643 }
651 if (name == "Spectrogram Y Smoothing") { 644 if (name == "Spectrogram Y Smoothing") {
652 setWindowSize(m_windowSize); 645 setWindowSize(m_windowSize);
653 invalidateImageCaches(); 646 invalidateRenderers();
654 invalidateMagnitudes(); 647 invalidateMagnitudes();
655 emit layerParametersChanged(); 648 emit layerParametersChanged();
656 } 649 }
657 if (name == "Spectrogram X Smoothing") { 650 if (name == "Spectrogram X Smoothing") {
658 invalidateImageCaches(); 651 invalidateRenderers();
659 invalidateMagnitudes(); 652 invalidateMagnitudes();
660 emit layerParametersChanged(); 653 emit layerParametersChanged();
661 } 654 }
662 if (name == "Tuning Frequency") { 655 if (name == "Tuning Frequency") {
663 emit layerParametersChanged(); 656 emit layerParametersChanged();
667 void 660 void
668 SpectrogramLayer::setChannel(int ch) 661 SpectrogramLayer::setChannel(int ch)
669 { 662 {
670 if (m_channel == ch) return; 663 if (m_channel == ch) return;
671 664
672 invalidateImageCaches(); 665 invalidateRenderers();
673 m_channel = ch; 666 m_channel = ch;
674 invalidateFFTModel(); 667 invalidateFFTModel();
675 668
676 emit layerParametersChanged(); 669 emit layerParametersChanged();
677 } 670 }
709 void 702 void
710 SpectrogramLayer::setWindowSize(int ws) 703 SpectrogramLayer::setWindowSize(int ws)
711 { 704 {
712 if (m_windowSize == ws) return; 705 if (m_windowSize == ws) return;
713 706
714 invalidateImageCaches(); 707 invalidateRenderers();
715 708
716 m_windowSize = ws; 709 m_windowSize = ws;
717 710
718 invalidateFFTModel(); 711 invalidateFFTModel();
719 712
729 void 722 void
730 SpectrogramLayer::setWindowHopLevel(int v) 723 SpectrogramLayer::setWindowHopLevel(int v)
731 { 724 {
732 if (m_windowHopLevel == v) return; 725 if (m_windowHopLevel == v) return;
733 726
734 invalidateImageCaches(); 727 invalidateRenderers();
735 728
736 m_windowHopLevel = v; 729 m_windowHopLevel = v;
737 730
738 invalidateFFTModel(); 731 invalidateFFTModel();
739 732
751 void 744 void
752 SpectrogramLayer::setWindowType(WindowType w) 745 SpectrogramLayer::setWindowType(WindowType w)
753 { 746 {
754 if (m_windowType == w) return; 747 if (m_windowType == w) return;
755 748
756 invalidateImageCaches(); 749 invalidateRenderers();
757 750
758 m_windowType = w; 751 m_windowType = w;
759 752
760 invalidateFFTModel(); 753 invalidateFFTModel();
761 754
774 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now " 767 // SVDEBUG << "SpectrogramLayer::setGain(" << gain << ") (my gain is now "
775 // << m_gain << ")" << endl; 768 // << m_gain << ")" << endl;
776 769
777 if (m_gain == gain) return; 770 if (m_gain == gain) return;
778 771
779 invalidateImageCaches(); 772 invalidateRenderers();
780 773
781 m_gain = gain; 774 m_gain = gain;
782 775
783 emit layerParametersChanged(); 776 emit layerParametersChanged();
784 } 777 }
792 void 785 void
793 SpectrogramLayer::setThreshold(float threshold) 786 SpectrogramLayer::setThreshold(float threshold)
794 { 787 {
795 if (m_threshold == threshold) return; 788 if (m_threshold == threshold) return;
796 789
797 invalidateImageCaches(); 790 invalidateRenderers();
798 791
799 m_threshold = threshold; 792 m_threshold = threshold;
800 793
801 emit layerParametersChanged(); 794 emit layerParametersChanged();
802 } 795 }
812 { 805 {
813 if (m_minFrequency == mf) return; 806 if (m_minFrequency == mf) return;
814 807
815 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl; 808 // SVDEBUG << "SpectrogramLayer::setMinFrequency: " << mf << endl;
816 809
817 invalidateImageCaches(); 810 invalidateRenderers();
818 invalidateMagnitudes(); 811 invalidateMagnitudes();
819 812
820 m_minFrequency = mf; 813 m_minFrequency = mf;
821 814
822 emit layerParametersChanged(); 815 emit layerParametersChanged();
833 { 826 {
834 if (m_maxFrequency == mf) return; 827 if (m_maxFrequency == mf) return;
835 828
836 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl; 829 // SVDEBUG << "SpectrogramLayer::setMaxFrequency: " << mf << endl;
837 830
838 invalidateImageCaches(); 831 invalidateRenderers();
839 invalidateMagnitudes(); 832 invalidateMagnitudes();
840 833
841 m_maxFrequency = mf; 834 m_maxFrequency = mf;
842 835
843 emit layerParametersChanged(); 836 emit layerParametersChanged();
850 } 843 }
851 844
852 void 845 void
853 SpectrogramLayer::setColourRotation(int r) 846 SpectrogramLayer::setColourRotation(int r)
854 { 847 {
855 invalidateImageCaches(); 848 invalidateRenderers();
856 849
857 if (r < 0) r = 0; 850 if (r < 0) r = 0;
858 if (r > 256) r = 256; 851 if (r > 256) r = 256;
859 int distance = r - m_colourRotation; 852 int distance = r - m_colourRotation;
860 853
861 if (distance != 0) { 854 if (distance != 0) {
862 rotatePalette(-distance); 855 //!!! rotatePalette(-distance);
863 m_colourRotation = r; 856 m_colourRotation = r;
864 } 857 }
865 858
866 emit layerParametersChanged(); 859 emit layerParametersChanged();
867 } 860 }
869 void 862 void
870 SpectrogramLayer::setColourScale(ColourScaleType colourScale) 863 SpectrogramLayer::setColourScale(ColourScaleType colourScale)
871 { 864 {
872 if (m_colourScale == colourScale) return; 865 if (m_colourScale == colourScale) return;
873 866
874 invalidateImageCaches(); 867 invalidateRenderers();
875 868
876 m_colourScale = colourScale; 869 m_colourScale = colourScale;
877 870
878 emit layerParametersChanged(); 871 emit layerParametersChanged();
879 } 872 }
887 void 880 void
888 SpectrogramLayer::setColourMap(int map) 881 SpectrogramLayer::setColourMap(int map)
889 { 882 {
890 if (m_colourMap == map) return; 883 if (m_colourMap == map) return;
891 884
892 invalidateImageCaches(); 885 invalidateRenderers();
893 886
894 m_colourMap = map; 887 m_colourMap = map;
895 initialisePalette();
896 888
897 emit layerParametersChanged(); 889 emit layerParametersChanged();
898 } 890 }
899 891
900 int 892 int
906 void 898 void
907 SpectrogramLayer::setBinScale(BinScale binScale) 899 SpectrogramLayer::setBinScale(BinScale binScale)
908 { 900 {
909 if (m_binScale == binScale) return; 901 if (m_binScale == binScale) return;
910 902
911 invalidateImageCaches(); 903 invalidateRenderers();
912 m_binScale = binScale; 904 m_binScale = binScale;
913 905
914 emit layerParametersChanged(); 906 emit layerParametersChanged();
915 } 907 }
916 908
923 void 915 void
924 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay) 916 SpectrogramLayer::setBinDisplay(BinDisplay binDisplay)
925 { 917 {
926 if (m_binDisplay == binDisplay) return; 918 if (m_binDisplay == binDisplay) return;
927 919
928 invalidateImageCaches(); 920 invalidateRenderers();
929 m_binDisplay = binDisplay; 921 m_binDisplay = binDisplay;
930 922
931 emit layerParametersChanged(); 923 emit layerParametersChanged();
932 } 924 }
933 925
940 void 932 void
941 SpectrogramLayer::setNormalization(ColumnNormalization n) 933 SpectrogramLayer::setNormalization(ColumnNormalization n)
942 { 934 {
943 if (m_normalization == n) return; 935 if (m_normalization == n) return;
944 936
945 invalidateImageCaches(); 937 invalidateRenderers();
946 invalidateMagnitudes(); 938 invalidateMagnitudes();
947 m_normalization = n; 939 m_normalization = n;
948 940
949 emit layerParametersChanged(); 941 emit layerParametersChanged();
950 } 942 }
958 void 950 void
959 SpectrogramLayer::setNormalizeVisibleArea(bool n) 951 SpectrogramLayer::setNormalizeVisibleArea(bool n)
960 { 952 {
961 if (m_normalizeVisibleArea == n) return; 953 if (m_normalizeVisibleArea == n) return;
962 954
963 invalidateImageCaches(); 955 invalidateRenderers();
964 invalidateMagnitudes(); 956 invalidateMagnitudes();
965 m_normalizeVisibleArea = n; 957 m_normalizeVisibleArea = n;
966 958
967 emit layerParametersChanged(); 959 emit layerParametersChanged();
968 } 960 }
987 return; 979 return;
988 } 980 }
989 981
990 Layer::setLayerDormant(v, true); 982 Layer::setLayerDormant(v, true);
991 983
992 const View *view = v->getView(); 984 invalidateRenderers();
993
994 invalidateImageCaches();
995
996 m_imageCaches.erase(view->getId());
997 985
998 //!!! in theory we should call invalidateFFTModel() if and 986 //!!! in theory we should call invalidateFFTModel() if and
999 //!!! only if there are no remaining views in which we are not 987 //!!! only if there are no remaining views in which we are not
1000 //!!! dormant 988 //!!! dormant
1001 989
1010 { 998 {
1011 #ifdef DEBUG_SPECTROGRAM_REPAINT 999 #ifdef DEBUG_SPECTROGRAM_REPAINT
1012 cerr << "SpectrogramLayer::cacheInvalid()" << endl; 1000 cerr << "SpectrogramLayer::cacheInvalid()" << endl;
1013 #endif 1001 #endif
1014 1002
1015 invalidateImageCaches(); 1003 invalidateRenderers();
1016 invalidateMagnitudes(); 1004 invalidateMagnitudes();
1017 } 1005 }
1018 1006
1019 void 1007 void
1020 SpectrogramLayer::cacheInvalid( 1008 SpectrogramLayer::cacheInvalid(
1033 // only those caches whose views contained some of the (from, to) 1021 // only those caches whose views contained some of the (from, to)
1034 // range. That's the right thing to do; it has been lost in 1022 // range. That's the right thing to do; it has been lost in
1035 // pulling out the image cache code, but it might not matter very 1023 // pulling out the image cache code, but it might not matter very
1036 // much, since the underlying models for spectrogram layers don't 1024 // much, since the underlying models for spectrogram layers don't
1037 // change very often. Let's see. 1025 // change very often. Let's see.
1038 invalidateImageCaches(); 1026 invalidateRenderers();
1039 invalidateMagnitudes(); 1027 invalidateMagnitudes();
1040 } 1028 }
1041 1029
1042 bool 1030 bool
1043 SpectrogramLayer::hasLightBackground() const 1031 SpectrogramLayer::hasLightBackground() const
1044 { 1032 {
1045 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground(); 1033 return ColourMapper(m_colourMap, 1.f, 255.f).hasLightBackground();
1046 }
1047
1048 void
1049 SpectrogramLayer::initialisePalette()
1050 {
1051 int formerRotation = m_colourRotation;
1052
1053 if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
1054 m_palette.setColour(NO_VALUE, Qt::white);
1055 } else {
1056 m_palette.setColour(NO_VALUE, Qt::black);
1057 }
1058
1059 ColourMapper mapper(m_colourMap, 1.f, 255.f);
1060
1061 for (int pixel = 1; pixel < 256; ++pixel) {
1062 m_palette.setColour((unsigned char)pixel, mapper.map(pixel));
1063 }
1064
1065 m_crosshairColour = mapper.getContrastingColour();
1066
1067 m_colourRotation = 0;
1068 rotatePalette(m_colourRotation - formerRotation);
1069 m_colourRotation = formerRotation;
1070
1071 m_drawBuffer = QImage();
1072 }
1073
1074 void
1075 SpectrogramLayer::rotatePalette(int distance)
1076 {
1077 QColor newPixels[256];
1078
1079 newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
1080
1081 for (int pixel = 1; pixel < 256; ++pixel) {
1082 int target = pixel + distance;
1083 while (target < 1) target += 255;
1084 while (target > 255) target -= 255;
1085 newPixels[target] = m_palette.getColour((unsigned char)pixel);
1086 }
1087
1088 for (int pixel = 0; pixel < 256; ++pixel) {
1089 m_palette.setColour((unsigned char)pixel, newPixels[pixel]);
1090 }
1091
1092 m_drawBuffer = QImage();
1093 }
1094
1095 unsigned char
1096 SpectrogramLayer::getDisplayValue(LayerGeometryProvider *v, double input) const
1097 {
1098 int value = 0;
1099
1100 double min = 0.0;
1101 double max = 1.0;
1102
1103 if (m_normalizeVisibleArea) {
1104 min = m_viewMags[v->getId()].getMin();
1105 max = m_viewMags[v->getId()].getMax();
1106 } else if (m_normalization != ColumnNormalization::Max1) {
1107 if (m_colourScale == ColourScaleType::Linear //||
1108 // m_colourScale == ColourScaleType::Meter) {
1109 ) {
1110 max = 0.1;
1111 }
1112 }
1113
1114 double thresh = -80.0;
1115
1116 if (max == 0.0) max = 1.0;
1117 if (max == min) min = max - 0.0001;
1118
1119 switch (m_colourScale) {
1120
1121 case ColourScaleType::Linear:
1122 value = int(((input - min) / (max - min)) * 255.0) + 1;
1123 break;
1124
1125 case ColourScaleType::Meter:
1126 value = AudioLevel::multiplier_to_preview
1127 ((input - min) / (max - min), 254) + 1;
1128 break;
1129
1130 //!!! check this
1131 /* case dBSquaredColourScale:
1132 input = ((input - min) * (input - min)) / ((max - min) * (max - min));
1133 if (input > 0.0) {
1134 input = 10.0 * log10(input);
1135 } else {
1136 input = thresh;
1137 }
1138 if (min > 0.0) {
1139 thresh = 10.0 * log10(min * min);
1140 if (thresh < -80.0) thresh = -80.0;
1141 }
1142 input = (input - thresh) / (-thresh);
1143 if (input < 0.0) input = 0.0;
1144 if (input > 1.0) input = 1.0;
1145 value = int(input * 255.0) + 1;
1146 break;
1147 */
1148 case ColourScaleType::Log:
1149 //!!! experiment with normalizing the visible area this way.
1150 //In any case, we need to have some indication of what the dB
1151 //scale is relative to.
1152 input = (input - min) / (max - min);
1153 if (input > 0.0) {
1154 input = 10.0 * log10(input);
1155 } else {
1156 input = thresh;
1157 }
1158 if (min > 0.0) {
1159 thresh = 10.0 * log10(min);
1160 if (thresh < -80.0) thresh = -80.0;
1161 }
1162 input = (input - thresh) / (-thresh);
1163 if (input < 0.0) input = 0.0;
1164 if (input > 1.0) input = 1.0;
1165 value = int(input * 255.0) + 1;
1166 break;
1167
1168 case ColourScaleType::Phase:
1169 value = int((input * 127.0 / M_PI) + 128);
1170 break;
1171
1172 case ColourScaleType::PlusMinusOne:
1173 case ColourScaleType::Absolute:
1174 default:
1175 ;
1176 }
1177
1178 if (value > UCHAR_MAX) value = UCHAR_MAX;
1179 if (value < 0) value = 0;
1180 return (unsigned char)value;
1181 } 1034 }
1182 1035
1183 double 1036 double
1184 SpectrogramLayer::getEffectiveMinFrequency() const 1037 SpectrogramLayer::getEffectiveMinFrequency() const
1185 { 1038 {
1597 SpectrogramLayer::setSynchronousPainting(bool synchronous) 1450 SpectrogramLayer::setSynchronousPainting(bool synchronous)
1598 { 1451 {
1599 m_synchronous = synchronous; 1452 m_synchronous = synchronous;
1600 } 1453 }
1601 1454
1602 ScrollableImageCache &
1603 SpectrogramLayer::getImageCacheReference(const LayerGeometryProvider *view) const
1604 {
1605 //!!! to go?
1606 if (m_imageCaches.find(view->getId()) == m_imageCaches.end()) {
1607 m_imageCaches[view->getId()] = ScrollableImageCache();
1608 }
1609 return m_imageCaches.at(view->getId());
1610 }
1611
1612 void
1613 SpectrogramLayer::paintAlternative(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1614 {
1615 static int depth = 0;
1616
1617 Colour3DPlotRenderer *renderer = getRenderer(v);
1618
1619 if (m_synchronous) {
1620 (void)renderer->render(v, paint, rect);
1621 return;
1622 }
1623
1624 ++depth;
1625 cerr << "paint depth " << depth << endl;
1626
1627 (void)renderer->renderTimeConstrained(v, paint, rect);
1628
1629 //!!! + mag range
1630
1631 QRect uncached = renderer->getLargestUncachedRect();
1632 if (uncached.width() > 0) {
1633 cerr << "updating rect at " << uncached.x() << " width "
1634 << uncached.width() << endl;
1635 v->updatePaintRect(uncached);
1636 }
1637
1638 cerr << "exiting paint depth " << depth << endl;
1639 --depth;
1640 }
1641
1642 Colour3DPlotRenderer * 1455 Colour3DPlotRenderer *
1643 SpectrogramLayer::getRenderer(LayerGeometryProvider *v) const 1456 SpectrogramLayer::getRenderer(LayerGeometryProvider *v) const
1644 { 1457 {
1645 if (m_renderers.find(v->getId()) == m_renderers.end()) { 1458 if (m_renderers.find(v->getId()) == m_renderers.end()) {
1646 1459
1680 1493
1681 return m_renderers[v->getId()]; 1494 return m_renderers[v->getId()];
1682 } 1495 }
1683 1496
1684 void 1497 void
1498 SpectrogramLayer::paintWithRenderer(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1499 {
1500 static int depth = 0;
1501
1502 Colour3DPlotRenderer *renderer = getRenderer(v);
1503
1504 if (m_synchronous) {
1505 (void)renderer->render(v, paint, rect);
1506 return;
1507 }
1508
1509 ++depth;
1510 cerr << "paint depth " << depth << endl;
1511
1512 (void)renderer->renderTimeConstrained(v, paint, rect);
1513
1514 //!!! + mag range
1515
1516 QRect uncached = renderer->getLargestUncachedRect();
1517 if (uncached.width() > 0) {
1518 cerr << "updating rect at " << uncached.x() << " width "
1519 << uncached.width() << endl;
1520 v->updatePaintRect(uncached);
1521 }
1522
1523 cerr << "exiting paint depth " << depth << endl;
1524 --depth;
1525 }
1526
1527 void
1685 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const 1528 SpectrogramLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
1686 { 1529 {
1687 Profiler profiler("SpectrogramLayer::paint", false); 1530 Profiler profiler("SpectrogramLayer::paint", false);
1688 1531
1689 #ifdef DEBUG_SPECTROGRAM_REPAINT 1532 #ifdef DEBUG_SPECTROGRAM_REPAINT
1690 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl; 1533 cerr << "SpectrogramLayer::paint() entering: m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << endl;
1691 1534
1692 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl; 1535 cerr << "SpectrogramLayer::paint(): rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
1693 #endif 1536 #endif
1694 1537
1695 sv_frame_t startFrame = v->getStartFrame();
1696
1697 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1538 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1698 return; 1539 return;
1699 } 1540 }
1700 1541
1701 if (isLayerDormant(v)) { 1542 if (isLayerDormant(v)) {
1702 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl; 1543 SVDEBUG << "SpectrogramLayer::paint(): Layer is dormant, making it undormant again" << endl;
1703 } 1544 }
1704 1545
1705 paintAlternative(v, paint, rect); 1546 paintWithRenderer(v, paint, rect);
1706 return;
1707
1708 //!!!
1709
1710 // Need to do this even if !isLayerDormant, as that could mean v
1711 // is not in the dormancy map at all -- we need it to be present
1712 // and accountable for when determining whether we need the cache
1713 // in the cache-fill thread above.
1714 //!!! no inter use cache-fill thread
1715 const_cast<SpectrogramLayer *>(this)->Layer::setLayerDormant(v, false);
1716
1717 int fftSize = getFFTSize();
1718
1719 const View *view = v->getView();
1720 ScrollableImageCache &cache = getImageCacheReference(view);
1721
1722 #ifdef DEBUG_SPECTROGRAM_REPAINT
1723 cerr << "SpectrogramLayer::paint(): image cache valid area from " << cache.getValidLeft() << " width " << cache.getValidWidth() << ", height " << cache.getSize().height() << endl;
1724 if (rect.x() + rect.width() + 1 < cache.getValidLeft() ||
1725 rect.x() > cache.getValidRight()) {
1726 cerr << "SpectrogramLayer: NOTE: requested rect is not contiguous with cache valid area" << endl;
1727 }
1728 #endif
1729
1730 int zoomLevel = v->getZoomLevel();
1731
1732 int x0 = v->getXForViewX(rect.x());
1733 int x1 = v->getXForViewX(rect.x() + rect.width());
1734 if (x0 < 0) x0 = 0;
1735 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
1736
1737 if (updateViewMagnitudes(v)) {
1738 #ifdef DEBUG_SPECTROGRAM_REPAINT
1739 cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
1740 #endif
1741 if (m_normalizeVisibleArea) {
1742 cache.invalidate();
1743 }
1744 }
1745
1746 if (cache.getZoomLevel() != zoomLevel ||
1747 cache.getSize() != v->getPaintSize()) {
1748 #ifdef DEBUG_SPECTROGRAM_REPAINT
1749 cerr << "SpectrogramLayer: resizing image cache from "
1750 << cache.getSize().width() << "x" << cache.getSize().height()
1751 << " to "
1752 << v->getPaintSize().width() << "x" << v->getPaintSize().height()
1753 << " and updating zoom level from " << cache.getZoomLevel()
1754 << " to " << zoomLevel
1755 << endl;
1756 #endif
1757 cache.resize(v->getPaintSize());
1758 cache.setZoomLevel(zoomLevel);
1759 cache.setStartFrame(startFrame);
1760 }
1761
1762 if (cache.isValid()) {
1763
1764 if (v->getXForFrame(cache.getStartFrame()) ==
1765 v->getXForFrame(startFrame) &&
1766 cache.getValidLeft() <= x0 &&
1767 cache.getValidRight() >= x1) {
1768
1769 #ifdef DEBUG_SPECTROGRAM_REPAINT
1770 cerr << "SpectrogramLayer: image cache hit!" << endl;
1771 #endif
1772
1773 paint.drawImage(rect, cache.getImage(), rect);
1774
1775 illuminateLocalFeatures(v, paint);
1776 return;
1777
1778 } else {
1779
1780 // cache doesn't begin at the right frame or doesn't
1781 // contain the complete view, but might be scrollable or
1782 // partially usable
1783
1784 #ifdef DEBUG_SPECTROGRAM_REPAINT
1785 cerr << "SpectrogramLayer: scrolling the image cache if applicable" << endl;
1786 #endif
1787
1788 cache.scrollTo(v, startFrame);
1789
1790 #ifdef DEBUG_SPECTROGRAM_REPAINT
1791 cerr << "SpectrogramLayer: after scrolling, cache valid from "
1792 << cache.getValidLeft() << " width " << cache.getValidWidth()
1793 << endl;
1794 #endif
1795 }
1796 }
1797
1798 bool rightToLeft = false;
1799
1800 if (!cache.isValid()) {
1801 if (!m_synchronous) {
1802 // When rendering the whole thing, start from somewhere near
1803 // the middle so that the region of interest appears first
1804
1805 //!!! (perhaps we should have some cunning test to avoid
1806 //!!! doing this if past repaints have appeared fast
1807 //!!! enough to do the whole width in one shot)
1808 if (x0 == 0 && x1 == v->getPaintWidth()) {
1809 x0 = int(x1 * 0.3);
1810 }
1811 }
1812 } else {
1813 // When rendering only a part of the cache, we need to make
1814 // sure that the part we're rendering is adjacent to (or
1815 // overlapping) a valid area of cache, if we have one. The
1816 // alternative is to ditch the valid area of cache and render
1817 // only the requested area, but that's risky because this can
1818 // happen when just waving the pointer over a small part of
1819 // the view -- if we lose the partly-built cache every time
1820 // the user does that, we'll never finish building it.
1821 int left = x0;
1822 int width = x1 - x0;
1823 bool isLeftOfValidArea = false;
1824 cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
1825 x0 = left;
1826 x1 = x0 + width;
1827
1828 // That call also told us whether we should be painting
1829 // sub-regions of our target region in right-to-left order in
1830 // order to ensure contiguity
1831 rightToLeft = isLeftOfValidArea;
1832 }
1833
1834 // We always paint the full height when refreshing the cache.
1835 // Smaller heights can be used when painting direct from cache
1836 // (further up in this function), but we want to ensure the cache
1837 // is coherent without having to worry about vertical matching of
1838 // required and valid areas as well as horizontal.
1839 int h = v->getPaintHeight();
1840
1841 int repaintWidth = x1 - x0;
1842
1843 #ifdef DEBUG_SPECTROGRAM_REPAINT
1844 cerr << "SpectrogramLayer: x0 " << x0 << ", x1 " << x1
1845 << ", repaintWidth " << repaintWidth << ", h " << h
1846 << ", rightToLeft " << rightToLeft << endl;
1847 #endif
1848
1849 sv_samplerate_t sr = m_model->getSampleRate();
1850
1851 // Set minFreq and maxFreq to the frequency extents of the possibly
1852 // zero-padded visible bin range, and displayMinFreq and displayMaxFreq
1853 // to the actual scale frequency extents (presumably not zero padded).
1854
1855 // If we are zero padding (i.e. oversampling) we want to use the
1856 // zero-padded equivalents of the bins that we would be using if
1857 // not zero padded, to avoid spaces at the top and bottom of the
1858 // display.
1859
1860 int maxbin = fftSize / 2;
1861 if (m_maxFrequency > 0) {
1862 maxbin = int((double(m_maxFrequency) * fftSize) / sr + 0.001);
1863 if (maxbin > fftSize / 2) maxbin = fftSize / 2;
1864 }
1865
1866 int minbin = 1;
1867 if (m_minFrequency > 0) {
1868 minbin = int((double(m_minFrequency) * fftSize) / sr + 0.001);
1869 // cerr << "m_minFrequency = " << m_minFrequency << " -> minbin = " << minbin << endl;
1870 if (minbin < 1) minbin = 1;
1871 if (minbin >= maxbin) minbin = maxbin - 1;
1872 }
1873
1874 int over = getFFTOversampling();
1875 minbin = minbin * over;
1876 maxbin = (maxbin + 1) * over - 1;
1877
1878 double minFreq = (double(minbin) * sr) / fftSize;
1879 double maxFreq = (double(maxbin) * sr) / fftSize;
1880
1881 double displayMinFreq = minFreq;
1882 double displayMaxFreq = maxFreq;
1883
1884 //!!! if (fftSize != getFFTSize()) {
1885 // displayMinFreq = getEffectiveMinFrequency();
1886 // displayMaxFreq = getEffectiveMaxFrequency();
1887 // }
1888
1889 // cerr << "(giving actual minFreq " << minFreq << " and display minFreq " << displayMinFreq << ")" << endl;
1890
1891 int increment = getWindowIncrement();
1892
1893 bool logarithmic = (m_binScale == BinScale::Log);
1894
1895 MagnitudeRange overallMag = m_viewMags[v->getId()];
1896 bool overallMagChanged = false;
1897
1898 #ifdef DEBUG_SPECTROGRAM_REPAINT
1899 cerr << "SpectrogramLayer: " << ((double(v->getFrameForX(1) - v->getFrameForX(0))) / increment) << " bin(s) per pixel" << endl;
1900 #endif
1901
1902 if (repaintWidth == 0) {
1903 SVDEBUG << "*** NOTE: repaintWidth == 0" << endl;
1904 }
1905
1906 Profiler outerprof("SpectrogramLayer::paint: all cols");
1907
1908 // The draw buffer contains a fragment at either our pixel
1909 // resolution (if there is more than one time-bin per pixel) or
1910 // time-bin resolution (if a time-bin spans more than one pixel).
1911 // We need to ensure that it starts and ends at points where a
1912 // time-bin boundary occurs at an exact pixel boundary, and with a
1913 // certain amount of overlap across existing pixels so that we can
1914 // scale and draw from it without smoothing errors at the edges.
1915
1916 // If (getFrameForX(x) / increment) * increment ==
1917 // getFrameForX(x), then x is a time-bin boundary. We want two
1918 // such boundaries at either side of the draw buffer -- one which
1919 // we draw up to, and one which we subsequently crop at.
1920
1921 bool bufferIsBinResolution = false;
1922 if (increment > zoomLevel) bufferIsBinResolution = true;
1923
1924 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
1925 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
1926
1927 int bufwid;
1928
1929 if (bufferIsBinResolution) {
1930
1931 for (int x = x0; ; --x) {
1932 sv_frame_t f = v->getFrameForX(x);
1933 if ((f / increment) * increment == f) {
1934 if (leftCropFrame == -1) leftCropFrame = f;
1935 else if (x < x0 - 2) {
1936 leftBoundaryFrame = f;
1937 break;
1938 }
1939 }
1940 }
1941 for (int x = x0 + repaintWidth; ; ++x) {
1942 sv_frame_t f = v->getFrameForX(x);
1943 if ((f / increment) * increment == f) {
1944 if (rightCropFrame == -1) rightCropFrame = f;
1945 else if (x > x0 + repaintWidth + 2) {
1946 rightBoundaryFrame = f;
1947 break;
1948 }
1949 }
1950 }
1951 #ifdef DEBUG_SPECTROGRAM_REPAINT
1952 cerr << "Left: crop: " << leftCropFrame << " (bin " << leftCropFrame/increment << "); boundary: " << leftBoundaryFrame << " (bin " << leftBoundaryFrame/increment << ")" << endl;
1953 cerr << "Right: crop: " << rightCropFrame << " (bin " << rightCropFrame/increment << "); boundary: " << rightBoundaryFrame << " (bin " << rightBoundaryFrame/increment << ")" << endl;
1954 #endif
1955
1956 bufwid = int((rightBoundaryFrame - leftBoundaryFrame) / increment);
1957
1958 } else {
1959
1960 bufwid = repaintWidth;
1961 }
1962
1963 vector<int> binforx(bufwid);
1964 vector<double> binfory(h);
1965
1966 bool usePeaksCache = false;
1967
1968 if (bufferIsBinResolution) {
1969 for (int x = 0; x < bufwid; ++x) {
1970 binforx[x] = int(leftBoundaryFrame / increment) + x;
1971 }
1972 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
1973 } else {
1974 for (int x = 0; x < bufwid; ++x) {
1975 double s0 = 0, s1 = 0;
1976 if (getXBinRange(v, x + x0, s0, s1)) {
1977 binforx[x] = int(s0 + 0.0001);
1978 } else {
1979 binforx[x] = -1; //???
1980 }
1981 }
1982 if (m_drawBuffer.width() < bufwid || m_drawBuffer.height() != h) {
1983 m_drawBuffer = QImage(bufwid, h, QImage::Format_Indexed8);
1984 }
1985 usePeaksCache = (increment * m_peakCacheDivisor) < zoomLevel;
1986 if (m_colourScale == ColourScaleType::Phase) usePeaksCache = false;
1987 }
1988
1989 for (int pixel = 0; pixel < 256; ++pixel) {
1990 m_drawBuffer.setColor((unsigned char)pixel,
1991 m_palette.getColour((unsigned char)pixel).rgb());
1992 }
1993
1994 m_drawBuffer.fill(0);
1995 int attainedBufwid = bufwid;
1996
1997 double softTimeLimit;
1998
1999 if (m_synchronous) {
2000
2001 // must paint the whole thing for synchronous mode, so give
2002 // "no timeout"
2003 softTimeLimit = 0.0;
2004
2005 } else if (bufferIsBinResolution) {
2006
2007 // calculating boundaries later will be too fiddly for partial
2008 // paints, and painting should be fast anyway when this is the
2009 // case because it means we're well zoomed in
2010 softTimeLimit = 0.0;
2011
2012 } else {
2013
2014 // neither limitation applies, so use a short soft limit
2015
2016 if (m_binDisplay == BinDisplay::PeakFrequencies) {
2017 softTimeLimit = 0.15;
2018 } else {
2019 softTimeLimit = 0.1;
2020 }
2021 }
2022
2023 if (m_binDisplay != BinDisplay::PeakFrequencies) {
2024
2025 for (int y = 0; y < h; ++y) {
2026 double q0 = 0, q1 = 0;
2027 if (!getYBinRange(v, h-y-1, q0, q1)) {
2028 binfory[y] = -1;
2029 } else {
2030 binfory[y] = q0;
2031 }
2032 }
2033
2034 attainedBufwid =
2035 paintDrawBuffer(v, bufwid, h, binforx, binfory,
2036 usePeaksCache,
2037 overallMag, overallMagChanged,
2038 rightToLeft,
2039 softTimeLimit);
2040
2041 } else {
2042
2043 attainedBufwid =
2044 paintDrawBufferPeakFrequencies(v, bufwid, h, binforx,
2045 minbin, maxbin,
2046 displayMinFreq, displayMaxFreq,
2047 logarithmic,
2048 overallMag, overallMagChanged,
2049 rightToLeft,
2050 softTimeLimit);
2051 }
2052
2053 int failedToRepaint = bufwid - attainedBufwid;
2054
2055 int paintedLeft = x0;
2056 int paintedWidth = x1 - x0;
2057
2058 if (failedToRepaint > 0) {
2059
2060 #ifdef DEBUG_SPECTROGRAM_REPAINT
2061 cerr << "SpectrogramLayer::paint(): Failed to repaint " << failedToRepaint << " of " << bufwid
2062 << " columns in time (so managed to repaint " << bufwid - failedToRepaint << ")" << endl;
2063 #endif
2064
2065 if (rightToLeft) {
2066 paintedLeft += failedToRepaint;
2067 }
2068
2069 paintedWidth -= failedToRepaint;
2070
2071 if (paintedWidth < 0) {
2072 paintedWidth = 0;
2073 }
2074
2075 } else if (failedToRepaint < 0) {
2076 cerr << "WARNING: failedToRepaint < 0 (= " << failedToRepaint << ")"
2077 << endl;
2078 failedToRepaint = 0;
2079 }
2080
2081 if (overallMagChanged) {
2082 m_viewMags[v->getId()] = overallMag;
2083 #ifdef DEBUG_SPECTROGRAM_REPAINT
2084 cerr << "SpectrogramLayer: Overall mag is now [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "] - will be updating" << endl;
2085 #endif
2086 }
2087
2088 outerprof.end();
2089
2090 Profiler profiler2("SpectrogramLayer::paint: draw image");
2091
2092 if (paintedWidth > 0) {
2093
2094 #ifdef DEBUG_SPECTROGRAM_REPAINT
2095 cerr << "SpectrogramLayer: Copying " << paintedWidth << "x" << h
2096 << " from draw buffer at " << paintedLeft - x0 << "," << 0
2097 << " to " << paintedWidth << "x" << h << " on cache at "
2098 << x0 << "," << 0 << endl;
2099 #endif
2100
2101 if (bufferIsBinResolution) {
2102
2103 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
2104 int scaledRight = v->getXForFrame(rightBoundaryFrame);
2105
2106 #ifdef DEBUG_SPECTROGRAM_REPAINT
2107 cerr << "SpectrogramLayer: Rescaling image from " << bufwid
2108 << "x" << h << " to "
2109 << scaledRight-scaledLeft << "x" << h << endl;
2110 #endif
2111
2112 Preferences::SpectrogramXSmoothing xsmoothing =
2113 Preferences::getInstance()->getSpectrogramXSmoothing();
2114
2115 QImage scaled = m_drawBuffer.scaled
2116 (scaledRight - scaledLeft, h,
2117 Qt::IgnoreAspectRatio,
2118 ((xsmoothing == Preferences::SpectrogramXInterpolated) ?
2119 Qt::SmoothTransformation : Qt::FastTransformation));
2120
2121 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
2122 int scaledRightCrop = v->getXForFrame(rightCropFrame);
2123
2124 #ifdef DEBUG_SPECTROGRAM_REPAINT
2125 cerr << "SpectrogramLayer: Drawing image region of width " << scaledRightCrop - scaledLeftCrop << " to "
2126 << scaledLeftCrop << " from " << scaledLeftCrop - scaledLeft << endl;
2127 #endif
2128
2129 int targetLeft = scaledLeftCrop;
2130 if (targetLeft < 0) {
2131 targetLeft = 0;
2132 }
2133
2134 int targetWidth = scaledRightCrop - targetLeft;
2135 if (targetLeft + targetWidth > cache.getSize().width()) {
2136 targetWidth = cache.getSize().width() - targetLeft;
2137 }
2138
2139 int sourceLeft = targetLeft - scaledLeft;
2140 if (sourceLeft < 0) {
2141 sourceLeft = 0;
2142 }
2143
2144 int sourceWidth = targetWidth;
2145
2146 if (targetWidth > 0) {
2147 cache.drawImage
2148 (targetLeft,
2149 targetWidth,
2150 scaled,
2151 sourceLeft,
2152 sourceWidth);
2153 }
2154
2155 } else {
2156
2157 cache.drawImage(paintedLeft, paintedWidth,
2158 m_drawBuffer,
2159 paintedLeft - x0, paintedWidth);
2160 }
2161 }
2162
2163 #ifdef DEBUG_SPECTROGRAM_REPAINT
2164 cerr << "SpectrogramLayer: Cache valid area now from " << cache.getValidLeft()
2165 << " width " << cache.getValidWidth() << ", height "
2166 << cache.getSize().height() << endl;
2167 #endif
2168
2169 QRect pr = rect & cache.getValidArea();
2170
2171 #ifdef DEBUG_SPECTROGRAM_REPAINT
2172 cerr << "SpectrogramLayer: Copying " << pr.width() << "x" << pr.height()
2173 << " from cache at " << pr.x() << "," << pr.y()
2174 << " to window" << endl;
2175 #endif
2176
2177 paint.drawImage(pr.x(), pr.y(), cache.getImage(),
2178 pr.x(), pr.y(), pr.width(), pr.height());
2179
2180 if (!m_synchronous) {
2181
2182 if (!m_normalizeVisibleArea || !overallMagChanged) {
2183
2184 QRect areaLeft(0, 0, cache.getValidLeft(), h);
2185 QRect areaRight(cache.getValidRight(), 0,
2186 cache.getSize().width() - cache.getValidRight(), h);
2187
2188 bool haveSpaceLeft = (areaLeft.width() > 0);
2189 bool haveSpaceRight = (areaRight.width() > 0);
2190
2191 bool updateLeft = haveSpaceLeft;
2192 bool updateRight = haveSpaceRight;
2193
2194 if (updateLeft && updateRight) {
2195 if (rightToLeft) {
2196 // we just did something adjoining the cache on
2197 // its left side, so now do something on its right
2198 updateLeft = false;
2199 } else {
2200 updateRight = false;
2201 }
2202 }
2203
2204 if (updateLeft) {
2205 #ifdef DEBUG_SPECTROGRAM_REPAINT
2206 cerr << "SpectrogramLayer::paint() updating left ("
2207 << areaLeft.x() << ", "
2208 << areaLeft.width() << ")" << endl;
2209 #endif
2210 v->updatePaintRect(areaLeft);
2211 }
2212
2213 if (updateRight) {
2214 #ifdef DEBUG_SPECTROGRAM_REPAINT
2215 cerr << "SpectrogramLayer::paint() updating right ("
2216 << areaRight.x() << ", "
2217 << areaRight.width() << ")" << endl;
2218 #endif
2219 v->updatePaintRect(areaRight);
2220 }
2221
2222 } else {
2223 // overallMagChanged
2224 cerr << "\noverallMagChanged - updating all\n" << endl;
2225 cache.invalidate();
2226 v->updatePaintRect(v->getPaintRect());
2227 }
2228 }
2229
2230 illuminateLocalFeatures(v, paint);
2231
2232 #ifdef DEBUG_SPECTROGRAM_REPAINT
2233 cerr << "SpectrogramLayer::paint() returning" << endl;
2234 #endif
2235 }
2236
2237 int
2238 SpectrogramLayer::paintDrawBufferPeakFrequencies(LayerGeometryProvider *v,
2239 int w,
2240 int h,
2241 const vector<int> &binforx,
2242 int minbin,
2243 int maxbin,
2244 double displayMinFreq,
2245 double displayMaxFreq,
2246 bool logarithmic,
2247 MagnitudeRange &overallMag,
2248 bool &overallMagChanged,
2249 bool rightToLeft,
2250 double softTimeLimit) const
2251 {
2252 Profiler profiler("SpectrogramLayer::paintDrawBufferPeakFrequencies");
2253
2254 #ifdef DEBUG_SPECTROGRAM_REPAINT
2255 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2256 #endif
2257 if (minbin < 0) minbin = 0;
2258 if (maxbin < 0) maxbin = minbin+1;
2259
2260 FFTModel *fft = getFFTModel();
2261 if (!fft) return 0;
2262
2263 FFTModel::PeakSet peakfreqs;
2264 vector<float> preparedColumn;
2265
2266 int psx = -1;
2267
2268 int minColumns = 4;
2269 bool haveTimeLimits = (softTimeLimit > 0.0);
2270 double hardTimeLimit = softTimeLimit * 2.0;
2271 bool overridingSoftLimit = false;
2272 auto startTime = chrono::steady_clock::now();
2273
2274 int start = 0;
2275 int finish = w;
2276 int step = 1;
2277
2278 if (rightToLeft) {
2279 start = w-1;
2280 finish = -1;
2281 step = -1;
2282 }
2283
2284 int columnCount = 0;
2285
2286 for (int x = start; x != finish; x += step) {
2287
2288 ++columnCount;
2289
2290 if (binforx[x] < 0) continue;
2291
2292 int sx0 = binforx[x];
2293 int sx1 = sx0;
2294 if (x+1 < w) sx1 = binforx[x+1];
2295 if (sx0 < 0) sx0 = sx1 - 1;
2296 if (sx0 < 0) continue;
2297 if (sx1 <= sx0) sx1 = sx0 + 1;
2298
2299 vector<float> pixelPeakColumn;
2300
2301 for (int sx = sx0; sx < sx1; ++sx) {
2302
2303 if (sx < 0 || sx >= int(fft->getWidth())) {
2304 continue;
2305 }
2306
2307 if (sx != psx) {
2308
2309 ColumnOp::Column column;
2310
2311 column = getColumnFromFFTModel(fft,
2312 sx,
2313 minbin,
2314 maxbin - minbin + 1);
2315
2316 if (m_colourScale != ColourScaleType::Phase) {
2317 column = ColumnOp::fftScale(column, getFFTSize());
2318 }
2319
2320 recordColumnExtents(column,
2321 sx,
2322 overallMag,
2323 overallMagChanged);
2324
2325 if (m_colourScale != ColourScaleType::Phase) {
2326 column = ColumnOp::normalize(column, m_normalization);
2327 }
2328
2329 preparedColumn = ColumnOp::applyGain(column, m_gain);
2330
2331 psx = sx;
2332 }
2333
2334 if (sx == sx0) {
2335 pixelPeakColumn = preparedColumn;
2336 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
2337 minbin, maxbin - 1);
2338 } else {
2339 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
2340 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
2341 preparedColumn[i]);
2342 }
2343 }
2344 }
2345
2346 if (!pixelPeakColumn.empty()) {
2347 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
2348 pi != peakfreqs.end(); ++pi) {
2349
2350 int bin = pi->first;
2351 double freq = pi->second;
2352
2353 if (bin < minbin) continue;
2354 if (bin > maxbin) break;
2355
2356 double value = pixelPeakColumn[bin - minbin];
2357
2358 double y = v->getYForFrequency
2359 (freq, displayMinFreq, displayMaxFreq, logarithmic);
2360
2361 int iy = int(y + 0.5);
2362 if (iy < 0 || iy >= h) continue;
2363
2364 m_drawBuffer.setPixel(x, iy, getDisplayValue(v, value));
2365 }
2366 }
2367
2368 if (haveTimeLimits) {
2369 if (columnCount >= minColumns) {
2370 auto t = chrono::steady_clock::now();
2371 double diff = chrono::duration<double>(t - startTime).count();
2372 if (diff > hardTimeLimit) {
2373 #ifdef DEBUG_SPECTROGRAM_REPAINT
2374 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: hard limit " << hardTimeLimit << " sec exceeded after "
2375 << columnCount << " columns with time " << diff << endl;
2376 #endif
2377 return columnCount;
2378 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2379 // If we're more than half way through by the time
2380 // we reach the soft limit, ignore it (though
2381 // still respect the hard limit, above). Otherwise
2382 // respect the soft limit and return now.
2383 if (columnCount > w/2) {
2384 overridingSoftLimit = true;
2385 } else {
2386 #ifdef DEBUG_SPECTROGRAM_REPAINT
2387 cerr << "SpectrogramLayer::paintDrawBufferPeakFrequencies: soft limit " << softTimeLimit << " sec exceeded after "
2388 << columnCount << " columns with time " << diff << endl;
2389 #endif
2390 return columnCount;
2391 }
2392 }
2393 }
2394 }
2395 }
2396
2397 return columnCount;
2398 }
2399
2400 vector<float>
2401 SpectrogramLayer::getColumnFromFFTModel(FFTModel *fft,
2402 int sx, // column number in model
2403 int minbin,
2404 int bincount) const
2405 {
2406 vector<float> values(bincount, 0.f);
2407
2408 if (m_colourScale == ColourScaleType::Phase) {
2409 fft->getPhasesAt(sx, values.data(), minbin, bincount);
2410 } else {
2411 fft->getMagnitudesAt(sx, values.data(), minbin, bincount);
2412 }
2413
2414 return values;
2415 }
2416
2417 vector<float>
2418 SpectrogramLayer::getColumnFromGenericModel(DenseThreeDimensionalModel *model,
2419 int sx, // column number in model
2420 int minbin,
2421 int bincount) const
2422 {
2423 if (m_colourScale == ColourScaleType::Phase) {
2424 throw std::logic_error("can't use phase scale with generic 3d model");
2425 }
2426
2427 auto col = model->getColumn(sx);
2428
2429 return vector<float>(col.data() + minbin,
2430 col.data() + minbin + bincount);
2431 }
2432
2433 void
2434 SpectrogramLayer::recordColumnExtents(const vector<float> &col,
2435 int sx, // column index, for m_columnMags
2436 MagnitudeRange &overallMag,
2437 bool &overallMagChanged) const
2438 {
2439 if (!in_range_for(m_columnMags, sx)) {
2440 m_columnMags.resize(sx + 1);
2441 }
2442 MagnitudeRange mr;
2443 for (auto v: col) {
2444 mr.sample(v);
2445 }
2446 m_columnMags[sx] = mr;
2447 if (overallMag.sample(mr)) {
2448 overallMagChanged = true;
2449 }
2450 }
2451
2452 int
2453 SpectrogramLayer::paintDrawBuffer(LayerGeometryProvider *v,
2454 int w,
2455 int h,
2456 const vector<int> &binforx,
2457 const vector<double> &binfory,
2458 bool usePeaksCache,
2459 MagnitudeRange &overallMag,
2460 bool &overallMagChanged,
2461 bool rightToLeft,
2462 double softTimeLimit) const
2463 {
2464 Profiler profiler("SpectrogramLayer::paintDrawBuffer");
2465
2466 int minbin = int(binfory[0] + 0.0001);
2467 int maxbin = int(binfory[h-1]);
2468
2469 #ifdef DEBUG_SPECTROGRAM_REPAINT
2470 cerr << "SpectrogramLayer::paintDrawBuffer: minbin " << minbin << ", maxbin " << maxbin << "; w " << w << ", h " << h << endl;
2471 #endif
2472 if (minbin < 0) minbin = 0;
2473 if (maxbin < 0) maxbin = minbin+1;
2474
2475 DenseThreeDimensionalModel *peakCacheModel = 0;
2476 FFTModel *fftModel = 0;
2477 DenseThreeDimensionalModel *sourceModel = 0;
2478
2479 #ifdef DEBUG_SPECTROGRAM_REPAINT
2480 cerr << "SpectrogramLayer::paintDrawBuffer: Note: bin display = " << int(m_binDisplay) << ", w = " << w << ", binforx[" << w-1 << "] = " << binforx[w-1] << ", binforx[0] = " << binforx[0] << endl;
2481 #endif
2482
2483 int divisor = 1;
2484 if (usePeaksCache) {
2485 peakCacheModel = getPeakCache();
2486 divisor = m_peakCacheDivisor;
2487 sourceModel = peakCacheModel;
2488 } else {
2489 fftModel = getFFTModel();
2490 sourceModel = fftModel;
2491 }
2492
2493 if (!sourceModel) return 0;
2494
2495 bool interpolate = false;
2496 Preferences::SpectrogramSmoothing smoothing =
2497 Preferences::getInstance()->getSpectrogramSmoothing();
2498 if (smoothing == Preferences::SpectrogramInterpolated ||
2499 smoothing == Preferences::SpectrogramZeroPaddedAndInterpolated) {
2500 if (m_binDisplay != BinDisplay::PeakBins &&
2501 m_binDisplay != BinDisplay::PeakFrequencies) {
2502 interpolate = true;
2503 }
2504 }
2505
2506 int psx = -1;
2507
2508 int minColumns = 4;
2509 bool haveTimeLimits = (softTimeLimit > 0.0);
2510 double hardTimeLimit = softTimeLimit * 2.0;
2511 bool overridingSoftLimit = false;
2512 auto startTime = chrono::steady_clock::now();
2513
2514 int start = 0;
2515 int finish = w;
2516 int step = 1;
2517
2518 if (rightToLeft) {
2519 start = w-1;
2520 finish = -1;
2521 step = -1;
2522 }
2523
2524 int columnCount = 0;
2525
2526 vector<float> preparedColumn;
2527
2528 for (int x = start; x != finish; x += step) {
2529
2530 // x is the on-canvas pixel coord; sx (later) will be the
2531 // source column index
2532
2533 ++columnCount;
2534
2535 if (binforx[x] < 0) continue;
2536
2537 int sx0 = binforx[x] / divisor;
2538 int sx1 = sx0;
2539 if (x+1 < w) sx1 = binforx[x+1] / divisor;
2540 if (sx0 < 0) sx0 = sx1 - 1;
2541 if (sx0 < 0) continue;
2542 if (sx1 <= sx0) sx1 = sx0 + 1;
2543
2544 vector<float> pixelPeakColumn;
2545
2546 for (int sx = sx0; sx < sx1; ++sx) {
2547
2548 #ifdef DEBUG_SPECTROGRAM_REPAINT
2549 // cerr << "sx = " << sx << endl;
2550 #endif
2551
2552 if (sx < 0 || sx >= sourceModel->getWidth()) {
2553 continue;
2554 }
2555
2556 if (sx != psx) {
2557
2558 // order:
2559 // get column -> scale -> record extents ->
2560 // normalise -> peak pick -> apply display gain ->
2561 // distribute/interpolate
2562
2563 ColumnOp::Column column;
2564
2565 if (peakCacheModel) {
2566 column = getColumnFromGenericModel(peakCacheModel,
2567 sx,
2568 minbin,
2569 maxbin - minbin + 1);
2570 } else {
2571 column = getColumnFromFFTModel(fftModel,
2572 sx,
2573 minbin,
2574 maxbin - minbin + 1);
2575 }
2576
2577 if (m_colourScale != ColourScaleType::Phase) {
2578 column = ColumnOp::fftScale(column, getFFTSize());
2579 }
2580
2581 recordColumnExtents(column,
2582 sx,
2583 overallMag,
2584 overallMagChanged);
2585
2586 if (m_colourScale != ColourScaleType::Phase) {
2587 column = ColumnOp::normalize(column, m_normalization);
2588 }
2589
2590 if (m_binDisplay == BinDisplay::PeakBins) {
2591 column = ColumnOp::peakPick(column);
2592 }
2593
2594 preparedColumn =
2595 ColumnOp::distribute(ColumnOp::applyGain(column, m_gain),
2596 h,
2597 binfory,
2598 minbin,
2599 interpolate);
2600
2601 psx = sx;
2602 }
2603
2604 if (sx == sx0) {
2605 pixelPeakColumn = preparedColumn;
2606 } else {
2607 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
2608 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
2609 preparedColumn[i]);
2610 }
2611 }
2612 }
2613
2614 if (!pixelPeakColumn.empty()) {
2615 for (int y = 0; y < h; ++y) {
2616 m_drawBuffer.setPixel(x,
2617 h-y-1,
2618 getDisplayValue(v, pixelPeakColumn[y]));
2619 }
2620 }
2621
2622 if (haveTimeLimits) {
2623 if (columnCount >= minColumns) {
2624 auto t = chrono::steady_clock::now();
2625 double diff = chrono::duration<double>(t - startTime).count();
2626 if (diff > hardTimeLimit) {
2627 #ifdef DEBUG_SPECTROGRAM_REPAINT
2628 cerr << "SpectrogramLayer::paintDrawBuffer: hard limit " << hardTimeLimit << " sec exceeded after "
2629 << columnCount << " columns with time " << diff << endl;
2630 #endif
2631 return columnCount;
2632 } else if (diff > softTimeLimit && !overridingSoftLimit) {
2633 // If we're more than half way through by the time
2634 // we reach the soft limit, ignore it (though
2635 // still respect the hard limit, above). Otherwise
2636 // respect the soft limit and return now.
2637 if (columnCount > w/2) {
2638 overridingSoftLimit = true;
2639 } else {
2640 #ifdef DEBUG_SPECTROGRAM_REPAINT
2641 cerr << "SpectrogramLayer::paintDrawBuffer: soft limit " << softTimeLimit << " sec exceeded after "
2642 << columnCount << " columns with time " << diff << endl;
2643 #endif
2644 return columnCount;
2645 }
2646 }
2647 }
2648 }
2649 }
2650
2651 return columnCount;
2652 } 1547 }
2653 1548
2654 void 1549 void
2655 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const 1550 SpectrogramLayer::illuminateLocalFeatures(LayerGeometryProvider *v, QPainter &paint) const
2656 { 1551 {
2764 int minf = int(lrint(min)); 1659 int minf = int(lrint(min));
2765 int maxf = int(lrint(max)); 1660 int maxf = int(lrint(max));
2766 1661
2767 if (m_minFrequency == minf && m_maxFrequency == maxf) return true; 1662 if (m_minFrequency == minf && m_maxFrequency == maxf) return true;
2768 1663
2769 invalidateImageCaches(); 1664 invalidateRenderers();
2770 invalidateMagnitudes(); 1665 invalidateMagnitudes();
2771 1666
2772 m_minFrequency = minf; 1667 m_minFrequency = minf;
2773 m_maxFrequency = maxf; 1668 m_maxFrequency = maxf;
2774 1669
2814 1709
2815 return true; 1710 return true;
2816 } 1711 }
2817 1712
2818 void 1713 void
2819 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *v, QMouseEvent *e) 1714 SpectrogramLayer::measureDoubleClick(LayerGeometryProvider *, QMouseEvent *)
2820 { 1715 {
1716 /*!!! replace this
2821 const View *view = v->getView(); 1717 const View *view = v->getView();
1718
2822 ScrollableImageCache &cache = getImageCacheReference(view); 1719 ScrollableImageCache &cache = getImageCacheReference(view);
2823 1720
2824 cerr << "cache width: " << cache.getSize().width() << ", height: " 1721 cerr << "cache width: " << cache.getSize().width() << ", height: "
2825 << cache.getSize().height() << endl; 1722 << cache.getSize().height() << endl;
2826 1723
2832 MeasureRect mr; 1729 MeasureRect mr;
2833 setMeasureRectFromPixrect(v, mr, rect); 1730 setMeasureRectFromPixrect(v, mr, rect);
2834 CommandHistory::getInstance()->addCommand 1731 CommandHistory::getInstance()->addCommand
2835 (new AddMeasurementRectCommand(this, mr)); 1732 (new AddMeasurementRectCommand(this, mr));
2836 } 1733 }
1734 */
2837 } 1735 }
2838 1736
2839 bool 1737 bool
2840 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint, 1738 SpectrogramLayer::getCrosshairExtents(LayerGeometryProvider *v, QPainter &paint,
2841 QPoint cursorPos, 1739 QPoint cursorPos,
3180 for (int i = 0; i < ch; ++i) { 2078 for (int i = 0; i < ch; ++i) {
3181 2079
3182 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1)); 2080 double dBval = dBmin + (((dBmax - dBmin) * i) / (ch - 1));
3183 int idb = int(dBval); 2081 int idb = int(dBval);
3184 2082
3185 double value = AudioLevel::dB_to_multiplier(dBval); 2083 //!!! replace this
3186 int colour = getDisplayValue(v, value * m_gain); 2084 // double value = AudioLevel::dB_to_multiplier(dBval);
3187 2085 // int colour = getDisplayValue(v, value * m_gain);
3188 paint.setPen(m_palette.getColour((unsigned char)colour)); 2086 // paint.setPen(m_palette.getColour((unsigned char)colour));
3189 2087
3190 int y = textHeight * topLines + 4 + ch - i; 2088 int y = textHeight * topLines + 4 + ch - i;
3191 2089
3192 paint.drawLine(5 + cw - cbw, y, cw + 2, y); 2090 paint.drawLine(5 + cw - cbw, y, cw + 2, y);
3193 2091