Mercurial > hg > svgui
comparison layer/SpectrogramLayer.cpp @ 86:93a7efc75fb7
* Switch spectrogram layer over to using the new rudimentary disk-backed
FFT cache
author | Chris Cannam |
---|---|
date | Wed, 03 May 2006 14:26:26 +0000 |
parents | d31c4f5230d7 |
children | 4b98bda7e94d |
comparison
equal
deleted
inserted
replaced
85:d31c4f5230d7 | 86:93a7efc75fb7 |
---|---|
19 #include "base/Profiler.h" | 19 #include "base/Profiler.h" |
20 #include "base/AudioLevel.h" | 20 #include "base/AudioLevel.h" |
21 #include "base/Window.h" | 21 #include "base/Window.h" |
22 #include "base/Pitch.h" | 22 #include "base/Pitch.h" |
23 #include "base/FFTCache.h" | 23 #include "base/FFTCache.h" |
24 #include "base/FFTFileCache.h" | |
24 | 25 |
25 #include <QPainter> | 26 #include <QPainter> |
26 #include <QImage> | 27 #include <QImage> |
27 #include <QPixmap> | 28 #include <QPixmap> |
28 #include <QRect> | 29 #include <QRect> |
64 m_colourScheme(DefaultColours), | 65 m_colourScheme(DefaultColours), |
65 m_frequencyScale(LinearFrequencyScale), | 66 m_frequencyScale(LinearFrequencyScale), |
66 m_binDisplay(AllBins), | 67 m_binDisplay(AllBins), |
67 m_normalizeColumns(false), | 68 m_normalizeColumns(false), |
68 m_cache(0), | 69 m_cache(0), |
70 m_writeCache(0), | |
69 m_cacheInvalid(true), | 71 m_cacheInvalid(true), |
70 m_pixmapCache(0), | 72 m_pixmapCache(0), |
71 m_pixmapCacheInvalid(true), | 73 m_pixmapCacheInvalid(true), |
72 m_fillThread(0), | 74 m_fillThread(0), |
73 m_updateTimer(0), | 75 m_updateTimer(0), |
102 m_exiting = true; | 104 m_exiting = true; |
103 m_condition.wakeAll(); | 105 m_condition.wakeAll(); |
104 if (m_fillThread) m_fillThread->wait(); | 106 if (m_fillThread) m_fillThread->wait(); |
105 delete m_fillThread; | 107 delete m_fillThread; |
106 | 108 |
109 delete m_writeCache; | |
107 delete m_cache; | 110 delete m_cache; |
108 } | 111 } |
109 | 112 |
110 void | 113 void |
111 SpectrogramLayer::setModel(const DenseTimeValueModel *model) | 114 SpectrogramLayer::setModel(const DenseTimeValueModel *model) |
926 if (m_cacheInvalid || !m_cache) return; | 929 if (m_cacheInvalid || !m_cache) return; |
927 | 930 |
928 int formerRotation = m_colourRotation; | 931 int formerRotation = m_colourRotation; |
929 | 932 |
930 if (m_colourScheme == BlackOnWhite) { | 933 if (m_colourScheme == BlackOnWhite) { |
931 m_cache->setColour(NO_VALUE, Qt::white); | 934 m_colourMap.setColour(NO_VALUE, Qt::white); |
932 } else { | 935 } else { |
933 m_cache->setColour(NO_VALUE, Qt::black); | 936 m_colourMap.setColour(NO_VALUE, Qt::black); |
934 } | 937 } |
935 | 938 |
936 for (int pixel = 1; pixel < 256; ++pixel) { | 939 for (int pixel = 1; pixel < 256; ++pixel) { |
937 | 940 |
938 QColor colour; | 941 QColor colour; |
988 colour = QColor::fromHsv(pixel, 255, 255); | 991 colour = QColor::fromHsv(pixel, 255, 255); |
989 m_crosshairColour = Qt::white; | 992 m_crosshairColour = Qt::white; |
990 break; | 993 break; |
991 } | 994 } |
992 | 995 |
993 m_cache->setColour(pixel, colour); | 996 m_colourMap.setColour(pixel, colour); |
994 } | 997 } |
995 | 998 |
996 m_colourRotation = 0; | 999 m_colourRotation = 0; |
997 rotateCacheColourmap(m_colourRotation - formerRotation); | 1000 rotateCacheColourmap(m_colourRotation - formerRotation); |
998 m_colourRotation = formerRotation; | 1001 m_colourRotation = formerRotation; |
1003 { | 1006 { |
1004 if (!m_cache) return; | 1007 if (!m_cache) return; |
1005 | 1008 |
1006 QColor newPixels[256]; | 1009 QColor newPixels[256]; |
1007 | 1010 |
1008 newPixels[NO_VALUE] = m_cache->getColour(NO_VALUE); | 1011 newPixels[NO_VALUE] = m_colourMap.getColour(NO_VALUE); |
1009 | 1012 |
1010 for (int pixel = 1; pixel < 256; ++pixel) { | 1013 for (int pixel = 1; pixel < 256; ++pixel) { |
1011 int target = pixel + distance; | 1014 int target = pixel + distance; |
1012 while (target < 1) target += 255; | 1015 while (target < 1) target += 255; |
1013 while (target > 255) target -= 255; | 1016 while (target > 255) target -= 255; |
1014 newPixels[target] = m_cache->getColour(pixel); | 1017 newPixels[target] = m_colourMap.getColour(pixel); |
1015 } | 1018 } |
1016 | 1019 |
1017 for (int pixel = 0; pixel < 256; ++pixel) { | 1020 for (int pixel = 0; pixel < 256; ++pixel) { |
1018 m_cache->setColour(pixel, newPixels[pixel]); | 1021 m_colourMap.setColour(pixel, newPixels[pixel]); |
1019 } | 1022 } |
1020 } | 1023 } |
1021 | 1024 |
1022 float | 1025 float |
1023 SpectrogramLayer::calculateFrequency(size_t bin, | 1026 SpectrogramLayer::calculateFrequency(size_t bin, |
1065 SpectrogramLayer::fillCacheColumn(int column, double *input, | 1068 SpectrogramLayer::fillCacheColumn(int column, double *input, |
1066 fftw_complex *output, | 1069 fftw_complex *output, |
1067 fftw_plan plan, | 1070 fftw_plan plan, |
1068 size_t windowSize, | 1071 size_t windowSize, |
1069 size_t increment, | 1072 size_t increment, |
1073 float *workbuffer, | |
1070 const Window<double> &windower) const | 1074 const Window<double> &windower) const |
1071 { | 1075 { |
1072 //!!! we _do_ need a lock for these references to the model | 1076 //!!! we _do_ need a lock for these references to the model |
1073 // though, don't we? | 1077 // though, don't we? |
1074 | 1078 |
1129 if (mag > factor) factor = mag; | 1133 if (mag > factor) factor = mag; |
1130 | 1134 |
1131 double phase = atan2(output[i][1], output[i][0]); | 1135 double phase = atan2(output[i][1], output[i][0]); |
1132 phase = princarg(phase); | 1136 phase = princarg(phase); |
1133 | 1137 |
1134 output[i][0] = mag; | 1138 workbuffer[i] = mag; |
1135 m_cache->setPhaseAt(column, i, phase); | 1139 workbuffer[i + windowSize/2] = phase; |
1136 } | 1140 } |
1137 | 1141 |
1138 m_cache->setNormalizationFactor(column, factor); | 1142 m_writeCache->setColumnAt(column, workbuffer, |
1139 | 1143 workbuffer + windowSize/2, factor); |
1140 for (size_t i = 0; i < windowSize/2; ++i) { | |
1141 m_cache->setMagnitudeAt(column, i, output[i][0]); | |
1142 } | |
1143 } | 1144 } |
1144 | 1145 |
1145 unsigned char | 1146 unsigned char |
1146 SpectrogramLayer::getDisplayValue(float input) const | 1147 SpectrogramLayer::getDisplayValue(float input) const |
1147 { | 1148 { |
1276 visibleStart = (visibleStart / windowIncrement) * windowIncrement; | 1277 visibleStart = (visibleStart / windowIncrement) * windowIncrement; |
1277 | 1278 |
1278 size_t width = (end - start) / windowIncrement + 1; | 1279 size_t width = (end - start) / windowIncrement + 1; |
1279 size_t height = windowSize / 2; | 1280 size_t height = windowSize / 2; |
1280 | 1281 |
1281 if (!m_layer.m_cache) { | 1282 //!!! if (!m_layer.m_cache) { |
1282 m_layer.m_cache = new FFTMemoryCache; | 1283 // m_layer.m_cache = new FFTMemoryCache; |
1283 } | 1284 // } |
1284 | 1285 if (!m_layer.m_writeCache) { |
1285 m_layer.m_cache->resize(width, height); | 1286 m_layer.m_writeCache = new FFTFileCache |
1287 (QString("%1").arg(getObjectExportId(&m_layer)), | |
1288 MatrixFileCache::ReadWrite); | |
1289 } | |
1290 m_layer.m_writeCache->resize(width, height); | |
1291 if (m_layer.m_cache) delete m_layer.m_cache; | |
1292 m_layer.m_cache = new FFTFileCache | |
1293 (QString("%1").arg(getObjectExportId(&m_layer)), | |
1294 MatrixFileCache::ReadOnly); | |
1295 | |
1286 m_layer.setCacheColourmap(); | 1296 m_layer.setCacheColourmap(); |
1287 //!!! m_layer.m_cache->reset(); | 1297 //!!! m_layer.m_writeCache->reset(); |
1288 | 1298 |
1289 // We don't need a lock when writing to or reading from | 1299 // We don't need a lock when writing to or reading from |
1290 // the pixels in the cache. We do need to ensure we have | 1300 // the pixels in the cache. We do need to ensure we have |
1291 // the width and height of the cache and the FFT | 1301 // the width and height of the cache and the FFT |
1292 // parameters known before we unlock, in case they change | 1302 // parameters known before we unlock, in case they change |
1301 fftw_malloc(windowSize * sizeof(double)); | 1311 fftw_malloc(windowSize * sizeof(double)); |
1302 | 1312 |
1303 fftw_complex *output = (fftw_complex *) | 1313 fftw_complex *output = (fftw_complex *) |
1304 fftw_malloc(windowSize * sizeof(fftw_complex)); | 1314 fftw_malloc(windowSize * sizeof(fftw_complex)); |
1305 | 1315 |
1316 float *workbuffer = (float *) | |
1317 fftw_malloc(windowSize * sizeof(float)); | |
1318 | |
1306 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input, | 1319 fftw_plan plan = fftw_plan_dft_r2c_1d(windowSize, input, |
1307 output, FFTW_ESTIMATE); | 1320 output, FFTW_ESTIMATE); |
1308 | 1321 |
1309 Window<double> windower(windowType, windowSize); | 1322 Window<double> windower(windowType, windowSize); |
1310 | 1323 |
1327 for (size_t f = visibleStart; f < end; f += windowIncrement) { | 1340 for (size_t f = visibleStart; f < end; f += windowIncrement) { |
1328 | 1341 |
1329 m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1342 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1330 input, output, plan, | 1343 input, output, plan, |
1331 windowSize, windowIncrement, | 1344 windowSize, windowIncrement, |
1332 windower); | 1345 workbuffer, windower); |
1333 | 1346 |
1334 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | 1347 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { |
1335 interrupted = true; | 1348 interrupted = true; |
1336 m_fillExtent = 0; | 1349 m_fillExtent = 0; |
1337 break; | 1350 break; |
1359 for (size_t f = start; f < remainingEnd; f += windowIncrement) { | 1372 for (size_t f = start; f < remainingEnd; f += windowIncrement) { |
1360 | 1373 |
1361 m_layer.fillCacheColumn(int((f - start) / windowIncrement), | 1374 m_layer.fillCacheColumn(int((f - start) / windowIncrement), |
1362 input, output, plan, | 1375 input, output, plan, |
1363 windowSize, windowIncrement, | 1376 windowSize, windowIncrement, |
1364 windower); | 1377 workbuffer, windower); |
1365 | 1378 |
1366 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { | 1379 if (m_layer.m_cacheInvalid || m_layer.m_exiting) { |
1367 interrupted = true; | 1380 interrupted = true; |
1368 m_fillExtent = 0; | 1381 m_fillExtent = 0; |
1369 break; | 1382 break; |
1380 } | 1393 } |
1381 | 1394 |
1382 fftw_destroy_plan(plan); | 1395 fftw_destroy_plan(plan); |
1383 fftw_free(output); | 1396 fftw_free(output); |
1384 fftw_free(input); | 1397 fftw_free(input); |
1398 fftw_free(workbuffer); | |
1385 | 1399 |
1386 if (!interrupted) { | 1400 if (!interrupted) { |
1387 m_fillExtent = end; | 1401 m_fillExtent = end; |
1388 m_fillCompletion = 100; | 1402 m_fillCompletion = 100; |
1389 } | 1403 } |
1811 int h = y1 - y0; | 1825 int h = y1 - y0; |
1812 | 1826 |
1813 // std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; | 1827 // std::cerr << "x0 " << x0 << ", x1 " << x1 << ", w " << w << ", h " << h << std::endl; |
1814 | 1828 |
1815 QImage scaled(w, h, QImage::Format_RGB32); | 1829 QImage scaled(w, h, QImage::Format_RGB32); |
1816 scaled.fill(m_cache->getColour(0).rgb()); | 1830 scaled.fill(m_colourMap.getColour(0).rgb()); |
1817 | 1831 |
1818 float ymag[h]; | 1832 float ymag[h]; |
1819 float ydiv[h]; | 1833 float ydiv[h]; |
1820 | 1834 |
1821 int sr = m_model->getSampleRate(); | 1835 int sr = m_model->getSampleRate(); |
1951 | 1965 |
1952 float avg = ymag[y] / ydiv[y]; | 1966 float avg = ymag[y] / ydiv[y]; |
1953 pixel = getDisplayValue(avg); | 1967 pixel = getDisplayValue(avg); |
1954 | 1968 |
1955 assert(x <= scaled.width()); | 1969 assert(x <= scaled.width()); |
1956 QColor c = m_cache->getColour(pixel); | 1970 QColor c = m_colourMap.getColour(pixel); |
1957 scaled.setPixel(x, y, | 1971 scaled.setPixel(x, y, |
1958 qRgb(c.red(), c.green(), c.blue())); | 1972 qRgb(c.red(), c.green(), c.blue())); |
1959 } | 1973 } |
1960 } | 1974 } |
1961 | 1975 |
2318 | 2332 |
2319 paint.save(); | 2333 paint.save(); |
2320 paint.setBrush(Qt::NoBrush); | 2334 paint.setBrush(Qt::NoBrush); |
2321 for (int i = 0; i < ch; ++i) { | 2335 for (int i = 0; i < ch; ++i) { |
2322 int v = (i * 255) / ch + 1; | 2336 int v = (i * 255) / ch + 1; |
2323 paint.setPen(m_cache->getColour(v)); | 2337 paint.setPen(m_colourMap.getColour(v)); |
2324 paint.drawLine(5, 4 + textHeight + ch - i, | 2338 paint.drawLine(5, 4 + textHeight + ch - i, |
2325 cw + 2, 4 + textHeight + ch - i); | 2339 cw + 2, 4 + textHeight + ch - i); |
2326 } | 2340 } |
2327 paint.restore(); | 2341 paint.restore(); |
2328 } | 2342 } |