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