comparison layer/SpectrogramLayer.cpp @ 133:9e6b3e239b9d

* Add zoom thumbwheels to Pane. Implement horizontal thumbwheel, and vertical depending on layer type (supported for waveform and spectrogram, though wrong for log-scale spectrogram at the moment). * Add bare bones of a spectrum layer. * Add window icon * Add shortcut for "insert time instant" on laptops without keypad enter (";") * Delete FFT processing thread when it exits (at least, next time we're asked for something interesting) * Get audio file extensions from the file readers, and thus from libsndfile for the wave file reader -- leads to rather a wide combo box in file dialog though * Better refresh order for spectrogram (redraw centre section first)
author Chris Cannam
date Fri, 04 Aug 2006 17:01:37 +0000
parents 5d3a483856ff
children 13949a6970ab
comparison
equal deleted inserted replaced
132:5d3a483856ff 133:9e6b3e239b9d
33 #include <iostream> 33 #include <iostream>
34 34
35 #include <cassert> 35 #include <cassert>
36 #include <cmath> 36 #include <cmath>
37 37
38 //#define DEBUG_SPECTROGRAM_REPAINT 1 38 #define DEBUG_SPECTROGRAM_REPAINT 1
39 39
40 SpectrogramLayer::SpectrogramLayer(Configuration config) : 40 SpectrogramLayer::SpectrogramLayer(Configuration config) :
41 Layer(), 41 Layer(),
42 m_model(0), 42 m_model(0),
43 m_channel(0), 43 m_channel(0),
55 m_colourScheme(DefaultColours), 55 m_colourScheme(DefaultColours),
56 m_frequencyScale(LinearFrequencyScale), 56 m_frequencyScale(LinearFrequencyScale),
57 m_binDisplay(AllBins), 57 m_binDisplay(AllBins),
58 m_normalizeColumns(false), 58 m_normalizeColumns(false),
59 m_normalizeVisibleArea(false), 59 m_normalizeVisibleArea(false),
60 m_lastEmittedZoomStep(-1),
60 m_updateTimer(0), 61 m_updateTimer(0),
61 m_candidateFillStartFrame(0), 62 m_candidateFillStartFrame(0),
62 m_exiting(false) 63 m_exiting(false)
63 { 64 {
64 if (config == MelodicRange) { 65 if (config == MelodicRange) {
485 case 6: setMinFrequency(500); break; 486 case 6: setMinFrequency(500); break;
486 case 7: setMinFrequency(1000); break; 487 case 7: setMinFrequency(1000); break;
487 case 8: setMinFrequency(4000); break; 488 case 8: setMinFrequency(4000); break;
488 case 9: setMinFrequency(10000); break; 489 case 9: setMinFrequency(10000); break;
489 } 490 }
491 int vs = getCurrentVerticalZoomStep();
492 if (vs != m_lastEmittedZoomStep) {
493 emit verticalZoomChanged();
494 m_lastEmittedZoomStep = vs;
495 }
490 } else if (name == "Max Frequency") { 496 } else if (name == "Max Frequency") {
491 switch (value) { 497 switch (value) {
492 case 0: setMaxFrequency(500); break; 498 case 0: setMaxFrequency(500); break;
493 case 1: setMaxFrequency(1000); break; 499 case 1: setMaxFrequency(1000); break;
494 case 2: setMaxFrequency(1500); break; 500 case 2: setMaxFrequency(1500); break;
499 case 7: setMaxFrequency(12000); break; 505 case 7: setMaxFrequency(12000); break;
500 case 8: setMaxFrequency(16000); break; 506 case 8: setMaxFrequency(16000); break;
501 default: 507 default:
502 case 9: setMaxFrequency(0); break; 508 case 9: setMaxFrequency(0); break;
503 } 509 }
510 int vs = getCurrentVerticalZoomStep();
511 if (vs != m_lastEmittedZoomStep) {
512 emit verticalZoomChanged();
513 m_lastEmittedZoomStep = vs;
514 }
504 } else if (name == "Colour Scale") { 515 } else if (name == "Colour Scale") {
505 switch (value) { 516 switch (value) {
506 default: 517 default:
507 case 0: setColourScale(LinearColourScale); break; 518 case 0: setColourScale(LinearColourScale); break;
508 case 1: setColourScale(MeterColourScale); break; 519 case 1: setColourScale(MeterColourScale); break;
1157 1168
1158 case dBColourScale: 1169 case dBColourScale:
1159 //!!! experiment with normalizing the visible area this way. 1170 //!!! experiment with normalizing the visible area this way.
1160 //In any case, we need to have some indication of what the dB 1171 //In any case, we need to have some indication of what the dB
1161 //scale is relative to. 1172 //scale is relative to.
1162 input = 10.f * log10f(input / max); 1173 input = input / max;
1174 if (input > 0.f) {
1175 input = 10.f * log10f(input);
1176 } else {
1177 input = thresh;
1178 }
1163 if (min > 0.f) { 1179 if (min > 0.f) {
1164 thresh = 10.f * log10f(min); 1180 thresh = 10.f * log10f(min);
1165 if (thresh < -80.f) thresh = -80.f; 1181 if (thresh < -80.f) thresh = -80.f;
1166 } 1182 }
1167 input = (input - thresh) / (-thresh); 1183 input = (input - thresh) / (-thresh);
1171 break; 1187 break;
1172 1188
1173 case OtherColourScale: 1189 case OtherColourScale:
1174 //!!! the "Other" scale is just where our current experiments go 1190 //!!! the "Other" scale is just where our current experiments go
1175 //!!! power rather than v 1191 //!!! power rather than v
1176 input = 10.f * log10f((input * input) / (max * max)); 1192 input = (input * input) / (max * max);
1193 if (input > 0.f) {
1194 input = 10.f * log10f(input);
1195 } else {
1196 input = thresh;
1197 }
1177 if (min > 0.f) { 1198 if (min > 0.f) {
1178 thresh = 10.f * log10f(min * min); 1199 thresh = 10.f * log10f(min * min);
1179 if (thresh < -80.f) thresh = -80.f; 1200 if (thresh < -80.f) thresh = -80.f;
1180 } 1201 }
1181 input = (input - thresh) / (-thresh); 1202 input = (input - thresh) / (-thresh);
1655 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl; 1676 std::cerr << "SpectrogramLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", m_updateTimer " << m_updateTimer << std::endl;
1656 1677
1657 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl; 1678 std::cerr << "rect is " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << std::endl;
1658 #endif 1679 #endif
1659 1680
1660 long sf = v->getStartFrame(); 1681 long startFrame = v->getStartFrame();
1661 if (sf < 0) m_candidateFillStartFrame = 0; 1682 if (startFrame < 0) m_candidateFillStartFrame = 0;
1662 else m_candidateFillStartFrame = sf; 1683 else m_candidateFillStartFrame = startFrame;
1663 1684
1664 if (!m_model || !m_model->isOK() || !m_model->isReady()) { 1685 if (!m_model || !m_model->isOK() || !m_model->isReady()) {
1665 return; 1686 return;
1666 } 1687 }
1667 1688
1693 1714
1694 #ifdef DEBUG_SPECTROGRAM_REPAINT 1715 #ifdef DEBUG_SPECTROGRAM_REPAINT
1695 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl; 1716 std::cerr << "SpectrogramLayer::paint(): Still cacheing = " << stillCacheing << std::endl;
1696 #endif 1717 #endif
1697 1718
1698 long startFrame = v->getStartFrame();
1699 int zoomLevel = v->getZoomLevel(); 1719 int zoomLevel = v->getZoomLevel();
1700 1720
1701 int x0 = 0; 1721 int x0 = 0;
1702 int x1 = v->width(); 1722 int x1 = v->width();
1703 int y0 = 0; 1723 int y0 = 0;
1822 y0 = rect.top(); 1842 y0 = rect.top();
1823 y1 = rect.bottom() + 1; 1843 y1 = rect.bottom() + 1;
1824 } 1844 }
1825 */ 1845 */
1826 1846
1847 if (updateViewMagnitudes(v)) {
1848 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1849 recreateWholePixmapCache = true;
1850 } else {
1851 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1852 }
1853
1827 if (recreateWholePixmapCache) { 1854 if (recreateWholePixmapCache) {
1828 x0 = 0; 1855 x0 = 0;
1829 x1 = v->width(); 1856 x1 = v->width();
1830 }
1831
1832 if (updateViewMagnitudes(v)) {
1833 std::cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1834 } else {
1835 std::cerr << "No change in magnitude range [" << m_viewMags[v].getMin() << "->" << m_viewMags[v].getMax() << "]" << std::endl;
1836 } 1857 }
1837 1858
1838 int paintBlockWidth = (300000 / zoomLevel); 1859 int paintBlockWidth = (300000 / zoomLevel);
1839 if (paintBlockWidth < 20) paintBlockWidth = 20; 1860 if (paintBlockWidth < 20) paintBlockWidth = 20;
1840 1861
1873 x1 - std::min(vx0, x0)), 1894 x1 - std::min(vx0, x0)),
1874 cache.validArea.height()); 1895 cache.validArea.height());
1875 1896
1876 } else { 1897 } else {
1877 if (x1 > x0 + paintBlockWidth) { 1898 if (x1 > x0 + paintBlockWidth) {
1878 x1 = x0 + paintBlockWidth; 1899 int sfx = x1;
1900 if (startFrame < 0) sfx = v->getXForFrame(0);
1901 if (sfx >= x0 && sfx + paintBlockWidth <= x1) {
1902 x0 = sfx;
1903 x1 = x0 + paintBlockWidth;
1904 } else {
1905 int mid = (x1 + x0) / 2;
1906 x0 = mid - paintBlockWidth/2;
1907 x1 = x0 + paintBlockWidth;
1908 }
1879 } 1909 }
1880 cache.validArea = QRect(x0, 0, x1 - x0, v->height()); 1910 cache.validArea = QRect(x0, 0, x1 - x0, v->height());
1881 } 1911 }
1882 1912
1883 int w = x1 - x0; 1913 int w = x1 - x0;
2154 2184
2155 std::cerr << "SpectrogramLayer: illuminate " 2185 std::cerr << "SpectrogramLayer: illuminate "
2156 << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl; 2186 << x0 << "," << y1 << " -> " << x1 << "," << y0 << std::endl;
2157 2187
2158 paint.setPen(Qt::white); 2188 paint.setPen(Qt::white);
2189
2190 //!!! should we be using paintCrosshairs for this?
2191
2159 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1); 2192 paint.drawRect(x0, y1, x1 - x0 + 1, y0 - y1 + 1);
2160 } 2193 }
2161 } 2194 }
2162 2195
2163 float 2196 float
2191 2224
2192 bool 2225 bool
2193 SpectrogramLayer::getValueExtents(float &min, float &max, 2226 SpectrogramLayer::getValueExtents(float &min, float &max,
2194 bool &logarithmic, QString &unit) const 2227 bool &logarithmic, QString &unit) const
2195 { 2228 {
2196 min = getEffectiveMinFrequency(); 2229 //!!!
2197 max = getEffectiveMaxFrequency(); 2230 // min = getEffectiveMinFrequency();
2231 // max = getEffectiveMaxFrequency();
2232
2233 if (!m_model) return false;
2234
2235 int sr = m_model->getSampleRate();
2236 min = float(sr) / m_fftSize;
2237 max = float(sr) / 2;
2238
2198 logarithmic = (m_frequencyScale == LogFrequencyScale); 2239 logarithmic = (m_frequencyScale == LogFrequencyScale);
2199 unit = "Hz"; 2240 unit = "Hz";
2200 return true; 2241 return true;
2201 } 2242 }
2202 2243
2225 2266
2226 m_minFrequency = minf; 2267 m_minFrequency = minf;
2227 m_maxFrequency = maxf; 2268 m_maxFrequency = maxf;
2228 2269
2229 emit layerParametersChanged(); 2270 emit layerParametersChanged();
2271
2272 int vs = getCurrentVerticalZoomStep();
2273 if (vs != m_lastEmittedZoomStep) {
2274 emit verticalZoomChanged();
2275 m_lastEmittedZoomStep = vs;
2276 }
2230 2277
2231 return true; 2278 return true;
2232 } 2279 }
2233 2280
2234 bool 2281 bool
2710 py = y; 2757 py = y;
2711 } 2758 }
2712 } 2759 }
2713 } 2760 }
2714 2761
2762 int
2763 SpectrogramLayer::getVerticalZoomSteps(int &defaultStep) const
2764 {
2765 defaultStep = 0;
2766 return 20; //!!!
2767 }
2768
2769 int
2770 SpectrogramLayer::getCurrentVerticalZoomStep() const
2771 {
2772 if (!m_model) return 0;
2773
2774 float dmin, dmax;
2775 getDisplayExtents(dmin, dmax);
2776
2777 float mmin, mmax;
2778 int sr = m_model->getSampleRate();
2779 mmin = float(sr) / m_fftSize;
2780 mmax = float(sr) / 2;
2781
2782 float mdist = mmax - mmin;
2783 float ddist = dmax - dmin;
2784
2785 int n = 0;
2786 float s2 = sqrtf(2);
2787 while (mdist > ddist) {
2788 if (++n > 20) break;
2789 mdist /= s2;
2790 }
2791
2792 return n;
2793 }
2794
2795 void
2796 SpectrogramLayer::setVerticalZoomStep(int step)
2797 {
2798 //!!! does not do the right thing for log scale
2799
2800 float dmin, dmax;
2801 getDisplayExtents(dmin, dmax);
2802
2803 float mmin, mmax;
2804 int sr = m_model->getSampleRate();
2805 mmin = float(sr) / m_fftSize;
2806 mmax = float(sr) / 2;
2807
2808 float ddist = mmax - mmin;
2809
2810 int n = 0;
2811 float s2 = sqrtf(2);
2812 while (n < step) {
2813 ddist /= s2;
2814 ++n;
2815 }
2816
2817 float dmid = (dmax + dmin) / 2;
2818 float newmin = dmid - ddist / 2;
2819 float newmax = dmid + ddist / 2;
2820
2821 if (newmin < mmin) newmin = mmin;
2822 if (newmax > mmax) newmax = mmax;
2823
2824 setMinFrequency(newmin);
2825 setMaxFrequency(newmax);
2826 }
2827
2715 QString 2828 QString
2716 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const 2829 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const
2717 { 2830 {
2718 QString s; 2831 QString s;
2719 2832
2813 bool normalizeColumns = 2926 bool normalizeColumns =
2814 (attributes.value("normalizeColumns").trimmed() == "true"); 2927 (attributes.value("normalizeColumns").trimmed() == "true");
2815 setNormalizeColumns(normalizeColumns); 2928 setNormalizeColumns(normalizeColumns);
2816 } 2929 }
2817 2930
2818
2819 #ifdef INCLUDE_MOCFILES
2820 #include "SpectrogramLayer.moc.cpp"
2821 #endif
2822