Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 35:10ba9276a315
* Add TextModel and TextLayer types
* Make View refresh work better when editing a model (previously edits might
not be refreshed if their visible changed area extended beyond the strict
frame range that was being modified in the model)
* Add phase-adjusted instantaneous frequency display to spectrogram layer
(still a work in progress)
* Pull maths aliases out into a separate header in dsp/maths so MathUtilities
can be included without introducing them
author | Chris Cannam |
---|---|
date | Mon, 20 Feb 2006 13:33:36 +0000 |
parents | c43f2c4f66f2 |
children | c28ebb4ba4de |
comparison
equal
deleted
inserted
replaced
34:c43f2c4f66f2 | 35:10ba9276a315 |
---|---|
12 #include "base/View.h" | 12 #include "base/View.h" |
13 #include "base/Profiler.h" | 13 #include "base/Profiler.h" |
14 #include "base/AudioLevel.h" | 14 #include "base/AudioLevel.h" |
15 #include "base/Window.h" | 15 #include "base/Window.h" |
16 #include "base/Pitch.h" | 16 #include "base/Pitch.h" |
17 | |
18 #include "dsp/maths/MathUtilities.h" | |
17 | 19 |
18 #include <QPainter> | 20 #include <QPainter> |
19 #include <QImage> | 21 #include <QImage> |
20 #include <QPixmap> | 22 #include <QPixmap> |
21 #include <QRect> | 23 #include <QRect> |
40 m_colourRotation(0), | 42 m_colourRotation(0), |
41 m_maxFrequency(8000), | 43 m_maxFrequency(8000), |
42 m_colourScale(dBColourScale), | 44 m_colourScale(dBColourScale), |
43 m_colourScheme(DefaultColours), | 45 m_colourScheme(DefaultColours), |
44 m_frequencyScale(LinearFrequencyScale), | 46 m_frequencyScale(LinearFrequencyScale), |
47 m_frequencyAdjustment(RawFrequency), | |
45 m_cache(0), | 48 m_cache(0), |
49 m_phaseAdjustCache(0), | |
46 m_cacheInvalid(true), | 50 m_cacheInvalid(true), |
47 m_pixmapCache(0), | 51 m_pixmapCache(0), |
48 m_pixmapCacheInvalid(true), | 52 m_pixmapCacheInvalid(true), |
49 m_fillThread(0), | 53 m_fillThread(0), |
50 m_updateTimer(0), | 54 m_updateTimer(0), |
72 m_condition.wakeAll(); | 76 m_condition.wakeAll(); |
73 if (m_fillThread) m_fillThread->wait(); | 77 if (m_fillThread) m_fillThread->wait(); |
74 delete m_fillThread; | 78 delete m_fillThread; |
75 | 79 |
76 delete m_cache; | 80 delete m_cache; |
81 delete m_phaseAdjustCache; | |
77 } | 82 } |
78 | 83 |
79 void | 84 void |
80 SpectrogramLayer::setModel(const DenseTimeValueModel *model) | 85 SpectrogramLayer::setModel(const DenseTimeValueModel *model) |
81 { | 86 { |
82 std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl; | 87 std::cerr << "SpectrogramLayer(" << this << "): setModel(" << model << ")" << std::endl; |
83 | 88 |
84 m_mutex.lock(); | 89 m_mutex.lock(); |
90 m_cacheInvalid = true; | |
85 m_model = model; | 91 m_model = model; |
86 delete m_cache; //!!! hang on, this isn't safe to do here is it? | 92 delete m_cache; //!!! hang on, this isn't safe to do here is it? |
87 // we need some sort of guard against the fill | 93 // we need some sort of guard against the fill |
88 // thread trying to read the defunct model too. | 94 // thread trying to read the defunct model too. |
89 // should we use a scavenger? | 95 // should we use a scavenger? |
90 m_cache = 0; | 96 m_cache = 0; |
97 delete m_phaseAdjustCache; //!!! likewise | |
98 m_phaseAdjustCache = 0; | |
91 m_mutex.unlock(); | 99 m_mutex.unlock(); |
92 | 100 |
93 if (!m_model || !m_model->isOK()) return; | 101 if (!m_model || !m_model->isOK()) return; |
94 | 102 |
95 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); | 103 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged())); |
118 list.push_back(tr("Window Overlap")); | 126 list.push_back(tr("Window Overlap")); |
119 list.push_back(tr("Gain")); | 127 list.push_back(tr("Gain")); |
120 list.push_back(tr("Colour Rotation")); | 128 list.push_back(tr("Colour Rotation")); |
121 list.push_back(tr("Max Frequency")); | 129 list.push_back(tr("Max Frequency")); |
122 list.push_back(tr("Frequency Scale")); | 130 list.push_back(tr("Frequency Scale")); |
131 list.push_back(tr("Frequency Adjustment")); | |
123 return list; | 132 return list; |
124 } | 133 } |
125 | 134 |
126 Layer::PropertyType | 135 Layer::PropertyType |
127 SpectrogramLayer::getPropertyType(const PropertyName &name) const | 136 SpectrogramLayer::getPropertyType(const PropertyName &name) const |
133 | 142 |
134 QString | 143 QString |
135 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const | 144 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const |
136 { | 145 { |
137 if (name == tr("Window Size") || | 146 if (name == tr("Window Size") || |
147 name == tr("Window Type") || | |
138 name == tr("Window Overlap")) return tr("Window"); | 148 name == tr("Window Overlap")) return tr("Window"); |
149 if (name == tr("Colour") || | |
150 name == tr("Colour Rotation")) return tr("Colour"); | |
139 if (name == tr("Gain") || | 151 if (name == tr("Gain") || |
140 name == tr("Colour Rotation") || | |
141 name == tr("Colour Scale")) return tr("Scale"); | 152 name == tr("Colour Scale")) return tr("Scale"); |
142 if (name == tr("Max Frequency") || | 153 if (name == tr("Max Frequency") || |
143 name == tr("Frequency Scale")) return tr("Frequency"); | 154 name == tr("Frequency Scale") || |
155 name == tr("Frequency Adjustment")) return tr("Frequency"); | |
144 return QString(); | 156 return QString(); |
145 } | 157 } |
146 | 158 |
147 int | 159 int |
148 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, | 160 SpectrogramLayer::getPropertyRangeAndValue(const PropertyName &name, |
230 | 242 |
231 *min = 0; | 243 *min = 0; |
232 *max = 1; | 244 *max = 1; |
233 deft = (int)m_frequencyScale; | 245 deft = (int)m_frequencyScale; |
234 | 246 |
247 } else if (name == tr("Frequency Adjustment")) { | |
248 | |
249 *min = 0; | |
250 *max = 2; | |
251 deft = (int)m_frequencyAdjustment; | |
252 | |
235 } else { | 253 } else { |
236 deft = Layer::getPropertyRangeAndValue(name, min, max); | 254 deft = Layer::getPropertyRangeAndValue(name, min, max); |
237 } | 255 } |
238 | 256 |
239 return deft; | 257 return deft; |
264 } | 282 } |
265 } | 283 } |
266 if (name == tr("Window Type")) { | 284 if (name == tr("Window Type")) { |
267 switch ((WindowType)value) { | 285 switch ((WindowType)value) { |
268 default: | 286 default: |
269 case RectangularWindow: return tr("Rectangular"); | 287 case RectangularWindow: return tr("Rectangle"); |
270 case BartlettWindow: return tr("Bartlett"); | 288 case BartlettWindow: return tr("Bartlett"); |
271 case HammingWindow: return tr("Hamming"); | 289 case HammingWindow: return tr("Hamming"); |
272 case HanningWindow: return tr("Hanning"); | 290 case HanningWindow: return tr("Hanning"); |
273 case BlackmanWindow: return tr("Blackman"); | 291 case BlackmanWindow: return tr("Blackman"); |
274 case GaussianWindow: return tr("Gaussian"); | 292 case GaussianWindow: return tr("Gaussian"); |
279 return QString("%1").arg(32 << value); | 297 return QString("%1").arg(32 << value); |
280 } | 298 } |
281 if (name == tr("Window Overlap")) { | 299 if (name == tr("Window Overlap")) { |
282 switch (value) { | 300 switch (value) { |
283 default: | 301 default: |
284 case 0: return tr("None"); | 302 case 0: return tr("0%"); |
285 case 1: return tr("25 %"); | 303 case 1: return tr("25%"); |
286 case 2: return tr("50 %"); | 304 case 2: return tr("50%"); |
287 case 3: return tr("75 %"); | 305 case 3: return tr("75%"); |
288 case 4: return tr("90 %"); | 306 case 4: return tr("90%"); |
289 } | 307 } |
290 } | 308 } |
291 if (name == tr("Max Frequency")) { | 309 if (name == tr("Max Frequency")) { |
292 switch (value) { | 310 switch (value) { |
293 default: | 311 default: |
306 if (name == tr("Frequency Scale")) { | 324 if (name == tr("Frequency Scale")) { |
307 switch (value) { | 325 switch (value) { |
308 default: | 326 default: |
309 case 0: return tr("Linear"); | 327 case 0: return tr("Linear"); |
310 case 1: return tr("Log"); | 328 case 1: return tr("Log"); |
329 } | |
330 } | |
331 if (name == tr("Frequency Adjustment")) { | |
332 switch (value) { | |
333 default: | |
334 case 0: return tr("Bins"); | |
335 case 1: return tr("Pitches"); | |
336 case 2: return tr("Peaks"); | |
311 } | 337 } |
312 } | 338 } |
313 return tr("<unknown>"); | 339 return tr("<unknown>"); |
314 } | 340 } |
315 | 341 |
364 switch (value) { | 390 switch (value) { |
365 default: | 391 default: |
366 case 0: setFrequencyScale(LinearFrequencyScale); break; | 392 case 0: setFrequencyScale(LinearFrequencyScale); break; |
367 case 1: setFrequencyScale(LogFrequencyScale); break; | 393 case 1: setFrequencyScale(LogFrequencyScale); break; |
368 } | 394 } |
395 } else if (name == tr("Frequency Adjustment")) { | |
396 switch (value) { | |
397 default: | |
398 case 0: setFrequencyAdjustment(RawFrequency); break; | |
399 case 1: setFrequencyAdjustment(PhaseAdjustedFrequency); break; | |
400 case 2: setFrequencyAdjustment(PhaseAdjustedPeaks); break; | |
401 } | |
369 } | 402 } |
370 } | 403 } |
371 | 404 |
372 void | 405 void |
373 SpectrogramLayer::setChannel(int ch) | 406 SpectrogramLayer::setChannel(int ch) |
582 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) | 615 SpectrogramLayer::setFrequencyScale(FrequencyScale frequencyScale) |
583 { | 616 { |
584 if (m_frequencyScale == frequencyScale) return; | 617 if (m_frequencyScale == frequencyScale) return; |
585 | 618 |
586 m_mutex.lock(); | 619 m_mutex.lock(); |
620 | |
587 // don't need to invalidate main cache here | 621 // don't need to invalidate main cache here |
588 m_pixmapCacheInvalid = true; | 622 m_pixmapCacheInvalid = true; |
589 | 623 |
590 m_frequencyScale = frequencyScale; | 624 m_frequencyScale = frequencyScale; |
591 | 625 |
596 | 630 |
597 SpectrogramLayer::FrequencyScale | 631 SpectrogramLayer::FrequencyScale |
598 SpectrogramLayer::getFrequencyScale() const | 632 SpectrogramLayer::getFrequencyScale() const |
599 { | 633 { |
600 return m_frequencyScale; | 634 return m_frequencyScale; |
635 } | |
636 | |
637 void | |
638 SpectrogramLayer::setFrequencyAdjustment(FrequencyAdjustment frequencyAdjustment) | |
639 { | |
640 if (m_frequencyAdjustment == frequencyAdjustment) return; | |
641 | |
642 m_mutex.lock(); | |
643 | |
644 m_cacheInvalid = true; | |
645 m_pixmapCacheInvalid = true; | |
646 | |
647 m_frequencyAdjustment = frequencyAdjustment; | |
648 | |
649 m_mutex.unlock(); | |
650 | |
651 fillCache(); | |
652 | |
653 emit layerParametersChanged(); | |
654 } | |
655 | |
656 SpectrogramLayer::FrequencyAdjustment | |
657 SpectrogramLayer::getFrequencyAdjustment() const | |
658 { | |
659 return m_frequencyAdjustment; | |
601 } | 660 } |
602 | 661 |
603 void | 662 void |
604 SpectrogramLayer::setLayerDormant(bool dormant) | 663 SpectrogramLayer::setLayerDormant(bool dormant) |
605 { | 664 { |
716 { | 775 { |
717 if (m_cacheInvalid || !m_cache) return; | 776 if (m_cacheInvalid || !m_cache) return; |
718 | 777 |
719 int formerRotation = m_colourRotation; | 778 int formerRotation = m_colourRotation; |
720 | 779 |
721 // m_cache->setNumColors(256); | |
722 | |
723 // m_cache->setColour(0, qRgb(255, 255, 255)); | |
724 m_cache->setColour(0, Qt::white); | 780 m_cache->setColour(0, Qt::white); |
725 | 781 |
726 for (int pixel = 1; pixel < 256; ++pixel) { | 782 for (int pixel = 1; pixel < 256; ++pixel) { |
727 | 783 |
728 QColor colour; | 784 QColor colour; |
762 case RedOnBlack: | 818 case RedOnBlack: |
763 colour = QColor::fromHsv(10, pixel, pixel); | 819 colour = QColor::fromHsv(10, pixel, pixel); |
764 break; | 820 break; |
765 } | 821 } |
766 | 822 |
767 // m_cache->setColor | |
768 // (pixel, qRgb(colour.red(), colour.green(), colour.blue())); | |
769 m_cache->setColour(pixel, colour); | 823 m_cache->setColour(pixel, colour); |
770 } | 824 } |
771 | 825 |
772 m_colourRotation = 0; | 826 m_colourRotation = 0; |
773 rotateCacheColourmap(m_colourRotation - formerRotation); | 827 rotateCacheColourmap(m_colourRotation - formerRotation); |
800 fftw_complex *output, | 854 fftw_complex *output, |
801 fftw_plan plan, | 855 fftw_plan plan, |
802 size_t windowSize, | 856 size_t windowSize, |
803 size_t increment, | 857 size_t increment, |
804 const Window<double> &windower, | 858 const Window<double> &windower, |
805 bool lock) const | 859 bool resetStoredPhase) const |
806 { | 860 { |
861 static std::vector<double> storedPhase; | |
862 | |
863 bool phaseAdjust = | |
864 (m_frequencyAdjustment == PhaseAdjustedFrequency || | |
865 m_frequencyAdjustment == PhaseAdjustedPeaks); | |
866 bool haveStoredPhase = true; | |
867 size_t sampleRate = 0; | |
868 | |
869 static int counter = 0; | |
870 | |
871 if (phaseAdjust) { | |
872 if (resetStoredPhase || (storedPhase.size() != windowSize / 2)) { | |
873 haveStoredPhase = false; | |
874 storedPhase.clear(); | |
875 for (size_t i = 0; i < windowSize / 2; ++i) { | |
876 storedPhase.push_back(0.0); | |
877 } | |
878 counter = 0; | |
879 } | |
880 ++counter; | |
881 sampleRate = m_model->getSampleRate(); | |
882 } | |
883 | |
807 int startFrame = increment * column; | 884 int startFrame = increment * column; |
808 int endFrame = startFrame + windowSize; | 885 int endFrame = startFrame + windowSize; |
809 | 886 |
810 startFrame -= int(windowSize - increment) / 2; | 887 startFrame -= int(windowSize - increment) / 2; |
811 endFrame -= int(windowSize - increment) / 2; | 888 endFrame -= int(windowSize - increment) / 2; |
831 } | 908 } |
832 } | 909 } |
833 | 910 |
834 windower.cut(input); | 911 windower.cut(input); |
835 | 912 |
913 for (size_t i = 0; i < windowSize/2; ++i) { | |
914 double temp = input[i]; | |
915 input[i] = input[i + windowSize/2]; | |
916 input[i + windowSize/2] = temp; | |
917 } | |
918 | |
836 fftw_execute(plan); | 919 fftw_execute(plan); |
837 | 920 |
838 // if (lock) m_mutex.lock(); | |
839 bool interrupted = false; | 921 bool interrupted = false; |
840 | 922 |
923 double prevMag = 0.0; | |
924 | |
841 for (size_t i = 0; i < windowSize / 2; ++i) { | 925 for (size_t i = 0; i < windowSize / 2; ++i) { |
842 | 926 |
843 int value = 0; | 927 int value = 0; |
928 double phase = 0.0; | |
929 | |
930 double mag = sqrt(output[i][0] * output[i][0] + | |
931 output[i][1] * output[i][1]); | |
932 mag /= windowSize / 2; | |
933 | |
934 if (phaseAdjust || (m_colourScale == PhaseColourScale)) { | |
935 | |
936 // phase = atan2(-output[i][1], output[i][0]); | |
937 // phase = atan2(output[i][1], output[i][0]); | |
938 phase = atan2(output[i][0], output[i][1]); | |
939 // phase = MathUtilities::princarg(phase); | |
940 } | |
941 | |
942 if (phaseAdjust && m_phaseAdjustCache && haveStoredPhase) { | |
943 | |
944 bool peak = true; | |
945 if (m_frequencyAdjustment == PhaseAdjustedPeaks) { | |
946 if (mag < prevMag) peak = false; | |
947 else { | |
948 double nextMag = 0.0; | |
949 if (i < windowSize / 2 - 1) { | |
950 nextMag = sqrt(output[i+1][0] * output[i+1][0] + | |
951 output[i+1][1] * output[i+1][1]); | |
952 nextMag /= windowSize / 2; | |
953 } | |
954 if (mag < nextMag) peak = false; | |
955 } | |
956 prevMag = mag; | |
957 } | |
958 | |
959 if (!peak) { | |
960 if (m_cacheInvalid || m_exiting) { | |
961 interrupted = true; | |
962 break; | |
963 } | |
964 m_phaseAdjustCache->setValueAt(column, i, SCHAR_MIN); | |
965 } else { | |
966 | |
967 // if (i > 45 && i < 55 && counter == 10) { | |
968 | |
969 double freq = (double(i) * sampleRate) / m_windowSize; | |
970 // std::cout << "\nbin = " << i << " initial estimate freq = " << freq | |
971 // << " mag = " << mag << std::endl; | |
972 | |
973 double prevPhase = storedPhase[i]; | |
974 | |
975 double expectedPhase = | |
976 prevPhase + (2 * M_PI * i * increment) / m_windowSize; | |
977 | |
978 double phaseError = MathUtilities::princarg(phase - expectedPhase); | |
979 | |
980 // if (fabs(phaseError) > (1.2 * (increment * M_PI) / m_windowSize)) { | |
981 // std::cout << "error > " << (1.2 * (increment * M_PI) / m_windowSize) << std::endl; | |
982 // }// else { | |
983 | |
984 // std::cout << "prevPhase = " << prevPhase << ", phase = " << phase | |
985 // << ", expected = " << MathUtilities::princarg(expectedPhase) << ", error = " | |
986 // << phaseError << std::endl; | |
987 | |
988 double newFreq = | |
989 (sampleRate * | |
990 (expectedPhase + phaseError - prevPhase)) / | |
991 //(prevPhase - (expectedPhase + phaseError))) / | |
992 (2 * M_PI * increment); | |
993 | |
994 // std::cout << freq << " (" << Pitch::getPitchLabelForFrequency(freq).toStdString() << ") -> " << newFreq << " (" << Pitch::getPitchLabelForFrequency(newFreq).toStdString() << ")" << std::endl; | |
995 // } | |
996 //} | |
997 | |
998 double binRange = (double(i + 1) * sampleRate) / windowSize - freq; | |
999 | |
1000 int offset = lrint(((newFreq - freq) / binRange) * 10);//!!! | |
1001 | |
1002 if (m_cacheInvalid || m_exiting) { | |
1003 interrupted = true; | |
1004 break; | |
1005 } | |
1006 if (offset > SCHAR_MIN && offset <= SCHAR_MAX) { | |
1007 signed char coff = offset; | |
1008 m_phaseAdjustCache->setValueAt(column, i, (unsigned char)coff); | |
1009 } else { | |
1010 m_phaseAdjustCache->setValueAt(column, i, 0); | |
1011 } | |
1012 } | |
1013 storedPhase[i] = phase; | |
1014 } | |
844 | 1015 |
845 if (m_colourScale == PhaseColourScale) { | 1016 if (m_colourScale == PhaseColourScale) { |
846 | 1017 |
847 double phase = atan2(-output[i][1], output[i][0]); | 1018 phase = MathUtilities::princarg(phase); |
848 value = int((phase * 128 / M_PI) + 128); | 1019 value = int((phase * 128 / M_PI) + 128); |
849 | 1020 |
850 } else { | 1021 } else { |
851 | 1022 |
852 double mag = sqrt(output[i][0] * output[i][0] + | 1023 double mag = sqrt(output[i][0] * output[i][0] + |
882 } | 1053 } |
883 | 1054 |
884 m_cache->setValueAt(column, i, value + 1); | 1055 m_cache->setValueAt(column, i, value + 1); |
885 } | 1056 } |
886 | 1057 |
887 // if (lock) m_mutex.unlock(); | |
888 return !interrupted; | 1058 return !interrupted; |
889 } | 1059 } |
890 | 1060 |
891 SpectrogramLayer::Cache::Cache(size_t width, size_t height) : | 1061 SpectrogramLayer::Cache::Cache(size_t width, size_t height) : |
892 m_width(width), | 1062 m_width(width), |
893 m_height(height) | 1063 m_height(height) |
894 { | 1064 { |
895 m_values = new unsigned char[m_width * m_height]; | 1065 // use malloc rather than new[], because we want to be able to use realloc |
1066 m_values = (unsigned char *) | |
1067 malloc(m_width * m_height * sizeof(unsigned char)); | |
1068 if (!m_values) throw std::bad_alloc(); | |
896 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char)); | 1069 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char)); |
897 } | 1070 } |
898 | 1071 |
899 SpectrogramLayer::Cache::~Cache() | 1072 SpectrogramLayer::Cache::~Cache() |
900 { | 1073 { |
901 delete[] m_values; | 1074 if (m_values) free(m_values); |
902 } | 1075 } |
1076 | |
1077 void | |
1078 SpectrogramLayer::Cache::resize(size_t width, size_t height) | |
1079 { | |
1080 m_values = (unsigned char *) | |
1081 realloc(m_values, m_width * m_height * sizeof(unsigned char)); | |
1082 if (!m_values) throw std::bad_alloc(); | |
1083 MUNLOCK(m_values, m_width * m_height * sizeof(unsigned char)); | |
1084 } | |
903 | 1085 |
904 size_t | 1086 size_t |
905 SpectrogramLayer::Cache::getWidth() const | 1087 SpectrogramLayer::Cache::getWidth() const |
906 { | 1088 { |
907 return m_width; | 1089 return m_width; |
962 | 1144 |
963 if (m_layer.m_dormant) { | 1145 if (m_layer.m_dormant) { |
964 | 1146 |
965 if (m_layer.m_cacheInvalid) { | 1147 if (m_layer.m_cacheInvalid) { |
966 delete m_layer.m_cache; | 1148 delete m_layer.m_cache; |
1149 delete m_layer.m_phaseAdjustCache; | |
967 m_layer.m_cache = 0; | 1150 m_layer.m_cache = 0; |
1151 m_layer.m_phaseAdjustCache = 0; | |
968 } | 1152 } |
969 | 1153 |
970 } else if (m_layer.m_model && m_layer.m_cacheInvalid) { | 1154 } else if (m_layer.m_model && m_layer.m_cacheInvalid) { |
971 | 1155 |
972 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl; | 1156 // std::cerr << "SpectrogramLayer::CacheFillThread::run: something to do" << std::endl; |
1001 windowIncrement; | 1185 windowIncrement; |
1002 } | 1186 } |
1003 visibleEnd = m_layer.m_view->getEndFrame(); | 1187 visibleEnd = m_layer.m_view->getEndFrame(); |
1004 } | 1188 } |
1005 | 1189 |
1006 delete m_layer.m_cache; | |
1007 size_t width = (end - start) / windowIncrement + 1; | 1190 size_t width = (end - start) / windowIncrement + 1; |
1008 size_t height = windowSize / 2; | 1191 size_t height = windowSize / 2; |
1009 m_layer.m_cache = new Cache(width, height); | 1192 |
1193 if (!m_layer.m_cache) { | |
1194 m_layer.m_cache = new Cache(width, height); | |
1195 } else if (width != m_layer.m_cache->getWidth() || | |
1196 height != m_layer.m_cache->getHeight()) { | |
1197 m_layer.m_cache->resize(width, height); | |
1198 } | |
1010 | 1199 |
1011 m_layer.setCacheColourmap(); | 1200 m_layer.setCacheColourmap(); |
1012 m_layer.m_cache->fill(0); | 1201 m_layer.m_cache->fill(0); |
1202 | |
1203 if (m_layer.m_frequencyAdjustment == PhaseAdjustedFrequency || | |
1204 m_layer.m_frequencyAdjustment == PhaseAdjustedPeaks) { | |
1205 | |
1206 if (!m_layer.m_phaseAdjustCache) { | |
1207 m_layer.m_phaseAdjustCache = new Cache(width, height); | |
1208 } else if (width != m_layer.m_phaseAdjustCache->getWidth() || | |
1209 height != m_layer.m_phaseAdjustCache->getHeight()) { | |
1210 m_layer.m_phaseAdjustCache->resize(width, height); | |
1211 } | |
1212 | |
1213 m_layer.m_phaseAdjustCache->fill(0); | |
1214 | |
1215 } else { | |
1216 delete m_layer.m_phaseAdjustCache; | |
1217 m_layer.m_phaseAdjustCache = 0; | |
1218 } | |
1013 | 1219 |
1014 // We don't need a lock when writing to or reading from | 1220 // We don't need a lock when writing to or reading from |
1015 // the pixels in the cache, because it's a fixed size | 1221 // the pixels in the cache, because it's a fixed size |
1016 // array. We do need to ensure we have the width and | 1222 // array. We do need to ensure we have the width and |
1017 // height of the cache and the FFT parameters fixed before | 1223 // height of the cache and the FFT parameters known before |
1018 // we unlock, in case they change in the model while we | 1224 // we unlock, in case they change in the model while we |
1019 // aren't holding a lock. It's safe for us to continue to | 1225 // aren't holding a lock. It's safe for us to continue to |
1020 // use the "old" values if that happens, because they will | 1226 // use the "old" values if that happens, because they will |
1021 // continue to match the dimensions of the actual cache | 1227 // continue to match the dimensions of the actual cache |
1022 // (which we manage, not the model). | 1228 // (which we manage, not the model). |
1051 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) { | 1257 for (size_t f = visibleStart; f < visibleEnd; f += windowIncrement) { |
1052 | 1258 |
1053 m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1259 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1054 input, output, plan, | 1260 input, output, plan, |
1055 windowSize, windowIncrement, | 1261 windowSize, windowIncrement, |
1056 windower, false); | 1262 //!!! actually if we're doing phase adjustment we also want to fill the column preceding the visible area so that we have the right values for the first visible one (also applies below) |
1263 windower, f == visibleStart); | |
1057 | 1264 |
1058 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | 1265 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { |
1059 interrupted = true; | 1266 interrupted = true; |
1060 m_fillExtent = 0; | 1267 m_fillExtent = 0; |
1061 break; | 1268 break; |
1077 for (size_t f = visibleEnd; f < end; f += windowIncrement) { | 1284 for (size_t f = visibleEnd; f < end; f += windowIncrement) { |
1078 | 1285 |
1079 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1286 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1080 input, output, plan, | 1287 input, output, plan, |
1081 windowSize, windowIncrement, | 1288 windowSize, windowIncrement, |
1082 windower, true)) { | 1289 windower, f == visibleEnd)) { |
1083 interrupted = true; | 1290 interrupted = true; |
1084 m_fillExtent = 0; | 1291 m_fillExtent = 0; |
1085 break; | 1292 break; |
1086 } | 1293 } |
1087 | 1294 |
1108 for (size_t f = start; f < remainingEnd; f += windowIncrement) { | 1315 for (size_t f = start; f < remainingEnd; f += windowIncrement) { |
1109 | 1316 |
1110 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1317 if (!m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1111 input, output, plan, | 1318 input, output, plan, |
1112 windowSize, windowIncrement, | 1319 windowSize, windowIncrement, |
1113 windower, true)) { | 1320 windower, f == start)) { |
1114 interrupted = true; | 1321 interrupted = true; |
1115 m_fillExtent = 0; | 1322 m_fillExtent = 0; |
1116 break; | 1323 break; |
1117 } | 1324 } |
1118 | 1325 |
1175 | 1382 |
1176 float minf = float(sr) / m_windowSize; | 1383 float minf = float(sr) / m_windowSize; |
1177 | 1384 |
1178 float maxlogf = log10f(maxf); | 1385 float maxlogf = log10f(maxf); |
1179 float minlogf = log10f(minf); | 1386 float minlogf = log10f(minf); |
1180 | 1387 |
1181 float logf0 = minlogf + ((maxlogf - minlogf) * (h - y - 1)) / h; | 1388 float logf0 = minlogf + ((maxlogf - minlogf) * (h - y - 1)) / h; |
1182 float logf1 = minlogf + ((maxlogf - minlogf) * (h - y)) / h; | 1389 float logf1 = minlogf + ((maxlogf - minlogf) * (h - y)) / h; |
1183 | 1390 |
1184 float f0 = pow(10.f, logf0); | 1391 float f0 = pow(10.f, logf0); |
1185 float f1 = pow(10.f, logf1); | 1392 float f1 = pow(10.f, logf1); |
1247 int q1i = int(q1); | 1454 int q1i = int(q1); |
1248 | 1455 |
1249 int sr = m_model->getSampleRate(); | 1456 int sr = m_model->getSampleRate(); |
1250 | 1457 |
1251 for (int q = q0i; q <= q1i; ++q) { | 1458 for (int q = q0i; q <= q1i; ++q) { |
1252 int binfreq = (sr * (q + 1)) / m_windowSize; | 1459 int binfreq = (sr * q) / m_windowSize; |
1253 if (q == q0i) freqMin = binfreq; | 1460 if (q == q0i) freqMin = binfreq; |
1254 if (q == q1i) freqMax = binfreq; | 1461 if (q == q1i) freqMax = binfreq; |
1255 } | 1462 } |
1256 return true; | 1463 return true; |
1464 } | |
1465 | |
1466 bool | |
1467 SpectrogramLayer::getAdjustedYBinSourceRange(int x, int y, | |
1468 float &freqMin, float &freqMax, | |
1469 float &adjFreqMin, float &adjFreqMax) | |
1470 const | |
1471 { | |
1472 float s0 = 0, s1 = 0; | |
1473 if (!getXBinRange(x, s0, s1)) return false; | |
1474 | |
1475 float q0 = 0, q1 = 0; | |
1476 if (!getYBinRange(y, q0, q1)) return false; | |
1477 | |
1478 int s0i = int(s0 + 0.001); | |
1479 int s1i = int(s1); | |
1480 | |
1481 int q0i = int(q0 + 0.001); | |
1482 int q1i = int(q1); | |
1483 | |
1484 int sr = m_model->getSampleRate(); | |
1485 | |
1486 bool haveAdj = false; | |
1487 | |
1488 for (int q = q0i; q <= q1i; ++q) { | |
1489 | |
1490 for (int s = s0i; s <= s1i; ++s) { | |
1491 | |
1492 float binfreq = (sr * q) / m_windowSize; | |
1493 if (q == q0i) freqMin = binfreq; | |
1494 if (q == q1i) freqMax = binfreq; | |
1495 | |
1496 if ((m_frequencyAdjustment == PhaseAdjustedFrequency || | |
1497 m_frequencyAdjustment == PhaseAdjustedPeaks) && | |
1498 m_phaseAdjustCache) { | |
1499 | |
1500 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q); | |
1501 int adjust = int((signed char)cadj); | |
1502 if (adjust == SCHAR_MIN && | |
1503 m_frequencyAdjustment == PhaseAdjustedPeaks) { | |
1504 continue; | |
1505 } | |
1506 | |
1507 float nextBinFreq = (sr * (q + 1)) / m_windowSize; | |
1508 float fadjust = (adjust * (nextBinFreq - binfreq)) / 10.0;//!!! | |
1509 float f = binfreq + fadjust; | |
1510 if (!haveAdj || f < adjFreqMin) adjFreqMin = f; | |
1511 if (!haveAdj || f > adjFreqMax) adjFreqMax = f; | |
1512 haveAdj = true; | |
1513 } | |
1514 } | |
1515 } | |
1516 | |
1517 if (!haveAdj) { | |
1518 adjFreqMin = adjFreqMax = 0.0f; | |
1519 } | |
1520 | |
1521 return haveAdj; | |
1257 } | 1522 } |
1258 | 1523 |
1259 bool | 1524 bool |
1260 SpectrogramLayer::getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const | 1525 SpectrogramLayer::getXYBinSourceRange(int x, int y, float &dbMin, float &dbMax) const |
1261 { | 1526 { |
1460 int h = y1 - y0; | 1725 int h = y1 - y0; |
1461 | 1726 |
1462 // std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; | 1727 // std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; |
1463 | 1728 |
1464 QImage scaled(w, h, QImage::Format_RGB32); | 1729 QImage scaled(w, h, QImage::Format_RGB32); |
1730 scaled.fill(0); | |
1731 | |
1732 float ymag[h]; | |
1733 float ydiv[h]; | |
1734 | |
1735 size_t bins = m_windowSize / 2; | |
1736 int sr = m_model->getSampleRate(); | |
1737 | |
1738 if (m_maxFrequency > 0) { | |
1739 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1); | |
1740 if (bins > m_windowSize / 2) bins = m_windowSize / 2; | |
1741 } | |
1742 | |
1743 float maxFreq = (float(bins) * sr) / m_windowSize; | |
1465 | 1744 |
1466 m_mutex.unlock(); | 1745 m_mutex.unlock(); |
1467 | 1746 |
1747 for (int x = 0; x < w; ++x) { | |
1748 | |
1749 m_mutex.lock(); | |
1750 if (m_cacheInvalid) { | |
1751 m_mutex.unlock(); | |
1752 break; | |
1753 } | |
1754 | |
1755 for (int y = 0; y < h; ++y) { | |
1756 ymag[y] = 0.0f; | |
1757 ydiv[y] = 0.0f; | |
1758 } | |
1759 | |
1760 float s0 = 0, s1 = 0; | |
1761 | |
1762 if (!getXBinRange(x0 + x, s0, s1)) { | |
1763 assert(x <= scaled.width()); | |
1764 for (int y = 0; y < h; ++y) { | |
1765 scaled.setPixel(x, y, qRgb(0, 0, 0)); | |
1766 } | |
1767 m_mutex.unlock(); | |
1768 continue; | |
1769 } | |
1770 | |
1771 int s0i = int(s0 + 0.001); | |
1772 int s1i = int(s1); | |
1773 | |
1774 for (int q = 0; q < bins; ++q) { | |
1775 | |
1776 for (int s = s0i; s <= s1i; ++s) { | |
1777 | |
1778 float sprop = 1.0; | |
1779 if (s == s0i) sprop *= (s + 1) - s0; | |
1780 if (s == s1i) sprop *= s1 - s; | |
1781 | |
1782 float f0 = (float(q) * sr) / m_windowSize; | |
1783 float f1 = (float(q + 1) * sr) / m_windowSize; | |
1784 | |
1785 if ((m_frequencyAdjustment == PhaseAdjustedFrequency || | |
1786 m_frequencyAdjustment == PhaseAdjustedPeaks) && | |
1787 m_phaseAdjustCache) { | |
1788 | |
1789 unsigned char cadj = m_phaseAdjustCache->getValueAt(s, q); | |
1790 int adjust = int((signed char)cadj); | |
1791 | |
1792 if (adjust == SCHAR_MIN && | |
1793 m_frequencyAdjustment == PhaseAdjustedPeaks) { | |
1794 continue; | |
1795 } | |
1796 | |
1797 float fadjust = (adjust * (f1 - f0)) / 10.0;//!!! was 100 | |
1798 f0 = f1 = f0 + fadjust; | |
1799 } | |
1800 | |
1801 float y0 = h - (h * f1) / maxFreq; | |
1802 float y1 = h - (h * f0) / maxFreq; | |
1803 | |
1804 if (m_frequencyScale == LogFrequencyScale) { | |
1805 | |
1806 float maxf = m_maxFrequency; | |
1807 if (maxf == 0.0) maxf = float(sr) / 2; | |
1808 | |
1809 float minf = float(sr) / m_windowSize; | |
1810 | |
1811 float maxlogf = log10f(maxf); | |
1812 float minlogf = log10f(minf); | |
1813 | |
1814 y0 = h - (h * (log10f(f1) - minlogf)) / (maxlogf - minlogf); | |
1815 y1 = h - (h * (log10f(f0) - minlogf)) / (maxlogf - minlogf); | |
1816 } | |
1817 | |
1818 int y0i = int(y0 + 0.001); | |
1819 int y1i = int(y1); | |
1820 | |
1821 for (int y = y0i; y <= y1i; ++y) { | |
1822 | |
1823 if (y < 0 || y >= h) continue; | |
1824 | |
1825 float yprop = sprop; | |
1826 if (y == y0i) yprop *= (y + 1) - y0; | |
1827 if (y == y1i) yprop *= y1 - y; | |
1828 | |
1829 ymag[y] += yprop * m_cache->getValueAt(s, q); | |
1830 ydiv[y] += yprop; | |
1831 } | |
1832 } | |
1833 } | |
1834 | |
1835 for (int y = 0; y < h; ++y) { | |
1836 | |
1837 int pixel = 1; | |
1838 | |
1839 if (ydiv[y] > 0.0) { | |
1840 pixel = int(ymag[y] / ydiv[y]); | |
1841 if (pixel > 255) pixel = 255; | |
1842 if (pixel < 1) pixel = 1; | |
1843 } | |
1844 | |
1845 assert(x <= scaled.width()); | |
1846 QColor c = m_cache->getColour(pixel); | |
1847 scaled.setPixel(x, y, | |
1848 qRgb(c.red(), c.green(), c.blue())); | |
1849 } | |
1850 | |
1851 | |
1852 m_mutex.unlock(); | |
1853 } | |
1854 | |
1855 #ifdef NOT_DEFINED | |
1468 for (int y = 0; y < h; ++y) { | 1856 for (int y = 0; y < h; ++y) { |
1469 | 1857 |
1470 m_mutex.lock(); | 1858 m_mutex.lock(); |
1471 if (m_cacheInvalid) { | 1859 if (m_cacheInvalid) { |
1472 m_mutex.unlock(); | 1860 m_mutex.unlock(); |
1551 } | 1939 } |
1552 } | 1940 } |
1553 | 1941 |
1554 m_mutex.unlock(); | 1942 m_mutex.unlock(); |
1555 } | 1943 } |
1944 #endif | |
1556 | 1945 |
1557 paint.drawImage(x0, y0, scaled); | 1946 paint.drawImage(x0, y0, scaled); |
1558 | 1947 |
1559 if (recreateWholePixmapCache) { | 1948 if (recreateWholePixmapCache) { |
1560 delete m_pixmapCache; | 1949 delete m_pixmapCache; |
1613 | 2002 |
1614 if (!m_model || !m_model->isOK()) return ""; | 2003 if (!m_model || !m_model->isOK()) return ""; |
1615 | 2004 |
1616 float dbMin = 0, dbMax = 0; | 2005 float dbMin = 0, dbMax = 0; |
1617 float freqMin = 0, freqMax = 0; | 2006 float freqMin = 0, freqMax = 0; |
2007 float adjFreqMin = 0, adjFreqMax = 0; | |
1618 QString pitchMin, pitchMax; | 2008 QString pitchMin, pitchMax; |
1619 RealTime rtMin, rtMax; | 2009 RealTime rtMin, rtMax; |
1620 | 2010 |
1621 bool haveDb = false; | 2011 bool haveDb = false; |
1622 | 2012 |
1623 if (!getXBinSourceRange(x, rtMin, rtMax)) return ""; | 2013 if (!getXBinSourceRange(x, rtMin, rtMax)) return ""; |
1624 if (!getYBinSourceRange(y, freqMin, freqMax)) return ""; | |
1625 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true; | 2014 if (getXYBinSourceRange(x, y, dbMin, dbMax)) haveDb = true; |
2015 | |
2016 QString adjFreqText = "", adjPitchText = ""; | |
2017 | |
2018 if ((m_frequencyAdjustment == PhaseAdjustedFrequency || | |
2019 m_frequencyAdjustment == PhaseAdjustedPeaks) && | |
2020 m_phaseAdjustCache) { | |
2021 | |
2022 if (!getAdjustedYBinSourceRange(x, y, freqMin, freqMax, | |
2023 adjFreqMin, adjFreqMax)) return ""; | |
2024 | |
2025 if (adjFreqMin != adjFreqMax) { | |
2026 adjFreqText = tr("Adjusted Frequency:\t%1 - %2 Hz\n") | |
2027 .arg(adjFreqMin).arg(adjFreqMax); | |
2028 adjPitchText = tr("Adjusted Pitch:\t%3 - %4\n") | |
2029 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin)) | |
2030 .arg(Pitch::getPitchLabelForFrequency(adjFreqMax)); | |
2031 } else { | |
2032 adjFreqText = tr("Adjusted Frequency:\t%1 Hz\n") | |
2033 .arg(adjFreqMin); | |
2034 adjPitchText = tr("Adjusted Pitch:\t%2\n") | |
2035 .arg(Pitch::getPitchLabelForFrequency(adjFreqMin)); | |
2036 } | |
2037 | |
2038 } else { | |
2039 | |
2040 if (!getYBinSourceRange(y, freqMin, freqMax)) return ""; | |
2041 } | |
1626 | 2042 |
1627 //!!! want to actually do a one-off FFT to recalculate the dB value! | 2043 //!!! want to actually do a one-off FFT to recalculate the dB value! |
1628 | 2044 |
1629 QString text; | 2045 QString text; |
1630 | 2046 |
1636 text += tr("Time:\t%1\n") | 2052 text += tr("Time:\t%1\n") |
1637 .arg(rtMin.toText(true).c_str()); | 2053 .arg(rtMin.toText(true).c_str()); |
1638 } | 2054 } |
1639 | 2055 |
1640 if (freqMin != freqMax) { | 2056 if (freqMin != freqMax) { |
1641 text += tr("Frequency:\t%1 - %2 Hz\nPitch:\t%3 - %4\n") | 2057 text += tr("Frequency:\t%1 - %2 Hz\n%3Pitch:\t%4 - %5\n%6") |
1642 .arg(freqMin) | 2058 .arg(freqMin) |
1643 .arg(freqMax) | 2059 .arg(freqMax) |
2060 .arg(adjFreqText) | |
1644 .arg(Pitch::getPitchLabelForFrequency(freqMin)) | 2061 .arg(Pitch::getPitchLabelForFrequency(freqMin)) |
1645 .arg(Pitch::getPitchLabelForFrequency(freqMax)); | 2062 .arg(Pitch::getPitchLabelForFrequency(freqMax)) |
2063 .arg(adjPitchText); | |
1646 } else { | 2064 } else { |
1647 text += tr("Frequency:\t%1 Hz\nPitch:\t%2\n") | 2065 text += tr("Frequency:\t%1 Hz\n%2Pitch:\t%3\n%4") |
1648 .arg(freqMin) | 2066 .arg(freqMin) |
1649 .arg(Pitch::getPitchLabelForFrequency(freqMin)); | 2067 .arg(adjFreqText) |
2068 .arg(Pitch::getPitchLabelForFrequency(freqMin)) | |
2069 .arg(adjPitchText); | |
1650 } | 2070 } |
1651 | 2071 |
1652 if (haveDb) { | 2072 if (haveDb) { |
1653 if (lrintf(dbMin) != lrintf(dbMax)) { | 2073 if (lrintf(dbMin) != lrintf(dbMax)) { |
1654 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax)); | 2074 text += tr("dB:\t%1 - %2").arg(lrintf(dbMin)).arg(lrintf(dbMax)); |
1740 | 2160 |
1741 s += QString("channel=\"%1\" " | 2161 s += QString("channel=\"%1\" " |
1742 "windowSize=\"%2\" " | 2162 "windowSize=\"%2\" " |
1743 "windowType=\"%3\" " | 2163 "windowType=\"%3\" " |
1744 "windowOverlap=\"%4\" " | 2164 "windowOverlap=\"%4\" " |
1745 "gain=\"%5\" " | 2165 "gain=\"%5\" ") |
1746 "maxFrequency=\"%6\" " | |
1747 "colourScale=\"%7\" " | |
1748 "colourScheme=\"%8\" " | |
1749 "frequencyScale=\"%9\"") | |
1750 .arg(m_channel) | 2166 .arg(m_channel) |
1751 .arg(m_windowSize) | 2167 .arg(m_windowSize) |
1752 .arg(m_windowType) | 2168 .arg(m_windowType) |
1753 .arg(m_windowOverlap) | 2169 .arg(m_windowOverlap) |
1754 .arg(m_gain) | 2170 .arg(m_gain); |
2171 | |
2172 s += QString("maxFrequency=\"%1\" " | |
2173 "colourScale=\"%2\" " | |
2174 "colourScheme=\"%3\" " | |
2175 "frequencyScale=\"%4\" " | |
2176 "frequencyAdjustment=\"%5\"") | |
1755 .arg(m_maxFrequency) | 2177 .arg(m_maxFrequency) |
1756 .arg(m_colourScale) | 2178 .arg(m_colourScale) |
1757 .arg(m_colourScheme) | 2179 .arg(m_colourScheme) |
1758 .arg(m_frequencyScale); | 2180 .arg(m_frequencyScale) |
2181 .arg(m_frequencyAdjustment); | |
1759 | 2182 |
1760 return Layer::toXmlString(indent, extraAttributes + " " + s); | 2183 return Layer::toXmlString(indent, extraAttributes + " " + s); |
1761 } | 2184 } |
1762 | 2185 |
1763 void | 2186 void |
1793 if (ok) setColourScheme(colourScheme); | 2216 if (ok) setColourScheme(colourScheme); |
1794 | 2217 |
1795 FrequencyScale frequencyScale = (FrequencyScale) | 2218 FrequencyScale frequencyScale = (FrequencyScale) |
1796 attributes.value("frequencyScale").toInt(&ok); | 2219 attributes.value("frequencyScale").toInt(&ok); |
1797 if (ok) setFrequencyScale(frequencyScale); | 2220 if (ok) setFrequencyScale(frequencyScale); |
2221 | |
2222 FrequencyAdjustment frequencyAdjustment = (FrequencyAdjustment) | |
2223 attributes.value("frequencyAdjustment").toInt(&ok); | |
2224 if (ok) setFrequencyAdjustment(frequencyAdjustment); | |
1798 } | 2225 } |
1799 | 2226 |
1800 | 2227 |
1801 #ifdef INCLUDE_MOCFILES | 2228 #ifdef INCLUDE_MOCFILES |
1802 #include "SpectrogramLayer.moc.cpp" | 2229 #include "SpectrogramLayer.moc.cpp" |