annotate data/fft/FFTDataServer.cpp @ 295:a2dc34ce146a

* Window should be centred on its nominal time. I'm not sure what the reasoning was behind the previous formulations of these two lines.
author Chris Cannam
date Thu, 06 Sep 2007 15:14:47 +0000
parents 3020904de772
children 5877d68815c7
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@148 1059 size_t got = m_model->getValues(m_channel, startFrame + pfx,
Chris@148 1060 endFrame, m_fftInput + off + pfx);
Chris@148 1061
Chris@148 1062 while (got + pfx < m_windowSize) {
Chris@148 1063 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1064 ++got;
Chris@148 1065 }
Chris@148 1066
Chris@148 1067 if (m_channel == -1) {
Chris@148 1068 int channels = m_model->getChannelCount();
Chris@148 1069 if (channels > 1) {
Chris@148 1070 for (size_t i = 0; i < m_windowSize; ++i) {
Chris@148 1071 m_fftInput[off + i] /= channels;
Chris@148 1072 }
Chris@148 1073 }
Chris@148 1074 }
Chris@148 1075
Chris@148 1076 m_windower.cut(m_fftInput + off);
Chris@148 1077
Chris@148 1078 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 1079 fftsample temp = m_fftInput[i];
Chris@148 1080 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
Chris@148 1081 m_fftInput[i + m_fftSize/2] = temp;
Chris@148 1082 }
Chris@148 1083
Chris@226 1084 fftf_execute(m_fftPlan);
Chris@148 1085
Chris@148 1086 fftsample factor = 0.0;
Chris@148 1087
Chris@203 1088 for (size_t i = 0; i <= m_fftSize/2; ++i) {
Chris@148 1089
Chris@264 1090 m_workbuffer[i] = m_fftOutput[i][0];
Chris@264 1091 m_workbuffer[i + m_fftSize/2 + 1] = m_fftOutput[i][1];
Chris@148 1092 }
Chris@148 1093
Chris@148 1094 cache->setColumnAt(col,
Chris@148 1095 m_workbuffer,
Chris@264 1096 m_workbuffer + m_fftSize/2+1);
Chris@154 1097
Chris@183 1098 if (m_suspended) {
Chris@183 1099 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1100 // resume();
Chris@183 1101 }
Chris@148 1102 }
Chris@148 1103
Chris@148 1104 size_t
Chris@148 1105 FFTDataServer::getFillCompletion() const
Chris@148 1106 {
Chris@148 1107 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1108 else return 100;
Chris@148 1109 }
Chris@148 1110
Chris@148 1111 size_t
Chris@148 1112 FFTDataServer::getFillExtent() const
Chris@148 1113 {
Chris@148 1114 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1115 else return m_model->getEndFrame();
Chris@148 1116 }
Chris@148 1117
Chris@148 1118 QString
Chris@148 1119 FFTDataServer::generateFileBasename() const
Chris@148 1120 {
Chris@148 1121 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1122 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1123 m_polar);
Chris@148 1124 }
Chris@148 1125
Chris@148 1126 QString
Chris@148 1127 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1128 int channel,
Chris@148 1129 WindowType windowType,
Chris@148 1130 size_t windowSize,
Chris@148 1131 size_t windowIncrement,
Chris@148 1132 size_t fftSize,
Chris@148 1133 bool polar)
Chris@148 1134 {
Chris@148 1135 char buffer[200];
Chris@148 1136
Chris@148 1137 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1138 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1139 (unsigned int)(channel + 1),
Chris@148 1140 (unsigned int)windowType,
Chris@148 1141 (unsigned int)windowSize,
Chris@148 1142 (unsigned int)windowIncrement,
Chris@148 1143 (unsigned int)fftSize,
Chris@148 1144 polar ? "-p" : "-r");
Chris@148 1145
Chris@148 1146 return buffer;
Chris@148 1147 }
Chris@148 1148
Chris@148 1149 void
Chris@148 1150 FFTDataServer::FillThread::run()
Chris@148 1151 {
Chris@148 1152 m_extent = 0;
Chris@148 1153 m_completion = 0;
Chris@148 1154
Chris@272 1155 while (!m_server.m_model->isReady() && !m_server.m_exiting) {
Chris@272 1156 sleep(1);
Chris@272 1157 }
Chris@272 1158 if (m_server.m_exiting) return;
Chris@272 1159
Chris@148 1160 size_t start = m_server.m_model->getStartFrame();
Chris@148 1161 size_t end = m_server.m_model->getEndFrame();
Chris@148 1162 size_t remainingEnd = end;
Chris@148 1163
Chris@148 1164 int counter = 0;
Chris@246 1165 int updateAt = 1;
Chris@246 1166 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1167 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1168
Chris@148 1169 if (m_fillFrom > start) {
Chris@148 1170
Chris@148 1171 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1172
Chris@148 1173 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1174
Chris@148 1175 if (m_server.m_exiting) return;
Chris@148 1176
Chris@148 1177 while (m_server.m_suspended) {
Chris@148 1178 #ifdef DEBUG_FFT_SERVER
Chris@193 1179 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1180 #endif
Chris@244 1181 {
Chris@244 1182 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1183 "FFTDataServer::m_writeMutex[run/1]");
Chris@244 1184 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1185 }
Chris@159 1186 #ifdef DEBUG_FFT_SERVER
Chris@193 1187 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1188 #endif
Chris@148 1189 if (m_server.m_exiting) return;
Chris@148 1190 }
Chris@148 1191
Chris@148 1192 if (++counter == updateAt) {
Chris@148 1193 m_extent = f;
Chris@148 1194 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1195 float(end - start)));
Chris@148 1196 counter = 0;
Chris@246 1197 if (updateAt < maxUpdateAt) {
Chris@246 1198 updateAt *= 2;
Chris@246 1199 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1200 }
Chris@148 1201 }
Chris@148 1202 }
Chris@148 1203
Chris@148 1204 remainingEnd = m_fillFrom;
Chris@148 1205 if (remainingEnd > start) --remainingEnd;
Chris@148 1206 else remainingEnd = start;
Chris@148 1207 }
Chris@148 1208
Chris@148 1209 size_t baseCompletion = m_completion;
Chris@148 1210
Chris@148 1211 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1212
Chris@148 1213 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1214
Chris@148 1215 if (m_server.m_exiting) return;
Chris@148 1216
Chris@148 1217 while (m_server.m_suspended) {
Chris@148 1218 #ifdef DEBUG_FFT_SERVER
Chris@193 1219 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1220 #endif
Chris@244 1221 {
Chris@244 1222 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1223 "FFTDataServer::m_writeMutex[run/2]");
Chris@244 1224 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1225 }
Chris@148 1226 if (m_server.m_exiting) return;
Chris@148 1227 }
Chris@148 1228
Chris@148 1229 if (++counter == updateAt) {
Chris@148 1230 m_extent = f;
Chris@148 1231 m_completion = baseCompletion +
Chris@148 1232 size_t(100 * fabsf(float(f - start) /
Chris@148 1233 float(end - start)));
Chris@148 1234 counter = 0;
Chris@246 1235 if (updateAt < maxUpdateAt) {
Chris@246 1236 updateAt *= 2;
Chris@246 1237 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1238 }
Chris@148 1239 }
Chris@148 1240 }
Chris@148 1241
Chris@148 1242 m_completion = 100;
Chris@148 1243 m_extent = end;
Chris@148 1244 }
Chris@148 1245