annotate data/fft/FFTDataServer.h @ 626:686fc1703a33

* Avoid failing on second and subsequent input files in single-output-file mode (because first input file caused output file to be open, so second input file failed because output "already existed")
author Chris Cannam
date Mon, 24 May 2010 16:04:10 +0000
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