comparison layer/SpectrogramLayer.cpp @ 40:3be4438b186d

* More fixes, tidying etc in spectrogram layer. Added a piano keyboard for the log frequency scale mode.
author Chris Cannam
date Fri, 24 Feb 2006 15:46:47 +0000
parents beb801473743
children f2c416cbdaa9
comparison
equal deleted inserted replaced
39:5ce844ec854a 40:3be4438b186d
65 setColourScale(LinearColourScale); 65 setColourScale(LinearColourScale);
66 } else if (config == MelodicPeaks) { 66 } else if (config == MelodicPeaks) {
67 setWindowSize(4096); 67 setWindowSize(4096);
68 setWindowOverlap(90); 68 setWindowOverlap(90);
69 setWindowType(BlackmanWindow); 69 setWindowType(BlackmanWindow);
70 setMaxFrequency(1500); 70 setMaxFrequency(2000);
71 setMinFrequency(40); 71 setMinFrequency(40);
72 setFrequencyScale(LogFrequencyScale); 72 setFrequencyScale(LogFrequencyScale);
73 setColourScale(dBColourScale); 73 setColourScale(dBColourScale);
74 setBinDisplay(PeakFrequencies); 74 setBinDisplay(PeakFrequencies);
75 setNormalizeColumns(true); 75 setNormalizeColumns(true);
316 case 0: return tr("Default"); 316 case 0: return tr("Default");
317 case 1: return tr("White on Black"); 317 case 1: return tr("White on Black");
318 case 2: return tr("Black on White"); 318 case 2: return tr("Black on White");
319 case 3: return tr("Red on Blue"); 319 case 3: return tr("Red on Blue");
320 case 4: return tr("Yellow on Black"); 320 case 4: return tr("Yellow on Black");
321 case 5: return tr("Red on Black"); 321 case 5: return tr("Fruit Salad");
322 } 322 }
323 } 323 }
324 if (name == tr("Colour Scale")) { 324 if (name == tr("Colour Scale")) {
325 switch (value) { 325 switch (value) {
326 default: 326 default:
420 case 0: setColourScheme(DefaultColours); break; 420 case 0: setColourScheme(DefaultColours); break;
421 case 1: setColourScheme(WhiteOnBlack); break; 421 case 1: setColourScheme(WhiteOnBlack); break;
422 case 2: setColourScheme(BlackOnWhite); break; 422 case 2: setColourScheme(BlackOnWhite); break;
423 case 3: setColourScheme(RedOnBlue); break; 423 case 3: setColourScheme(RedOnBlue); break;
424 case 4: setColourScheme(YellowOnBlack); break; 424 case 4: setColourScheme(YellowOnBlack); break;
425 case 5: setColourScheme(RedOnBlack); break; 425 case 5: setColourScheme(Rainbow); break;
426 } 426 }
427 } else if (name == tr("Window Type")) { 427 } else if (name == tr("Window Type")) {
428 setWindowType(WindowType(value)); 428 setWindowType(WindowType(value));
429 } else if (name == tr("Window Size")) { 429 } else if (name == tr("Window Size")) {
430 setWindowSize(32 << value); 430 setWindowSize(32 << value);
582 } 582 }
583 583
584 void 584 void
585 SpectrogramLayer::setGain(float gain) 585 SpectrogramLayer::setGain(float gain)
586 { 586 {
587 if (m_gain == gain) return; //!!! inadequate for floats! 587 if (m_gain == gain) return;
588 588
589 m_mutex.lock(); 589 m_mutex.lock();
590 m_pixmapCacheInvalid = true; 590 m_pixmapCacheInvalid = true;
591 591
592 m_gain = gain; 592 m_gain = gain;
605 } 605 }
606 606
607 void 607 void
608 SpectrogramLayer::setThreshold(float threshold) 608 SpectrogramLayer::setThreshold(float threshold)
609 { 609 {
610 if (m_threshold == threshold) return; //!!! inadequate for floats! 610 if (m_threshold == threshold) return;
611 611
612 m_mutex.lock(); 612 m_mutex.lock();
613 m_pixmapCacheInvalid = true; 613 m_pixmapCacheInvalid = true;
614 614
615 m_threshold = threshold; 615 m_threshold = threshold;
955 256 - px, 955 256 - px,
956 pixel, 956 pixel,
957 pixel / 4); 957 pixel / 4);
958 break; 958 break;
959 959
960 case RedOnBlack: 960 case Rainbow:
961 colour = QColor::fromHsv(10, pixel, pixel); 961 hue = 250 - pixel;
962 if (hue < 0) hue += 256;
963 colour = QColor::fromHsv(pixel, 255, 255);
962 break; 964 break;
963 } 965 }
964 966
965 m_cache->setColour(pixel, colour); 967 m_cache->setColour(pixel, colour);
966 } 968 }
1117 unsigned char 1119 unsigned char
1118 SpectrogramLayer::getDisplayValue(float input) const 1120 SpectrogramLayer::getDisplayValue(float input) const
1119 { 1121 {
1120 int value; 1122 int value;
1121 1123
1122 if (m_colourScale == PhaseColourScale) { 1124 switch (m_colourScale) {
1123 1125
1124 value = int((input * 127 / M_PI) + 128); 1126 default:
1125 1127 case LinearColourScale:
1126 } else { 1128 value = int
1127 1129 (input * (m_normalizeColumns ? 1.0 : 50.0) * 255.0) + 1;
1128 switch (m_colourScale) { 1130 break;
1129 1131
1130 default: 1132 case MeterColourScale:
1131 case LinearColourScale: 1133 value = AudioLevel::multiplier_to_preview
1132 value = int(input * 50 * 255) + 1; 1134 (input * (m_normalizeColumns ? 1.0 : 50.0), 255) + 1;
1133 break; 1135 break;
1134 1136
1135 case MeterColourScale: 1137 case dBColourScale:
1136 value = AudioLevel::multiplier_to_preview(input * 50, 255) + 1; 1138 input = 20.0 * log10(input);
1137 break; 1139 input = (input + 80.0) / 80.0;
1138 1140 if (input < 0.0) input = 0.0;
1139 case dBColourScale: 1141 if (input > 1.0) input = 1.0;
1140 input = 20.0 * log10(input); 1142 value = int(input * 255.0) + 1;
1141 input = (input + 80.0) / 80.0; 1143 break;
1142 if (input < 0.0) input = 0.0; 1144
1143 if (input > 1.0) input = 1.0; 1145 case PhaseColourScale:
1144 value = int(input * 255) + 1; 1146 value = int((input * 127.0 / M_PI) + 128);
1145 } 1147 break;
1146 } 1148 }
1147 1149
1148 if (value > UCHAR_MAX) value = UCHAR_MAX; 1150 if (value > UCHAR_MAX) value = UCHAR_MAX;
1149 if (value < 0) value = 0; 1151 if (value < 0) value = 0;
1150 return value; 1152 return value;
1153 }
1154
1155 float
1156 SpectrogramLayer::getInputForDisplayValue(unsigned char uc) const
1157 {
1158 int value = uc;
1159 float input;
1160
1161 switch (m_colourScale) {
1162
1163 default:
1164 case LinearColourScale:
1165 input = float(value - 1) / 255.0 / (m_normalizeColumns ? 1 : 50);
1166 break;
1167
1168 case MeterColourScale:
1169 input = AudioLevel::preview_to_multiplier(value - 1, 255)
1170 / (m_normalizeColumns ? 1.0 : 50.0);
1171 break;
1172
1173 case dBColourScale:
1174 input = float(value - 1) / 255.0;
1175 input = (input * 80.0) - 80.0;
1176 input = powf(10.0, input) / 20.0;
1177 value = int(input);
1178 break;
1179
1180 case PhaseColourScale:
1181 input = float(value - 128) * M_PI / 127.0;
1182 break;
1183 }
1184
1185 return input;
1151 } 1186 }
1152 1187
1153 1188
1154 SpectrogramLayer::Cache::Cache() : 1189 SpectrogramLayer::Cache::Cache() :
1155 m_width(0), 1190 m_width(0),
1218 for (size_t x = 0; x < m_width; ++x) { 1253 for (size_t x = 0; x < m_width; ++x) {
1219 for (size_t y = 0; y < m_height; ++y) { 1254 for (size_t y = 0; y < m_height; ++y) {
1220 m_magnitude[y][x] = 0; 1255 m_magnitude[y][x] = 0;
1221 m_phase[y][x] = 0; 1256 m_phase[y][x] = 0;
1222 } 1257 }
1223 m_factor[x] = 1.0f; 1258 m_factor[x] = 1.0;
1224 } 1259 }
1225 } 1260 }
1226 1261
1227 void 1262 void
1228 SpectrogramLayer::CacheFillThread::run() 1263 SpectrogramLayer::CacheFillThread::run()
1426 1461
1427 if (!interrupted) m_layer.m_condition.wait(&m_layer.m_mutex, 2000); 1462 if (!interrupted) m_layer.m_condition.wait(&m_layer.m_mutex, 2000);
1428 } 1463 }
1429 } 1464 }
1430 1465
1466 float
1467 SpectrogramLayer::getEffectiveMinFrequency() const
1468 {
1469 int sr = m_model->getSampleRate();
1470 float minf = float(sr) / m_windowSize;
1471
1472 if (m_minFrequency > 0.0) {
1473 size_t minbin = size_t((double(m_minFrequency) * m_windowSize) / sr + 0.01);
1474 if (minbin < 1) minbin = 1;
1475 minf = minbin * sr / m_windowSize;
1476 }
1477
1478 return minf;
1479 }
1480
1481 float
1482 SpectrogramLayer::getEffectiveMaxFrequency() const
1483 {
1484 int sr = m_model->getSampleRate();
1485 float maxf = float(sr) / 2;
1486
1487 if (m_maxFrequency > 0.0) {
1488 size_t maxbin = size_t((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
1489 if (maxbin > m_windowSize / 2) maxbin = m_windowSize / 2;
1490 maxf = maxbin * sr / m_windowSize;
1491 }
1492
1493 return maxf;
1494 }
1495
1431 bool 1496 bool
1432 SpectrogramLayer::getYBinRange(int y, float &q0, float &q1) const 1497 SpectrogramLayer::getYBinRange(int y, float &q0, float &q1) const
1433 { 1498 {
1434 int h = m_view->height(); 1499 int h = m_view->height();
1435 if (y < 0 || y >= h) return false; 1500 if (y < 0 || y >= h) return false;
1436 1501
1437 int sr = m_model->getSampleRate(); 1502 int sr = m_model->getSampleRate();
1438 float minf = float(sr) / m_windowSize; 1503 float minf = getEffectiveMinFrequency();
1439 float maxf = float(sr) / 2; 1504 float maxf = getEffectiveMaxFrequency();
1440
1441 if (m_minFrequency > 0.0) minf = m_minFrequency;
1442 if (m_maxFrequency > 0.0) maxf = m_maxFrequency;
1443 1505
1444 bool logarithmic = (m_frequencyScale == LogFrequencyScale); 1506 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1445 1507
1446 q0 = m_view->getFrequencyForY(y, minf, maxf, logarithmic); 1508 q0 = m_view->getFrequencyForY(y, minf, maxf, logarithmic);
1447 q1 = m_view->getFrequencyForY(y - 1, minf, maxf, logarithmic); 1509 q1 = m_view->getFrequencyForY(y - 1, minf, maxf, logarithmic);
1448 1510
1449 // Now map these on to actual bins 1511 // Now map these on to actual bins
1450 1512
1451 int b0 = (q0 * m_windowSize) / sr; 1513 int b0 = int((q0 * m_windowSize) / sr);
1452 int b1 = (q1 * m_windowSize) / sr; 1514 int b1 = int((q1 * m_windowSize) / sr);
1453 1515
1516 //!!! this is supposed to return fractions-of-bins, as it were, hence the floats
1454 q0 = b0; 1517 q0 = b0;
1455 q1 = b1; 1518 q1 = b1;
1456 1519
1457 // q0 = (b0 * sr) / m_windowSize; 1520 // q0 = (b0 * sr) / m_windowSize;
1458 // q1 = (b1 * sr) / m_windowSize; 1521 // q1 = (b1 * sr) / m_windowSize;
1563 1626
1564 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue; 1627 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
1565 1628
1566 float freq = binfreq; 1629 float freq = binfreq;
1567 bool steady = false; 1630 bool steady = false;
1568 1631
1569 if (s < m_cache->getWidth() - 1) { 1632 if (s < int(m_cache->getWidth()) - 1) {
1570 1633
1571 freq = calculateFrequency(q, 1634 freq = calculateFrequency(q,
1572 windowSize, 1635 windowSize,
1573 windowIncrement, 1636 windowIncrement,
1574 sr, 1637 sr,
1583 } 1646 }
1584 } 1647 }
1585 } 1648 }
1586 1649
1587 if (!haveAdj) { 1650 if (!haveAdj) {
1588 adjFreqMin = adjFreqMax = 0.0f; 1651 adjFreqMin = adjFreqMax = 0.0;
1589 } 1652 }
1590 1653
1591 return haveAdj; 1654 return haveAdj;
1592 } 1655 }
1593 1656
1812 if (m_maxFrequency > 0) { 1875 if (m_maxFrequency > 0) {
1813 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1); 1876 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
1814 if (bins > m_windowSize / 2) bins = m_windowSize / 2; 1877 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
1815 } 1878 }
1816 1879
1817 size_t minbin = 0; 1880 size_t minbin = 1;
1818 if (m_minFrequency > 0) { 1881 if (m_minFrequency > 0) {
1819 minbin = int((double(m_minFrequency) * m_windowSize) / sr + 0.1); 1882 minbin = int((double(m_minFrequency) * m_windowSize) / sr + 0.1);
1883 if (minbin < 1) minbin = 1;
1820 if (minbin >= bins) minbin = bins - 1; 1884 if (minbin >= bins) minbin = bins - 1;
1821 } 1885 }
1822 1886
1823 float minFreq = (float(minbin) * sr) / m_windowSize; 1887 float minFreq = (float(minbin) * sr) / m_windowSize;
1824 float maxFreq = (float(bins) * sr) / m_windowSize; 1888 float maxFreq = (float(bins) * sr) / m_windowSize;
1825 1889
1826 size_t increment = getWindowIncrement(); 1890 size_t increment = getWindowIncrement();
1891
1892 bool logarithmic = (m_frequencyScale == LogFrequencyScale);
1827 1893
1828 m_mutex.unlock(); 1894 m_mutex.unlock();
1829 1895
1830 for (int x = 0; x < w; ++x) { 1896 for (int x = 0; x < w; ++x) {
1831 1897
1834 m_mutex.unlock(); 1900 m_mutex.unlock();
1835 break; 1901 break;
1836 } 1902 }
1837 1903
1838 for (int y = 0; y < h; ++y) { 1904 for (int y = 0; y < h; ++y) {
1839 ymag[y] = 0.0f; 1905 ymag[y] = 0.0;
1840 ydiv[y] = 0.0f; 1906 ydiv[y] = 0.0;
1841 } 1907 }
1842 1908
1843 float s0 = 0, s1 = 0; 1909 float s0 = 0, s1 = 0;
1844 1910
1845 if (!getXBinRange(x0 + x, s0, s1)) { 1911 if (!getXBinRange(x0 + x, s0, s1)) {
1854 int s0i = int(s0 + 0.001); 1920 int s0i = int(s0 + 0.001);
1855 int s1i = int(s1); 1921 int s1i = int(s1);
1856 1922
1857 for (size_t q = minbin; q < bins; ++q) { 1923 for (size_t q = minbin; q < bins; ++q) {
1858 1924
1925 float f0 = (float(q) * sr) / m_windowSize;
1926 float f1 = (float(q + 1) * sr) / m_windowSize;
1927
1928 float y0 = 0, y1 = 0;
1929
1930 if (m_binDisplay != PeakFrequencies ||
1931 s1i >= int(m_cache->getWidth())) {
1932 y0 = m_view->getYForFrequency(f1, minFreq, maxFreq, logarithmic);
1933 y1 = m_view->getYForFrequency(f0, minFreq, maxFreq, logarithmic);
1934 }
1935
1859 for (int s = s0i; s <= s1i; ++s) { 1936 for (int s = s0i; s <= s1i; ++s) {
1937
1938 if (m_binDisplay == PeakBins ||
1939 m_binDisplay == PeakFrequencies) {
1940 if (!m_cache->isLocalPeak(s, q)) continue;
1941 }
1942
1943 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
1860 1944
1861 float sprop = 1.0; 1945 float sprop = 1.0;
1862 if (s == s0i) sprop *= (s + 1) - s0; 1946 if (s == s0i) sprop *= (s + 1) - s0;
1863 if (s == s1i) sprop *= s1 - s; 1947 if (s == s1i) sprop *= s1 - s;
1864
1865 float f0 = (float(q) * sr) / m_windowSize;
1866 float f1 = (float(q + 1) * sr) / m_windowSize;
1867 1948
1868 if (m_binDisplay == PeakFrequencies && 1949 if (m_binDisplay == PeakFrequencies &&
1869 s < m_cache->getWidth() - 1) { 1950 s < int(m_cache->getWidth()) - 1) {
1870 1951
1871 bool steady = false; 1952 bool steady = false;
1872 f0 = f1 = calculateFrequency(q, 1953 f0 = f1 = calculateFrequency(q,
1873 m_windowSize, 1954 m_windowSize,
1874 increment, 1955 increment,
1875 sr, 1956 sr,
1876 m_cache->getPhaseAt(s, q), 1957 m_cache->getPhaseAt(s, q),
1877 m_cache->getPhaseAt(s+1, q), 1958 m_cache->getPhaseAt(s+1, q),
1878 steady); 1959 steady);
1960
1961 y0 = y1 = m_view->getYForFrequency
1962 (f0, minFreq, maxFreq, logarithmic);
1879 } 1963 }
1880
1881 float y0 = m_view->getYForFrequency
1882 (f1, minFreq, maxFreq,
1883 m_frequencyScale == LogFrequencyScale);
1884
1885 float y1 = m_view->getYForFrequency
1886 (f0, minFreq, maxFreq,
1887 m_frequencyScale == LogFrequencyScale);
1888 1964
1889 int y0i = int(y0 + 0.001); 1965 int y0i = int(y0 + 0.001);
1890 int y1i = int(y1); 1966 int y1i = int(y1);
1891 1967
1892 for (int y = y0i; y <= y1i; ++y) { 1968 for (int y = y0i; y <= y1i; ++y) {
1894 if (y < 0 || y >= h) continue; 1970 if (y < 0 || y >= h) continue;
1895 1971
1896 float yprop = sprop; 1972 float yprop = sprop;
1897 if (y == y0i) yprop *= (y + 1) - y0; 1973 if (y == y0i) yprop *= (y + 1) - y0;
1898 if (y == y1i) yprop *= y1 - y; 1974 if (y == y1i) yprop *= y1 - y;
1899
1900 if (m_binDisplay == PeakBins ||
1901 m_binDisplay == PeakFrequencies) {
1902 if (!m_cache->isLocalPeak(s, q)) continue;
1903 }
1904
1905 if (!m_cache->isOverThreshold(s, q, m_threshold)) continue;
1906 1975
1907 float value; 1976 float value;
1908 1977
1909 if (m_colourScale == PhaseColourScale) { 1978 if (m_colourScale == PhaseColourScale) {
1910 value = m_cache->getPhaseAt(s, q); 1979 value = m_cache->getPhaseAt(s, q);
1920 } 1989 }
1921 } 1990 }
1922 1991
1923 for (int y = 0; y < h; ++y) { 1992 for (int y = 0; y < h; ++y) {
1924 1993
1925 unsigned char pixel = 0;
1926
1927 if (ydiv[y] > 0.0) { 1994 if (ydiv[y] > 0.0) {
1995
1996 unsigned char pixel = 0;
1997
1928 float avg = ymag[y] / ydiv[y]; 1998 float avg = ymag[y] / ydiv[y];
1929 pixel = getDisplayValue(avg); 1999 pixel = getDisplayValue(avg);
2000
2001 assert(x <= scaled.width());
2002 QColor c = m_cache->getColour(pixel);
2003 scaled.setPixel(x, y,
2004 qRgb(c.red(), c.green(), c.blue()));
1930 } 2005 }
1931
1932 assert(x <= scaled.width());
1933 QColor c = m_cache->getColour(pixel);
1934 scaled.setPixel(x, y,
1935 qRgb(c.red(), c.green(), c.blue()));
1936 } 2006 }
1937 2007
1938 m_mutex.unlock(); 2008 m_mutex.unlock();
1939 } 2009 }
1940 2010
2088 2158
2089 return text; 2159 return text;
2090 } 2160 }
2091 2161
2092 int 2162 int
2163 SpectrogramLayer::getColourScaleWidth(QPainter &paint) const
2164 {
2165 int cw;
2166
2167 switch (m_colourScale) {
2168 default:
2169 case LinearColourScale:
2170 cw = paint.fontMetrics().width(QString("0.00"));
2171 break;
2172
2173 case MeterColourScale:
2174 case dBColourScale:
2175 cw = std::max(paint.fontMetrics().width(tr("-Inf")),
2176 paint.fontMetrics().width(tr("-90")));
2177 break;
2178
2179 case PhaseColourScale:
2180 cw = paint.fontMetrics().width(QString("-") + QChar(0x3c0));
2181 break;
2182 }
2183
2184 return cw;
2185 }
2186
2187 int
2093 SpectrogramLayer::getVerticalScaleWidth(QPainter &paint) const 2188 SpectrogramLayer::getVerticalScaleWidth(QPainter &paint) const
2094 { 2189 {
2095 if (!m_model || !m_model->isOK()) return 0; 2190 if (!m_model || !m_model->isOK()) return 0;
2191
2192 int cw = getColourScaleWidth(paint);
2096 2193
2097 int tw = paint.fontMetrics().width(QString("%1") 2194 int tw = paint.fontMetrics().width(QString("%1")
2098 .arg(m_maxFrequency > 0 ? 2195 .arg(m_maxFrequency > 0 ?
2099 m_maxFrequency - 1 : 2196 m_maxFrequency - 1 :
2100 m_model->getSampleRate() / 2)); 2197 m_model->getSampleRate() / 2));
2101 2198
2102 int fw = paint.fontMetrics().width(QString("43Hz")); 2199 int fw = paint.fontMetrics().width(QString("43Hz"));
2103 if (tw < fw) tw = fw; 2200 if (tw < fw) tw = fw;
2104 2201
2105 return tw + 13; 2202 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
2203
2204 return cw + tickw + tw + 13;
2106 } 2205 }
2107 2206
2108 void 2207 void
2109 SpectrogramLayer::paintVerticalScale(QPainter &paint, QRect rect) const 2208 SpectrogramLayer::paintVerticalScale(QPainter &paint, QRect rect) const
2110 { 2209 {
2111 if (!m_model || !m_model->isOK()) { 2210 if (!m_model || !m_model->isOK()) {
2112 return; 2211 return;
2113 } 2212 }
2114 2213
2115 int h = rect.height(), w = rect.width(); 2214 int h = rect.height(), w = rect.width();
2215
2216 int tickw = (m_frequencyScale == LogFrequencyScale ? 10 : 4);
2217 int pkw = (m_frequencyScale == LogFrequencyScale ? 10 : 0);
2116 2218
2117 size_t bins = m_windowSize / 2; 2219 size_t bins = m_windowSize / 2;
2118 int sr = m_model->getSampleRate(); 2220 int sr = m_model->getSampleRate();
2119 2221
2120 if (m_maxFrequency > 0) { 2222 if (m_maxFrequency > 0) {
2121 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1); 2223 bins = int((double(m_maxFrequency) * m_windowSize) / sr + 0.1);
2122 if (bins > m_windowSize / 2) bins = m_windowSize / 2; 2224 if (bins > m_windowSize / 2) bins = m_windowSize / 2;
2123 } 2225 }
2124 2226
2227 int cw = getColourScaleWidth(paint);
2228
2125 int py = -1; 2229 int py = -1;
2126 int textHeight = paint.fontMetrics().height(); 2230 int textHeight = paint.fontMetrics().height();
2127 int toff = -textHeight + paint.fontMetrics().ascent() + 2; 2231 int toff = -textHeight + paint.fontMetrics().ascent() + 2;
2232
2233 if (m_cache && !m_cacheInvalid && h > textHeight * 2 + 10) { //!!! lock?
2234
2235 int ch = h - textHeight * 2 - 8;
2236 paint.drawRect(4, textHeight + 4, cw - 1, ch + 1);
2237
2238 QString top, bottom;
2239
2240 switch (m_colourScale) {
2241 default:
2242 case LinearColourScale:
2243 top = (m_normalizeColumns ? "1.0" : "0.02");
2244 bottom = (m_normalizeColumns ? "0.0" : "0.00");
2245 break;
2246
2247 case MeterColourScale:
2248 top = (m_normalizeColumns ? QString("0") :
2249 QString("%1").arg(int(AudioLevel::multiplier_to_dB(0.02))));
2250 bottom = QString("%1").
2251 arg(int(AudioLevel::multiplier_to_dB
2252 (AudioLevel::preview_to_multiplier(0, 255))));
2253 break;
2254
2255 case dBColourScale:
2256 top = "0";
2257 bottom = "-80";
2258 break;
2259
2260 case PhaseColourScale:
2261 top = QChar(0x3c0);
2262 bottom = "-" + top;
2263 break;
2264 }
2265
2266 paint.drawText((cw + 6 - paint.fontMetrics().width(top)) / 2,
2267 2 + textHeight + toff, top);
2268
2269 paint.drawText((cw + 6 - paint.fontMetrics().width(bottom)) / 2,
2270 h + toff - 3, bottom);
2271
2272 paint.save();
2273 paint.setBrush(Qt::NoBrush);
2274 for (int i = 0; i < ch; ++i) {
2275 int v = (i * 255) / ch + 1;
2276 paint.setPen(m_cache->getColour(v));
2277 paint.drawLine(5, 4 + textHeight + ch - i,
2278 cw + 2, 4 + textHeight + ch - i);
2279 }
2280 paint.restore();
2281 }
2282
2283 paint.drawLine(cw + 7, 0, cw + 7, h);
2128 2284
2129 int bin = -1; 2285 int bin = -1;
2130 2286
2131 for (int y = 0; y < m_view->height(); ++y) { 2287 for (int y = 0; y < m_view->height(); ++y) {
2132 2288
2140 bin = int(q0); 2296 bin = int(q0);
2141 } else { 2297 } else {
2142 continue; 2298 continue;
2143 } 2299 }
2144 2300
2145 int freq = (sr * (bin + 1)) / m_windowSize; 2301 int freq = (sr * bin) / m_windowSize;
2146 2302
2147 if (py >= 0 && (vy - py) < textHeight - 1) { 2303 if (py >= 0 && (vy - py) < textHeight - 1) {
2148 paint.drawLine(w - 4, h - vy, w, h - vy); 2304 if (m_frequencyScale == LinearFrequencyScale) {
2305 paint.drawLine(w - tickw, h - vy, w, h - vy);
2306 }
2149 continue; 2307 continue;
2150 } 2308 }
2151 2309
2152 QString text = QString("%1").arg(freq); 2310 QString text = QString("%1").arg(freq);
2153 if (bin == 0) text = QString("%1Hz").arg(freq); 2311 if (bin == 1) text = QString("%1Hz").arg(freq); // bin 0 is DC
2154 paint.drawLine(0, h - vy, w, h - vy); 2312 paint.drawLine(cw + 7, h - vy, w - pkw - 1, h - vy);
2155 2313
2156 if (h - vy - textHeight >= -2) { 2314 if (h - vy - textHeight >= -2) {
2157 int tx = w - 10 - paint.fontMetrics().width(text); 2315 int tx = w - 3 - paint.fontMetrics().width(text) - std::max(tickw, pkw);
2158 paint.drawText(tx, h - vy + toff, text); 2316 paint.drawText(tx, h - vy + toff, text);
2159 } 2317 }
2160 2318
2161 py = vy; 2319 py = vy;
2320 }
2321
2322 if (m_frequencyScale == LogFrequencyScale) {
2323
2324 paint.drawLine(w - pkw - 1, 0, w - pkw - 1, h);
2325
2326 int sr = m_model->getSampleRate();//!!! lock?
2327 float minf = getEffectiveMinFrequency();
2328 float maxf = getEffectiveMaxFrequency();
2329
2330 int py = h;
2331 paint.setBrush(paint.pen().color());
2332
2333 for (int i = 0; i < 128; ++i) {
2334
2335 float f = Pitch::getFrequencyForPitch(i);
2336 int y = lrintf(m_view->getYForFrequency(f, minf, maxf, true));
2337 int n = (i % 12);
2338 if (n == 1 || n == 3 || n == 6 || n == 8 || n == 10) {
2339 // black notes
2340 paint.drawLine(w - pkw, y, w, y);
2341 paint.drawRect(w - pkw, y - (py-y)/4, pkw/2, 2*((py-y)/4));
2342 } else if (n == 0 || n == 5) {
2343 // C, A
2344 if (py < h) {
2345 paint.drawLine(w - pkw, (y + py) / 2, w, (y + py) / 2);
2346 }
2347 }
2348
2349 py = y;
2350 }
2162 } 2351 }
2163 } 2352 }
2164 2353
2165 QString 2354 QString
2166 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const 2355 SpectrogramLayer::toXmlString(QString indent, QString extraAttributes) const