# HG changeset patch # User Chris Cannam # Date 1434117106 -3600 # Node ID 420fc961c0c460ea259af1e2b80c3b16da95525f # Parent 655cd4e68e9a13a205dae7dfcefe1a5e49cae34b Gut the old code, but don't replace it yet (so nothing will link yet) diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTCacheReader.h --- a/data/fft/FFTCacheReader.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_CACHE_READER_H_ -#define _FFT_CACHE_READER_H_ - -#include "FFTCacheStorageType.h" -#include - -class FFTCacheReader -{ -public: - virtual ~FFTCacheReader() { } - - virtual int getWidth() const = 0; - virtual int getHeight() const = 0; - - virtual float getMagnitudeAt(int x, int y) const = 0; - virtual float getNormalizedMagnitudeAt(int x, int y) const = 0; - virtual float getMaximumMagnitudeAt(int x) const = 0; - virtual float getPhaseAt(int x, int y) const = 0; - - virtual void getValuesAt(int x, int y, float &real, float &imag) const = 0; - virtual void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const = 0; - - virtual bool haveSetColumnAt(int x) const = 0; - - virtual FFTCache::StorageType getStorageType() const = 0; -}; - -#endif diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTCacheStorageType.h --- a/data/fft/FFTCacheStorageType.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_CACHE_STORAGE_TYPE_H_ -#define _FFT_CACHE_STORAGE_TYPE_H_ - -namespace FFTCache { -enum StorageType { //!!! dup - Compact, // 16 bits normalized polar - Rectangular, // floating point real+imag - Polar // floating point mag+phase -}; -} - -#endif diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTCacheWriter.h --- a/data/fft/FFTCacheWriter.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_CACHE_WRITER_H_ -#define _FFT_CACHE_WRITER_H_ - -#include - -class FFTCacheWriter -{ -public: - virtual ~FFTCacheWriter() { } - - virtual int getWidth() const = 0; - virtual int getHeight() const = 0; - - virtual void setColumnAt(int x, float *mags, float *phases, float factor) = 0; - virtual void setColumnAt(int x, float *reals, float *imags) = 0; - - virtual bool haveSetColumnAt(int x) const = 0; - - virtual void allColumnsWritten() = 0; // notify cache to close - - virtual FFTCache::StorageType getStorageType() const = 0; -}; - -#endif - diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTDataServer.cpp --- a/data/fft/FFTDataServer.cpp Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1601 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "FFTDataServer.h" - -#include "FFTFileCacheReader.h" -#include "FFTFileCacheWriter.h" -#include "FFTMemoryCache.h" - -#include "model/DenseTimeValueModel.h" - -#include "system/System.h" - -#include "base/StorageAdviser.h" -#include "base/Exceptions.h" -#include "base/Profiler.h" -#include "base/Thread.h" // for debug mutex locker - -#include - -#include - -//#define DEBUG_FFT_SERVER 1 -//#define DEBUG_FFT_SERVER_FILL 1 - -#ifdef DEBUG_FFT_SERVER_FILL -#ifndef DEBUG_FFT_SERVER -#define DEBUG_FFT_SERVER 1 -#endif -#endif - - -FFTDataServer::ServerMap FFTDataServer::m_servers; -FFTDataServer::ServerQueue FFTDataServer::m_releasedServers; -QMutex FFTDataServer::m_serverMapMutex; - -FFTDataServer * -FFTDataServer::getInstance(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame) -{ - QString n = generateFileBasename(model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar); - - FFTDataServer *server = 0; - - MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getInstance::m_serverMapMutex"); - - if ((server = findServer(n))) { - return server; - } - - QString npn = generateFileBasename(model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - !polar); - - if ((server = findServer(npn))) { - return server; - } - - try { - server = new FFTDataServer(n, - model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar, - criteria, - fillFromFrame); - } catch (InsufficientDiscSpace) { - delete server; - server = 0; - } - - if (server) { - m_servers[n] = ServerCountPair(server, 1); - } - - return server; -} - -FFTDataServer * -FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame) -{ - // Fuzzy matching: - // - // -- if we're asked for polar and have non-polar, use it (and - // vice versa). This one is vital, and we do it for non-fuzzy as - // well (above). - // - // -- if we're asked for an instance with a given fft size and we - // have one already with a multiple of that fft size but the same - // window size and type (and model), we can draw the results from - // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the - // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the - // same window plus zero padding). - // - // -- if we're asked for an instance with a given window type and - // size and fft size and we have one already the same but with a - // smaller increment, we can draw the results from it (provided - // our increment is a multiple of its) - // - // The FFTModel knows how to interpret these things. In - // both cases we require that the larger one is a power-of-two - // multiple of the smaller (e.g. even though in principle you can - // draw the results at increment 256 from those at increment 768 - // or 1536, the model doesn't support this). - - { - MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getFuzzyInstance::m_serverMapMutex"); - - ServerMap::iterator best = m_servers.end(); - int bestdist = -1; - - for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { - - FFTDataServer *server = i->second.first; - - if (server->getModel() == model && - (server->getChannel() == channel || model->getChannelCount() == 1) && - server->getWindowType() == windowType && - server->getWindowSize() == windowSize && - server->getWindowIncrement() <= windowIncrement && - server->getFFTSize() >= fftSize) { - - if ((windowIncrement % server->getWindowIncrement()) != 0) continue; - int ratio = windowIncrement / server->getWindowIncrement(); - bool poweroftwo = true; - while (ratio > 1) { - if (ratio & 0x1) { - poweroftwo = false; - break; - } - ratio >>= 1; - } - if (!poweroftwo) continue; - - if ((server->getFFTSize() % fftSize) != 0) continue; - ratio = server->getFFTSize() / fftSize; - while (ratio > 1) { - if (ratio & 0x1) { - poweroftwo = false; - break; - } - ratio >>= 1; - } - if (!poweroftwo) continue; - - int distance = 0; - - if (server->getPolar() != polar) distance += 1; - - distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15; - distance += ((server->getFFTSize() / fftSize) - 1) * 10; - - if (server->getFillCompletion() < 50) distance += 100; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl; -#endif - - if (bestdist == -1 || distance < bestdist) { - bestdist = distance; - best = i; - } - } - } - - if (bestdist >= 0) { - FFTDataServer *server = best->second.first; -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl; -#endif - claimInstance(server, false); - return server; - } - } - - // Nothing found, make a new one - - return getInstance(model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar, - criteria, - fillFromFrame); -} - -FFTDataServer * -FFTDataServer::findServer(QString n) -{ -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::findServer(\"" << n << "\")" << std::endl; -#endif - - if (m_servers.find(n) != m_servers.end()) { - - FFTDataServer *server = m_servers[n].first; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::findServer(\"" << n << "\"): found " << server << std::endl; -#endif - - claimInstance(server, false); - - return server; - } - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::findServer(\"" << n << "\"): not found" << std::endl; -#endif - - return 0; -} - -void -FFTDataServer::claimInstance(FFTDataServer *server) -{ - claimInstance(server, true); -} - -void -FFTDataServer::claimInstance(FFTDataServer *server, bool needLock) -{ - MutexLocker locker(needLock ? &m_serverMapMutex : 0, - "FFTDataServer::claimInstance::m_serverMapMutex"); - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl; -#endif - - for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { - if (i->second.first == server) { - - for (ServerQueue::iterator j = m_releasedServers.begin(); - j != m_releasedServers.end(); ++j) { - - if (*j == server) { -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl; -#endif - m_releasedServers.erase(j); - break; - } - } - - ++i->second.second; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl; -#endif - - return; - } - } - - cerr << "ERROR: FFTDataServer::claimInstance: instance " - << server << " unknown!" << endl; -} - -void -FFTDataServer::releaseInstance(FFTDataServer *server) -{ - releaseInstance(server, true); -} - -void -FFTDataServer::releaseInstance(FFTDataServer *server, bool needLock) -{ - MutexLocker locker(needLock ? &m_serverMapMutex : 0, - "FFTDataServer::releaseInstance::m_serverMapMutex"); - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl; -#endif - - // -- if ref count > 0, decrement and return - // -- if the instance hasn't been used at all, delete it immediately - // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts, - // leave them hanging around - // -- if N instances with zero refcounts remain, delete the one that - // was last released first - // -- if we run out of disk space when allocating an instance, go back - // and delete the spare N instances before trying again - // -- have an additional method to indicate that a model has been - // destroyed, so that we can delete all of its fft server instances - - for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { - if (i->second.first == server) { - if (i->second.second == 0) { - cerr << "ERROR: FFTDataServer::releaseInstance(" - << server << "): instance not allocated" << endl; - } else if (--i->second.second == 0) { -/*!!! - if (server->m_lastUsedCache == -1) { // never used -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::releaseInstance: instance " - << server << " has never been used, erasing" - << std::endl; -#endif - delete server; - m_servers.erase(i); - } else { -*/ -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::releaseInstance: instance " - << server << " no longer in use, marking for possible collection" - << std::endl; -#endif - bool found = false; - for (ServerQueue::iterator j = m_releasedServers.begin(); - j != m_releasedServers.end(); ++j) { - if (*j == server) { - cerr << "ERROR: FFTDataServer::releaseInstance(" - << server << "): server is already in " - << "released servers list" << endl; - found = true; - } - } - if (!found) m_releasedServers.push_back(server); - server->suspend(); - purgeLimbo(); -//!!! } - } else { -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::releaseInstance: instance " - << server << " now has refcount " << i->second.second - << std::endl; -#endif - } - return; - } - } - - cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): " - << "instance not found" << endl; -} - -void -FFTDataServer::purgeLimbo(int maxSize) -{ -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " - << m_releasedServers.size() << " candidates" << std::endl; -#endif - - while (int(m_releasedServers.size()) > maxSize) { - - FFTDataServer *server = *m_releasedServers.begin(); - - bool found = false; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::purgeLimbo: considering candidate " - << server << std::endl; -#endif - - for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { - - if (i->second.first == server) { - found = true; - if (i->second.second > 0) { - cerr << "ERROR: FFTDataServer::purgeLimbo: Server " - << server << " is in released queue, but still has non-zero refcount " - << i->second.second << endl; - // ... so don't delete it - break; - } -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it" - << std::endl; -#endif - - m_servers.erase(i); - delete server; - break; - } - } - - if (!found) { - cerr << "ERROR: FFTDataServer::purgeLimbo: Server " - << server << " is in released queue, but not in server map!" - << endl; - delete server; - } - - m_releasedServers.pop_front(); - } - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " - << m_releasedServers.size() << " remain" << std::endl; -#endif - -} - -void -FFTDataServer::modelAboutToBeDeleted(Model *model) -{ - MutexLocker locker(&m_serverMapMutex, - "FFTDataServer::modelAboutToBeDeleted::m_serverMapMutex"); - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")" - << std::endl; -#endif - - for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { - - FFTDataServer *server = i->second.first; - - if (server->getModel() == model) { - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is " - << server << std::endl; -#endif - - if (i->second.second > 0) { - cerr << "WARNING: FFTDataServer::modelAboutToBeDeleted: Model " << model << " (\"" << model->objectName() << "\") is about to be deleted, but is still being referred to by FFT server " << server << " with non-zero refcount " << i->second.second << endl; - server->suspendWrites(); - return; - } - for (ServerQueue::iterator j = m_releasedServers.begin(); - j != m_releasedServers.end(); ++j) { - if (*j == server) { -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl; -#endif - m_releasedServers.erase(j); - break; - } - } -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl; -#endif - m_servers.erase(i); - delete server; - return; - } - } -} - -FFTDataServer::FFTDataServer(QString fileBaseName, - const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame) : - m_fileBaseName(fileBaseName), - m_model(model), - m_channel(channel), - m_windower(windowType, windowSize), - m_windowSize(windowSize), - m_windowIncrement(windowIncrement), - m_fftSize(fftSize), - m_polar(polar), - m_width(0), - m_height(0), - m_cacheWidth(0), - m_cacheWidthPower(0), - m_cacheWidthMask(0), - m_criteria(criteria), - m_fftInput(0), - m_exiting(false), - m_suspended(true), //!!! or false? - m_fillThread(0) -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << endl; -#endif - - //!!! end is not correct until model finished reading -- what to do??? - - sv_frame_t start = m_model->getStartFrame(); - sv_frame_t end = m_model->getEndFrame(); - - m_width = int((end - start) / m_windowIncrement) + 1; - m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2 - -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << "): dimensions are " - << m_width << "x" << m_height << endl; -#endif - - int maxCacheSize = 20 * 1024 * 1024; - int columnSize = int(m_height * sizeof(fftsample) * 2 + sizeof(fftsample)); - if (m_width < ((maxCacheSize * 2) / columnSize)) m_cacheWidth = m_width; - else m_cacheWidth = maxCacheSize / columnSize; - -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << "): cache width nominal " - << m_cacheWidth << ", actual "; -#endif - - int bits = 0; - while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; } - m_cacheWidthPower = bits + 1; - m_cacheWidth = 2; - while (bits) { m_cacheWidth <<= 1; --bits; } - m_cacheWidthMask = m_cacheWidth - 1; - -#ifdef DEBUG_FFT_SERVER - cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask " - << m_cacheWidthMask << ")" << endl; -#endif - - if (m_criteria == StorageAdviser::NoCriteria) { - - // assume "spectrogram" criteria for polar ffts, and "feature - // extraction" criteria for rectangular ones. - - if (m_polar) { - m_criteria = StorageAdviser::Criteria - (StorageAdviser::SpeedCritical | - StorageAdviser::LongRetentionLikely); - } else { - m_criteria = StorageAdviser::Criteria - (StorageAdviser::PrecisionCritical); - } - } - - for (int i = 0; i <= m_width / m_cacheWidth; ++i) { - m_caches.push_back(0); - } - - m_fftInput = (fftsample *) - fftf_malloc(fftSize * sizeof(fftsample)); - - m_fftOutput = (fftf_complex *) - fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex)); - - m_workbuffer = (float *) - fftf_malloc((fftSize+2) * sizeof(float)); - - m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize, - m_fftInput, - m_fftOutput, - FFTW_MEASURE); - - if (!m_fftPlan) { - cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << endl; - throw(0); - } - - m_fillThread = new FillThread(*this, fillFromFrame); -} - -FFTDataServer::~FFTDataServer() -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << endl; -#endif - - m_suspended = false; - m_exiting = true; - m_condition.wakeAll(); - if (m_fillThread) { - m_fillThread->wait(); - delete m_fillThread; - } - -// MutexLocker locker(&m_writeMutex, -// "FFTDataServer::~FFTDataServer::m_writeMutex"); - - QMutexLocker mlocker(&m_fftBuffersLock); - QWriteLocker wlocker(&m_cacheVectorLock); - - for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { - if (*i) { - delete *i; - } - } - - deleteProcessingData(); -} - -void -FFTDataServer::deleteProcessingData() -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << endl; -#endif - if (m_fftInput) { - fftf_destroy_plan(m_fftPlan); - fftf_free(m_fftInput); - fftf_free(m_fftOutput); - fftf_free(m_workbuffer); - } - m_fftInput = 0; -} - -void -FFTDataServer::suspend() -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << endl; -#endif - Profiler profiler("FFTDataServer::suspend", false); - - QMutexLocker locker(&m_fftBuffersLock); - m_suspended = true; -} - -void -FFTDataServer::suspendWrites() -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << endl; -#endif - Profiler profiler("FFTDataServer::suspendWrites", false); - - m_suspended = true; -} - -void -FFTDataServer::resume() -{ -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << endl; -#endif - Profiler profiler("FFTDataServer::resume", false); - - m_suspended = false; - if (m_fillThread) { - if (m_fillThread->isFinished()) { - delete m_fillThread; - m_fillThread = 0; - deleteProcessingData(); - } else if (!m_fillThread->isRunning()) { - m_fillThread->start(); - } else { - m_condition.wakeAll(); - } - } -} - -void -FFTDataServer::getStorageAdvice(int w, int h, - bool &memoryCache, bool &compactCache) -{ - if (w < 0 || h < 0) throw std::domain_error("width & height must be non-negative"); - size_t cells = size_t(w) * h; - size_t minimumSize = (cells / 1024) * sizeof(uint16_t); // kb - size_t maximumSize = (cells / 1024) * sizeof(float); // kb - - // We don't have a compact rectangular representation, and compact - // of course is never precision-critical - - bool canCompact = true; - if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) { - canCompact = false; - minimumSize = maximumSize; // don't use compact - } - - StorageAdviser::Recommendation recommendation; - - try { - - recommendation = - StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); - - } catch (InsufficientDiscSpace s) { - - // Delete any unused servers we may have been leaving around - // in case we wanted them again - - purgeLimbo(0); - - // This time we don't catch InsufficientDiscSpace -- we - // haven't allocated anything yet and can safely let the - // exception out to indicate to the caller that we can't - // handle it. - - recommendation = - StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); - } - -// cerr << "Recommendation was: " << recommendation << endl; - - memoryCache = false; - - if ((recommendation & StorageAdviser::UseMemory) || - (recommendation & StorageAdviser::PreferMemory)) { - memoryCache = true; - } - - compactCache = canCompact && - (recommendation & StorageAdviser::ConserveSpace); - -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << endl; - - cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << endl; -#endif -} - -bool -FFTDataServer::makeCache(int c) -{ - // Creating the cache could take a significant amount of time. We - // don't want to block readers on m_cacheVectorLock while this is - // happening, but we do want to block any further calls to - // makeCache. So we use this lock solely to serialise this - // particular function -- it isn't used anywhere else. - - QMutexLocker locker(&m_cacheCreationMutex); - - m_cacheVectorLock.lockForRead(); - if (m_caches[c]) { - // someone else must have created the cache between our - // testing for it and taking the mutex - m_cacheVectorLock.unlock(); - return true; - } - m_cacheVectorLock.unlock(); - - // Now m_cacheCreationMutex is held, but m_cacheVectorLock is not - // -- readers can proceed, but callers to this function will block - - CacheBlock *cb = new CacheBlock; - - QString name = QString("%1-%2").arg(m_fileBaseName).arg(c); - - int width = m_cacheWidth; - if (c * m_cacheWidth + width > m_width) { - width = m_width - c * m_cacheWidth; - } - - bool memoryCache = false; - bool compactCache = false; - - getStorageAdvice(width, m_height, memoryCache, compactCache); - - bool success = false; - - if (memoryCache) { - - try { - - cb->memoryCache = new FFTMemoryCache - (compactCache ? FFTCache::Compact : - m_polar ? FFTCache::Polar : - FFTCache::Rectangular, - width, m_height); - - success = true; - - } catch (std::bad_alloc) { - - delete cb->memoryCache; - cb->memoryCache = 0; - - cerr << "WARNING: Memory allocation failed when creating" - << " FFT memory cache no. " << c << " of " << width - << "x" << m_height << " (of total width " << m_width - << "): falling back to disc cache" << endl; - - memoryCache = false; - } - } - - if (!memoryCache) { - - try { - - cb->fileCacheWriter = new FFTFileCacheWriter - (name, - compactCache ? FFTCache::Compact : - m_polar ? FFTCache::Polar : - FFTCache::Rectangular, - width, m_height); - - success = true; - - } catch (std::exception &e) { - - delete cb->fileCacheWriter; - cb->fileCacheWriter = 0; - - cerr << "ERROR: Failed to construct disc cache for FFT data: " - << e.what() << endl; - - throw; - } - } - - m_cacheVectorLock.lockForWrite(); - - m_caches[c] = cb; - - m_cacheVectorLock.unlock(); - - return success; -} - -bool -FFTDataServer::makeCacheReader(int c) -{ - // preconditions: m_caches[c] exists and contains a file writer; - // m_cacheVectorLock is not locked by this thread -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::makeCacheReader(" << c << ")" << std::endl; -#endif - - QThread *me = QThread::currentThread(); - QWriteLocker locker(&m_cacheVectorLock); - CacheBlock *cb(m_caches.at(c)); - if (!cb || !cb->fileCacheWriter) return false; - - try { - - cb->fileCacheReader[me] = new FFTFileCacheReader(cb->fileCacheWriter); - - } catch (std::exception &e) { - - delete cb->fileCacheReader[me]; - cb->fileCacheReader.erase(me); - - cerr << "ERROR: Failed to construct disc cache reader for FFT data: " - << e.what() << endl; - return false; - } - - // erase a reader that looks like it may no longer going to be - // used by this thread for a while (leaving alone the current - // and previous cache readers) - int deleteCandidate = c - 2; - if (deleteCandidate < 0) deleteCandidate = c + 2; - if (deleteCandidate >= (int)m_caches.size()) { - return true; - } - - cb = m_caches.at(deleteCandidate); - if (cb && cb->fileCacheReader.find(me) != cb->fileCacheReader.end()) { -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::makeCacheReader: Deleting probably unpopular reader " << deleteCandidate << " for this thread (as I create reader " << c << ")" << std::endl; -#endif - delete cb->fileCacheReader[me]; - cb->fileCacheReader.erase(me); - } - - return true; -} - -float -FFTDataServer::getMagnitudeAt(int x, int y) -{ - Profiler profiler("FFTDataServer::getMagnitudeAt", false); - - if (x >= m_width || y >= m_height) return 0; - - float val = 0; - - try { - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return 0; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getMagnitudeAt: filling"); -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn(" - << x << ")" << std::endl; -#endif - fillColumn(x); - } - - val = cache->getMagnitudeAt(col, y); - - } catch (std::exception &e) { - m_error = e.what(); - } - - return val; -} - -bool -FFTDataServer::getMagnitudesAt(int x, float *values, int minbin, int count, int step) -{ - Profiler profiler("FFTDataServer::getMagnitudesAt", false); - - if (x >= m_width) return false; - - if (minbin >= m_height) minbin = m_height - 1; - if (count == 0) count = (m_height - minbin) / step; - else if (minbin + count * step > m_height) { - count = (m_height - minbin) / step; - } - - try { - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return false; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getMagnitudesAt: filling"); - fillColumn(x); - } - - cache->getMagnitudesAt(col, values, minbin, count, step); - - } catch (std::exception &e) { - m_error = e.what(); - return false; - } - - return true; -} - -float -FFTDataServer::getNormalizedMagnitudeAt(int x, int y) -{ - Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false); - - if (x >= m_width || y >= m_height) return 0; - - float val = 0; - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return 0; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling"); - fillColumn(x); - } - val = cache->getNormalizedMagnitudeAt(col, y); - - } catch (std::exception &e) { - m_error = e.what(); - } - - return val; -} - -bool -FFTDataServer::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count, int step) -{ - Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false); - - if (x >= m_width) return false; - - if (minbin >= m_height) minbin = m_height - 1; - if (count == 0) count = (m_height - minbin) / step; - else if (minbin + count * step > m_height) { - count = (m_height - minbin) / step; - } - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return false; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling"); - fillColumn(x); - } - - for (int i = 0; i < count; ++i) { - values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin); - } - - } catch (std::exception &e) { - m_error = e.what(); - return false; - } - - return true; -} - -float -FFTDataServer::getMaximumMagnitudeAt(int x) -{ - Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false); - - if (x >= m_width) return 0; - - float val = 0; - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return 0; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling"); - fillColumn(x); - } - val = cache->getMaximumMagnitudeAt(col); - - } catch (std::exception &e) { - m_error = e.what(); - } - - return val; -} - -float -FFTDataServer::getPhaseAt(int x, int y) -{ - Profiler profiler("FFTDataServer::getPhaseAt", false); - - if (x >= m_width || y >= m_height) return 0; - - float val = 0; - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return 0; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getPhaseAt: filling"); - fillColumn(x); - } - val = cache->getPhaseAt(col, y); - - } catch (std::exception &e) { - m_error = e.what(); - } - - return val; -} - -bool -FFTDataServer::getPhasesAt(int x, float *values, int minbin, int count, int step) -{ - Profiler profiler("FFTDataServer::getPhasesAt", false); - - if (x >= m_width) return false; - - if (minbin >= m_height) minbin = m_height - 1; - if (count == 0) count = (m_height - minbin) / step; - else if (minbin + count * step > m_height) { - count = (m_height - minbin) / step; - } - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return false; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getPhasesAt: filling"); - fillColumn(x); - } - - for (int i = 0; i < count; ++i) { - values[i] = cache->getPhaseAt(col, i * step + minbin); - } - - } catch (std::exception &e) { - m_error = e.what(); - return false; - } - - return true; -} - -void -FFTDataServer::getValuesAt(int x, int y, float &real, float &imaginary) -{ - Profiler profiler("FFTDataServer::getValuesAt", false); - - if (x >= m_width || y >= m_height) { - real = 0; - imaginary = 0; - return; - } - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - - if (!cache) { - real = 0; - imaginary = 0; - return; - } - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") { - real = 0; - imaginary = 0; - return; - } - Profiler profiler("FFTDataServer::getValuesAt: filling"); -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl; -#endif - fillColumn(x); - } - - cache->getValuesAt(col, y, real, imaginary); - - } catch (std::exception &e) { - m_error = e.what(); - } -} - -bool -FFTDataServer::getValuesAt(int x, float *reals, float *imaginaries, int minbin, int count, int step) -{ - Profiler profiler("FFTDataServer::getValuesAt", false); - - if (x >= m_width) return false; - - if (minbin >= m_height) minbin = m_height - 1; - if (count == 0) count = (m_height - minbin) / step; - else if (minbin + count * step > m_height) { - count = (m_height - minbin) / step; - } - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return false; - - if (!cache->haveSetColumnAt(col)) { - if (getError() != "") return false; - Profiler profiler("FFTDataServer::getValuesAt: filling"); - fillColumn(x); - } - - for (int i = 0; i < count; ++i) { - cache->getValuesAt(col, i * step + minbin, reals[i], imaginaries[i]); - } - - } catch (std::exception &e) { - m_error = e.what(); - return false; - } - - return true; -} - -bool -FFTDataServer::isColumnReady(int x) -{ - Profiler profiler("FFTDataServer::isColumnReady", false); - - if (x >= m_width) return true; - - if (!haveCache(x)) { -/*!!! - if (m_lastUsedCache == -1) { - if (m_suspended) { - std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl; - resume(); - } - m_fillThread->start(); - } -*/ - return false; - } - - try { - - int col; - FFTCacheReader *cache = getCacheReader(x, col); - if (!cache) return true; - - return cache->haveSetColumnAt(col); - - } catch (std::exception &e) { - m_error = e.what(); - return false; - } -} - -void -FFTDataServer::fillColumn(int x) -{ - Profiler profiler("FFTDataServer::fillColumn", false); - - if (!m_model->isReady()) { - cerr << "WARNING: FFTDataServer::fillColumn(" - << x << "): model not yet ready" << endl; - return; - } -/* - if (!m_fftInput) { - cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " - << "input has already been completed and discarded?" - << endl; - return; - } -*/ - if (x >= m_width) { - cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " - << "x > width (" << x << " > " << m_width << ")" - << endl; - return; - } - - int col; -#ifdef DEBUG_FFT_SERVER_FILL - cout << "FFTDataServer::fillColumn(" << x << ")" << endl; -#endif - FFTCacheWriter *cache = getCacheWriter(x, col); - if (!cache) return; - - int winsize = m_windowSize; - int fftsize = m_fftSize; - int hs = fftsize/2; - - sv_frame_t pfx = 0; - int off = (fftsize - winsize) / 2; - - sv_frame_t startFrame = m_windowIncrement * sv_frame_t(x); - sv_frame_t endFrame = startFrame + m_windowSize; - - // FFT windows are centred at the respective audio sample frame, - // so the first one is centred at 0 - startFrame -= winsize / 2; - endFrame -= winsize / 2; - -#ifdef DEBUG_FFT_SERVER_FILL - std::cerr << "FFTDataServer::fillColumn: requesting frames " - << startFrame + pfx << " -> " << endFrame << " ( = " - << endFrame - (startFrame + pfx) << ") at index " - << off + pfx << " in buffer of size " << m_fftSize - << " with window size " << m_windowSize - << " from channel " << m_channel << std::endl; -#endif - - QMutexLocker locker(&m_fftBuffersLock); - - // We may have been called from a function that wanted to obtain a - // column using an FFTCacheReader. Before calling us, it checked - // whether the column was available already, and the reader - // reported that it wasn't. Now we test again, with the mutex - // held, to avoid a race condition in case another thread has - // called fillColumn at the same time. - if (cache->haveSetColumnAt(x & m_cacheWidthMask)) { - return; - } - - if (!m_fftInput) { - cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " - << "input has already been completed and discarded?" - << endl; - return; - } - - for (int i = 0; i < off; ++i) { - m_fftInput[i] = 0.0; - } - - for (int i = 0; i < off; ++i) { - m_fftInput[fftsize - i - 1] = 0.0; - } - - if (startFrame < 0) { - pfx = -startFrame; - for (int i = 0; i < pfx; ++i) { - m_fftInput[off + i] = 0.0; - } - } - - sv_frame_t count = 0; - if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx); - - sv_frame_t got = m_model->getData(m_channel, startFrame + pfx, - count, m_fftInput + off + pfx); - - while (got + pfx < winsize) { - m_fftInput[off + got + pfx] = 0.0; - ++got; - } - - if (m_channel == -1) { - int channels = m_model->getChannelCount(); - if (channels > 1) { - for (int i = 0; i < winsize; ++i) { - m_fftInput[off + i] /= float(channels); - } - } - } - - m_windower.cut(m_fftInput + off); - - for (int i = 0; i < hs; ++i) { - fftsample temp = m_fftInput[i]; - m_fftInput[i] = m_fftInput[i + hs]; - m_fftInput[i + hs] = temp; - } - - fftf_execute(m_fftPlan); - - float factor = 0.f; - - if (cache->getStorageType() == FFTCache::Compact || - cache->getStorageType() == FFTCache::Polar) { - - for (int i = 0; i <= hs; ++i) { - fftsample real = m_fftOutput[i][0]; - fftsample imag = m_fftOutput[i][1]; - float mag = sqrtf(real * real + imag * imag); - m_workbuffer[i] = mag; - m_workbuffer[i + hs + 1] = atan2f(imag, real); - if (mag > factor) factor = mag; - } - - } else { - - for (int i = 0; i <= hs; ++i) { - m_workbuffer[i] = m_fftOutput[i][0]; - m_workbuffer[i + hs + 1] = m_fftOutput[i][1]; - } - } - - Profiler subprof("FFTDataServer::fillColumn: set to cache"); - - if (cache->getStorageType() == FFTCache::Compact || - cache->getStorageType() == FFTCache::Polar) { - - cache->setColumnAt(col, - m_workbuffer, - m_workbuffer + hs + 1, - factor); - - } else { - - cache->setColumnAt(col, - m_workbuffer, - m_workbuffer + hs + 1); - } - - if (m_suspended) { -// std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl; -// resume(); - } -} - -void -FFTDataServer::fillComplete() -{ - for (int i = 0; i < int(m_caches.size()); ++i) { - if (!m_caches[i]) continue; - if (m_caches[i]->memoryCache) { - m_caches[i]->memoryCache->allColumnsWritten(); - } - if (m_caches[i]->fileCacheWriter) { - m_caches[i]->fileCacheWriter->allColumnsWritten(); - } - } -} - -QString -FFTDataServer::getError() const -{ - QString err; - if (m_error != "") { - err = m_error; -// cerr << "FFTDataServer::getError: err (server " << this << ") = " << err << endl; - } else { - MutexLocker locker(&m_fftBuffersLock, "FFTDataServer::getError"); - if (m_fillThread) { - err = m_fillThread->getError(); -// cerr << "FFTDataServer::getError: err (server " << this << ", from thread " << m_fillThread -// << ") = " << err << endl; - } - } - return err; -} - -int -FFTDataServer::getFillCompletion() const -{ - if (m_fillThread) return m_fillThread->getCompletion(); - else return 100; -} - -sv_frame_t -FFTDataServer::getFillExtent() const -{ - if (m_fillThread) return m_fillThread->getExtent(); - else return m_model->getEndFrame(); -} - -QString -FFTDataServer::generateFileBasename() const -{ - return generateFileBasename(m_model, m_channel, m_windower.getType(), - m_windowSize, m_windowIncrement, m_fftSize, - m_polar); -} - -QString -FFTDataServer::generateFileBasename(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar) -{ - return QString("%1-%2-%3-%4-%5-%6%7") - .arg(XmlExportable::getObjectExportId(model)) - .arg(channel + 1) - .arg((int)windowType) - .arg(windowSize) - .arg(windowIncrement) - .arg(fftSize) - .arg(polar ? "-p" : "-r"); -} - -void -FFTDataServer::FillThread::run() -{ -#ifdef DEBUG_FFT_SERVER_FILL - std::cerr << "FFTDataServer::FillThread::run()" << std::endl; -#endif - - m_extent = 0; - m_completion = 0; - - while (!m_server.m_model->isReady() && !m_server.m_exiting) { -#ifdef DEBUG_FFT_SERVER_FILL - std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl; -#endif - sleep(1); - } - if (m_server.m_exiting) return; - - sv_frame_t start = m_server.m_model->getStartFrame(); - sv_frame_t end = m_server.m_model->getEndFrame(); - sv_frame_t remainingEnd = end; - - int counter = 0; - int updateAt = 1; - int maxUpdateAt = int(end / m_server.m_windowIncrement) / 20; - if (maxUpdateAt < 100) maxUpdateAt = 100; - - if (m_fillFrom > start) { - - for (sv_frame_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) { - - try { - m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); - } catch (std::exception &e) { - MutexLocker locker(&m_server.m_fftBuffersLock, - "FFTDataServer::run::m_fftBuffersLock [err]"); - m_threadError = e.what(); - std::cerr << "FFTDataServer::FillThread::run: exception: " << m_threadError << " (thread = " << this << " from server " << &m_server << ")" << std::endl; - m_server.fillComplete(); - m_completion = 100; - m_extent = end; - return; - } - - if (m_server.m_exiting) return; - - while (m_server.m_suspended) { -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl; -#endif - MutexLocker locker(&m_server.m_fftBuffersLock, - "FFTDataServer::run::m_fftBuffersLock [1]"); - if (m_server.m_suspended && !m_server.m_exiting) { - m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000); - } -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << endl; -#endif - if (m_server.m_exiting) return; - } - - if (++counter == updateAt) { - m_extent = f; - m_completion = int(100 * fabsf(float(f - m_fillFrom) / - float(end - start))); - counter = 0; - if (updateAt < maxUpdateAt) { - updateAt *= 2; - if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; - } - } - } - - remainingEnd = m_fillFrom; - if (remainingEnd > start) --remainingEnd; - else remainingEnd = start; - } - - int baseCompletion = m_completion; - - for (sv_frame_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) { - - try { - m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); - } catch (std::exception &e) { - MutexLocker locker(&m_server.m_fftBuffersLock, - "FFTDataServer::run::m_fftBuffersLock [err]"); - m_threadError = e.what(); - std::cerr << "FFTDataServer::FillThread::run: exception: " << m_threadError << " (thread = " << this << " from server " << &m_server << ")" << std::endl; - m_server.fillComplete(); - m_completion = 100; - m_extent = end; - return; - } - - if (m_server.m_exiting) return; - - while (m_server.m_suspended) { -#ifdef DEBUG_FFT_SERVER - cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl; -#endif - { - MutexLocker locker(&m_server.m_fftBuffersLock, - "FFTDataServer::run::m_fftBuffersLock [2]"); - if (m_server.m_suspended && !m_server.m_exiting) { - m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000); - } - } - if (m_server.m_exiting) return; - } - - if (++counter == updateAt) { - m_extent = f; - m_completion = baseCompletion + - int(100 * fabsf(float(f - start) / - float(end - start))); - counter = 0; - if (updateAt < maxUpdateAt) { - updateAt *= 2; - if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; - } - } - } - - m_server.fillComplete(); - m_completion = 100; - m_extent = end; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "FFTDataServer::FillThread::run exiting" << std::endl; -#endif -} - diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTDataServer.h --- a/data/fft/FFTDataServer.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_DATA_SERVER_H_ -#define _FFT_DATA_SERVER_H_ - -#include "base/Window.h" -#include "base/Thread.h" -#include "base/StorageAdviser.h" - -#include "FFTapi.h" -#include "FFTFileCacheReader.h" -#include "FFTFileCacheWriter.h" -#include "FFTMemoryCache.h" - -#include -#include -#include -#include -#include - -#include -#include - -class DenseTimeValueModel; -class Model; - -class FFTDataServer -{ -public: - static FFTDataServer *getInstance(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria = - StorageAdviser::NoCriteria, - sv_frame_t fillFromFrame = 0); - - static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria = - StorageAdviser::NoCriteria, - sv_frame_t fillFromFrame = 0); - - static void claimInstance(FFTDataServer *); - static void releaseInstance(FFTDataServer *); - - static void modelAboutToBeDeleted(Model *); - - const DenseTimeValueModel *getModel() const { return m_model; } - int getChannel() const { return m_channel; } - WindowType getWindowType() const { return m_windower.getType(); } - int getWindowSize() const { return m_windowSize; } - int getWindowIncrement() const { return m_windowIncrement; } - int getFFTSize() const { return m_fftSize; } - bool getPolar() const { return m_polar; } - - int getWidth() const { return m_width; } - int getHeight() const { return m_height; } - - float getMagnitudeAt(int x, int y); - float getNormalizedMagnitudeAt(int x, int y); - float getMaximumMagnitudeAt(int x); - float getPhaseAt(int x, int y); - void getValuesAt(int x, int y, float &real, float &imaginary); - bool isColumnReady(int x); - - bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); - bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); - bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1); - bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0, int step = 1); - - void suspend(); - void suspendWrites(); - void resume(); // also happens automatically if new data needed - - // Convenience functions: - - bool isLocalPeak(int x, int y) { - float mag = getMagnitudeAt(x, y); - if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false; - if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false; - return true; - } - bool isOverThreshold(int x, int y, float threshold) { - return getMagnitudeAt(x, y) > threshold; - } - - QString getError() const; - int getFillCompletion() const; - sv_frame_t getFillExtent() const; - -private: - FFTDataServer(QString fileBaseName, - const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame = 0); - - virtual ~FFTDataServer(); - - FFTDataServer(const FFTDataServer &); // not implemented - FFTDataServer &operator=(const FFTDataServer &); // not implemented - - typedef float fftsample; - - QString m_fileBaseName; - const DenseTimeValueModel *m_model; - int m_channel; - - Window m_windower; - - int m_windowSize; - int m_windowIncrement; - int m_fftSize; - bool m_polar; - - int m_width; - int m_height; - int m_cacheWidth; - int m_cacheWidthPower; - int m_cacheWidthMask; - - struct CacheBlock { - FFTMemoryCache *memoryCache; - typedef std::map ThreadReaderMap; - ThreadReaderMap fileCacheReader; - FFTFileCacheWriter *fileCacheWriter; - CacheBlock() : memoryCache(0), fileCacheWriter(0) { } - ~CacheBlock() { - delete memoryCache; - while (!fileCacheReader.empty()) { - delete fileCacheReader.begin()->second; - fileCacheReader.erase(fileCacheReader.begin()); - } - delete fileCacheWriter; - } - }; - - typedef std::vector CacheVector; - CacheVector m_caches; - QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use - QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls - - FFTCacheReader *getCacheReader(int x, int &col) { - Profiler profiler("FFTDataServer::getCacheReader"); - col = x & m_cacheWidthMask; - int c = x >> m_cacheWidthPower; - m_cacheVectorLock.lockForRead(); - CacheBlock *cb(m_caches.at(c)); - if (cb) { - if (cb->memoryCache) { - m_cacheVectorLock.unlock(); - return cb->memoryCache; - } - if (cb->fileCacheWriter) { - QThread *me = QThread::currentThread(); - CacheBlock::ThreadReaderMap &map = cb->fileCacheReader; - if (map.find(me) == map.end()) { - m_cacheVectorLock.unlock(); - if (!makeCacheReader(c)) return 0; - return getCacheReader(x, col); - } - FFTCacheReader *reader = cb->fileCacheReader[me]; - m_cacheVectorLock.unlock(); - return reader; - } - // if cb exists but cb->fileCacheWriter doesn't, creation - // must have failed: don't try again - m_cacheVectorLock.unlock(); - return 0; - } - m_cacheVectorLock.unlock(); - if (getError() != "") return 0; - if (!makeCache(c)) return 0; - return getCacheReader(x, col); - } - - FFTCacheWriter *getCacheWriter(int x, int &col) { - Profiler profiler("FFTDataServer::getCacheWriter"); - col = x & m_cacheWidthMask; - int c = x >> m_cacheWidthPower; - { - QReadLocker locker(&m_cacheVectorLock); - CacheBlock *cb(m_caches.at(c)); - if (cb) { - if (cb->memoryCache) return cb->memoryCache; - if (cb->fileCacheWriter) return cb->fileCacheWriter; - // if cb exists, creation must have failed: don't try again - return 0; - } - } - if (!makeCache(c)) return 0; - return getCacheWriter(x, col); - } - - bool haveCache(int x) { - int c = x >> m_cacheWidthPower; - return (m_caches.at(c) != 0); - } - - bool makeCache(int c); - bool makeCacheReader(int c); - - StorageAdviser::Criteria m_criteria; - - void getStorageAdvice(int w, int h, bool &memory, bool &compact); - - mutable QMutex m_fftBuffersLock; - QWaitCondition m_condition; - - fftsample *m_fftInput; - fftf_complex *m_fftOutput; - float *m_workbuffer; - fftf_plan m_fftPlan; - - class FillThread : public Thread - { - public: - FillThread(FFTDataServer &server, sv_frame_t fillFromFrame) : - m_server(server), m_extent(0), m_completion(0), - m_fillFrom(fillFromFrame) { } - - sv_frame_t getExtent() const { return m_extent; } - int getCompletion() const { return m_completion ? m_completion : 1; } - QString getError() const { return m_threadError; } - virtual void run(); - - protected: - FFTDataServer &m_server; - sv_frame_t m_extent; - int m_completion; - sv_frame_t m_fillFrom; - QString m_threadError; - }; - - bool m_exiting; - bool m_suspended; - FillThread *m_fillThread; - QString m_error; - - void deleteProcessingData(); - void fillColumn(int x); - void fillComplete(); - - QString generateFileBasename() const; - static QString generateFileBasename(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar); - - typedef std::pair ServerCountPair; - typedef std::map ServerMap; - typedef std::deque ServerQueue; - - static ServerMap m_servers; - static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount - static QMutex m_serverMapMutex; - static FFTDataServer *findServer(QString); // call with serverMapMutex held - static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held - - static void claimInstance(FFTDataServer *, bool needLock); - static void releaseInstance(FFTDataServer *, bool needLock); - -}; - -#endif diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTFileCacheReader.cpp --- a/data/fft/FFTFileCacheReader.cpp Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,281 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "FFTFileCacheReader.h" -#include "FFTFileCacheWriter.h" - -#include "fileio/MatrixFile.h" - -#include "base/Profiler.h" -#include "base/Thread.h" -#include "base/Exceptions.h" - -#include - -//#define DEBUG_FFT_FILE_CACHE_READER 1 - -// The underlying matrix has height (m_height * 2 + 1). In each -// column we store magnitude at [0], [2] etc and phase at [1], [3] -// etc, and then store the normalization factor (maximum magnitude) at -// [m_height * 2]. In compact mode, the factor takes two cells. - -FFTFileCacheReader::FFTFileCacheReader(FFTFileCacheWriter *writer) : - m_readbuf(0), - m_readbufCol(0), - m_readbufWidth(0), - m_readbufGood(false), - m_storageType(writer->getStorageType()), - m_factorSize(m_storageType == FFTCache::Compact ? 2 : 1), - m_mfc(new MatrixFile - (writer->getFileBase(), - MatrixFile::ReadOnly, - int((m_storageType == FFTCache::Compact) ? sizeof(uint16_t) : sizeof(float)), - writer->getWidth(), - writer->getHeight() * 2 + m_factorSize)) -{ -#ifdef DEBUG_FFT_FILE_CACHE_READER - cerr << "FFTFileCacheReader: storage type is " << (m_storageType == FFTCache::Compact ? "Compact" : m_storageType == FFTCache::Polar ? "Polar" : "Rectangular") << endl; -#endif -} - -FFTFileCacheReader::~FFTFileCacheReader() -{ - if (m_readbuf) delete[] m_readbuf; - delete m_mfc; -} - -int -FFTFileCacheReader::getWidth() const -{ - return m_mfc->getWidth(); -} - -int -FFTFileCacheReader::getHeight() const -{ - int mh = m_mfc->getHeight(); - if (mh > m_factorSize) return (mh - m_factorSize) / 2; - else return 0; -} - -float -FFTFileCacheReader::getMagnitudeAt(int x, int y) const -{ - Profiler profiler("FFTFileCacheReader::getMagnitudeAt", false); - - float value = 0.f; - - switch (m_storageType) { - - case FFTCache::Compact: - value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f) - * getNormalizationFactor(x); - break; - - case FFTCache::Rectangular: - { - float real, imag; - getValuesAt(x, y, real, imag); - value = sqrtf(real * real + imag * imag); - break; - } - - case FFTCache::Polar: - value = getFromReadBufStandard(x, y * 2); - break; - } - - return value; -} - -float -FFTFileCacheReader::getNormalizedMagnitudeAt(int x, int y) const -{ - float value = 0.f; - - switch (m_storageType) { - - case FFTCache::Compact: - value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.f; - break; - - case FFTCache::Rectangular: - case FFTCache::Polar: - { - float mag = getMagnitudeAt(x, y); - float factor = getNormalizationFactor(x); - if (factor != 0) value = mag / factor; - else value = 0.f; - break; - } - } - - return value; -} - -float -FFTFileCacheReader::getMaximumMagnitudeAt(int x) const -{ - return getNormalizationFactor(x); -} - -float -FFTFileCacheReader::getPhaseAt(int x, int y) const -{ - float value = 0.f; - - switch (m_storageType) { - - case FFTCache::Compact: - value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.f) * float(M_PI); - break; - - case FFTCache::Rectangular: - { - float real, imag; - getValuesAt(x, y, real, imag); - value = atan2f(imag, real); - break; - } - - case FFTCache::Polar: - value = getFromReadBufStandard(x, y * 2 + 1); - break; - } - - return value; -} - -void -FFTFileCacheReader::getValuesAt(int x, int y, float &real, float &imag) const -{ -// SVDEBUG << "FFTFileCacheReader::getValuesAt(" << x << "," << y << ")" << endl; - - switch (m_storageType) { - - case FFTCache::Rectangular: - real = getFromReadBufStandard(x, y * 2); - imag = getFromReadBufStandard(x, y * 2 + 1); - return; - - case FFTCache::Compact: - case FFTCache::Polar: - float mag = getMagnitudeAt(x, y); - float phase = getPhaseAt(x, y); - real = mag * cosf(phase); - imag = mag * sinf(phase); - return; - } -} - -void -FFTFileCacheReader::getMagnitudesAt(int x, float *values, int minbin, int count, int step) const -{ - Profiler profiler("FFTFileCacheReader::getMagnitudesAt"); - - switch (m_storageType) { - - case FFTCache::Compact: - for (int i = 0; i < count; ++i) { - int y = minbin + i * step; - values[i] = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f) - * getNormalizationFactor(x); - } - break; - - case FFTCache::Rectangular: - { - float real, imag; - for (int i = 0; i < count; ++i) { - int y = minbin + i * step; - real = getFromReadBufStandard(x, y * 2); - imag = getFromReadBufStandard(x, y * 2 + 1); - values[i] = sqrtf(real * real + imag * imag); - } - break; - } - - case FFTCache::Polar: - for (int i = 0; i < count; ++i) { - int y = minbin + i * step; - values[i] = getFromReadBufStandard(x, y * 2); - } - break; - } -} - -bool -FFTFileCacheReader::haveSetColumnAt(int x) const -{ - if (m_readbuf && m_readbufGood && - (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) { -// SVDEBUG << "FFTFileCacheReader::haveSetColumnAt: short-circuiting; we know about this one" << endl; - return true; - } - return m_mfc->haveSetColumnAt(x); -} - -size_t -FFTFileCacheReader::getCacheSize(int width, int height, - FFTCache::StorageType type) -{ - return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width * - (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) + - 2 * sizeof(int); // matrix file header size -} - -void -FFTFileCacheReader::populateReadBuf(int x) const -{ - Profiler profiler("FFTFileCacheReader::populateReadBuf", false); - -// SVDEBUG << "FFTFileCacheReader::populateReadBuf(" << x << ")" << endl; - - if (!m_readbuf) { - m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()]; - } - - m_readbufGood = false; - - try { - bool good = false; - if (m_mfc->haveSetColumnAt(x)) { - // If the column is not available, we have no obligation - // to do anything with the readbuf -- we can cheerfully - // return garbage. It's the responsibility of the caller - // to check haveSetColumnAt before trusting any retrieved - // data. However, we do record whether the data in the - // readbuf is good or not, because we can use that to - // return an immediate result for haveSetColumnAt if the - // column is right. - good = true; - m_mfc->getColumnAt(x, m_readbuf); - } - if (m_mfc->haveSetColumnAt(x + 1)) { - m_mfc->getColumnAt - (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight()); - m_readbufWidth = 2; - } else { - m_readbufWidth = 1; - } - m_readbufGood = good; - } catch (FileReadFailed f) { - cerr << "ERROR: FFTFileCacheReader::populateReadBuf: File read failed: " - << f.what() << endl; - memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize()); - } - m_readbufCol = x; -} - diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTFileCacheReader.h --- a/data/fft/FFTFileCacheReader.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_FILE_CACHE_READER_H_ -#define _FFT_FILE_CACHE_READER_H_ - -#include "data/fileio/MatrixFile.h" -#include "FFTCacheReader.h" -#include "FFTCacheStorageType.h" - -class FFTFileCacheWriter; - -class FFTFileCacheReader : public FFTCacheReader -{ -public: - FFTFileCacheReader(FFTFileCacheWriter *); - ~FFTFileCacheReader(); - - int getWidth() const; - int getHeight() const; - - float getMagnitudeAt(int x, int y) const; - float getNormalizedMagnitudeAt(int x, int y) const; - float getMaximumMagnitudeAt(int x) const; - float getPhaseAt(int x, int y) const; - - void getValuesAt(int x, int y, float &real, float &imag) const; - void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const; - - bool haveSetColumnAt(int x) const; - - static size_t getCacheSize(int width, int height, - FFTCache::StorageType type); - - FFTCache::StorageType getStorageType() const { return m_storageType; } - -protected: - mutable char *m_readbuf; - mutable int m_readbufCol; - mutable int m_readbufWidth; - mutable bool m_readbufGood; - - float getFromReadBufStandard(int x, int y) const { - float v; - if (m_readbuf && - (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) { - v = ((float *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y]; - return v; - } else { - populateReadBuf(x); - v = getFromReadBufStandard(x, y); - return v; - } - } - - float getFromReadBufCompactUnsigned(int x, int y) const { - float v; - if (m_readbuf && - (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) { - v = ((uint16_t *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y]; - return v; - } else { - populateReadBuf(x); - v = getFromReadBufCompactUnsigned(x, y); - return v; - } - } - - float getFromReadBufCompactSigned(int x, int y) const { - float v; - if (m_readbuf && - (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) { - v = ((int16_t *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y]; - return v; - } else { - populateReadBuf(x); - v = getFromReadBufCompactSigned(x, y); - return v; - } - } - - void populateReadBuf(int x) const; - - float getNormalizationFactor(int col) const { - int h = m_mfc->getHeight(); - if (h < m_factorSize) return 0; - if (m_storageType != FFTCache::Compact) { - return getFromReadBufStandard(col, h - 1); - } else { - union { - float f; - uint16_t u[2]; - } factor; - if (!m_readbuf || - !(m_readbufCol == col || - (m_readbufWidth > 1 && m_readbufCol+1 == col))) { - populateReadBuf(col); - } - int ix = (col - m_readbufCol) * m_mfc->getHeight() + h; - factor.u[0] = ((uint16_t *)m_readbuf)[ix - 2]; - factor.u[1] = ((uint16_t *)m_readbuf)[ix - 1]; - return factor.f; - } - } - - FFTCache::StorageType m_storageType; - int m_factorSize; - MatrixFile *m_mfc; -}; - -#endif diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTFileCacheWriter.cpp --- a/data/fft/FFTFileCacheWriter.cpp Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,195 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "FFTFileCacheWriter.h" - -#include "fileio/MatrixFile.h" - -#include "base/Profiler.h" -#include "base/Thread.h" -#include "base/Exceptions.h" - -#include - -//#define DEBUG_FFT_FILE_CACHE_WRITER 1 - - -// The underlying matrix has height (m_height * 2 + 1). In each -// column we store magnitude at [0], [2] etc and phase at [1], [3] -// etc, and then store the normalization factor (maximum magnitude) at -// [m_height * 2]. In compact mode, the factor takes two cells. - -FFTFileCacheWriter::FFTFileCacheWriter(QString fileBase, - FFTCache::StorageType storageType, - int width, int height) : - m_writebuf(0), - m_fileBase(fileBase), - m_storageType(storageType), - m_factorSize(storageType == FFTCache::Compact ? 2 : 1), - m_mfc(new MatrixFile - (fileBase, MatrixFile::WriteOnly, - int((storageType == FFTCache::Compact) ? sizeof(uint16_t) : sizeof(float)), - width, height * 2 + m_factorSize)) -{ -#ifdef DEBUG_FFT_FILE_CACHE_WRITER - cerr << "FFTFileCacheWriter: storage type is " << (storageType == FFTCache::Compact ? "Compact" : storageType == FFTCache::Polar ? "Polar" : "Rectangular") << ", size " << width << "x" << height << endl; -#endif - m_mfc->setAutoClose(true); - m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()]; -} - -FFTFileCacheWriter::~FFTFileCacheWriter() -{ - if (m_writebuf) delete[] m_writebuf; - delete m_mfc; -} - -QString -FFTFileCacheWriter::getFileBase() const -{ - return m_fileBase; -} - -int -FFTFileCacheWriter::getWidth() const -{ - return m_mfc->getWidth(); -} - -int -FFTFileCacheWriter::getHeight() const -{ - int mh = m_mfc->getHeight(); - if (mh > m_factorSize) return (mh - m_factorSize) / 2; - else return 0; -} - -bool -FFTFileCacheWriter::haveSetColumnAt(int x) const -{ - return m_mfc->haveSetColumnAt(x); -} - -void -FFTFileCacheWriter::setColumnAt(int x, float *mags, float *phases, float factor) -{ - int h = getHeight(); - - switch (m_storageType) { - - case FFTCache::Compact: - for (int y = 0; y < h; ++y) { - ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0); - ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI)); - } - break; - - case FFTCache::Rectangular: - for (int y = 0; y < h; ++y) { - ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]); - ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]); - } - break; - - case FFTCache::Polar: - for (int y = 0; y < h; ++y) { - ((float *)m_writebuf)[y * 2] = mags[y]; - ((float *)m_writebuf)[y * 2 + 1] = phases[y]; - } - break; - } - - static float maxFactor = 0; - if (factor > maxFactor) maxFactor = factor; -#ifdef DEBUG_FFT_FILE_CACHE_WRITER - cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << endl; -#endif - - setNormalizationFactorToWritebuf(factor); - - m_mfc->setColumnAt(x, m_writebuf); -} - -void -FFTFileCacheWriter::setColumnAt(int x, float *real, float *imag) -{ - int h = getHeight(); - - float factor = 0.0f; - - switch (m_storageType) { - - case FFTCache::Compact: - for (int y = 0; y < h; ++y) { - float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); - if (mag > factor) factor = mag; - } - for (int y = 0; y < h; ++y) { - float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); - float phase = atan2f(imag[y], real[y]); - ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0); - ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI)); - } - break; - - case FFTCache::Rectangular: - for (int y = 0; y < h; ++y) { - ((float *)m_writebuf)[y * 2] = real[y]; - ((float *)m_writebuf)[y * 2 + 1] = imag[y]; - float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); - if (mag > factor) factor = mag; - } - break; - - case FFTCache::Polar: - for (int y = 0; y < h; ++y) { - float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]); - if (mag > factor) factor = mag; - ((float *)m_writebuf)[y * 2] = mag; - float phase = atan2f(imag[y], real[y]); - ((float *)m_writebuf)[y * 2 + 1] = phase; - } - break; - } - - static float maxFactor = 0; - if (factor > maxFactor) maxFactor = factor; -#ifdef DEBUG_FFT_FILE_CACHE_WRITER - cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << endl; -#endif - - setNormalizationFactorToWritebuf(factor); - - m_mfc->setColumnAt(x, m_writebuf); -} - -size_t -FFTFileCacheWriter::getCacheSize(int width, int height, - FFTCache::StorageType type) -{ - return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width * - (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) + - 2 * sizeof(int); // matrix file header size -} - -void -FFTFileCacheWriter::allColumnsWritten() -{ -#ifdef DEBUG_FFT_FILE_CACHE_WRITER - SVDEBUG << "FFTFileCacheWriter::allColumnsWritten" << endl; -#endif - m_mfc->close(); -} - diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTFileCacheWriter.h --- a/data/fft/FFTFileCacheWriter.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006-2009 Chris Cannam and QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_FILE_CACHE_WRITER_H_ -#define _FFT_FILE_CACHE_WRITER_H_ - -#include "FFTCacheStorageType.h" -#include "FFTCacheWriter.h" -#include "data/fileio/MatrixFile.h" - -class FFTFileCacheWriter : public FFTCacheWriter -{ -public: - FFTFileCacheWriter(QString fileBase, - FFTCache::StorageType storageType, - int width, int height); - ~FFTFileCacheWriter(); - - int getWidth() const; - int getHeight() const; - - void setColumnAt(int x, float *mags, float *phases, float factor); - void setColumnAt(int x, float *reals, float *imags); - - static size_t getCacheSize(int width, int height, - FFTCache::StorageType type); - - bool haveSetColumnAt(int x) const; - - void allColumnsWritten(); - - QString getFileBase() const; - FFTCache::StorageType getStorageType() const { return m_storageType; } - -protected: - char *m_writebuf; - - void setNormalizationFactorToWritebuf(float newfactor) { - int h = m_mfc->getHeight(); - if (h < m_factorSize) return; - if (m_storageType != FFTCache::Compact) { - ((float *)m_writebuf)[h - 1] = newfactor; - } else { - union { - float f; - uint16_t u[2]; - } factor; - factor.f = newfactor; - ((uint16_t *)m_writebuf)[h - 2] = factor.u[0]; - ((uint16_t *)m_writebuf)[h - 1] = factor.u[1]; - } - } - - QString m_fileBase; - FFTCache::StorageType m_storageType; - int m_factorSize; - MatrixFile *m_mfc; -}; - -#endif diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTMemoryCache.cpp --- a/data/fft/FFTMemoryCache.cpp Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "FFTMemoryCache.h" -#include "system/System.h" - -#include -#include - -//#define DEBUG_FFT_MEMORY_CACHE 1 - -FFTMemoryCache::FFTMemoryCache(FFTCache::StorageType storageType, - int width, int height) : - m_width(width), - m_height(height), - m_magnitude(0), - m_phase(0), - m_fmagnitude(0), - m_fphase(0), - m_freal(0), - m_fimag(0), - m_factor(0), - m_storageType(storageType) -{ -#ifdef DEBUG_FFT_MEMORY_CACHE - cerr << "FFTMemoryCache[" << this << "]::FFTMemoryCache (type " - << m_storageType << "), size " << m_width << "x" << m_height << endl; -#endif - - initialise(); -} - -FFTMemoryCache::~FFTMemoryCache() -{ -#ifdef DEBUG_FFT_MEMORY_CACHE - cerr << "FFTMemoryCache[" << this << "]::~FFTMemoryCache" << endl; -#endif - - for (int i = 0; i < m_width; ++i) { - if (m_magnitude && m_magnitude[i]) free(m_magnitude[i]); - if (m_phase && m_phase[i]) free(m_phase[i]); - if (m_fmagnitude && m_fmagnitude[i]) free(m_fmagnitude[i]); - if (m_fphase && m_fphase[i]) free(m_fphase[i]); - if (m_freal && m_freal[i]) free(m_freal[i]); - if (m_fimag && m_fimag[i]) free(m_fimag[i]); - } - - if (m_magnitude) free(m_magnitude); - if (m_phase) free(m_phase); - if (m_fmagnitude) free(m_fmagnitude); - if (m_fphase) free(m_fphase); - if (m_freal) free(m_freal); - if (m_fimag) free(m_fimag); - if (m_factor) free(m_factor); -} - -void -FFTMemoryCache::initialise() -{ - Profiler profiler("FFTMemoryCache::initialise"); - - int width = m_width, height = m_height; - -#ifdef DEBUG_FFT_MEMORY_CACHE - cerr << "FFTMemoryCache[" << this << "]::initialise(" << width << "x" << height << " = " << width*height << ")" << endl; -#endif - - if (m_storageType == FFTCache::Compact) { - initialise(m_magnitude); - initialise(m_phase); - } else if (m_storageType == FFTCache::Polar) { - initialise(m_fmagnitude); - initialise(m_fphase); - } else { - initialise(m_freal); - initialise(m_fimag); - } - - m_colset.resize(width); - - m_factor = (float *)realloc(m_factor, width * sizeof(float)); - - m_width = width; - m_height = height; - -#ifdef DEBUG_FFT_MEMORY_CACHE - cerr << "done, width = " << m_width << " height = " << m_height << endl; -#endif -} - -void -FFTMemoryCache::initialise(uint16_t **&array) -{ - array = (uint16_t **)malloc(m_width * sizeof(uint16_t *)); - if (!array) throw std::bad_alloc(); - MUNLOCK(array, m_width * sizeof(uint16_t *)); - - for (int i = 0; i < m_width; ++i) { - array[i] = (uint16_t *)malloc(m_height * sizeof(uint16_t)); - if (!array[i]) throw std::bad_alloc(); - MUNLOCK(array[i], m_height * sizeof(uint16_t)); - } -} - -void -FFTMemoryCache::initialise(float **&array) -{ - array = (float **)malloc(m_width * sizeof(float *)); - if (!array) throw std::bad_alloc(); - MUNLOCK(array, m_width * sizeof(float *)); - - for (int i = 0; i < m_width; ++i) { - array[i] = (float *)malloc(m_height * sizeof(float)); - if (!array[i]) throw std::bad_alloc(); - MUNLOCK(array[i], m_height * sizeof(float)); - } -} - -void -FFTMemoryCache::setColumnAt(int x, float *mags, float *phases, float factor) -{ - Profiler profiler("FFTMemoryCache::setColumnAt: from polar"); - - setNormalizationFactor(x, factor); - - if (m_storageType == FFTCache::Rectangular) { - Profiler subprof("FFTMemoryCache::setColumnAt: polar to cart"); - for (int y = 0; y < m_height; ++y) { - m_freal[x][y] = mags[y] * cosf(phases[y]); - m_fimag[x][y] = mags[y] * sinf(phases[y]); - } - } else { - for (int y = 0; y < m_height; ++y) { - setMagnitudeAt(x, y, mags[y]); - setPhaseAt(x, y, phases[y]); - } - } - - m_colsetLock.lockForWrite(); - m_colset.set(x); - m_colsetLock.unlock(); -} - -void -FFTMemoryCache::setColumnAt(int x, float *reals, float *imags) -{ - Profiler profiler("FFTMemoryCache::setColumnAt: from cart"); - - float max = 0.0; - - switch (m_storageType) { - - case FFTCache::Rectangular: - for (int y = 0; y < m_height; ++y) { - m_freal[x][y] = reals[y]; - m_fimag[x][y] = imags[y]; - float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]); - if (mag > max) max = mag; - } - break; - - case FFTCache::Compact: - case FFTCache::Polar: - { - Profiler subprof("FFTMemoryCache::setColumnAt: cart to polar"); - for (int y = 0; y < m_height; ++y) { - float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]); - float phase = atan2f(imags[y], reals[y]); - reals[y] = mag; - imags[y] = phase; - if (mag > max) max = mag; - } - break; - } - }; - - if (m_storageType == FFTCache::Rectangular) { - m_factor[x] = max; - m_colsetLock.lockForWrite(); - m_colset.set(x); - m_colsetLock.unlock(); - } else { - setColumnAt(x, reals, imags, max); - } -} - -size_t -FFTMemoryCache::getCacheSize(int width, int height, FFTCache::StorageType type) -{ - size_t sz = 0; - - switch (type) { - - case FFTCache::Compact: - sz = (height * 2 + 1) * width * sizeof(uint16_t); - break; - - case FFTCache::Polar: - case FFTCache::Rectangular: - sz = (height * 2 + 1) * width * sizeof(float); - break; - } - - return sz; -} - diff -r 655cd4e68e9a -r 420fc961c0c4 data/fft/FFTMemoryCache.h --- a/data/fft/FFTMemoryCache.h Fri Jun 12 13:46:44 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _FFT_MEMORY_CACHE_H_ -#define _FFT_MEMORY_CACHE_H_ - -#include "FFTCacheReader.h" -#include "FFTCacheWriter.h" -#include "FFTCacheStorageType.h" -#include "base/ResizeableBitset.h" -#include "base/Profiler.h" - -#include - -/** - * In-memory FFT cache. For this we want to cache magnitude with - * enough resolution to have gain applied afterwards and determine - * whether something is a peak or not, and also cache phase rather - * than only phase-adjusted frequency so that we don't have to - * recalculate if switching between phase and magnitude displays. At - * the same time, we don't want to take up too much memory. It's not - * expected to be accurate enough to be used as input for DSP or - * resynthesis code. - * - * This implies probably 16 bits for a normalized magnitude and at - * most 16 bits for phase. - * - * Each column's magnitudes are expected to be stored normalized - * to [0,1] with respect to the column, so the normalization - * factor should be calculated before all values in a column, and - * set appropriately. - */ - -class FFTMemoryCache : public FFTCacheReader, public FFTCacheWriter -{ -public: - FFTMemoryCache(FFTCache::StorageType storageType, - int width, int height); - ~FFTMemoryCache(); - - int getWidth() const { return m_width; } - int getHeight() const { return m_height; } - - float getMagnitudeAt(int x, int y) const { - if (m_storageType == FFTCache::Rectangular) { - Profiler profiler("FFTMemoryCache::getMagnitudeAt: cart to polar"); - return sqrtf(m_freal[x][y] * m_freal[x][y] + - m_fimag[x][y] * m_fimag[x][y]); - } else { - return getNormalizedMagnitudeAt(x, y) * m_factor[x]; - } - } - - float getNormalizedMagnitudeAt(int x, int y) const { - if (m_storageType == FFTCache::Rectangular) return getMagnitudeAt(x, y) / m_factor[x]; - else if (m_storageType == FFTCache::Polar) return m_fmagnitude[x][y]; - else return float(m_magnitude[x][y]) / 65535.f; - } - - float getMaximumMagnitudeAt(int x) const { - return m_factor[x]; - } - - float getPhaseAt(int x, int y) const { - if (m_storageType == FFTCache::Rectangular) { - Profiler profiler("FFTMemoryCache::getValuesAt: cart to polar"); - return atan2f(m_fimag[x][y], m_freal[x][y]); - } else if (m_storageType == FFTCache::Polar) { - return m_fphase[x][y]; - } else { - int16_t i = (int16_t)m_phase[x][y]; - return float(i / 32767.0 * M_PI); - } - } - - void getValuesAt(int x, int y, float &real, float &imag) const { - if (m_storageType == FFTCache::Rectangular) { - real = m_freal[x][y]; - imag = m_fimag[x][y]; - } else { - Profiler profiler("FFTMemoryCache::getValuesAt: polar to cart"); - float mag = getMagnitudeAt(x, y); - float phase = getPhaseAt(x, y); - real = mag * cosf(phase); - imag = mag * sinf(phase); - } - } - - void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const - { - if (m_storageType == FFTCache::Rectangular) { - for (int i = 0; i < count; ++i) { - int y = i * step + minbin; - values[i] = sqrtf(m_freal[x][y] * m_freal[x][y] + - m_fimag[x][y] * m_fimag[x][y]); - } - } else if (m_storageType == FFTCache::Polar) { - for (int i = 0; i < count; ++i) { - int y = i * step + minbin; - values[i] = m_fmagnitude[x][y] * m_factor[x]; - } - } else { - for (int i = 0; i < count; ++i) { - int y = i * step + minbin; - values[i] = float(double(m_magnitude[x][y]) * m_factor[x] / 65535.0); - } - } - } - - bool haveSetColumnAt(int x) const { - m_colsetLock.lockForRead(); - bool have = m_colset.get(x); - m_colsetLock.unlock(); - return have; - } - - void setColumnAt(int x, float *mags, float *phases, float factor); - - void setColumnAt(int x, float *reals, float *imags); - - void allColumnsWritten() { } - - static size_t getCacheSize(int width, int height, - FFTCache::StorageType type); - - FFTCache::StorageType getStorageType() const { return m_storageType; } - -private: - int m_width; - int m_height; - uint16_t **m_magnitude; - uint16_t **m_phase; - float **m_fmagnitude; - float **m_fphase; - float **m_freal; - float **m_fimag; - float *m_factor; - FFTCache::StorageType m_storageType; - ResizeableBitset m_colset; - mutable QReadWriteLock m_colsetLock; - - void initialise(); - - void setNormalizationFactor(int x, float factor) { - if (x < m_width) m_factor[x] = factor; - } - - void setMagnitudeAt(int x, int y, float mag) { - // norm factor must already be set - setNormalizedMagnitudeAt(x, y, mag / m_factor[x]); - } - - void setNormalizedMagnitudeAt(int x, int y, float norm) { - if (x < m_width && y < m_height) { - if (m_storageType == FFTCache::Polar) m_fmagnitude[x][y] = norm; - else m_magnitude[x][y] = uint16_t(norm * 65535.0); - } - } - - void setPhaseAt(int x, int y, float phase) { - // phase in range -pi -> pi - if (x < m_width && y < m_height) { - if (m_storageType == FFTCache::Polar) m_fphase[x][y] = phase; - else m_phase[x][y] = uint16_t(int16_t((phase * 32767) / M_PI)); - } - } - - void initialise(uint16_t **&); - void initialise(float **&); -}; - - -#endif - diff -r 655cd4e68e9a -r 420fc961c0c4 data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Fri Jun 12 13:46:44 2015 +0100 +++ b/data/model/FFTModel.cpp Fri Jun 12 14:51:46 2015 +0100 @@ -15,7 +15,6 @@ #include "FFTModel.h" #include "DenseTimeValueModel.h" -#include "AggregateWaveModel.h" #include "base/Profiler.h" #include "base/Pitch.h" @@ -23,178 +22,43 @@ #include #include +#include #ifndef __GNUC__ #include #endif +using namespace std; + FFTModel::FFTModel(const DenseTimeValueModel *model, int channel, WindowType windowType, int windowSize, int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame) : - //!!! ZoomConstraint! - m_server(0), - m_xshift(0), - m_yshift(0) + int fftSize) : + m_model(model), + m_channel(channel), + m_windowType(windowType), + m_windowSize(windowSize), + m_windowIncrement(windowIncrement), + m_fftSize(fftSize), + m_windower(windowType, windowSize) { - setSourceModel(const_cast(model)); //!!! hmm. - - m_server = getServer(model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar, - criteria, - fillFromFrame); - - if (!m_server) return; // caller should check isOK() - - int xratio = windowIncrement / m_server->getWindowIncrement(); - int yratio = m_server->getFFTSize() / fftSize; - - while (xratio > 1) { - if (xratio & 0x1) { - cerr << "ERROR: FFTModel: Window increment ratio " - << windowIncrement << " / " - << m_server->getWindowIncrement() - << " must be a power of two" << endl; - assert(!(xratio & 0x1)); - } - ++m_xshift; - xratio >>= 1; - } - - while (yratio > 1) { - if (yratio & 0x1) { - cerr << "ERROR: FFTModel: FFT size ratio " - << m_server->getFFTSize() << " / " << fftSize - << " must be a power of two" << endl; - assert(!(yratio & 0x1)); - } - ++m_yshift; - yratio >>= 1; - } } FFTModel::~FFTModel() { - if (m_server) FFTDataServer::releaseInstance(m_server); } void FFTModel::sourceModelAboutToBeDeleted() { - if (m_sourceModel) { - cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_sourceModel << ")" << endl; - if (m_server) { - FFTDataServer::releaseInstance(m_server); - m_server = 0; - } - FFTDataServer::modelAboutToBeDeleted(m_sourceModel); + if (m_model) { + cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl; + m_model = 0; } } -FFTDataServer * -FFTModel::getServer(const DenseTimeValueModel *model, - int channel, - WindowType windowType, - int windowSize, - int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria, - sv_frame_t fillFromFrame) -{ - // Obviously, an FFT model of channel C (where C != -1) of an - // aggregate model is the same as the FFT model of the appropriate - // channel of whichever model that aggregate channel is drawn - // from. We should use that model here, in case we already have - // the data for it or will be wanting the same data again later. - - // If the channel is -1 (i.e. mixture of all channels), then we - // can't do this shortcut unless the aggregate model only has one - // channel or contains exactly all of the channels of a single - // other model. That isn't very likely -- if it were the case, - // why would we be using an aggregate model? - - if (channel >= 0) { - - const AggregateWaveModel *aggregate = - dynamic_cast(model); - - if (aggregate && channel < aggregate->getComponentCount()) { - - AggregateWaveModel::ModelChannelSpec spec = - aggregate->getComponent(channel); - - return getServer(spec.model, - spec.channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar, - criteria, - fillFromFrame); - } - } - - // The normal case - - return FFTDataServer::getFuzzyInstance(model, - channel, - windowType, - windowSize, - windowIncrement, - fftSize, - polar, - criteria, - fillFromFrame); -} - -sv_samplerate_t -FFTModel::getSampleRate() const -{ - return isOK() ? m_server->getModel()->getSampleRate() : 0; -} - -FFTModel::Column -FFTModel::getColumn(int x) const -{ - Profiler profiler("FFTModel::getColumn", false); - - Column result; - - result.clear(); - int h = getHeight(); - result.reserve(h); - -#ifdef __GNUC__ - float magnitudes[h]; -#else - float *magnitudes = (float *)alloca(h * sizeof(float)); -#endif - - if (m_server->getMagnitudesAt(x << m_xshift, magnitudes)) { - - for (int y = 0; y < h; ++y) { - result.push_back(magnitudes[y]); - } - - } else { - for (int i = 0; i < h; ++i) result.push_back(0.f); - } - - return result; -} - QString FFTModel::getBinName(int n) const { @@ -209,10 +73,7 @@ { if (!isOK()) return false; - sv_samplerate_t sampleRate = m_server->getModel()->getSampleRate(); - - int fftSize = m_server->getFFTSize() >> m_yshift; - frequency = double(y * sampleRate) / fftSize; + frequency = double(y * getSampleRate()) / m_fftSize; if (x+1 >= getWidth()) return false; @@ -230,17 +91,15 @@ int incr = getResolution(); - double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / fftSize; + double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / m_fftSize; double phaseError = princarg(newPhase - expectedPhase); -// bool stable = (fabsf(phaseError) < (1.1f * (m_windowIncrement * M_PI) / m_fftSize)); - // The new frequency estimate based on the phase error resulting // from assuming the "native" frequency of this bin frequency = - (sampleRate * (expectedPhase + phaseError - oldPhase)) / + (getSampleRate() * (expectedPhase + phaseError - oldPhase)) / (2.0 * M_PI * incr); return true; @@ -293,8 +152,8 @@ sv_samplerate_t sampleRate = getSampleRate(); - std::deque window; - std::vector inrange; + deque window; + vector inrange; float dist = 0.5; int medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin, dist); @@ -331,8 +190,8 @@ else binmax = values.size()-1; } - std::deque sorted(window); - std::sort(sorted.begin(), sorted.end()); + deque sorted(window); + sort(sorted.begin(), sorted.end()); float median = sorted[int(float(sorted.size()) * dist)]; int centrebin = 0; @@ -380,15 +239,14 @@ if (type == MajorPeaks) return 10; if (bin == 0) return 3; - int fftSize = m_server->getFFTSize() >> m_yshift; - double binfreq = (sampleRate * bin) / fftSize; + double binfreq = (getSampleRate() * bin) / m_fftSize; double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq); - int hibin = int(lrint((hifreq * fftSize) / sampleRate)); + int hibin = int(lrint((hifreq * m_fftSize) / getSampleRate())); int medianWinSize = hibin - bin; if (medianWinSize < 3) medianWinSize = 3; - percentile = 0.5f + float(binfreq / sampleRate); + percentile = 0.5f + float(binfreq / getSampleRate()); return medianWinSize; } @@ -404,7 +262,6 @@ PeakLocationSet locations = getPeaks(type, x, ymin, ymax); sv_samplerate_t sampleRate = getSampleRate(); - int fftSize = m_server->getFFTSize() >> m_yshift; int incr = getResolution(); // This duplicates some of the work of estimateStableFrequency to @@ -412,7 +269,7 @@ // columns, instead of jumping back and forth between columns x and // x+1, which may be significantly slower if re-seeking is needed - std::vector phases; + vector phases; for (PeakLocationSet::iterator i = locations.begin(); i != locations.end(); ++i) { phases.push_back(getPhaseAt(x, *i)); @@ -423,13 +280,11 @@ i != locations.end(); ++i) { double oldPhase = phases[phaseIndex]; double newPhase = getPhaseAt(x+1, *i); - double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / fftSize; + double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / m_fftSize; double phaseError = princarg(newPhase - expectedPhase); double frequency = (sampleRate * (expectedPhase + phaseError - oldPhase)) / (2 * M_PI * incr); -// bool stable = (fabsf(phaseError) < (1.1f * (incr * M_PI) / fftSize)); -// if (stable) peaks[*i] = frequency; ++phaseIndex; } @@ -437,12 +292,3 @@ return peaks; } -FFTModel::FFTModel(const FFTModel &model) : - DenseThreeDimensionalModel(), - m_server(model.m_server), - m_xshift(model.m_xshift), - m_yshift(model.m_yshift) -{ - FFTDataServer::claimInstance(m_server); -} - diff -r 655cd4e68e9a -r 420fc961c0c4 data/model/FFTModel.h --- a/data/model/FFTModel.h Fri Jun 12 13:46:44 2015 +0100 +++ b/data/model/FFTModel.h Fri Jun 12 14:51:46 2015 +0100 @@ -16,8 +16,10 @@ #ifndef FFT_MODEL_H #define FFT_MODEL_H -#include "data/fft/FFTDataServer.h" #include "DenseThreeDimensionalModel.h" +#include "DenseTimeValueModel.h" + +#include "base/Window.h" #include #include @@ -25,11 +27,8 @@ /** * An implementation of DenseThreeDimensionalModel that makes FFT data * derived from a DenseTimeValueModel available as a generic data - * grid. The FFT data is acquired using FFTDataServer. Note that any - * of the accessor functions may throw AllocationFailed if a cache - * resize fails. + * grid. */ - class FFTModel : public DenseThreeDimensionalModel { Q_OBJECT @@ -43,111 +42,61 @@ * If the model has multiple channels use only the given channel, * unless the channel is -1 in which case merge all available * channels. - * - * If polar is true, the data will normally be retrieved from the - * FFT model in magnitude/phase form; otherwise it will normally - * be retrieved in "cartesian" real/imaginary form. The results - * should be the same either way, but a "polar" model addressed in - * "cartesian" form or vice versa may suffer a performance - * penalty. - * - * The fillFromColumn argument gives a hint that the FFT data - * server should aim to start calculating FFT data at that column - * number if possible, as that is likely to be requested first. */ FFTModel(const DenseTimeValueModel *model, int channel, WindowType windowType, int windowSize, int windowIncrement, - int fftSize, - bool polar, - StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria, - sv_frame_t fillFromFrame = 0); + int fftSize); ~FFTModel(); - inline float getMagnitudeAt(int x, int y) { - return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift); - } - inline float getNormalizedMagnitudeAt(int x, int y) { - return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift); - } - inline float getMaximumMagnitudeAt(int x) { - return m_server->getMaximumMagnitudeAt(x << m_xshift); - } - inline float getPhaseAt(int x, int y) { - return m_server->getPhaseAt(x << m_xshift, y << m_yshift); - } - inline void getValuesAt(int x, int y, float &real, float &imaginary) { - m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary); - } - inline bool isColumnAvailable(int x) const { - return m_server->isColumnReady(x << m_xshift); - } - - inline bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) { - return m_server->getMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); - } - inline bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) { - return m_server->getNormalizedMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); - } - inline bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) { - return m_server->getPhasesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio()); - } - inline bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) { - return m_server->getValuesAt(x << m_xshift, reals, imaginaries, minbin << m_yshift, count, getYRatio()); - } - - inline sv_frame_t getFillExtent() const { return m_server->getFillExtent(); } - // DenseThreeDimensionalModel and Model methods: // - inline virtual int getWidth() const { - return m_server->getWidth() >> m_xshift; - } - inline virtual int getHeight() const { - // If there is no y-shift, the server's height (based on its - // fftsize/2 + 1) is correct. If there is a shift, then the - // server is using a larger fft size than we want, so we shift - // it right as many times as necessary, but then we need to - // re-add the "+1" part (because ((fftsize*2)/2 + 1) / 2 != - // fftsize/2 + 1). - return (m_server->getHeight() >> m_yshift) + (m_yshift > 0 ? 1 : 0); - } - virtual float getValueAt(int x, int y) const { - return const_cast(this)->getMagnitudeAt(x, y); - } - virtual bool isOK() const { - // Return true if the model was constructed successfully (not - // necessarily whether an error has occurred since - // construction, use getError for that) - return m_server && m_server->getModel(); - } - virtual sv_frame_t getStartFrame() const { - return 0; - } + virtual int getWidth() const; + virtual int getHeight() const; + virtual float getValueAt(int x, int y) const { return getMagnitudeAt(x, y); } + virtual bool isOK() const { return m_model && m_model->isOK(); } + virtual sv_frame_t getStartFrame() const { return 0; } virtual sv_frame_t getEndFrame() const { return sv_frame_t(getWidth()) * getResolution() + getResolution(); } - virtual sv_samplerate_t getSampleRate() const; - virtual int getResolution() const { - return m_server->getWindowIncrement() << m_xshift; + virtual sv_samplerate_t getSampleRate() const { + return isOK() ? m_model->getSampleRate() : 0; } - virtual int getYBinCount() const { - return getHeight(); + virtual int getResolution() const { return m_windowIncrement; } + virtual int getYBinCount() const { return getHeight(); } + virtual float getMinimumLevel() const { return 0.f; } // Can't provide + virtual float getMaximumLevel() const { return 1.f; } // Can't provide + virtual Column getColumn(int x) const; // magnitudes + virtual QString getBinName(int n) const; + virtual bool shouldUseLogValueScale() const { return true; } + virtual int getCompletion() const { + int c = 100; + if (m_model) (void)m_model->isReady(&c); + return c; } - virtual float getMinimumLevel() const { - return 0.f; // Can't provide - } - virtual float getMaximumLevel() const { - return 1.f; // Can't provide - } - virtual Column getColumn(int x) const; - virtual QString getBinName(int n) const; + virtual QString getError() const { return ""; } //!!!??? + virtual sv_frame_t getFillExtent() const { return getEndFrame(); } - virtual bool shouldUseLogValueScale() const { - return true; // Although obviously it's up to the user... - } + // FFTModel methods: + // + int getChannel() const { return m_channel; } + WindowType getWindowType() const { return m_windowType; } + int getWindowSize() const { return m_windowSize; } + int getWindowIncrement() const { return m_windowIncrement; } + int getFFTSize() const { return m_fftSize; } + + float getMagnitudeAt(int x, int y) const; + float getNormalizedMagnitudeAt(int x, int y) const; + float getMaximumMagnitudeAt(int x) const; + float getPhaseAt(int x, int y) const; + void getValuesAt(int x, int y, float &real, float &imaginary) const; + bool isColumnAvailable(int x) const; + bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const; + bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const; + bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) const; + bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) const; /** * Calculate an estimated frequency for a stable signal in this @@ -179,13 +128,6 @@ virtual PeakSet getPeakFrequencies(PeakPickType type, int x, int ymin = 0, int ymax = 0); - virtual int getCompletion() const { return m_server->getFillCompletion(); } - virtual QString getError() const { return m_server->getError(); } - - virtual void suspend() { m_server->suspend(); } - virtual void suspendWrites() { m_server->suspendWrites(); } - virtual void resume() { m_server->resume(); } - QString getTypeName() const { return tr("FFT"); } public slots: @@ -195,23 +137,16 @@ FFTModel(const FFTModel &); // not implemented FFTModel &operator=(const FFTModel &); // not implemented - FFTDataServer *m_server; - int m_xshift; - int m_yshift; - - FFTDataServer *getServer(const DenseTimeValueModel *, - int, WindowType, int, int, int, - bool, StorageAdviser::Criteria, sv_frame_t); - + const DenseTimeValueModel *m_model; + int m_channel; + WindowType m_windowType; + int m_windowSize; + int m_windowIncrement; + int m_fftSize; + Window m_windower; + int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate, int bin, float &percentile) const; - - int getYRatio() { - int ys = m_yshift; - int r = 1; - while (ys) { --ys; r <<= 1; } - return r; - } }; #endif diff -r 655cd4e68e9a -r 420fc961c0c4 svcore.pro --- a/svcore.pro Fri Jun 12 13:46:44 2015 +0100 +++ b/svcore.pro Fri Jun 12 14:51:46 2015 +0100 @@ -119,13 +119,6 @@ base/XmlExportable.cpp HEADERS += data/fft/FFTapi.h \ - data/fft/FFTCacheReader.h \ - data/fft/FFTCacheStorageType.h \ - data/fft/FFTCacheWriter.h \ - data/fft/FFTDataServer.h \ - data/fft/FFTFileCacheReader.h \ - data/fft/FFTFileCacheWriter.h \ - data/fft/FFTMemoryCache.h \ data/fileio/AudioFileReader.h \ data/fileio/AudioFileReaderFactory.h \ data/fileio/BZipFileDevice.h \ @@ -184,10 +177,6 @@ data/osc/OSCMessage.h \ data/osc/OSCQueue.h SOURCES += data/fft/FFTapi.cpp \ - data/fft/FFTDataServer.cpp \ - data/fft/FFTFileCacheReader.cpp \ - data/fft/FFTFileCacheWriter.cpp \ - data/fft/FFTMemoryCache.cpp \ data/fileio/AudioFileReader.cpp \ data/fileio/AudioFileReaderFactory.cpp \ data/fileio/BZipFileDevice.cpp \ diff -r 655cd4e68e9a -r 420fc961c0c4 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Fri Jun 12 13:46:44 2015 +0100 +++ b/transform/FeatureExtractionModelTransformer.cpp Fri Jun 12 14:51:46 2015 +0100 @@ -606,9 +606,7 @@ primaryTransform.getWindowType(), blockSize, stepSize, - blockSize, - false, - StorageAdviser::PrecisionCritical); + blockSize); if (!model->isOK() || model->getError() != "") { QString err = model->getError(); delete model; @@ -618,7 +616,6 @@ //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer: error is: " + err); } - model->resume(); fftModels.push_back(model); cerr << "created model for channel " << ch << endl; }