annotate data/fft/FFTDataServer.h @ 537:3cc4b7cd2aa5

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +0000
parents 115f60df1e4d
children a5a17152b6df
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@148 46 size_t windowSize,
Chris@148 47 size_t windowIncrement,
Chris@148 48 size_t fftSize,
Chris@148 49 bool polar,
Chris@334 50 StorageAdviser::Criteria criteria =
Chris@334 51 StorageAdviser::NoCriteria,
Chris@148 52 size_t 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@148 57 size_t windowSize,
Chris@148 58 size_t windowIncrement,
Chris@148 59 size_t fftSize,
Chris@148 60 bool polar,
Chris@334 61 StorageAdviser::Criteria criteria =
Chris@334 62 StorageAdviser::NoCriteria,
Chris@148 63 size_t 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@148 73 size_t getWindowSize() const { return m_windowSize; }
Chris@148 74 size_t getWindowIncrement() const { return m_windowIncrement; }
Chris@148 75 size_t getFFTSize() const { return m_fftSize; }
Chris@148 76 bool getPolar() const { return m_polar; }
Chris@148 77
Chris@148 78 size_t getWidth() const { return m_width; }
Chris@148 79 size_t getHeight() const { return m_height; }
Chris@148 80
Chris@148 81 float getMagnitudeAt(size_t x, size_t y);
Chris@148 82 float getNormalizedMagnitudeAt(size_t x, size_t y);
Chris@148 83 float getMaximumMagnitudeAt(size_t x);
Chris@148 84 float getPhaseAt(size_t x, size_t y);
Chris@148 85 void getValuesAt(size_t x, size_t y, float &real, float &imaginary);
Chris@148 86 bool isColumnReady(size_t x);
Chris@148 87
Chris@408 88 bool getMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 89 bool getNormalizedMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 90 bool getPhasesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 91
Chris@148 92 void suspend();
Chris@155 93 void suspendWrites();
Chris@154 94 void resume(); // also happens automatically if new data needed
Chris@148 95
Chris@148 96 // Convenience functions:
Chris@148 97
Chris@148 98 bool isLocalPeak(size_t x, size_t y) {
Chris@148 99 float mag = getMagnitudeAt(x, y);
Chris@148 100 if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
Chris@148 101 if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false;
Chris@148 102 return true;
Chris@148 103 }
Chris@148 104 bool isOverThreshold(size_t x, size_t y, float threshold) {
Chris@148 105 return getMagnitudeAt(x, y) > threshold;
Chris@148 106 }
Chris@148 107
Chris@148 108 size_t getFillCompletion() const;
Chris@148 109 size_t getFillExtent() const;
Chris@148 110
Chris@148 111 private:
Chris@148 112 FFTDataServer(QString fileBaseName,
Chris@148 113 const DenseTimeValueModel *model,
Chris@148 114 int channel,
Chris@148 115 WindowType windowType,
Chris@148 116 size_t windowSize,
Chris@148 117 size_t windowIncrement,
Chris@148 118 size_t fftSize,
Chris@148 119 bool polar,
Chris@334 120 StorageAdviser::Criteria criteria,
Chris@148 121 size_t fillFromColumn = 0);
Chris@148 122
Chris@148 123 virtual ~FFTDataServer();
Chris@148 124
Chris@148 125 FFTDataServer(const FFTDataServer &); // not implemented
Chris@148 126 FFTDataServer &operator=(const FFTDataServer &); // not implemented
Chris@148 127
Chris@148 128 typedef float fftsample;
Chris@148 129
Chris@148 130 QString m_fileBaseName;
Chris@148 131 const DenseTimeValueModel *m_model;
Chris@148 132 int m_channel;
Chris@148 133
Chris@148 134 Window<fftsample> m_windower;
Chris@148 135
Chris@148 136 size_t m_windowSize;
Chris@148 137 size_t m_windowIncrement;
Chris@148 138 size_t m_fftSize;
Chris@148 139 bool m_polar;
Chris@148 140
Chris@148 141 size_t m_width;
Chris@148 142 size_t m_height;
Chris@148 143 size_t m_cacheWidth;
Chris@183 144 size_t m_cacheWidthPower;
Chris@183 145 size_t m_cacheWidthMask;
Chris@359 146
Chris@537 147 struct CacheBlock {
Chris@537 148 FFTMemoryCache *memoryCache;
Chris@537 149 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
Chris@537 150 ThreadReaderMap fileCacheReader;
Chris@537 151 FFTFileCacheWriter *fileCacheWriter;
Chris@537 152 CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
Chris@537 153 ~CacheBlock() {
Chris@537 154 delete memoryCache;
Chris@537 155 while (!fileCacheReader.empty()) {
Chris@537 156 delete fileCacheReader.begin()->second;
Chris@537 157 fileCacheReader.erase(fileCacheReader.begin());
Chris@537 158 }
Chris@537 159 delete fileCacheWriter;
Chris@537 160 }
Chris@537 161 };
Chris@537 162
Chris@537 163 typedef std::vector<CacheBlock *> CacheVector;
Chris@537 164 CacheVector m_caches;
Chris@537 165 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
Chris@537 166
Chris@537 167 FFTCacheReader *getCacheReader(size_t x, size_t &col) {
Chris@537 168 Profiler profiler("FFTDataServer::getCacheReader");
Chris@537 169 col = x & m_cacheWidthMask;
Chris@359 170 int c = x >> m_cacheWidthPower;
Chris@537 171 m_cacheVectorLock.lockForRead();
Chris@537 172 CacheBlock *cb(m_caches.at(c));
Chris@537 173 if (cb) {
Chris@537 174 if (cb->memoryCache) return cb->memoryCache;
Chris@537 175 if (cb->fileCacheWriter) {
Chris@537 176 QThread *me = QThread::currentThread();
Chris@537 177 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
Chris@537 178 if (map.find(me) == map.end()) {
Chris@537 179 m_cacheVectorLock.unlock();
Chris@537 180 if (!makeCacheReader(c)) return 0;
Chris@537 181 return getCacheReader(x, col);
Chris@537 182 }
Chris@537 183 FFTCacheReader *reader = cb->fileCacheReader.at(me);
Chris@537 184 m_cacheVectorLock.unlock();
Chris@537 185 return reader;
Chris@537 186 }
Chris@537 187 // if cb exists but cb->fileCacheWriter doesn't, creation
Chris@537 188 // must have failed: don't try again
Chris@537 189 m_cacheVectorLock.unlock();
Chris@537 190 return 0;
Chris@537 191 }
Chris@537 192 m_cacheVectorLock.unlock();
Chris@537 193 if (!makeCache(c)) return 0;
Chris@537 194 return getCacheReader(x, col);
Chris@359 195 }
Chris@537 196
Chris@537 197 FFTCacheWriter *getCacheWriter(size_t x, size_t &col) {
Chris@537 198 Profiler profiler("FFTDataServer::getCacheWriter");
Chris@537 199 col = x & m_cacheWidthMask;
Chris@537 200 int c = x >> m_cacheWidthPower;
Chris@537 201 {
Chris@537 202 QReadLocker locker(&m_cacheVectorLock);
Chris@537 203 CacheBlock *cb(m_caches.at(c));
Chris@537 204 if (cb) {
Chris@537 205 if (cb->memoryCache) return cb->memoryCache;
Chris@537 206 if (cb->fileCacheWriter) return cb->fileCacheWriter;
Chris@537 207 // if cb exists, creation must have failed: don't try again
Chris@537 208 return 0;
Chris@537 209 }
Chris@537 210 }
Chris@537 211 if (!makeCache(c)) return 0;
Chris@537 212 return getCacheWriter(x, col);
Chris@537 213 }
Chris@537 214
Chris@359 215 bool haveCache(size_t x) {
Chris@359 216 int c = x >> m_cacheWidthPower;
Chris@537 217 return (m_caches[c] != 0);
Chris@359 218 }
Chris@148 219
Chris@537 220 bool makeCache(int c);
Chris@537 221 bool makeCacheReader(int c);
Chris@537 222
Chris@359 223 StorageAdviser::Criteria m_criteria;
Chris@359 224
Chris@359 225 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact);
Chris@148 226
Chris@148 227 QMutex m_writeMutex;
Chris@148 228 QWaitCondition m_condition;
Chris@148 229
Chris@148 230 fftsample *m_fftInput;
Chris@226 231 fftf_complex *m_fftOutput;
Chris@148 232 float *m_workbuffer;
Chris@226 233 fftf_plan m_fftPlan;
Chris@148 234
Chris@148 235 class FillThread : public Thread
Chris@148 236 {
Chris@148 237 public:
Chris@148 238 FillThread(FFTDataServer &server, size_t fillFromColumn) :
Chris@148 239 m_server(server), m_extent(0), m_completion(0),
Chris@148 240 m_fillFrom(fillFromColumn) { }
Chris@148 241
Chris@148 242 size_t getExtent() const { return m_extent; }
Chris@148 243 size_t getCompletion() const { return m_completion ? m_completion : 1; }
Chris@148 244 virtual void run();
Chris@148 245
Chris@148 246 protected:
Chris@148 247 FFTDataServer &m_server;
Chris@148 248 size_t m_extent;
Chris@148 249 size_t m_completion;
Chris@148 250 size_t m_fillFrom;
Chris@148 251 };
Chris@148 252
Chris@148 253 bool m_exiting;
Chris@148 254 bool m_suspended;
Chris@148 255 FillThread *m_fillThread;
Chris@148 256
Chris@148 257 void deleteProcessingData();
Chris@408 258 void fillColumn(size_t x, bool lockHeld);
Chris@537 259 void fillComplete();
Chris@148 260
Chris@148 261 QString generateFileBasename() const;
Chris@148 262 static QString generateFileBasename(const DenseTimeValueModel *model,
Chris@148 263 int channel,
Chris@148 264 WindowType windowType,
Chris@148 265 size_t windowSize,
Chris@148 266 size_t windowIncrement,
Chris@148 267 size_t fftSize,
Chris@148 268 bool polar);
Chris@148 269
Chris@148 270 typedef std::pair<FFTDataServer *, int> ServerCountPair;
Chris@148 271 typedef std::map<QString, ServerCountPair> ServerMap;
Chris@215 272 typedef std::deque<FFTDataServer *> ServerQueue;
Chris@148 273
Chris@148 274 static ServerMap m_servers;
Chris@215 275 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
Chris@148 276 static QMutex m_serverMapMutex;
Chris@148 277 static FFTDataServer *findServer(QString); // call with serverMapMutex held
Chris@148 278 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
Chris@216 279
Chris@216 280 static void claimInstance(FFTDataServer *, bool needLock);
Chris@216 281 static void releaseInstance(FFTDataServer *, bool needLock);
Chris@216 282
Chris@148 283 };
Chris@148 284
Chris@148 285 #endif