svcore  1.9
FFTDataServer.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "FFTDataServer.h"
17 
18 #include "FFTFileCacheReader.h"
19 #include "FFTFileCacheWriter.h"
20 #include "FFTMemoryCache.h"
21 
23 
24 #include "system/System.h"
25 
26 #include "base/StorageAdviser.h"
27 #include "base/Exceptions.h"
28 #include "base/Profiler.h"
29 #include "base/Thread.h" // for debug mutex locker
30 
31 #include <QWriteLocker>
32 
33 //#define DEBUG_FFT_SERVER 1
34 //#define DEBUG_FFT_SERVER_FILL 1
35 
36 #ifdef DEBUG_FFT_SERVER_FILL
37 #ifndef DEBUG_FFT_SERVER
38 #define DEBUG_FFT_SERVER 1
39 #endif
40 #endif
41 
42 
46 
49  int channel,
50  WindowType windowType,
51  int windowSize,
52  int windowIncrement,
53  int fftSize,
54  bool polar,
55  StorageAdviser::Criteria criteria,
56  int fillFromColumn)
57 {
58  QString n = generateFileBasename(model,
59  channel,
60  windowType,
61  windowSize,
62  windowIncrement,
63  fftSize,
64  polar);
65 
66  FFTDataServer *server = 0;
67 
68  MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getInstance::m_serverMapMutex");
69 
70  if ((server = findServer(n))) {
71  return server;
72  }
73 
74  QString npn = generateFileBasename(model,
75  channel,
76  windowType,
77  windowSize,
78  windowIncrement,
79  fftSize,
80  !polar);
81 
82  if ((server = findServer(npn))) {
83  return server;
84  }
85 
86  try {
87  server = new FFTDataServer(n,
88  model,
89  channel,
90  windowType,
91  windowSize,
92  windowIncrement,
93  fftSize,
94  polar,
95  criteria,
96  fillFromColumn);
97  } catch (InsufficientDiscSpace) {
98  delete server;
99  server = 0;
100  }
101 
102  if (server) {
103  m_servers[n] = ServerCountPair(server, 1);
104  }
105 
106  return server;
107 }
108 
111  int channel,
112  WindowType windowType,
113  int windowSize,
114  int windowIncrement,
115  int fftSize,
116  bool polar,
117  StorageAdviser::Criteria criteria,
118  int fillFromColumn)
119 {
120  // Fuzzy matching:
121  //
122  // -- if we're asked for polar and have non-polar, use it (and
123  // vice versa). This one is vital, and we do it for non-fuzzy as
124  // well (above).
125  //
126  // -- if we're asked for an instance with a given fft size and we
127  // have one already with a multiple of that fft size but the same
128  // window size and type (and model), we can draw the results from
129  // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the
130  // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the
131  // same window plus zero padding).
132  //
133  // -- if we're asked for an instance with a given window type and
134  // size and fft size and we have one already the same but with a
135  // smaller increment, we can draw the results from it (provided
136  // our increment is a multiple of its)
137  //
138  // The FFTModel knows how to interpret these things. In
139  // both cases we require that the larger one is a power-of-two
140  // multiple of the smaller (e.g. even though in principle you can
141  // draw the results at increment 256 from those at increment 768
142  // or 1536, the model doesn't support this).
143 
144  {
145  MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getFuzzyInstance::m_serverMapMutex");
146 
147  ServerMap::iterator best = m_servers.end();
148  int bestdist = -1;
149 
150  for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
151 
152  FFTDataServer *server = i->second.first;
153 
154  if (server->getModel() == model &&
155  (server->getChannel() == channel || model->getChannelCount() == 1) &&
156  server->getWindowType() == windowType &&
157  server->getWindowSize() == windowSize &&
158  server->getWindowIncrement() <= windowIncrement &&
159  server->getFFTSize() >= fftSize) {
160 
161  if ((windowIncrement % server->getWindowIncrement()) != 0) continue;
162  int ratio = windowIncrement / server->getWindowIncrement();
163  bool poweroftwo = true;
164  while (ratio > 1) {
165  if (ratio & 0x1) {
166  poweroftwo = false;
167  break;
168  }
169  ratio >>= 1;
170  }
171  if (!poweroftwo) continue;
172 
173  if ((server->getFFTSize() % fftSize) != 0) continue;
174  ratio = server->getFFTSize() / fftSize;
175  while (ratio > 1) {
176  if (ratio & 0x1) {
177  poweroftwo = false;
178  break;
179  }
180  ratio >>= 1;
181  }
182  if (!poweroftwo) continue;
183 
184  int distance = 0;
185 
186  if (server->getPolar() != polar) distance += 1;
187 
188  distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15;
189  distance += ((server->getFFTSize() / fftSize) - 1) * 10;
190 
191  if (server->getFillCompletion() < 50) distance += 100;
192 
193 #ifdef DEBUG_FFT_SERVER
194  std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl;
195 #endif
196 
197  if (bestdist == -1 || distance < bestdist) {
198  bestdist = distance;
199  best = i;
200  }
201  }
202  }
203 
204  if (bestdist >= 0) {
205  FFTDataServer *server = best->second.first;
206 #ifdef DEBUG_FFT_SERVER
207  std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl;
208 #endif
209  claimInstance(server, false);
210  return server;
211  }
212  }
213 
214  // Nothing found, make a new one
215 
216  return getInstance(model,
217  channel,
218  windowType,
219  windowSize,
220  windowIncrement,
221  fftSize,
222  polar,
223  criteria,
224  fillFromColumn);
225 }
226 
229 {
230 #ifdef DEBUG_FFT_SERVER
231  std::cerr << "FFTDataServer::findServer(\"" << n << "\")" << std::endl;
232 #endif
233 
234  if (m_servers.find(n) != m_servers.end()) {
235 
236  FFTDataServer *server = m_servers[n].first;
237 
238 #ifdef DEBUG_FFT_SERVER
239  std::cerr << "FFTDataServer::findServer(\"" << n << "\"): found " << server << std::endl;
240 #endif
241 
242  claimInstance(server, false);
243 
244  return server;
245  }
246 
247 #ifdef DEBUG_FFT_SERVER
248  std::cerr << "FFTDataServer::findServer(\"" << n << "\"): not found" << std::endl;
249 #endif
250 
251  return 0;
252 }
253 
254 void
256 {
257  claimInstance(server, true);
258 }
259 
260 void
262 {
263  MutexLocker locker(needLock ? &m_serverMapMutex : 0,
264  "FFTDataServer::claimInstance::m_serverMapMutex");
265 
266 #ifdef DEBUG_FFT_SERVER
267  std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl;
268 #endif
269 
270  for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
271  if (i->second.first == server) {
272 
273  for (ServerQueue::iterator j = m_releasedServers.begin();
274  j != m_releasedServers.end(); ++j) {
275 
276  if (*j == server) {
277 #ifdef DEBUG_FFT_SERVER
278  std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl;
279 #endif
280  m_releasedServers.erase(j);
281  break;
282  }
283  }
284 
285  ++i->second.second;
286 
287 #ifdef DEBUG_FFT_SERVER
288  std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl;
289 #endif
290 
291  return;
292  }
293  }
294 
295  cerr << "ERROR: FFTDataServer::claimInstance: instance "
296  << server << " unknown!" << endl;
297 }
298 
299 void
301 {
302  releaseInstance(server, true);
303 }
304 
305 void
307 {
308  MutexLocker locker(needLock ? &m_serverMapMutex : 0,
309  "FFTDataServer::releaseInstance::m_serverMapMutex");
310 
311 #ifdef DEBUG_FFT_SERVER
312  std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl;
313 #endif
314 
315  // -- if ref count > 0, decrement and return
316  // -- if the instance hasn't been used at all, delete it immediately
317  // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts,
318  // leave them hanging around
319  // -- if N instances with zero refcounts remain, delete the one that
320  // was last released first
321  // -- if we run out of disk space when allocating an instance, go back
322  // and delete the spare N instances before trying again
323  // -- have an additional method to indicate that a model has been
324  // destroyed, so that we can delete all of its fft server instances
325 
326  for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
327  if (i->second.first == server) {
328  if (i->second.second == 0) {
329  cerr << "ERROR: FFTDataServer::releaseInstance("
330  << server << "): instance not allocated" << endl;
331  } else if (--i->second.second == 0) {
343 #ifdef DEBUG_FFT_SERVER
344  std::cerr << "FFTDataServer::releaseInstance: instance "
345  << server << " no longer in use, marking for possible collection"
346  << std::endl;
347 #endif
348  bool found = false;
349  for (ServerQueue::iterator j = m_releasedServers.begin();
350  j != m_releasedServers.end(); ++j) {
351  if (*j == server) {
352  cerr << "ERROR: FFTDataServer::releaseInstance("
353  << server << "): server is already in "
354  << "released servers list" << endl;
355  found = true;
356  }
357  }
358  if (!found) m_releasedServers.push_back(server);
359  server->suspend();
360  purgeLimbo();
362  } else {
363 #ifdef DEBUG_FFT_SERVER
364  std::cerr << "FFTDataServer::releaseInstance: instance "
365  << server << " now has refcount " << i->second.second
366  << std::endl;
367 #endif
368  }
369  return;
370  }
371  }
372 
373  cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
374  << "instance not found" << endl;
375 }
376 
377 void
379 {
380 #ifdef DEBUG_FFT_SERVER
381  std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
382  << m_releasedServers.size() << " candidates" << std::endl;
383 #endif
384 
385  while (int(m_releasedServers.size()) > maxSize) {
386 
387  FFTDataServer *server = *m_releasedServers.begin();
388 
389  bool found = false;
390 
391 #ifdef DEBUG_FFT_SERVER
392  std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
393  << server << std::endl;
394 #endif
395 
396  for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
397 
398  if (i->second.first == server) {
399  found = true;
400  if (i->second.second > 0) {
401  cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
402  << server << " is in released queue, but still has non-zero refcount "
403  << i->second.second << endl;
404  // ... so don't delete it
405  break;
406  }
407 #ifdef DEBUG_FFT_SERVER
408  std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
409  << std::endl;
410 #endif
411 
412  m_servers.erase(i);
413  delete server;
414  break;
415  }
416  }
417 
418  if (!found) {
419  cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
420  << server << " is in released queue, but not in server map!"
421  << endl;
422  delete server;
423  }
424 
425  m_releasedServers.pop_front();
426  }
427 
428 #ifdef DEBUG_FFT_SERVER
429  std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
430  << m_releasedServers.size() << " remain" << std::endl;
431 #endif
432 
433 }
434 
435 void
437 {
439  "FFTDataServer::modelAboutToBeDeleted::m_serverMapMutex");
440 
441 #ifdef DEBUG_FFT_SERVER
442  std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
443  << std::endl;
444 #endif
445 
446  for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
447 
448  FFTDataServer *server = i->second.first;
449 
450  if (server->getModel() == model) {
451 
452 #ifdef DEBUG_FFT_SERVER
453  std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
454  << server << std::endl;
455 #endif
456 
457  if (i->second.second > 0) {
458  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;
459  server->suspendWrites();
460  return;
461  }
462  for (ServerQueue::iterator j = m_releasedServers.begin();
463  j != m_releasedServers.end(); ++j) {
464  if (*j == server) {
465 #ifdef DEBUG_FFT_SERVER
466  std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
467 #endif
468  m_releasedServers.erase(j);
469  break;
470  }
471  }
472 #ifdef DEBUG_FFT_SERVER
473  std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
474 #endif
475  m_servers.erase(i);
476  delete server;
477  return;
478  }
479  }
480 }
481 
482 FFTDataServer::FFTDataServer(QString fileBaseName,
483  const DenseTimeValueModel *model,
484  int channel,
485  WindowType windowType,
486  int windowSize,
487  int windowIncrement,
488  int fftSize,
489  bool polar,
490  StorageAdviser::Criteria criteria,
491  int fillFromColumn) :
492  m_fileBaseName(fileBaseName),
493  m_model(model),
494  m_channel(channel),
495  m_windower(windowType, windowSize),
496  m_windowSize(windowSize),
497  m_windowIncrement(windowIncrement),
498  m_fftSize(fftSize),
499  m_polar(polar),
500  m_width(0),
501  m_height(0),
502  m_cacheWidth(0),
503  m_cacheWidthPower(0),
504  m_cacheWidthMask(0),
505  m_criteria(criteria),
506  m_fftInput(0),
507  m_exiting(false),
508  m_suspended(true),
509  m_fillThread(0)
510 {
511 #ifdef DEBUG_FFT_SERVER
512  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << endl;
513 #endif
514 
516 
517  int start = m_model->getStartFrame();
518  int end = m_model->getEndFrame();
519 
520  m_width = (end - start) / m_windowIncrement + 1;
521  m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
522 
523 #ifdef DEBUG_FFT_SERVER
524  cerr << "FFTDataServer(" << this << "): dimensions are "
525  << m_width << "x" << m_height << endl;
526 #endif
527 
528  int maxCacheSize = 20 * 1024 * 1024;
529  int columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
530  if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
531  else m_cacheWidth = maxCacheSize / columnSize;
532 
533 #ifdef DEBUG_FFT_SERVER
534  cerr << "FFTDataServer(" << this << "): cache width nominal "
535  << m_cacheWidth << ", actual ";
536 #endif
537 
538  int bits = 0;
539  while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
540  m_cacheWidthPower = bits + 1;
541  m_cacheWidth = 2;
542  while (bits) { m_cacheWidth <<= 1; --bits; }
544 
545 #ifdef DEBUG_FFT_SERVER
546  cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
547  << m_cacheWidthMask << ")" << endl;
548 #endif
549 
551 
552  // assume "spectrogram" criteria for polar ffts, and "feature
553  // extraction" criteria for rectangular ones.
554 
555  if (m_polar) {
559  } else {
562  }
563  }
564 
565  for (int i = 0; i <= m_width / m_cacheWidth; ++i) {
566  m_caches.push_back(0);
567  }
568 
569  m_fftInput = (fftsample *)
570  fftf_malloc(fftSize * sizeof(fftsample));
571 
573  fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
574 
575  m_workbuffer = (float *)
576  fftf_malloc((fftSize+2) * sizeof(float));
577 
579  m_fftInput,
580  m_fftOutput,
581  FFTW_MEASURE);
582 
583  if (!m_fftPlan) {
584  cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << endl;
585  throw(0);
586  }
587 
588  m_fillThread = new FillThread(*this, fillFromColumn);
589 }
590 
592 {
593 #ifdef DEBUG_FFT_SERVER
594  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << endl;
595 #endif
596 
597  m_suspended = false;
598  m_exiting = true;
599  m_condition.wakeAll();
600  if (m_fillThread) {
601  m_fillThread->wait();
602  delete m_fillThread;
603  }
604 
605 // MutexLocker locker(&m_writeMutex,
606 // "FFTDataServer::~FFTDataServer::m_writeMutex");
607 
608  QMutexLocker mlocker(&m_fftBuffersLock);
609  QWriteLocker wlocker(&m_cacheVectorLock);
610 
611  for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
612  if (*i) {
613  delete *i;
614  }
615  }
616 
618 }
619 
620 void
622 {
623 #ifdef DEBUG_FFT_SERVER
624  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << endl;
625 #endif
626  if (m_fftInput) {
631  }
632  m_fftInput = 0;
633 }
634 
635 void
637 {
638 #ifdef DEBUG_FFT_SERVER
639  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << endl;
640 #endif
641  Profiler profiler("FFTDataServer::suspend", false);
642 
643  QMutexLocker locker(&m_fftBuffersLock);
644  m_suspended = true;
645 }
646 
647 void
649 {
650 #ifdef DEBUG_FFT_SERVER
651  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << endl;
652 #endif
653  Profiler profiler("FFTDataServer::suspendWrites", false);
654 
655  m_suspended = true;
656 }
657 
658 void
660 {
661 #ifdef DEBUG_FFT_SERVER
662  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << endl;
663 #endif
664  Profiler profiler("FFTDataServer::resume", false);
665 
666  m_suspended = false;
667  if (m_fillThread) {
668  if (m_fillThread->isFinished()) {
669  delete m_fillThread;
670  m_fillThread = 0;
672  } else if (!m_fillThread->isRunning()) {
673  m_fillThread->start();
674  } else {
675  m_condition.wakeAll();
676  }
677  }
678 }
679 
680 void
682  bool &memoryCache, bool &compactCache)
683 {
684  int cells = w * h;
685  int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
686  int maximumSize = (cells / 1024) * sizeof(float); // kb
687 
688  // We don't have a compact rectangular representation, and compact
689  // of course is never precision-critical
690 
691  bool canCompact = true;
693  canCompact = false;
694  minimumSize = maximumSize; // don't use compact
695  }
696 
697  StorageAdviser::Recommendation recommendation;
698 
699  try {
700 
701  recommendation =
702  StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
703 
704  } catch (InsufficientDiscSpace s) {
705 
706  // Delete any unused servers we may have been leaving around
707  // in case we wanted them again
708 
709  purgeLimbo(0);
710 
711  // This time we don't catch InsufficientDiscSpace -- we
712  // haven't allocated anything yet and can safely let the
713  // exception out to indicate to the caller that we can't
714  // handle it.
715 
716  recommendation =
717  StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
718  }
719 
720 // cerr << "Recommendation was: " << recommendation << endl;
721 
722  memoryCache = false;
723 
724  if ((recommendation & StorageAdviser::UseMemory) ||
725  (recommendation & StorageAdviser::PreferMemory)) {
726  memoryCache = true;
727  }
728 
729  compactCache = canCompact &&
730  (recommendation & StorageAdviser::ConserveSpace);
731 
732 #ifdef DEBUG_FFT_SERVER
733  cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << endl;
734 
735  cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << endl;
736 #endif
737 }
738 
739 bool
741 {
742  // Creating the cache could take a significant amount of time. We
743  // don't want to block readers on m_cacheVectorLock while this is
744  // happening, but we do want to block any further calls to
745  // makeCache. So we use this lock solely to serialise this
746  // particular function -- it isn't used anywhere else.
747 
748  QMutexLocker locker(&m_cacheCreationMutex);
749 
750  m_cacheVectorLock.lockForRead();
751  if (m_caches[c]) {
752  // someone else must have created the cache between our
753  // testing for it and taking the mutex
754  m_cacheVectorLock.unlock();
755  return true;
756  }
757  m_cacheVectorLock.unlock();
758 
759  // Now m_cacheCreationMutex is held, but m_cacheVectorLock is not
760  // -- readers can proceed, but callers to this function will block
761 
762  CacheBlock *cb = new CacheBlock;
763 
764  QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
765 
766  int width = m_cacheWidth;
767  if (c * m_cacheWidth + width > m_width) {
768  width = m_width - c * m_cacheWidth;
769  }
770 
771  bool memoryCache = false;
772  bool compactCache = false;
773 
774  getStorageAdvice(width, m_height, memoryCache, compactCache);
775 
776  bool success = false;
777 
778  if (memoryCache) {
779 
780  try {
781 
782  cb->memoryCache = new FFTMemoryCache
783  (compactCache ? FFTCache::Compact :
786  width, m_height);
787 
788  success = true;
789 
790  } catch (std::bad_alloc) {
791 
792  delete cb->memoryCache;
793  cb->memoryCache = 0;
794 
795  cerr << "WARNING: Memory allocation failed when creating"
796  << " FFT memory cache no. " << c << " of " << width
797  << "x" << m_height << " (of total width " << m_width
798  << "): falling back to disc cache" << endl;
799 
800  memoryCache = false;
801  }
802  }
803 
804  if (!memoryCache) {
805 
806  try {
807 
809  (name,
810  compactCache ? FFTCache::Compact :
813  width, m_height);
814 
815  success = true;
816 
817  } catch (std::exception &e) {
818 
819  delete cb->fileCacheWriter;
820  cb->fileCacheWriter = 0;
821 
822  cerr << "ERROR: Failed to construct disc cache for FFT data: "
823  << e.what() << endl;
824 
825  throw;
826  }
827  }
828 
829  m_cacheVectorLock.lockForWrite();
830 
831  m_caches[c] = cb;
832 
833  m_cacheVectorLock.unlock();
834 
835  return success;
836 }
837 
838 bool
840 {
841  // preconditions: m_caches[c] exists and contains a file writer;
842  // m_cacheVectorLock is not locked by this thread
843 #ifdef DEBUG_FFT_SERVER
844  std::cerr << "FFTDataServer::makeCacheReader(" << c << ")" << std::endl;
845 #endif
846 
847  QThread *me = QThread::currentThread();
848  QWriteLocker locker(&m_cacheVectorLock);
849  CacheBlock *cb(m_caches.at(c));
850  if (!cb || !cb->fileCacheWriter) return false;
851 
852  try {
853 
854  cb->fileCacheReader[me] = new FFTFileCacheReader(cb->fileCacheWriter);
855 
856  } catch (std::exception &e) {
857 
858  delete cb->fileCacheReader[me];
859  cb->fileCacheReader.erase(me);
860 
861  cerr << "ERROR: Failed to construct disc cache reader for FFT data: "
862  << e.what() << endl;
863  return false;
864  }
865 
866  // erase a reader that looks like it may no longer going to be
867  // used by this thread for a while (leaving alone the current
868  // and previous cache readers)
869  int deleteCandidate = c - 2;
870  if (deleteCandidate < 0) deleteCandidate = c + 2;
871  if (deleteCandidate >= (int)m_caches.size()) {
872  return true;
873  }
874 
875  cb = m_caches.at(deleteCandidate);
876  if (cb && cb->fileCacheReader.find(me) != cb->fileCacheReader.end()) {
877 #ifdef DEBUG_FFT_SERVER
878  std::cerr << "FFTDataServer::makeCacheReader: Deleting probably unpopular reader " << deleteCandidate << " for this thread (as I create reader " << c << ")" << std::endl;
879 #endif
880  delete cb->fileCacheReader[me];
881  cb->fileCacheReader.erase(me);
882  }
883 
884  return true;
885 }
886 
887 float
889 {
890  Profiler profiler("FFTDataServer::getMagnitudeAt", false);
891 
892  if (x >= m_width || y >= m_height) return 0;
893 
894  float val = 0;
895 
896  try {
897  int col;
898  FFTCacheReader *cache = getCacheReader(x, col);
899  if (!cache) return 0;
900 
901  if (!cache->haveSetColumnAt(col)) {
902  Profiler profiler("FFTDataServer::getMagnitudeAt: filling");
903 #ifdef DEBUG_FFT_SERVER
904  std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
905  << x << ")" << std::endl;
906 #endif
907  fillColumn(x);
908  }
909 
910  val = cache->getMagnitudeAt(col, y);
911 
912  } catch (std::exception &e) {
913  m_error = e.what();
914  }
915 
916  return val;
917 }
918 
919 bool
920 FFTDataServer::getMagnitudesAt(int x, float *values, int minbin, int count, int step)
921 {
922  Profiler profiler("FFTDataServer::getMagnitudesAt", false);
923 
924  if (x >= m_width) return false;
925 
926  if (minbin >= m_height) minbin = m_height - 1;
927  if (count == 0) count = (m_height - minbin) / step;
928  else if (minbin + count * step > m_height) {
929  count = (m_height - minbin) / step;
930  }
931 
932  try {
933  int col;
934  FFTCacheReader *cache = getCacheReader(x, col);
935  if (!cache) return false;
936 
937  if (!cache->haveSetColumnAt(col)) {
938  Profiler profiler("FFTDataServer::getMagnitudesAt: filling");
939  fillColumn(x);
940  }
941 
942  cache->getMagnitudesAt(col, values, minbin, count, step);
943 
944  } catch (std::exception &e) {
945  m_error = e.what();
946  return false;
947  }
948 
949  return true;
950 }
951 
952 float
954 {
955  Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
956 
957  if (x >= m_width || y >= m_height) return 0;
958 
959  float val = 0;
960 
961  try {
962 
963  int col;
964  FFTCacheReader *cache = getCacheReader(x, col);
965  if (!cache) return 0;
966 
967  if (!cache->haveSetColumnAt(col)) {
968  Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling");
969  fillColumn(x);
970  }
971  val = cache->getNormalizedMagnitudeAt(col, y);
972 
973  } catch (std::exception &e) {
974  m_error = e.what();
975  }
976 
977  return val;
978 }
979 
980 bool
981 FFTDataServer::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count, int step)
982 {
983  Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false);
984 
985  if (x >= m_width) return false;
986 
987  if (minbin >= m_height) minbin = m_height - 1;
988  if (count == 0) count = (m_height - minbin) / step;
989  else if (minbin + count * step > m_height) {
990  count = (m_height - minbin) / step;
991  }
992 
993  try {
994 
995  int col;
996  FFTCacheReader *cache = getCacheReader(x, col);
997  if (!cache) return false;
998 
999  if (!cache->haveSetColumnAt(col)) {
1000  Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling");
1001  fillColumn(x);
1002  }
1003 
1004  for (int i = 0; i < count; ++i) {
1005  values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin);
1006  }
1007 
1008  } catch (std::exception &e) {
1009  m_error = e.what();
1010  return false;
1011  }
1012 
1013  return true;
1014 }
1015 
1016 float
1018 {
1019  Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
1020 
1021  if (x >= m_width) return 0;
1022 
1023  float val = 0;
1024 
1025  try {
1026 
1027  int col;
1028  FFTCacheReader *cache = getCacheReader(x, col);
1029  if (!cache) return 0;
1030 
1031  if (!cache->haveSetColumnAt(col)) {
1032  Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling");
1033  fillColumn(x);
1034  }
1035  val = cache->getMaximumMagnitudeAt(col);
1036 
1037  } catch (std::exception &e) {
1038  m_error = e.what();
1039  }
1040 
1041  return val;
1042 }
1043 
1044 float
1046 {
1047  Profiler profiler("FFTDataServer::getPhaseAt", false);
1048 
1049  if (x >= m_width || y >= m_height) return 0;
1050 
1051  float val = 0;
1052 
1053  try {
1054 
1055  int col;
1056  FFTCacheReader *cache = getCacheReader(x, col);
1057  if (!cache) return 0;
1058 
1059  if (!cache->haveSetColumnAt(col)) {
1060  Profiler profiler("FFTDataServer::getPhaseAt: filling");
1061  fillColumn(x);
1062  }
1063  val = cache->getPhaseAt(col, y);
1064 
1065  } catch (std::exception &e) {
1066  m_error = e.what();
1067  }
1068 
1069  return val;
1070 }
1071 
1072 bool
1073 FFTDataServer::getPhasesAt(int x, float *values, int minbin, int count, int step)
1074 {
1075  Profiler profiler("FFTDataServer::getPhasesAt", false);
1076 
1077  if (x >= m_width) return false;
1078 
1079  if (minbin >= m_height) minbin = m_height - 1;
1080  if (count == 0) count = (m_height - minbin) / step;
1081  else if (minbin + count * step > m_height) {
1082  count = (m_height - minbin) / step;
1083  }
1084 
1085  try {
1086 
1087  int col;
1088  FFTCacheReader *cache = getCacheReader(x, col);
1089  if (!cache) return false;
1090 
1091  if (!cache->haveSetColumnAt(col)) {
1092  Profiler profiler("FFTDataServer::getPhasesAt: filling");
1093  fillColumn(x);
1094  }
1095 
1096  for (int i = 0; i < count; ++i) {
1097  values[i] = cache->getPhaseAt(col, i * step + minbin);
1098  }
1099 
1100  } catch (std::exception &e) {
1101  m_error = e.what();
1102  return false;
1103  }
1104 
1105  return true;
1106 }
1107 
1108 void
1109 FFTDataServer::getValuesAt(int x, int y, float &real, float &imaginary)
1110 {
1111  Profiler profiler("FFTDataServer::getValuesAt", false);
1112 
1113  if (x >= m_width || y >= m_height) {
1114  real = 0;
1115  imaginary = 0;
1116  return;
1117  }
1118 
1119  try {
1120 
1121  int col;
1122  FFTCacheReader *cache = getCacheReader(x, col);
1123 
1124  if (!cache) {
1125  real = 0;
1126  imaginary = 0;
1127  return;
1128  }
1129 
1130  if (!cache->haveSetColumnAt(col)) {
1131  Profiler profiler("FFTDataServer::getValuesAt: filling");
1132 #ifdef DEBUG_FFT_SERVER
1133  std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
1134 #endif
1135  fillColumn(x);
1136  }
1137 
1138  cache->getValuesAt(col, y, real, imaginary);
1139 
1140  } catch (std::exception &e) {
1141  m_error = e.what();
1142  }
1143 }
1144 
1145 bool
1146 FFTDataServer::getValuesAt(int x, float *reals, float *imaginaries, int minbin, int count, int step)
1147 {
1148  Profiler profiler("FFTDataServer::getValuesAt", false);
1149 
1150  if (x >= m_width) return false;
1151 
1152  if (minbin >= m_height) minbin = m_height - 1;
1153  if (count == 0) count = (m_height - minbin) / step;
1154  else if (minbin + count * step > m_height) {
1155  count = (m_height - minbin) / step;
1156  }
1157 
1158  try {
1159 
1160  int col;
1161  FFTCacheReader *cache = getCacheReader(x, col);
1162  if (!cache) return false;
1163 
1164  if (!cache->haveSetColumnAt(col)) {
1165  Profiler profiler("FFTDataServer::getValuesAt: filling");
1166  fillColumn(x);
1167  }
1168 
1169  for (int i = 0; i < count; ++i) {
1170  cache->getValuesAt(col, i * step + minbin, reals[i], imaginaries[i]);
1171  }
1172 
1173  } catch (std::exception &e) {
1174  m_error = e.what();
1175  return false;
1176  }
1177 
1178  return true;
1179 }
1180 
1181 bool
1183 {
1184  Profiler profiler("FFTDataServer::isColumnReady", false);
1185 
1186  if (x >= m_width) return true;
1187 
1188  if (!haveCache(x)) {
1198  return false;
1199  }
1200 
1201  try {
1202 
1203  int col;
1204  FFTCacheReader *cache = getCacheReader(x, col);
1205  if (!cache) return true;
1206 
1207  return cache->haveSetColumnAt(col);
1208 
1209  } catch (std::exception &e) {
1210  m_error = e.what();
1211  return false;
1212  }
1213 }
1214 
1215 void
1217 {
1218  Profiler profiler("FFTDataServer::fillColumn", false);
1219 
1220  if (!m_model->isReady()) {
1221  cerr << "WARNING: FFTDataServer::fillColumn("
1222  << x << "): model not yet ready" << endl;
1223  return;
1224  }
1225 /*
1226  if (!m_fftInput) {
1227  cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
1228  << "input has already been completed and discarded?"
1229  << endl;
1230  return;
1231  }
1232 */
1233  if (x >= m_width) {
1234  cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
1235  << "x > width (" << x << " > " << m_width << ")"
1236  << endl;
1237  return;
1238  }
1239 
1240  int col;
1241 #ifdef DEBUG_FFT_SERVER_FILL
1242  cout << "FFTDataServer::fillColumn(" << x << ")" << endl;
1243 #endif
1244  FFTCacheWriter *cache = getCacheWriter(x, col);
1245  if (!cache) return;
1246 
1247  int winsize = m_windowSize;
1248  int fftsize = m_fftSize;
1249  int hs = fftsize/2;
1250 
1251  int pfx = 0;
1252  int off = (fftsize - winsize) / 2;
1253 
1254  int startFrame = m_windowIncrement * x;
1255  int endFrame = startFrame + m_windowSize;
1256 
1257  startFrame -= winsize / 2;
1258  endFrame -= winsize / 2;
1259 
1260 #ifdef DEBUG_FFT_SERVER_FILL
1261  std::cerr << "FFTDataServer::fillColumn: requesting frames "
1262  << startFrame + pfx << " -> " << endFrame << " ( = "
1263  << endFrame - (startFrame + pfx) << ") at index "
1264  << off + pfx << " in buffer of size " << m_fftSize
1265  << " with window size " << m_windowSize
1266  << " from channel " << m_channel << std::endl;
1267 #endif
1268 
1269  QMutexLocker locker(&m_fftBuffersLock);
1270 
1271  // We may have been called from a function that wanted to obtain a
1272  // column using an FFTCacheReader. Before calling us, it checked
1273  // whether the column was available already, and the reader
1274  // reported that it wasn't. Now we test again, with the mutex
1275  // held, to avoid a race condition in case another thread has
1276  // called fillColumn at the same time.
1277  if (cache->haveSetColumnAt(x & m_cacheWidthMask)) {
1278  return;
1279  }
1280 
1281  if (!m_fftInput) {
1282  cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
1283  << "input has already been completed and discarded?"
1284  << endl;
1285  return;
1286  }
1287 
1288  for (int i = 0; i < off; ++i) {
1289  m_fftInput[i] = 0.0;
1290  }
1291 
1292  for (int i = 0; i < off; ++i) {
1293  m_fftInput[fftsize - i - 1] = 0.0;
1294  }
1295 
1296  if (startFrame < 0) {
1297  pfx = -startFrame;
1298  for (int i = 0; i < pfx; ++i) {
1299  m_fftInput[off + i] = 0.0;
1300  }
1301  }
1302 
1303  int count = 0;
1304  if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
1305 
1306  int got = m_model->getData(m_channel, startFrame + pfx,
1307  count, m_fftInput + off + pfx);
1308 
1309  while (got + pfx < winsize) {
1310  m_fftInput[off + got + pfx] = 0.0;
1311  ++got;
1312  }
1313 
1314  if (m_channel == -1) {
1315  int channels = m_model->getChannelCount();
1316  if (channels > 1) {
1317  for (int i = 0; i < winsize; ++i) {
1318  m_fftInput[off + i] /= channels;
1319  }
1320  }
1321  }
1322 
1323  m_windower.cut(m_fftInput + off);
1324 
1325  for (int i = 0; i < hs; ++i) {
1326  fftsample temp = m_fftInput[i];
1327  m_fftInput[i] = m_fftInput[i + hs];
1328  m_fftInput[i + hs] = temp;
1329  }
1330 
1332 
1333  float factor = 0.f;
1334 
1335  if (cache->getStorageType() == FFTCache::Compact ||
1336  cache->getStorageType() == FFTCache::Polar) {
1337 
1338  for (int i = 0; i <= hs; ++i) {
1339  fftsample real = m_fftOutput[i][0];
1340  fftsample imag = m_fftOutput[i][1];
1341  float mag = sqrtf(real * real + imag * imag);
1342  m_workbuffer[i] = mag;
1343  m_workbuffer[i + hs + 1] = atan2f(imag, real);
1344  if (mag > factor) factor = mag;
1345  }
1346 
1347  } else {
1348 
1349  for (int i = 0; i <= hs; ++i) {
1350  m_workbuffer[i] = m_fftOutput[i][0];
1351  m_workbuffer[i + hs + 1] = m_fftOutput[i][1];
1352  }
1353  }
1354 
1355  Profiler subprof("FFTDataServer::fillColumn: set to cache");
1356 
1357  if (cache->getStorageType() == FFTCache::Compact ||
1358  cache->getStorageType() == FFTCache::Polar) {
1359 
1360  cache->setColumnAt(col,
1361  m_workbuffer,
1362  m_workbuffer + hs + 1,
1363  factor);
1364 
1365  } else {
1366 
1367  cache->setColumnAt(col,
1368  m_workbuffer,
1369  m_workbuffer + hs + 1);
1370  }
1371 
1372  if (m_suspended) {
1373 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
1374 // resume();
1375  }
1376 }
1377 
1378 void
1380 {
1381  for (int i = 0; i < int(m_caches.size()); ++i) {
1382  if (!m_caches[i]) continue;
1383  if (m_caches[i]->memoryCache) {
1384  m_caches[i]->memoryCache->allColumnsWritten();
1385  }
1386  if (m_caches[i]->fileCacheWriter) {
1387  m_caches[i]->fileCacheWriter->allColumnsWritten();
1388  }
1389  }
1390 }
1391 
1392 QString
1394 {
1395  if (m_error != "") return m_error;
1396  else if (m_fillThread) return m_fillThread->getError();
1397  else return "";
1398 }
1399 
1400 int
1402 {
1403  if (m_fillThread) return m_fillThread->getCompletion();
1404  else return 100;
1405 }
1406 
1407 int
1409 {
1410  if (m_fillThread) return m_fillThread->getExtent();
1411  else return m_model->getEndFrame();
1412 }
1413 
1414 QString
1416 {
1419  m_polar);
1420 }
1421 
1422 QString
1424  int channel,
1425  WindowType windowType,
1426  int windowSize,
1427  int windowIncrement,
1428  int fftSize,
1429  bool polar)
1430 {
1431  char buffer[200];
1432 
1433  sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
1434  (unsigned int)XmlExportable::getObjectExportId(model),
1435  (unsigned int)(channel + 1),
1436  (unsigned int)windowType,
1437  (unsigned int)windowSize,
1438  (unsigned int)windowIncrement,
1439  (unsigned int)fftSize,
1440  polar ? "-p" : "-r");
1441 
1442  return buffer;
1443 }
1444 
1445 void
1447 {
1448 #ifdef DEBUG_FFT_SERVER_FILL
1449  std::cerr << "FFTDataServer::FillThread::run()" << std::endl;
1450 #endif
1451 
1452  m_extent = 0;
1453  m_completion = 0;
1454 
1455  while (!m_server.m_model->isReady() && !m_server.m_exiting) {
1456 #ifdef DEBUG_FFT_SERVER_FILL
1457  std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl;
1458 #endif
1459  sleep(1);
1460  }
1461  if (m_server.m_exiting) return;
1462 
1464  int end = m_server.m_model->getEndFrame();
1465  int remainingEnd = end;
1466 
1467  int counter = 0;
1468  int updateAt = 1;
1469  int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
1470  if (maxUpdateAt < 100) maxUpdateAt = 100;
1471 
1472  if (m_fillFrom > start) {
1473 
1474  for (int f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
1475 
1476  try {
1478  } catch (std::exception &e) {
1479  std::cerr << "FFTDataServer::FillThread::run: exception: " << e.what() << std::endl;
1480  m_error = e.what();
1482  m_completion = 100;
1483  m_extent = end;
1484  return;
1485  }
1486 
1487  if (m_server.m_exiting) return;
1488 
1489  while (m_server.m_suspended) {
1490 #ifdef DEBUG_FFT_SERVER
1491  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl;
1492 #endif
1494  "FFTDataServer::run::m_fftBuffersLock [1]");
1497  }
1498 #ifdef DEBUG_FFT_SERVER
1499  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << endl;
1500 #endif
1501  if (m_server.m_exiting) return;
1502  }
1503 
1504  if (++counter == updateAt) {
1505  m_extent = f;
1506  m_completion = int(100 * fabsf(float(f - m_fillFrom) /
1507  float(end - start)));
1508  counter = 0;
1509  if (updateAt < maxUpdateAt) {
1510  updateAt *= 2;
1511  if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
1512  }
1513  }
1514  }
1515 
1516  remainingEnd = m_fillFrom;
1517  if (remainingEnd > start) --remainingEnd;
1518  else remainingEnd = start;
1519  }
1520 
1521  int baseCompletion = m_completion;
1522 
1523  for (int f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
1524 
1525  try {
1527  } catch (std::exception &e) {
1528  std::cerr << "FFTDataServer::FillThread::run: exception: " << e.what() << std::endl;
1529  m_error = e.what();
1531  m_completion = 100;
1532  m_extent = end;
1533  return;
1534  }
1535 
1536  if (m_server.m_exiting) return;
1537 
1538  while (m_server.m_suspended) {
1539 #ifdef DEBUG_FFT_SERVER
1540  cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl;
1541 #endif
1542  {
1544  "FFTDataServer::run::m_fftBuffersLock [2]");
1547  }
1548  }
1549  if (m_server.m_exiting) return;
1550  }
1551 
1552  if (++counter == updateAt) {
1553  m_extent = f;
1554  m_completion = baseCompletion +
1555  int(100 * fabsf(float(f - start) /
1556  float(end - start)));
1557  counter = 0;
1558  if (updateAt < maxUpdateAt) {
1559  updateAt *= 2;
1560  if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
1561  }
1562  }
1563  }
1564 
1566  m_completion = 100;
1567  m_extent = end;
1568 
1569 #ifdef DEBUG_FFT_SERVER
1570  std::cerr << "FFTDataServer::FillThread::run exiting" << std::endl;
1571 #endif
1572 }
1573 
fftf_plan m_fftPlan
bool haveCache(int x)
virtual int getChannelCount() const =0
Return the number of distinct channels for this model.
void deleteProcessingData()
FillThread * m_fillThread
static QMutex m_serverMapMutex
bool getNormalizedMagnitudesAt(int x, float *values, int minbin=0, int count=0, int step=1)
bool getPolar() const
Definition: FFTDataServer.h:76
virtual void getValuesAt(int x, int y, float &real, float &imag) const =0
FFTCacheWriter * getCacheWriter(int x, int &col)
QString m_fileBaseName
FFTDataServer & m_server
ThreadReaderMap fileCacheReader
fftsample * m_fftInput
QReadWriteLock m_cacheVectorLock
FFTMemoryCache * memoryCache
int getChannel() const
Definition: FFTDataServer.h:71
virtual float getMagnitudeAt(int x, int y) const =0
QString getError() const
static ServerMap m_servers
virtual int getStartFrame() const =0
Return the first audio frame spanned by the model.
void start()
Definition: Thread.cpp:34
Window< fftsample > m_windower
FFTDataServer(QString fileBaseName, const DenseTimeValueModel *model, int channel, WindowType windowType, int windowSize, int windowIncrement, int fftSize, bool polar, StorageAdviser::Criteria criteria, int fillFromColumn=0)
float getMaximumMagnitudeAt(int x)
const DenseTimeValueModel * getModel() const
Definition: FFTDataServer.h:70
fftf_complex * m_fftOutput
static int getObjectExportId(const void *)
bool makeCache(int c)
#define fftf_malloc
Definition: FFTapi.h:24
static void purgeLimbo(int maxSize=3)
virtual FFTCache::StorageType getStorageType() const =0
float getMagnitudeAt(int x, int y)
virtual bool haveSetColumnAt(int x) const =0
virtual int getData(int channel, int start, int count, float *buffer) const =0
Get the specified set of samples from the given channel of the model in single-precision floating-poi...
std::pair< FFTDataServer *, int > ServerCountPair
void fillColumn(int x)
WindowType
Definition: Window.h:27
QWaitCondition m_condition
virtual ~FFTDataServer()
virtual bool isReady(int *completion=0) const
Return true if the model has finished loading or calculating all its data, for a model that is capabl...
Definition: Model.h:142
int getWindowIncrement() const
Definition: FFTDataServer.h:74
#define fftf_destroy_plan
Definition: FFTapi.h:30
QMutex m_cacheCreationMutex
FFTFileCacheWriter * fileCacheWriter
QMutex m_fftBuffersLock
bool getMagnitudesAt(int x, float *values, int minbin=0, int count=0, int step=1)
static FFTDataServer * findServer(QString)
virtual float getPhaseAt(int x, int y) const =0
WindowType getWindowType() const
Definition: FFTDataServer.h:72
virtual float getNormalizedMagnitudeAt(int x, int y) const =0
void getStorageAdvice(int w, int h, bool &memory, bool &compact)
virtual float getMaximumMagnitudeAt(int x) const =0
int getFillExtent() const
float getPhaseAt(int x, int y)
const DenseTimeValueModel * m_model
virtual void setColumnAt(int x, float *mags, float *phases, float factor)=0
virtual bool haveSetColumnAt(int x) const =0
Model is the base class for all data models that represent any sort of data on a time scale based on ...
Definition: Model.h:35
#define fftf_execute
Definition: FFTapi.h:29
static void modelAboutToBeDeleted(Model *)
bool isColumnReady(int x)
int getFillCompletion() const
static void claimInstance(FFTDataServer *)
static Recommendation recommend(Criteria criteria, int minimumSize, int maximumSize)
Recommend where to store some data, given certain storage and recall criteria.
#define fftf_free
Definition: FFTapi.h:25
StorageAdviser::Criteria m_criteria
FFTCacheReader * getCacheReader(int x, int &col)
static void releaseInstance(FFTDataServer *)
void getValuesAt(int x, int y, float &real, float &imaginary)
int getFFTSize() const
Definition: FFTDataServer.h:75
Base class for models containing dense two-dimensional data (value against time).
virtual int getEndFrame() const =0
Return the last audio frame spanned by the model.
float * m_workbuffer
std::map< QString, ServerCountPair > ServerMap
QString getError() const
bool makeCacheReader(int c)
int getWindowSize() const
Definition: FFTDataServer.h:73
float getNormalizedMagnitudeAt(int x, int y)
#define fftf_plan_dft_r2c_1d
Definition: FFTapi.h:27
WindowType getType() const
Definition: Window.h:69
virtual void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const =0
#define fftf_complex
Definition: FFTapi.h:23
static FFTDataServer * getInstance(const DenseTimeValueModel *model, int channel, WindowType windowType, int windowSize, int windowIncrement, int fftSize, bool polar, StorageAdviser::Criteria criteria=StorageAdviser::NoCriteria, int fillFromColumn=0)
std::deque< FFTDataServer * > ServerQueue
QString generateFileBasename() const
In-memory FFT cache.
bool getPhasesAt(int x, float *values, int minbin=0, int count=0, int step=1)
static FFTDataServer * getFuzzyInstance(const DenseTimeValueModel *model, int channel, WindowType windowType, int windowSize, int windowIncrement, int fftSize, bool polar, StorageAdviser::Criteria criteria=StorageAdviser::NoCriteria, int fillFromColumn=0)
CacheVector m_caches
void cut(T *src) const
Definition: Window.h:61
Profile point instance class.
Definition: Profiler.h:86
static ServerQueue m_releasedServers