annotate data/fft/FFTDataServer.cpp @ 498:fdf5930b7ccc

* Bring FeatureWriter and RDFFeatureWriter into the fold (from Runner) so that we can use them to export features from SV as well
author Chris Cannam
date Fri, 28 Nov 2008 13:47:11 +0000
parents b6dc6c7f402c
children 83eae5239db6
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@497 454 server->suspendWrites();
Chris@362 455 return;
Chris@215 456 }
Chris@215 457 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@215 458 j != m_releasedServers.end(); ++j) {
Chris@215 459 if (*j == server) {
Chris@216 460 #ifdef DEBUG_FFT_SERVER
Chris@216 461 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
Chris@216 462 #endif
Chris@215 463 m_releasedServers.erase(j);
Chris@215 464 break;
Chris@215 465 }
Chris@215 466 }
Chris@216 467 #ifdef DEBUG_FFT_SERVER
Chris@216 468 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
Chris@216 469 #endif
Chris@215 470 m_servers.erase(i);
Chris@215 471 delete server;
Chris@215 472 return;
Chris@215 473 }
Chris@148 474 }
Chris@148 475 }
Chris@148 476
Chris@148 477 FFTDataServer::FFTDataServer(QString fileBaseName,
Chris@148 478 const DenseTimeValueModel *model,
Chris@148 479 int channel,
Chris@148 480 WindowType windowType,
Chris@148 481 size_t windowSize,
Chris@148 482 size_t windowIncrement,
Chris@148 483 size_t fftSize,
Chris@148 484 bool polar,
Chris@334 485 StorageAdviser::Criteria criteria,
Chris@148 486 size_t fillFromColumn) :
Chris@148 487 m_fileBaseName(fileBaseName),
Chris@148 488 m_model(model),
Chris@148 489 m_channel(channel),
Chris@148 490 m_windower(windowType, windowSize),
Chris@148 491 m_windowSize(windowSize),
Chris@148 492 m_windowIncrement(windowIncrement),
Chris@148 493 m_fftSize(fftSize),
Chris@148 494 m_polar(polar),
Chris@183 495 m_width(0),
Chris@183 496 m_height(0),
Chris@183 497 m_cacheWidth(0),
Chris@359 498 m_cacheWidthPower(0),
Chris@359 499 m_cacheWidthMask(0),
Chris@148 500 m_lastUsedCache(-1),
Chris@359 501 m_criteria(criteria),
Chris@148 502 m_fftInput(0),
Chris@148 503 m_exiting(false),
Chris@153 504 m_suspended(true), //!!! or false?
Chris@148 505 m_fillThread(0)
Chris@148 506 {
Chris@193 507 #ifdef DEBUG_FFT_SERVER
Chris@193 508 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::endl;
Chris@193 509 #endif
Chris@193 510
Chris@272 511 //!!! end is not correct until model finished reading -- what to do???
Chris@272 512
Chris@148 513 size_t start = m_model->getStartFrame();
Chris@148 514 size_t end = m_model->getEndFrame();
Chris@148 515
Chris@148 516 m_width = (end - start) / m_windowIncrement + 1;
Chris@203 517 m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
Chris@148 518
Chris@216 519 #ifdef DEBUG_FFT_SERVER
Chris@216 520 std::cerr << "FFTDataServer(" << this << "): dimensions are "
Chris@216 521 << m_width << "x" << m_height << std::endl;
Chris@216 522 #endif
Chris@216 523
Chris@148 524 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 525 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
Chris@148 526 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
Chris@148 527 else m_cacheWidth = maxCacheSize / columnSize;
Chris@148 528
Chris@359 529 #ifdef DEBUG_FFT_SERVER
Chris@359 530 std::cerr << "FFTDataServer(" << this << "): cache width nominal "
Chris@359 531 << m_cacheWidth << ", actual ";
Chris@359 532 #endif
Chris@359 533
Chris@148 534 int bits = 0;
Chris@359 535 while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
Chris@359 536 m_cacheWidthPower = bits + 1;
Chris@148 537 m_cacheWidth = 2;
Chris@148 538 while (bits) { m_cacheWidth <<= 1; --bits; }
Chris@359 539 m_cacheWidthMask = m_cacheWidth - 1;
Chris@172 540
Chris@359 541 #ifdef DEBUG_FFT_SERVER
Chris@359 542 std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
Chris@359 543 << m_cacheWidthMask << ")" << std::endl;
Chris@359 544 #endif
Chris@359 545
Chris@359 546 if (m_criteria == StorageAdviser::NoCriteria) {
Chris@172 547
Chris@334 548 // assume "spectrogram" criteria for polar ffts, and "feature
Chris@334 549 // extraction" criteria for rectangular ones.
Chris@334 550
Chris@334 551 if (m_polar) {
Chris@359 552 m_criteria = StorageAdviser::Criteria
Chris@334 553 (StorageAdviser::SpeedCritical |
Chris@334 554 StorageAdviser::LongRetentionLikely);
Chris@334 555 } else {
Chris@359 556 m_criteria = StorageAdviser::Criteria
Chris@334 557 (StorageAdviser::PrecisionCritical);
Chris@334 558 }
Chris@172 559 }
Chris@172 560
Chris@148 561 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
Chris@148 562 m_caches.push_back(0);
Chris@148 563 }
Chris@148 564
Chris@148 565 m_fftInput = (fftsample *)
Chris@226 566 fftf_malloc(fftSize * sizeof(fftsample));
Chris@148 567
Chris@226 568 m_fftOutput = (fftf_complex *)
Chris@226 569 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
Chris@148 570
Chris@148 571 m_workbuffer = (float *)
Chris@226 572 fftf_malloc((fftSize+2) * sizeof(float));
Chris@148 573
Chris@226 574 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
Chris@334 575 m_fftInput,
Chris@334 576 m_fftOutput,
Chris@334 577 FFTW_MEASURE);
Chris@148 578
Chris@148 579 if (!m_fftPlan) {
Chris@226 580 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
Chris@148 581 throw(0);
Chris@148 582 }
Chris@148 583
Chris@148 584 m_fillThread = new FillThread(*this, fillFromColumn);
Chris@148 585 }
Chris@148 586
Chris@148 587 FFTDataServer::~FFTDataServer()
Chris@148 588 {
Chris@148 589 #ifdef DEBUG_FFT_SERVER
Chris@193 590 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl;
Chris@148 591 #endif
Chris@148 592
Chris@155 593 m_suspended = false;
Chris@148 594 m_exiting = true;
Chris@148 595 m_condition.wakeAll();
Chris@148 596 if (m_fillThread) {
Chris@148 597 m_fillThread->wait();
Chris@148 598 delete m_fillThread;
Chris@148 599 }
Chris@148 600
Chris@244 601 MutexLocker locker(&m_writeMutex,
Chris@408 602 "FFTDataServer::~FFTDataServer::m_writeMutex");
Chris@148 603
Chris@148 604 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@359 605
Chris@205 606 if (*i) {
Chris@205 607 delete *i;
Chris@205 608 }
Chris@148 609 }
Chris@148 610
Chris@148 611 deleteProcessingData();
Chris@148 612 }
Chris@148 613
Chris@148 614 void
Chris@148 615 FFTDataServer::deleteProcessingData()
Chris@148 616 {
Chris@193 617 #ifdef DEBUG_FFT_SERVER
Chris@193 618 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl;
Chris@193 619 #endif
Chris@148 620 if (m_fftInput) {
Chris@226 621 fftf_destroy_plan(m_fftPlan);
Chris@226 622 fftf_free(m_fftInput);
Chris@226 623 fftf_free(m_fftOutput);
Chris@226 624 fftf_free(m_workbuffer);
Chris@148 625 }
Chris@148 626 m_fftInput = 0;
Chris@148 627 }
Chris@148 628
Chris@148 629 void
Chris@148 630 FFTDataServer::suspend()
Chris@148 631 {
Chris@148 632 #ifdef DEBUG_FFT_SERVER
Chris@193 633 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl;
Chris@148 634 #endif
Chris@183 635 Profiler profiler("FFTDataServer::suspend", false);
Chris@183 636
Chris@244 637 MutexLocker locker(&m_writeMutex,
Chris@408 638 "FFTDataServer::suspend::m_writeMutex");
Chris@148 639 m_suspended = true;
Chris@148 640 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 641 if (*i) (*i)->suspend();
Chris@148 642 }
Chris@148 643 }
Chris@148 644
Chris@148 645 void
Chris@155 646 FFTDataServer::suspendWrites()
Chris@155 647 {
Chris@155 648 #ifdef DEBUG_FFT_SERVER
Chris@193 649 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::endl;
Chris@155 650 #endif
Chris@183 651 Profiler profiler("FFTDataServer::suspendWrites", false);
Chris@183 652
Chris@155 653 m_suspended = true;
Chris@155 654 }
Chris@155 655
Chris@155 656 void
Chris@148 657 FFTDataServer::resume()
Chris@148 658 {
Chris@154 659 #ifdef DEBUG_FFT_SERVER
Chris@193 660 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::endl;
Chris@154 661 #endif
Chris@183 662 Profiler profiler("FFTDataServer::resume", false);
Chris@183 663
Chris@148 664 m_suspended = false;
Chris@157 665 if (m_fillThread) {
Chris@157 666 if (m_fillThread->isFinished()) {
Chris@157 667 delete m_fillThread;
Chris@157 668 m_fillThread = 0;
Chris@157 669 deleteProcessingData();
Chris@411 670 } else if (!m_fillThread->isRunning()) {
Chris@411 671 m_fillThread->start();
Chris@157 672 } else {
Chris@157 673 m_condition.wakeAll();
Chris@157 674 }
Chris@157 675 }
Chris@148 676 }
Chris@148 677
Chris@359 678 void
Chris@359 679 FFTDataServer::getStorageAdvice(size_t w, size_t h,
Chris@359 680 bool &memoryCache, bool &compactCache)
Chris@359 681 {
Chris@359 682 int cells = w * h;
Chris@359 683 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
Chris@359 684 int maximumSize = (cells / 1024) * sizeof(float); // kb
Chris@359 685
Chris@359 686 // We don't have a compact rectangular representation, and compact
Chris@359 687 // of course is never precision-critical
Chris@359 688
Chris@359 689 bool canCompact = true;
Chris@359 690 if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
Chris@359 691 canCompact = false;
Chris@359 692 minimumSize = maximumSize; // don't use compact
Chris@359 693 }
Chris@359 694
Chris@359 695 StorageAdviser::Recommendation recommendation;
Chris@359 696
Chris@359 697 try {
Chris@359 698
Chris@359 699 recommendation =
Chris@359 700 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
Chris@359 701
Chris@359 702 } catch (InsufficientDiscSpace s) {
Chris@359 703
Chris@359 704 // Delete any unused servers we may have been leaving around
Chris@359 705 // in case we wanted them again
Chris@359 706
Chris@359 707 purgeLimbo(0);
Chris@359 708
Chris@359 709 // This time we don't catch InsufficientDiscSpace -- we
Chris@359 710 // haven't allocated anything yet and can safely let the
Chris@359 711 // exception out to indicate to the caller that we can't
Chris@359 712 // handle it.
Chris@359 713
Chris@359 714 recommendation =
Chris@359 715 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
Chris@359 716 }
Chris@359 717
Chris@436 718 // std::cerr << "Recommendation was: " << recommendation << std::endl;
Chris@359 719
Chris@359 720 memoryCache = false;
Chris@359 721
Chris@359 722 if ((recommendation & StorageAdviser::UseMemory) ||
Chris@359 723 (recommendation & StorageAdviser::PreferMemory)) {
Chris@359 724 memoryCache = true;
Chris@359 725 }
Chris@359 726
Chris@359 727 compactCache = canCompact &&
Chris@359 728 (recommendation & StorageAdviser::ConserveSpace);
Chris@359 729
Chris@374 730 #ifdef DEBUG_FFT_SERVER
Chris@359 731 std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl;
Chris@359 732
Chris@359 733 std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl;
Chris@359 734 #endif
Chris@359 735 }
Chris@359 736
Chris@148 737 FFTCache *
Chris@148 738 FFTDataServer::getCacheAux(size_t c)
Chris@148 739 {
Chris@183 740 Profiler profiler("FFTDataServer::getCacheAux", false);
Chris@193 741 #ifdef DEBUG_FFT_SERVER
Chris@193 742 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl;
Chris@193 743 #endif
Chris@183 744
Chris@244 745 MutexLocker locker(&m_writeMutex,
Chris@408 746 "FFTDataServer::getCacheAux::m_writeMutex");
Chris@148 747
Chris@148 748 if (m_lastUsedCache == -1) {
Chris@148 749 m_fillThread->start();
Chris@148 750 }
Chris@148 751
Chris@148 752 if (int(c) != m_lastUsedCache) {
Chris@148 753
Chris@148 754 // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
Chris@148 755
Chris@148 756 for (IntQueue::iterator i = m_dormantCaches.begin();
Chris@148 757 i != m_dormantCaches.end(); ++i) {
Chris@259 758 if (*i == int(c)) {
Chris@148 759 m_dormantCaches.erase(i);
Chris@148 760 break;
Chris@148 761 }
Chris@148 762 }
Chris@148 763
Chris@148 764 if (m_lastUsedCache >= 0) {
Chris@148 765 bool inDormant = false;
Chris@148 766 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
Chris@148 767 if (m_dormantCaches[i] == m_lastUsedCache) {
Chris@148 768 inDormant = true;
Chris@148 769 break;
Chris@148 770 }
Chris@148 771 }
Chris@148 772 if (!inDormant) {
Chris@148 773 m_dormantCaches.push_back(m_lastUsedCache);
Chris@148 774 }
Chris@148 775 while (m_dormantCaches.size() > 4) {
Chris@148 776 int dc = m_dormantCaches.front();
Chris@148 777 m_dormantCaches.pop_front();
Chris@148 778 m_caches[dc]->suspend();
Chris@148 779 }
Chris@148 780 }
Chris@148 781 }
Chris@148 782
Chris@148 783 if (m_caches[c]) {
Chris@148 784 m_lastUsedCache = c;
Chris@148 785 return m_caches[c];
Chris@148 786 }
Chris@148 787
Chris@148 788 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
Chris@148 789
Chris@172 790 FFTCache *cache = 0;
Chris@172 791
Chris@213 792 size_t width = m_cacheWidth;
Chris@213 793 if (c * m_cacheWidth + width > m_width) {
Chris@213 794 width = m_width - c * m_cacheWidth;
Chris@213 795 }
Chris@213 796
Chris@359 797 bool memoryCache = false;
Chris@359 798 bool compactCache = false;
Chris@359 799
Chris@359 800 getStorageAdvice(width, m_height, memoryCache, compactCache);
Chris@359 801
Chris@200 802 try {
Chris@264 803
Chris@359 804 if (memoryCache) {
Chris@172 805
Chris@264 806 cache = new FFTMemoryCache
Chris@359 807 (compactCache ? FFTMemoryCache::Compact :
Chris@359 808 m_polar ? FFTMemoryCache::Polar :
Chris@359 809 FFTMemoryCache::Rectangular);
Chris@172 810
Chris@200 811 } else {
Chris@172 812
Chris@264 813 cache = new FFTFileCache
Chris@264 814 (name,
Chris@264 815 MatrixFile::ReadWrite,
Chris@359 816 compactCache ? FFTFileCache::Compact :
Chris@359 817 m_polar ? FFTFileCache::Polar :
Chris@359 818 FFTFileCache::Rectangular);
Chris@200 819 }
Chris@200 820
Chris@200 821 cache->resize(width, m_height);
Chris@200 822 cache->reset();
Chris@200 823
Chris@213 824 } catch (std::bad_alloc) {
Chris@205 825
Chris@213 826 delete cache;
Chris@213 827 cache = 0;
Chris@213 828
Chris@359 829 if (memoryCache) {
Chris@213 830
Chris@213 831 std::cerr << "WARNING: Memory allocation failed when resizing"
Chris@213 832 << " FFT memory cache no. " << c << " to " << width
Chris@213 833 << "x" << m_height << " (of total width " << m_width
Chris@213 834 << "): falling back to disc cache" << std::endl;
Chris@213 835
Chris@213 836 try {
Chris@213 837
Chris@359 838 purgeLimbo(0);
Chris@359 839
Chris@359 840 cache = new FFTFileCache(name,
Chris@359 841 MatrixFile::ReadWrite,
Chris@213 842 FFTFileCache::Compact);
Chris@213 843
Chris@213 844 cache->resize(width, m_height);
Chris@213 845 cache->reset();
Chris@213 846
Chris@213 847 } catch (std::bad_alloc) {
Chris@213 848
Chris@213 849 delete cache;
Chris@213 850 cache = 0;
Chris@213 851 }
Chris@213 852 }
Chris@213 853
Chris@387 854 if (!cache) {
Chris@213 855 std::cerr << "ERROR: Memory allocation failed when resizing"
Chris@213 856 << " FFT file cache no. " << c << " to " << width
Chris@213 857 << "x" << m_height << " (of total width " << m_width
Chris@213 858 << "): abandoning this cache" << std::endl;
Chris@387 859
Chris@387 860 throw AllocationFailed("Failed to create or resize an FFT model slice");
Chris@213 861 }
Chris@172 862 }
Chris@148 863
Chris@148 864 m_caches[c] = cache;
Chris@148 865 m_lastUsedCache = c;
Chris@148 866 return cache;
Chris@148 867 }
Chris@148 868
Chris@148 869 float
Chris@148 870 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 871 {
Chris@183 872 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
Chris@183 873
Chris@217 874 if (x >= m_width || y >= m_height) return 0;
Chris@217 875
Chris@148 876 size_t col;
Chris@148 877 FFTCache *cache = getCache(x, col);
Chris@200 878 if (!cache) return 0;
Chris@148 879
Chris@148 880 if (!cache->haveSetColumnAt(col)) {
Chris@408 881 Profiler profiler("FFTDataServer::getMagnitudeAt: filling");
Chris@280 882 #ifdef DEBUG_FFT_SERVER
Chris@183 883 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
Chris@183 884 << x << ")" << std::endl;
Chris@280 885 #endif
Chris@408 886 // hold mutex so that write thread doesn't mess with class
Chris@408 887 // member data in fillColumn
Chris@408 888 MutexLocker locker(&m_writeMutex,
Chris@408 889 "FFTDataServer::getMagnitudeAt: m_writeMutex");
Chris@408 890 fillColumn(x, true);
Chris@148 891 }
Chris@148 892 return cache->getMagnitudeAt(col, y);
Chris@148 893 }
Chris@148 894
Chris@408 895 bool
Chris@408 896 FFTDataServer::getMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step)
Chris@408 897 {
Chris@408 898 Profiler profiler("FFTDataServer::getMagnitudesAt", false);
Chris@408 899
Chris@408 900 if (x >= m_width) return false;
Chris@408 901
Chris@408 902 if (minbin >= m_height) minbin = m_height - 1;
Chris@408 903 if (count == 0) count = (m_height - minbin) / step;
Chris@408 904 else if (minbin + count * step > m_height) {
Chris@408 905 count = (m_height - minbin) / step;
Chris@408 906 }
Chris@408 907
Chris@408 908 size_t col;
Chris@408 909 FFTCache *cache = getCache(x, col);
Chris@408 910 if (!cache) return false;
Chris@408 911
Chris@408 912 if (!cache->haveSetColumnAt(col)) {
Chris@408 913 Profiler profiler("FFTDataServer::getMagnitudesAt: filling");
Chris@408 914 MutexLocker locker(&m_writeMutex,
Chris@408 915 "FFTDataServer::getMagnitudesAt: m_writeMutex");
Chris@408 916 fillColumn(x, true);
Chris@408 917 }
Chris@408 918
Chris@408 919 for (size_t i = 0; i < count; ++i) {
Chris@408 920 values[i] = cache->getMagnitudeAt(col, i * step + minbin);
Chris@408 921 }
Chris@408 922
Chris@408 923 return true;
Chris@408 924 }
Chris@408 925
Chris@148 926 float
Chris@148 927 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 928 {
Chris@183 929 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
Chris@183 930
Chris@217 931 if (x >= m_width || y >= m_height) return 0;
Chris@217 932
Chris@148 933 size_t col;
Chris@148 934 FFTCache *cache = getCache(x, col);
Chris@200 935 if (!cache) return 0;
Chris@148 936
Chris@148 937 if (!cache->haveSetColumnAt(col)) {
Chris@408 938 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling");
Chris@408 939 // hold mutex so that write thread doesn't mess with class
Chris@408 940 // member data in fillColumn
Chris@408 941 MutexLocker locker(&m_writeMutex,
Chris@408 942 "FFTDataServer::getNormalizedMagnitudeAt: m_writeMutex");
Chris@408 943 fillColumn(x, true);
Chris@148 944 }
Chris@148 945 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 946 }
Chris@148 947
Chris@408 948 bool
Chris@408 949 FFTDataServer::getNormalizedMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step)
Chris@408 950 {
Chris@408 951 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false);
Chris@408 952
Chris@408 953 if (x >= m_width) return false;
Chris@408 954
Chris@408 955 if (minbin >= m_height) minbin = m_height - 1;
Chris@408 956 if (count == 0) count = (m_height - minbin) / step;
Chris@408 957 else if (minbin + count * step > m_height) {
Chris@408 958 count = (m_height - minbin) / step;
Chris@408 959 }
Chris@408 960
Chris@408 961 size_t col;
Chris@408 962 FFTCache *cache = getCache(x, col);
Chris@408 963 if (!cache) return false;
Chris@408 964
Chris@408 965 if (!cache->haveSetColumnAt(col)) {
Chris@408 966 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling");
Chris@408 967 MutexLocker locker(&m_writeMutex,
Chris@408 968 "FFTDataServer::getNormalizedMagnitudesAt: m_writeMutex");
Chris@408 969 fillColumn(x, true);
Chris@408 970 }
Chris@408 971
Chris@408 972 for (size_t i = 0; i < count; ++i) {
Chris@408 973 values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin);
Chris@408 974 }
Chris@408 975
Chris@408 976 return true;
Chris@408 977 }
Chris@408 978
Chris@148 979 float
Chris@148 980 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 981 {
Chris@183 982 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
Chris@183 983
Chris@217 984 if (x >= m_width) return 0;
Chris@217 985
Chris@148 986 size_t col;
Chris@148 987 FFTCache *cache = getCache(x, col);
Chris@200 988 if (!cache) return 0;
Chris@148 989
Chris@148 990 if (!cache->haveSetColumnAt(col)) {
Chris@408 991 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling");
Chris@408 992 // hold mutex so that write thread doesn't mess with class
Chris@408 993 // member data in fillColumn
Chris@408 994 MutexLocker locker(&m_writeMutex,
Chris@408 995 "FFTDataServer::getMaximumMagnitudeAt: m_writeMutex");
Chris@408 996 fillColumn(x, true);
Chris@148 997 }
Chris@148 998 return cache->getMaximumMagnitudeAt(col);
Chris@148 999 }
Chris@148 1000
Chris@148 1001 float
Chris@148 1002 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 1003 {
Chris@183 1004 Profiler profiler("FFTDataServer::getPhaseAt", false);
Chris@183 1005
Chris@217 1006 if (x >= m_width || y >= m_height) return 0;
Chris@217 1007
Chris@148 1008 size_t col;
Chris@148 1009 FFTCache *cache = getCache(x, col);
Chris@200 1010 if (!cache) return 0;
Chris@148 1011
Chris@148 1012 if (!cache->haveSetColumnAt(col)) {
Chris@408 1013 Profiler profiler("FFTDataServer::getPhaseAt: filling");
Chris@408 1014 // hold mutex so that write thread doesn't mess with class
Chris@408 1015 // member data in fillColumn
Chris@408 1016 MutexLocker locker(&m_writeMutex,
Chris@408 1017 "FFTDataServer::getPhaseAt: m_writeMutex");
Chris@408 1018 fillColumn(x, true);
Chris@148 1019 }
Chris@148 1020 return cache->getPhaseAt(col, y);
Chris@148 1021 }
Chris@148 1022
Chris@408 1023 bool
Chris@408 1024 FFTDataServer::getPhasesAt(size_t x, float *values, size_t minbin, size_t count, size_t step)
Chris@408 1025 {
Chris@408 1026 Profiler profiler("FFTDataServer::getPhasesAt", false);
Chris@408 1027
Chris@408 1028 if (x >= m_width) return false;
Chris@408 1029
Chris@408 1030 if (minbin >= m_height) minbin = m_height - 1;
Chris@408 1031 if (count == 0) count = (m_height - minbin) / step;
Chris@408 1032 else if (minbin + count * step > m_height) {
Chris@408 1033 count = (m_height - minbin) / step;
Chris@408 1034 }
Chris@408 1035
Chris@408 1036 size_t col;
Chris@408 1037 FFTCache *cache = getCache(x, col);
Chris@408 1038 if (!cache) return false;
Chris@408 1039
Chris@408 1040 if (!cache->haveSetColumnAt(col)) {
Chris@408 1041 Profiler profiler("FFTDataServer::getPhasesAt: filling");
Chris@408 1042 MutexLocker locker(&m_writeMutex,
Chris@408 1043 "FFTDataServer::getPhasesAt: m_writeMutex");
Chris@408 1044 fillColumn(x, true);
Chris@408 1045 }
Chris@408 1046
Chris@408 1047 for (size_t i = 0; i < count; ++i) {
Chris@408 1048 values[i] = cache->getPhaseAt(col, i * step + minbin);
Chris@408 1049 }
Chris@408 1050
Chris@408 1051 return true;
Chris@408 1052 }
Chris@408 1053
Chris@148 1054 void
Chris@148 1055 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 1056 {
Chris@183 1057 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@183 1058
Chris@216 1059 if (x >= m_width || y >= m_height) {
Chris@216 1060 real = 0;
Chris@216 1061 imaginary = 0;
Chris@216 1062 return;
Chris@216 1063 }
Chris@216 1064
Chris@148 1065 size_t col;
Chris@148 1066 FFTCache *cache = getCache(x, col);
Chris@216 1067
Chris@216 1068 if (!cache) {
Chris@216 1069 real = 0;
Chris@216 1070 imaginary = 0;
Chris@216 1071 return;
Chris@216 1072 }
Chris@148 1073
Chris@148 1074 if (!cache->haveSetColumnAt(col)) {
Chris@408 1075 Profiler profiler("FFTDataServer::getValuesAt: filling");
Chris@148 1076 #ifdef DEBUG_FFT_SERVER
Chris@148 1077 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
Chris@148 1078 #endif
Chris@408 1079 // hold mutex so that write thread doesn't mess with class
Chris@408 1080 // member data in fillColumn
Chris@408 1081 MutexLocker locker(&m_writeMutex,
Chris@408 1082 "FFTDataServer::getValuesAt: m_writeMutex");
Chris@408 1083 fillColumn(x, true);
Chris@148 1084 }
Chris@264 1085
Chris@264 1086 cache->getValuesAt(col, y, real, imaginary);
Chris@148 1087 }
Chris@148 1088
Chris@148 1089 bool
Chris@148 1090 FFTDataServer::isColumnReady(size_t x)
Chris@148 1091 {
Chris@183 1092 Profiler profiler("FFTDataServer::isColumnReady", false);
Chris@183 1093
Chris@217 1094 if (x >= m_width) return true;
Chris@217 1095
Chris@148 1096 if (!haveCache(x)) {
Chris@148 1097 if (m_lastUsedCache == -1) {
Chris@183 1098 if (m_suspended) {
Chris@183 1099 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
Chris@183 1100 resume();
Chris@183 1101 }
Chris@148 1102 m_fillThread->start();
Chris@148 1103 }
Chris@148 1104 return false;
Chris@148 1105 }
Chris@148 1106
Chris@148 1107 size_t col;
Chris@148 1108 FFTCache *cache = getCache(x, col);
Chris@200 1109 if (!cache) return true;
Chris@148 1110
Chris@148 1111 return cache->haveSetColumnAt(col);
Chris@148 1112 }
Chris@148 1113
Chris@148 1114 void
Chris@408 1115 FFTDataServer::fillColumn(size_t x, bool lockHeld)
Chris@148 1116 {
Chris@183 1117 Profiler profiler("FFTDataServer::fillColumn", false);
Chris@183 1118
Chris@272 1119 if (!m_model->isReady()) {
Chris@272 1120 std::cerr << "WARNING: FFTDataServer::fillColumn("
Chris@272 1121 << x << "): model not yet ready" << std::endl;
Chris@272 1122 return;
Chris@272 1123 }
Chris@272 1124
Chris@217 1125 if (!m_fftInput) {
Chris@217 1126 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1127 << "input has already been completed and discarded?"
Chris@217 1128 << std::endl;
Chris@217 1129 return;
Chris@217 1130 }
Chris@217 1131
Chris@217 1132 if (x >= m_width) {
Chris@217 1133 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1134 << "x > width (" << x << " > " << m_width << ")"
Chris@217 1135 << std::endl;
Chris@411 1136 // abort(); //!!!
Chris@217 1137 return;
Chris@217 1138 }
Chris@217 1139
Chris@148 1140 size_t col;
Chris@148 1141 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 1142 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 1143 #endif
Chris@148 1144 FFTCache *cache = getCache(x, col);
Chris@200 1145 if (!cache) return;
Chris@148 1146
Chris@408 1147 {
Chris@408 1148 MutexLocker locker(lockHeld ? 0 : &m_writeMutex,
Chris@408 1149 "FFTDataServer::fillColumn::m_writeMutex [1]");
Chris@148 1150
Chris@408 1151 if (cache->haveSetColumnAt(col)) return;
Chris@408 1152 }
Chris@408 1153
Chris@408 1154 int winsize = m_windowSize;
Chris@408 1155 int fftsize = m_fftSize;
Chris@408 1156 int hs = fftsize/2;
Chris@408 1157
Chris@408 1158 int pfx = 0;
Chris@408 1159 int off = (fftsize - winsize) / 2;
Chris@148 1160
Chris@148 1161 int startFrame = m_windowIncrement * x;
Chris@148 1162 int endFrame = startFrame + m_windowSize;
Chris@148 1163
Chris@408 1164 startFrame -= winsize / 2;
Chris@408 1165 endFrame -= winsize / 2;
Chris@148 1166
Chris@408 1167 for (int i = 0; i < off; ++i) {
Chris@408 1168 m_fftInput[i] = 0.0;
Chris@408 1169 }
Chris@148 1170
Chris@408 1171 for (int i = 0; i < off; ++i) {
Chris@408 1172 m_fftInput[fftsize - i - 1] = 0.0;
Chris@148 1173 }
Chris@148 1174
Chris@148 1175 if (startFrame < 0) {
Chris@408 1176 pfx = -startFrame;
Chris@408 1177 for (int i = 0; i < pfx; ++i) {
Chris@148 1178 m_fftInput[off + i] = 0.0;
Chris@148 1179 }
Chris@148 1180 }
Chris@148 1181
Chris@195 1182 #ifdef DEBUG_FFT_SERVER_FILL
Chris@193 1183 std::cerr << "FFTDataServer::fillColumn: requesting frames "
Chris@193 1184 << startFrame + pfx << " -> " << endFrame << " ( = "
Chris@193 1185 << endFrame - (startFrame + pfx) << ") at index "
Chris@193 1186 << off + pfx << " in buffer of size " << m_fftSize
Chris@193 1187 << " with window size " << m_windowSize
Chris@193 1188 << " from channel " << m_channel << std::endl;
Chris@195 1189 #endif
Chris@193 1190
Chris@408 1191 int count = 0;
Chris@300 1192 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
Chris@300 1193
Chris@408 1194 int got = m_model->getData(m_channel, startFrame + pfx,
Chris@408 1195 count, m_fftInput + off + pfx);
Chris@148 1196
Chris@408 1197 while (got + pfx < winsize) {
Chris@148 1198 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1199 ++got;
Chris@148 1200 }
Chris@148 1201
Chris@148 1202 if (m_channel == -1) {
Chris@148 1203 int channels = m_model->getChannelCount();
Chris@148 1204 if (channels > 1) {
Chris@408 1205 for (int i = 0; i < winsize; ++i) {
Chris@148 1206 m_fftInput[off + i] /= channels;
Chris@148 1207 }
Chris@148 1208 }
Chris@148 1209 }
Chris@148 1210
Chris@148 1211 m_windower.cut(m_fftInput + off);
Chris@148 1212
Chris@408 1213 for (int i = 0; i < hs; ++i) {
Chris@148 1214 fftsample temp = m_fftInput[i];
Chris@408 1215 m_fftInput[i] = m_fftInput[i + hs];
Chris@408 1216 m_fftInput[i + hs] = temp;
Chris@148 1217 }
Chris@148 1218
Chris@226 1219 fftf_execute(m_fftPlan);
Chris@148 1220
Chris@408 1221 // If our cache uses polar storage, it's more friendly for us to
Chris@408 1222 // do the conversion before taking the write mutex
Chris@148 1223
Chris@408 1224 float factor = 0.f;
Chris@148 1225
Chris@408 1226 if (cache->getStorageType() == FFTCache::Compact ||
Chris@408 1227 cache->getStorageType() == FFTCache::Polar) {
Chris@408 1228
Chris@408 1229 for (int i = 0; i <= hs; ++i) {
Chris@408 1230 fftsample real = m_fftOutput[i][0];
Chris@408 1231 fftsample imag = m_fftOutput[i][1];
Chris@408 1232 float mag = sqrtf(real * real + imag * imag);
Chris@408 1233 m_workbuffer[i] = mag;
Chris@408 1234 m_workbuffer[i + hs + 1] = atan2f(imag, real);
Chris@408 1235 if (mag > factor) factor = mag;
Chris@408 1236 }
Chris@408 1237
Chris@408 1238 } else {
Chris@408 1239
Chris@408 1240 for (int i = 0; i <= hs; ++i) {
Chris@408 1241 m_workbuffer[i] = m_fftOutput[i][0];
Chris@408 1242 m_workbuffer[i + hs + 1] = m_fftOutput[i][1];
Chris@408 1243 }
Chris@148 1244 }
Chris@148 1245
Chris@408 1246 Profiler subprof("FFTDataServer::fillColumn: set to cache");
Chris@408 1247
Chris@408 1248 {
Chris@408 1249 MutexLocker locker(lockHeld ? 0 : &m_writeMutex,
Chris@408 1250 "FFTDataServer::fillColumn: m_writeMutex [2]");
Chris@408 1251
Chris@408 1252 if (cache->getStorageType() == FFTCache::Compact ||
Chris@408 1253 cache->getStorageType() == FFTCache::Polar) {
Chris@408 1254
Chris@408 1255 cache->setColumnAt(col,
Chris@408 1256 m_workbuffer,
Chris@408 1257 m_workbuffer + hs + 1,
Chris@408 1258 factor);
Chris@408 1259
Chris@408 1260 } else {
Chris@408 1261
Chris@408 1262 cache->setColumnAt(col,
Chris@408 1263 m_workbuffer,
Chris@408 1264 m_workbuffer + hs + 1);
Chris@408 1265 }
Chris@408 1266 }
Chris@154 1267
Chris@183 1268 if (m_suspended) {
Chris@183 1269 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1270 // resume();
Chris@183 1271 }
Chris@148 1272 }
Chris@148 1273
Chris@148 1274 size_t
Chris@148 1275 FFTDataServer::getFillCompletion() const
Chris@148 1276 {
Chris@148 1277 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1278 else return 100;
Chris@148 1279 }
Chris@148 1280
Chris@148 1281 size_t
Chris@148 1282 FFTDataServer::getFillExtent() const
Chris@148 1283 {
Chris@148 1284 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1285 else return m_model->getEndFrame();
Chris@148 1286 }
Chris@148 1287
Chris@148 1288 QString
Chris@148 1289 FFTDataServer::generateFileBasename() const
Chris@148 1290 {
Chris@148 1291 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1292 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1293 m_polar);
Chris@148 1294 }
Chris@148 1295
Chris@148 1296 QString
Chris@148 1297 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1298 int channel,
Chris@148 1299 WindowType windowType,
Chris@148 1300 size_t windowSize,
Chris@148 1301 size_t windowIncrement,
Chris@148 1302 size_t fftSize,
Chris@148 1303 bool polar)
Chris@148 1304 {
Chris@148 1305 char buffer[200];
Chris@148 1306
Chris@148 1307 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1308 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1309 (unsigned int)(channel + 1),
Chris@148 1310 (unsigned int)windowType,
Chris@148 1311 (unsigned int)windowSize,
Chris@148 1312 (unsigned int)windowIncrement,
Chris@148 1313 (unsigned int)fftSize,
Chris@148 1314 polar ? "-p" : "-r");
Chris@148 1315
Chris@148 1316 return buffer;
Chris@148 1317 }
Chris@148 1318
Chris@148 1319 void
Chris@148 1320 FFTDataServer::FillThread::run()
Chris@148 1321 {
Chris@411 1322 #ifdef DEBUG_FFT_SERVER_FILL
Chris@411 1323 std::cerr << "FFTDataServer::FillThread::run()" << std::endl;
Chris@411 1324 #endif
Chris@411 1325
Chris@148 1326 m_extent = 0;
Chris@148 1327 m_completion = 0;
Chris@148 1328
Chris@272 1329 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
Chris@411 1330 #ifdef DEBUG_FFT_SERVER_FILL
Chris@411 1331 std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl;
Chris@411 1332 #endif
Chris@272 1333 sleep(1);
Chris@272 1334 }
Chris@272 1335 if (m_server.m_exiting) return;
Chris@272 1336
Chris@148 1337 size_t start = m_server.m_model->getStartFrame();
Chris@148 1338 size_t end = m_server.m_model->getEndFrame();
Chris@148 1339 size_t remainingEnd = end;
Chris@148 1340
Chris@148 1341 int counter = 0;
Chris@246 1342 int updateAt = 1;
Chris@246 1343 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1344 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1345
Chris@148 1346 if (m_fillFrom > start) {
Chris@148 1347
Chris@148 1348 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1349
Chris@408 1350 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement),
Chris@408 1351 false);
Chris@148 1352
Chris@148 1353 if (m_server.m_exiting) return;
Chris@148 1354
Chris@148 1355 while (m_server.m_suspended) {
Chris@148 1356 #ifdef DEBUG_FFT_SERVER
Chris@193 1357 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1358 #endif
Chris@244 1359 {
Chris@244 1360 MutexLocker locker(&m_server.m_writeMutex,
Chris@408 1361 "FFTDataServer::run::m_writeMutex [1]");
Chris@244 1362 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1363 }
Chris@159 1364 #ifdef DEBUG_FFT_SERVER
Chris@193 1365 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1366 #endif
Chris@148 1367 if (m_server.m_exiting) return;
Chris@148 1368 }
Chris@148 1369
Chris@148 1370 if (++counter == updateAt) {
Chris@148 1371 m_extent = f;
Chris@148 1372 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1373 float(end - start)));
Chris@148 1374 counter = 0;
Chris@246 1375 if (updateAt < maxUpdateAt) {
Chris@246 1376 updateAt *= 2;
Chris@246 1377 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1378 }
Chris@148 1379 }
Chris@148 1380 }
Chris@148 1381
Chris@148 1382 remainingEnd = m_fillFrom;
Chris@148 1383 if (remainingEnd > start) --remainingEnd;
Chris@148 1384 else remainingEnd = start;
Chris@148 1385 }
Chris@148 1386
Chris@148 1387 size_t baseCompletion = m_completion;
Chris@148 1388
Chris@148 1389 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1390
Chris@408 1391 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement),
Chris@408 1392 false);
Chris@148 1393
Chris@148 1394 if (m_server.m_exiting) return;
Chris@148 1395
Chris@148 1396 while (m_server.m_suspended) {
Chris@148 1397 #ifdef DEBUG_FFT_SERVER
Chris@193 1398 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1399 #endif
Chris@244 1400 {
Chris@244 1401 MutexLocker locker(&m_server.m_writeMutex,
Chris@408 1402 "FFTDataServer::run::m_writeMutex [2]");
Chris@244 1403 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1404 }
Chris@148 1405 if (m_server.m_exiting) return;
Chris@148 1406 }
Chris@148 1407
Chris@148 1408 if (++counter == updateAt) {
Chris@148 1409 m_extent = f;
Chris@148 1410 m_completion = baseCompletion +
Chris@148 1411 size_t(100 * fabsf(float(f - start) /
Chris@148 1412 float(end - start)));
Chris@148 1413 counter = 0;
Chris@246 1414 if (updateAt < maxUpdateAt) {
Chris@246 1415 updateAt *= 2;
Chris@246 1416 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1417 }
Chris@148 1418 }
Chris@148 1419 }
Chris@148 1420
Chris@148 1421 m_completion = 100;
Chris@148 1422 m_extent = end;
Chris@148 1423 }
Chris@148 1424