comparison layer/SpectrogramLayer.cpp @ 97:a0e7edf9703a

* Use fractional window overlaps in the spectrogram, instead of percentages (90% is kind of meaningless when none of your window sizes are divisible by 10!) * ResizeableBitmap -> ResizeableBitset and the odd other tidy up
author Chris Cannam
date Wed, 10 May 2006 11:43:52 +0000
parents 095916d7ed4d
children 0f36cdf407a6
comparison
equal deleted inserted replaced
96:095916d7ed4d 97:a0e7edf9703a
32 #include <iostream> 32 #include <iostream>
33 33
34 #include <cassert> 34 #include <cassert>
35 #include <cmath> 35 #include <cmath>
36 36
37 //#define DEBUG_SPECTROGRAM_REPAINT 1 37 #define DEBUG_SPECTROGRAM_REPAINT 1
38 38
39 static double mod(double x, double y) 39 static double mod(double x, double y)
40 { 40 {
41 double a = floor(x / y); 41 double a = floor(x / y);
42 double b = x - (y * a); 42 double b = x - (y * a);
53 Layer(), 53 Layer(),
54 m_model(0), 54 m_model(0),
55 m_channel(0), 55 m_channel(0),
56 m_windowSize(1024), 56 m_windowSize(1024),
57 m_windowType(HanningWindow), 57 m_windowType(HanningWindow),
58 m_windowOverlap(50), 58 m_windowHopLevel(2),
59 m_gain(1.0), 59 m_gain(1.0),
60 m_threshold(0.0), 60 m_threshold(0.0),
61 m_colourRotation(0), 61 m_colourRotation(0),
62 m_minFrequency(0), 62 m_minFrequency(0),
63 m_maxFrequency(8000), 63 m_maxFrequency(8000),
75 m_lastFillExtent(0), 75 m_lastFillExtent(0),
76 m_exiting(false) 76 m_exiting(false)
77 { 77 {
78 if (config == MelodicRange) { 78 if (config == MelodicRange) {
79 setWindowSize(8192); 79 setWindowSize(8192);
80 setWindowOverlap(90); 80 setWindowHopLevel(4);
81 setWindowType(ParzenWindow); 81 setWindowType(ParzenWindow);
82 setMaxFrequency(1000); 82 setMaxFrequency(1000);
83 setColourScale(LinearColourScale); 83 setColourScale(LinearColourScale);
84 } else if (config == MelodicPeaks) { 84 } else if (config == MelodicPeaks) {
85 setWindowSize(4096); 85 setWindowSize(4096);
86 setWindowOverlap(90); 86 setWindowHopLevel(5);
87 setWindowType(BlackmanWindow); 87 setWindowType(BlackmanWindow);
88 setMaxFrequency(2000); 88 setMaxFrequency(2000);
89 setMinFrequency(40); 89 setMinFrequency(40);
90 setFrequencyScale(LogFrequencyScale); 90 setFrequencyScale(LogFrequencyScale);
91 setColourScale(MeterColourScale); 91 setColourScale(MeterColourScale);
141 PropertyList list; 141 PropertyList list;
142 list.push_back("Colour"); 142 list.push_back("Colour");
143 list.push_back("Colour Scale"); 143 list.push_back("Colour Scale");
144 list.push_back("Window Type"); 144 list.push_back("Window Type");
145 list.push_back("Window Size"); 145 list.push_back("Window Size");
146 list.push_back("Window Overlap"); 146 list.push_back("Window Increment");
147 list.push_back("Normalize Columns"); 147 list.push_back("Normalize Columns");
148 list.push_back("Bin Display"); 148 list.push_back("Bin Display");
149 list.push_back("Threshold"); 149 list.push_back("Threshold");
150 list.push_back("Gain"); 150 list.push_back("Gain");
151 list.push_back("Colour Rotation"); 151 list.push_back("Colour Rotation");
160 { 160 {
161 if (name == "Colour") return tr("Colour"); 161 if (name == "Colour") return tr("Colour");
162 if (name == "Colour Scale") return tr("Colour Scale"); 162 if (name == "Colour Scale") return tr("Colour Scale");
163 if (name == "Window Type") return tr("Window Type"); 163 if (name == "Window Type") return tr("Window Type");
164 if (name == "Window Size") return tr("Window Size"); 164 if (name == "Window Size") return tr("Window Size");
165 if (name == "Window Overlap") return tr("Window Overlap"); 165 if (name == "Window Increment") return tr("Window Increment");
166 if (name == "Normalize Columns") return tr("Normalize Columns"); 166 if (name == "Normalize Columns") return tr("Normalize Columns");
167 if (name == "Bin Display") return tr("Bin Display"); 167 if (name == "Bin Display") return tr("Bin Display");
168 if (name == "Threshold") return tr("Threshold"); 168 if (name == "Threshold") return tr("Threshold");
169 if (name == "Gain") return tr("Gain"); 169 if (name == "Gain") return tr("Gain");
170 if (name == "Colour Rotation") return tr("Colour Rotation"); 170 if (name == "Colour Rotation") return tr("Colour Rotation");
187 QString 187 QString
188 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const 188 SpectrogramLayer::getPropertyGroupName(const PropertyName &name) const
189 { 189 {
190 if (name == "Window Size" || 190 if (name == "Window Size" ||
191 name == "Window Type" || 191 name == "Window Type" ||
192 name == "Window Overlap") return tr("Window"); 192 name == "Window Increment") return tr("Window");
193 if (name == "Colour" || 193 if (name == "Colour" ||
194 name == "Gain" || 194 name == "Gain" ||
195 name == "Threshold" || 195 name == "Threshold" ||
196 name == "Colour Rotation") return tr("Colour"); 196 name == "Colour Rotation") return tr("Colour");
197 if (name == "Normalize Columns" || 197 if (name == "Normalize Columns" ||
267 267
268 deft = 0; 268 deft = 0;
269 int ws = m_windowSize; 269 int ws = m_windowSize;
270 while (ws > 32) { ws >>= 1; deft ++; } 270 while (ws > 32) { ws >>= 1; deft ++; }
271 271
272 } else if (name == "Window Overlap") { 272 } else if (name == "Window Increment") {
273 273
274 *min = 0; 274 *min = 0;
275 *max = 4; 275 *max = 5;
276 276
277 deft = m_windowOverlap / 25; 277 deft = m_windowHopLevel;
278 if (m_windowOverlap == 90) deft = 4;
279 278
280 } else if (name == "Min Frequency") { 279 } else if (name == "Min Frequency") {
281 280
282 *min = 0; 281 *min = 0;
283 *max = 9; 282 *max = 9;
374 } 373 }
375 } 374 }
376 if (name == "Window Size") { 375 if (name == "Window Size") {
377 return QString("%1").arg(32 << value); 376 return QString("%1").arg(32 << value);
378 } 377 }
379 if (name == "Window Overlap") { 378 if (name == "Window Increment") {
380 switch (value) { 379 switch (value) {
381 default: 380 default:
382 case 0: return tr("0%"); 381 case 0: return tr("1/1");
383 case 1: return tr("25%"); 382 case 1: return tr("3/4");
384 case 2: return tr("50%"); 383 case 2: return tr("1/2");
385 case 3: return tr("75%"); 384 case 3: return tr("1/4");
386 case 4: return tr("90%"); 385 case 4: return tr("1/8");
386 case 5: return tr("1/16");
387 } 387 }
388 } 388 }
389 if (name == "Min Frequency") { 389 if (name == "Min Frequency") {
390 switch (value) { 390 switch (value) {
391 default: 391 default:
457 } 457 }
458 } else if (name == "Window Type") { 458 } else if (name == "Window Type") {
459 setWindowType(WindowType(value)); 459 setWindowType(WindowType(value));
460 } else if (name == "Window Size") { 460 } else if (name == "Window Size") {
461 setWindowSize(32 << value); 461 setWindowSize(32 << value);
462 } else if (name == "Window Overlap") { 462 } else if (name == "Window Increment") {
463 if (value == 4) setWindowOverlap(90); 463 setWindowHopLevel(value);
464 else setWindowOverlap(25 * value);
465 } else if (name == "Min Frequency") { 464 } else if (name == "Min Frequency") {
466 switch (value) { 465 switch (value) {
467 default: 466 default:
468 case 0: setMinFrequency(0); break; 467 case 0: setMinFrequency(0); break;
469 case 1: setMinFrequency(10); break; 468 case 1: setMinFrequency(10); break;
586 { 585 {
587 return m_windowSize; 586 return m_windowSize;
588 } 587 }
589 588
590 void 589 void
591 SpectrogramLayer::setWindowOverlap(size_t wi) 590 SpectrogramLayer::setWindowHopLevel(size_t v)
592 { 591 {
593 if (m_windowOverlap == wi) return; 592 if (m_windowHopLevel == v) return;
594 593
595 m_mutex.lock(); 594 m_mutex.lock();
596 m_cacheInvalid = true; 595 m_cacheInvalid = true;
597 invalidatePixmapCaches(); 596 invalidatePixmapCaches();
598 597
599 m_windowOverlap = wi; 598 m_windowHopLevel = v;
600 599
601 m_mutex.unlock(); 600 m_mutex.unlock();
602 601
603 emit layerParametersChanged(); 602 emit layerParametersChanged();
604 603
605 fillCache(); 604 fillCache();
606 } 605 }
607 606
608 size_t 607 size_t
609 SpectrogramLayer::getWindowOverlap() const 608 SpectrogramLayer::getWindowHopLevel() const
610 { 609 {
611 return m_windowOverlap; 610 return m_windowHopLevel;
612 } 611 }
613 612
614 void 613 void
615 SpectrogramLayer::setWindowType(WindowType w) 614 SpectrogramLayer::setWindowType(WindowType w)
616 { 615 {
1153 1152
1154 fftw_execute(plan); 1153 fftw_execute(plan);
1155 1154
1156 double factor = 0.0; 1155 double factor = 0.0;
1157 1156
1158 // Calculate magnitude and phase from real and imaginary in
1159 // output[i][0] and output[i][1] respectively, and store the phase
1160 // straight into cache and the magnitude back into output[i][0]
1161 // (because we'll need to know the normalization factor,
1162 // i.e. maximum magnitude in this column, before we can store it)
1163
1164 for (size_t i = 0; i < windowSize/2; ++i) { 1157 for (size_t i = 0; i < windowSize/2; ++i) {
1165 1158
1166 double mag = sqrt(output[i][0] * output[i][0] + 1159 double mag = sqrt(output[i][0] * output[i][0] +
1167 output[i][1] * output[i][1]); 1160 output[i][1] * output[i][1]);
1168 mag /= windowSize / 2; 1161 mag /= windowSize / 2;
1308 1301
1309 WindowType windowType = m_layer.m_windowType; 1302 WindowType windowType = m_layer.m_windowType;
1310 size_t windowSize = m_layer.m_windowSize; 1303 size_t windowSize = m_layer.m_windowSize;
1311 size_t windowIncrement = m_layer.getWindowIncrement(); 1304 size_t windowIncrement = m_layer.getWindowIncrement();
1312 1305
1306 std::cerr << "\nWINDOW INCREMENT: " << windowIncrement << " (for hop level " << m_layer.m_windowHopLevel << ")\n" << std::endl;
1307
1313 size_t visibleStart = m_layer.m_candidateFillStartFrame; 1308 size_t visibleStart = m_layer.m_candidateFillStartFrame;
1314 visibleStart = (visibleStart / windowIncrement) * windowIncrement; 1309 visibleStart = (visibleStart / windowIncrement) * windowIncrement;
1315 1310
1316 size_t width = (end - start) / windowIncrement + 1; 1311 size_t width = (end - start) / windowIncrement + 1;
1317 size_t height = windowSize / 2; 1312 size_t height = windowSize / 2;
1673 1668
1674 for (int q = q0i; q <= q1i; ++q) { 1669 for (int q = q0i; q <= q1i; ++q) {
1675 for (int s = s0i; s <= s1i; ++s) { 1670 for (int s = s0i; s <= s1i; ++s) {
1676 if (s >= 0 && q >= 0 && s < cw && q < ch) { 1671 if (s >= 0 && q >= 0 && s < cw && q < ch) {
1677 1672
1678 if (!m_cache->haveColumnAt(s)) continue; 1673 if (!m_cache->haveSetColumnAt(s)) continue;
1679 1674
1680 float value; 1675 float value;
1681 1676
1682 value = m_cache->getPhaseAt(s, q); 1677 value = m_cache->getPhaseAt(s, q);
1683 if (!have || value < phaseMin) { phaseMin = value; } 1678 if (!have || value < phaseMin) { phaseMin = value; }
1955 1950
1956 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) { 1951 if (m_drawBuffer.width() < w || m_drawBuffer.height() < h) {
1957 m_drawBuffer = QImage(w, h, QImage::Format_RGB32); 1952 m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
1958 } 1953 }
1959 1954
1960 // if (m_binDisplay == PeakFrequencies) { 1955 m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
1961 m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
1962 // }
1963 1956
1964 int sr = m_model->getSampleRate(); 1957 int sr = m_model->getSampleRate();
1965 1958
1966 size_t bins = m_windowSize / 2; 1959 size_t bins = m_windowSize / 2;
1967 if (m_maxFrequency > 0) { 1960 if (m_maxFrequency > 0) {
2029 } 2022 }
2030 } 2023 }
2031 2024
2032 for (int s = s0i; s <= s1i; ++s) { 2025 for (int s = s0i; s <= s1i; ++s) {
2033 2026
2034 if (!m_cache->haveColumnAt(s)) continue; 2027 if (!m_cache->haveSetColumnAt(s)) continue;
2035 2028
2036 for (size_t q = minbin; q < bins; ++q) { 2029 for (size_t q = minbin; q < bins; ++q) {
2037 2030
2038 float y0 = yval[q + 1]; 2031 float y0 = yval[q + 1];
2039 float y1 = yval[q]; 2032 float y1 = yval[q];
2577 QString s; 2570 QString s;
2578 2571
2579 s += QString("channel=\"%1\" " 2572 s += QString("channel=\"%1\" "
2580 "windowSize=\"%2\" " 2573 "windowSize=\"%2\" "
2581 "windowType=\"%3\" " 2574 "windowType=\"%3\" "
2582 "windowOverlap=\"%4\" " 2575 "windowHopLevel=\"%4\" "
2583 "gain=\"%5\" " 2576 "gain=\"%5\" "
2584 "threshold=\"%6\" ") 2577 "threshold=\"%6\" ")
2585 .arg(m_channel) 2578 .arg(m_channel)
2586 .arg(m_windowSize) 2579 .arg(m_windowSize)
2587 .arg(m_windowType) 2580 .arg(m_windowType)
2588 .arg(m_windowOverlap) 2581 .arg(m_windowHopLevel)
2589 .arg(m_gain) 2582 .arg(m_gain)
2590 .arg(m_threshold); 2583 .arg(m_threshold);
2591 2584
2592 s += QString("minFrequency=\"%1\" " 2585 s += QString("minFrequency=\"%1\" "
2593 "maxFrequency=\"%2\" " 2586 "maxFrequency=\"%2\" "
2622 2615
2623 WindowType windowType = (WindowType) 2616 WindowType windowType = (WindowType)
2624 attributes.value("windowType").toInt(&ok); 2617 attributes.value("windowType").toInt(&ok);
2625 if (ok) setWindowType(windowType); 2618 if (ok) setWindowType(windowType);
2626 2619
2627 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok); 2620 size_t windowHopLevel = attributes.value("windowHopLevel").toUInt(&ok);
2628 if (ok) setWindowOverlap(windowOverlap); 2621 if (ok) setWindowHopLevel(windowHopLevel);
2622 else {
2623 size_t windowOverlap = attributes.value("windowOverlap").toUInt(&ok);
2624 // a percentage value
2625 if (ok) {
2626 if (windowOverlap == 0) setWindowHopLevel(0);
2627 else if (windowOverlap == 25) setWindowHopLevel(1);
2628 else if (windowOverlap == 50) setWindowHopLevel(2);
2629 else if (windowOverlap == 75) setWindowHopLevel(3);
2630 else if (windowOverlap == 90) setWindowHopLevel(4);
2631 }
2632 }
2629 2633
2630 float gain = attributes.value("gain").toFloat(&ok); 2634 float gain = attributes.value("gain").toFloat(&ok);
2631 if (ok) setGain(gain); 2635 if (ok) setGain(gain);
2632 2636
2633 float threshold = attributes.value("threshold").toFloat(&ok); 2637 float threshold = attributes.value("threshold").toFloat(&ok);