Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@202: This file copyright 2006 Chris Cannam and QMUL. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #ifndef _FFT_DATA_SERVER_H_ Chris@148: #define _FFT_DATA_SERVER_H_ Chris@148: Chris@148: #include "base/Window.h" Chris@148: #include "base/Thread.h" Chris@334: #include "base/StorageAdviser.h" Chris@148: Chris@226: #include "FFTapi.h" Chris@537: #include "FFTFileCacheReader.h" Chris@537: #include "FFTFileCacheWriter.h" Chris@537: #include "FFTMemoryCache.h" Chris@148: Chris@148: #include Chris@537: #include Chris@537: #include Chris@148: #include Chris@148: #include Chris@148: Chris@148: #include Chris@148: #include Chris@148: Chris@148: class DenseTimeValueModel; Chris@215: class Model; Chris@148: Chris@148: class FFTDataServer Chris@148: { Chris@148: public: Chris@148: static FFTDataServer *getInstance(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@929: int windowSize, Chris@929: int windowIncrement, Chris@929: int fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria = Chris@334: StorageAdviser::NoCriteria, Chris@1045: sv_frame_t fillFromFrame = 0); Chris@148: Chris@148: static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@929: int windowSize, Chris@929: int windowIncrement, Chris@929: int fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria = Chris@334: StorageAdviser::NoCriteria, Chris@1045: sv_frame_t fillFromFrame = 0); Chris@148: Chris@152: static void claimInstance(FFTDataServer *); Chris@148: static void releaseInstance(FFTDataServer *); Chris@148: Chris@215: static void modelAboutToBeDeleted(Model *); Chris@215: Chris@148: const DenseTimeValueModel *getModel() const { return m_model; } Chris@148: int getChannel() const { return m_channel; } Chris@148: WindowType getWindowType() const { return m_windower.getType(); } Chris@1081: int getWindowSize() const { return m_windowSize; } Chris@1081: int getWindowIncrement() const { return m_windowIncrement; } Chris@1081: int getFFTSize() const { return m_fftSize; } Chris@148: bool getPolar() const { return m_polar; } Chris@148: Chris@1081: int getWidth() const { return m_width; } Chris@1081: int getHeight() const { return m_height; } Chris@148: Chris@929: float getMagnitudeAt(int x, int y); Chris@929: float getNormalizedMagnitudeAt(int x, int y); Chris@929: float getMaximumMagnitudeAt(int x); Chris@929: float getPhaseAt(int x, int y); Chris@929: void getValuesAt(int x, int y, float &real, float &imaginary); Chris@929: bool isColumnReady(int x); Chris@148: Chris@929: bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); Chris@929: bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); Chris@929: bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); Chris@929: bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0, int step = 1); Chris@408: Chris@148: void suspend(); Chris@155: void suspendWrites(); Chris@154: void resume(); // also happens automatically if new data needed Chris@148: Chris@148: // Convenience functions: Chris@148: Chris@929: bool isLocalPeak(int x, int y) { Chris@148: float mag = getMagnitudeAt(x, y); Chris@148: if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false; Chris@148: if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false; Chris@148: return true; Chris@148: } Chris@929: bool isOverThreshold(int x, int y, float threshold) { Chris@148: return getMagnitudeAt(x, y) > threshold; Chris@148: } Chris@148: Chris@678: QString getError() const; Chris@929: int getFillCompletion() const; Chris@1038: sv_frame_t getFillExtent() const; Chris@148: Chris@148: private: Chris@148: FFTDataServer(QString fileBaseName, Chris@148: const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@929: int windowSize, Chris@929: int windowIncrement, Chris@929: int fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria, Chris@1045: sv_frame_t fillFromFrame = 0); Chris@148: Chris@148: virtual ~FFTDataServer(); Chris@148: Chris@148: FFTDataServer(const FFTDataServer &); // not implemented Chris@148: FFTDataServer &operator=(const FFTDataServer &); // not implemented Chris@148: Chris@148: typedef float fftsample; Chris@148: Chris@148: QString m_fileBaseName; Chris@148: const DenseTimeValueModel *m_model; Chris@148: int m_channel; Chris@148: Chris@148: Window m_windower; Chris@148: Chris@929: int m_windowSize; Chris@929: int m_windowIncrement; Chris@929: int m_fftSize; Chris@148: bool m_polar; Chris@148: Chris@929: int m_width; Chris@929: int m_height; Chris@929: int m_cacheWidth; Chris@929: int m_cacheWidthPower; Chris@929: int m_cacheWidthMask; Chris@359: Chris@537: struct CacheBlock { Chris@537: FFTMemoryCache *memoryCache; Chris@537: typedef std::map ThreadReaderMap; Chris@537: ThreadReaderMap fileCacheReader; Chris@537: FFTFileCacheWriter *fileCacheWriter; Chris@537: CacheBlock() : memoryCache(0), fileCacheWriter(0) { } Chris@537: ~CacheBlock() { Chris@537: delete memoryCache; Chris@537: while (!fileCacheReader.empty()) { Chris@537: delete fileCacheReader.begin()->second; Chris@537: fileCacheReader.erase(fileCacheReader.begin()); Chris@537: } Chris@537: delete fileCacheWriter; Chris@537: } Chris@537: }; Chris@537: Chris@537: typedef std::vector CacheVector; Chris@537: CacheVector m_caches; Chris@537: QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use Chris@548: QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls Chris@537: Chris@929: FFTCacheReader *getCacheReader(int x, int &col) { Chris@537: Profiler profiler("FFTDataServer::getCacheReader"); Chris@537: col = x & m_cacheWidthMask; Chris@359: int c = x >> m_cacheWidthPower; Chris@537: m_cacheVectorLock.lockForRead(); Chris@537: CacheBlock *cb(m_caches.at(c)); Chris@537: if (cb) { Chris@547: if (cb->memoryCache) { Chris@547: m_cacheVectorLock.unlock(); Chris@547: return cb->memoryCache; Chris@547: } Chris@537: if (cb->fileCacheWriter) { Chris@537: QThread *me = QThread::currentThread(); Chris@537: CacheBlock::ThreadReaderMap &map = cb->fileCacheReader; Chris@537: if (map.find(me) == map.end()) { Chris@537: m_cacheVectorLock.unlock(); Chris@537: if (!makeCacheReader(c)) return 0; Chris@537: return getCacheReader(x, col); Chris@537: } Chris@539: FFTCacheReader *reader = cb->fileCacheReader[me]; Chris@537: m_cacheVectorLock.unlock(); Chris@537: return reader; Chris@537: } Chris@537: // if cb exists but cb->fileCacheWriter doesn't, creation Chris@537: // must have failed: don't try again Chris@537: m_cacheVectorLock.unlock(); Chris@537: return 0; Chris@537: } Chris@537: m_cacheVectorLock.unlock(); Chris@1081: if (getError() != "") return 0; Chris@537: if (!makeCache(c)) return 0; Chris@537: return getCacheReader(x, col); Chris@359: } Chris@537: Chris@929: FFTCacheWriter *getCacheWriter(int x, int &col) { Chris@537: Profiler profiler("FFTDataServer::getCacheWriter"); Chris@537: col = x & m_cacheWidthMask; Chris@537: int c = x >> m_cacheWidthPower; Chris@537: { Chris@537: QReadLocker locker(&m_cacheVectorLock); Chris@537: CacheBlock *cb(m_caches.at(c)); Chris@537: if (cb) { Chris@537: if (cb->memoryCache) return cb->memoryCache; Chris@537: if (cb->fileCacheWriter) return cb->fileCacheWriter; Chris@537: // if cb exists, creation must have failed: don't try again Chris@537: return 0; Chris@537: } Chris@537: } Chris@537: if (!makeCache(c)) return 0; Chris@537: return getCacheWriter(x, col); Chris@537: } Chris@537: Chris@929: bool haveCache(int x) { Chris@359: int c = x >> m_cacheWidthPower; Chris@548: return (m_caches.at(c) != 0); Chris@359: } Chris@148: Chris@537: bool makeCache(int c); Chris@537: bool makeCacheReader(int c); Chris@537: Chris@359: StorageAdviser::Criteria m_criteria; Chris@359: Chris@929: void getStorageAdvice(int w, int h, bool &memory, bool &compact); Chris@148: Chris@1081: mutable QMutex m_fftBuffersLock; Chris@148: QWaitCondition m_condition; Chris@148: Chris@148: fftsample *m_fftInput; Chris@226: fftf_complex *m_fftOutput; Chris@148: float *m_workbuffer; Chris@226: fftf_plan m_fftPlan; Chris@148: Chris@148: class FillThread : public Thread Chris@148: { Chris@148: public: Chris@1045: FillThread(FFTDataServer &server, sv_frame_t fillFromFrame) : Chris@148: m_server(server), m_extent(0), m_completion(0), Chris@1045: m_fillFrom(fillFromFrame) { } Chris@148: Chris@1038: sv_frame_t getExtent() const { return m_extent; } Chris@929: int getCompletion() const { return m_completion ? m_completion : 1; } Chris@1081: QString getError() const { return m_threadError; } Chris@148: virtual void run(); Chris@148: Chris@148: protected: Chris@148: FFTDataServer &m_server; Chris@1038: sv_frame_t m_extent; Chris@929: int m_completion; Chris@1038: sv_frame_t m_fillFrom; Chris@1081: QString m_threadError; Chris@148: }; Chris@148: Chris@148: bool m_exiting; Chris@148: bool m_suspended; Chris@148: FillThread *m_fillThread; Chris@678: QString m_error; Chris@148: Chris@148: void deleteProcessingData(); Chris@929: void fillColumn(int x); Chris@537: void fillComplete(); Chris@148: Chris@148: QString generateFileBasename() const; Chris@148: static QString generateFileBasename(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@929: int windowSize, Chris@929: int windowIncrement, Chris@929: int fftSize, Chris@148: bool polar); Chris@148: Chris@148: typedef std::pair ServerCountPair; Chris@148: typedef std::map ServerMap; Chris@215: typedef std::deque ServerQueue; Chris@148: Chris@148: static ServerMap m_servers; Chris@215: static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount Chris@148: static QMutex m_serverMapMutex; Chris@148: static FFTDataServer *findServer(QString); // call with serverMapMutex held Chris@148: static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held Chris@216: Chris@216: static void claimInstance(FFTDataServer *, bool needLock); Chris@216: static void releaseInstance(FFTDataServer *, bool needLock); Chris@216: Chris@148: }; Chris@148: Chris@148: #endif