annotate data/fft/FFTDataServer.cpp @ 588:d04b8674b710

* Try to identify the properly conformant audio file structure written out by Sonic Annotator (but we still don't actually import it yet)
author Chris Cannam
date Wed, 13 May 2009 13:30:08 +0000
parents dfc4dd561bb6
children 948271d124ac
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@148 51 size_t windowSize,
Chris@148 52 size_t windowIncrement,
Chris@148 53 size_t fftSize,
Chris@148 54 bool polar,
Chris@334 55 StorageAdviser::Criteria criteria,
Chris@148 56 size_t 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@148 113 size_t windowSize,
Chris@148 114 size_t windowIncrement,
Chris@148 115 size_t fftSize,
Chris@148 116 bool polar,
Chris@334 117 StorageAdviser::Criteria criteria,
Chris@148 118 size_t 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@216 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@216 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@216 231 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\")" << 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@216 239 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): 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@216 248 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): 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@216 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@216 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@216 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@152 295 std::cerr << "ERROR: FFTDataServer::claimInstance: instance "
Chris@152 296 << server << " unknown!" << std::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@148 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@148 329 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@148 330 << server << "): instance not allocated" << std::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@216 335 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 336 << server << " has never been used, erasing"
Chris@216 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@216 344 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 345 << server << " no longer in use, marking for possible collection"
Chris@216 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@216 352 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@216 353 << server << "): server is already in "
Chris@216 354 << "released servers list" << std::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@216 364 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 365 << server << " now has refcount " << i->second.second
Chris@216 366 << std::endl;
Chris@216 367 #endif
Chris@148 368 }
Chris@148 369 return;
Chris@148 370 }
Chris@148 371 }
Chris@148 372
Chris@148 373 std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
Chris@148 374 << "instance not found" << std::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@216 381 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 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@216 392 std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
Chris@216 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@215 401 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 402 << server << " is in released queue, but still has non-zero refcount "
Chris@215 403 << i->second.second << std::endl;
Chris@215 404 // ... so don't delete it
Chris@215 405 break;
Chris@215 406 }
Chris@216 407 #ifdef DEBUG_FFT_SERVER
Chris@216 408 std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
Chris@216 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@215 419 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 420 << server << " is in released queue, but not in server map!"
Chris@215 421 << std::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@216 429 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 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@216 442 std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
Chris@216 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@216 453 std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
Chris@216 454 << server << std::endl;
Chris@216 455 #endif
Chris@216 456
Chris@215 457 if (i->second.second > 0) {
Chris@362 458 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 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@216 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@216 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@148 486 size_t windowSize,
Chris@148 487 size_t windowIncrement,
Chris@148 488 size_t fftSize,
Chris@148 489 bool polar,
Chris@334 490 StorageAdviser::Criteria criteria,
Chris@148 491 size_t 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@193 512 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::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@148 517 size_t start = m_model->getStartFrame();
Chris@148 518 size_t 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@216 524 std::cerr << "FFTDataServer(" << this << "): dimensions are "
Chris@216 525 << m_width << "x" << m_height << std::endl;
Chris@216 526 #endif
Chris@216 527
Chris@148 528 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 529 size_t 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@359 534 std::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@359 546 std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
Chris@359 547 << m_cacheWidthMask << ")" << std::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@148 565 for (size_t 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@226 584 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::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@193 594 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::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@193 624 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::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@193 639 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::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@193 651 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::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@193 662 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::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@359 681 FFTDataServer::getStorageAdvice(size_t w, size_t 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@436 720 // std::cerr << "Recommendation was: " << recommendation << std::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@359 733 std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl;
Chris@359 734
Chris@359 735 std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::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@213 766 size_t 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@537 795 std::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@213 798 << "): falling back to disc cache" << std::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@537 817 } catch (std::exception e) {
Chris@537 818
Chris@537 819 delete cb->fileCacheWriter;
Chris@537 820 cb->fileCacheWriter = 0;
Chris@537 821
Chris@537 822 std::cerr << "ERROR: Failed to construct disc cache for FFT data: "
Chris@537 823 << e.what() << std::endl;
Chris@537 824 }
Chris@537 825 }
Chris@537 826
Chris@548 827 m_cacheVectorLock.lockForWrite();
Chris@548 828
Chris@537 829 m_caches[c] = cb;
Chris@537 830
Chris@548 831 m_cacheVectorLock.unlock();
Chris@548 832
Chris@537 833 return success;
Chris@148 834 }
Chris@537 835
Chris@537 836 bool
Chris@537 837 FFTDataServer::makeCacheReader(int c)
Chris@537 838 {
Chris@537 839 // preconditions: m_caches[c] exists and contains a file writer;
Chris@537 840 // m_cacheVectorLock is not locked by this thread
Chris@537 841 #ifdef DEBUG_FFT_SERVER
Chris@537 842 std::cerr << "FFTDataServer::makeCacheReader(" << c << ")" << std::endl;
Chris@537 843 #endif
Chris@148 844
Chris@537 845 QThread *me = QThread::currentThread();
Chris@537 846 QWriteLocker locker(&m_cacheVectorLock);
Chris@537 847 CacheBlock *cb(m_caches.at(c));
Chris@537 848 if (!cb || !cb->fileCacheWriter) return false;
Chris@537 849
Chris@537 850 try {
Chris@537 851
Chris@537 852 cb->fileCacheReader[me] = new FFTFileCacheReader(cb->fileCacheWriter);
Chris@537 853
Chris@537 854 } catch (std::exception e) {
Chris@537 855
Chris@537 856 delete cb->fileCacheReader[me];
Chris@537 857 cb->fileCacheReader.erase(me);
Chris@537 858
Chris@537 859 std::cerr << "ERROR: Failed to construct disc cache reader for FFT data: "
Chris@537 860 << e.what() << std::endl;
Chris@537 861 return false;
Chris@537 862 }
Chris@537 863
Chris@537 864 // erase a reader that looks like it may no longer going to be
Chris@537 865 // used by this thread for a while (leaving alone the current
Chris@537 866 // and previous cache readers)
Chris@537 867 int deleteCandidate = c - 2;
Chris@537 868 if (deleteCandidate < 0) deleteCandidate = c + 2;
Chris@537 869 if (deleteCandidate >= m_caches.size()) {
Chris@537 870 return true;
Chris@537 871 }
Chris@537 872
Chris@537 873 cb = m_caches.at(deleteCandidate);
Chris@537 874 if (cb && cb->fileCacheReader.find(me) != cb->fileCacheReader.end()) {
Chris@537 875 #ifdef DEBUG_FFT_SERVER
Chris@537 876 std::cerr << "FFTDataServer::makeCacheReader: Deleting probably unpopular reader " << deleteCandidate << " for this thread (as I create reader " << c << ")" << std::endl;
Chris@537 877 #endif
Chris@537 878 delete cb->fileCacheReader[me];
Chris@537 879 cb->fileCacheReader.erase(me);
Chris@537 880 }
Chris@537 881
Chris@537 882 return true;
Chris@537 883 }
Chris@537 884
Chris@148 885 float
Chris@148 886 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 887 {
Chris@183 888 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
Chris@183 889
Chris@217 890 if (x >= m_width || y >= m_height) return 0;
Chris@217 891
Chris@148 892 size_t col;
Chris@537 893 FFTCacheReader *cache = getCacheReader(x, col);
Chris@200 894 if (!cache) return 0;
Chris@148 895
Chris@537 896 //!!! n.b. can throw
Chris@148 897 if (!cache->haveSetColumnAt(col)) {
Chris@408 898 Profiler profiler("FFTDataServer::getMagnitudeAt: filling");
Chris@280 899 #ifdef DEBUG_FFT_SERVER
Chris@183 900 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
Chris@183 901 << x << ")" << std::endl;
Chris@280 902 #endif
Chris@550 903 fillColumn(x);
Chris@148 904 }
Chris@148 905 return cache->getMagnitudeAt(col, y);
Chris@148 906 }
Chris@148 907
Chris@408 908 bool
Chris@408 909 FFTDataServer::getMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step)
Chris@408 910 {
Chris@408 911 Profiler profiler("FFTDataServer::getMagnitudesAt", false);
Chris@408 912
Chris@408 913 if (x >= m_width) return false;
Chris@408 914
Chris@408 915 if (minbin >= m_height) minbin = m_height - 1;
Chris@408 916 if (count == 0) count = (m_height - minbin) / step;
Chris@408 917 else if (minbin + count * step > m_height) {
Chris@408 918 count = (m_height - minbin) / step;
Chris@408 919 }
Chris@408 920
Chris@408 921 size_t col;
Chris@537 922 FFTCacheReader *cache = getCacheReader(x, col);
Chris@408 923 if (!cache) return false;
Chris@408 924
Chris@537 925 //!!! n.b. can throw
Chris@408 926 if (!cache->haveSetColumnAt(col)) {
Chris@408 927 Profiler profiler("FFTDataServer::getMagnitudesAt: filling");
Chris@550 928 fillColumn(x);
Chris@408 929 }
Chris@408 930
Chris@509 931 cache->getMagnitudesAt(col, values, minbin, count, step);
Chris@509 932
Chris@408 933 return true;
Chris@408 934 }
Chris@408 935
Chris@148 936 float
Chris@148 937 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 938 {
Chris@183 939 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
Chris@183 940
Chris@217 941 if (x >= m_width || y >= m_height) return 0;
Chris@217 942
Chris@148 943 size_t col;
Chris@537 944 FFTCacheReader *cache = getCacheReader(x, col);
Chris@200 945 if (!cache) return 0;
Chris@148 946
Chris@537 947 //!!! n.b. can throw
Chris@148 948 if (!cache->haveSetColumnAt(col)) {
Chris@408 949 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling");
Chris@550 950 fillColumn(x);
Chris@148 951 }
Chris@148 952 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 953 }
Chris@148 954
Chris@408 955 bool
Chris@408 956 FFTDataServer::getNormalizedMagnitudesAt(size_t x, float *values, size_t minbin, size_t count, size_t step)
Chris@408 957 {
Chris@408 958 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false);
Chris@408 959
Chris@408 960 if (x >= m_width) return false;
Chris@408 961
Chris@408 962 if (minbin >= m_height) minbin = m_height - 1;
Chris@408 963 if (count == 0) count = (m_height - minbin) / step;
Chris@408 964 else if (minbin + count * step > m_height) {
Chris@408 965 count = (m_height - minbin) / step;
Chris@408 966 }
Chris@408 967
Chris@408 968 size_t col;
Chris@537 969 FFTCacheReader *cache = getCacheReader(x, col);
Chris@408 970 if (!cache) return false;
Chris@408 971
Chris@537 972 //!!! n.b. can throw
Chris@408 973 if (!cache->haveSetColumnAt(col)) {
Chris@408 974 Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling");
Chris@550 975 fillColumn(x);
Chris@408 976 }
Chris@408 977
Chris@408 978 for (size_t i = 0; i < count; ++i) {
Chris@408 979 values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin);
Chris@408 980 }
Chris@408 981
Chris@408 982 return true;
Chris@408 983 }
Chris@408 984
Chris@148 985 float
Chris@148 986 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 987 {
Chris@183 988 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
Chris@183 989
Chris@217 990 if (x >= m_width) return 0;
Chris@217 991
Chris@148 992 size_t col;
Chris@537 993 FFTCacheReader *cache = getCacheReader(x, col);
Chris@200 994 if (!cache) return 0;
Chris@148 995
Chris@537 996 //!!! n.b. can throw
Chris@148 997 if (!cache->haveSetColumnAt(col)) {
Chris@408 998 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling");
Chris@550 999 fillColumn(x);
Chris@148 1000 }
Chris@148 1001 return cache->getMaximumMagnitudeAt(col);
Chris@148 1002 }
Chris@148 1003
Chris@148 1004 float
Chris@148 1005 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 1006 {
Chris@183 1007 Profiler profiler("FFTDataServer::getPhaseAt", false);
Chris@183 1008
Chris@217 1009 if (x >= m_width || y >= m_height) return 0;
Chris@217 1010
Chris@148 1011 size_t col;
Chris@537 1012 FFTCacheReader *cache = getCacheReader(x, col);
Chris@200 1013 if (!cache) return 0;
Chris@148 1014
Chris@537 1015 //!!! n.b. can throw
Chris@148 1016 if (!cache->haveSetColumnAt(col)) {
Chris@408 1017 Profiler profiler("FFTDataServer::getPhaseAt: filling");
Chris@550 1018 fillColumn(x);
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@537 1037 FFTCacheReader *cache = getCacheReader(x, col);
Chris@408 1038 if (!cache) return false;
Chris@408 1039
Chris@537 1040 //!!! n.b. can throw
Chris@408 1041 if (!cache->haveSetColumnAt(col)) {
Chris@408 1042 Profiler profiler("FFTDataServer::getPhasesAt: filling");
Chris@550 1043 fillColumn(x);
Chris@408 1044 }
Chris@408 1045
Chris@408 1046 for (size_t i = 0; i < count; ++i) {
Chris@408 1047 values[i] = cache->getPhaseAt(col, i * step + minbin);
Chris@408 1048 }
Chris@408 1049
Chris@408 1050 return true;
Chris@408 1051 }
Chris@408 1052
Chris@148 1053 void
Chris@148 1054 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 1055 {
Chris@183 1056 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@183 1057
Chris@216 1058 if (x >= m_width || y >= m_height) {
Chris@216 1059 real = 0;
Chris@216 1060 imaginary = 0;
Chris@216 1061 return;
Chris@216 1062 }
Chris@216 1063
Chris@148 1064 size_t col;
Chris@537 1065 FFTCacheReader *cache = getCacheReader(x, col);
Chris@216 1066
Chris@216 1067 if (!cache) {
Chris@216 1068 real = 0;
Chris@216 1069 imaginary = 0;
Chris@216 1070 return;
Chris@216 1071 }
Chris@148 1072
Chris@537 1073 //!!! n.b. can throw
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@550 1079 fillColumn(x);
Chris@148 1080 }
Chris@264 1081
Chris@264 1082 cache->getValuesAt(col, y, real, imaginary);
Chris@148 1083 }
Chris@148 1084
Chris@148 1085 bool
Chris@556 1086 FFTDataServer::getValuesAt(size_t x, float *reals, float *imaginaries, size_t minbin, size_t count, size_t step)
Chris@556 1087 {
Chris@556 1088 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@556 1089
Chris@556 1090 if (x >= m_width) return false;
Chris@556 1091
Chris@556 1092 if (minbin >= m_height) minbin = m_height - 1;
Chris@556 1093 if (count == 0) count = (m_height - minbin) / step;
Chris@556 1094 else if (minbin + count * step > m_height) {
Chris@556 1095 count = (m_height - minbin) / step;
Chris@556 1096 }
Chris@556 1097
Chris@556 1098 size_t col;
Chris@556 1099 FFTCacheReader *cache = getCacheReader(x, col);
Chris@556 1100 if (!cache) return false;
Chris@556 1101
Chris@556 1102 //!!! n.b. can throw
Chris@556 1103 if (!cache->haveSetColumnAt(col)) {
Chris@556 1104 Profiler profiler("FFTDataServer::getValuesAt: filling");
Chris@556 1105 fillColumn(x);
Chris@556 1106 }
Chris@556 1107
Chris@556 1108 for (size_t i = 0; i < count; ++i) {
Chris@556 1109 cache->getValuesAt(col, i * step + minbin, reals[i], imaginaries[i]);
Chris@556 1110 }
Chris@556 1111
Chris@556 1112 return true;
Chris@556 1113 }
Chris@556 1114
Chris@556 1115 bool
Chris@148 1116 FFTDataServer::isColumnReady(size_t x)
Chris@148 1117 {
Chris@183 1118 Profiler profiler("FFTDataServer::isColumnReady", false);
Chris@183 1119
Chris@217 1120 if (x >= m_width) return true;
Chris@217 1121
Chris@148 1122 if (!haveCache(x)) {
Chris@537 1123 /*!!!
Chris@148 1124 if (m_lastUsedCache == -1) {
Chris@183 1125 if (m_suspended) {
Chris@183 1126 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
Chris@183 1127 resume();
Chris@183 1128 }
Chris@148 1129 m_fillThread->start();
Chris@148 1130 }
Chris@537 1131 */
Chris@148 1132 return false;
Chris@148 1133 }
Chris@148 1134
Chris@148 1135 size_t col;
Chris@537 1136 FFTCacheReader *cache = getCacheReader(x, col);
Chris@200 1137 if (!cache) return true;
Chris@148 1138
Chris@537 1139 //!!! n.b. can throw
Chris@148 1140 return cache->haveSetColumnAt(col);
Chris@148 1141 }
Chris@148 1142
Chris@148 1143 void
Chris@550 1144 FFTDataServer::fillColumn(size_t x)
Chris@148 1145 {
Chris@183 1146 Profiler profiler("FFTDataServer::fillColumn", false);
Chris@183 1147
Chris@272 1148 if (!m_model->isReady()) {
Chris@272 1149 std::cerr << "WARNING: FFTDataServer::fillColumn("
Chris@272 1150 << x << "): model not yet ready" << std::endl;
Chris@272 1151 return;
Chris@272 1152 }
Chris@549 1153 /*
Chris@217 1154 if (!m_fftInput) {
Chris@217 1155 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1156 << "input has already been completed and discarded?"
Chris@217 1157 << std::endl;
Chris@217 1158 return;
Chris@217 1159 }
Chris@549 1160 */
Chris@217 1161 if (x >= m_width) {
Chris@217 1162 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1163 << "x > width (" << x << " > " << m_width << ")"
Chris@217 1164 << std::endl;
Chris@217 1165 return;
Chris@217 1166 }
Chris@217 1167
Chris@148 1168 size_t col;
Chris@148 1169 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 1170 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 1171 #endif
Chris@537 1172 FFTCacheWriter *cache = getCacheWriter(x, col);
Chris@200 1173 if (!cache) return;
Chris@148 1174
Chris@408 1175 int winsize = m_windowSize;
Chris@408 1176 int fftsize = m_fftSize;
Chris@408 1177 int hs = fftsize/2;
Chris@408 1178
Chris@408 1179 int pfx = 0;
Chris@408 1180 int off = (fftsize - winsize) / 2;
Chris@148 1181
Chris@148 1182 int startFrame = m_windowIncrement * x;
Chris@148 1183 int endFrame = startFrame + m_windowSize;
Chris@148 1184
Chris@408 1185 startFrame -= winsize / 2;
Chris@408 1186 endFrame -= winsize / 2;
Chris@148 1187
Chris@549 1188 #ifdef DEBUG_FFT_SERVER_FILL
Chris@549 1189 std::cerr << "FFTDataServer::fillColumn: requesting frames "
Chris@549 1190 << startFrame + pfx << " -> " << endFrame << " ( = "
Chris@549 1191 << endFrame - (startFrame + pfx) << ") at index "
Chris@549 1192 << off + pfx << " in buffer of size " << m_fftSize
Chris@549 1193 << " with window size " << m_windowSize
Chris@549 1194 << " from channel " << m_channel << std::endl;
Chris@549 1195 #endif
Chris@549 1196
Chris@549 1197 QMutexLocker locker(&m_fftBuffersLock);
Chris@549 1198
Chris@550 1199 // We may have been called from a function that wanted to obtain a
Chris@550 1200 // column using an FFTCacheReader. Before calling us, it checked
Chris@550 1201 // whether the column was available already, and the reader
Chris@550 1202 // reported that it wasn't. Now we test again, with the mutex
Chris@550 1203 // held, to avoid a race condition in case another thread has
Chris@550 1204 // called fillColumn at the same time.
Chris@550 1205 if (cache->haveSetColumnAt(x & m_cacheWidthMask)) {
Chris@550 1206 return;
Chris@549 1207 }
Chris@549 1208
Chris@549 1209 if (!m_fftInput) {
Chris@549 1210 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@549 1211 << "input has already been completed and discarded?"
Chris@549 1212 << std::endl;
Chris@549 1213 return;
Chris@549 1214 }
Chris@549 1215
Chris@408 1216 for (int i = 0; i < off; ++i) {
Chris@408 1217 m_fftInput[i] = 0.0;
Chris@408 1218 }
Chris@148 1219
Chris@408 1220 for (int i = 0; i < off; ++i) {
Chris@408 1221 m_fftInput[fftsize - i - 1] = 0.0;
Chris@148 1222 }
Chris@148 1223
Chris@148 1224 if (startFrame < 0) {
Chris@408 1225 pfx = -startFrame;
Chris@408 1226 for (int i = 0; i < pfx; ++i) {
Chris@148 1227 m_fftInput[off + i] = 0.0;
Chris@148 1228 }
Chris@148 1229 }
Chris@148 1230
Chris@408 1231 int count = 0;
Chris@300 1232 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
Chris@300 1233
Chris@408 1234 int got = m_model->getData(m_channel, startFrame + pfx,
Chris@408 1235 count, m_fftInput + off + pfx);
Chris@148 1236
Chris@408 1237 while (got + pfx < winsize) {
Chris@148 1238 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1239 ++got;
Chris@148 1240 }
Chris@148 1241
Chris@148 1242 if (m_channel == -1) {
Chris@148 1243 int channels = m_model->getChannelCount();
Chris@148 1244 if (channels > 1) {
Chris@408 1245 for (int i = 0; i < winsize; ++i) {
Chris@148 1246 m_fftInput[off + i] /= channels;
Chris@148 1247 }
Chris@148 1248 }
Chris@148 1249 }
Chris@148 1250
Chris@148 1251 m_windower.cut(m_fftInput + off);
Chris@148 1252
Chris@408 1253 for (int i = 0; i < hs; ++i) {
Chris@148 1254 fftsample temp = m_fftInput[i];
Chris@408 1255 m_fftInput[i] = m_fftInput[i + hs];
Chris@408 1256 m_fftInput[i + hs] = temp;
Chris@148 1257 }
Chris@148 1258
Chris@226 1259 fftf_execute(m_fftPlan);
Chris@148 1260
Chris@408 1261 float factor = 0.f;
Chris@148 1262
Chris@408 1263 if (cache->getStorageType() == FFTCache::Compact ||
Chris@408 1264 cache->getStorageType() == FFTCache::Polar) {
Chris@408 1265
Chris@408 1266 for (int i = 0; i <= hs; ++i) {
Chris@408 1267 fftsample real = m_fftOutput[i][0];
Chris@408 1268 fftsample imag = m_fftOutput[i][1];
Chris@408 1269 float mag = sqrtf(real * real + imag * imag);
Chris@408 1270 m_workbuffer[i] = mag;
Chris@408 1271 m_workbuffer[i + hs + 1] = atan2f(imag, real);
Chris@408 1272 if (mag > factor) factor = mag;
Chris@408 1273 }
Chris@408 1274
Chris@408 1275 } else {
Chris@408 1276
Chris@408 1277 for (int i = 0; i <= hs; ++i) {
Chris@408 1278 m_workbuffer[i] = m_fftOutput[i][0];
Chris@408 1279 m_workbuffer[i + hs + 1] = m_fftOutput[i][1];
Chris@408 1280 }
Chris@148 1281 }
Chris@148 1282
Chris@408 1283 Profiler subprof("FFTDataServer::fillColumn: set to cache");
Chris@408 1284
Chris@549 1285 if (cache->getStorageType() == FFTCache::Compact ||
Chris@549 1286 cache->getStorageType() == FFTCache::Polar) {
Chris@549 1287
Chris@549 1288 cache->setColumnAt(col,
Chris@549 1289 m_workbuffer,
Chris@549 1290 m_workbuffer + hs + 1,
Chris@549 1291 factor);
Chris@408 1292
Chris@549 1293 } else {
Chris@408 1294
Chris@549 1295 cache->setColumnAt(col,
Chris@549 1296 m_workbuffer,
Chris@549 1297 m_workbuffer + hs + 1);
Chris@408 1298 }
Chris@154 1299
Chris@183 1300 if (m_suspended) {
Chris@183 1301 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1302 // resume();
Chris@183 1303 }
Chris@148 1304 }
Chris@148 1305
Chris@537 1306 void
Chris@537 1307 FFTDataServer::fillComplete()
Chris@537 1308 {
Chris@537 1309 for (int i = 0; i < int(m_caches.size()); ++i) {
Chris@537 1310 if (m_caches[i]->memoryCache) {
Chris@537 1311 m_caches[i]->memoryCache->allColumnsWritten();
Chris@537 1312 }
Chris@537 1313 if (m_caches[i]->fileCacheWriter) {
Chris@537 1314 m_caches[i]->fileCacheWriter->allColumnsWritten();
Chris@537 1315 }
Chris@537 1316 }
Chris@537 1317 }
Chris@537 1318
Chris@148 1319 size_t
Chris@148 1320 FFTDataServer::getFillCompletion() const
Chris@148 1321 {
Chris@148 1322 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1323 else return 100;
Chris@148 1324 }
Chris@148 1325
Chris@148 1326 size_t
Chris@148 1327 FFTDataServer::getFillExtent() const
Chris@148 1328 {
Chris@148 1329 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1330 else return m_model->getEndFrame();
Chris@148 1331 }
Chris@148 1332
Chris@148 1333 QString
Chris@148 1334 FFTDataServer::generateFileBasename() const
Chris@148 1335 {
Chris@148 1336 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1337 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1338 m_polar);
Chris@148 1339 }
Chris@148 1340
Chris@148 1341 QString
Chris@148 1342 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1343 int channel,
Chris@148 1344 WindowType windowType,
Chris@148 1345 size_t windowSize,
Chris@148 1346 size_t windowIncrement,
Chris@148 1347 size_t fftSize,
Chris@148 1348 bool polar)
Chris@148 1349 {
Chris@148 1350 char buffer[200];
Chris@148 1351
Chris@148 1352 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1353 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1354 (unsigned int)(channel + 1),
Chris@148 1355 (unsigned int)windowType,
Chris@148 1356 (unsigned int)windowSize,
Chris@148 1357 (unsigned int)windowIncrement,
Chris@148 1358 (unsigned int)fftSize,
Chris@148 1359 polar ? "-p" : "-r");
Chris@148 1360
Chris@148 1361 return buffer;
Chris@148 1362 }
Chris@148 1363
Chris@148 1364 void
Chris@148 1365 FFTDataServer::FillThread::run()
Chris@148 1366 {
Chris@411 1367 #ifdef DEBUG_FFT_SERVER_FILL
Chris@411 1368 std::cerr << "FFTDataServer::FillThread::run()" << std::endl;
Chris@411 1369 #endif
Chris@411 1370
Chris@148 1371 m_extent = 0;
Chris@148 1372 m_completion = 0;
Chris@148 1373
Chris@272 1374 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
Chris@411 1375 #ifdef DEBUG_FFT_SERVER_FILL
Chris@411 1376 std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl;
Chris@411 1377 #endif
Chris@272 1378 sleep(1);
Chris@272 1379 }
Chris@272 1380 if (m_server.m_exiting) return;
Chris@272 1381
Chris@148 1382 size_t start = m_server.m_model->getStartFrame();
Chris@148 1383 size_t end = m_server.m_model->getEndFrame();
Chris@148 1384 size_t remainingEnd = end;
Chris@148 1385
Chris@148 1386 int counter = 0;
Chris@246 1387 int updateAt = 1;
Chris@246 1388 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1389 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1390
Chris@148 1391 if (m_fillFrom > start) {
Chris@148 1392
Chris@148 1393 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1394
Chris@549 1395 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1396
Chris@148 1397 if (m_server.m_exiting) return;
Chris@148 1398
Chris@148 1399 while (m_server.m_suspended) {
Chris@148 1400 #ifdef DEBUG_FFT_SERVER
Chris@193 1401 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1402 #endif
Chris@549 1403 MutexLocker locker(&m_server.m_fftBuffersLock,
Chris@549 1404 "FFTDataServer::run::m_fftBuffersLock [1]");
Chris@549 1405 if (m_server.m_suspended && !m_server.m_exiting) {
Chris@549 1406 m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000);
Chris@244 1407 }
Chris@159 1408 #ifdef DEBUG_FFT_SERVER
Chris@193 1409 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1410 #endif
Chris@148 1411 if (m_server.m_exiting) return;
Chris@148 1412 }
Chris@148 1413
Chris@148 1414 if (++counter == updateAt) {
Chris@148 1415 m_extent = f;
Chris@148 1416 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1417 float(end - start)));
Chris@148 1418 counter = 0;
Chris@246 1419 if (updateAt < maxUpdateAt) {
Chris@246 1420 updateAt *= 2;
Chris@246 1421 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1422 }
Chris@148 1423 }
Chris@148 1424 }
Chris@148 1425
Chris@148 1426 remainingEnd = m_fillFrom;
Chris@148 1427 if (remainingEnd > start) --remainingEnd;
Chris@148 1428 else remainingEnd = start;
Chris@148 1429 }
Chris@148 1430
Chris@148 1431 size_t baseCompletion = m_completion;
Chris@148 1432
Chris@148 1433 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1434
Chris@549 1435 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1436
Chris@148 1437 if (m_server.m_exiting) return;
Chris@148 1438
Chris@148 1439 while (m_server.m_suspended) {
Chris@148 1440 #ifdef DEBUG_FFT_SERVER
Chris@193 1441 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1442 #endif
Chris@244 1443 {
Chris@549 1444 MutexLocker locker(&m_server.m_fftBuffersLock,
Chris@549 1445 "FFTDataServer::run::m_fftBuffersLock [2]");
Chris@549 1446 if (m_server.m_suspended && !m_server.m_exiting) {
Chris@549 1447 m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000);
Chris@549 1448 }
Chris@244 1449 }
Chris@148 1450 if (m_server.m_exiting) return;
Chris@148 1451 }
Chris@148 1452
Chris@148 1453 if (++counter == updateAt) {
Chris@148 1454 m_extent = f;
Chris@148 1455 m_completion = baseCompletion +
Chris@148 1456 size_t(100 * fabsf(float(f - start) /
Chris@148 1457 float(end - start)));
Chris@148 1458 counter = 0;
Chris@246 1459 if (updateAt < maxUpdateAt) {
Chris@246 1460 updateAt *= 2;
Chris@246 1461 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1462 }
Chris@148 1463 }
Chris@148 1464 }
Chris@148 1465
Chris@537 1466 m_server.fillComplete();
Chris@148 1467 m_completion = 100;
Chris@148 1468 m_extent = end;
Chris@537 1469
Chris@537 1470 #ifdef DEBUG_FFT_SERVER
Chris@537 1471 std::cerr << "FFTDataServer::FillThread::run exiting" << std::endl;
Chris@537 1472 #endif
Chris@148 1473 }
Chris@148 1474