lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: This file copyright 2006 Chris Cannam and QMUL. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #include "FFTDataServer.h" lbajardsilogic@0: lbajardsilogic@0: #include "FFTFileCache.h" lbajardsilogic@0: #include "FFTMemoryCache.h" lbajardsilogic@0: lbajardsilogic@0: #include "model/DenseTimeValueModel.h" lbajardsilogic@0: lbajardsilogic@0: #include "system/System.h" lbajardsilogic@0: lbajardsilogic@0: #include "base/StorageAdviser.h" lbajardsilogic@0: #include "base/Exceptions.h" lbajardsilogic@0: #include "base/Profiler.h" lbajardsilogic@0: #include "base/Thread.h" // for debug mutex locker lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: //#define DEBUG_FFT_SERVER 1 lbajardsilogic@0: //#define DEBUG_FFT_SERVER_FILL 1 lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER_FILL lbajardsilogic@0: #ifndef DEBUG_FFT_SERVER lbajardsilogic@0: #define DEBUG_FFT_SERVER 1 lbajardsilogic@0: #endif lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: FFTDataServer::ServerMap FFTDataServer::m_servers; lbajardsilogic@0: FFTDataServer::ServerQueue FFTDataServer::m_releasedServers; lbajardsilogic@0: QMutex FFTDataServer::m_serverMapMutex; lbajardsilogic@0: lbajardsilogic@0: FFTDataServer * lbajardsilogic@0: FFTDataServer::getInstance(const DenseTimeValueModel *model, lbajardsilogic@0: int channel, lbajardsilogic@0: WindowType windowType, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t fftSize, lbajardsilogic@0: bool polar, lbajardsilogic@0: size_t fillFromColumn) lbajardsilogic@0: { lbajardsilogic@0: QString n = generateFileBasename(model, lbajardsilogic@0: channel, lbajardsilogic@0: windowType, lbajardsilogic@0: windowSize, lbajardsilogic@0: windowIncrement, lbajardsilogic@0: fftSize, lbajardsilogic@0: polar); lbajardsilogic@0: lbajardsilogic@0: FFTDataServer *server = 0; lbajardsilogic@0: lbajardsilogic@0: MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getInstance]"); lbajardsilogic@0: lbajardsilogic@0: if ((server = findServer(n))) { lbajardsilogic@0: return server; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString npn = generateFileBasename(model, lbajardsilogic@0: channel, lbajardsilogic@0: windowType, lbajardsilogic@0: windowSize, lbajardsilogic@0: windowIncrement, lbajardsilogic@0: fftSize, lbajardsilogic@0: !polar); lbajardsilogic@0: lbajardsilogic@0: if ((server = findServer(npn))) { lbajardsilogic@0: return server; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: try { lbajardsilogic@0: server = new FFTDataServer(n, lbajardsilogic@0: model, lbajardsilogic@0: channel, lbajardsilogic@0: windowType, lbajardsilogic@0: windowSize, lbajardsilogic@0: windowIncrement, lbajardsilogic@0: fftSize, lbajardsilogic@0: polar, lbajardsilogic@0: fillFromColumn); lbajardsilogic@0: } catch (InsufficientDiscSpace) { lbajardsilogic@0: delete server; lbajardsilogic@0: server = 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (server) { lbajardsilogic@0: m_servers[n] = ServerCountPair(server, 1); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: return server; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTDataServer * lbajardsilogic@0: FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model, lbajardsilogic@0: int channel, lbajardsilogic@0: WindowType windowType, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t fftSize, lbajardsilogic@0: bool polar, lbajardsilogic@0: size_t fillFromColumn) lbajardsilogic@0: { lbajardsilogic@0: // Fuzzy matching: lbajardsilogic@0: // lbajardsilogic@0: // -- if we're asked for polar and have non-polar, use it (and lbajardsilogic@0: // vice versa). This one is vital, and we do it for non-fuzzy as lbajardsilogic@0: // well (above). lbajardsilogic@0: // lbajardsilogic@0: // -- if we're asked for an instance with a given fft size and we lbajardsilogic@0: // have one already with a multiple of that fft size but the same lbajardsilogic@0: // window size and type (and model), we can draw the results from lbajardsilogic@0: // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the lbajardsilogic@0: // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the lbajardsilogic@0: // same window plus zero padding). lbajardsilogic@0: // lbajardsilogic@0: // -- if we're asked for an instance with a given window type and lbajardsilogic@0: // size and fft size and we have one already the same but with a lbajardsilogic@0: // smaller increment, we can draw the results from it (provided lbajardsilogic@0: // our increment is a multiple of its) lbajardsilogic@0: // lbajardsilogic@0: // The FFTModel knows how to interpret these things. In lbajardsilogic@0: // both cases we require that the larger one is a power-of-two lbajardsilogic@0: // multiple of the smaller (e.g. even though in principle you can lbajardsilogic@0: // draw the results at increment 256 from those at increment 768 lbajardsilogic@0: // or 1536, the model doesn't support this). lbajardsilogic@0: lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getFuzzyInstance]"); lbajardsilogic@0: lbajardsilogic@0: ServerMap::iterator best = m_servers.end(); lbajardsilogic@0: int bestdist = -1; lbajardsilogic@0: lbajardsilogic@0: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { lbajardsilogic@0: lbajardsilogic@0: FFTDataServer *server = i->second.first; lbajardsilogic@0: lbajardsilogic@0: if (server->getModel() == model && lbajardsilogic@0: (server->getChannel() == channel || model->getChannelCount() == 1) && lbajardsilogic@0: server->getWindowType() == windowType && lbajardsilogic@0: server->getWindowSize() == windowSize && lbajardsilogic@0: server->getWindowIncrement() <= windowIncrement && lbajardsilogic@0: server->getFFTSize() >= fftSize) { lbajardsilogic@0: lbajardsilogic@0: if ((windowIncrement % server->getWindowIncrement()) != 0) continue; lbajardsilogic@0: int ratio = windowIncrement / server->getWindowIncrement(); lbajardsilogic@0: bool poweroftwo = true; lbajardsilogic@0: while (ratio > 1) { lbajardsilogic@0: if (ratio & 0x1) { lbajardsilogic@0: poweroftwo = false; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: ratio >>= 1; lbajardsilogic@0: } lbajardsilogic@0: if (!poweroftwo) continue; lbajardsilogic@0: lbajardsilogic@0: if ((server->getFFTSize() % fftSize) != 0) continue; lbajardsilogic@0: ratio = server->getFFTSize() / fftSize; lbajardsilogic@0: while (ratio > 1) { lbajardsilogic@0: if (ratio & 0x1) { lbajardsilogic@0: poweroftwo = false; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: ratio >>= 1; lbajardsilogic@0: } lbajardsilogic@0: if (!poweroftwo) continue; lbajardsilogic@0: lbajardsilogic@0: int distance = 0; lbajardsilogic@0: lbajardsilogic@0: if (server->getPolar() != polar) distance += 1; lbajardsilogic@0: lbajardsilogic@0: distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15; lbajardsilogic@0: distance += ((server->getFFTSize() / fftSize) - 1) * 10; lbajardsilogic@0: lbajardsilogic@0: if (server->getFillCompletion() < 50) distance += 100; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (bestdist == -1 || distance < bestdist) { lbajardsilogic@0: bestdist = distance; lbajardsilogic@0: best = i; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (bestdist >= 0) { lbajardsilogic@0: FFTDataServer *server = best->second.first; lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: claimInstance(server, false); lbajardsilogic@0: return server; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // Nothing found, make a new one lbajardsilogic@0: lbajardsilogic@0: return getInstance(model, lbajardsilogic@0: channel, lbajardsilogic@0: windowType, lbajardsilogic@0: windowSize, lbajardsilogic@0: windowIncrement, lbajardsilogic@0: fftSize, lbajardsilogic@0: polar, lbajardsilogic@0: fillFromColumn); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTDataServer * lbajardsilogic@0: FFTDataServer::findServer(QString n) lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (m_servers.find(n) != m_servers.end()) { lbajardsilogic@0: lbajardsilogic@0: FFTDataServer *server = m_servers[n].first; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): found " << server << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: claimInstance(server, false); lbajardsilogic@0: lbajardsilogic@0: return server; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): not found" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::claimInstance(FFTDataServer *server) lbajardsilogic@0: { lbajardsilogic@0: claimInstance(server, true); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::claimInstance(FFTDataServer *server, bool needLock) lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(needLock ? &m_serverMapMutex : 0, lbajardsilogic@0: "FFTDataServer::m_serverMapMutex[claimInstance]"); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { lbajardsilogic@0: if (i->second.first == server) { lbajardsilogic@0: lbajardsilogic@0: for (ServerQueue::iterator j = m_releasedServers.begin(); lbajardsilogic@0: j != m_releasedServers.end(); ++j) { lbajardsilogic@0: lbajardsilogic@0: if (*j == server) { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: m_releasedServers.erase(j); lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: ++i->second.second; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::claimInstance: instance " lbajardsilogic@0: << server << " unknown!" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::releaseInstance(FFTDataServer *server) lbajardsilogic@0: { lbajardsilogic@0: releaseInstance(server, true); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::releaseInstance(FFTDataServer *server, bool needLock) lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(needLock ? &m_serverMapMutex : 0, lbajardsilogic@0: "FFTDataServer::m_serverMapMutex[releaseInstance]"); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: // -- if ref count > 0, decrement and return lbajardsilogic@0: // -- if the instance hasn't been used at all, delete it immediately lbajardsilogic@0: // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts, lbajardsilogic@0: // leave them hanging around lbajardsilogic@0: // -- if N instances with zero refcounts remain, delete the one that lbajardsilogic@0: // was last released first lbajardsilogic@0: // -- if we run out of disk space when allocating an instance, go back lbajardsilogic@0: // and delete the spare N instances before trying again lbajardsilogic@0: // -- have an additional method to indicate that a model has been lbajardsilogic@0: // destroyed, so that we can delete all of its fft server instances lbajardsilogic@0: lbajardsilogic@0: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { lbajardsilogic@0: if (i->second.first == server) { lbajardsilogic@0: if (i->second.second == 0) { lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::releaseInstance(" lbajardsilogic@0: << server << "): instance not allocated" << std::endl; lbajardsilogic@0: } else if (--i->second.second == 0) { lbajardsilogic@0: if (server->m_lastUsedCache == -1) { // never used lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::releaseInstance: instance " lbajardsilogic@0: << server << " has never been used, erasing" lbajardsilogic@0: << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: delete server; lbajardsilogic@0: m_servers.erase(i); lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::releaseInstance: instance " lbajardsilogic@0: << server << " no longer in use, marking for possible collection" lbajardsilogic@0: << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: bool found = false; lbajardsilogic@0: for (ServerQueue::iterator j = m_releasedServers.begin(); lbajardsilogic@0: j != m_releasedServers.end(); ++j) { lbajardsilogic@0: if (*j == server) { lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::releaseInstance(" lbajardsilogic@0: << server << "): server is already in " lbajardsilogic@0: << "released servers list" << std::endl; lbajardsilogic@0: found = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (!found) m_releasedServers.push_back(server); lbajardsilogic@0: server->suspend(); lbajardsilogic@0: purgeLimbo(); lbajardsilogic@0: } lbajardsilogic@0: } else { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::releaseInstance: instance " lbajardsilogic@0: << server << " now has refcount " << i->second.second lbajardsilogic@0: << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: } lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): " lbajardsilogic@0: << "instance not found" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::purgeLimbo(int maxSize) lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " lbajardsilogic@0: << m_releasedServers.size() << " candidates" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: while (int(m_releasedServers.size()) > maxSize) { lbajardsilogic@0: lbajardsilogic@0: FFTDataServer *server = *m_releasedServers.begin(); lbajardsilogic@0: lbajardsilogic@0: bool found = false; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::purgeLimbo: considering candidate " lbajardsilogic@0: << server << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { lbajardsilogic@0: lbajardsilogic@0: if (i->second.first == server) { lbajardsilogic@0: found = true; lbajardsilogic@0: if (i->second.second > 0) { lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server " lbajardsilogic@0: << server << " is in released queue, but still has non-zero refcount " lbajardsilogic@0: << i->second.second << std::endl; lbajardsilogic@0: // ... so don't delete it lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it" lbajardsilogic@0: << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: m_servers.erase(i); lbajardsilogic@0: delete server; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!found) { lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server " lbajardsilogic@0: << server << " is in released queue, but not in server map!" lbajardsilogic@0: << std::endl; lbajardsilogic@0: delete server; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_releasedServers.pop_front(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " lbajardsilogic@0: << m_releasedServers.size() << " remain" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::modelAboutToBeDeleted(Model *model) lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(&m_serverMapMutex, lbajardsilogic@0: "FFTDataServer::m_serverMapMutex[modelAboutToBeDeleted]"); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")" lbajardsilogic@0: << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { lbajardsilogic@0: lbajardsilogic@0: FFTDataServer *server = i->second.first; lbajardsilogic@0: lbajardsilogic@0: if (server->getModel() == model) { lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is " lbajardsilogic@0: << server << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: if (i->second.second > 0) { lbajardsilogic@0: std::cerr << "ERROR: FFTDataServer::modelAboutToBeDeleted: Model " << model << " (\"" << model->objectName().toStdString() << "\") is about to be deleted, but is still being referred to by FFT server " << server << " with non-zero refcount " << i->second.second << std::endl; lbajardsilogic@0: } lbajardsilogic@0: for (ServerQueue::iterator j = m_releasedServers.begin(); lbajardsilogic@0: j != m_releasedServers.end(); ++j) { lbajardsilogic@0: if (*j == server) { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: m_releasedServers.erase(j); lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: m_servers.erase(i); lbajardsilogic@0: delete server; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTDataServer::FFTDataServer(QString fileBaseName, lbajardsilogic@0: const DenseTimeValueModel *model, lbajardsilogic@0: int channel, lbajardsilogic@0: WindowType windowType, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t fftSize, lbajardsilogic@0: bool polar, lbajardsilogic@0: size_t fillFromColumn) : lbajardsilogic@0: m_fileBaseName(fileBaseName), lbajardsilogic@0: m_model(model), lbajardsilogic@0: m_channel(channel), lbajardsilogic@0: m_windower(windowType, windowSize), lbajardsilogic@0: m_windowSize(windowSize), lbajardsilogic@0: m_windowIncrement(windowIncrement), lbajardsilogic@0: m_fftSize(fftSize), lbajardsilogic@0: m_polar(polar), lbajardsilogic@0: m_width(0), lbajardsilogic@0: m_height(0), lbajardsilogic@0: m_cacheWidth(0), lbajardsilogic@0: m_memoryCache(false), lbajardsilogic@0: m_compactCache(false), lbajardsilogic@0: m_lastUsedCache(-1), lbajardsilogic@0: m_fftInput(0), lbajardsilogic@0: m_exiting(false), lbajardsilogic@0: m_suspended(true), //!!! or false? lbajardsilogic@0: m_fillThread(0) lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: size_t start = m_model->getStartFrame(); lbajardsilogic@0: size_t end = m_model->getEndFrame(); lbajardsilogic@0: lbajardsilogic@0: m_width = (end - start) / m_windowIncrement + 1; lbajardsilogic@0: m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2 lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << "): dimensions are " lbajardsilogic@0: << m_width << "x" << m_height << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: size_t maxCacheSize = 20 * 1024 * 1024; lbajardsilogic@0: size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample); lbajardsilogic@0: if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width; lbajardsilogic@0: else m_cacheWidth = maxCacheSize / columnSize; lbajardsilogic@0: lbajardsilogic@0: int bits = 0; lbajardsilogic@0: while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; } lbajardsilogic@0: m_cacheWidth = 2; lbajardsilogic@0: while (bits) { m_cacheWidth <<= 1; --bits; } lbajardsilogic@0: lbajardsilogic@0: //!!! Need to pass in what this server is intended for lbajardsilogic@0: // (e.g. playback processing, spectrogram, feature extraction), lbajardsilogic@0: // or pass in something akin to the storage adviser criteria. lbajardsilogic@0: // That probably goes alongside the polar argument. lbajardsilogic@0: // For now we'll assume "spectrogram" criteria for polar ffts, lbajardsilogic@0: // and "feature extraction" criteria for rectangular ones. lbajardsilogic@0: lbajardsilogic@0: StorageAdviser::Criteria criteria; lbajardsilogic@0: if (m_polar) { lbajardsilogic@0: criteria = StorageAdviser::Criteria lbajardsilogic@0: (StorageAdviser::SpeedCritical | StorageAdviser::LongRetentionLikely); lbajardsilogic@0: } else { lbajardsilogic@0: criteria = StorageAdviser::Criteria(StorageAdviser::PrecisionCritical); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int cells = m_width * m_height; lbajardsilogic@0: int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb lbajardsilogic@0: int maximumSize = (cells / 1024) * sizeof(float); // kb lbajardsilogic@0: lbajardsilogic@0: StorageAdviser::Recommendation recommendation; lbajardsilogic@0: lbajardsilogic@0: try { lbajardsilogic@0: lbajardsilogic@0: recommendation = lbajardsilogic@0: StorageAdviser::recommend(criteria, minimumSize, maximumSize); lbajardsilogic@0: lbajardsilogic@0: } catch (InsufficientDiscSpace s) { lbajardsilogic@0: lbajardsilogic@0: // Delete any unused servers we may have been leaving around lbajardsilogic@0: // in case we wanted them again lbajardsilogic@0: lbajardsilogic@0: purgeLimbo(0); lbajardsilogic@0: lbajardsilogic@0: // This time we don't catch InsufficientDiscSpace -- we lbajardsilogic@0: // haven't allocated anything yet and can safely let the lbajardsilogic@0: // exception out to indicate to the caller that we can't lbajardsilogic@0: // handle it. lbajardsilogic@0: lbajardsilogic@0: recommendation = lbajardsilogic@0: StorageAdviser::recommend(criteria, minimumSize, maximumSize); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "Recommendation was: " << recommendation << std::endl; lbajardsilogic@0: lbajardsilogic@0: m_memoryCache = ((recommendation & StorageAdviser::UseMemory) || lbajardsilogic@0: (recommendation & StorageAdviser::PreferMemory)); lbajardsilogic@0: lbajardsilogic@0: m_compactCache = (recommendation & StorageAdviser::ConserveSpace); lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: StorageAdviser::notifyPlannedAllocation lbajardsilogic@0: (m_memoryCache ? StorageAdviser::MemoryAllocation : lbajardsilogic@0: StorageAdviser::DiscAllocation, lbajardsilogic@0: m_compactCache ? minimumSize : maximumSize); lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) { lbajardsilogic@0: m_caches.push_back(0); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fftInput = (fftsample *) lbajardsilogic@0: fftf_malloc(fftSize * sizeof(fftsample)); lbajardsilogic@0: lbajardsilogic@0: m_fftOutput = (fftf_complex *) lbajardsilogic@0: fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex)); lbajardsilogic@0: lbajardsilogic@0: m_workbuffer = (float *) lbajardsilogic@0: fftf_malloc((fftSize+2) * sizeof(float)); lbajardsilogic@0: lbajardsilogic@0: m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize, lbajardsilogic@0: m_fftInput, lbajardsilogic@0: m_fftOutput, lbajardsilogic@0: FFTW_ESTIMATE); lbajardsilogic@0: lbajardsilogic@0: if (!m_fftPlan) { lbajardsilogic@0: std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl; lbajardsilogic@0: throw(0); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fillThread = new FillThread(*this, fillFromColumn); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTDataServer::~FFTDataServer() lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: m_suspended = false; lbajardsilogic@0: m_exiting = true; lbajardsilogic@0: m_condition.wakeAll(); lbajardsilogic@0: if (m_fillThread) { lbajardsilogic@0: m_fillThread->wait(); lbajardsilogic@0: delete m_fillThread; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: MutexLocker locker(&m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[~FFTDataServer]"); lbajardsilogic@0: lbajardsilogic@0: for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { lbajardsilogic@0: if (*i) { lbajardsilogic@0: delete *i; lbajardsilogic@0: } else { lbajardsilogic@0: StorageAdviser::notifyDoneAllocation lbajardsilogic@0: (m_memoryCache ? StorageAdviser::MemoryAllocation : lbajardsilogic@0: StorageAdviser::DiscAllocation, lbajardsilogic@0: m_cacheWidth * m_height * lbajardsilogic@0: (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: deleteProcessingData(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::deleteProcessingData() lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: if (m_fftInput) { lbajardsilogic@0: fftf_destroy_plan(m_fftPlan); lbajardsilogic@0: fftf_free(m_fftInput); lbajardsilogic@0: fftf_free(m_fftOutput); lbajardsilogic@0: fftf_free(m_workbuffer); lbajardsilogic@0: } lbajardsilogic@0: m_fftInput = 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::suspend() lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: Profiler profiler("FFTDataServer::suspend", false); lbajardsilogic@0: lbajardsilogic@0: MutexLocker locker(&m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[suspend]"); lbajardsilogic@0: m_suspended = true; lbajardsilogic@0: for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { lbajardsilogic@0: if (*i) (*i)->suspend(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::suspendWrites() lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: Profiler profiler("FFTDataServer::suspendWrites", false); lbajardsilogic@0: lbajardsilogic@0: m_suspended = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::resume() lbajardsilogic@0: { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: Profiler profiler("FFTDataServer::resume", false); lbajardsilogic@0: lbajardsilogic@0: m_suspended = false; lbajardsilogic@0: if (m_fillThread) { lbajardsilogic@0: if (m_fillThread->isFinished()) { lbajardsilogic@0: delete m_fillThread; lbajardsilogic@0: m_fillThread = 0; lbajardsilogic@0: deleteProcessingData(); lbajardsilogic@0: } else { lbajardsilogic@0: m_condition.wakeAll(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: FFTCache * lbajardsilogic@0: FFTDataServer::getCacheAux(size_t c) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getCacheAux", false); lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: MutexLocker locker(&m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[getCacheAux]"); lbajardsilogic@0: lbajardsilogic@0: if (m_lastUsedCache == -1) { lbajardsilogic@0: m_fillThread->start(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (int(c) != m_lastUsedCache) { lbajardsilogic@0: lbajardsilogic@0: // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl; lbajardsilogic@0: lbajardsilogic@0: for (IntQueue::iterator i = m_dormantCaches.begin(); lbajardsilogic@0: i != m_dormantCaches.end(); ++i) { lbajardsilogic@0: if (*i == int(c)) { lbajardsilogic@0: m_dormantCaches.erase(i); lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_lastUsedCache >= 0) { lbajardsilogic@0: bool inDormant = false; lbajardsilogic@0: for (size_t i = 0; i < m_dormantCaches.size(); ++i) { lbajardsilogic@0: if (m_dormantCaches[i] == m_lastUsedCache) { lbajardsilogic@0: inDormant = true; lbajardsilogic@0: break; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: if (!inDormant) { lbajardsilogic@0: m_dormantCaches.push_back(m_lastUsedCache); lbajardsilogic@0: } lbajardsilogic@0: while (m_dormantCaches.size() > 4) { lbajardsilogic@0: int dc = m_dormantCaches.front(); lbajardsilogic@0: m_dormantCaches.pop_front(); lbajardsilogic@0: m_caches[dc]->suspend(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_caches[c]) { lbajardsilogic@0: m_lastUsedCache = c; lbajardsilogic@0: return m_caches[c]; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString name = QString("%1-%2").arg(m_fileBaseName).arg(c); lbajardsilogic@0: lbajardsilogic@0: FFTCache *cache = 0; lbajardsilogic@0: lbajardsilogic@0: size_t width = m_cacheWidth; lbajardsilogic@0: if (c * m_cacheWidth + width > m_width) { lbajardsilogic@0: width = m_width - c * m_cacheWidth; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: try { lbajardsilogic@0: lbajardsilogic@0: if (m_memoryCache) { lbajardsilogic@0: lbajardsilogic@0: cache = new FFTMemoryCache(); lbajardsilogic@0: lbajardsilogic@0: } else if (m_compactCache) { lbajardsilogic@0: lbajardsilogic@0: cache = new FFTFileCache(name, MatrixFile::ReadWrite, lbajardsilogic@0: FFTFileCache::Compact); lbajardsilogic@0: lbajardsilogic@0: } else { lbajardsilogic@0: lbajardsilogic@0: cache = new FFTFileCache(name, MatrixFile::ReadWrite, lbajardsilogic@0: m_polar ? FFTFileCache::Polar : lbajardsilogic@0: FFTFileCache::Rectangular); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: cache->resize(width, m_height); lbajardsilogic@0: cache->reset(); lbajardsilogic@0: lbajardsilogic@0: } catch (std::bad_alloc) { lbajardsilogic@0: lbajardsilogic@0: delete cache; lbajardsilogic@0: cache = 0; lbajardsilogic@0: lbajardsilogic@0: if (m_memoryCache) { lbajardsilogic@0: lbajardsilogic@0: std::cerr << "WARNING: Memory allocation failed when resizing" lbajardsilogic@0: << " FFT memory cache no. " << c << " to " << width lbajardsilogic@0: << "x" << m_height << " (of total width " << m_width lbajardsilogic@0: << "): falling back to disc cache" << std::endl; lbajardsilogic@0: lbajardsilogic@0: try { lbajardsilogic@0: lbajardsilogic@0: cache = new FFTFileCache(name, MatrixFile::ReadWrite, lbajardsilogic@0: FFTFileCache::Compact); lbajardsilogic@0: lbajardsilogic@0: cache->resize(width, m_height); lbajardsilogic@0: cache->reset(); lbajardsilogic@0: lbajardsilogic@0: } catch (std::bad_alloc) { lbajardsilogic@0: lbajardsilogic@0: delete cache; lbajardsilogic@0: cache = 0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (cache) { lbajardsilogic@0: std::cerr << "ERROR: Memory allocation failed when resizing" lbajardsilogic@0: << " FFT file cache no. " << c << " to " << width lbajardsilogic@0: << "x" << m_height << " (of total width " << m_width lbajardsilogic@0: << "): abandoning this cache" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: //!!! Shouldn't be using QtGui here. Need a better way to report this. lbajardsilogic@0: QMessageBox::critical lbajardsilogic@0: (0, QApplication::tr("FFT cache resize failed"), lbajardsilogic@0: QApplication::tr lbajardsilogic@0: ("Failed to create or resize an FFT model slice.\n" lbajardsilogic@0: "There may be insufficient memory or disc space to continue.")); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: StorageAdviser::notifyDoneAllocation lbajardsilogic@0: (m_memoryCache ? StorageAdviser::MemoryAllocation : lbajardsilogic@0: StorageAdviser::DiscAllocation, lbajardsilogic@0: width * m_height * lbajardsilogic@0: (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1); lbajardsilogic@0: lbajardsilogic@0: m_caches[c] = cache; lbajardsilogic@0: m_lastUsedCache = c; lbajardsilogic@0: return cache; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: FFTDataServer::getMagnitudeAt(size_t x, size_t y) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getMagnitudeAt", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width || y >= m_height) return 0; lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return 0; lbajardsilogic@0: lbajardsilogic@0: if (!cache->haveSetColumnAt(col)) { lbajardsilogic@0: std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn(" lbajardsilogic@0: << x << ")" << std::endl; lbajardsilogic@0: fillColumn(x); lbajardsilogic@0: } lbajardsilogic@0: return cache->getMagnitudeAt(col, y); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width || y >= m_height) return 0; lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return 0; lbajardsilogic@0: lbajardsilogic@0: if (!cache->haveSetColumnAt(col)) { lbajardsilogic@0: fillColumn(x); lbajardsilogic@0: } lbajardsilogic@0: return cache->getNormalizedMagnitudeAt(col, y); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: FFTDataServer::getMaximumMagnitudeAt(size_t x) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width) return 0; lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return 0; lbajardsilogic@0: lbajardsilogic@0: if (!cache->haveSetColumnAt(col)) { lbajardsilogic@0: fillColumn(x); lbajardsilogic@0: } lbajardsilogic@0: return cache->getMaximumMagnitudeAt(col); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float lbajardsilogic@0: FFTDataServer::getPhaseAt(size_t x, size_t y) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getPhaseAt", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width || y >= m_height) return 0; lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return 0; lbajardsilogic@0: lbajardsilogic@0: if (!cache->haveSetColumnAt(col)) { lbajardsilogic@0: fillColumn(x); lbajardsilogic@0: } lbajardsilogic@0: return cache->getPhaseAt(col, y); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::getValuesAt", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width || y >= m_height) { lbajardsilogic@0: real = 0; lbajardsilogic@0: imaginary = 0; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: lbajardsilogic@0: if (!cache) { lbajardsilogic@0: real = 0; lbajardsilogic@0: imaginary = 0; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!cache->haveSetColumnAt(col)) { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: fillColumn(x); lbajardsilogic@0: } lbajardsilogic@0: float magnitude = cache->getMagnitudeAt(col, y); lbajardsilogic@0: float phase = cache->getPhaseAt(col, y); lbajardsilogic@0: real = magnitude * cosf(phase); lbajardsilogic@0: imaginary = magnitude * sinf(phase); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: FFTDataServer::isColumnReady(size_t x) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::isColumnReady", false); lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width) return true; lbajardsilogic@0: lbajardsilogic@0: if (!haveCache(x)) { lbajardsilogic@0: if (m_lastUsedCache == -1) { lbajardsilogic@0: if (m_suspended) { lbajardsilogic@0: std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl; lbajardsilogic@0: resume(); lbajardsilogic@0: } lbajardsilogic@0: m_fillThread->start(); lbajardsilogic@0: } lbajardsilogic@0: return false; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return true; lbajardsilogic@0: lbajardsilogic@0: return cache->haveSetColumnAt(col); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::fillColumn(size_t x) lbajardsilogic@0: { lbajardsilogic@0: Profiler profiler("FFTDataServer::fillColumn", false); lbajardsilogic@0: lbajardsilogic@0: if (!m_fftInput) { lbajardsilogic@0: std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " lbajardsilogic@0: << "input has already been completed and discarded?" lbajardsilogic@0: << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (x >= m_width) { lbajardsilogic@0: std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " lbajardsilogic@0: << "x > width (" << x << " > " << m_width << ")" lbajardsilogic@0: << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t col; lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER_FILL lbajardsilogic@0: std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: FFTCache *cache = getCache(x, col); lbajardsilogic@0: if (!cache) return; lbajardsilogic@0: lbajardsilogic@0: MutexLocker locker(&m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[fillColumn]"); lbajardsilogic@0: lbajardsilogic@0: if (cache->haveSetColumnAt(col)) return; lbajardsilogic@0: lbajardsilogic@0: int startFrame = m_windowIncrement * x; lbajardsilogic@0: int endFrame = startFrame + m_windowSize; lbajardsilogic@0: lbajardsilogic@0: startFrame -= int(m_windowSize - m_windowIncrement) / 2; lbajardsilogic@0: endFrame -= int(m_windowSize - m_windowIncrement) / 2; lbajardsilogic@0: size_t pfx = 0; lbajardsilogic@0: lbajardsilogic@0: size_t off = (m_fftSize - m_windowSize) / 2; lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i < off; ++i) { lbajardsilogic@0: m_fftInput[i] = 0.0; lbajardsilogic@0: m_fftInput[m_fftSize - i - 1] = 0.0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (startFrame < 0) { lbajardsilogic@0: pfx = size_t(-startFrame); lbajardsilogic@0: for (size_t i = 0; i < pfx; ++i) { lbajardsilogic@0: m_fftInput[off + i] = 0.0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER_FILL lbajardsilogic@0: std::cerr << "FFTDataServer::fillColumn: requesting frames " lbajardsilogic@0: << startFrame + pfx << " -> " << endFrame << " ( = " lbajardsilogic@0: << endFrame - (startFrame + pfx) << ") at index " lbajardsilogic@0: << off + pfx << " in buffer of size " << m_fftSize lbajardsilogic@0: << " with window size " << m_windowSize lbajardsilogic@0: << " from channel " << m_channel << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: size_t got = m_model->getValues(m_channel, startFrame + pfx, lbajardsilogic@0: endFrame, m_fftInput + off + pfx); lbajardsilogic@0: lbajardsilogic@0: while (got + pfx < m_windowSize) { lbajardsilogic@0: m_fftInput[off + got + pfx] = 0.0; lbajardsilogic@0: ++got; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_channel == -1) { lbajardsilogic@0: int channels = m_model->getChannelCount(); lbajardsilogic@0: if (channels > 1) { lbajardsilogic@0: for (size_t i = 0; i < m_windowSize; ++i) { lbajardsilogic@0: m_fftInput[off + i] /= channels; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_windower.cut(m_fftInput + off); lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i < m_fftSize/2; ++i) { lbajardsilogic@0: fftsample temp = m_fftInput[i]; lbajardsilogic@0: m_fftInput[i] = m_fftInput[i + m_fftSize/2]; lbajardsilogic@0: m_fftInput[i + m_fftSize/2] = temp; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: fftf_execute(m_fftPlan); lbajardsilogic@0: lbajardsilogic@0: fftsample factor = 0.0; lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i <= m_fftSize/2; ++i) { lbajardsilogic@0: lbajardsilogic@0: fftsample mag = sqrtf(m_fftOutput[i][0] * m_fftOutput[i][0] + lbajardsilogic@0: m_fftOutput[i][1] * m_fftOutput[i][1]); lbajardsilogic@0: mag /= m_windowSize / 2; lbajardsilogic@0: lbajardsilogic@0: if (mag > factor) factor = mag; lbajardsilogic@0: lbajardsilogic@0: fftsample phase = atan2f(m_fftOutput[i][1], m_fftOutput[i][0]); lbajardsilogic@0: phase = princargf(phase); lbajardsilogic@0: lbajardsilogic@0: m_workbuffer[i] = mag; lbajardsilogic@0: m_workbuffer[i + m_fftSize/2+1] = phase; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: cache->setColumnAt(col, lbajardsilogic@0: m_workbuffer, lbajardsilogic@0: m_workbuffer + m_fftSize/2+1, lbajardsilogic@0: factor); lbajardsilogic@0: lbajardsilogic@0: if (m_suspended) { lbajardsilogic@0: // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl; lbajardsilogic@0: // resume(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: FFTDataServer::getFillCompletion() const lbajardsilogic@0: { lbajardsilogic@0: if (m_fillThread) return m_fillThread->getCompletion(); lbajardsilogic@0: else return 100; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t lbajardsilogic@0: FFTDataServer::getFillExtent() const lbajardsilogic@0: { lbajardsilogic@0: if (m_fillThread) return m_fillThread->getExtent(); lbajardsilogic@0: else return m_model->getEndFrame(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: FFTDataServer::generateFileBasename() const lbajardsilogic@0: { lbajardsilogic@0: return generateFileBasename(m_model, m_channel, m_windower.getType(), lbajardsilogic@0: m_windowSize, m_windowIncrement, m_fftSize, lbajardsilogic@0: m_polar); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: FFTDataServer::generateFileBasename(const DenseTimeValueModel *model, lbajardsilogic@0: int channel, lbajardsilogic@0: WindowType windowType, lbajardsilogic@0: size_t windowSize, lbajardsilogic@0: size_t windowIncrement, lbajardsilogic@0: size_t fftSize, lbajardsilogic@0: bool polar) lbajardsilogic@0: { lbajardsilogic@0: char buffer[200]; lbajardsilogic@0: lbajardsilogic@0: sprintf(buffer, "%u-%u-%u-%u-%u-%u%s", lbajardsilogic@0: (unsigned int)XmlExportable::getObjectExportId(model), lbajardsilogic@0: (unsigned int)(channel + 1), lbajardsilogic@0: (unsigned int)windowType, lbajardsilogic@0: (unsigned int)windowSize, lbajardsilogic@0: (unsigned int)windowIncrement, lbajardsilogic@0: (unsigned int)fftSize, lbajardsilogic@0: polar ? "-p" : "-r"); lbajardsilogic@0: lbajardsilogic@0: return buffer; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: FFTDataServer::FillThread::run() lbajardsilogic@0: { lbajardsilogic@0: m_extent = 0; lbajardsilogic@0: m_completion = 0; lbajardsilogic@0: lbajardsilogic@0: size_t start = m_server.m_model->getStartFrame(); lbajardsilogic@0: size_t end = m_server.m_model->getEndFrame(); lbajardsilogic@0: size_t remainingEnd = end; lbajardsilogic@0: lbajardsilogic@0: int counter = 0; lbajardsilogic@0: int updateAt = 1; lbajardsilogic@0: int maxUpdateAt = (end / m_server.m_windowIncrement) / 20; lbajardsilogic@0: if (maxUpdateAt < 100) maxUpdateAt = 100; lbajardsilogic@0: lbajardsilogic@0: if (m_fillFrom > start) { lbajardsilogic@0: lbajardsilogic@0: for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) { lbajardsilogic@0: lbajardsilogic@0: m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); lbajardsilogic@0: lbajardsilogic@0: if (m_server.m_exiting) return; lbajardsilogic@0: lbajardsilogic@0: while (m_server.m_suspended) { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(&m_server.m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[run/1]"); lbajardsilogic@0: m_server.m_condition.wait(&m_server.m_writeMutex, 10000); lbajardsilogic@0: } lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: if (m_server.m_exiting) return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (++counter == updateAt) { lbajardsilogic@0: m_extent = f; lbajardsilogic@0: m_completion = size_t(100 * fabsf(float(f - m_fillFrom) / lbajardsilogic@0: float(end - start))); lbajardsilogic@0: counter = 0; lbajardsilogic@0: if (updateAt < maxUpdateAt) { lbajardsilogic@0: updateAt *= 2; lbajardsilogic@0: if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: remainingEnd = m_fillFrom; lbajardsilogic@0: if (remainingEnd > start) --remainingEnd; lbajardsilogic@0: else remainingEnd = start; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t baseCompletion = m_completion; lbajardsilogic@0: lbajardsilogic@0: for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) { lbajardsilogic@0: lbajardsilogic@0: m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); lbajardsilogic@0: lbajardsilogic@0: if (m_server.m_exiting) return; lbajardsilogic@0: lbajardsilogic@0: while (m_server.m_suspended) { lbajardsilogic@0: #ifdef DEBUG_FFT_SERVER lbajardsilogic@0: std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: { lbajardsilogic@0: MutexLocker locker(&m_server.m_writeMutex, lbajardsilogic@0: "FFTDataServer::m_writeMutex[run/2]"); lbajardsilogic@0: m_server.m_condition.wait(&m_server.m_writeMutex, 10000); lbajardsilogic@0: } lbajardsilogic@0: if (m_server.m_exiting) return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (++counter == updateAt) { lbajardsilogic@0: m_extent = f; lbajardsilogic@0: m_completion = baseCompletion + lbajardsilogic@0: size_t(100 * fabsf(float(f - start) / lbajardsilogic@0: float(end - start))); lbajardsilogic@0: counter = 0; lbajardsilogic@0: if (updateAt < maxUpdateAt) { lbajardsilogic@0: updateAt *= 2; lbajardsilogic@0: if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_completion = 100; lbajardsilogic@0: m_extent = end; lbajardsilogic@0: } lbajardsilogic@0: