Chris@148: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@148: Chris@148: /* Chris@148: Sonic Visualiser Chris@148: An audio file viewer and annotation editor. Chris@148: Centre for Digital Music, Queen Mary, University of London. Chris@148: This file copyright 2006 Chris Cannam. Chris@148: Chris@148: This program is free software; you can redistribute it and/or Chris@148: modify it under the terms of the GNU General Public License as Chris@148: published by the Free Software Foundation; either version 2 of the Chris@148: License, or (at your option) any later version. See the file Chris@148: COPYING included with this distribution for more information. Chris@148: */ Chris@148: Chris@148: #include "FFTDataServer.h" Chris@148: Chris@148: #include "FFTFileCache.h" Chris@148: Chris@148: #include "model/DenseTimeValueModel.h" Chris@148: Chris@148: #include "base/System.h" Chris@148: Chris@148: //#define DEBUG_FFT_SERVER 1 Chris@148: //#define DEBUG_FFT_SERVER_FILL 1 Chris@148: Chris@148: #ifdef DEBUG_FFT_SERVER_FILL Chris@148: #define DEBUG_FFT_SERVER Chris@148: #endif Chris@148: Chris@148: FFTDataServer::ServerMap FFTDataServer::m_servers; Chris@148: QMutex FFTDataServer::m_serverMapMutex; Chris@148: Chris@148: FFTDataServer * Chris@148: FFTDataServer::getInstance(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@148: size_t fillFromColumn) Chris@148: { Chris@148: QString n = generateFileBasename(model, Chris@148: channel, Chris@148: windowType, Chris@148: windowSize, Chris@148: windowIncrement, Chris@148: fftSize, Chris@148: polar); Chris@148: Chris@148: FFTDataServer *server = 0; Chris@148: Chris@148: QMutexLocker locker(&m_serverMapMutex); Chris@148: Chris@148: if ((server = findServer(n))) { Chris@148: return server; Chris@148: } Chris@148: Chris@148: QString npn = generateFileBasename(model, Chris@148: channel, Chris@148: windowType, Chris@148: windowSize, Chris@148: windowIncrement, Chris@148: fftSize, Chris@148: !polar); Chris@148: Chris@148: if ((server = findServer(npn))) { Chris@148: return server; Chris@148: } Chris@148: Chris@148: m_servers[n] = ServerCountPair Chris@148: (new FFTDataServer(n, Chris@148: model, Chris@148: channel, Chris@148: windowType, Chris@148: windowSize, Chris@148: windowIncrement, Chris@148: fftSize, Chris@148: polar, Chris@148: fillFromColumn), Chris@148: 1); Chris@148: Chris@148: return m_servers[n].first; Chris@148: } Chris@148: Chris@148: FFTDataServer * Chris@148: FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@148: size_t fillFromColumn) Chris@148: { Chris@148: // Fuzzy matching: Chris@148: // Chris@148: // -- if we're asked for polar and have non-polar, use it (and Chris@148: // vice versa). This one is vital, and we do it for non-fuzzy as Chris@148: // well (above). Chris@148: // Chris@148: // -- if we're asked for an instance with a given fft size and we Chris@148: // have one already with a multiple of that fft size but the same Chris@148: // window size and type (and model), we can draw the results from Chris@148: // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the Chris@148: // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the Chris@148: // same window plus zero padding). Chris@148: // Chris@148: // -- if we're asked for an instance with a given window type and Chris@148: // size and fft size and we have one already the same but with a Chris@148: // smaller increment, we can draw the results from it (provided Chris@148: // our increment is a multiple of its) Chris@148: // Chris@148: // The FFTFuzzyAdapter knows how to interpret these things. In Chris@148: // both cases we require that the larger one is a power-of-two Chris@148: // multiple of the smaller (e.g. even though in principle you can Chris@148: // draw the results at increment 256 from those at increment 768 Chris@148: // or 1536, the fuzzy adapter doesn't support this). Chris@148: Chris@148: { Chris@148: QMutexLocker locker(&m_serverMapMutex); Chris@148: Chris@148: ServerMap::iterator best = m_servers.end(); Chris@148: int bestdist = -1; Chris@148: Chris@148: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { Chris@148: Chris@148: FFTDataServer *server = i->second.first; Chris@148: Chris@148: if (server->getModel() == model && Chris@148: (server->getChannel() == channel || model->getChannelCount() == 1) && Chris@148: server->getWindowType() == windowType && Chris@148: server->getWindowSize() == windowSize && Chris@148: server->getWindowIncrement() <= windowIncrement && Chris@148: server->getFFTSize() >= fftSize) { Chris@148: Chris@148: if ((windowIncrement % server->getWindowIncrement()) != 0) continue; Chris@148: int ratio = windowIncrement / server->getWindowIncrement(); Chris@148: bool poweroftwo = true; Chris@148: while (ratio > 1) { Chris@148: if (ratio & 0x1) { Chris@148: poweroftwo = false; Chris@148: break; Chris@148: } Chris@148: ratio >>= 1; Chris@148: } Chris@148: if (!poweroftwo) continue; Chris@148: Chris@148: if ((server->getFFTSize() % fftSize) != 0) continue; Chris@148: ratio = server->getFFTSize() / fftSize; Chris@148: while (ratio > 1) { Chris@148: if (ratio & 0x1) { Chris@148: poweroftwo = false; Chris@148: break; Chris@148: } Chris@148: ratio >>= 1; Chris@148: } Chris@148: if (!poweroftwo) continue; Chris@148: Chris@148: int distance = 0; Chris@148: Chris@148: if (server->getPolar() != polar) distance += 1; Chris@148: Chris@148: distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15; Chris@148: distance += ((server->getFFTSize() / fftSize) - 1) * 10; Chris@148: Chris@148: if (server->getFillCompletion() < 50) distance += 100; Chris@148: Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "Distance " << distance << ", best is " << bestdist << std::endl; Chris@148: #endif Chris@148: Chris@148: if (bestdist == -1 || distance < bestdist) { Chris@148: bestdist = distance; Chris@148: best = i; Chris@148: } Chris@148: } Chris@148: } Chris@148: Chris@148: if (bestdist >= 0) { Chris@148: ++best->second.second; Chris@148: return best->second.first; Chris@148: } Chris@148: } Chris@148: Chris@148: // Nothing found, make a new one Chris@148: Chris@148: return getInstance(model, Chris@148: channel, Chris@148: windowType, Chris@148: windowSize, Chris@148: windowIncrement, Chris@148: fftSize, Chris@148: polar, Chris@148: fillFromColumn); Chris@148: } Chris@148: Chris@148: FFTDataServer * Chris@148: FFTDataServer::findServer(QString n) Chris@148: { Chris@148: if (m_servers.find(n) != m_servers.end()) { Chris@148: ++m_servers[n].second; Chris@148: return m_servers[n].first; Chris@148: } Chris@148: Chris@148: return 0; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::releaseInstance(FFTDataServer *server) Chris@148: { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl; Chris@148: #endif Chris@148: Chris@148: QMutexLocker locker(&m_serverMapMutex); Chris@148: Chris@148: //!!! not a good strategy. Want something like: Chris@148: Chris@148: // -- if ref count > 0, decrement and return Chris@148: // -- if the instance hasn't been used at all, delete it immediately Chris@148: // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts, Chris@148: // leave them hanging around Chris@148: // -- if N instances with zero refcounts remain, delete the one that Chris@148: // was last released first Chris@148: // -- if we run out of disk space when allocating an instance, go back Chris@148: // and delete the spare N instances before trying again Chris@148: // -- have an additional method to indicate that a model has been Chris@148: // destroyed, so that we can delete all of its fft server instances Chris@148: Chris@148: // also: Chris@148: // Chris@148: Chris@148: for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { Chris@148: if (i->second.first == server) { Chris@148: if (i->second.second == 0) { Chris@148: std::cerr << "ERROR: FFTDataServer::releaseInstance(" Chris@148: << server << "): instance not allocated" << std::endl; Chris@148: } else if (--i->second.second == 0) { Chris@148: if (server->m_lastUsedCache == -1) { // never used Chris@148: delete server; Chris@148: m_servers.erase(i); Chris@148: } else { Chris@148: server->suspend(); Chris@148: purgeLimbo(); Chris@148: } Chris@148: } Chris@148: return; Chris@148: } Chris@148: } Chris@148: Chris@148: std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): " Chris@148: << "instance not found" << std::endl; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::purgeLimbo(int maxSize) Chris@148: { Chris@148: ServerMap::iterator i = m_servers.end(); Chris@148: Chris@148: int count = 0; Chris@148: Chris@148: while (i != m_servers.begin()) { Chris@148: --i; Chris@148: if (i->second.second == 0) { Chris@148: if (++count > maxSize) { Chris@148: delete i->second.first; Chris@148: m_servers.erase(i); Chris@148: return; Chris@148: } Chris@148: } Chris@148: } Chris@148: } Chris@148: Chris@148: FFTDataServer::FFTDataServer(QString fileBaseName, Chris@148: const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar, Chris@148: size_t fillFromColumn) : Chris@148: m_fileBaseName(fileBaseName), Chris@148: m_model(model), Chris@148: m_channel(channel), Chris@148: m_windower(windowType, windowSize), Chris@148: m_windowSize(windowSize), Chris@148: m_windowIncrement(windowIncrement), Chris@148: m_fftSize(fftSize), Chris@148: m_polar(polar), Chris@148: m_lastUsedCache(-1), Chris@148: m_fftInput(0), Chris@148: m_exiting(false), Chris@148: m_fillThread(0) Chris@148: { Chris@148: size_t start = m_model->getStartFrame(); Chris@148: size_t end = m_model->getEndFrame(); Chris@148: Chris@148: m_width = (end - start) / m_windowIncrement + 1; Chris@148: m_height = m_fftSize / 2; Chris@148: Chris@148: size_t maxCacheSize = 20 * 1024 * 1024; Chris@148: size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample); Chris@148: if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width; Chris@148: else m_cacheWidth = maxCacheSize / columnSize; Chris@148: Chris@148: int bits = 0; Chris@148: while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; } Chris@148: m_cacheWidth = 2; Chris@148: while (bits) { m_cacheWidth <<= 1; --bits; } Chris@148: Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl; Chris@148: #endif Chris@148: Chris@148: for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) { Chris@148: m_caches.push_back(0); Chris@148: } Chris@148: Chris@148: m_fftInput = (fftsample *) Chris@148: fftwf_malloc(fftSize * sizeof(fftsample)); Chris@148: Chris@148: m_fftOutput = (fftwf_complex *) Chris@148: fftwf_malloc(fftSize * sizeof(fftwf_complex)); Chris@148: Chris@148: m_workbuffer = (float *) Chris@148: fftwf_malloc(fftSize * sizeof(float)); Chris@148: Chris@148: m_fftPlan = fftwf_plan_dft_r2c_1d(m_fftSize, Chris@148: m_fftInput, Chris@148: m_fftOutput, Chris@148: FFTW_ESTIMATE); Chris@148: Chris@148: if (!m_fftPlan) { Chris@148: std::cerr << "ERROR: fftwf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl; Chris@148: throw(0); Chris@148: } Chris@148: Chris@148: m_fillThread = new FillThread(*this, fillFromColumn); Chris@148: Chris@148: //!!! respond appropriately when thread exits (deleteProcessingData etc) Chris@148: } Chris@148: Chris@148: FFTDataServer::~FFTDataServer() Chris@148: { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer(" << this << ")::~FFTDataServer()" << std::endl; Chris@148: #endif Chris@148: Chris@148: m_exiting = true; Chris@148: m_condition.wakeAll(); Chris@148: if (m_fillThread) { Chris@148: m_fillThread->wait(); Chris@148: delete m_fillThread; Chris@148: } Chris@148: Chris@148: QMutexLocker locker(&m_writeMutex); Chris@148: Chris@148: for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { Chris@148: delete *i; Chris@148: } Chris@148: Chris@148: deleteProcessingData(); Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::deleteProcessingData() Chris@148: { Chris@148: if (m_fftInput) { Chris@148: fftwf_destroy_plan(m_fftPlan); Chris@148: fftwf_free(m_fftInput); Chris@148: fftwf_free(m_fftOutput); Chris@148: fftwf_free(m_workbuffer); Chris@148: } Chris@148: m_fftInput = 0; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::suspend() Chris@148: { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer(" << this << "): suspend" << std::endl; Chris@148: #endif Chris@148: QMutexLocker locker(&m_writeMutex); Chris@148: m_suspended = true; Chris@148: for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { Chris@148: if (*i) (*i)->suspend(); Chris@148: } Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::resume() Chris@148: { Chris@148: m_suspended = false; Chris@148: m_condition.wakeAll(); Chris@148: } Chris@148: Chris@148: FFTCache * Chris@148: FFTDataServer::getCacheAux(size_t c) Chris@148: { Chris@148: QMutexLocker locker(&m_writeMutex); Chris@148: Chris@148: if (m_lastUsedCache == -1) { Chris@148: m_fillThread->start(); Chris@148: } Chris@148: Chris@148: if (int(c) != m_lastUsedCache) { Chris@148: Chris@148: // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl; Chris@148: Chris@148: for (IntQueue::iterator i = m_dormantCaches.begin(); Chris@148: i != m_dormantCaches.end(); ++i) { Chris@148: if (*i == c) { Chris@148: m_dormantCaches.erase(i); Chris@148: break; Chris@148: } Chris@148: } Chris@148: Chris@148: if (m_lastUsedCache >= 0) { Chris@148: bool inDormant = false; Chris@148: for (size_t i = 0; i < m_dormantCaches.size(); ++i) { Chris@148: if (m_dormantCaches[i] == m_lastUsedCache) { Chris@148: inDormant = true; Chris@148: break; Chris@148: } Chris@148: } Chris@148: if (!inDormant) { Chris@148: m_dormantCaches.push_back(m_lastUsedCache); Chris@148: } Chris@148: while (m_dormantCaches.size() > 4) { Chris@148: int dc = m_dormantCaches.front(); Chris@148: m_dormantCaches.pop_front(); Chris@148: m_caches[dc]->suspend(); Chris@148: } Chris@148: } Chris@148: } Chris@148: Chris@148: if (m_caches[c]) { Chris@148: m_lastUsedCache = c; Chris@148: return m_caches[c]; Chris@148: } Chris@148: Chris@148: QString name = QString("%1-%2").arg(m_fileBaseName).arg(c); Chris@148: Chris@148: FFTCache *cache = new FFTFileCache(name, MatrixFile::ReadWrite, Chris@148: m_polar ? FFTFileCache::Polar : Chris@148: FFTFileCache::Rectangular); Chris@148: Chris@148: size_t width = m_cacheWidth; Chris@148: if (c * m_cacheWidth + width > m_width) { Chris@148: width = m_width - c * m_cacheWidth; Chris@148: } Chris@148: Chris@148: cache->resize(width, m_height); Chris@148: cache->reset(); Chris@148: Chris@148: m_caches[c] = cache; Chris@148: m_lastUsedCache = c; Chris@148: Chris@148: return cache; Chris@148: } Chris@148: Chris@148: float Chris@148: FFTDataServer::getMagnitudeAt(size_t x, size_t y) Chris@148: { Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: if (!cache->haveSetColumnAt(col)) { Chris@148: fillColumn(x); Chris@148: } Chris@148: return cache->getMagnitudeAt(col, y); Chris@148: } Chris@148: Chris@148: float Chris@148: FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y) Chris@148: { Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: if (!cache->haveSetColumnAt(col)) { Chris@148: fillColumn(x); Chris@148: } Chris@148: return cache->getNormalizedMagnitudeAt(col, y); Chris@148: } Chris@148: Chris@148: float Chris@148: FFTDataServer::getMaximumMagnitudeAt(size_t x) Chris@148: { Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: if (!cache->haveSetColumnAt(col)) { Chris@148: fillColumn(x); Chris@148: } Chris@148: return cache->getMaximumMagnitudeAt(col); Chris@148: } Chris@148: Chris@148: float Chris@148: FFTDataServer::getPhaseAt(size_t x, size_t y) Chris@148: { Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: if (!cache->haveSetColumnAt(col)) { Chris@148: fillColumn(x); Chris@148: } Chris@148: return cache->getPhaseAt(col, y); Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary) Chris@148: { Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: if (!cache->haveSetColumnAt(col)) { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl; Chris@148: #endif Chris@148: fillColumn(x); Chris@148: } Chris@148: float magnitude = cache->getMagnitudeAt(col, y); Chris@148: float phase = cache->getPhaseAt(col, y); Chris@148: real = magnitude * cosf(phase); Chris@148: imaginary = magnitude * sinf(phase); Chris@148: } Chris@148: Chris@148: bool Chris@148: FFTDataServer::isColumnReady(size_t x) Chris@148: { Chris@148: if (!haveCache(x)) { Chris@148: if (m_lastUsedCache == -1) { Chris@148: m_fillThread->start(); Chris@148: } Chris@148: return false; Chris@148: } Chris@148: Chris@148: size_t col; Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: return cache->haveSetColumnAt(col); Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::fillColumn(size_t x) Chris@148: { Chris@148: size_t col; Chris@148: #ifdef DEBUG_FFT_SERVER_FILL Chris@148: std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl; Chris@148: #endif Chris@148: FFTCache *cache = getCache(x, col); Chris@148: Chris@148: QMutexLocker locker(&m_writeMutex); Chris@148: Chris@148: if (cache->haveSetColumnAt(col)) return; Chris@148: Chris@148: int startFrame = m_windowIncrement * x; Chris@148: int endFrame = startFrame + m_windowSize; Chris@148: Chris@148: startFrame -= int(m_windowSize - m_windowIncrement) / 2; Chris@148: endFrame -= int(m_windowSize - m_windowIncrement) / 2; Chris@148: size_t pfx = 0; Chris@148: Chris@148: size_t off = (m_fftSize - m_windowSize) / 2; Chris@148: Chris@148: for (size_t i = 0; i < off; ++i) { Chris@148: m_fftInput[i] = 0.0; Chris@148: m_fftInput[m_fftSize - i - 1] = 0.0; Chris@148: } Chris@148: Chris@148: if (startFrame < 0) { Chris@148: pfx = size_t(-startFrame); Chris@148: for (size_t i = 0; i < pfx; ++i) { Chris@148: m_fftInput[off + i] = 0.0; Chris@148: } Chris@148: } Chris@148: Chris@148: size_t got = m_model->getValues(m_channel, startFrame + pfx, Chris@148: endFrame, m_fftInput + off + pfx); Chris@148: Chris@148: while (got + pfx < m_windowSize) { Chris@148: m_fftInput[off + got + pfx] = 0.0; Chris@148: ++got; Chris@148: } Chris@148: Chris@148: if (m_channel == -1) { Chris@148: int channels = m_model->getChannelCount(); Chris@148: if (channels > 1) { Chris@148: for (size_t i = 0; i < m_windowSize; ++i) { Chris@148: m_fftInput[off + i] /= channels; Chris@148: } Chris@148: } Chris@148: } Chris@148: Chris@148: m_windower.cut(m_fftInput + off); Chris@148: Chris@148: for (size_t i = 0; i < m_fftSize/2; ++i) { Chris@148: fftsample temp = m_fftInput[i]; Chris@148: m_fftInput[i] = m_fftInput[i + m_fftSize/2]; Chris@148: m_fftInput[i + m_fftSize/2] = temp; Chris@148: } Chris@148: Chris@148: fftwf_execute(m_fftPlan); Chris@148: Chris@148: fftsample factor = 0.0; Chris@148: Chris@148: for (size_t i = 0; i < m_fftSize/2; ++i) { Chris@148: Chris@148: fftsample mag = sqrtf(m_fftOutput[i][0] * m_fftOutput[i][0] + Chris@148: m_fftOutput[i][1] * m_fftOutput[i][1]); Chris@148: mag /= m_windowSize / 2; Chris@148: Chris@148: if (mag > factor) factor = mag; Chris@148: Chris@148: fftsample phase = atan2f(m_fftOutput[i][1], m_fftOutput[i][0]); Chris@148: phase = princargf(phase); Chris@148: Chris@148: m_workbuffer[i] = mag; Chris@148: m_workbuffer[i + m_fftSize/2] = phase; Chris@148: } Chris@148: Chris@148: cache->setColumnAt(col, Chris@148: m_workbuffer, Chris@148: m_workbuffer + m_fftSize/2, Chris@148: factor); Chris@148: } Chris@148: Chris@148: size_t Chris@148: FFTDataServer::getFillCompletion() const Chris@148: { Chris@148: if (m_fillThread) return m_fillThread->getCompletion(); Chris@148: else return 100; Chris@148: } Chris@148: Chris@148: size_t Chris@148: FFTDataServer::getFillExtent() const Chris@148: { Chris@148: if (m_fillThread) return m_fillThread->getExtent(); Chris@148: else return m_model->getEndFrame(); Chris@148: } Chris@148: Chris@148: QString Chris@148: FFTDataServer::generateFileBasename() const Chris@148: { Chris@148: return generateFileBasename(m_model, m_channel, m_windower.getType(), Chris@148: m_windowSize, m_windowIncrement, m_fftSize, Chris@148: m_polar); Chris@148: } Chris@148: Chris@148: QString Chris@148: FFTDataServer::generateFileBasename(const DenseTimeValueModel *model, Chris@148: int channel, Chris@148: WindowType windowType, Chris@148: size_t windowSize, Chris@148: size_t windowIncrement, Chris@148: size_t fftSize, Chris@148: bool polar) Chris@148: { Chris@148: char buffer[200]; Chris@148: Chris@148: sprintf(buffer, "%u-%u-%u-%u-%u-%u%s", Chris@148: (unsigned int)XmlExportable::getObjectExportId(model), Chris@148: (unsigned int)(channel + 1), Chris@148: (unsigned int)windowType, Chris@148: (unsigned int)windowSize, Chris@148: (unsigned int)windowIncrement, Chris@148: (unsigned int)fftSize, Chris@148: polar ? "-p" : "-r"); Chris@148: Chris@148: return buffer; Chris@148: } Chris@148: Chris@148: void Chris@148: FFTDataServer::FillThread::run() Chris@148: { Chris@148: m_extent = 0; Chris@148: m_completion = 0; Chris@148: Chris@148: size_t start = m_server.m_model->getStartFrame(); Chris@148: size_t end = m_server.m_model->getEndFrame(); Chris@148: size_t remainingEnd = end; Chris@148: Chris@148: int counter = 0; Chris@148: int updateAt = (end / m_server.m_windowIncrement) / 20; Chris@148: if (updateAt < 100) updateAt = 100; Chris@148: Chris@148: if (m_fillFrom > start) { Chris@148: Chris@148: for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) { Chris@148: Chris@148: m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); Chris@148: Chris@148: if (m_server.m_exiting) return; Chris@148: Chris@148: while (m_server.m_suspended) { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer(" << this << "): suspended, waiting..." << std::endl; Chris@148: #endif Chris@148: m_server.m_writeMutex.lock(); Chris@148: m_server.m_condition.wait(&m_server.m_writeMutex, 10000); Chris@148: m_server.m_writeMutex.unlock(); Chris@148: if (m_server.m_exiting) return; Chris@148: } Chris@148: Chris@148: if (++counter == updateAt) { Chris@148: m_extent = f; Chris@148: m_completion = size_t(100 * fabsf(float(f - m_fillFrom) / Chris@148: float(end - start))); Chris@148: counter = 0; Chris@148: } Chris@148: } Chris@148: Chris@148: remainingEnd = m_fillFrom; Chris@148: if (remainingEnd > start) --remainingEnd; Chris@148: else remainingEnd = start; Chris@148: } Chris@148: Chris@148: size_t baseCompletion = m_completion; Chris@148: Chris@148: for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) { Chris@148: Chris@148: m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); Chris@148: Chris@148: if (m_server.m_exiting) return; Chris@148: Chris@148: while (m_server.m_suspended) { Chris@148: #ifdef DEBUG_FFT_SERVER Chris@148: std::cerr << "FFTDataServer(" << this << "): suspended, waiting..." << std::endl; Chris@148: #endif Chris@148: m_server.m_writeMutex.lock(); Chris@148: m_server.m_condition.wait(&m_server.m_writeMutex, 10000); Chris@148: m_server.m_writeMutex.unlock(); Chris@148: if (m_server.m_exiting) return; Chris@148: } Chris@148: Chris@148: if (++counter == updateAt) { Chris@148: m_extent = f; Chris@148: m_completion = baseCompletion + Chris@148: size_t(100 * fabsf(float(f - start) / Chris@148: float(end - start))); Chris@148: counter = 0; Chris@148: } Chris@148: } Chris@148: Chris@148: m_completion = 100; Chris@148: m_extent = end; Chris@148: } Chris@148: