annotate data/fft/FFTDataServer.h @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents 53e5dc8439e7
children 948271d124ac
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@556 91 bool getValuesAt(size_t x, float *reals, float *imaginaries, size_t minbin = 0, size_t count = 0, size_t 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@148 99 bool isLocalPeak(size_t x, size_t 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@148 105 bool isOverThreshold(size_t x, size_t y, float threshold) {
Chris@148 106 return getMagnitudeAt(x, y) > threshold;
Chris@148 107 }
Chris@148 108
Chris@148 109 size_t getFillCompletion() const;
Chris@148 110 size_t getFillExtent() const;
Chris@148 111
Chris@148 112 private:
Chris@148 113 FFTDataServer(QString fileBaseName,
Chris@148 114 const DenseTimeValueModel *model,
Chris@148 115 int channel,
Chris@148 116 WindowType windowType,
Chris@148 117 size_t windowSize,
Chris@148 118 size_t windowIncrement,
Chris@148 119 size_t fftSize,
Chris@148 120 bool polar,
Chris@334 121 StorageAdviser::Criteria criteria,
Chris@148 122 size_t fillFromColumn = 0);
Chris@148 123
Chris@148 124 virtual ~FFTDataServer();
Chris@148 125
Chris@148 126 FFTDataServer(const FFTDataServer &); // not implemented
Chris@148 127 FFTDataServer &operator=(const FFTDataServer &); // not implemented
Chris@148 128
Chris@148 129 typedef float fftsample;
Chris@148 130
Chris@148 131 QString m_fileBaseName;
Chris@148 132 const DenseTimeValueModel *m_model;
Chris@148 133 int m_channel;
Chris@148 134
Chris@148 135 Window<fftsample> m_windower;
Chris@148 136
Chris@148 137 size_t m_windowSize;
Chris@148 138 size_t m_windowIncrement;
Chris@148 139 size_t m_fftSize;
Chris@148 140 bool m_polar;
Chris@148 141
Chris@148 142 size_t m_width;
Chris@148 143 size_t m_height;
Chris@148 144 size_t m_cacheWidth;
Chris@183 145 size_t m_cacheWidthPower;
Chris@183 146 size_t m_cacheWidthMask;
Chris@359 147
Chris@537 148 struct CacheBlock {
Chris@537 149 FFTMemoryCache *memoryCache;
Chris@537 150 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
Chris@537 151 ThreadReaderMap fileCacheReader;
Chris@537 152 FFTFileCacheWriter *fileCacheWriter;
Chris@537 153 CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
Chris@537 154 ~CacheBlock() {
Chris@537 155 delete memoryCache;
Chris@537 156 while (!fileCacheReader.empty()) {
Chris@537 157 delete fileCacheReader.begin()->second;
Chris@537 158 fileCacheReader.erase(fileCacheReader.begin());
Chris@537 159 }
Chris@537 160 delete fileCacheWriter;
Chris@537 161 }
Chris@537 162 };
Chris@537 163
Chris@537 164 typedef std::vector<CacheBlock *> CacheVector;
Chris@537 165 CacheVector m_caches;
Chris@537 166 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
Chris@548 167 QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls
Chris@537 168
Chris@537 169 FFTCacheReader *getCacheReader(size_t x, size_t &col) {
Chris@537 170 Profiler profiler("FFTDataServer::getCacheReader");
Chris@537 171 col = x & m_cacheWidthMask;
Chris@359 172 int c = x >> m_cacheWidthPower;
Chris@537 173 m_cacheVectorLock.lockForRead();
Chris@537 174 CacheBlock *cb(m_caches.at(c));
Chris@537 175 if (cb) {
Chris@547 176 if (cb->memoryCache) {
Chris@547 177 m_cacheVectorLock.unlock();
Chris@547 178 return cb->memoryCache;
Chris@547 179 }
Chris@537 180 if (cb->fileCacheWriter) {
Chris@537 181 QThread *me = QThread::currentThread();
Chris@537 182 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
Chris@537 183 if (map.find(me) == map.end()) {
Chris@537 184 m_cacheVectorLock.unlock();
Chris@537 185 if (!makeCacheReader(c)) return 0;
Chris@537 186 return getCacheReader(x, col);
Chris@537 187 }
Chris@539 188 FFTCacheReader *reader = cb->fileCacheReader[me];
Chris@537 189 m_cacheVectorLock.unlock();
Chris@537 190 return reader;
Chris@537 191 }
Chris@537 192 // if cb exists but cb->fileCacheWriter doesn't, creation
Chris@537 193 // must have failed: don't try again
Chris@537 194 m_cacheVectorLock.unlock();
Chris@537 195 return 0;
Chris@537 196 }
Chris@537 197 m_cacheVectorLock.unlock();
Chris@537 198 if (!makeCache(c)) return 0;
Chris@537 199 return getCacheReader(x, col);
Chris@359 200 }
Chris@537 201
Chris@537 202 FFTCacheWriter *getCacheWriter(size_t x, size_t &col) {
Chris@537 203 Profiler profiler("FFTDataServer::getCacheWriter");
Chris@537 204 col = x & m_cacheWidthMask;
Chris@537 205 int c = x >> m_cacheWidthPower;
Chris@537 206 {
Chris@537 207 QReadLocker locker(&m_cacheVectorLock);
Chris@537 208 CacheBlock *cb(m_caches.at(c));
Chris@537 209 if (cb) {
Chris@537 210 if (cb->memoryCache) return cb->memoryCache;
Chris@537 211 if (cb->fileCacheWriter) return cb->fileCacheWriter;
Chris@537 212 // if cb exists, creation must have failed: don't try again
Chris@537 213 return 0;
Chris@537 214 }
Chris@537 215 }
Chris@537 216 if (!makeCache(c)) return 0;
Chris@537 217 return getCacheWriter(x, col);
Chris@537 218 }
Chris@537 219
Chris@359 220 bool haveCache(size_t x) {
Chris@359 221 int c = x >> m_cacheWidthPower;
Chris@548 222 return (m_caches.at(c) != 0);
Chris@359 223 }
Chris@148 224
Chris@537 225 bool makeCache(int c);
Chris@537 226 bool makeCacheReader(int c);
Chris@537 227
Chris@359 228 StorageAdviser::Criteria m_criteria;
Chris@359 229
Chris@359 230 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact);
Chris@148 231
Chris@549 232 QMutex m_fftBuffersLock;
Chris@148 233 QWaitCondition m_condition;
Chris@148 234
Chris@148 235 fftsample *m_fftInput;
Chris@226 236 fftf_complex *m_fftOutput;
Chris@148 237 float *m_workbuffer;
Chris@226 238 fftf_plan m_fftPlan;
Chris@148 239
Chris@148 240 class FillThread : public Thread
Chris@148 241 {
Chris@148 242 public:
Chris@148 243 FillThread(FFTDataServer &server, size_t fillFromColumn) :
Chris@148 244 m_server(server), m_extent(0), m_completion(0),
Chris@148 245 m_fillFrom(fillFromColumn) { }
Chris@148 246
Chris@148 247 size_t getExtent() const { return m_extent; }
Chris@148 248 size_t getCompletion() const { return m_completion ? m_completion : 1; }
Chris@148 249 virtual void run();
Chris@148 250
Chris@148 251 protected:
Chris@148 252 FFTDataServer &m_server;
Chris@148 253 size_t m_extent;
Chris@148 254 size_t m_completion;
Chris@148 255 size_t m_fillFrom;
Chris@148 256 };
Chris@148 257
Chris@148 258 bool m_exiting;
Chris@148 259 bool m_suspended;
Chris@148 260 FillThread *m_fillThread;
Chris@148 261
Chris@148 262 void deleteProcessingData();
Chris@550 263 void fillColumn(size_t x);
Chris@537 264 void fillComplete();
Chris@148 265
Chris@148 266 QString generateFileBasename() const;
Chris@148 267 static QString generateFileBasename(const DenseTimeValueModel *model,
Chris@148 268 int channel,
Chris@148 269 WindowType windowType,
Chris@148 270 size_t windowSize,
Chris@148 271 size_t windowIncrement,
Chris@148 272 size_t fftSize,
Chris@148 273 bool polar);
Chris@148 274
Chris@148 275 typedef std::pair<FFTDataServer *, int> ServerCountPair;
Chris@148 276 typedef std::map<QString, ServerCountPair> ServerMap;
Chris@215 277 typedef std::deque<FFTDataServer *> ServerQueue;
Chris@148 278
Chris@148 279 static ServerMap m_servers;
Chris@215 280 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
Chris@148 281 static QMutex m_serverMapMutex;
Chris@148 282 static FFTDataServer *findServer(QString); // call with serverMapMutex held
Chris@148 283 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
Chris@216 284
Chris@216 285 static void claimInstance(FFTDataServer *, bool needLock);
Chris@216 286 static void releaseInstance(FFTDataServer *, bool needLock);
Chris@216 287
Chris@148 288 };
Chris@148 289
Chris@148 290 #endif