annotate data/fft/FFTDataServer.h @ 458:f60360209e5c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents 115f60df1e4d
children 3cc4b7cd2aa5
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@148 24
Chris@148 25 #include <QMutex>
Chris@148 26 #include <QWaitCondition>
Chris@148 27 #include <QString>
Chris@148 28
Chris@148 29 #include <vector>
Chris@148 30 #include <deque>
Chris@148 31
Chris@148 32 class DenseTimeValueModel;
Chris@215 33 class Model;
Chris@148 34 class FFTCache;
Chris@148 35
Chris@148 36 class FFTDataServer
Chris@148 37 {
Chris@148 38 public:
Chris@148 39 static FFTDataServer *getInstance(const DenseTimeValueModel *model,
Chris@148 40 int channel,
Chris@148 41 WindowType windowType,
Chris@148 42 size_t windowSize,
Chris@148 43 size_t windowIncrement,
Chris@148 44 size_t fftSize,
Chris@148 45 bool polar,
Chris@334 46 StorageAdviser::Criteria criteria =
Chris@334 47 StorageAdviser::NoCriteria,
Chris@148 48 size_t fillFromColumn = 0);
Chris@148 49
Chris@148 50 static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model,
Chris@148 51 int channel,
Chris@148 52 WindowType windowType,
Chris@148 53 size_t windowSize,
Chris@148 54 size_t windowIncrement,
Chris@148 55 size_t fftSize,
Chris@148 56 bool polar,
Chris@334 57 StorageAdviser::Criteria criteria =
Chris@334 58 StorageAdviser::NoCriteria,
Chris@148 59 size_t fillFromColumn = 0);
Chris@148 60
Chris@152 61 static void claimInstance(FFTDataServer *);
Chris@148 62 static void releaseInstance(FFTDataServer *);
Chris@148 63
Chris@215 64 static void modelAboutToBeDeleted(Model *);
Chris@215 65
Chris@148 66 const DenseTimeValueModel *getModel() const { return m_model; }
Chris@148 67 int getChannel() const { return m_channel; }
Chris@148 68 WindowType getWindowType() const { return m_windower.getType(); }
Chris@148 69 size_t getWindowSize() const { return m_windowSize; }
Chris@148 70 size_t getWindowIncrement() const { return m_windowIncrement; }
Chris@148 71 size_t getFFTSize() const { return m_fftSize; }
Chris@148 72 bool getPolar() const { return m_polar; }
Chris@148 73
Chris@148 74 size_t getWidth() const { return m_width; }
Chris@148 75 size_t getHeight() const { return m_height; }
Chris@148 76
Chris@148 77 float getMagnitudeAt(size_t x, size_t y);
Chris@148 78 float getNormalizedMagnitudeAt(size_t x, size_t y);
Chris@148 79 float getMaximumMagnitudeAt(size_t x);
Chris@148 80 float getPhaseAt(size_t x, size_t y);
Chris@148 81 void getValuesAt(size_t x, size_t y, float &real, float &imaginary);
Chris@148 82 bool isColumnReady(size_t x);
Chris@148 83
Chris@408 84 bool getMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 85 bool getNormalizedMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 86 bool getPhasesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 87
Chris@148 88 void suspend();
Chris@155 89 void suspendWrites();
Chris@154 90 void resume(); // also happens automatically if new data needed
Chris@148 91
Chris@148 92 // Convenience functions:
Chris@148 93
Chris@148 94 bool isLocalPeak(size_t x, size_t y) {
Chris@148 95 float mag = getMagnitudeAt(x, y);
Chris@148 96 if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
Chris@148 97 if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false;
Chris@148 98 return true;
Chris@148 99 }
Chris@148 100 bool isOverThreshold(size_t x, size_t y, float threshold) {
Chris@148 101 return getMagnitudeAt(x, y) > threshold;
Chris@148 102 }
Chris@148 103
Chris@148 104 size_t getFillCompletion() const;
Chris@148 105 size_t getFillExtent() const;
Chris@148 106
Chris@148 107 private:
Chris@148 108 FFTDataServer(QString fileBaseName,
Chris@148 109 const DenseTimeValueModel *model,
Chris@148 110 int channel,
Chris@148 111 WindowType windowType,
Chris@148 112 size_t windowSize,
Chris@148 113 size_t windowIncrement,
Chris@148 114 size_t fftSize,
Chris@148 115 bool polar,
Chris@334 116 StorageAdviser::Criteria criteria,
Chris@148 117 size_t fillFromColumn = 0);
Chris@148 118
Chris@148 119 virtual ~FFTDataServer();
Chris@148 120
Chris@148 121 FFTDataServer(const FFTDataServer &); // not implemented
Chris@148 122 FFTDataServer &operator=(const FFTDataServer &); // not implemented
Chris@148 123
Chris@148 124 typedef float fftsample;
Chris@148 125
Chris@148 126 QString m_fileBaseName;
Chris@148 127 const DenseTimeValueModel *m_model;
Chris@148 128 int m_channel;
Chris@148 129
Chris@148 130 Window<fftsample> m_windower;
Chris@148 131
Chris@148 132 size_t m_windowSize;
Chris@148 133 size_t m_windowIncrement;
Chris@148 134 size_t m_fftSize;
Chris@148 135 bool m_polar;
Chris@148 136
Chris@148 137 size_t m_width;
Chris@148 138 size_t m_height;
Chris@148 139 size_t m_cacheWidth;
Chris@183 140 size_t m_cacheWidthPower;
Chris@183 141 size_t m_cacheWidthMask;
Chris@359 142
Chris@359 143 int m_lastUsedCache;
Chris@359 144 FFTCache *getCache(size_t x, size_t &col) {
Chris@359 145 col = x & m_cacheWidthMask;
Chris@359 146 int c = x >> m_cacheWidthPower;
Chris@359 147 // The only use of m_lastUsedCache without a lock is to
Chris@359 148 // establish whether a cache has been created at all (they're
Chris@359 149 // created on demand, but not destroyed until the server is).
Chris@359 150 if (c == m_lastUsedCache) return m_caches[c];
Chris@359 151 else return getCacheAux(c);
Chris@359 152 }
Chris@359 153 bool haveCache(size_t x) {
Chris@359 154 int c = x >> m_cacheWidthPower;
Chris@359 155 if (c == m_lastUsedCache) return true;
Chris@359 156 else return (m_caches[c] != 0);
Chris@359 157 }
Chris@148 158
Chris@148 159 typedef std::vector<FFTCache *> CacheVector;
Chris@148 160 CacheVector m_caches;
Chris@148 161
Chris@148 162 typedef std::deque<int> IntQueue;
Chris@148 163 IntQueue m_dormantCaches;
Chris@148 164
Chris@359 165 StorageAdviser::Criteria m_criteria;
Chris@359 166
Chris@359 167 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact);
Chris@148 168
Chris@148 169 FFTCache *getCacheAux(size_t c);
Chris@148 170 QMutex m_writeMutex;
Chris@148 171 QWaitCondition m_condition;
Chris@148 172
Chris@148 173 fftsample *m_fftInput;
Chris@226 174 fftf_complex *m_fftOutput;
Chris@148 175 float *m_workbuffer;
Chris@226 176 fftf_plan m_fftPlan;
Chris@148 177
Chris@148 178 class FillThread : public Thread
Chris@148 179 {
Chris@148 180 public:
Chris@148 181 FillThread(FFTDataServer &server, size_t fillFromColumn) :
Chris@148 182 m_server(server), m_extent(0), m_completion(0),
Chris@148 183 m_fillFrom(fillFromColumn) { }
Chris@148 184
Chris@148 185 size_t getExtent() const { return m_extent; }
Chris@148 186 size_t getCompletion() const { return m_completion ? m_completion : 1; }
Chris@148 187 virtual void run();
Chris@148 188
Chris@148 189 protected:
Chris@148 190 FFTDataServer &m_server;
Chris@148 191 size_t m_extent;
Chris@148 192 size_t m_completion;
Chris@148 193 size_t m_fillFrom;
Chris@148 194 };
Chris@148 195
Chris@148 196 bool m_exiting;
Chris@148 197 bool m_suspended;
Chris@148 198 FillThread *m_fillThread;
Chris@148 199
Chris@148 200 void deleteProcessingData();
Chris@408 201 void fillColumn(size_t x, bool lockHeld);
Chris@148 202
Chris@148 203 QString generateFileBasename() const;
Chris@148 204 static QString generateFileBasename(const DenseTimeValueModel *model,
Chris@148 205 int channel,
Chris@148 206 WindowType windowType,
Chris@148 207 size_t windowSize,
Chris@148 208 size_t windowIncrement,
Chris@148 209 size_t fftSize,
Chris@148 210 bool polar);
Chris@148 211
Chris@148 212 typedef std::pair<FFTDataServer *, int> ServerCountPair;
Chris@148 213 typedef std::map<QString, ServerCountPair> ServerMap;
Chris@215 214 typedef std::deque<FFTDataServer *> ServerQueue;
Chris@148 215
Chris@148 216 static ServerMap m_servers;
Chris@215 217 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
Chris@148 218 static QMutex m_serverMapMutex;
Chris@148 219 static FFTDataServer *findServer(QString); // call with serverMapMutex held
Chris@148 220 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
Chris@216 221
Chris@216 222 static void claimInstance(FFTDataServer *, bool needLock);
Chris@216 223 static void releaseInstance(FFTDataServer *, bool needLock);
Chris@216 224
Chris@148 225 };
Chris@148 226
Chris@148 227 #endif