svcore  1.9
FFTDataServer.h
Go to the documentation of this file.
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