svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #ifndef _FFT_DATA_SERVER_H_ 00017 #define _FFT_DATA_SERVER_H_ 00018 00019 #include "base/Window.h" 00020 #include "base/Thread.h" 00021 #include "base/StorageAdviser.h" 00022 00023 #include "FFTapi.h" 00024 #include "FFTFileCacheReader.h" 00025 #include "FFTFileCacheWriter.h" 00026 #include "FFTMemoryCache.h" 00027 00028 #include <QMutex> 00029 #include <QReadWriteLock> 00030 #include <QReadLocker> 00031 #include <QWaitCondition> 00032 #include <QString> 00033 00034 #include <vector> 00035 #include <deque> 00036 00037 class DenseTimeValueModel; 00038 class Model; 00039 00040 class FFTDataServer 00041 { 00042 public: 00043 static FFTDataServer *getInstance(const DenseTimeValueModel *model, 00044 int channel, 00045 WindowType windowType, 00046 int windowSize, 00047 int windowIncrement, 00048 int fftSize, 00049 bool polar, 00050 StorageAdviser::Criteria criteria = 00051 StorageAdviser::NoCriteria, 00052 int fillFromColumn = 0); 00053 00054 static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model, 00055 int channel, 00056 WindowType windowType, 00057 int windowSize, 00058 int windowIncrement, 00059 int fftSize, 00060 bool polar, 00061 StorageAdviser::Criteria criteria = 00062 StorageAdviser::NoCriteria, 00063 int fillFromColumn = 0); 00064 00065 static void claimInstance(FFTDataServer *); 00066 static void releaseInstance(FFTDataServer *); 00067 00068 static void modelAboutToBeDeleted(Model *); 00069 00070 const DenseTimeValueModel *getModel() const { return m_model; } 00071 int getChannel() const { return m_channel; } 00072 WindowType getWindowType() const { return m_windower.getType(); } 00073 int getWindowSize() const { return m_windowSize; } 00074 int getWindowIncrement() const { return m_windowIncrement; } 00075 int getFFTSize() const { return m_fftSize; } 00076 bool getPolar() const { return m_polar; } 00077 00078 int getWidth() const { return m_width; } 00079 int getHeight() const { return m_height; } 00080 00081 float getMagnitudeAt(int x, int y); 00082 float getNormalizedMagnitudeAt(int x, int y); 00083 float getMaximumMagnitudeAt(int x); 00084 float getPhaseAt(int x, int y); 00085 void getValuesAt(int x, int y, float &real, float &imaginary); 00086 bool isColumnReady(int x); 00087 00088 bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); 00089 bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); 00090 bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); 00091 bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0, int step = 1); 00092 00093 void suspend(); 00094 void suspendWrites(); 00095 void resume(); // also happens automatically if new data needed 00096 00097 // Convenience functions: 00098 00099 bool isLocalPeak(int x, int y) { 00100 float mag = getMagnitudeAt(x, y); 00101 if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false; 00102 if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false; 00103 return true; 00104 } 00105 bool isOverThreshold(int x, int y, float threshold) { 00106 return getMagnitudeAt(x, y) > threshold; 00107 } 00108 00109 QString getError() const; 00110 int getFillCompletion() const; 00111 int getFillExtent() const; 00112 00113 private: 00114 FFTDataServer(QString fileBaseName, 00115 const DenseTimeValueModel *model, 00116 int channel, 00117 WindowType windowType, 00118 int windowSize, 00119 int windowIncrement, 00120 int fftSize, 00121 bool polar, 00122 StorageAdviser::Criteria criteria, 00123 int fillFromColumn = 0); 00124 00125 virtual ~FFTDataServer(); 00126 00127 FFTDataServer(const FFTDataServer &); // not implemented 00128 FFTDataServer &operator=(const FFTDataServer &); // not implemented 00129 00130 typedef float fftsample; 00131 00132 QString m_fileBaseName; 00133 const DenseTimeValueModel *m_model; 00134 int m_channel; 00135 00136 Window<fftsample> m_windower; 00137 00138 int m_windowSize; 00139 int m_windowIncrement; 00140 int m_fftSize; 00141 bool m_polar; 00142 00143 int m_width; 00144 int m_height; 00145 int m_cacheWidth; 00146 int m_cacheWidthPower; 00147 int m_cacheWidthMask; 00148 00149 struct CacheBlock { 00150 FFTMemoryCache *memoryCache; 00151 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap; 00152 ThreadReaderMap fileCacheReader; 00153 FFTFileCacheWriter *fileCacheWriter; 00154 CacheBlock() : memoryCache(0), fileCacheWriter(0) { } 00155 ~CacheBlock() { 00156 delete memoryCache; 00157 while (!fileCacheReader.empty()) { 00158 delete fileCacheReader.begin()->second; 00159 fileCacheReader.erase(fileCacheReader.begin()); 00160 } 00161 delete fileCacheWriter; 00162 } 00163 }; 00164 00165 typedef std::vector<CacheBlock *> CacheVector; 00166 CacheVector m_caches; 00167 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use 00168 QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls 00169 00170 FFTCacheReader *getCacheReader(int x, int &col) { 00171 Profiler profiler("FFTDataServer::getCacheReader"); 00172 col = x & m_cacheWidthMask; 00173 int c = x >> m_cacheWidthPower; 00174 m_cacheVectorLock.lockForRead(); 00175 CacheBlock *cb(m_caches.at(c)); 00176 if (cb) { 00177 if (cb->memoryCache) { 00178 m_cacheVectorLock.unlock(); 00179 return cb->memoryCache; 00180 } 00181 if (cb->fileCacheWriter) { 00182 QThread *me = QThread::currentThread(); 00183 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader; 00184 if (map.find(me) == map.end()) { 00185 m_cacheVectorLock.unlock(); 00186 if (!makeCacheReader(c)) return 0; 00187 return getCacheReader(x, col); 00188 } 00189 FFTCacheReader *reader = cb->fileCacheReader[me]; 00190 m_cacheVectorLock.unlock(); 00191 return reader; 00192 } 00193 // if cb exists but cb->fileCacheWriter doesn't, creation 00194 // must have failed: don't try again 00195 m_cacheVectorLock.unlock(); 00196 return 0; 00197 } 00198 m_cacheVectorLock.unlock(); 00199 if (!makeCache(c)) return 0; 00200 return getCacheReader(x, col); 00201 } 00202 00203 FFTCacheWriter *getCacheWriter(int x, int &col) { 00204 Profiler profiler("FFTDataServer::getCacheWriter"); 00205 col = x & m_cacheWidthMask; 00206 int c = x >> m_cacheWidthPower; 00207 { 00208 QReadLocker locker(&m_cacheVectorLock); 00209 CacheBlock *cb(m_caches.at(c)); 00210 if (cb) { 00211 if (cb->memoryCache) return cb->memoryCache; 00212 if (cb->fileCacheWriter) return cb->fileCacheWriter; 00213 // if cb exists, creation must have failed: don't try again 00214 return 0; 00215 } 00216 } 00217 if (!makeCache(c)) return 0; 00218 return getCacheWriter(x, col); 00219 } 00220 00221 bool haveCache(int x) { 00222 int c = x >> m_cacheWidthPower; 00223 return (m_caches.at(c) != 0); 00224 } 00225 00226 bool makeCache(int c); 00227 bool makeCacheReader(int c); 00228 00229 StorageAdviser::Criteria m_criteria; 00230 00231 void getStorageAdvice(int w, int h, bool &memory, bool &compact); 00232 00233 QMutex m_fftBuffersLock; 00234 QWaitCondition m_condition; 00235 00236 fftsample *m_fftInput; 00237 fftf_complex *m_fftOutput; 00238 float *m_workbuffer; 00239 fftf_plan m_fftPlan; 00240 00241 class FillThread : public Thread 00242 { 00243 public: 00244 FillThread(FFTDataServer &server, int fillFromColumn) : 00245 m_server(server), m_extent(0), m_completion(0), 00246 m_fillFrom(fillFromColumn) { } 00247 00248 int getExtent() const { return m_extent; } 00249 int getCompletion() const { return m_completion ? m_completion : 1; } 00250 QString getError() const { return m_error; } 00251 virtual void run(); 00252 00253 protected: 00254 FFTDataServer &m_server; 00255 int m_extent; 00256 int m_completion; 00257 int m_fillFrom; 00258 QString m_error; 00259 }; 00260 00261 bool m_exiting; 00262 bool m_suspended; 00263 FillThread *m_fillThread; 00264 QString m_error; 00265 00266 void deleteProcessingData(); 00267 void fillColumn(int x); 00268 void fillComplete(); 00269 00270 QString generateFileBasename() const; 00271 static QString generateFileBasename(const DenseTimeValueModel *model, 00272 int channel, 00273 WindowType windowType, 00274 int windowSize, 00275 int windowIncrement, 00276 int fftSize, 00277 bool polar); 00278 00279 typedef std::pair<FFTDataServer *, int> ServerCountPair; 00280 typedef std::map<QString, ServerCountPair> ServerMap; 00281 typedef std::deque<FFTDataServer *> ServerQueue; 00282 00283 static ServerMap m_servers; 00284 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount 00285 static QMutex m_serverMapMutex; 00286 static FFTDataServer *findServer(QString); // call with serverMapMutex held 00287 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held 00288 00289 static void claimInstance(FFTDataServer *, bool needLock); 00290 static void releaseInstance(FFTDataServer *, bool needLock); 00291 00292 }; 00293 00294 #endif