annotate data/fft/FFTDataServer.h @ 974:65494d0d9ded

Fix memory leak
author Chris Cannam
date Wed, 03 Sep 2014 11:22:46 +0100
parents 59e7fe1b1003
children cc27f35aa75c
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #ifndef _FFT_DATA_SERVER_H_
Chris@148 17 #define _FFT_DATA_SERVER_H_
Chris@148 18
Chris@148 19 #include "base/Window.h"
Chris@148 20 #include "base/Thread.h"
Chris@334 21 #include "base/StorageAdviser.h"
Chris@148 22
Chris@226 23 #include "FFTapi.h"
Chris@537 24 #include "FFTFileCacheReader.h"
Chris@537 25 #include "FFTFileCacheWriter.h"
Chris@537 26 #include "FFTMemoryCache.h"
Chris@148 27
Chris@148 28 #include <QMutex>
Chris@537 29 #include <QReadWriteLock>
Chris@537 30 #include <QReadLocker>
Chris@148 31 #include <QWaitCondition>
Chris@148 32 #include <QString>
Chris@148 33
Chris@148 34 #include <vector>
Chris@148 35 #include <deque>
Chris@148 36
Chris@148 37 class DenseTimeValueModel;
Chris@215 38 class Model;
Chris@148 39
Chris@148 40 class FFTDataServer
Chris@148 41 {
Chris@148 42 public:
Chris@148 43 static FFTDataServer *getInstance(const DenseTimeValueModel *model,
Chris@148 44 int channel,
Chris@148 45 WindowType windowType,
Chris@929 46 int windowSize,
Chris@929 47 int windowIncrement,
Chris@929 48 int fftSize,
Chris@148 49 bool polar,
Chris@334 50 StorageAdviser::Criteria criteria =
Chris@334 51 StorageAdviser::NoCriteria,
Chris@929 52 int fillFromColumn = 0);
Chris@148 53
Chris@148 54 static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model,
Chris@148 55 int channel,
Chris@148 56 WindowType windowType,
Chris@929 57 int windowSize,
Chris@929 58 int windowIncrement,
Chris@929 59 int fftSize,
Chris@148 60 bool polar,
Chris@334 61 StorageAdviser::Criteria criteria =
Chris@334 62 StorageAdviser::NoCriteria,
Chris@929 63 int fillFromColumn = 0);
Chris@148 64
Chris@152 65 static void claimInstance(FFTDataServer *);
Chris@148 66 static void releaseInstance(FFTDataServer *);
Chris@148 67
Chris@215 68 static void modelAboutToBeDeleted(Model *);
Chris@215 69
Chris@148 70 const DenseTimeValueModel *getModel() const { return m_model; }
Chris@148 71 int getChannel() const { return m_channel; }
Chris@148 72 WindowType getWindowType() const { return m_windower.getType(); }
Chris@929 73 int getWindowSize() const { return m_windowSize; }
Chris@929 74 int getWindowIncrement() const { return m_windowIncrement; }
Chris@929 75 int getFFTSize() const { return m_fftSize; }
Chris@148 76 bool getPolar() const { return m_polar; }
Chris@148 77
Chris@929 78 int getWidth() const { return m_width; }
Chris@929 79 int getHeight() const { return m_height; }
Chris@148 80
Chris@929 81 float getMagnitudeAt(int x, int y);
Chris@929 82 float getNormalizedMagnitudeAt(int x, int y);
Chris@929 83 float getMaximumMagnitudeAt(int x);
Chris@929 84 float getPhaseAt(int x, int y);
Chris@929 85 void getValuesAt(int x, int y, float &real, float &imaginary);
Chris@929 86 bool isColumnReady(int x);
Chris@148 87
Chris@929 88 bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
Chris@929 89 bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
Chris@929 90 bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
Chris@929 91 bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0, int step = 1);
Chris@408 92
Chris@148 93 void suspend();
Chris@155 94 void suspendWrites();
Chris@154 95 void resume(); // also happens automatically if new data needed
Chris@148 96
Chris@148 97 // Convenience functions:
Chris@148 98
Chris@929 99 bool isLocalPeak(int x, int y) {
Chris@148 100 float mag = getMagnitudeAt(x, y);
Chris@148 101 if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
Chris@148 102 if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false;
Chris@148 103 return true;
Chris@148 104 }
Chris@929 105 bool isOverThreshold(int x, int y, float threshold) {
Chris@148 106 return getMagnitudeAt(x, y) > threshold;
Chris@148 107 }
Chris@148 108
Chris@678 109 QString getError() const;
Chris@929 110 int getFillCompletion() const;
Chris@929 111 int getFillExtent() const;
Chris@148 112
Chris@148 113 private:
Chris@148 114 FFTDataServer(QString fileBaseName,
Chris@148 115 const DenseTimeValueModel *model,
Chris@148 116 int channel,
Chris@148 117 WindowType windowType,
Chris@929 118 int windowSize,
Chris@929 119 int windowIncrement,
Chris@929 120 int fftSize,
Chris@148 121 bool polar,
Chris@334 122 StorageAdviser::Criteria criteria,
Chris@929 123 int fillFromColumn = 0);
Chris@148 124
Chris@148 125 virtual ~FFTDataServer();
Chris@148 126
Chris@148 127 FFTDataServer(const FFTDataServer &); // not implemented
Chris@148 128 FFTDataServer &operator=(const FFTDataServer &); // not implemented
Chris@148 129
Chris@148 130 typedef float fftsample;
Chris@148 131
Chris@148 132 QString m_fileBaseName;
Chris@148 133 const DenseTimeValueModel *m_model;
Chris@148 134 int m_channel;
Chris@148 135
Chris@148 136 Window<fftsample> m_windower;
Chris@148 137
Chris@929 138 int m_windowSize;
Chris@929 139 int m_windowIncrement;
Chris@929 140 int m_fftSize;
Chris@148 141 bool m_polar;
Chris@148 142
Chris@929 143 int m_width;
Chris@929 144 int m_height;
Chris@929 145 int m_cacheWidth;
Chris@929 146 int m_cacheWidthPower;
Chris@929 147 int m_cacheWidthMask;
Chris@359 148
Chris@537 149 struct CacheBlock {
Chris@537 150 FFTMemoryCache *memoryCache;
Chris@537 151 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
Chris@537 152 ThreadReaderMap fileCacheReader;
Chris@537 153 FFTFileCacheWriter *fileCacheWriter;
Chris@537 154 CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
Chris@537 155 ~CacheBlock() {
Chris@537 156 delete memoryCache;
Chris@537 157 while (!fileCacheReader.empty()) {
Chris@537 158 delete fileCacheReader.begin()->second;
Chris@537 159 fileCacheReader.erase(fileCacheReader.begin());
Chris@537 160 }
Chris@537 161 delete fileCacheWriter;
Chris@537 162 }
Chris@537 163 };
Chris@537 164
Chris@537 165 typedef std::vector<CacheBlock *> CacheVector;
Chris@537 166 CacheVector m_caches;
Chris@537 167 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
Chris@548 168 QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls
Chris@537 169
Chris@929 170 FFTCacheReader *getCacheReader(int x, int &col) {
Chris@537 171 Profiler profiler("FFTDataServer::getCacheReader");
Chris@537 172 col = x & m_cacheWidthMask;
Chris@359 173 int c = x >> m_cacheWidthPower;
Chris@537 174 m_cacheVectorLock.lockForRead();
Chris@537 175 CacheBlock *cb(m_caches.at(c));
Chris@537 176 if (cb) {
Chris@547 177 if (cb->memoryCache) {
Chris@547 178 m_cacheVectorLock.unlock();
Chris@547 179 return cb->memoryCache;
Chris@547 180 }
Chris@537 181 if (cb->fileCacheWriter) {
Chris@537 182 QThread *me = QThread::currentThread();
Chris@537 183 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
Chris@537 184 if (map.find(me) == map.end()) {
Chris@537 185 m_cacheVectorLock.unlock();
Chris@537 186 if (!makeCacheReader(c)) return 0;
Chris@537 187 return getCacheReader(x, col);
Chris@537 188 }
Chris@539 189 FFTCacheReader *reader = cb->fileCacheReader[me];
Chris@537 190 m_cacheVectorLock.unlock();
Chris@537 191 return reader;
Chris@537 192 }
Chris@537 193 // if cb exists but cb->fileCacheWriter doesn't, creation
Chris@537 194 // must have failed: don't try again
Chris@537 195 m_cacheVectorLock.unlock();
Chris@537 196 return 0;
Chris@537 197 }
Chris@537 198 m_cacheVectorLock.unlock();
Chris@537 199 if (!makeCache(c)) return 0;
Chris@537 200 return getCacheReader(x, col);
Chris@359 201 }
Chris@537 202
Chris@929 203 FFTCacheWriter *getCacheWriter(int x, int &col) {
Chris@537 204 Profiler profiler("FFTDataServer::getCacheWriter");
Chris@537 205 col = x & m_cacheWidthMask;
Chris@537 206 int c = x >> m_cacheWidthPower;
Chris@537 207 {
Chris@537 208 QReadLocker locker(&m_cacheVectorLock);
Chris@537 209 CacheBlock *cb(m_caches.at(c));
Chris@537 210 if (cb) {
Chris@537 211 if (cb->memoryCache) return cb->memoryCache;
Chris@537 212 if (cb->fileCacheWriter) return cb->fileCacheWriter;
Chris@537 213 // if cb exists, creation must have failed: don't try again
Chris@537 214 return 0;
Chris@537 215 }
Chris@537 216 }
Chris@537 217 if (!makeCache(c)) return 0;
Chris@537 218 return getCacheWriter(x, col);
Chris@537 219 }
Chris@537 220
Chris@929 221 bool haveCache(int x) {
Chris@359 222 int c = x >> m_cacheWidthPower;
Chris@548 223 return (m_caches.at(c) != 0);
Chris@359 224 }
Chris@148 225
Chris@537 226 bool makeCache(int c);
Chris@537 227 bool makeCacheReader(int c);
Chris@537 228
Chris@359 229 StorageAdviser::Criteria m_criteria;
Chris@359 230
Chris@929 231 void getStorageAdvice(int w, int h, bool &memory, bool &compact);
Chris@148 232
Chris@549 233 QMutex m_fftBuffersLock;
Chris@148 234 QWaitCondition m_condition;
Chris@148 235
Chris@148 236 fftsample *m_fftInput;
Chris@226 237 fftf_complex *m_fftOutput;
Chris@148 238 float *m_workbuffer;
Chris@226 239 fftf_plan m_fftPlan;
Chris@148 240
Chris@148 241 class FillThread : public Thread
Chris@148 242 {
Chris@148 243 public:
Chris@929 244 FillThread(FFTDataServer &server, int fillFromColumn) :
Chris@148 245 m_server(server), m_extent(0), m_completion(0),
Chris@148 246 m_fillFrom(fillFromColumn) { }
Chris@148 247
Chris@929 248 int getExtent() const { return m_extent; }
Chris@929 249 int getCompletion() const { return m_completion ? m_completion : 1; }
Chris@678 250 QString getError() const { return m_error; }
Chris@148 251 virtual void run();
Chris@148 252
Chris@148 253 protected:
Chris@148 254 FFTDataServer &m_server;
Chris@929 255 int m_extent;
Chris@929 256 int m_completion;
Chris@929 257 int m_fillFrom;
Chris@678 258 QString m_error;
Chris@148 259 };
Chris@148 260
Chris@148 261 bool m_exiting;
Chris@148 262 bool m_suspended;
Chris@148 263 FillThread *m_fillThread;
Chris@678 264 QString m_error;
Chris@148 265
Chris@148 266 void deleteProcessingData();
Chris@929 267 void fillColumn(int x);
Chris@537 268 void fillComplete();
Chris@148 269
Chris@148 270 QString generateFileBasename() const;
Chris@148 271 static QString generateFileBasename(const DenseTimeValueModel *model,
Chris@148 272 int channel,
Chris@148 273 WindowType windowType,
Chris@929 274 int windowSize,
Chris@929 275 int windowIncrement,
Chris@929 276 int fftSize,
Chris@148 277 bool polar);
Chris@148 278
Chris@148 279 typedef std::pair<FFTDataServer *, int> ServerCountPair;
Chris@148 280 typedef std::map<QString, ServerCountPair> ServerMap;
Chris@215 281 typedef std::deque<FFTDataServer *> ServerQueue;
Chris@148 282
Chris@148 283 static ServerMap m_servers;
Chris@215 284 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
Chris@148 285 static QMutex m_serverMapMutex;
Chris@148 286 static FFTDataServer *findServer(QString); // call with serverMapMutex held
Chris@148 287 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
Chris@216 288
Chris@216 289 static void claimInstance(FFTDataServer *, bool needLock);
Chris@216 290 static void releaseInstance(FFTDataServer *, bool needLock);
Chris@216 291
Chris@148 292 };
Chris@148 293
Chris@148 294 #endif