annotate data/fft/FFTDataServer.h @ 823:f0558e69a074

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