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