svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2006 Chris Cannam and QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "FFTDataServer.h" 00017 00018 #include "FFTFileCacheReader.h" 00019 #include "FFTFileCacheWriter.h" 00020 #include "FFTMemoryCache.h" 00021 00022 #include "model/DenseTimeValueModel.h" 00023 00024 #include "system/System.h" 00025 00026 #include "base/StorageAdviser.h" 00027 #include "base/Exceptions.h" 00028 #include "base/Profiler.h" 00029 #include "base/Thread.h" // for debug mutex locker 00030 00031 #include <QWriteLocker> 00032 00033 //#define DEBUG_FFT_SERVER 1 00034 //#define DEBUG_FFT_SERVER_FILL 1 00035 00036 #ifdef DEBUG_FFT_SERVER_FILL 00037 #ifndef DEBUG_FFT_SERVER 00038 #define DEBUG_FFT_SERVER 1 00039 #endif 00040 #endif 00041 00042 00043 FFTDataServer::ServerMap FFTDataServer::m_servers; 00044 FFTDataServer::ServerQueue FFTDataServer::m_releasedServers; 00045 QMutex FFTDataServer::m_serverMapMutex; 00046 00047 FFTDataServer * 00048 FFTDataServer::getInstance(const DenseTimeValueModel *model, 00049 int channel, 00050 WindowType windowType, 00051 int windowSize, 00052 int windowIncrement, 00053 int fftSize, 00054 bool polar, 00055 StorageAdviser::Criteria criteria, 00056 int fillFromColumn) 00057 { 00058 QString n = generateFileBasename(model, 00059 channel, 00060 windowType, 00061 windowSize, 00062 windowIncrement, 00063 fftSize, 00064 polar); 00065 00066 FFTDataServer *server = 0; 00067 00068 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getInstance::m_serverMapMutex"); 00069 00070 if ((server = findServer(n))) { 00071 return server; 00072 } 00073 00074 QString npn = generateFileBasename(model, 00075 channel, 00076 windowType, 00077 windowSize, 00078 windowIncrement, 00079 fftSize, 00080 !polar); 00081 00082 if ((server = findServer(npn))) { 00083 return server; 00084 } 00085 00086 try { 00087 server = new FFTDataServer(n, 00088 model, 00089 channel, 00090 windowType, 00091 windowSize, 00092 windowIncrement, 00093 fftSize, 00094 polar, 00095 criteria, 00096 fillFromColumn); 00097 } catch (InsufficientDiscSpace) { 00098 delete server; 00099 server = 0; 00100 } 00101 00102 if (server) { 00103 m_servers[n] = ServerCountPair(server, 1); 00104 } 00105 00106 return server; 00107 } 00108 00109 FFTDataServer * 00110 FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model, 00111 int channel, 00112 WindowType windowType, 00113 int windowSize, 00114 int windowIncrement, 00115 int fftSize, 00116 bool polar, 00117 StorageAdviser::Criteria criteria, 00118 int fillFromColumn) 00119 { 00120 // Fuzzy matching: 00121 // 00122 // -- if we're asked for polar and have non-polar, use it (and 00123 // vice versa). This one is vital, and we do it for non-fuzzy as 00124 // well (above). 00125 // 00126 // -- if we're asked for an instance with a given fft size and we 00127 // have one already with a multiple of that fft size but the same 00128 // window size and type (and model), we can draw the results from 00129 // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the 00130 // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the 00131 // same window plus zero padding). 00132 // 00133 // -- if we're asked for an instance with a given window type and 00134 // size and fft size and we have one already the same but with a 00135 // smaller increment, we can draw the results from it (provided 00136 // our increment is a multiple of its) 00137 // 00138 // The FFTModel knows how to interpret these things. In 00139 // both cases we require that the larger one is a power-of-two 00140 // multiple of the smaller (e.g. even though in principle you can 00141 // draw the results at increment 256 from those at increment 768 00142 // or 1536, the model doesn't support this). 00143 00144 { 00145 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getFuzzyInstance::m_serverMapMutex"); 00146 00147 ServerMap::iterator best = m_servers.end(); 00148 int bestdist = -1; 00149 00150 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { 00151 00152 FFTDataServer *server = i->second.first; 00153 00154 if (server->getModel() == model && 00155 (server->getChannel() == channel || model->getChannelCount() == 1) && 00156 server->getWindowType() == windowType && 00157 server->getWindowSize() == windowSize && 00158 server->getWindowIncrement() <= windowIncrement && 00159 server->getFFTSize() >= fftSize) { 00160 00161 if ((windowIncrement % server->getWindowIncrement()) != 0) continue; 00162 int ratio = windowIncrement / server->getWindowIncrement(); 00163 bool poweroftwo = true; 00164 while (ratio > 1) { 00165 if (ratio & 0x1) { 00166 poweroftwo = false; 00167 break; 00168 } 00169 ratio >>= 1; 00170 } 00171 if (!poweroftwo) continue; 00172 00173 if ((server->getFFTSize() % fftSize) != 0) continue; 00174 ratio = server->getFFTSize() / fftSize; 00175 while (ratio > 1) { 00176 if (ratio & 0x1) { 00177 poweroftwo = false; 00178 break; 00179 } 00180 ratio >>= 1; 00181 } 00182 if (!poweroftwo) continue; 00183 00184 int distance = 0; 00185 00186 if (server->getPolar() != polar) distance += 1; 00187 00188 distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15; 00189 distance += ((server->getFFTSize() / fftSize) - 1) * 10; 00190 00191 if (server->getFillCompletion() < 50) distance += 100; 00192 00193 #ifdef DEBUG_FFT_SERVER 00194 std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl; 00195 #endif 00196 00197 if (bestdist == -1 || distance < bestdist) { 00198 bestdist = distance; 00199 best = i; 00200 } 00201 } 00202 } 00203 00204 if (bestdist >= 0) { 00205 FFTDataServer *server = best->second.first; 00206 #ifdef DEBUG_FFT_SERVER 00207 std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl; 00208 #endif 00209 claimInstance(server, false); 00210 return server; 00211 } 00212 } 00213 00214 // Nothing found, make a new one 00215 00216 return getInstance(model, 00217 channel, 00218 windowType, 00219 windowSize, 00220 windowIncrement, 00221 fftSize, 00222 polar, 00223 criteria, 00224 fillFromColumn); 00225 } 00226 00227 FFTDataServer * 00228 FFTDataServer::findServer(QString n) 00229 { 00230 #ifdef DEBUG_FFT_SERVER 00231 std::cerr << "FFTDataServer::findServer(\"" << n << "\")" << std::endl; 00232 #endif 00233 00234 if (m_servers.find(n) != m_servers.end()) { 00235 00236 FFTDataServer *server = m_servers[n].first; 00237 00238 #ifdef DEBUG_FFT_SERVER 00239 std::cerr << "FFTDataServer::findServer(\"" << n << "\"): found " << server << std::endl; 00240 #endif 00241 00242 claimInstance(server, false); 00243 00244 return server; 00245 } 00246 00247 #ifdef DEBUG_FFT_SERVER 00248 std::cerr << "FFTDataServer::findServer(\"" << n << "\"): not found" << std::endl; 00249 #endif 00250 00251 return 0; 00252 } 00253 00254 void 00255 FFTDataServer::claimInstance(FFTDataServer *server) 00256 { 00257 claimInstance(server, true); 00258 } 00259 00260 void 00261 FFTDataServer::claimInstance(FFTDataServer *server, bool needLock) 00262 { 00263 MutexLocker locker(needLock ? &m_serverMapMutex : 0, 00264 "FFTDataServer::claimInstance::m_serverMapMutex"); 00265 00266 #ifdef DEBUG_FFT_SERVER 00267 std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl; 00268 #endif 00269 00270 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { 00271 if (i->second.first == server) { 00272 00273 for (ServerQueue::iterator j = m_releasedServers.begin(); 00274 j != m_releasedServers.end(); ++j) { 00275 00276 if (*j == server) { 00277 #ifdef DEBUG_FFT_SERVER 00278 std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl; 00279 #endif 00280 m_releasedServers.erase(j); 00281 break; 00282 } 00283 } 00284 00285 ++i->second.second; 00286 00287 #ifdef DEBUG_FFT_SERVER 00288 std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl; 00289 #endif 00290 00291 return; 00292 } 00293 } 00294 00295 cerr << "ERROR: FFTDataServer::claimInstance: instance " 00296 << server << " unknown!" << endl; 00297 } 00298 00299 void 00300 FFTDataServer::releaseInstance(FFTDataServer *server) 00301 { 00302 releaseInstance(server, true); 00303 } 00304 00305 void 00306 FFTDataServer::releaseInstance(FFTDataServer *server, bool needLock) 00307 { 00308 MutexLocker locker(needLock ? &m_serverMapMutex : 0, 00309 "FFTDataServer::releaseInstance::m_serverMapMutex"); 00310 00311 #ifdef DEBUG_FFT_SERVER 00312 std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl; 00313 #endif 00314 00315 // -- if ref count > 0, decrement and return 00316 // -- if the instance hasn't been used at all, delete it immediately 00317 // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts, 00318 // leave them hanging around 00319 // -- if N instances with zero refcounts remain, delete the one that 00320 // was last released first 00321 // -- if we run out of disk space when allocating an instance, go back 00322 // and delete the spare N instances before trying again 00323 // -- have an additional method to indicate that a model has been 00324 // destroyed, so that we can delete all of its fft server instances 00325 00326 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { 00327 if (i->second.first == server) { 00328 if (i->second.second == 0) { 00329 cerr << "ERROR: FFTDataServer::releaseInstance(" 00330 << server << "): instance not allocated" << endl; 00331 } else if (--i->second.second == 0) { 00343 #ifdef DEBUG_FFT_SERVER 00344 std::cerr << "FFTDataServer::releaseInstance: instance " 00345 << server << " no longer in use, marking for possible collection" 00346 << std::endl; 00347 #endif 00348 bool found = false; 00349 for (ServerQueue::iterator j = m_releasedServers.begin(); 00350 j != m_releasedServers.end(); ++j) { 00351 if (*j == server) { 00352 cerr << "ERROR: FFTDataServer::releaseInstance(" 00353 << server << "): server is already in " 00354 << "released servers list" << endl; 00355 found = true; 00356 } 00357 } 00358 if (!found) m_releasedServers.push_back(server); 00359 server->suspend(); 00360 purgeLimbo(); 00362 } else { 00363 #ifdef DEBUG_FFT_SERVER 00364 std::cerr << "FFTDataServer::releaseInstance: instance " 00365 << server << " now has refcount " << i->second.second 00366 << std::endl; 00367 #endif 00368 } 00369 return; 00370 } 00371 } 00372 00373 cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): " 00374 << "instance not found" << endl; 00375 } 00376 00377 void 00378 FFTDataServer::purgeLimbo(int maxSize) 00379 { 00380 #ifdef DEBUG_FFT_SERVER 00381 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " 00382 << m_releasedServers.size() << " candidates" << std::endl; 00383 #endif 00384 00385 while (int(m_releasedServers.size()) > maxSize) { 00386 00387 FFTDataServer *server = *m_releasedServers.begin(); 00388 00389 bool found = false; 00390 00391 #ifdef DEBUG_FFT_SERVER 00392 std::cerr << "FFTDataServer::purgeLimbo: considering candidate " 00393 << server << std::endl; 00394 #endif 00395 00396 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { 00397 00398 if (i->second.first == server) { 00399 found = true; 00400 if (i->second.second > 0) { 00401 cerr << "ERROR: FFTDataServer::purgeLimbo: Server " 00402 << server << " is in released queue, but still has non-zero refcount " 00403 << i->second.second << endl; 00404 // ... so don't delete it 00405 break; 00406 } 00407 #ifdef DEBUG_FFT_SERVER 00408 std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it" 00409 << std::endl; 00410 #endif 00411 00412 m_servers.erase(i); 00413 delete server; 00414 break; 00415 } 00416 } 00417 00418 if (!found) { 00419 cerr << "ERROR: FFTDataServer::purgeLimbo: Server " 00420 << server << " is in released queue, but not in server map!" 00421 << endl; 00422 delete server; 00423 } 00424 00425 m_releasedServers.pop_front(); 00426 } 00427 00428 #ifdef DEBUG_FFT_SERVER 00429 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): " 00430 << m_releasedServers.size() << " remain" << std::endl; 00431 #endif 00432 00433 } 00434 00435 void 00436 FFTDataServer::modelAboutToBeDeleted(Model *model) 00437 { 00438 MutexLocker locker(&m_serverMapMutex, 00439 "FFTDataServer::modelAboutToBeDeleted::m_serverMapMutex"); 00440 00441 #ifdef DEBUG_FFT_SERVER 00442 std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")" 00443 << std::endl; 00444 #endif 00445 00446 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) { 00447 00448 FFTDataServer *server = i->second.first; 00449 00450 if (server->getModel() == model) { 00451 00452 #ifdef DEBUG_FFT_SERVER 00453 std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is " 00454 << server << std::endl; 00455 #endif 00456 00457 if (i->second.second > 0) { 00458 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; 00459 server->suspendWrites(); 00460 return; 00461 } 00462 for (ServerQueue::iterator j = m_releasedServers.begin(); 00463 j != m_releasedServers.end(); ++j) { 00464 if (*j == server) { 00465 #ifdef DEBUG_FFT_SERVER 00466 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl; 00467 #endif 00468 m_releasedServers.erase(j); 00469 break; 00470 } 00471 } 00472 #ifdef DEBUG_FFT_SERVER 00473 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl; 00474 #endif 00475 m_servers.erase(i); 00476 delete server; 00477 return; 00478 } 00479 } 00480 } 00481 00482 FFTDataServer::FFTDataServer(QString fileBaseName, 00483 const DenseTimeValueModel *model, 00484 int channel, 00485 WindowType windowType, 00486 int windowSize, 00487 int windowIncrement, 00488 int fftSize, 00489 bool polar, 00490 StorageAdviser::Criteria criteria, 00491 int fillFromColumn) : 00492 m_fileBaseName(fileBaseName), 00493 m_model(model), 00494 m_channel(channel), 00495 m_windower(windowType, windowSize), 00496 m_windowSize(windowSize), 00497 m_windowIncrement(windowIncrement), 00498 m_fftSize(fftSize), 00499 m_polar(polar), 00500 m_width(0), 00501 m_height(0), 00502 m_cacheWidth(0), 00503 m_cacheWidthPower(0), 00504 m_cacheWidthMask(0), 00505 m_criteria(criteria), 00506 m_fftInput(0), 00507 m_exiting(false), 00508 m_suspended(true), 00509 m_fillThread(0) 00510 { 00511 #ifdef DEBUG_FFT_SERVER 00512 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << endl; 00513 #endif 00514 00516 00517 int start = m_model->getStartFrame(); 00518 int end = m_model->getEndFrame(); 00519 00520 m_width = (end - start) / m_windowIncrement + 1; 00521 m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2 00522 00523 #ifdef DEBUG_FFT_SERVER 00524 cerr << "FFTDataServer(" << this << "): dimensions are " 00525 << m_width << "x" << m_height << endl; 00526 #endif 00527 00528 int maxCacheSize = 20 * 1024 * 1024; 00529 int columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample); 00530 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width; 00531 else m_cacheWidth = maxCacheSize / columnSize; 00532 00533 #ifdef DEBUG_FFT_SERVER 00534 cerr << "FFTDataServer(" << this << "): cache width nominal " 00535 << m_cacheWidth << ", actual "; 00536 #endif 00537 00538 int bits = 0; 00539 while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; } 00540 m_cacheWidthPower = bits + 1; 00541 m_cacheWidth = 2; 00542 while (bits) { m_cacheWidth <<= 1; --bits; } 00543 m_cacheWidthMask = m_cacheWidth - 1; 00544 00545 #ifdef DEBUG_FFT_SERVER 00546 cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask " 00547 << m_cacheWidthMask << ")" << endl; 00548 #endif 00549 00550 if (m_criteria == StorageAdviser::NoCriteria) { 00551 00552 // assume "spectrogram" criteria for polar ffts, and "feature 00553 // extraction" criteria for rectangular ones. 00554 00555 if (m_polar) { 00556 m_criteria = StorageAdviser::Criteria 00557 (StorageAdviser::SpeedCritical | 00558 StorageAdviser::LongRetentionLikely); 00559 } else { 00560 m_criteria = StorageAdviser::Criteria 00561 (StorageAdviser::PrecisionCritical); 00562 } 00563 } 00564 00565 for (int i = 0; i <= m_width / m_cacheWidth; ++i) { 00566 m_caches.push_back(0); 00567 } 00568 00569 m_fftInput = (fftsample *) 00570 fftf_malloc(fftSize * sizeof(fftsample)); 00571 00572 m_fftOutput = (fftf_complex *) 00573 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex)); 00574 00575 m_workbuffer = (float *) 00576 fftf_malloc((fftSize+2) * sizeof(float)); 00577 00578 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize, 00579 m_fftInput, 00580 m_fftOutput, 00581 FFTW_MEASURE); 00582 00583 if (!m_fftPlan) { 00584 cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << endl; 00585 throw(0); 00586 } 00587 00588 m_fillThread = new FillThread(*this, fillFromColumn); 00589 } 00590 00591 FFTDataServer::~FFTDataServer() 00592 { 00593 #ifdef DEBUG_FFT_SERVER 00594 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << endl; 00595 #endif 00596 00597 m_suspended = false; 00598 m_exiting = true; 00599 m_condition.wakeAll(); 00600 if (m_fillThread) { 00601 m_fillThread->wait(); 00602 delete m_fillThread; 00603 } 00604 00605 // MutexLocker locker(&m_writeMutex, 00606 // "FFTDataServer::~FFTDataServer::m_writeMutex"); 00607 00608 QMutexLocker mlocker(&m_fftBuffersLock); 00609 QWriteLocker wlocker(&m_cacheVectorLock); 00610 00611 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { 00612 if (*i) { 00613 delete *i; 00614 } 00615 } 00616 00617 deleteProcessingData(); 00618 } 00619 00620 void 00621 FFTDataServer::deleteProcessingData() 00622 { 00623 #ifdef DEBUG_FFT_SERVER 00624 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << endl; 00625 #endif 00626 if (m_fftInput) { 00627 fftf_destroy_plan(m_fftPlan); 00628 fftf_free(m_fftInput); 00629 fftf_free(m_fftOutput); 00630 fftf_free(m_workbuffer); 00631 } 00632 m_fftInput = 0; 00633 } 00634 00635 void 00636 FFTDataServer::suspend() 00637 { 00638 #ifdef DEBUG_FFT_SERVER 00639 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << endl; 00640 #endif 00641 Profiler profiler("FFTDataServer::suspend", false); 00642 00643 QMutexLocker locker(&m_fftBuffersLock); 00644 m_suspended = true; 00645 } 00646 00647 void 00648 FFTDataServer::suspendWrites() 00649 { 00650 #ifdef DEBUG_FFT_SERVER 00651 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << endl; 00652 #endif 00653 Profiler profiler("FFTDataServer::suspendWrites", false); 00654 00655 m_suspended = true; 00656 } 00657 00658 void 00659 FFTDataServer::resume() 00660 { 00661 #ifdef DEBUG_FFT_SERVER 00662 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << endl; 00663 #endif 00664 Profiler profiler("FFTDataServer::resume", false); 00665 00666 m_suspended = false; 00667 if (m_fillThread) { 00668 if (m_fillThread->isFinished()) { 00669 delete m_fillThread; 00670 m_fillThread = 0; 00671 deleteProcessingData(); 00672 } else if (!m_fillThread->isRunning()) { 00673 m_fillThread->start(); 00674 } else { 00675 m_condition.wakeAll(); 00676 } 00677 } 00678 } 00679 00680 void 00681 FFTDataServer::getStorageAdvice(int w, int h, 00682 bool &memoryCache, bool &compactCache) 00683 { 00684 int cells = w * h; 00685 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb 00686 int maximumSize = (cells / 1024) * sizeof(float); // kb 00687 00688 // We don't have a compact rectangular representation, and compact 00689 // of course is never precision-critical 00690 00691 bool canCompact = true; 00692 if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) { 00693 canCompact = false; 00694 minimumSize = maximumSize; // don't use compact 00695 } 00696 00697 StorageAdviser::Recommendation recommendation; 00698 00699 try { 00700 00701 recommendation = 00702 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); 00703 00704 } catch (InsufficientDiscSpace s) { 00705 00706 // Delete any unused servers we may have been leaving around 00707 // in case we wanted them again 00708 00709 purgeLimbo(0); 00710 00711 // This time we don't catch InsufficientDiscSpace -- we 00712 // haven't allocated anything yet and can safely let the 00713 // exception out to indicate to the caller that we can't 00714 // handle it. 00715 00716 recommendation = 00717 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); 00718 } 00719 00720 // cerr << "Recommendation was: " << recommendation << endl; 00721 00722 memoryCache = false; 00723 00724 if ((recommendation & StorageAdviser::UseMemory) || 00725 (recommendation & StorageAdviser::PreferMemory)) { 00726 memoryCache = true; 00727 } 00728 00729 compactCache = canCompact && 00730 (recommendation & StorageAdviser::ConserveSpace); 00731 00732 #ifdef DEBUG_FFT_SERVER 00733 cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << endl; 00734 00735 cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << endl; 00736 #endif 00737 } 00738 00739 bool 00740 FFTDataServer::makeCache(int c) 00741 { 00742 // Creating the cache could take a significant amount of time. We 00743 // don't want to block readers on m_cacheVectorLock while this is 00744 // happening, but we do want to block any further calls to 00745 // makeCache. So we use this lock solely to serialise this 00746 // particular function -- it isn't used anywhere else. 00747 00748 QMutexLocker locker(&m_cacheCreationMutex); 00749 00750 m_cacheVectorLock.lockForRead(); 00751 if (m_caches[c]) { 00752 // someone else must have created the cache between our 00753 // testing for it and taking the mutex 00754 m_cacheVectorLock.unlock(); 00755 return true; 00756 } 00757 m_cacheVectorLock.unlock(); 00758 00759 // Now m_cacheCreationMutex is held, but m_cacheVectorLock is not 00760 // -- readers can proceed, but callers to this function will block 00761 00762 CacheBlock *cb = new CacheBlock; 00763 00764 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c); 00765 00766 int width = m_cacheWidth; 00767 if (c * m_cacheWidth + width > m_width) { 00768 width = m_width - c * m_cacheWidth; 00769 } 00770 00771 bool memoryCache = false; 00772 bool compactCache = false; 00773 00774 getStorageAdvice(width, m_height, memoryCache, compactCache); 00775 00776 bool success = false; 00777 00778 if (memoryCache) { 00779 00780 try { 00781 00782 cb->memoryCache = new FFTMemoryCache 00783 (compactCache ? FFTCache::Compact : 00784 m_polar ? FFTCache::Polar : 00785 FFTCache::Rectangular, 00786 width, m_height); 00787 00788 success = true; 00789 00790 } catch (std::bad_alloc) { 00791 00792 delete cb->memoryCache; 00793 cb->memoryCache = 0; 00794 00795 cerr << "WARNING: Memory allocation failed when creating" 00796 << " FFT memory cache no. " << c << " of " << width 00797 << "x" << m_height << " (of total width " << m_width 00798 << "): falling back to disc cache" << endl; 00799 00800 memoryCache = false; 00801 } 00802 } 00803 00804 if (!memoryCache) { 00805 00806 try { 00807 00808 cb->fileCacheWriter = new FFTFileCacheWriter 00809 (name, 00810 compactCache ? FFTCache::Compact : 00811 m_polar ? FFTCache::Polar : 00812 FFTCache::Rectangular, 00813 width, m_height); 00814 00815 success = true; 00816 00817 } catch (std::exception &e) { 00818 00819 delete cb->fileCacheWriter; 00820 cb->fileCacheWriter = 0; 00821 00822 cerr << "ERROR: Failed to construct disc cache for FFT data: " 00823 << e.what() << endl; 00824 00825 throw; 00826 } 00827 } 00828 00829 m_cacheVectorLock.lockForWrite(); 00830 00831 m_caches[c] = cb; 00832 00833 m_cacheVectorLock.unlock(); 00834 00835 return success; 00836 } 00837 00838 bool 00839 FFTDataServer::makeCacheReader(int c) 00840 { 00841 // preconditions: m_caches[c] exists and contains a file writer; 00842 // m_cacheVectorLock is not locked by this thread 00843 #ifdef DEBUG_FFT_SERVER 00844 std::cerr << "FFTDataServer::makeCacheReader(" << c << ")" << std::endl; 00845 #endif 00846 00847 QThread *me = QThread::currentThread(); 00848 QWriteLocker locker(&m_cacheVectorLock); 00849 CacheBlock *cb(m_caches.at(c)); 00850 if (!cb || !cb->fileCacheWriter) return false; 00851 00852 try { 00853 00854 cb->fileCacheReader[me] = new FFTFileCacheReader(cb->fileCacheWriter); 00855 00856 } catch (std::exception &e) { 00857 00858 delete cb->fileCacheReader[me]; 00859 cb->fileCacheReader.erase(me); 00860 00861 cerr << "ERROR: Failed to construct disc cache reader for FFT data: " 00862 << e.what() << endl; 00863 return false; 00864 } 00865 00866 // erase a reader that looks like it may no longer going to be 00867 // used by this thread for a while (leaving alone the current 00868 // and previous cache readers) 00869 int deleteCandidate = c - 2; 00870 if (deleteCandidate < 0) deleteCandidate = c + 2; 00871 if (deleteCandidate >= (int)m_caches.size()) { 00872 return true; 00873 } 00874 00875 cb = m_caches.at(deleteCandidate); 00876 if (cb && cb->fileCacheReader.find(me) != cb->fileCacheReader.end()) { 00877 #ifdef DEBUG_FFT_SERVER 00878 std::cerr << "FFTDataServer::makeCacheReader: Deleting probably unpopular reader " << deleteCandidate << " for this thread (as I create reader " << c << ")" << std::endl; 00879 #endif 00880 delete cb->fileCacheReader[me]; 00881 cb->fileCacheReader.erase(me); 00882 } 00883 00884 return true; 00885 } 00886 00887 float 00888 FFTDataServer::getMagnitudeAt(int x, int y) 00889 { 00890 Profiler profiler("FFTDataServer::getMagnitudeAt", false); 00891 00892 if (x >= m_width || y >= m_height) return 0; 00893 00894 float val = 0; 00895 00896 try { 00897 int col; 00898 FFTCacheReader *cache = getCacheReader(x, col); 00899 if (!cache) return 0; 00900 00901 if (!cache->haveSetColumnAt(col)) { 00902 Profiler profiler("FFTDataServer::getMagnitudeAt: filling"); 00903 #ifdef DEBUG_FFT_SERVER 00904 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn(" 00905 << x << ")" << std::endl; 00906 #endif 00907 fillColumn(x); 00908 } 00909 00910 val = cache->getMagnitudeAt(col, y); 00911 00912 } catch (std::exception &e) { 00913 m_error = e.what(); 00914 } 00915 00916 return val; 00917 } 00918 00919 bool 00920 FFTDataServer::getMagnitudesAt(int x, float *values, int minbin, int count, int step) 00921 { 00922 Profiler profiler("FFTDataServer::getMagnitudesAt", false); 00923 00924 if (x >= m_width) return false; 00925 00926 if (minbin >= m_height) minbin = m_height - 1; 00927 if (count == 0) count = (m_height - minbin) / step; 00928 else if (minbin + count * step > m_height) { 00929 count = (m_height - minbin) / step; 00930 } 00931 00932 try { 00933 int col; 00934 FFTCacheReader *cache = getCacheReader(x, col); 00935 if (!cache) return false; 00936 00937 if (!cache->haveSetColumnAt(col)) { 00938 Profiler profiler("FFTDataServer::getMagnitudesAt: filling"); 00939 fillColumn(x); 00940 } 00941 00942 cache->getMagnitudesAt(col, values, minbin, count, step); 00943 00944 } catch (std::exception &e) { 00945 m_error = e.what(); 00946 return false; 00947 } 00948 00949 return true; 00950 } 00951 00952 float 00953 FFTDataServer::getNormalizedMagnitudeAt(int x, int y) 00954 { 00955 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false); 00956 00957 if (x >= m_width || y >= m_height) return 0; 00958 00959 float val = 0; 00960 00961 try { 00962 00963 int col; 00964 FFTCacheReader *cache = getCacheReader(x, col); 00965 if (!cache) return 0; 00966 00967 if (!cache->haveSetColumnAt(col)) { 00968 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling"); 00969 fillColumn(x); 00970 } 00971 val = cache->getNormalizedMagnitudeAt(col, y); 00972 00973 } catch (std::exception &e) { 00974 m_error = e.what(); 00975 } 00976 00977 return val; 00978 } 00979 00980 bool 00981 FFTDataServer::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count, int step) 00982 { 00983 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false); 00984 00985 if (x >= m_width) return false; 00986 00987 if (minbin >= m_height) minbin = m_height - 1; 00988 if (count == 0) count = (m_height - minbin) / step; 00989 else if (minbin + count * step > m_height) { 00990 count = (m_height - minbin) / step; 00991 } 00992 00993 try { 00994 00995 int col; 00996 FFTCacheReader *cache = getCacheReader(x, col); 00997 if (!cache) return false; 00998 00999 if (!cache->haveSetColumnAt(col)) { 01000 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling"); 01001 fillColumn(x); 01002 } 01003 01004 for (int i = 0; i < count; ++i) { 01005 values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin); 01006 } 01007 01008 } catch (std::exception &e) { 01009 m_error = e.what(); 01010 return false; 01011 } 01012 01013 return true; 01014 } 01015 01016 float 01017 FFTDataServer::getMaximumMagnitudeAt(int x) 01018 { 01019 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false); 01020 01021 if (x >= m_width) return 0; 01022 01023 float val = 0; 01024 01025 try { 01026 01027 int col; 01028 FFTCacheReader *cache = getCacheReader(x, col); 01029 if (!cache) return 0; 01030 01031 if (!cache->haveSetColumnAt(col)) { 01032 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling"); 01033 fillColumn(x); 01034 } 01035 val = cache->getMaximumMagnitudeAt(col); 01036 01037 } catch (std::exception &e) { 01038 m_error = e.what(); 01039 } 01040 01041 return val; 01042 } 01043 01044 float 01045 FFTDataServer::getPhaseAt(int x, int y) 01046 { 01047 Profiler profiler("FFTDataServer::getPhaseAt", false); 01048 01049 if (x >= m_width || y >= m_height) return 0; 01050 01051 float val = 0; 01052 01053 try { 01054 01055 int col; 01056 FFTCacheReader *cache = getCacheReader(x, col); 01057 if (!cache) return 0; 01058 01059 if (!cache->haveSetColumnAt(col)) { 01060 Profiler profiler("FFTDataServer::getPhaseAt: filling"); 01061 fillColumn(x); 01062 } 01063 val = cache->getPhaseAt(col, y); 01064 01065 } catch (std::exception &e) { 01066 m_error = e.what(); 01067 } 01068 01069 return val; 01070 } 01071 01072 bool 01073 FFTDataServer::getPhasesAt(int x, float *values, int minbin, int count, int step) 01074 { 01075 Profiler profiler("FFTDataServer::getPhasesAt", false); 01076 01077 if (x >= m_width) return false; 01078 01079 if (minbin >= m_height) minbin = m_height - 1; 01080 if (count == 0) count = (m_height - minbin) / step; 01081 else if (minbin + count * step > m_height) { 01082 count = (m_height - minbin) / step; 01083 } 01084 01085 try { 01086 01087 int col; 01088 FFTCacheReader *cache = getCacheReader(x, col); 01089 if (!cache) return false; 01090 01091 if (!cache->haveSetColumnAt(col)) { 01092 Profiler profiler("FFTDataServer::getPhasesAt: filling"); 01093 fillColumn(x); 01094 } 01095 01096 for (int i = 0; i < count; ++i) { 01097 values[i] = cache->getPhaseAt(col, i * step + minbin); 01098 } 01099 01100 } catch (std::exception &e) { 01101 m_error = e.what(); 01102 return false; 01103 } 01104 01105 return true; 01106 } 01107 01108 void 01109 FFTDataServer::getValuesAt(int x, int y, float &real, float &imaginary) 01110 { 01111 Profiler profiler("FFTDataServer::getValuesAt", false); 01112 01113 if (x >= m_width || y >= m_height) { 01114 real = 0; 01115 imaginary = 0; 01116 return; 01117 } 01118 01119 try { 01120 01121 int col; 01122 FFTCacheReader *cache = getCacheReader(x, col); 01123 01124 if (!cache) { 01125 real = 0; 01126 imaginary = 0; 01127 return; 01128 } 01129 01130 if (!cache->haveSetColumnAt(col)) { 01131 Profiler profiler("FFTDataServer::getValuesAt: filling"); 01132 #ifdef DEBUG_FFT_SERVER 01133 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl; 01134 #endif 01135 fillColumn(x); 01136 } 01137 01138 cache->getValuesAt(col, y, real, imaginary); 01139 01140 } catch (std::exception &e) { 01141 m_error = e.what(); 01142 } 01143 } 01144 01145 bool 01146 FFTDataServer::getValuesAt(int x, float *reals, float *imaginaries, int minbin, int count, int step) 01147 { 01148 Profiler profiler("FFTDataServer::getValuesAt", false); 01149 01150 if (x >= m_width) return false; 01151 01152 if (minbin >= m_height) minbin = m_height - 1; 01153 if (count == 0) count = (m_height - minbin) / step; 01154 else if (minbin + count * step > m_height) { 01155 count = (m_height - minbin) / step; 01156 } 01157 01158 try { 01159 01160 int col; 01161 FFTCacheReader *cache = getCacheReader(x, col); 01162 if (!cache) return false; 01163 01164 if (!cache->haveSetColumnAt(col)) { 01165 Profiler profiler("FFTDataServer::getValuesAt: filling"); 01166 fillColumn(x); 01167 } 01168 01169 for (int i = 0; i < count; ++i) { 01170 cache->getValuesAt(col, i * step + minbin, reals[i], imaginaries[i]); 01171 } 01172 01173 } catch (std::exception &e) { 01174 m_error = e.what(); 01175 return false; 01176 } 01177 01178 return true; 01179 } 01180 01181 bool 01182 FFTDataServer::isColumnReady(int x) 01183 { 01184 Profiler profiler("FFTDataServer::isColumnReady", false); 01185 01186 if (x >= m_width) return true; 01187 01188 if (!haveCache(x)) { 01198 return false; 01199 } 01200 01201 try { 01202 01203 int col; 01204 FFTCacheReader *cache = getCacheReader(x, col); 01205 if (!cache) return true; 01206 01207 return cache->haveSetColumnAt(col); 01208 01209 } catch (std::exception &e) { 01210 m_error = e.what(); 01211 return false; 01212 } 01213 } 01214 01215 void 01216 FFTDataServer::fillColumn(int x) 01217 { 01218 Profiler profiler("FFTDataServer::fillColumn", false); 01219 01220 if (!m_model->isReady()) { 01221 cerr << "WARNING: FFTDataServer::fillColumn(" 01222 << x << "): model not yet ready" << endl; 01223 return; 01224 } 01225 /* 01226 if (!m_fftInput) { 01227 cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " 01228 << "input has already been completed and discarded?" 01229 << endl; 01230 return; 01231 } 01232 */ 01233 if (x >= m_width) { 01234 cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " 01235 << "x > width (" << x << " > " << m_width << ")" 01236 << endl; 01237 return; 01238 } 01239 01240 int col; 01241 #ifdef DEBUG_FFT_SERVER_FILL 01242 cout << "FFTDataServer::fillColumn(" << x << ")" << endl; 01243 #endif 01244 FFTCacheWriter *cache = getCacheWriter(x, col); 01245 if (!cache) return; 01246 01247 int winsize = m_windowSize; 01248 int fftsize = m_fftSize; 01249 int hs = fftsize/2; 01250 01251 int pfx = 0; 01252 int off = (fftsize - winsize) / 2; 01253 01254 int startFrame = m_windowIncrement * x; 01255 int endFrame = startFrame + m_windowSize; 01256 01257 startFrame -= winsize / 2; 01258 endFrame -= winsize / 2; 01259 01260 #ifdef DEBUG_FFT_SERVER_FILL 01261 std::cerr << "FFTDataServer::fillColumn: requesting frames " 01262 << startFrame + pfx << " -> " << endFrame << " ( = " 01263 << endFrame - (startFrame + pfx) << ") at index " 01264 << off + pfx << " in buffer of size " << m_fftSize 01265 << " with window size " << m_windowSize 01266 << " from channel " << m_channel << std::endl; 01267 #endif 01268 01269 QMutexLocker locker(&m_fftBuffersLock); 01270 01271 // We may have been called from a function that wanted to obtain a 01272 // column using an FFTCacheReader. Before calling us, it checked 01273 // whether the column was available already, and the reader 01274 // reported that it wasn't. Now we test again, with the mutex 01275 // held, to avoid a race condition in case another thread has 01276 // called fillColumn at the same time. 01277 if (cache->haveSetColumnAt(x & m_cacheWidthMask)) { 01278 return; 01279 } 01280 01281 if (!m_fftInput) { 01282 cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): " 01283 << "input has already been completed and discarded?" 01284 << endl; 01285 return; 01286 } 01287 01288 for (int i = 0; i < off; ++i) { 01289 m_fftInput[i] = 0.0; 01290 } 01291 01292 for (int i = 0; i < off; ++i) { 01293 m_fftInput[fftsize - i - 1] = 0.0; 01294 } 01295 01296 if (startFrame < 0) { 01297 pfx = -startFrame; 01298 for (int i = 0; i < pfx; ++i) { 01299 m_fftInput[off + i] = 0.0; 01300 } 01301 } 01302 01303 int count = 0; 01304 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx); 01305 01306 int got = m_model->getData(m_channel, startFrame + pfx, 01307 count, m_fftInput + off + pfx); 01308 01309 while (got + pfx < winsize) { 01310 m_fftInput[off + got + pfx] = 0.0; 01311 ++got; 01312 } 01313 01314 if (m_channel == -1) { 01315 int channels = m_model->getChannelCount(); 01316 if (channels > 1) { 01317 for (int i = 0; i < winsize; ++i) { 01318 m_fftInput[off + i] /= channels; 01319 } 01320 } 01321 } 01322 01323 m_windower.cut(m_fftInput + off); 01324 01325 for (int i = 0; i < hs; ++i) { 01326 fftsample temp = m_fftInput[i]; 01327 m_fftInput[i] = m_fftInput[i + hs]; 01328 m_fftInput[i + hs] = temp; 01329 } 01330 01331 fftf_execute(m_fftPlan); 01332 01333 float factor = 0.f; 01334 01335 if (cache->getStorageType() == FFTCache::Compact || 01336 cache->getStorageType() == FFTCache::Polar) { 01337 01338 for (int i = 0; i <= hs; ++i) { 01339 fftsample real = m_fftOutput[i][0]; 01340 fftsample imag = m_fftOutput[i][1]; 01341 float mag = sqrtf(real * real + imag * imag); 01342 m_workbuffer[i] = mag; 01343 m_workbuffer[i + hs + 1] = atan2f(imag, real); 01344 if (mag > factor) factor = mag; 01345 } 01346 01347 } else { 01348 01349 for (int i = 0; i <= hs; ++i) { 01350 m_workbuffer[i] = m_fftOutput[i][0]; 01351 m_workbuffer[i + hs + 1] = m_fftOutput[i][1]; 01352 } 01353 } 01354 01355 Profiler subprof("FFTDataServer::fillColumn: set to cache"); 01356 01357 if (cache->getStorageType() == FFTCache::Compact || 01358 cache->getStorageType() == FFTCache::Polar) { 01359 01360 cache->setColumnAt(col, 01361 m_workbuffer, 01362 m_workbuffer + hs + 1, 01363 factor); 01364 01365 } else { 01366 01367 cache->setColumnAt(col, 01368 m_workbuffer, 01369 m_workbuffer + hs + 1); 01370 } 01371 01372 if (m_suspended) { 01373 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl; 01374 // resume(); 01375 } 01376 } 01377 01378 void 01379 FFTDataServer::fillComplete() 01380 { 01381 for (int i = 0; i < int(m_caches.size()); ++i) { 01382 if (!m_caches[i]) continue; 01383 if (m_caches[i]->memoryCache) { 01384 m_caches[i]->memoryCache->allColumnsWritten(); 01385 } 01386 if (m_caches[i]->fileCacheWriter) { 01387 m_caches[i]->fileCacheWriter->allColumnsWritten(); 01388 } 01389 } 01390 } 01391 01392 QString 01393 FFTDataServer::getError() const 01394 { 01395 if (m_error != "") return m_error; 01396 else if (m_fillThread) return m_fillThread->getError(); 01397 else return ""; 01398 } 01399 01400 int 01401 FFTDataServer::getFillCompletion() const 01402 { 01403 if (m_fillThread) return m_fillThread->getCompletion(); 01404 else return 100; 01405 } 01406 01407 int 01408 FFTDataServer::getFillExtent() const 01409 { 01410 if (m_fillThread) return m_fillThread->getExtent(); 01411 else return m_model->getEndFrame(); 01412 } 01413 01414 QString 01415 FFTDataServer::generateFileBasename() const 01416 { 01417 return generateFileBasename(m_model, m_channel, m_windower.getType(), 01418 m_windowSize, m_windowIncrement, m_fftSize, 01419 m_polar); 01420 } 01421 01422 QString 01423 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model, 01424 int channel, 01425 WindowType windowType, 01426 int windowSize, 01427 int windowIncrement, 01428 int fftSize, 01429 bool polar) 01430 { 01431 char buffer[200]; 01432 01433 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s", 01434 (unsigned int)XmlExportable::getObjectExportId(model), 01435 (unsigned int)(channel + 1), 01436 (unsigned int)windowType, 01437 (unsigned int)windowSize, 01438 (unsigned int)windowIncrement, 01439 (unsigned int)fftSize, 01440 polar ? "-p" : "-r"); 01441 01442 return buffer; 01443 } 01444 01445 void 01446 FFTDataServer::FillThread::run() 01447 { 01448 #ifdef DEBUG_FFT_SERVER_FILL 01449 std::cerr << "FFTDataServer::FillThread::run()" << std::endl; 01450 #endif 01451 01452 m_extent = 0; 01453 m_completion = 0; 01454 01455 while (!m_server.m_model->isReady() && !m_server.m_exiting) { 01456 #ifdef DEBUG_FFT_SERVER_FILL 01457 std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl; 01458 #endif 01459 sleep(1); 01460 } 01461 if (m_server.m_exiting) return; 01462 01463 int start = m_server.m_model->getStartFrame(); 01464 int end = m_server.m_model->getEndFrame(); 01465 int remainingEnd = end; 01466 01467 int counter = 0; 01468 int updateAt = 1; 01469 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20; 01470 if (maxUpdateAt < 100) maxUpdateAt = 100; 01471 01472 if (m_fillFrom > start) { 01473 01474 for (int f = m_fillFrom; f < end; f += m_server.m_windowIncrement) { 01475 01476 try { 01477 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); 01478 } catch (std::exception &e) { 01479 std::cerr << "FFTDataServer::FillThread::run: exception: " << e.what() << std::endl; 01480 m_error = e.what(); 01481 m_server.fillComplete(); 01482 m_completion = 100; 01483 m_extent = end; 01484 return; 01485 } 01486 01487 if (m_server.m_exiting) return; 01488 01489 while (m_server.m_suspended) { 01490 #ifdef DEBUG_FFT_SERVER 01491 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl; 01492 #endif 01493 MutexLocker locker(&m_server.m_fftBuffersLock, 01494 "FFTDataServer::run::m_fftBuffersLock [1]"); 01495 if (m_server.m_suspended && !m_server.m_exiting) { 01496 m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000); 01497 } 01498 #ifdef DEBUG_FFT_SERVER 01499 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << endl; 01500 #endif 01501 if (m_server.m_exiting) return; 01502 } 01503 01504 if (++counter == updateAt) { 01505 m_extent = f; 01506 m_completion = int(100 * fabsf(float(f - m_fillFrom) / 01507 float(end - start))); 01508 counter = 0; 01509 if (updateAt < maxUpdateAt) { 01510 updateAt *= 2; 01511 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; 01512 } 01513 } 01514 } 01515 01516 remainingEnd = m_fillFrom; 01517 if (remainingEnd > start) --remainingEnd; 01518 else remainingEnd = start; 01519 } 01520 01521 int baseCompletion = m_completion; 01522 01523 for (int f = start; f < remainingEnd; f += m_server.m_windowIncrement) { 01524 01525 try { 01526 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement)); 01527 } catch (std::exception &e) { 01528 std::cerr << "FFTDataServer::FillThread::run: exception: " << e.what() << std::endl; 01529 m_error = e.what(); 01530 m_server.fillComplete(); 01531 m_completion = 100; 01532 m_extent = end; 01533 return; 01534 } 01535 01536 if (m_server.m_exiting) return; 01537 01538 while (m_server.m_suspended) { 01539 #ifdef DEBUG_FFT_SERVER 01540 cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl; 01541 #endif 01542 { 01543 MutexLocker locker(&m_server.m_fftBuffersLock, 01544 "FFTDataServer::run::m_fftBuffersLock [2]"); 01545 if (m_server.m_suspended && !m_server.m_exiting) { 01546 m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000); 01547 } 01548 } 01549 if (m_server.m_exiting) return; 01550 } 01551 01552 if (++counter == updateAt) { 01553 m_extent = f; 01554 m_completion = baseCompletion + 01555 int(100 * fabsf(float(f - start) / 01556 float(end - start))); 01557 counter = 0; 01558 if (updateAt < maxUpdateAt) { 01559 updateAt *= 2; 01560 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt; 01561 } 01562 } 01563 } 01564 01565 m_server.fillComplete(); 01566 m_completion = 100; 01567 m_extent = end; 01568 01569 #ifdef DEBUG_FFT_SERVER 01570 std::cerr << "FFTDataServer::FillThread::run exiting" << std::endl; 01571 #endif 01572 } 01573