annotate data/fft/FFTDataServer.cpp @ 489:82ab61fa9223

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