From b6528bdb7c9db26db5481a14cd0046d02c868667 Mon Sep 17 00:00:00 2001 From: Mike Walters Date: Sun, 6 Sep 2015 01:38:25 +0100 Subject: [PATCH] Cache FFT result --- spectrogram.cpp | 44 +++++++++++++++++++++++++++++++++++++------- spectrogram.h | 6 +++++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/spectrogram.cpp b/spectrogram.cpp index 640937d..775ad8c 100644 --- a/spectrogram.cpp +++ b/spectrogram.cpp @@ -62,12 +62,21 @@ void Spectrogram::paintEvent(QPaintEvent *event) if (inputSource != nullptr) { int height = rect.height(); - - float *line = (float*)malloc(fftSize * sizeof(float)); + int tileLine = linesPerTile(); QImage image(fftSize, height, QImage::Format_RGB32); + float *tile; + for (int y = 0; y < height; y++) { - getLine(line, rect.y() + y); + if (tileLine >= linesPerTile()) { + int tileId = (rect.y() + y) / linesPerTile(); + tile = getTile(tileId); + + // Handle case where we want to draw from part-way through the first tile + tileLine = (rect.y() + y) - (tileId * linesPerTile()); + } + + float *line = &tile[tileLine * fftSize]; for (int x = 0; x < fftSize; x++) { float powerRange = std::abs(int(powerMin - powerMax)); // Cast to remove overload ambiguity float normPower = (line[x] - powerMax) * -1.0f / powerRange; @@ -75,24 +84,41 @@ void Spectrogram::paintEvent(QPaintEvent *event) image.setPixel(x, y, colormap[(uint8_t)(normPower * (256 - 1))]); } + tileLine++; } QPixmap pixmap = QPixmap::fromImage(image); painter.drawPixmap(QRect(0, rect.y(), fftSize, height), pixmap); - free(line); - paintTimeAxis(&painter, rect); } qDebug() << "Paint: " << timer.elapsed() << "ms"; } -void Spectrogram::getLine(float *dest, int y) +float* Spectrogram::getTile(off_t tile) +{ + auto iter = fftCache.find(qMakePair(zoomLevel, tile)); + if (iter != fftCache.end()) + return iter.value(); + + float *dest = new float[tileSize]; + float *ptr = dest; + off_t sample = tile * linesPerTile() * getStride(); + while ((ptr - dest) < tileSize) { + getLine(ptr, sample); + sample += getStride(); + ptr += fftSize; + } + fftCache.insert(qMakePair(zoomLevel, tile), dest); + return dest; +} + +void Spectrogram::getLine(float *dest, off_t sample) { if (inputSource && fft) { fftwf_complex buffer[fftSize]; - inputSource->getSamples(buffer, y * getStride(), fftSize); + inputSource->getSamples(buffer, sample, fftSize); for (int i = 0; i < fftSize; i++) { buffer[i][0] *= window[i]; @@ -189,3 +215,7 @@ QString Spectrogram::sampleToTime(off_t sample) { return QString::number((float)sample / sampleRate).append("s"); } + +int Spectrogram::linesPerTile() { + return tileSize / fftSize; +} diff --git a/spectrogram.h b/spectrogram.h index bad1d79..5805093 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -32,11 +32,13 @@ protected: private: const int linesPerGraduation = 50; + const int tileSize = 32768; // This must be a multiple of the maximum FFT size InputSource *inputSource = nullptr; FFT *fft = nullptr; std::unique_ptr window; fftwf_complex *lineBuffer = nullptr; + QHash, float*> fftCache; uint colormap[256]; int sampleRate; @@ -45,8 +47,10 @@ private: float powerMax; float powerMin; - void getLine(float *dest, int y); + float* getTile(off_t tile); + void getLine(float *dest, off_t sample); void paintTimeAxis(QPainter *painter, QRect rect); off_t lineToSample(int line); QString sampleToTime(off_t sample); + int linesPerTile(); }; \ No newline at end of file