svcore  1.9
FFTDataServer.cpp
Go to the documentation of this file.
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