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 "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" // for debug mutex locker
00029 
00030 #include <QMessageBox>
00031 #include <QApplication>
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                            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     // 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::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     // 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.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     // -- 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                 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) { // never used
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                     // ... so don't delete it
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; // DC == 0, Nyquist == fftsize/2
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         // assume "spectrogram" criteria for polar ffts, and "feature
00551         // extraction" criteria for rectangular ones.
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); // kb
00684     int maximumSize = (cells / 1024) * sizeof(float); // kb
00685 
00686     // We don't have a compact rectangular representation, and compact
00687     // of course is never precision-critical
00688 
00689     bool canCompact = true;
00690     if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
00691         canCompact = false;
00692         minimumSize = maximumSize; // don't use compact
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         // Delete any unused servers we may have been leaving around
00705         // in case we wanted them again
00706 
00707         purgeLimbo(0);
00708 
00709         // This time we don't catch InsufficientDiscSpace -- we
00710         // haven't allocated anything yet and can safely let the
00711         // exception out to indicate to the caller that we can't
00712         // handle it.
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 //        std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
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 //        std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
01112 //        resume();
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 

Generated on Wed Feb 20 15:45:25 2008 for SonicVisualiser by  doxygen 1.5.1