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