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"