annotate data/fft/FFTDataServer.cpp @ 319:3ff8f571da09

* Hoist alignment model set/query up to Model, so any models can be aligned * Add Model::aboutToDelete and aboutToBeDeleted for management of models that are contained by or referred to by other models instead of only the document
author Chris Cannam
date Wed, 24 Oct 2007 15:21:38 +0000
parents 5877d68815c7
children aa8dbac62024
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@236 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@148 55 size_t fillFromColumn)
Chris@148 56 {
Chris@148 57 QString n = generateFileBasename(model,
Chris@148 58 channel,
Chris@148 59 windowType,
Chris@148 60 windowSize,
Chris@148 61 windowIncrement,
Chris@148 62 fftSize,
Chris@148 63 polar);
Chris@148 64
Chris@148 65 FFTDataServer *server = 0;
Chris@148 66
Chris@244 67 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getInstance]");
Chris@148 68
Chris@148 69 if ((server = findServer(n))) {
Chris@148 70 return server;
Chris@148 71 }
Chris@148 72
Chris@148 73 QString npn = generateFileBasename(model,
Chris@148 74 channel,
Chris@148 75 windowType,
Chris@148 76 windowSize,
Chris@148 77 windowIncrement,
Chris@148 78 fftSize,
Chris@148 79 !polar);
Chris@148 80
Chris@148 81 if ((server = findServer(npn))) {
Chris@148 82 return server;
Chris@148 83 }
Chris@148 84
Chris@200 85 try {
Chris@200 86 server = new FFTDataServer(n,
Chris@200 87 model,
Chris@200 88 channel,
Chris@200 89 windowType,
Chris@200 90 windowSize,
Chris@200 91 windowIncrement,
Chris@200 92 fftSize,
Chris@200 93 polar,
Chris@200 94 fillFromColumn);
Chris@200 95 } catch (InsufficientDiscSpace) {
Chris@200 96 delete server;
Chris@200 97 server = 0;
Chris@200 98 }
Chris@148 99
Chris@200 100 if (server) {
Chris@200 101 m_servers[n] = ServerCountPair(server, 1);
Chris@200 102 }
Chris@200 103
Chris@200 104 return server;
Chris@148 105 }
Chris@148 106
Chris@148 107 FFTDataServer *
Chris@148 108 FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model,
Chris@148 109 int channel,
Chris@148 110 WindowType windowType,
Chris@148 111 size_t windowSize,
Chris@148 112 size_t windowIncrement,
Chris@148 113 size_t fftSize,
Chris@148 114 bool polar,
Chris@148 115 size_t fillFromColumn)
Chris@148 116 {
Chris@148 117 // Fuzzy matching:
Chris@148 118 //
Chris@148 119 // -- if we're asked for polar and have non-polar, use it (and
Chris@148 120 // vice versa). This one is vital, and we do it for non-fuzzy as
Chris@148 121 // well (above).
Chris@148 122 //
Chris@148 123 // -- if we're asked for an instance with a given fft size and we
Chris@148 124 // have one already with a multiple of that fft size but the same
Chris@148 125 // window size and type (and model), we can draw the results from
Chris@148 126 // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the
Chris@148 127 // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the
Chris@148 128 // same window plus zero padding).
Chris@148 129 //
Chris@148 130 // -- if we're asked for an instance with a given window type and
Chris@148 131 // size and fft size and we have one already the same but with a
Chris@148 132 // smaller increment, we can draw the results from it (provided
Chris@148 133 // our increment is a multiple of its)
Chris@148 134 //
Chris@152 135 // The FFTModel knows how to interpret these things. In
Chris@148 136 // both cases we require that the larger one is a power-of-two
Chris@148 137 // multiple of the smaller (e.g. even though in principle you can
Chris@148 138 // draw the results at increment 256 from those at increment 768
Chris@152 139 // or 1536, the model doesn't support this).
Chris@148 140
Chris@148 141 {
Chris@244 142 MutexLocker locker(&m_serverMapMutex, "FFTDataServer::m_serverMapMutex[getFuzzyInstance]");
Chris@148 143
Chris@148 144 ServerMap::iterator best = m_servers.end();
Chris@148 145 int bestdist = -1;
Chris@148 146
Chris@148 147 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@148 148
Chris@148 149 FFTDataServer *server = i->second.first;
Chris@148 150
Chris@148 151 if (server->getModel() == model &&
Chris@148 152 (server->getChannel() == channel || model->getChannelCount() == 1) &&
Chris@148 153 server->getWindowType() == windowType &&
Chris@148 154 server->getWindowSize() == windowSize &&
Chris@148 155 server->getWindowIncrement() <= windowIncrement &&
Chris@148 156 server->getFFTSize() >= fftSize) {
Chris@148 157
Chris@148 158 if ((windowIncrement % server->getWindowIncrement()) != 0) continue;
Chris@148 159 int ratio = windowIncrement / server->getWindowIncrement();
Chris@148 160 bool poweroftwo = true;
Chris@148 161 while (ratio > 1) {
Chris@148 162 if (ratio & 0x1) {
Chris@148 163 poweroftwo = false;
Chris@148 164 break;
Chris@148 165 }
Chris@148 166 ratio >>= 1;
Chris@148 167 }
Chris@148 168 if (!poweroftwo) continue;
Chris@148 169
Chris@148 170 if ((server->getFFTSize() % fftSize) != 0) continue;
Chris@148 171 ratio = server->getFFTSize() / fftSize;
Chris@148 172 while (ratio > 1) {
Chris@148 173 if (ratio & 0x1) {
Chris@148 174 poweroftwo = false;
Chris@148 175 break;
Chris@148 176 }
Chris@148 177 ratio >>= 1;
Chris@148 178 }
Chris@148 179 if (!poweroftwo) continue;
Chris@148 180
Chris@148 181 int distance = 0;
Chris@148 182
Chris@148 183 if (server->getPolar() != polar) distance += 1;
Chris@148 184
Chris@148 185 distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15;
Chris@148 186 distance += ((server->getFFTSize() / fftSize) - 1) * 10;
Chris@148 187
Chris@148 188 if (server->getFillCompletion() < 50) distance += 100;
Chris@148 189
Chris@148 190 #ifdef DEBUG_FFT_SERVER
Chris@216 191 std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl;
Chris@148 192 #endif
Chris@148 193
Chris@148 194 if (bestdist == -1 || distance < bestdist) {
Chris@148 195 bestdist = distance;
Chris@148 196 best = i;
Chris@148 197 }
Chris@148 198 }
Chris@148 199 }
Chris@148 200
Chris@148 201 if (bestdist >= 0) {
Chris@216 202 FFTDataServer *server = best->second.first;
Chris@216 203 #ifdef DEBUG_FFT_SERVER
Chris@216 204 std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl;
Chris@216 205 #endif
Chris@216 206 claimInstance(server, false);
Chris@216 207 return server;
Chris@148 208 }
Chris@148 209 }
Chris@148 210
Chris@148 211 // Nothing found, make a new one
Chris@148 212
Chris@148 213 return getInstance(model,
Chris@148 214 channel,
Chris@148 215 windowType,
Chris@148 216 windowSize,
Chris@148 217 windowIncrement,
Chris@148 218 fftSize,
Chris@148 219 polar,
Chris@148 220 fillFromColumn);
Chris@148 221 }
Chris@148 222
Chris@148 223 FFTDataServer *
Chris@148 224 FFTDataServer::findServer(QString n)
Chris@148 225 {
Chris@216 226 #ifdef DEBUG_FFT_SERVER
Chris@216 227 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\")" << std::endl;
Chris@216 228 #endif
Chris@216 229
Chris@148 230 if (m_servers.find(n) != m_servers.end()) {
Chris@216 231
Chris@216 232 FFTDataServer *server = m_servers[n].first;
Chris@216 233
Chris@216 234 #ifdef DEBUG_FFT_SERVER
Chris@216 235 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): found " << server << std::endl;
Chris@216 236 #endif
Chris@216 237
Chris@216 238 claimInstance(server, false);
Chris@216 239
Chris@216 240 return server;
Chris@148 241 }
Chris@148 242
Chris@216 243 #ifdef DEBUG_FFT_SERVER
Chris@216 244 std::cerr << "FFTDataServer::findServer(\"" << n.toStdString() << "\"): not found" << std::endl;
Chris@216 245 #endif
Chris@216 246
Chris@148 247 return 0;
Chris@148 248 }
Chris@148 249
Chris@148 250 void
Chris@152 251 FFTDataServer::claimInstance(FFTDataServer *server)
Chris@152 252 {
Chris@216 253 claimInstance(server, true);
Chris@216 254 }
Chris@216 255
Chris@216 256 void
Chris@216 257 FFTDataServer::claimInstance(FFTDataServer *server, bool needLock)
Chris@216 258 {
Chris@244 259 MutexLocker locker(needLock ? &m_serverMapMutex : 0,
Chris@244 260 "FFTDataServer::m_serverMapMutex[claimInstance]");
Chris@216 261
Chris@216 262 #ifdef DEBUG_FFT_SERVER
Chris@216 263 std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl;
Chris@216 264 #endif
Chris@152 265
Chris@152 266 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@152 267 if (i->second.first == server) {
Chris@215 268
Chris@215 269 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@215 270 j != m_releasedServers.end(); ++j) {
Chris@216 271
Chris@215 272 if (*j == server) {
Chris@216 273 #ifdef DEBUG_FFT_SERVER
Chris@216 274 std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl;
Chris@216 275 #endif
Chris@215 276 m_releasedServers.erase(j);
Chris@215 277 break;
Chris@215 278 }
Chris@215 279 }
Chris@215 280
Chris@152 281 ++i->second.second;
Chris@216 282
Chris@216 283 #ifdef DEBUG_FFT_SERVER
Chris@216 284 std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl;
Chris@216 285 #endif
Chris@216 286
Chris@152 287 return;
Chris@152 288 }
Chris@152 289 }
Chris@152 290
Chris@152 291 std::cerr << "ERROR: FFTDataServer::claimInstance: instance "
Chris@152 292 << server << " unknown!" << std::endl;
Chris@152 293 }
Chris@152 294
Chris@152 295 void
Chris@148 296 FFTDataServer::releaseInstance(FFTDataServer *server)
Chris@148 297 {
Chris@216 298 releaseInstance(server, true);
Chris@216 299 }
Chris@216 300
Chris@216 301 void
Chris@216 302 FFTDataServer::releaseInstance(FFTDataServer *server, bool needLock)
Chris@216 303 {
Chris@244 304 MutexLocker locker(needLock ? &m_serverMapMutex : 0,
Chris@244 305 "FFTDataServer::m_serverMapMutex[releaseInstance]");
Chris@216 306
Chris@148 307 #ifdef DEBUG_FFT_SERVER
Chris@148 308 std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl;
Chris@148 309 #endif
Chris@148 310
Chris@148 311 // -- if ref count > 0, decrement and return
Chris@148 312 // -- if the instance hasn't been used at all, delete it immediately
Chris@148 313 // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts,
Chris@148 314 // leave them hanging around
Chris@148 315 // -- if N instances with zero refcounts remain, delete the one that
Chris@148 316 // was last released first
Chris@148 317 // -- if we run out of disk space when allocating an instance, go back
Chris@148 318 // and delete the spare N instances before trying again
Chris@148 319 // -- have an additional method to indicate that a model has been
Chris@148 320 // destroyed, so that we can delete all of its fft server instances
Chris@148 321
Chris@148 322 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@148 323 if (i->second.first == server) {
Chris@148 324 if (i->second.second == 0) {
Chris@148 325 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@148 326 << server << "): instance not allocated" << std::endl;
Chris@148 327 } else if (--i->second.second == 0) {
Chris@148 328 if (server->m_lastUsedCache == -1) { // never used
Chris@216 329 #ifdef DEBUG_FFT_SERVER
Chris@216 330 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 331 << server << " has never been used, erasing"
Chris@216 332 << std::endl;
Chris@216 333 #endif
Chris@148 334 delete server;
Chris@148 335 m_servers.erase(i);
Chris@148 336 } else {
Chris@216 337 #ifdef DEBUG_FFT_SERVER
Chris@216 338 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 339 << server << " no longer in use, marking for possible collection"
Chris@216 340 << std::endl;
Chris@216 341 #endif
Chris@216 342 bool found = false;
Chris@216 343 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@216 344 j != m_releasedServers.end(); ++j) {
Chris@216 345 if (*j == server) {
Chris@216 346 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@216 347 << server << "): server is already in "
Chris@216 348 << "released servers list" << std::endl;
Chris@216 349 found = true;
Chris@216 350 }
Chris@216 351 }
Chris@216 352 if (!found) m_releasedServers.push_back(server);
Chris@148 353 server->suspend();
Chris@148 354 purgeLimbo();
Chris@148 355 }
Chris@216 356 } else {
Chris@216 357 #ifdef DEBUG_FFT_SERVER
Chris@216 358 std::cerr << "FFTDataServer::releaseInstance: instance "
Chris@216 359 << server << " now has refcount " << i->second.second
Chris@216 360 << std::endl;
Chris@216 361 #endif
Chris@148 362 }
Chris@148 363 return;
Chris@148 364 }
Chris@148 365 }
Chris@148 366
Chris@148 367 std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
Chris@148 368 << "instance not found" << std::endl;
Chris@148 369 }
Chris@148 370
Chris@148 371 void
Chris@148 372 FFTDataServer::purgeLimbo(int maxSize)
Chris@148 373 {
Chris@216 374 #ifdef DEBUG_FFT_SERVER
Chris@216 375 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 376 << m_releasedServers.size() << " candidates" << std::endl;
Chris@216 377 #endif
Chris@216 378
Chris@259 379 while (int(m_releasedServers.size()) > maxSize) {
Chris@148 380
Chris@215 381 FFTDataServer *server = *m_releasedServers.begin();
Chris@148 382
Chris@215 383 bool found = false;
Chris@215 384
Chris@216 385 #ifdef DEBUG_FFT_SERVER
Chris@216 386 std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
Chris@216 387 << server << std::endl;
Chris@216 388 #endif
Chris@216 389
Chris@215 390 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@215 391
Chris@215 392 if (i->second.first == server) {
Chris@215 393 found = true;
Chris@215 394 if (i->second.second > 0) {
Chris@215 395 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 396 << server << " is in released queue, but still has non-zero refcount "
Chris@215 397 << i->second.second << std::endl;
Chris@215 398 // ... so don't delete it
Chris@215 399 break;
Chris@215 400 }
Chris@216 401 #ifdef DEBUG_FFT_SERVER
Chris@216 402 std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
Chris@216 403 << std::endl;
Chris@216 404 #endif
Chris@216 405
Chris@148 406 m_servers.erase(i);
Chris@215 407 delete server;
Chris@215 408 break;
Chris@148 409 }
Chris@148 410 }
Chris@215 411
Chris@215 412 if (!found) {
Chris@215 413 std::cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
Chris@215 414 << server << " is in released queue, but not in server map!"
Chris@215 415 << std::endl;
Chris@215 416 delete server;
Chris@215 417 }
Chris@215 418
Chris@215 419 m_releasedServers.pop_front();
Chris@215 420 }
Chris@216 421
Chris@216 422 #ifdef DEBUG_FFT_SERVER
Chris@216 423 std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
Chris@216 424 << m_releasedServers.size() << " remain" << std::endl;
Chris@216 425 #endif
Chris@216 426
Chris@215 427 }
Chris@215 428
Chris@215 429 void
Chris@215 430 FFTDataServer::modelAboutToBeDeleted(Model *model)
Chris@215 431 {
Chris@244 432 MutexLocker locker(&m_serverMapMutex,
Chris@244 433 "FFTDataServer::m_serverMapMutex[modelAboutToBeDeleted]");
Chris@215 434
Chris@216 435 #ifdef DEBUG_FFT_SERVER
Chris@216 436 std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
Chris@216 437 << std::endl;
Chris@216 438 #endif
Chris@216 439
Chris@215 440 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@215 441
Chris@215 442 FFTDataServer *server = i->second.first;
Chris@215 443
Chris@215 444 if (server->getModel() == model) {
Chris@216 445
Chris@216 446 #ifdef DEBUG_FFT_SERVER
Chris@216 447 std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
Chris@216 448 << server << std::endl;
Chris@216 449 #endif
Chris@216 450
Chris@215 451 if (i->second.second > 0) {
Chris@215 452 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 453 }
Chris@215 454 for (ServerQueue::iterator j = m_releasedServers.begin();
Chris@215 455 j != m_releasedServers.end(); ++j) {
Chris@215 456 if (*j == server) {
Chris@216 457 #ifdef DEBUG_FFT_SERVER
Chris@216 458 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
Chris@216 459 #endif
Chris@215 460 m_releasedServers.erase(j);
Chris@215 461 break;
Chris@215 462 }
Chris@215 463 }
Chris@216 464 #ifdef DEBUG_FFT_SERVER
Chris@216 465 std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
Chris@216 466 #endif
Chris@215 467 m_servers.erase(i);
Chris@215 468 delete server;
Chris@215 469 return;
Chris@215 470 }
Chris@148 471 }
Chris@148 472 }
Chris@148 473
Chris@148 474 FFTDataServer::FFTDataServer(QString fileBaseName,
Chris@148 475 const DenseTimeValueModel *model,
Chris@148 476 int channel,
Chris@148 477 WindowType windowType,
Chris@148 478 size_t windowSize,
Chris@148 479 size_t windowIncrement,
Chris@148 480 size_t fftSize,
Chris@148 481 bool polar,
Chris@148 482 size_t fillFromColumn) :
Chris@148 483 m_fileBaseName(fileBaseName),
Chris@148 484 m_model(model),
Chris@148 485 m_channel(channel),
Chris@148 486 m_windower(windowType, windowSize),
Chris@148 487 m_windowSize(windowSize),
Chris@148 488 m_windowIncrement(windowIncrement),
Chris@148 489 m_fftSize(fftSize),
Chris@148 490 m_polar(polar),
Chris@183 491 m_width(0),
Chris@183 492 m_height(0),
Chris@183 493 m_cacheWidth(0),
Chris@172 494 m_memoryCache(false),
Chris@172 495 m_compactCache(false),
Chris@148 496 m_lastUsedCache(-1),
Chris@148 497 m_fftInput(0),
Chris@148 498 m_exiting(false),
Chris@153 499 m_suspended(true), //!!! or false?
Chris@148 500 m_fillThread(0)
Chris@148 501 {
Chris@193 502 #ifdef DEBUG_FFT_SERVER
Chris@193 503 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << std::endl;
Chris@193 504 #endif
Chris@193 505
Chris@272 506 //!!! end is not correct until model finished reading -- what to do???
Chris@272 507
Chris@148 508 size_t start = m_model->getStartFrame();
Chris@148 509 size_t end = m_model->getEndFrame();
Chris@148 510
Chris@148 511 m_width = (end - start) / m_windowIncrement + 1;
Chris@203 512 m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
Chris@148 513
Chris@216 514 #ifdef DEBUG_FFT_SERVER
Chris@216 515 std::cerr << "FFTDataServer(" << this << "): dimensions are "
Chris@216 516 << m_width << "x" << m_height << std::endl;
Chris@216 517 #endif
Chris@216 518
Chris@148 519 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 520 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
Chris@148 521 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
Chris@148 522 else m_cacheWidth = maxCacheSize / columnSize;
Chris@148 523
Chris@148 524 int bits = 0;
Chris@148 525 while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; }
Chris@148 526 m_cacheWidth = 2;
Chris@148 527 while (bits) { m_cacheWidth <<= 1; --bits; }
Chris@172 528
Chris@172 529 //!!! Need to pass in what this server is intended for
Chris@172 530 // (e.g. playback processing, spectrogram, feature extraction),
Chris@172 531 // or pass in something akin to the storage adviser criteria.
Chris@172 532 // That probably goes alongside the polar argument.
Chris@172 533 // For now we'll assume "spectrogram" criteria for polar ffts,
Chris@172 534 // and "feature extraction" criteria for rectangular ones.
Chris@172 535
Chris@172 536 StorageAdviser::Criteria criteria;
Chris@172 537 if (m_polar) {
Chris@172 538 criteria = StorageAdviser::Criteria
Chris@264 539 (StorageAdviser::SpeedCritical |
Chris@264 540 StorageAdviser::LongRetentionLikely);
Chris@172 541 } else {
Chris@264 542 criteria = StorageAdviser::Criteria
Chris@264 543 (StorageAdviser::PrecisionCritical);
Chris@172 544 }
Chris@172 545
Chris@172 546 int cells = m_width * m_height;
Chris@172 547 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
Chris@172 548 int maximumSize = (cells / 1024) * sizeof(float); // kb
Chris@215 549
Chris@215 550 StorageAdviser::Recommendation recommendation;
Chris@172 551
Chris@215 552 try {
Chris@215 553
Chris@215 554 recommendation =
Chris@215 555 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
Chris@215 556
Chris@215 557 } catch (InsufficientDiscSpace s) {
Chris@215 558
Chris@215 559 // Delete any unused servers we may have been leaving around
Chris@215 560 // in case we wanted them again
Chris@215 561
Chris@215 562 purgeLimbo(0);
Chris@215 563
Chris@215 564 // This time we don't catch InsufficientDiscSpace -- we
Chris@215 565 // haven't allocated anything yet and can safely let the
Chris@215 566 // exception out to indicate to the caller that we can't
Chris@215 567 // handle it.
Chris@215 568
Chris@215 569 recommendation =
Chris@215 570 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
Chris@215 571 }
Chris@172 572
Chris@264 573 std::cerr << "Recommendation was: " << recommendation << std::endl;
Chris@172 574
Chris@264 575 m_memoryCache = false;
Chris@264 576
Chris@264 577 if (recommendation & StorageAdviser::UseMemory) {
Chris@264 578
Chris@264 579 // can't use disc, must use memory
Chris@264 580
Chris@264 581 m_memoryCache = true;
Chris@264 582
Chris@264 583 } else if (recommendation & StorageAdviser::PreferMemory) {
Chris@264 584
Chris@264 585 // if memory is recommended, we use it if we're using polar
Chris@264 586 // coordinates; but we don't have a native rectangular memory
Chris@264 587 // cache, so we might as well use disc if we want rectangular
Chris@264 588 // coordinates rather than have all the bother of converting
Chris@264 589 // every time
Chris@264 590
Chris@264 591 if (m_polar) m_memoryCache = true;
Chris@264 592 }
Chris@172 593
Chris@172 594 m_compactCache = (recommendation & StorageAdviser::ConserveSpace);
Chris@148 595
Chris@148 596 #ifdef DEBUG_FFT_SERVER
Chris@148 597 std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl;
Chris@148 598 #endif
Chris@148 599
Chris@205 600 StorageAdviser::notifyPlannedAllocation
Chris@205 601 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@205 602 StorageAdviser::DiscAllocation,
Chris@205 603 m_compactCache ? minimumSize : maximumSize);
Chris@205 604
Chris@148 605 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
Chris@148 606 m_caches.push_back(0);
Chris@148 607 }
Chris@148 608
Chris@148 609 m_fftInput = (fftsample *)
Chris@226 610 fftf_malloc(fftSize * sizeof(fftsample));
Chris@148 611
Chris@226 612 m_fftOutput = (fftf_complex *)
Chris@226 613 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
Chris@148 614
Chris@148 615 m_workbuffer = (float *)
Chris@226 616 fftf_malloc((fftSize+2) * sizeof(float));
Chris@148 617
Chris@226 618 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
Chris@148 619 m_fftInput,
Chris@148 620 m_fftOutput,
Chris@289 621 FFTW_MEASURE);
Chris@148 622
Chris@148 623 if (!m_fftPlan) {
Chris@226 624 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
Chris@148 625 throw(0);
Chris@148 626 }
Chris@148 627
Chris@148 628 m_fillThread = new FillThread(*this, fillFromColumn);
Chris@148 629 }
Chris@148 630
Chris@148 631 FFTDataServer::~FFTDataServer()
Chris@148 632 {
Chris@148 633 #ifdef DEBUG_FFT_SERVER
Chris@193 634 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl;
Chris@148 635 #endif
Chris@148 636
Chris@155 637 m_suspended = false;
Chris@148 638 m_exiting = true;
Chris@148 639 m_condition.wakeAll();
Chris@148 640 if (m_fillThread) {
Chris@148 641 m_fillThread->wait();
Chris@148 642 delete m_fillThread;
Chris@148 643 }
Chris@148 644
Chris@244 645 MutexLocker locker(&m_writeMutex,
Chris@244 646 "FFTDataServer::m_writeMutex[~FFTDataServer]");
Chris@148 647
Chris@148 648 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@205 649 if (*i) {
Chris@205 650 delete *i;
Chris@205 651 } else {
Chris@205 652 StorageAdviser::notifyDoneAllocation
Chris@205 653 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@205 654 StorageAdviser::DiscAllocation,
Chris@205 655 m_cacheWidth * m_height *
Chris@205 656 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
Chris@205 657 }
Chris@148 658 }
Chris@148 659
Chris@148 660 deleteProcessingData();
Chris@148 661 }
Chris@148 662
Chris@148 663 void
Chris@148 664 FFTDataServer::deleteProcessingData()
Chris@148 665 {
Chris@193 666 #ifdef DEBUG_FFT_SERVER
Chris@193 667 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl;
Chris@193 668 #endif
Chris@148 669 if (m_fftInput) {
Chris@226 670 fftf_destroy_plan(m_fftPlan);
Chris@226 671 fftf_free(m_fftInput);
Chris@226 672 fftf_free(m_fftOutput);
Chris@226 673 fftf_free(m_workbuffer);
Chris@148 674 }
Chris@148 675 m_fftInput = 0;
Chris@148 676 }
Chris@148 677
Chris@148 678 void
Chris@148 679 FFTDataServer::suspend()
Chris@148 680 {
Chris@148 681 #ifdef DEBUG_FFT_SERVER
Chris@193 682 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl;
Chris@148 683 #endif
Chris@183 684 Profiler profiler("FFTDataServer::suspend", false);
Chris@183 685
Chris@244 686 MutexLocker locker(&m_writeMutex,
Chris@244 687 "FFTDataServer::m_writeMutex[suspend]");
Chris@148 688 m_suspended = true;
Chris@148 689 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 690 if (*i) (*i)->suspend();
Chris@148 691 }
Chris@148 692 }
Chris@148 693
Chris@148 694 void
Chris@155 695 FFTDataServer::suspendWrites()
Chris@155 696 {
Chris@155 697 #ifdef DEBUG_FFT_SERVER
Chris@193 698 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::endl;
Chris@155 699 #endif
Chris@183 700 Profiler profiler("FFTDataServer::suspendWrites", false);
Chris@183 701
Chris@155 702 m_suspended = true;
Chris@155 703 }
Chris@155 704
Chris@155 705 void
Chris@148 706 FFTDataServer::resume()
Chris@148 707 {
Chris@154 708 #ifdef DEBUG_FFT_SERVER
Chris@193 709 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::endl;
Chris@154 710 #endif
Chris@183 711 Profiler profiler("FFTDataServer::resume", false);
Chris@183 712
Chris@148 713 m_suspended = false;
Chris@157 714 if (m_fillThread) {
Chris@157 715 if (m_fillThread->isFinished()) {
Chris@157 716 delete m_fillThread;
Chris@157 717 m_fillThread = 0;
Chris@157 718 deleteProcessingData();
Chris@157 719 } else {
Chris@157 720 m_condition.wakeAll();
Chris@157 721 }
Chris@157 722 }
Chris@148 723 }
Chris@148 724
Chris@148 725 FFTCache *
Chris@148 726 FFTDataServer::getCacheAux(size_t c)
Chris@148 727 {
Chris@183 728 Profiler profiler("FFTDataServer::getCacheAux", false);
Chris@193 729 #ifdef DEBUG_FFT_SERVER
Chris@193 730 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl;
Chris@193 731 #endif
Chris@183 732
Chris@244 733 MutexLocker locker(&m_writeMutex,
Chris@244 734 "FFTDataServer::m_writeMutex[getCacheAux]");
Chris@148 735
Chris@148 736 if (m_lastUsedCache == -1) {
Chris@148 737 m_fillThread->start();
Chris@148 738 }
Chris@148 739
Chris@148 740 if (int(c) != m_lastUsedCache) {
Chris@148 741
Chris@148 742 // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
Chris@148 743
Chris@148 744 for (IntQueue::iterator i = m_dormantCaches.begin();
Chris@148 745 i != m_dormantCaches.end(); ++i) {
Chris@259 746 if (*i == int(c)) {
Chris@148 747 m_dormantCaches.erase(i);
Chris@148 748 break;
Chris@148 749 }
Chris@148 750 }
Chris@148 751
Chris@148 752 if (m_lastUsedCache >= 0) {
Chris@148 753 bool inDormant = false;
Chris@148 754 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
Chris@148 755 if (m_dormantCaches[i] == m_lastUsedCache) {
Chris@148 756 inDormant = true;
Chris@148 757 break;
Chris@148 758 }
Chris@148 759 }
Chris@148 760 if (!inDormant) {
Chris@148 761 m_dormantCaches.push_back(m_lastUsedCache);
Chris@148 762 }
Chris@148 763 while (m_dormantCaches.size() > 4) {
Chris@148 764 int dc = m_dormantCaches.front();
Chris@148 765 m_dormantCaches.pop_front();
Chris@148 766 m_caches[dc]->suspend();
Chris@148 767 }
Chris@148 768 }
Chris@148 769 }
Chris@148 770
Chris@148 771 if (m_caches[c]) {
Chris@148 772 m_lastUsedCache = c;
Chris@148 773 return m_caches[c];
Chris@148 774 }
Chris@148 775
Chris@148 776 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
Chris@148 777
Chris@172 778 FFTCache *cache = 0;
Chris@172 779
Chris@213 780 size_t width = m_cacheWidth;
Chris@213 781 if (c * m_cacheWidth + width > m_width) {
Chris@213 782 width = m_width - c * m_cacheWidth;
Chris@213 783 }
Chris@213 784
Chris@200 785 try {
Chris@264 786
Chris@200 787 if (m_memoryCache) {
Chris@172 788
Chris@264 789 cache = new FFTMemoryCache
Chris@264 790 (m_compactCache ? FFTMemoryCache::Compact :
Chris@264 791 FFTMemoryCache::Polar);
Chris@172 792
Chris@200 793 } else if (m_compactCache) {
Chris@172 794
Chris@264 795 cache = new FFTFileCache
Chris@264 796 (name,
Chris@264 797 MatrixFile::ReadWrite,
Chris@264 798 FFTFileCache::Compact);
Chris@172 799
Chris@200 800 } else {
Chris@172 801
Chris@264 802 cache = new FFTFileCache
Chris@264 803 (name,
Chris@264 804 MatrixFile::ReadWrite,
Chris@264 805 m_polar ? FFTFileCache::Polar :
Chris@264 806 FFTFileCache::Rectangular);
Chris@200 807 }
Chris@200 808
Chris@200 809 cache->resize(width, m_height);
Chris@200 810 cache->reset();
Chris@200 811
Chris@213 812 } catch (std::bad_alloc) {
Chris@205 813
Chris@213 814 delete cache;
Chris@213 815 cache = 0;
Chris@213 816
Chris@213 817 if (m_memoryCache) {
Chris@213 818
Chris@213 819 std::cerr << "WARNING: Memory allocation failed when resizing"
Chris@213 820 << " FFT memory cache no. " << c << " to " << width
Chris@213 821 << "x" << m_height << " (of total width " << m_width
Chris@213 822 << "): falling back to disc cache" << std::endl;
Chris@213 823
Chris@213 824 try {
Chris@213 825
Chris@213 826 cache = new FFTFileCache(name, MatrixFile::ReadWrite,
Chris@213 827 FFTFileCache::Compact);
Chris@213 828
Chris@213 829 cache->resize(width, m_height);
Chris@213 830 cache->reset();
Chris@213 831
Chris@213 832 } catch (std::bad_alloc) {
Chris@213 833
Chris@213 834 delete cache;
Chris@213 835 cache = 0;
Chris@213 836 }
Chris@213 837 }
Chris@213 838
Chris@213 839 if (cache) {
Chris@213 840 std::cerr << "ERROR: Memory allocation failed when resizing"
Chris@213 841 << " FFT file cache no. " << c << " to " << width
Chris@213 842 << "x" << m_height << " (of total width " << m_width
Chris@213 843 << "): abandoning this cache" << std::endl;
Chris@213 844 }
Chris@213 845
Chris@200 846 //!!! Shouldn't be using QtGui here. Need a better way to report this.
Chris@200 847 QMessageBox::critical
Chris@200 848 (0, QApplication::tr("FFT cache resize failed"),
Chris@200 849 QApplication::tr
Chris@200 850 ("Failed to create or resize an FFT model slice.\n"
Chris@200 851 "There may be insufficient memory or disc space to continue."));
Chris@172 852 }
Chris@148 853
Chris@213 854 StorageAdviser::notifyDoneAllocation
Chris@213 855 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@213 856 StorageAdviser::DiscAllocation,
Chris@213 857 width * m_height *
Chris@213 858 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
Chris@213 859
Chris@148 860 m_caches[c] = cache;
Chris@148 861 m_lastUsedCache = c;
Chris@148 862 return cache;
Chris@148 863 }
Chris@148 864
Chris@148 865 float
Chris@148 866 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 867 {
Chris@183 868 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
Chris@183 869
Chris@217 870 if (x >= m_width || y >= m_height) return 0;
Chris@217 871
Chris@148 872 size_t col;
Chris@148 873 FFTCache *cache = getCache(x, col);
Chris@200 874 if (!cache) return 0;
Chris@148 875
Chris@148 876 if (!cache->haveSetColumnAt(col)) {
Chris@280 877 #ifdef DEBUG_FFT_SERVER
Chris@183 878 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
Chris@183 879 << x << ")" << std::endl;
Chris@280 880 #endif
Chris@148 881 fillColumn(x);
Chris@148 882 }
Chris@148 883 return cache->getMagnitudeAt(col, y);
Chris@148 884 }
Chris@148 885
Chris@148 886 float
Chris@148 887 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 888 {
Chris@183 889 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
Chris@183 890
Chris@217 891 if (x >= m_width || y >= m_height) return 0;
Chris@217 892
Chris@148 893 size_t col;
Chris@148 894 FFTCache *cache = getCache(x, col);
Chris@200 895 if (!cache) return 0;
Chris@148 896
Chris@148 897 if (!cache->haveSetColumnAt(col)) {
Chris@148 898 fillColumn(x);
Chris@148 899 }
Chris@148 900 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 901 }
Chris@148 902
Chris@148 903 float
Chris@148 904 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 905 {
Chris@183 906 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
Chris@183 907
Chris@217 908 if (x >= m_width) return 0;
Chris@217 909
Chris@148 910 size_t col;
Chris@148 911 FFTCache *cache = getCache(x, col);
Chris@200 912 if (!cache) return 0;
Chris@148 913
Chris@148 914 if (!cache->haveSetColumnAt(col)) {
Chris@148 915 fillColumn(x);
Chris@148 916 }
Chris@148 917 return cache->getMaximumMagnitudeAt(col);
Chris@148 918 }
Chris@148 919
Chris@148 920 float
Chris@148 921 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 922 {
Chris@183 923 Profiler profiler("FFTDataServer::getPhaseAt", false);
Chris@183 924
Chris@217 925 if (x >= m_width || y >= m_height) return 0;
Chris@217 926
Chris@148 927 size_t col;
Chris@148 928 FFTCache *cache = getCache(x, col);
Chris@200 929 if (!cache) return 0;
Chris@148 930
Chris@148 931 if (!cache->haveSetColumnAt(col)) {
Chris@148 932 fillColumn(x);
Chris@148 933 }
Chris@148 934 return cache->getPhaseAt(col, y);
Chris@148 935 }
Chris@148 936
Chris@148 937 void
Chris@148 938 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 939 {
Chris@183 940 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@183 941
Chris@216 942 if (x >= m_width || y >= m_height) {
Chris@216 943 real = 0;
Chris@216 944 imaginary = 0;
Chris@216 945 return;
Chris@216 946 }
Chris@216 947
Chris@148 948 size_t col;
Chris@148 949 FFTCache *cache = getCache(x, col);
Chris@216 950
Chris@216 951 if (!cache) {
Chris@216 952 real = 0;
Chris@216 953 imaginary = 0;
Chris@216 954 return;
Chris@216 955 }
Chris@148 956
Chris@148 957 if (!cache->haveSetColumnAt(col)) {
Chris@148 958 #ifdef DEBUG_FFT_SERVER
Chris@148 959 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
Chris@148 960 #endif
Chris@148 961 fillColumn(x);
Chris@148 962 }
Chris@264 963
Chris@264 964 cache->getValuesAt(col, y, real, imaginary);
Chris@148 965 }
Chris@148 966
Chris@148 967 bool
Chris@148 968 FFTDataServer::isColumnReady(size_t x)
Chris@148 969 {
Chris@183 970 Profiler profiler("FFTDataServer::isColumnReady", false);
Chris@183 971
Chris@217 972 if (x >= m_width) return true;
Chris@217 973
Chris@148 974 if (!haveCache(x)) {
Chris@148 975 if (m_lastUsedCache == -1) {
Chris@183 976 if (m_suspended) {
Chris@183 977 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
Chris@183 978 resume();
Chris@183 979 }
Chris@148 980 m_fillThread->start();
Chris@148 981 }
Chris@148 982 return false;
Chris@148 983 }
Chris@148 984
Chris@148 985 size_t col;
Chris@148 986 FFTCache *cache = getCache(x, col);
Chris@200 987 if (!cache) return true;
Chris@148 988
Chris@148 989 return cache->haveSetColumnAt(col);
Chris@148 990 }
Chris@148 991
Chris@148 992 void
Chris@148 993 FFTDataServer::fillColumn(size_t x)
Chris@148 994 {
Chris@183 995 Profiler profiler("FFTDataServer::fillColumn", false);
Chris@183 996
Chris@272 997 if (!m_model->isReady()) {
Chris@272 998 std::cerr << "WARNING: FFTDataServer::fillColumn("
Chris@272 999 << x << "): model not yet ready" << std::endl;
Chris@272 1000 return;
Chris@272 1001 }
Chris@272 1002
Chris@217 1003 if (!m_fftInput) {
Chris@217 1004 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1005 << "input has already been completed and discarded?"
Chris@217 1006 << std::endl;
Chris@217 1007 return;
Chris@217 1008 }
Chris@217 1009
Chris@217 1010 if (x >= m_width) {
Chris@217 1011 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 1012 << "x > width (" << x << " > " << m_width << ")"
Chris@217 1013 << std::endl;
Chris@217 1014 return;
Chris@217 1015 }
Chris@217 1016
Chris@148 1017 size_t col;
Chris@148 1018 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 1019 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 1020 #endif
Chris@148 1021 FFTCache *cache = getCache(x, col);
Chris@200 1022 if (!cache) return;
Chris@148 1023
Chris@244 1024 MutexLocker locker(&m_writeMutex,
Chris@244 1025 "FFTDataServer::m_writeMutex[fillColumn]");
Chris@148 1026
Chris@148 1027 if (cache->haveSetColumnAt(col)) return;
Chris@148 1028
Chris@148 1029 int startFrame = m_windowIncrement * x;
Chris@148 1030 int endFrame = startFrame + m_windowSize;
Chris@148 1031
Chris@295 1032 startFrame -= int(m_windowSize) / 2;
Chris@295 1033 endFrame -= int(m_windowSize) / 2;
Chris@148 1034 size_t pfx = 0;
Chris@148 1035
Chris@148 1036 size_t off = (m_fftSize - m_windowSize) / 2;
Chris@148 1037
Chris@148 1038 for (size_t i = 0; i < off; ++i) {
Chris@148 1039 m_fftInput[i] = 0.0;
Chris@148 1040 m_fftInput[m_fftSize - i - 1] = 0.0;
Chris@148 1041 }
Chris@148 1042
Chris@148 1043 if (startFrame < 0) {
Chris@148 1044 pfx = size_t(-startFrame);
Chris@148 1045 for (size_t i = 0; i < pfx; ++i) {
Chris@148 1046 m_fftInput[off + i] = 0.0;
Chris@148 1047 }
Chris@148 1048 }
Chris@148 1049
Chris@195 1050 #ifdef DEBUG_FFT_SERVER_FILL
Chris@193 1051 std::cerr << "FFTDataServer::fillColumn: requesting frames "
Chris@193 1052 << startFrame + pfx << " -> " << endFrame << " ( = "
Chris@193 1053 << endFrame - (startFrame + pfx) << ") at index "
Chris@193 1054 << off + pfx << " in buffer of size " << m_fftSize
Chris@193 1055 << " with window size " << m_windowSize
Chris@193 1056 << " from channel " << m_channel << std::endl;
Chris@195 1057 #endif
Chris@193 1058
Chris@300 1059 size_t count = 0;
Chris@300 1060 if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
Chris@300 1061
Chris@300 1062 size_t got = m_model->getData(m_channel, startFrame + pfx,
Chris@300 1063 count, m_fftInput + off + pfx);
Chris@148 1064
Chris@148 1065 while (got + pfx < m_windowSize) {
Chris@148 1066 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1067 ++got;
Chris@148 1068 }
Chris@148 1069
Chris@148 1070 if (m_channel == -1) {
Chris@148 1071 int channels = m_model->getChannelCount();
Chris@148 1072 if (channels > 1) {
Chris@148 1073 for (size_t i = 0; i < m_windowSize; ++i) {
Chris@148 1074 m_fftInput[off + i] /= channels;
Chris@148 1075 }
Chris@148 1076 }
Chris@148 1077 }
Chris@148 1078
Chris@148 1079 m_windower.cut(m_fftInput + off);
Chris@148 1080
Chris@148 1081 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 1082 fftsample temp = m_fftInput[i];
Chris@148 1083 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
Chris@148 1084 m_fftInput[i + m_fftSize/2] = temp;
Chris@148 1085 }
Chris@148 1086
Chris@226 1087 fftf_execute(m_fftPlan);
Chris@148 1088
Chris@148 1089 fftsample factor = 0.0;
Chris@148 1090
Chris@203 1091 for (size_t i = 0; i <= m_fftSize/2; ++i) {
Chris@148 1092
Chris@264 1093 m_workbuffer[i] = m_fftOutput[i][0];
Chris@264 1094 m_workbuffer[i + m_fftSize/2 + 1] = m_fftOutput[i][1];
Chris@148 1095 }
Chris@148 1096
Chris@148 1097 cache->setColumnAt(col,
Chris@148 1098 m_workbuffer,
Chris@264 1099 m_workbuffer + m_fftSize/2+1);
Chris@154 1100
Chris@183 1101 if (m_suspended) {
Chris@183 1102 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1103 // resume();
Chris@183 1104 }
Chris@148 1105 }
Chris@148 1106
Chris@148 1107 size_t
Chris@148 1108 FFTDataServer::getFillCompletion() const
Chris@148 1109 {
Chris@148 1110 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1111 else return 100;
Chris@148 1112 }
Chris@148 1113
Chris@148 1114 size_t
Chris@148 1115 FFTDataServer::getFillExtent() const
Chris@148 1116 {
Chris@148 1117 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1118 else return m_model->getEndFrame();
Chris@148 1119 }
Chris@148 1120
Chris@148 1121 QString
Chris@148 1122 FFTDataServer::generateFileBasename() const
Chris@148 1123 {
Chris@148 1124 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1125 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1126 m_polar);
Chris@148 1127 }
Chris@148 1128
Chris@148 1129 QString
Chris@148 1130 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1131 int channel,
Chris@148 1132 WindowType windowType,
Chris@148 1133 size_t windowSize,
Chris@148 1134 size_t windowIncrement,
Chris@148 1135 size_t fftSize,
Chris@148 1136 bool polar)
Chris@148 1137 {
Chris@148 1138 char buffer[200];
Chris@148 1139
Chris@148 1140 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1141 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1142 (unsigned int)(channel + 1),
Chris@148 1143 (unsigned int)windowType,
Chris@148 1144 (unsigned int)windowSize,
Chris@148 1145 (unsigned int)windowIncrement,
Chris@148 1146 (unsigned int)fftSize,
Chris@148 1147 polar ? "-p" : "-r");
Chris@148 1148
Chris@148 1149 return buffer;
Chris@148 1150 }
Chris@148 1151
Chris@148 1152 void
Chris@148 1153 FFTDataServer::FillThread::run()
Chris@148 1154 {
Chris@148 1155 m_extent = 0;
Chris@148 1156 m_completion = 0;
Chris@148 1157
Chris@272 1158 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
Chris@272 1159 sleep(1);
Chris@272 1160 }
Chris@272 1161 if (m_server.m_exiting) return;
Chris@272 1162
Chris@148 1163 size_t start = m_server.m_model->getStartFrame();
Chris@148 1164 size_t end = m_server.m_model->getEndFrame();
Chris@148 1165 size_t remainingEnd = end;
Chris@148 1166
Chris@148 1167 int counter = 0;
Chris@246 1168 int updateAt = 1;
Chris@246 1169 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1170 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1171
Chris@148 1172 if (m_fillFrom > start) {
Chris@148 1173
Chris@148 1174 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1175
Chris@148 1176 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1177
Chris@148 1178 if (m_server.m_exiting) return;
Chris@148 1179
Chris@148 1180 while (m_server.m_suspended) {
Chris@148 1181 #ifdef DEBUG_FFT_SERVER
Chris@193 1182 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1183 #endif
Chris@244 1184 {
Chris@244 1185 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1186 "FFTDataServer::m_writeMutex[run/1]");
Chris@244 1187 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1188 }
Chris@159 1189 #ifdef DEBUG_FFT_SERVER
Chris@193 1190 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1191 #endif
Chris@148 1192 if (m_server.m_exiting) return;
Chris@148 1193 }
Chris@148 1194
Chris@148 1195 if (++counter == updateAt) {
Chris@148 1196 m_extent = f;
Chris@148 1197 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1198 float(end - start)));
Chris@148 1199 counter = 0;
Chris@246 1200 if (updateAt < maxUpdateAt) {
Chris@246 1201 updateAt *= 2;
Chris@246 1202 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1203 }
Chris@148 1204 }
Chris@148 1205 }
Chris@148 1206
Chris@148 1207 remainingEnd = m_fillFrom;
Chris@148 1208 if (remainingEnd > start) --remainingEnd;
Chris@148 1209 else remainingEnd = start;
Chris@148 1210 }
Chris@148 1211
Chris@148 1212 size_t baseCompletion = m_completion;
Chris@148 1213
Chris@148 1214 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1215
Chris@148 1216 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1217
Chris@148 1218 if (m_server.m_exiting) return;
Chris@148 1219
Chris@148 1220 while (m_server.m_suspended) {
Chris@148 1221 #ifdef DEBUG_FFT_SERVER
Chris@193 1222 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1223 #endif
Chris@244 1224 {
Chris@244 1225 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1226 "FFTDataServer::m_writeMutex[run/2]");
Chris@244 1227 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1228 }
Chris@148 1229 if (m_server.m_exiting) return;
Chris@148 1230 }
Chris@148 1231
Chris@148 1232 if (++counter == updateAt) {
Chris@148 1233 m_extent = f;
Chris@148 1234 m_completion = baseCompletion +
Chris@148 1235 size_t(100 * fabsf(float(f - start) /
Chris@148 1236 float(end - start)));
Chris@148 1237 counter = 0;
Chris@246 1238 if (updateAt < maxUpdateAt) {
Chris@246 1239 updateAt *= 2;
Chris@246 1240 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1241 }
Chris@148 1242 }
Chris@148 1243 }
Chris@148 1244
Chris@148 1245 m_completion = 100;
Chris@148 1246 m_extent = end;
Chris@148 1247 }
Chris@148 1248