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@148: Chris@148: #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: class FFTCache; 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@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria = Chris@334: StorageAdviser::NoCriteria, Chris@148: size_t fillFromColumn = 0); Chris@148: Chris@148: static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria = Chris@334: StorageAdviser::NoCriteria, Chris@148: size_t fillFromColumn = 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@148: size_t getWindowSize() const { return m_windowSize; } Chris@148: size_t getWindowIncrement() const { return m_windowIncrement; } Chris@148: size_t getFFTSize() const { return m_fftSize; } Chris@148: bool getPolar() const { return m_polar; } Chris@148: Chris@148: size_t getWidth() const { return m_width; } Chris@148: size_t getHeight() const { return m_height; } Chris@148: Chris@148: float getMagnitudeAt(size_t x, size_t y); Chris@148: float getNormalizedMagnitudeAt(size_t x, size_t y); Chris@148: float getMaximumMagnitudeAt(size_t x); Chris@148: float getPhaseAt(size_t x, size_t y); Chris@148: void getValuesAt(size_t x, size_t y, float &real, float &imaginary); Chris@148: bool isColumnReady(size_t x); Chris@148: 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@148: bool isLocalPeak(size_t x, size_t 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@148: bool isOverThreshold(size_t x, size_t y, float threshold) { Chris@148: return getMagnitudeAt(x, y) > threshold; Chris@148: } Chris@148: Chris@148: size_t getFillCompletion() const; Chris@148: size_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@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@334: StorageAdviser::Criteria criteria, Chris@148: size_t fillFromColumn = 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@148: size_t m_windowSize; Chris@148: size_t m_windowIncrement; Chris@148: size_t m_fftSize; Chris@148: bool m_polar; Chris@148: Chris@148: size_t m_width; Chris@148: size_t m_height; Chris@148: size_t m_cacheWidth; Chris@183: size_t m_cacheWidthPower; Chris@183: size_t m_cacheWidthMask; Chris@383: Chris@383: int m_lastUsedCache; Chris@383: FFTCache *getCache(size_t x, size_t &col) { Chris@383: col = x & m_cacheWidthMask; Chris@383: int c = x >> m_cacheWidthPower; Chris@383: // The only use of m_lastUsedCache without a lock is to Chris@383: // establish whether a cache has been created at all (they're Chris@383: // created on demand, but not destroyed until the server is). Chris@383: if (c == m_lastUsedCache) return m_caches[c]; Chris@383: else return getCacheAux(c); Chris@383: } Chris@383: bool haveCache(size_t x) { Chris@383: int c = x >> m_cacheWidthPower; Chris@383: if (c == m_lastUsedCache) return true; Chris@383: else return (m_caches[c] != 0); Chris@383: } Chris@148: Chris@148: typedef std::vector CacheVector; Chris@148: CacheVector m_caches; Chris@148: Chris@148: typedef std::deque IntQueue; Chris@148: IntQueue m_dormantCaches; Chris@148: Chris@383: StorageAdviser::Criteria m_criteria; Chris@383: Chris@383: void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact); Chris@148: Chris@148: FFTCache *getCacheAux(size_t c); Chris@148: QMutex m_writeMutex; 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@148: FillThread(FFTDataServer &server, size_t fillFromColumn) : Chris@148: m_server(server), m_extent(0), m_completion(0), Chris@148: m_fillFrom(fillFromColumn) { } Chris@148: Chris@148: size_t getExtent() const { return m_extent; } Chris@148: size_t getCompletion() const { return m_completion ? m_completion : 1; } Chris@148: virtual void run(); Chris@148: Chris@148: protected: Chris@148: FFTDataServer &m_server; Chris@148: size_t m_extent; Chris@148: size_t m_completion; Chris@148: size_t m_fillFrom; Chris@148: }; Chris@148: Chris@148: bool m_exiting; Chris@148: bool m_suspended; Chris@148: FillThread *m_fillThread; Chris@148: Chris@148: void deleteProcessingData(); Chris@148: void fillColumn(size_t x); Chris@148: Chris@148: QString generateFileBasename() const; Chris@148: static QString generateFileBasename(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t 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