annotate data/fft/FFTDataServer.cpp @ 335:02d2ad95ea52 spectrogram-cache-rejig

* Get storage advice for each cache in an FFT data server. Allows us to be more confident about the actual memory situation and cut over from memory to disc part way through an FFT calculation if necessary. StorageAdviser is now a bit too optimistic though (it's too keen to allocate large numbers of small blocks in memory).
author Chris Cannam
date Tue, 13 Nov 2007 13:54:10 +0000
parents aa8dbac62024
children cc4eb32efc6c 6f6ab834449d
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@200 30 #include <QMessageBox>
Chris@200 31 #include <QApplication>
Chris@200 32
Chris@335 33 #define DEBUG_FFT_SERVER 1
Chris@194 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@244 68 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getInstance]");
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@244 145 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getFuzzyInstance]");
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@244 264 "FFTDataServer::m_serverMapMutex[claimInstance]");
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@244 309 "FFTDataServer::m_serverMapMutex[releaseInstance]");
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@148 332 if (server->m_lastUsedCache == -1) { // never used
Chris@216 333 #ifdef DEBUG_FFT_SERVER
Chris@216 334 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 335 << server << " has never been used, erasing"
Chris@216 336 << std::endl;
Chris@216 337 #endif
Chris@148 338 delete server;
Chris@148 339 m_servers.erase(i);
Chris@148 340 } else {
Chris@216 341 #ifdef DEBUG_FFT_SERVER
Chris@216 342 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 343 << server << " no longer in use, marking for possible collection"
Chris@216 344 << std::endl;
Chris@216 345 #endif
Chris@216 346 bool found = false;
Chris@216 347 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@216 348 j != m_releasedServers.end(); ++j) {
Chris@216 349 if (*j == server) {
Chris@216 350 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@216 351 << server << "): server is already in "
Chris@216 352 << "released servers list" << std::endl;
Chris@216 353 found = true;
Chris@216 354 }
Chris@216 355 }
Chris@216 356 if (!found) m_releasedServers.push_back(server);
Chris@148 357 server->suspend();
Chris@148 358 purgeLimbo();
Chris@148 359 }
Chris@216 360 } else {
Chris@216 361 #ifdef DEBUG_FFT_SERVER
Chris@216 362 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 363 << server << " now has refcount " << i->second.second
Chris@216 364 << std::endl;
Chris@216 365 #endif
Chris@148 366 }
Chris@148 367 return;
Chris@148 368 }
Chris@148 369 }
Chris@148 370
Chris@148 371 std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
Chris@148 372 << "instance not found" << std::endl;
Chris@148 373 }
Chris@148 374
Chris@148 375 void
Chris@148 376 FFTDataServer::purgeLimbo(int maxSize)
Chris@148 377 {
Chris@216 378 #ifdef DEBUG_FFT_SERVER
Chris@216 379 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 380 << m_releasedServers.size() << " candidates" << std::endl;
Chris@216 381 #endif
Chris@216 382
Chris@259 383 while (int(m_releasedServers.size()) > maxSize) {
Chris@148 384
Chris@215 385 FFTDataServer *server = *m_releasedServers.begin();
Chris@148 386
Chris@215 387 bool found = false;
Chris@215 388
Chris@216 389 #ifdef DEBUG_FFT_SERVER
Chris@216 390 std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
Chris@216 391 << server << std::endl;
Chris@216 392 #endif
Chris@216 393
Chris@215 394 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@215 395
Chris@215 396 if (i->second.first == server) {
Chris@215 397 found = true;
Chris@215 398 if (i->second.second > 0) {
Chris@215 399 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 400 << server << " is in released queue, but still has non-zero refcount "
Chris@215 401 << i->second.second << std::endl;
Chris@215 402 // ... so don't delete it
Chris@215 403 break;
Chris@215 404 }
Chris@216 405 #ifdef DEBUG_FFT_SERVER
Chris@216 406 std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
Chris@216 407 << std::endl;
Chris@216 408 #endif
Chris@216 409
Chris@148 410 m_servers.erase(i);
Chris@215 411 delete server;
Chris@215 412 break;
Chris@148 413 }
Chris@148 414 }
Chris@215 415
Chris@215 416 if (!found) {
Chris@215 417 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 418 << server << " is in released queue, but not in server map!"
Chris@215 419 << std::endl;
Chris@215 420 delete server;
Chris@215 421 }
Chris@215 422
Chris@215 423 m_releasedServers.pop_front();
Chris@215 424 }
Chris@216 425
Chris@216 426 #ifdef DEBUG_FFT_SERVER
Chris@216 427 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 428 << m_releasedServers.size() << " remain" << std::endl;
Chris@216 429 #endif
Chris@216 430
Chris@215 431 }
Chris@215 432
Chris@215 433 void
Chris@215 434 FFTDataServer::modelAboutToBeDeleted(Model *model)
Chris@215 435 {
Chris@244 436 MutexLocker locker(&m_serverMapMutex,
Chris@244 437 "FFTDataServer::m_serverMapMutex[modelAboutToBeDeleted]");
Chris@215 438
Chris@216 439 #ifdef DEBUG_FFT_SERVER
Chris@216 440 std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
Chris@216 441 << std::endl;
Chris@216 442 #endif
Chris@216 443
Chris@215 444 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@215 445
Chris@215 446 FFTDataServer *server = i->second.first;
Chris@215 447
Chris@215 448 if (server->getModel() == model) {
Chris@216 449
Chris@216 450 #ifdef DEBUG_FFT_SERVER
Chris@216 451 std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
Chris@216 452 << server << std::endl;
Chris@216 453 #endif
Chris@216 454
Chris@215 455 if (i->second.second > 0) {
Chris@215 456 std::cerr << "ERROR: 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@215 457 }
Chris@215 458 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@215 459 j != m_releasedServers.end(); ++j) {
Chris@215 460 if (*j == server) {
Chris@216 461 #ifdef DEBUG_FFT_SERVER
Chris@216 462 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
Chris@216 463 #endif
Chris@215 464 m_releasedServers.erase(j);
Chris@215 465 break;
Chris@215 466 }
Chris@215 467 }
Chris@216 468 #ifdef DEBUG_FFT_SERVER
Chris@216 469 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
Chris@216 470 #endif
Chris@215 471 m_servers.erase(i);
Chris@215 472 delete server;
Chris@215 473 return;
Chris@215 474 }
Chris@148 475 }
Chris@148 476 }
Chris@148 477
Chris@148 478 FFTDataServer::FFTDataServer(QString fileBaseName,
Chris@148 479 const DenseTimeValueModel *model,
Chris@148 480 int channel,
Chris@148 481 WindowType windowType,
Chris@148 482 size_t windowSize,
Chris@148 483 size_t windowIncrement,
Chris@148 484 size_t fftSize,
Chris@148 485 bool polar,
Chris@334 486 StorageAdviser::Criteria criteria,
Chris@148 487 size_t fillFromColumn) :
Chris@148 488 m_fileBaseName(fileBaseName),
Chris@148 489 m_model(model),
Chris@148 490 m_channel(channel),
Chris@148 491 m_windower(windowType, windowSize),
Chris@148 492 m_windowSize(windowSize),
Chris@148 493 m_windowIncrement(windowIncrement),
Chris@148 494 m_fftSize(fftSize),
Chris@148 495 m_polar(polar),
Chris@183 496 m_width(0),
Chris@183 497 m_height(0),
Chris@183 498 m_cacheWidth(0),
Chris@335 499 m_cacheWidthPower(0),
Chris@335 500 m_cacheWidthMask(0),
Chris@148 501 m_lastUsedCache(-1),
Chris@335 502 m_criteria(criteria),
Chris@148 503 m_fftInput(0),
Chris@148 504 m_exiting(false),
Chris@153 505 m_suspended(true), //!!! or false?
Chris@148 506 m_fillThread(0)
Chris@148 507 {
Chris@193 508 #ifdef DEBUG_FFT_SERVER
Chris@193 509 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::endl;
Chris@193 510 #endif
Chris@193 511
Chris@272 512 //!!! end is not correct until model finished reading -- what to do???
Chris@272 513
Chris@148 514 size_t start = m_model->getStartFrame();
Chris@148 515 size_t end = m_model->getEndFrame();
Chris@148 516
Chris@148 517 m_width = (end - start) / m_windowIncrement + 1;
Chris@203 518 m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
Chris@148 519
Chris@216 520 #ifdef DEBUG_FFT_SERVER
Chris@216 521 std::cerr << "FFTDataServer(" << this << "): dimensions are "
Chris@216 522 << m_width << "x" << m_height << std::endl;
Chris@216 523 #endif
Chris@216 524
Chris@148 525 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 526 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
Chris@148 527 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
Chris@148 528 else m_cacheWidth = maxCacheSize / columnSize;
Chris@148 529
Chris@335 530 #ifdef DEBUG_FFT_SERVER
Chris@335 531 std::cerr << "FFTDataServer(" << this << "): cache width nominal "
Chris@335 532 << m_cacheWidth << ", actual ";
Chris@335 533 #endif
Chris@335 534
Chris@148 535 int bits = 0;
Chris@335 536 while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
Chris@335 537 m_cacheWidthPower = bits + 1;
Chris@148 538 m_cacheWidth = 2;
Chris@148 539 while (bits) { m_cacheWidth <<= 1; --bits; }
Chris@335 540 m_cacheWidthMask = m_cacheWidth - 1;
Chris@172 541
Chris@335 542 #ifdef DEBUG_FFT_SERVER
Chris@335 543 std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
Chris@335 544 << m_cacheWidthMask << ")" << std::endl;
Chris@335 545 #endif
Chris@335 546
Chris@335 547 if (m_criteria == StorageAdviser::NoCriteria) {
Chris@172 548
Chris@334 549 // assume "spectrogram" criteria for polar ffts, and "feature
Chris@334 550 // extraction" criteria for rectangular ones.
Chris@334 551
Chris@334 552 if (m_polar) {
Chris@335 553 m_criteria = StorageAdviser::Criteria
Chris@334 554 (StorageAdviser::SpeedCritical |
Chris@334 555 StorageAdviser::LongRetentionLikely);
Chris@334 556 } else {
Chris@335 557 m_criteria = StorageAdviser::Criteria
Chris@334 558 (StorageAdviser::PrecisionCritical);
Chris@334 559 }
Chris@172 560 }
Chris@172 561
Chris@148 562 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
Chris@148 563 m_caches.push_back(0);
Chris@148 564 }
Chris@148 565
Chris@148 566 m_fftInput = (fftsample *)
Chris@226 567 fftf_malloc(fftSize * sizeof(fftsample));
Chris@148 568
Chris@226 569 m_fftOutput = (fftf_complex *)
Chris@226 570 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
Chris@148 571
Chris@148 572 m_workbuffer = (float *)
Chris@226 573 fftf_malloc((fftSize+2) * sizeof(float));
Chris@148 574
Chris@226 575 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
Chris@334 576 m_fftInput,
Chris@334 577 m_fftOutput,
Chris@334 578 FFTW_MEASURE);
Chris@148 579
Chris@148 580 if (!m_fftPlan) {
Chris@226 581 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
Chris@148 582 throw(0);
Chris@148 583 }
Chris@148 584
Chris@148 585 m_fillThread = new FillThread(*this, fillFromColumn);
Chris@148 586 }
Chris@148 587
Chris@148 588 FFTDataServer::~FFTDataServer()
Chris@148 589 {
Chris@148 590 #ifdef DEBUG_FFT_SERVER
Chris@193 591 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl;
Chris@148 592 #endif
Chris@148 593
Chris@155 594 m_suspended = false;
Chris@148 595 m_exiting = true;
Chris@148 596 m_condition.wakeAll();
Chris@148 597 if (m_fillThread) {
Chris@148 598 m_fillThread->wait();
Chris@148 599 delete m_fillThread;
Chris@148 600 }
Chris@148 601
Chris@244 602 MutexLocker locker(&m_writeMutex,
Chris@244 603 "FFTDataServer::m_writeMutex[~FFTDataServer]");
Chris@148 604
Chris@148 605 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@335 606
Chris@205 607 if (*i) {
Chris@205 608 delete *i;
Chris@205 609 }
Chris@148 610 }
Chris@148 611
Chris@148 612 deleteProcessingData();
Chris@148 613 }
Chris@148 614
Chris@148 615 void
Chris@148 616 FFTDataServer::deleteProcessingData()
Chris@148 617 {
Chris@193 618 #ifdef DEBUG_FFT_SERVER
Chris@193 619 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl;
Chris@193 620 #endif
Chris@148 621 if (m_fftInput) {
Chris@226 622 fftf_destroy_plan(m_fftPlan);
Chris@226 623 fftf_free(m_fftInput);
Chris@226 624 fftf_free(m_fftOutput);
Chris@226 625 fftf_free(m_workbuffer);
Chris@148 626 }
Chris@148 627 m_fftInput = 0;
Chris@148 628 }
Chris@148 629
Chris@148 630 void
Chris@148 631 FFTDataServer::suspend()
Chris@148 632 {
Chris@148 633 #ifdef DEBUG_FFT_SERVER
Chris@193 634 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl;
Chris@148 635 #endif
Chris@183 636 Profiler profiler("FFTDataServer::suspend", false);
Chris@183 637
Chris@244 638 MutexLocker locker(&m_writeMutex,
Chris@244 639 "FFTDataServer::m_writeMutex[suspend]");
Chris@148 640 m_suspended = true;
Chris@148 641 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 642 if (*i) (*i)->suspend();
Chris@148 643 }
Chris@148 644 }
Chris@148 645
Chris@148 646 void
Chris@155 647 FFTDataServer::suspendWrites()
Chris@155 648 {
Chris@155 649 #ifdef DEBUG_FFT_SERVER
Chris@193 650 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::endl;
Chris@155 651 #endif
Chris@183 652 Profiler profiler("FFTDataServer::suspendWrites", false);
Chris@183 653
Chris@155 654 m_suspended = true;
Chris@155 655 }
Chris@155 656
Chris@155 657 void
Chris@148 658 FFTDataServer::resume()
Chris@148 659 {
Chris@154 660 #ifdef DEBUG_FFT_SERVER
Chris@193 661 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::endl;
Chris@154 662 #endif
Chris@183 663 Profiler profiler("FFTDataServer::resume", false);
Chris@183 664
Chris@148 665 m_suspended = false;
Chris@157 666 if (m_fillThread) {
Chris@157 667 if (m_fillThread->isFinished()) {
Chris@157 668 delete m_fillThread;
Chris@157 669 m_fillThread = 0;
Chris@157 670 deleteProcessingData();
Chris@157 671 } else {
Chris@157 672 m_condition.wakeAll();
Chris@157 673 }
Chris@157 674 }
Chris@148 675 }
Chris@148 676
Chris@335 677 void
Chris@335 678 FFTDataServer::getStorageAdvice(size_t w, size_t h,
Chris@335 679 bool &memoryCache, bool &compactCache)
Chris@335 680 {
Chris@335 681 int cells = w * h;
Chris@335 682 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
Chris@335 683 int maximumSize = (cells / 1024) * sizeof(float); // kb
Chris@335 684
Chris@335 685 // We don't have a compact rectangular representation, and compact
Chris@335 686 // of course is never precision-critical
Chris@335 687
Chris@335 688 bool canCompact = true;
Chris@335 689 if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
Chris@335 690 canCompact = false;
Chris@335 691 minimumSize = maximumSize; // don't use compact
Chris@335 692 }
Chris@335 693
Chris@335 694 StorageAdviser::Recommendation recommendation;
Chris@335 695
Chris@335 696 try {
Chris@335 697
Chris@335 698 recommendation =
Chris@335 699 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
Chris@335 700
Chris@335 701 } catch (InsufficientDiscSpace s) {
Chris@335 702
Chris@335 703 // Delete any unused servers we may have been leaving around
Chris@335 704 // in case we wanted them again
Chris@335 705
Chris@335 706 purgeLimbo(0);
Chris@335 707
Chris@335 708 // This time we don't catch InsufficientDiscSpace -- we
Chris@335 709 // haven't allocated anything yet and can safely let the
Chris@335 710 // exception out to indicate to the caller that we can't
Chris@335 711 // handle it.
Chris@335 712
Chris@335 713 recommendation =
Chris@335 714 StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
Chris@335 715 }
Chris@335 716
Chris@335 717 std::cerr << "Recommendation was: " << recommendation << std::endl;
Chris@335 718
Chris@335 719 memoryCache = false;
Chris@335 720
Chris@335 721 if ((recommendation & StorageAdviser::UseMemory) ||
Chris@335 722 (recommendation & StorageAdviser::PreferMemory)) {
Chris@335 723 memoryCache = true;
Chris@335 724 }
Chris@335 725
Chris@335 726 compactCache = canCompact &&
Chris@335 727 (recommendation & StorageAdviser::ConserveSpace);
Chris@335 728
Chris@335 729 std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl;
Chris@335 730
Chris@335 731 #ifdef DEBUG_FFT_SERVER
Chris@335 732 std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl;
Chris@335 733 #endif
Chris@335 734 }
Chris@335 735
Chris@148 736 FFTCache *
Chris@148 737 FFTDataServer::getCacheAux(size_t c)
Chris@148 738 {
Chris@183 739 Profiler profiler("FFTDataServer::getCacheAux", false);
Chris@193 740 #ifdef DEBUG_FFT_SERVER
Chris@193 741 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl;
Chris@193 742 #endif
Chris@183 743
Chris@244 744 MutexLocker locker(&m_writeMutex,
Chris@244 745 "FFTDataServer::m_writeMutex[getCacheAux]");
Chris@148 746
Chris@148 747 if (m_lastUsedCache == -1) {
Chris@148 748 m_fillThread->start();
Chris@148 749 }
Chris@148 750
Chris@148 751 if (int(c) != m_lastUsedCache) {
Chris@148 752
Chris@148 753 // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
Chris@148 754
Chris@148 755 for (IntQueue::iterator i = m_dormantCaches.begin();
Chris@148 756 i != m_dormantCaches.end(); ++i) {
Chris@259 757 if (*i == int(c)) {
Chris@148 758 m_dormantCaches.erase(i);
Chris@148 759 break;
Chris@148 760 }
Chris@148 761 }
Chris@148 762
Chris@148 763 if (m_lastUsedCache >= 0) {
Chris@148 764 bool inDormant = false;
Chris@148 765 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
Chris@148 766 if (m_dormantCaches[i] == m_lastUsedCache) {
Chris@148 767 inDormant = true;
Chris@148 768 break;
Chris@148 769 }
Chris@148 770 }
Chris@148 771 if (!inDormant) {
Chris@148 772 m_dormantCaches.push_back(m_lastUsedCache);
Chris@148 773 }
Chris@148 774 while (m_dormantCaches.size() > 4) {
Chris@148 775 int dc = m_dormantCaches.front();
Chris@148 776 m_dormantCaches.pop_front();
Chris@148 777 m_caches[dc]->suspend();
Chris@148 778 }
Chris@148 779 }
Chris@148 780 }
Chris@148 781
Chris@148 782 if (m_caches[c]) {
Chris@148 783 m_lastUsedCache = c;
Chris@148 784 return m_caches[c];
Chris@148 785 }
Chris@148 786
Chris@148 787 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
Chris@148 788
Chris@172 789 FFTCache *cache = 0;
Chris@172 790
Chris@213 791 size_t width = m_cacheWidth;
Chris@213 792 if (c * m_cacheWidth + width > m_width) {
Chris@213 793 width = m_width - c * m_cacheWidth;
Chris@213 794 }
Chris@213 795
Chris@335 796 bool memoryCache = false;
Chris@335 797 bool compactCache = false;
Chris@335 798
Chris@335 799 getStorageAdvice(width, m_height, memoryCache, compactCache);
Chris@335 800
Chris@200 801 try {
Chris@264 802
Chris@335 803 if (memoryCache) {
Chris@172 804
Chris@264 805 cache = new FFTMemoryCache
Chris@335 806 (compactCache ? FFTMemoryCache::Compact :
Chris@335 807 m_polar ? FFTMemoryCache::Polar :
Chris@335 808 FFTMemoryCache::Rectangular);
Chris@172 809
Chris@200 810 } else {
Chris@172 811
Chris@264 812 cache = new FFTFileCache
Chris@264 813 (name,
Chris@264 814 MatrixFile::ReadWrite,
Chris@335 815 compactCache ? FFTFileCache::Compact :
Chris@335 816 m_polar ? FFTFileCache::Polar :
Chris@335 817 FFTFileCache::Rectangular);
Chris@200 818 }
Chris@200 819
Chris@200 820 cache->resize(width, m_height);
Chris@200 821 cache->reset();
Chris@200 822
Chris@213 823 } catch (std::bad_alloc) {
Chris@205 824
Chris@213 825 delete cache;
Chris@213 826 cache = 0;
Chris@213 827
Chris@335 828 if (memoryCache) {
Chris@213 829
Chris@213 830 std::cerr << "WARNING: Memory allocation failed when resizing"
Chris@213 831 << " FFT memory cache no. " << c << " to " << width
Chris@213 832 << "x" << m_height << " (of total width " << m_width
Chris@213 833 << "): falling back to disc cache" << std::endl;
Chris@213 834
Chris@213 835 try {
Chris@213 836
Chris@335 837 purgeLimbo(0);
Chris@335 838
Chris@335 839 cache = new FFTFileCache(name,
Chris@335 840 MatrixFile::ReadWrite,
Chris@213 841 FFTFileCache::Compact);
Chris@213 842
Chris@213 843 cache->resize(width, m_height);
Chris@213 844 cache->reset();
Chris@213 845
Chris@213 846 } catch (std::bad_alloc) {
Chris@213 847
Chris@213 848 delete cache;
Chris@213 849 cache = 0;
Chris@213 850 }
Chris@213 851 }
Chris@213 852
Chris@213 853 if (cache) {
Chris@213 854 std::cerr << "ERROR: Memory allocation failed when resizing"
Chris@213 855 << " FFT file cache no. " << c << " to " << width
Chris@213 856 << "x" << m_height << " (of total width " << m_width
Chris@213 857 << "): abandoning this cache" << std::endl;
Chris@213 858 }
Chris@213 859
Chris@200 860 //!!! Shouldn't be using QtGui here. Need a better way to report this.
Chris@200 861 QMessageBox::critical
Chris@200 862 (0, QApplication::tr("FFT cache resize failed"),
Chris@200 863 QApplication::tr
Chris@200 864 ("Failed to create or resize an FFT model slice.\n"
Chris@200 865 "There may be insufficient memory or disc space to continue."));
Chris@172 866 }
Chris@148 867
Chris@148 868 m_caches[c] = cache;
Chris@148 869 m_lastUsedCache = c;
Chris@148 870 return cache;
Chris@148 871 }
Chris@148 872
Chris@148 873 float
Chris@148 874 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 875 {
Chris@183 876 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
Chris@183 877
Chris@217 878 if (x >= m_width || y >= m_height) return 0;
Chris@217 879
Chris@148 880 size_t col;
Chris@148 881 FFTCache *cache = getCache(x, col);
Chris@200 882 if (!cache) return 0;
Chris@148 883
Chris@148 884 if (!cache->haveSetColumnAt(col)) {
Chris@280 885 #ifdef DEBUG_FFT_SERVER
Chris@183 886 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
Chris@183 887 << x << ")" << std::endl;
Chris@280 888 #endif
Chris@148 889 fillColumn(x);
Chris@148 890 }
Chris@148 891 return cache->getMagnitudeAt(col, y);
Chris@148 892 }
Chris@148 893
Chris@148 894 float
Chris@148 895 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 896 {
Chris@183 897 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
Chris@183 898
Chris@217 899 if (x >= m_width || y >= m_height) return 0;
Chris@217 900
Chris@148 901 size_t col;
Chris@148 902 FFTCache *cache = getCache(x, col);
Chris@200 903 if (!cache) return 0;
Chris@148 904
Chris@148 905 if (!cache->haveSetColumnAt(col)) {
Chris@148 906 fillColumn(x);
Chris@148 907 }
Chris@148 908 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 909 }
Chris@148 910
Chris@148 911 float
Chris@148 912 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 913 {
Chris@183 914 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
Chris@183 915
Chris@217 916 if (x >= m_width) return 0;
Chris@217 917
Chris@148 918 size_t col;
Chris@148 919 FFTCache *cache = getCache(x, col);
Chris@200 920 if (!cache) return 0;
Chris@148 921
Chris@148 922 if (!cache->haveSetColumnAt(col)) {
Chris@148 923 fillColumn(x);
Chris@148 924 }
Chris@148 925 return cache->getMaximumMagnitudeAt(col);
Chris@148 926 }
Chris@148 927
Chris@148 928 float
Chris@148 929 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 930 {
Chris@183 931 Profiler profiler("FFTDataServer::getPhaseAt", false);
Chris@183 932
Chris@217 933 if (x >= m_width || y >= m_height) return 0;
Chris@217 934
Chris@148 935 size_t col;
Chris@148 936 FFTCache *cache = getCache(x, col);
Chris@200 937 if (!cache) return 0;
Chris@148 938
Chris@148 939 if (!cache->haveSetColumnAt(col)) {
Chris@148 940 fillColumn(x);
Chris@148 941 }
Chris@148 942 return cache->getPhaseAt(col, y);
Chris@148 943 }
Chris@148 944
Chris@148 945 void
Chris@148 946 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 947 {
Chris@183 948 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@183 949
Chris@216 950 if (x >= m_width || y >= m_height) {
Chris@216 951 real = 0;
Chris@216 952 imaginary = 0;
Chris@216 953 return;
Chris@216 954 }
Chris@216 955
Chris@148 956 size_t col;
Chris@148 957 FFTCache *cache = getCache(x, col);
Chris@216 958
Chris@216 959 if (!cache) {
Chris@216 960 real = 0;
Chris@216 961 imaginary = 0;
Chris@216 962 return;
Chris@216 963 }
Chris@148 964
Chris@148 965 if (!cache->haveSetColumnAt(col)) {
Chris@148 966 #ifdef DEBUG_FFT_SERVER
Chris@148 967 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
Chris@148 968 #endif
Chris@148 969 fillColumn(x);
Chris@148 970 }
Chris@264 971
Chris@264 972 cache->getValuesAt(col, y, real, imaginary);
Chris@148 973 }
Chris@148 974
Chris@148 975 bool
Chris@148 976 FFTDataServer::isColumnReady(size_t x)
Chris@148 977 {
Chris@183 978 Profiler profiler("FFTDataServer::isColumnReady", false);
Chris@183 979
Chris@217 980 if (x >= m_width) return true;
Chris@217 981
Chris@148 982 if (!haveCache(x)) {
Chris@148 983 if (m_lastUsedCache == -1) {
Chris@183 984 if (m_suspended) {
Chris@183 985 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
Chris@183 986 resume();
Chris@183 987 }
Chris@148 988 m_fillThread->start();
Chris@148 989 }
Chris@148 990 return false;
Chris@148 991 }
Chris@148 992
Chris@148 993 size_t col;
Chris@148 994 FFTCache *cache = getCache(x, col);
Chris@200 995 if (!cache) return true;
Chris@148 996
Chris@148 997 return cache->haveSetColumnAt(col);
Chris@148 998 }
Chris@148 999
Chris@148 1000 void
Chris@148 1001 FFTDataServer::fillColumn(size_t x)
Chris@148 1002 {
Chris@183 1003 Profiler profiler("FFTDataServer::fillColumn", false);
Chris@183 1004
Chris@272 1005 if (!m_model->isReady()) {
Chris@272 1006 std::cerr << "WARNING: FFTDataServer::fillColumn("
Chris@272 1007 << x << "): model not yet ready" << std::endl;
Chris@272 1008 return;
Chris@272 1009 }
Chris@272 1010
Chris@217 1011 if (!m_fftInput) {
Chris@217 1012 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1013 << "input has already been completed and discarded?"
Chris@217 1014 << std::endl;
Chris@217 1015 return;
Chris@217 1016 }
Chris@217 1017
Chris@217 1018 if (x >= m_width) {
Chris@217 1019 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1020 << "x > width (" << x << " > " << m_width << ")"
Chris@217 1021 << std::endl;
Chris@217 1022 return;
Chris@217 1023 }
Chris@217 1024
Chris@148 1025 size_t col;
Chris@148 1026 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 1027 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 1028 #endif
Chris@148 1029 FFTCache *cache = getCache(x, col);
Chris@200 1030 if (!cache) return;
Chris@148 1031
Chris@244 1032 MutexLocker locker(&m_writeMutex,
Chris@244 1033 "FFTDataServer::m_writeMutex[fillColumn]");
Chris@148 1034
Chris@148 1035 if (cache->haveSetColumnAt(col)) return;
Chris@148 1036
Chris@148 1037 int startFrame = m_windowIncrement * x;
Chris@148 1038 int endFrame = startFrame + m_windowSize;
Chris@148 1039
Chris@295 1040 startFrame -= int(m_windowSize) / 2;
Chris@295 1041 endFrame -= int(m_windowSize) / 2;
Chris@148 1042 size_t pfx = 0;
Chris@148 1043
Chris@148 1044 size_t off = (m_fftSize - m_windowSize) / 2;
Chris@148 1045
Chris@148 1046 for (size_t i = 0; i < off; ++i) {
Chris@148 1047 m_fftInput[i] = 0.0;
Chris@148 1048 m_fftInput[m_fftSize - i - 1] = 0.0;
Chris@148 1049 }
Chris@148 1050
Chris@148 1051 if (startFrame < 0) {
Chris@148 1052 pfx = size_t(-startFrame);
Chris@148 1053 for (size_t i = 0; i < pfx; ++i) {
Chris@148 1054 m_fftInput[off + i] = 0.0;
Chris@148 1055 }
Chris@148 1056 }
Chris@148 1057
Chris@195 1058 #ifdef DEBUG_FFT_SERVER_FILL
Chris@193 1059 std::cerr << "FFTDataServer::fillColumn: requesting frames "
Chris@193 1060 << startFrame + pfx << " -> " << endFrame << " ( = "
Chris@193 1061 << endFrame - (startFrame + pfx) << ") at index "
Chris@193 1062 << off + pfx << " in buffer of size " << m_fftSize
Chris@193 1063 << " with window size " << m_windowSize
Chris@193 1064 << " from channel " << m_channel << std::endl;
Chris@195 1065 #endif
Chris@193 1066
Chris@300 1067 size_t count = 0;
Chris@300 1068 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
Chris@300 1069
Chris@300 1070 size_t got = m_model->getData(m_channel, startFrame + pfx,
Chris@300 1071 count, m_fftInput + off + pfx);
Chris@148 1072
Chris@148 1073 while (got + pfx < m_windowSize) {
Chris@148 1074 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1075 ++got;
Chris@148 1076 }
Chris@148 1077
Chris@148 1078 if (m_channel == -1) {
Chris@148 1079 int channels = m_model->getChannelCount();
Chris@148 1080 if (channels > 1) {
Chris@148 1081 for (size_t i = 0; i < m_windowSize; ++i) {
Chris@148 1082 m_fftInput[off + i] /= channels;
Chris@148 1083 }
Chris@148 1084 }
Chris@148 1085 }
Chris@148 1086
Chris@148 1087 m_windower.cut(m_fftInput + off);
Chris@148 1088
Chris@148 1089 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 1090 fftsample temp = m_fftInput[i];
Chris@148 1091 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
Chris@148 1092 m_fftInput[i + m_fftSize/2] = temp;
Chris@148 1093 }
Chris@148 1094
Chris@226 1095 fftf_execute(m_fftPlan);
Chris@148 1096
Chris@148 1097 fftsample factor = 0.0;
Chris@148 1098
Chris@203 1099 for (size_t i = 0; i <= m_fftSize/2; ++i) {
Chris@148 1100
Chris@264 1101 m_workbuffer[i] = m_fftOutput[i][0];
Chris@264 1102 m_workbuffer[i + m_fftSize/2 + 1] = m_fftOutput[i][1];
Chris@148 1103 }
Chris@148 1104
Chris@148 1105 cache->setColumnAt(col,
Chris@148 1106 m_workbuffer,
Chris@264 1107 m_workbuffer + m_fftSize/2+1);
Chris@154 1108
Chris@183 1109 if (m_suspended) {
Chris@183 1110 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1111 // resume();
Chris@183 1112 }
Chris@148 1113 }
Chris@148 1114
Chris@148 1115 size_t
Chris@148 1116 FFTDataServer::getFillCompletion() const
Chris@148 1117 {
Chris@148 1118 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1119 else return 100;
Chris@148 1120 }
Chris@148 1121
Chris@148 1122 size_t
Chris@148 1123 FFTDataServer::getFillExtent() const
Chris@148 1124 {
Chris@148 1125 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1126 else return m_model->getEndFrame();
Chris@148 1127 }
Chris@148 1128
Chris@148 1129 QString
Chris@148 1130 FFTDataServer::generateFileBasename() const
Chris@148 1131 {
Chris@148 1132 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1133 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1134 m_polar);
Chris@148 1135 }
Chris@148 1136
Chris@148 1137 QString
Chris@148 1138 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1139 int channel,
Chris@148 1140 WindowType windowType,
Chris@148 1141 size_t windowSize,
Chris@148 1142 size_t windowIncrement,
Chris@148 1143 size_t fftSize,
Chris@148 1144 bool polar)
Chris@148 1145 {
Chris@148 1146 char buffer[200];
Chris@148 1147
Chris@148 1148 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1149 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1150 (unsigned int)(channel + 1),
Chris@148 1151 (unsigned int)windowType,
Chris@148 1152 (unsigned int)windowSize,
Chris@148 1153 (unsigned int)windowIncrement,
Chris@148 1154 (unsigned int)fftSize,
Chris@148 1155 polar ? "-p" : "-r");
Chris@148 1156
Chris@148 1157 return buffer;
Chris@148 1158 }
Chris@148 1159
Chris@148 1160 void
Chris@148 1161 FFTDataServer::FillThread::run()
Chris@148 1162 {
Chris@148 1163 m_extent = 0;
Chris@148 1164 m_completion = 0;
Chris@148 1165
Chris@272 1166 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
Chris@272 1167 sleep(1);
Chris@272 1168 }
Chris@272 1169 if (m_server.m_exiting) return;
Chris@272 1170
Chris@148 1171 size_t start = m_server.m_model->getStartFrame();
Chris@148 1172 size_t end = m_server.m_model->getEndFrame();
Chris@148 1173 size_t remainingEnd = end;
Chris@148 1174
Chris@148 1175 int counter = 0;
Chris@246 1176 int updateAt = 1;
Chris@246 1177 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1178 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1179
Chris@148 1180 if (m_fillFrom > start) {
Chris@148 1181
Chris@148 1182 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1183
Chris@148 1184 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1185
Chris@148 1186 if (m_server.m_exiting) return;
Chris@148 1187
Chris@148 1188 while (m_server.m_suspended) {
Chris@148 1189 #ifdef DEBUG_FFT_SERVER
Chris@193 1190 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1191 #endif
Chris@244 1192 {
Chris@244 1193 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1194 "FFTDataServer::m_writeMutex[run/1]");
Chris@244 1195 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1196 }
Chris@159 1197 #ifdef DEBUG_FFT_SERVER
Chris@193 1198 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1199 #endif
Chris@148 1200 if (m_server.m_exiting) return;
Chris@148 1201 }
Chris@148 1202
Chris@148 1203 if (++counter == updateAt) {
Chris@148 1204 m_extent = f;
Chris@148 1205 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1206 float(end - start)));
Chris@148 1207 counter = 0;
Chris@246 1208 if (updateAt < maxUpdateAt) {
Chris@246 1209 updateAt *= 2;
Chris@246 1210 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1211 }
Chris@148 1212 }
Chris@148 1213 }
Chris@148 1214
Chris@148 1215 remainingEnd = m_fillFrom;
Chris@148 1216 if (remainingEnd > start) --remainingEnd;
Chris@148 1217 else remainingEnd = start;
Chris@148 1218 }
Chris@148 1219
Chris@148 1220 size_t baseCompletion = m_completion;
Chris@148 1221
Chris@148 1222 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1223
Chris@148 1224 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1225
Chris@148 1226 if (m_server.m_exiting) return;
Chris@148 1227
Chris@148 1228 while (m_server.m_suspended) {
Chris@148 1229 #ifdef DEBUG_FFT_SERVER
Chris@193 1230 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1231 #endif
Chris@244 1232 {
Chris@244 1233 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1234 "FFTDataServer::m_writeMutex[run/2]");
Chris@244 1235 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1236 }
Chris@148 1237 if (m_server.m_exiting) return;
Chris@148 1238 }
Chris@148 1239
Chris@148 1240 if (++counter == updateAt) {
Chris@148 1241 m_extent = f;
Chris@148 1242 m_completion = baseCompletion +
Chris@148 1243 size_t(100 * fabsf(float(f - start) /
Chris@148 1244 float(end - start)));
Chris@148 1245 counter = 0;
Chris@246 1246 if (updateAt < maxUpdateAt) {
Chris@246 1247 updateAt *= 2;
Chris@246 1248 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1249 }
Chris@148 1250 }
Chris@148 1251 }
Chris@148 1252
Chris@148 1253 m_completion = 100;
Chris@148 1254 m_extent = end;
Chris@148 1255 }
Chris@148 1256