annotate data/fft/FFTDataServer.cpp @ 985:f073d924a7c3

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