00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "FFTDataServer.h"
00017
00018 #include "FFTFileCache.h"
00019 #include "FFTMemoryCache.h"
00020
00021 #include "model/DenseTimeValueModel.h"
00022
00023 #include "system/System.h"
00024
00025 #include "base/StorageAdviser.h"
00026 #include "base/Exceptions.h"
00027 #include "base/Profiler.h"
00028 #include "base/Thread.h"
00029
00030 #include <QMessageBox>
00031 #include <QApplication>
00032
00033
00034
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 size_t windowSize,
00052 size_t windowIncrement,
00053 size_t fftSize,
00054 bool polar,
00055 StorageAdviser::Criteria criteria,
00056 size_t 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::m_serverMapMutex[getInstance]");
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 size_t windowSize,
00114 size_t windowIncrement,
00115 size_t fftSize,
00116 bool polar,
00117 StorageAdviser::Criteria criteria,
00118 size_t fillFromColumn)
00119 {
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 {
00145 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getFuzzyInstance]");
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
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.toStdString() << "\")" << 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.toStdString() << "\"): 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.toStdString() << "\"): 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::m_serverMapMutex[claimInstance]");
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 std::cerr << "ERROR: FFTDataServer::claimInstance: instance "
00296 << server << " unknown!" << std::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::m_serverMapMutex[releaseInstance]");
00310
00311 #ifdef DEBUG_FFT_SERVER
00312 std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl;
00313 #endif
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
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 std::cerr << "ERROR: FFTDataServer::releaseInstance("
00330 << server << "): instance not allocated" << std::endl;
00331 } else if (--i->second.second == 0) {
00332 if (server->m_lastUsedCache == -1) {
00333 #ifdef DEBUG_FFT_SERVER
00334 std::cerr << "FFTDataServer::releaseInstance: instance "
00335 << server << " has never been used, erasing"
00336 << std::endl;
00337 #endif
00338 delete server;
00339 m_servers.erase(i);
00340 } else {
00341 #ifdef DEBUG_FFT_SERVER
00342 std::cerr << "FFTDataServer::releaseInstance: instance "
00343 << server << " no longer in use, marking for possible collection"
00344 << std::endl;
00345 #endif
00346 bool found = false;
00347 for (ServerQueue::iterator j = m_releasedServers.begin();
00348 j != m_releasedServers.end(); ++j) {
00349 if (*j == server) {
00350 std::cerr << "ERROR: FFTDataServer::releaseInstance("
00351 << server << "): server is already in "
00352 << "released servers list" << std::endl;
00353 found = true;
00354 }
00355 }
00356 if (!found) m_releasedServers.push_back(server);
00357 server->suspend();
00358 purgeLimbo();
00359 }
00360 } else {
00361 #ifdef DEBUG_FFT_SERVER
00362 std::cerr << "FFTDataServer::releaseInstance: instance "
00363 << server << " now has refcount " << i->second.second
00364 << std::endl;
00365 #endif
00366 }
00367 return;
00368 }
00369 }
00370
00371 std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
00372 << "instance not found" << std::endl;
00373 }
00374
00375 void
00376 FFTDataServer::purgeLimbo(int maxSize)
00377 {
00378 #ifdef DEBUG_FFT_SERVER
00379 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
00380 << m_releasedServers.size() << " candidates" << std::endl;
00381 #endif
00382
00383 while (int(m_releasedServers.size()) > maxSize) {
00384
00385 FFTDataServer *server = *m_releasedServers.begin();
00386
00387 bool found = false;
00388
00389 #ifdef DEBUG_FFT_SERVER
00390 std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
00391 << server << std::endl;
00392 #endif
00393
00394 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
00395
00396 if (i->second.first == server) {
00397 found = true;
00398 if (i->second.second > 0) {
00399 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
00400 << server << " is in released queue, but still has non-zero refcount "
00401 << i->second.second << std::endl;
00402
00403 break;
00404 }
00405 #ifdef DEBUG_FFT_SERVER
00406 std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
00407 << std::endl;
00408 #endif
00409
00410 m_servers.erase(i);
00411 delete server;
00412 break;
00413 }
00414 }
00415
00416 if (!found) {
00417 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
00418 << server << " is in released queue, but not in server map!"
00419 << std::endl;
00420 delete server;
00421 }
00422
00423 m_releasedServers.pop_front();
00424 }
00425
00426 #ifdef DEBUG_FFT_SERVER
00427 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
00428 << m_releasedServers.size() << " remain" << std::endl;
00429 #endif
00430
00431 }
00432
00433 void
00434 FFTDataServer::modelAboutToBeDeleted(Model *model)
00435 {
00436 MutexLocker locker(&m_serverMapMutex,
00437 "FFTDataServer::m_serverMapMutex[modelAboutToBeDeleted]");
00438
00439 #ifdef DEBUG_FFT_SERVER
00440 std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
00441 << std::endl;
00442 #endif
00443
00444 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
00445
00446 FFTDataServer *server = i->second.first;
00447
00448 if (server->getModel() == model) {
00449
00450 #ifdef DEBUG_FFT_SERVER
00451 std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
00452 << server << std::endl;
00453 #endif
00454
00455 if (i->second.second > 0) {
00456 std::cerr << "WARNING: 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;
00457 return;
00458 }
00459 for (ServerQueue::iterator j = m_releasedServers.begin();
00460 j != m_releasedServers.end(); ++j) {
00461 if (*j == server) {
00462 #ifdef DEBUG_FFT_SERVER
00463 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
00464 #endif
00465 m_releasedServers.erase(j);
00466 break;
00467 }
00468 }
00469 #ifdef DEBUG_FFT_SERVER
00470 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
00471 #endif
00472 m_servers.erase(i);
00473 delete server;
00474 return;
00475 }
00476 }
00477 }
00478
00479 FFTDataServer::FFTDataServer(QString fileBaseName,
00480 const DenseTimeValueModel *model,
00481 int channel,
00482 WindowType windowType,
00483 size_t windowSize,
00484 size_t windowIncrement,
00485 size_t fftSize,
00486 bool polar,
00487 StorageAdviser::Criteria criteria,
00488 size_t fillFromColumn) :
00489 m_fileBaseName(fileBaseName),
00490 m_model(model),
00491 m_channel(channel),
00492 m_windower(windowType, windowSize),
00493 m_windowSize(windowSize),
00494 m_windowIncrement(windowIncrement),
00495 m_fftSize(fftSize),
00496 m_polar(polar),
00497 m_width(0),
00498 m_height(0),
00499 m_cacheWidth(0),
00500 m_cacheWidthPower(0),
00501 m_cacheWidthMask(0),
00502 m_lastUsedCache(-1),
00503 m_criteria(criteria),
00504 m_fftInput(0),
00505 m_exiting(false),
00506 m_suspended(true),
00507 m_fillThread(0)
00508 {
00509 #ifdef DEBUG_FFT_SERVER
00510 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::endl;
00511 #endif
00512
00514
00515 size_t start = m_model->getStartFrame();
00516 size_t end = m_model->getEndFrame();
00517
00518 m_width = (end - start) / m_windowIncrement + 1;
00519 m_height = m_fftSize / 2 + 1;
00520
00521 #ifdef DEBUG_FFT_SERVER
00522 std::cerr << "FFTDataServer(" << this << "): dimensions are "
00523 << m_width << "x" << m_height << std::endl;
00524 #endif
00525
00526 size_t maxCacheSize = 20 * 1024 * 1024;
00527 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
00528 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
00529 else m_cacheWidth = maxCacheSize / columnSize;
00530
00531 #ifdef DEBUG_FFT_SERVER
00532 std::cerr << "FFTDataServer(" << this << "): cache width nominal "
00533 << m_cacheWidth << ", actual ";
00534 #endif
00535
00536 int bits = 0;
00537 while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
00538 m_cacheWidthPower = bits + 1;
00539 m_cacheWidth = 2;
00540 while (bits) { m_cacheWidth <<= 1; --bits; }
00541 m_cacheWidthMask = m_cacheWidth - 1;
00542
00543 #ifdef DEBUG_FFT_SERVER
00544 std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
00545 << m_cacheWidthMask << ")" << std::endl;
00546 #endif
00547
00548 if (m_criteria == StorageAdviser::NoCriteria) {
00549
00550
00551
00552
00553 if (m_polar) {
00554 m_criteria = StorageAdviser::Criteria
00555 (StorageAdviser::SpeedCritical |
00556 StorageAdviser::LongRetentionLikely);
00557 } else {
00558 m_criteria = StorageAdviser::Criteria
00559 (StorageAdviser::PrecisionCritical);
00560 }
00561 }
00562
00563 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
00564 m_caches.push_back(0);
00565 }
00566
00567 m_fftInput = (fftsample *)
00568 fftf_malloc(fftSize * sizeof(fftsample));
00569
00570 m_fftOutput = (fftf_complex *)
00571 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
00572
00573 m_workbuffer = (float *)
00574 fftf_malloc((fftSize+2) * sizeof(float));
00575
00576 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
00577 m_fftInput,
00578 m_fftOutput,
00579 FFTW_MEASURE);
00580
00581 if (!m_fftPlan) {
00582 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
00583 throw(0);
00584 }
00585
00586 m_fillThread = new FillThread(*this, fillFromColumn);
00587 }
00588
00589 FFTDataServer::~FFTDataServer()
00590 {
00591 #ifdef DEBUG_FFT_SERVER
00592 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl;
00593 #endif
00594
00595 m_suspended = false;
00596 m_exiting = true;
00597 m_condition.wakeAll();
00598 if (m_fillThread) {
00599 m_fillThread->wait();
00600 delete m_fillThread;
00601 }
00602
00603 MutexLocker locker(&m_writeMutex,
00604 "FFTDataServer::m_writeMutex[~FFTDataServer]");
00605
00606 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
00607
00608 if (*i) {
00609 delete *i;
00610 }
00611 }
00612
00613 deleteProcessingData();
00614 }
00615
00616 void
00617 FFTDataServer::deleteProcessingData()
00618 {
00619 #ifdef DEBUG_FFT_SERVER
00620 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl;
00621 #endif
00622 if (m_fftInput) {
00623 fftf_destroy_plan(m_fftPlan);
00624 fftf_free(m_fftInput);
00625 fftf_free(m_fftOutput);
00626 fftf_free(m_workbuffer);
00627 }
00628 m_fftInput = 0;
00629 }
00630
00631 void
00632 FFTDataServer::suspend()
00633 {
00634 #ifdef DEBUG_FFT_SERVER
00635 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl;
00636 #endif
00637 Profiler profiler("FFTDataServer::suspend", false);
00638
00639 MutexLocker locker(&m_writeMutex,
00640 "FFTDataServer::m_writeMutex[suspend]");
00641 m_suspended = true;
00642 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
00643 if (*i) (*i)->suspend();
00644 }
00645 }
00646
00647 void
00648 FFTDataServer::suspendWrites()
00649 {
00650 #ifdef DEBUG_FFT_SERVER
00651 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::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 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::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 {
00673 m_condition.wakeAll();
00674 }
00675 }
00676 }
00677
00678 void
00679 FFTDataServer::getStorageAdvice(size_t w, size_t h,
00680 bool &memoryCache, bool &compactCache)
00681 {
00682 int cells = w * h;
00683 int minimumSize = (cells / 1024) * sizeof(uint16_t);
00684 int maximumSize = (cells / 1024) * sizeof(float);
00685
00686
00687
00688
00689 bool canCompact = true;
00690 if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
00691 canCompact = false;
00692 minimumSize = maximumSize;
00693 }
00694
00695 StorageAdviser::Recommendation recommendation;
00696
00697 try {
00698
00699 recommendation =
00700 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
00701
00702 } catch (InsufficientDiscSpace s) {
00703
00704
00705
00706
00707 purgeLimbo(0);
00708
00709
00710
00711
00712
00713
00714 recommendation =
00715 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
00716 }
00717
00718 std::cerr << "Recommendation was: " << recommendation << std::endl;
00719
00720 memoryCache = false;
00721
00722 if ((recommendation & StorageAdviser::UseMemory) ||
00723 (recommendation & StorageAdviser::PreferMemory)) {
00724 memoryCache = true;
00725 }
00726
00727 compactCache = canCompact &&
00728 (recommendation & StorageAdviser::ConserveSpace);
00729
00730 #ifdef DEBUG_FFT_SERVER
00731 std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl;
00732
00733 std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl;
00734 #endif
00735 }
00736
00737 FFTCache *
00738 FFTDataServer::getCacheAux(size_t c)
00739 {
00740 Profiler profiler("FFTDataServer::getCacheAux", false);
00741 #ifdef DEBUG_FFT_SERVER
00742 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl;
00743 #endif
00744
00745 MutexLocker locker(&m_writeMutex,
00746 "FFTDataServer::m_writeMutex[getCacheAux]");
00747
00748 if (m_lastUsedCache == -1) {
00749 m_fillThread->start();
00750 }
00751
00752 if (int(c) != m_lastUsedCache) {
00753
00754
00755
00756 for (IntQueue::iterator i = m_dormantCaches.begin();
00757 i != m_dormantCaches.end(); ++i) {
00758 if (*i == int(c)) {
00759 m_dormantCaches.erase(i);
00760 break;
00761 }
00762 }
00763
00764 if (m_lastUsedCache >= 0) {
00765 bool inDormant = false;
00766 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
00767 if (m_dormantCaches[i] == m_lastUsedCache) {
00768 inDormant = true;
00769 break;
00770 }
00771 }
00772 if (!inDormant) {
00773 m_dormantCaches.push_back(m_lastUsedCache);
00774 }
00775 while (m_dormantCaches.size() > 4) {
00776 int dc = m_dormantCaches.front();
00777 m_dormantCaches.pop_front();
00778 m_caches[dc]->suspend();
00779 }
00780 }
00781 }
00782
00783 if (m_caches[c]) {
00784 m_lastUsedCache = c;
00785 return m_caches[c];
00786 }
00787
00788 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
00789
00790 FFTCache *cache = 0;
00791
00792 size_t width = m_cacheWidth;
00793 if (c * m_cacheWidth + width > m_width) {
00794 width = m_width - c * m_cacheWidth;
00795 }
00796
00797 bool memoryCache = false;
00798 bool compactCache = false;
00799
00800 getStorageAdvice(width, m_height, memoryCache, compactCache);
00801
00802 try {
00803
00804 if (memoryCache) {
00805
00806 cache = new FFTMemoryCache
00807 (compactCache ? FFTMemoryCache::Compact :
00808 m_polar ? FFTMemoryCache::Polar :
00809 FFTMemoryCache::Rectangular);
00810
00811 } else {
00812
00813 cache = new FFTFileCache
00814 (name,
00815 MatrixFile::ReadWrite,
00816 compactCache ? FFTFileCache::Compact :
00817 m_polar ? FFTFileCache::Polar :
00818 FFTFileCache::Rectangular);
00819 }
00820
00821 cache->resize(width, m_height);
00822 cache->reset();
00823
00824 } catch (std::bad_alloc) {
00825
00826 delete cache;
00827 cache = 0;
00828
00829 if (memoryCache) {
00830
00831 std::cerr << "WARNING: Memory allocation failed when resizing"
00832 << " FFT memory cache no. " << c << " to " << width
00833 << "x" << m_height << " (of total width " << m_width
00834 << "): falling back to disc cache" << std::endl;
00835
00836 try {
00837
00838 purgeLimbo(0);
00839
00840 cache = new FFTFileCache(name,
00841 MatrixFile::ReadWrite,
00842 FFTFileCache::Compact);
00843
00844 cache->resize(width, m_height);
00845 cache->reset();
00846
00847 } catch (std::bad_alloc) {
00848
00849 delete cache;
00850 cache = 0;
00851 }
00852 }
00853
00854 if (cache) {
00855 std::cerr << "ERROR: Memory allocation failed when resizing"
00856 << " FFT file cache no. " << c << " to " << width
00857 << "x" << m_height << " (of total width " << m_width
00858 << "): abandoning this cache" << std::endl;
00859 }
00860
00862 QMessageBox::critical
00863 (0, QApplication::tr("FFT cache resize failed"),
00864 QApplication::tr
00865 ("Failed to create or resize an FFT model slice.\n"
00866 "There may be insufficient memory or disc space to continue."));
00867 }
00868
00869 m_caches[c] = cache;
00870 m_lastUsedCache = c;
00871 return cache;
00872 }
00873
00874 float
00875 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
00876 {
00877 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
00878
00879 if (x >= m_width || y >= m_height) return 0;
00880
00881 size_t col;
00882 FFTCache *cache = getCache(x, col);
00883 if (!cache) return 0;
00884
00885 if (!cache->haveSetColumnAt(col)) {
00886 #ifdef DEBUG_FFT_SERVER
00887 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
00888 << x << ")" << std::endl;
00889 #endif
00890 fillColumn(x);
00891 }
00892 return cache->getMagnitudeAt(col, y);
00893 }
00894
00895 float
00896 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
00897 {
00898 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
00899
00900 if (x >= m_width || y >= m_height) return 0;
00901
00902 size_t col;
00903 FFTCache *cache = getCache(x, col);
00904 if (!cache) return 0;
00905
00906 if (!cache->haveSetColumnAt(col)) {
00907 fillColumn(x);
00908 }
00909 return cache->getNormalizedMagnitudeAt(col, y);
00910 }
00911
00912 float
00913 FFTDataServer::getMaximumMagnitudeAt(size_t x)
00914 {
00915 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
00916
00917 if (x >= m_width) return 0;
00918
00919 size_t col;
00920 FFTCache *cache = getCache(x, col);
00921 if (!cache) return 0;
00922
00923 if (!cache->haveSetColumnAt(col)) {
00924 fillColumn(x);
00925 }
00926 return cache->getMaximumMagnitudeAt(col);
00927 }
00928
00929 float
00930 FFTDataServer::getPhaseAt(size_t x, size_t y)
00931 {
00932 Profiler profiler("FFTDataServer::getPhaseAt", false);
00933
00934 if (x >= m_width || y >= m_height) return 0;
00935
00936 size_t col;
00937 FFTCache *cache = getCache(x, col);
00938 if (!cache) return 0;
00939
00940 if (!cache->haveSetColumnAt(col)) {
00941 fillColumn(x);
00942 }
00943 return cache->getPhaseAt(col, y);
00944 }
00945
00946 void
00947 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
00948 {
00949 Profiler profiler("FFTDataServer::getValuesAt", false);
00950
00951 if (x >= m_width || y >= m_height) {
00952 real = 0;
00953 imaginary = 0;
00954 return;
00955 }
00956
00957 size_t col;
00958 FFTCache *cache = getCache(x, col);
00959
00960 if (!cache) {
00961 real = 0;
00962 imaginary = 0;
00963 return;
00964 }
00965
00966 if (!cache->haveSetColumnAt(col)) {
00967 #ifdef DEBUG_FFT_SERVER
00968 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
00969 #endif
00970 fillColumn(x);
00971 }
00972
00973 cache->getValuesAt(col, y, real, imaginary);
00974 }
00975
00976 bool
00977 FFTDataServer::isColumnReady(size_t x)
00978 {
00979 Profiler profiler("FFTDataServer::isColumnReady", false);
00980
00981 if (x >= m_width) return true;
00982
00983 if (!haveCache(x)) {
00984 if (m_lastUsedCache == -1) {
00985 if (m_suspended) {
00986 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
00987 resume();
00988 }
00989 m_fillThread->start();
00990 }
00991 return false;
00992 }
00993
00994 size_t col;
00995 FFTCache *cache = getCache(x, col);
00996 if (!cache) return true;
00997
00998 return cache->haveSetColumnAt(col);
00999 }
01000
01001 void
01002 FFTDataServer::fillColumn(size_t x)
01003 {
01004 Profiler profiler("FFTDataServer::fillColumn", false);
01005
01006 if (!m_model->isReady()) {
01007 std::cerr << "WARNING: FFTDataServer::fillColumn("
01008 << x << "): model not yet ready" << std::endl;
01009 return;
01010 }
01011
01012 if (!m_fftInput) {
01013 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
01014 << "input has already been completed and discarded?"
01015 << std::endl;
01016 return;
01017 }
01018
01019 if (x >= m_width) {
01020 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
01021 << "x > width (" << x << " > " << m_width << ")"
01022 << std::endl;
01023 return;
01024 }
01025
01026 size_t col;
01027 #ifdef DEBUG_FFT_SERVER_FILL
01028 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
01029 #endif
01030 FFTCache *cache = getCache(x, col);
01031 if (!cache) return;
01032
01033 MutexLocker locker(&m_writeMutex,
01034 "FFTDataServer::m_writeMutex[fillColumn]");
01035
01036 if (cache->haveSetColumnAt(col)) return;
01037
01038 int startFrame = m_windowIncrement * x;
01039 int endFrame = startFrame + m_windowSize;
01040
01041 startFrame -= int(m_windowSize) / 2;
01042 endFrame -= int(m_windowSize) / 2;
01043 size_t pfx = 0;
01044
01045 size_t off = (m_fftSize - m_windowSize) / 2;
01046
01047 for (size_t i = 0; i < off; ++i) {
01048 m_fftInput[i] = 0.0;
01049 m_fftInput[m_fftSize - i - 1] = 0.0;
01050 }
01051
01052 if (startFrame < 0) {
01053 pfx = size_t(-startFrame);
01054 for (size_t i = 0; i < pfx; ++i) {
01055 m_fftInput[off + i] = 0.0;
01056 }
01057 }
01058
01059 #ifdef DEBUG_FFT_SERVER_FILL
01060 std::cerr << "FFTDataServer::fillColumn: requesting frames "
01061 << startFrame + pfx << " -> " << endFrame << " ( = "
01062 << endFrame - (startFrame + pfx) << ") at index "
01063 << off + pfx << " in buffer of size " << m_fftSize
01064 << " with window size " << m_windowSize
01065 << " from channel " << m_channel << std::endl;
01066 #endif
01067
01068 size_t count = 0;
01069 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
01070
01071 size_t got = m_model->getData(m_channel, startFrame + pfx,
01072 count, m_fftInput + off + pfx);
01073
01074 while (got + pfx < m_windowSize) {
01075 m_fftInput[off + got + pfx] = 0.0;
01076 ++got;
01077 }
01078
01079 if (m_channel == -1) {
01080 int channels = m_model->getChannelCount();
01081 if (channels > 1) {
01082 for (size_t i = 0; i < m_windowSize; ++i) {
01083 m_fftInput[off + i] /= channels;
01084 }
01085 }
01086 }
01087
01088 m_windower.cut(m_fftInput + off);
01089
01090 for (size_t i = 0; i < m_fftSize/2; ++i) {
01091 fftsample temp = m_fftInput[i];
01092 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
01093 m_fftInput[i + m_fftSize/2] = temp;
01094 }
01095
01096 fftf_execute(m_fftPlan);
01097
01098 fftsample factor = 0.0;
01099
01100 for (size_t i = 0; i <= m_fftSize/2; ++i) {
01101
01102 m_workbuffer[i] = m_fftOutput[i][0];
01103 m_workbuffer[i + m_fftSize/2 + 1] = m_fftOutput[i][1];
01104 }
01105
01106 cache->setColumnAt(col,
01107 m_workbuffer,
01108 m_workbuffer + m_fftSize/2+1);
01109
01110 if (m_suspended) {
01111
01112
01113 }
01114 }
01115
01116 size_t
01117 FFTDataServer::getFillCompletion() const
01118 {
01119 if (m_fillThread) return m_fillThread->getCompletion();
01120 else return 100;
01121 }
01122
01123 size_t
01124 FFTDataServer::getFillExtent() const
01125 {
01126 if (m_fillThread) return m_fillThread->getExtent();
01127 else return m_model->getEndFrame();
01128 }
01129
01130 QString
01131 FFTDataServer::generateFileBasename() const
01132 {
01133 return generateFileBasename(m_model, m_channel, m_windower.getType(),
01134 m_windowSize, m_windowIncrement, m_fftSize,
01135 m_polar);
01136 }
01137
01138 QString
01139 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
01140 int channel,
01141 WindowType windowType,
01142 size_t windowSize,
01143 size_t windowIncrement,
01144 size_t fftSize,
01145 bool polar)
01146 {
01147 char buffer[200];
01148
01149 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
01150 (unsigned int)XmlExportable::getObjectExportId(model),
01151 (unsigned int)(channel + 1),
01152 (unsigned int)windowType,
01153 (unsigned int)windowSize,
01154 (unsigned int)windowIncrement,
01155 (unsigned int)fftSize,
01156 polar ? "-p" : "-r");
01157
01158 return buffer;
01159 }
01160
01161 void
01162 FFTDataServer::FillThread::run()
01163 {
01164 m_extent = 0;
01165 m_completion = 0;
01166
01167 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
01168 sleep(1);
01169 }
01170 if (m_server.m_exiting) return;
01171
01172 size_t start = m_server.m_model->getStartFrame();
01173 size_t end = m_server.m_model->getEndFrame();
01174 size_t remainingEnd = end;
01175
01176 int counter = 0;
01177 int updateAt = 1;
01178 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
01179 if (maxUpdateAt < 100) maxUpdateAt = 100;
01180
01181 if (m_fillFrom > start) {
01182
01183 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
01184
01185 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
01186
01187 if (m_server.m_exiting) return;
01188
01189 while (m_server.m_suspended) {
01190 #ifdef DEBUG_FFT_SERVER
01191 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
01192 #endif
01193 {
01194 MutexLocker locker(&m_server.m_writeMutex,
01195 "FFTDataServer::m_writeMutex[run/1]");
01196 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
01197 }
01198 #ifdef DEBUG_FFT_SERVER
01199 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
01200 #endif
01201 if (m_server.m_exiting) return;
01202 }
01203
01204 if (++counter == updateAt) {
01205 m_extent = f;
01206 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
01207 float(end - start)));
01208 counter = 0;
01209 if (updateAt < maxUpdateAt) {
01210 updateAt *= 2;
01211 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
01212 }
01213 }
01214 }
01215
01216 remainingEnd = m_fillFrom;
01217 if (remainingEnd > start) --remainingEnd;
01218 else remainingEnd = start;
01219 }
01220
01221 size_t baseCompletion = m_completion;
01222
01223 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
01224
01225 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
01226
01227 if (m_server.m_exiting) return;
01228
01229 while (m_server.m_suspended) {
01230 #ifdef DEBUG_FFT_SERVER
01231 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
01232 #endif
01233 {
01234 MutexLocker locker(&m_server.m_writeMutex,
01235 "FFTDataServer::m_writeMutex[run/2]");
01236 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
01237 }
01238 if (m_server.m_exiting) return;
01239 }
01240
01241 if (++counter == updateAt) {
01242 m_extent = f;
01243 m_completion = baseCompletion +
01244 size_t(100 * fabsf(float(f - start) /
01245 float(end - start)));
01246 counter = 0;
01247 if (updateAt < maxUpdateAt) {
01248 updateAt *= 2;
01249 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
01250 }
01251 }
01252 }
01253
01254 m_completion = 100;
01255 m_extent = end;
01256 }
01257