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 }