annotate data/fft/FFTDataServer.cpp @ 263:71dfc6ab3b54

* Threaded mp3/ogg file reading. Not activated yet, as it doesn't work in context (SV needs to know the duration of its main model at the outset)
author Chris Cannam
date Thu, 24 May 2007 16:20:22 +0000
parents dc46851837d6
children 260032c26c4f
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@148 506 size_t start = m_model->getStartFrame();
Chris@148 507 size_t end = m_model->getEndFrame();
Chris@148 508
Chris@148 509 m_width = (end - start) / m_windowIncrement + 1;
Chris@203 510 m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
Chris@148 511
Chris@216 512 #ifdef DEBUG_FFT_SERVER
Chris@216 513 std::cerr << "FFTDataServer(" << this << "): dimensions are "
Chris@216 514 << m_width << "x" << m_height << std::endl;
Chris@216 515 #endif
Chris@216 516
Chris@148 517 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 518 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
Chris@148 519 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
Chris@148 520 else m_cacheWidth = maxCacheSize / columnSize;
Chris@148 521
Chris@148 522 int bits = 0;
Chris@148 523 while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; }
Chris@148 524 m_cacheWidth = 2;
Chris@148 525 while (bits) { m_cacheWidth <<= 1; --bits; }
Chris@172 526
Chris@172 527 //!!! Need to pass in what this server is intended for
Chris@172 528 // (e.g. playback processing, spectrogram, feature extraction),
Chris@172 529 // or pass in something akin to the storage adviser criteria.
Chris@172 530 // That probably goes alongside the polar argument.
Chris@172 531 // For now we'll assume "spectrogram" criteria for polar ffts,
Chris@172 532 // and "feature extraction" criteria for rectangular ones.
Chris@172 533
Chris@172 534 StorageAdviser::Criteria criteria;
Chris@172 535 if (m_polar) {
Chris@172 536 criteria = StorageAdviser::Criteria
Chris@172 537 (StorageAdviser::SpeedCritical | StorageAdviser::LongRetentionLikely);
Chris@172 538 } else {
Chris@172 539 criteria = StorageAdviser::Criteria(StorageAdviser::PrecisionCritical);
Chris@172 540 }
Chris@172 541
Chris@172 542 int cells = m_width * m_height;
Chris@172 543 int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
Chris@172 544 int maximumSize = (cells / 1024) * sizeof(float); // kb
Chris@215 545
Chris@215 546 StorageAdviser::Recommendation recommendation;
Chris@172 547
Chris@215 548 try {
Chris@215 549
Chris@215 550 recommendation =
Chris@215 551 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
Chris@215 552
Chris@215 553 } catch (InsufficientDiscSpace s) {
Chris@215 554
Chris@215 555 // Delete any unused servers we may have been leaving around
Chris@215 556 // in case we wanted them again
Chris@215 557
Chris@215 558 purgeLimbo(0);
Chris@215 559
Chris@215 560 // This time we don't catch InsufficientDiscSpace -- we
Chris@215 561 // haven't allocated anything yet and can safely let the
Chris@215 562 // exception out to indicate to the caller that we can't
Chris@215 563 // handle it.
Chris@215 564
Chris@215 565 recommendation =
Chris@215 566 StorageAdviser::recommend(criteria, minimumSize, maximumSize);
Chris@215 567 }
Chris@172 568
Chris@259 569 // std::cerr << "Recommendation was: " << recommendation << std::endl;
Chris@172 570
Chris@172 571 m_memoryCache = ((recommendation & StorageAdviser::UseMemory) ||
Chris@172 572 (recommendation & StorageAdviser::PreferMemory));
Chris@172 573
Chris@172 574 m_compactCache = (recommendation & StorageAdviser::ConserveSpace);
Chris@148 575
Chris@148 576 #ifdef DEBUG_FFT_SERVER
Chris@148 577 std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl;
Chris@148 578 #endif
Chris@148 579
Chris@205 580 StorageAdviser::notifyPlannedAllocation
Chris@205 581 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@205 582 StorageAdviser::DiscAllocation,
Chris@205 583 m_compactCache ? minimumSize : maximumSize);
Chris@205 584
Chris@148 585 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
Chris@148 586 m_caches.push_back(0);
Chris@148 587 }
Chris@148 588
Chris@148 589 m_fftInput = (fftsample *)
Chris@226 590 fftf_malloc(fftSize * sizeof(fftsample));
Chris@148 591
Chris@226 592 m_fftOutput = (fftf_complex *)
Chris@226 593 fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
Chris@148 594
Chris@148 595 m_workbuffer = (float *)
Chris@226 596 fftf_malloc((fftSize+2) * sizeof(float));
Chris@148 597
Chris@226 598 m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
Chris@148 599 m_fftInput,
Chris@148 600 m_fftOutput,
Chris@148 601 FFTW_ESTIMATE);
Chris@148 602
Chris@148 603 if (!m_fftPlan) {
Chris@226 604 std::cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
Chris@148 605 throw(0);
Chris@148 606 }
Chris@148 607
Chris@148 608 m_fillThread = new FillThread(*this, fillFromColumn);
Chris@148 609 }
Chris@148 610
Chris@148 611 FFTDataServer::~FFTDataServer()
Chris@148 612 {
Chris@148 613 #ifdef DEBUG_FFT_SERVER
Chris@193 614 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << std::endl;
Chris@148 615 #endif
Chris@148 616
Chris@155 617 m_suspended = false;
Chris@148 618 m_exiting = true;
Chris@148 619 m_condition.wakeAll();
Chris@148 620 if (m_fillThread) {
Chris@148 621 m_fillThread->wait();
Chris@148 622 delete m_fillThread;
Chris@148 623 }
Chris@148 624
Chris@244 625 MutexLocker locker(&m_writeMutex,
Chris@244 626 "FFTDataServer::m_writeMutex[~FFTDataServer]");
Chris@148 627
Chris@148 628 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@205 629 if (*i) {
Chris@205 630 delete *i;
Chris@205 631 } else {
Chris@205 632 StorageAdviser::notifyDoneAllocation
Chris@205 633 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@205 634 StorageAdviser::DiscAllocation,
Chris@205 635 m_cacheWidth * m_height *
Chris@205 636 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
Chris@205 637 }
Chris@148 638 }
Chris@148 639
Chris@148 640 deleteProcessingData();
Chris@148 641 }
Chris@148 642
Chris@148 643 void
Chris@148 644 FFTDataServer::deleteProcessingData()
Chris@148 645 {
Chris@193 646 #ifdef DEBUG_FFT_SERVER
Chris@193 647 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << std::endl;
Chris@193 648 #endif
Chris@148 649 if (m_fftInput) {
Chris@226 650 fftf_destroy_plan(m_fftPlan);
Chris@226 651 fftf_free(m_fftInput);
Chris@226 652 fftf_free(m_fftOutput);
Chris@226 653 fftf_free(m_workbuffer);
Chris@148 654 }
Chris@148 655 m_fftInput = 0;
Chris@148 656 }
Chris@148 657
Chris@148 658 void
Chris@148 659 FFTDataServer::suspend()
Chris@148 660 {
Chris@148 661 #ifdef DEBUG_FFT_SERVER
Chris@193 662 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << std::endl;
Chris@148 663 #endif
Chris@183 664 Profiler profiler("FFTDataServer::suspend", false);
Chris@183 665
Chris@244 666 MutexLocker locker(&m_writeMutex,
Chris@244 667 "FFTDataServer::m_writeMutex[suspend]");
Chris@148 668 m_suspended = true;
Chris@148 669 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 670 if (*i) (*i)->suspend();
Chris@148 671 }
Chris@148 672 }
Chris@148 673
Chris@148 674 void
Chris@155 675 FFTDataServer::suspendWrites()
Chris@155 676 {
Chris@155 677 #ifdef DEBUG_FFT_SERVER
Chris@193 678 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << std::endl;
Chris@155 679 #endif
Chris@183 680 Profiler profiler("FFTDataServer::suspendWrites", false);
Chris@183 681
Chris@155 682 m_suspended = true;
Chris@155 683 }
Chris@155 684
Chris@155 685 void
Chris@148 686 FFTDataServer::resume()
Chris@148 687 {
Chris@154 688 #ifdef DEBUG_FFT_SERVER
Chris@193 689 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << std::endl;
Chris@154 690 #endif
Chris@183 691 Profiler profiler("FFTDataServer::resume", false);
Chris@183 692
Chris@148 693 m_suspended = false;
Chris@157 694 if (m_fillThread) {
Chris@157 695 if (m_fillThread->isFinished()) {
Chris@157 696 delete m_fillThread;
Chris@157 697 m_fillThread = 0;
Chris@157 698 deleteProcessingData();
Chris@157 699 } else {
Chris@157 700 m_condition.wakeAll();
Chris@157 701 }
Chris@157 702 }
Chris@148 703 }
Chris@148 704
Chris@148 705 FFTCache *
Chris@148 706 FFTDataServer::getCacheAux(size_t c)
Chris@148 707 {
Chris@183 708 Profiler profiler("FFTDataServer::getCacheAux", false);
Chris@193 709 #ifdef DEBUG_FFT_SERVER
Chris@193 710 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::getCacheAux" << std::endl;
Chris@193 711 #endif
Chris@183 712
Chris@244 713 MutexLocker locker(&m_writeMutex,
Chris@244 714 "FFTDataServer::m_writeMutex[getCacheAux]");
Chris@148 715
Chris@148 716 if (m_lastUsedCache == -1) {
Chris@148 717 m_fillThread->start();
Chris@148 718 }
Chris@148 719
Chris@148 720 if (int(c) != m_lastUsedCache) {
Chris@148 721
Chris@148 722 // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
Chris@148 723
Chris@148 724 for (IntQueue::iterator i = m_dormantCaches.begin();
Chris@148 725 i != m_dormantCaches.end(); ++i) {
Chris@259 726 if (*i == int(c)) {
Chris@148 727 m_dormantCaches.erase(i);
Chris@148 728 break;
Chris@148 729 }
Chris@148 730 }
Chris@148 731
Chris@148 732 if (m_lastUsedCache >= 0) {
Chris@148 733 bool inDormant = false;
Chris@148 734 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
Chris@148 735 if (m_dormantCaches[i] == m_lastUsedCache) {
Chris@148 736 inDormant = true;
Chris@148 737 break;
Chris@148 738 }
Chris@148 739 }
Chris@148 740 if (!inDormant) {
Chris@148 741 m_dormantCaches.push_back(m_lastUsedCache);
Chris@148 742 }
Chris@148 743 while (m_dormantCaches.size() > 4) {
Chris@148 744 int dc = m_dormantCaches.front();
Chris@148 745 m_dormantCaches.pop_front();
Chris@148 746 m_caches[dc]->suspend();
Chris@148 747 }
Chris@148 748 }
Chris@148 749 }
Chris@148 750
Chris@148 751 if (m_caches[c]) {
Chris@148 752 m_lastUsedCache = c;
Chris@148 753 return m_caches[c];
Chris@148 754 }
Chris@148 755
Chris@148 756 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
Chris@148 757
Chris@172 758 FFTCache *cache = 0;
Chris@172 759
Chris@213 760 size_t width = m_cacheWidth;
Chris@213 761 if (c * m_cacheWidth + width > m_width) {
Chris@213 762 width = m_width - c * m_cacheWidth;
Chris@213 763 }
Chris@213 764
Chris@200 765 try {
Chris@200 766
Chris@200 767 if (m_memoryCache) {
Chris@172 768
Chris@200 769 cache = new FFTMemoryCache();
Chris@172 770
Chris@200 771 } else if (m_compactCache) {
Chris@172 772
Chris@200 773 cache = new FFTFileCache(name, MatrixFile::ReadWrite,
Chris@200 774 FFTFileCache::Compact);
Chris@172 775
Chris@200 776 } else {
Chris@172 777
Chris@200 778 cache = new FFTFileCache(name, MatrixFile::ReadWrite,
Chris@200 779 m_polar ? FFTFileCache::Polar :
Chris@200 780 FFTFileCache::Rectangular);
Chris@200 781 }
Chris@200 782
Chris@200 783 cache->resize(width, m_height);
Chris@200 784 cache->reset();
Chris@200 785
Chris@213 786 } catch (std::bad_alloc) {
Chris@205 787
Chris@213 788 delete cache;
Chris@213 789 cache = 0;
Chris@213 790
Chris@213 791 if (m_memoryCache) {
Chris@213 792
Chris@213 793 std::cerr << "WARNING: Memory allocation failed when resizing"
Chris@213 794 << " FFT memory cache no. " << c << " to " << width
Chris@213 795 << "x" << m_height << " (of total width " << m_width
Chris@213 796 << "): falling back to disc cache" << std::endl;
Chris@213 797
Chris@213 798 try {
Chris@213 799
Chris@213 800 cache = new FFTFileCache(name, MatrixFile::ReadWrite,
Chris@213 801 FFTFileCache::Compact);
Chris@213 802
Chris@213 803 cache->resize(width, m_height);
Chris@213 804 cache->reset();
Chris@213 805
Chris@213 806 } catch (std::bad_alloc) {
Chris@213 807
Chris@213 808 delete cache;
Chris@213 809 cache = 0;
Chris@213 810 }
Chris@213 811 }
Chris@213 812
Chris@213 813 if (cache) {
Chris@213 814 std::cerr << "ERROR: Memory allocation failed when resizing"
Chris@213 815 << " FFT file cache no. " << c << " to " << width
Chris@213 816 << "x" << m_height << " (of total width " << m_width
Chris@213 817 << "): abandoning this cache" << std::endl;
Chris@213 818 }
Chris@213 819
Chris@200 820 //!!! Shouldn't be using QtGui here. Need a better way to report this.
Chris@200 821 QMessageBox::critical
Chris@200 822 (0, QApplication::tr("FFT cache resize failed"),
Chris@200 823 QApplication::tr
Chris@200 824 ("Failed to create or resize an FFT model slice.\n"
Chris@200 825 "There may be insufficient memory or disc space to continue."));
Chris@172 826 }
Chris@148 827
Chris@213 828 StorageAdviser::notifyDoneAllocation
Chris@213 829 (m_memoryCache ? StorageAdviser::MemoryAllocation :
Chris@213 830 StorageAdviser::DiscAllocation,
Chris@213 831 width * m_height *
Chris@213 832 (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1);
Chris@213 833
Chris@148 834 m_caches[c] = cache;
Chris@148 835 m_lastUsedCache = c;
Chris@148 836 return cache;
Chris@148 837 }
Chris@148 838
Chris@148 839 float
Chris@148 840 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 841 {
Chris@183 842 Profiler profiler("FFTDataServer::getMagnitudeAt", false);
Chris@183 843
Chris@217 844 if (x >= m_width || y >= m_height) return 0;
Chris@217 845
Chris@148 846 size_t col;
Chris@148 847 FFTCache *cache = getCache(x, col);
Chris@200 848 if (!cache) return 0;
Chris@148 849
Chris@148 850 if (!cache->haveSetColumnAt(col)) {
Chris@183 851 std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
Chris@183 852 << x << ")" << std::endl;
Chris@148 853 fillColumn(x);
Chris@148 854 }
Chris@148 855 return cache->getMagnitudeAt(col, y);
Chris@148 856 }
Chris@148 857
Chris@148 858 float
Chris@148 859 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 860 {
Chris@183 861 Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
Chris@183 862
Chris@217 863 if (x >= m_width || y >= m_height) return 0;
Chris@217 864
Chris@148 865 size_t col;
Chris@148 866 FFTCache *cache = getCache(x, col);
Chris@200 867 if (!cache) return 0;
Chris@148 868
Chris@148 869 if (!cache->haveSetColumnAt(col)) {
Chris@148 870 fillColumn(x);
Chris@148 871 }
Chris@148 872 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 873 }
Chris@148 874
Chris@148 875 float
Chris@148 876 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 877 {
Chris@183 878 Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
Chris@183 879
Chris@217 880 if (x >= m_width) return 0;
Chris@217 881
Chris@148 882 size_t col;
Chris@148 883 FFTCache *cache = getCache(x, col);
Chris@200 884 if (!cache) return 0;
Chris@148 885
Chris@148 886 if (!cache->haveSetColumnAt(col)) {
Chris@148 887 fillColumn(x);
Chris@148 888 }
Chris@148 889 return cache->getMaximumMagnitudeAt(col);
Chris@148 890 }
Chris@148 891
Chris@148 892 float
Chris@148 893 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 894 {
Chris@183 895 Profiler profiler("FFTDataServer::getPhaseAt", false);
Chris@183 896
Chris@217 897 if (x >= m_width || y >= m_height) return 0;
Chris@217 898
Chris@148 899 size_t col;
Chris@148 900 FFTCache *cache = getCache(x, col);
Chris@200 901 if (!cache) return 0;
Chris@148 902
Chris@148 903 if (!cache->haveSetColumnAt(col)) {
Chris@148 904 fillColumn(x);
Chris@148 905 }
Chris@148 906 return cache->getPhaseAt(col, y);
Chris@148 907 }
Chris@148 908
Chris@148 909 void
Chris@148 910 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 911 {
Chris@183 912 Profiler profiler("FFTDataServer::getValuesAt", false);
Chris@183 913
Chris@216 914 if (x >= m_width || y >= m_height) {
Chris@216 915 real = 0;
Chris@216 916 imaginary = 0;
Chris@216 917 return;
Chris@216 918 }
Chris@216 919
Chris@148 920 size_t col;
Chris@148 921 FFTCache *cache = getCache(x, col);
Chris@216 922
Chris@216 923 if (!cache) {
Chris@216 924 real = 0;
Chris@216 925 imaginary = 0;
Chris@216 926 return;
Chris@216 927 }
Chris@148 928
Chris@148 929 if (!cache->haveSetColumnAt(col)) {
Chris@148 930 #ifdef DEBUG_FFT_SERVER
Chris@148 931 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
Chris@148 932 #endif
Chris@148 933 fillColumn(x);
Chris@148 934 }
Chris@148 935 float magnitude = cache->getMagnitudeAt(col, y);
Chris@148 936 float phase = cache->getPhaseAt(col, y);
Chris@148 937 real = magnitude * cosf(phase);
Chris@148 938 imaginary = magnitude * sinf(phase);
Chris@148 939 }
Chris@148 940
Chris@148 941 bool
Chris@148 942 FFTDataServer::isColumnReady(size_t x)
Chris@148 943 {
Chris@183 944 Profiler profiler("FFTDataServer::isColumnReady", false);
Chris@183 945
Chris@217 946 if (x >= m_width) return true;
Chris@217 947
Chris@148 948 if (!haveCache(x)) {
Chris@148 949 if (m_lastUsedCache == -1) {
Chris@183 950 if (m_suspended) {
Chris@183 951 std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
Chris@183 952 resume();
Chris@183 953 }
Chris@148 954 m_fillThread->start();
Chris@148 955 }
Chris@148 956 return false;
Chris@148 957 }
Chris@148 958
Chris@148 959 size_t col;
Chris@148 960 FFTCache *cache = getCache(x, col);
Chris@200 961 if (!cache) return true;
Chris@148 962
Chris@148 963 return cache->haveSetColumnAt(col);
Chris@148 964 }
Chris@148 965
Chris@148 966 void
Chris@148 967 FFTDataServer::fillColumn(size_t x)
Chris@148 968 {
Chris@183 969 Profiler profiler("FFTDataServer::fillColumn", false);
Chris@183 970
Chris@217 971 if (!m_fftInput) {
Chris@217 972 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 973 << "input has already been completed and discarded?"
Chris@217 974 << std::endl;
Chris@217 975 return;
Chris@217 976 }
Chris@217 977
Chris@217 978 if (x >= m_width) {
Chris@217 979 std::cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
Chris@217 980 << "x > width (" << x << " > " << m_width << ")"
Chris@217 981 << std::endl;
Chris@217 982 return;
Chris@217 983 }
Chris@217 984
Chris@148 985 size_t col;
Chris@148 986 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 987 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 988 #endif
Chris@148 989 FFTCache *cache = getCache(x, col);
Chris@200 990 if (!cache) return;
Chris@148 991
Chris@244 992 MutexLocker locker(&m_writeMutex,
Chris@244 993 "FFTDataServer::m_writeMutex[fillColumn]");
Chris@148 994
Chris@148 995 if (cache->haveSetColumnAt(col)) return;
Chris@148 996
Chris@148 997 int startFrame = m_windowIncrement * x;
Chris@148 998 int endFrame = startFrame + m_windowSize;
Chris@148 999
Chris@148 1000 startFrame -= int(m_windowSize - m_windowIncrement) / 2;
Chris@148 1001 endFrame -= int(m_windowSize - m_windowIncrement) / 2;
Chris@148 1002 size_t pfx = 0;
Chris@148 1003
Chris@148 1004 size_t off = (m_fftSize - m_windowSize) / 2;
Chris@148 1005
Chris@148 1006 for (size_t i = 0; i < off; ++i) {
Chris@148 1007 m_fftInput[i] = 0.0;
Chris@148 1008 m_fftInput[m_fftSize - i - 1] = 0.0;
Chris@148 1009 }
Chris@148 1010
Chris@148 1011 if (startFrame < 0) {
Chris@148 1012 pfx = size_t(-startFrame);
Chris@148 1013 for (size_t i = 0; i < pfx; ++i) {
Chris@148 1014 m_fftInput[off + i] = 0.0;
Chris@148 1015 }
Chris@148 1016 }
Chris@148 1017
Chris@195 1018 #ifdef DEBUG_FFT_SERVER_FILL
Chris@193 1019 std::cerr << "FFTDataServer::fillColumn: requesting frames "
Chris@193 1020 << startFrame + pfx << " -> " << endFrame << " ( = "
Chris@193 1021 << endFrame - (startFrame + pfx) << ") at index "
Chris@193 1022 << off + pfx << " in buffer of size " << m_fftSize
Chris@193 1023 << " with window size " << m_windowSize
Chris@193 1024 << " from channel " << m_channel << std::endl;
Chris@195 1025 #endif
Chris@193 1026
Chris@148 1027 size_t got = m_model->getValues(m_channel, startFrame + pfx,
Chris@148 1028 endFrame, m_fftInput + off + pfx);
Chris@148 1029
Chris@148 1030 while (got + pfx < m_windowSize) {
Chris@148 1031 m_fftInput[off + got + pfx] = 0.0;
Chris@148 1032 ++got;
Chris@148 1033 }
Chris@148 1034
Chris@148 1035 if (m_channel == -1) {
Chris@148 1036 int channels = m_model->getChannelCount();
Chris@148 1037 if (channels > 1) {
Chris@148 1038 for (size_t i = 0; i < m_windowSize; ++i) {
Chris@148 1039 m_fftInput[off + i] /= channels;
Chris@148 1040 }
Chris@148 1041 }
Chris@148 1042 }
Chris@148 1043
Chris@148 1044 m_windower.cut(m_fftInput + off);
Chris@148 1045
Chris@148 1046 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 1047 fftsample temp = m_fftInput[i];
Chris@148 1048 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
Chris@148 1049 m_fftInput[i + m_fftSize/2] = temp;
Chris@148 1050 }
Chris@148 1051
Chris@226 1052 fftf_execute(m_fftPlan);
Chris@148 1053
Chris@148 1054 fftsample factor = 0.0;
Chris@148 1055
Chris@203 1056 for (size_t i = 0; i <= m_fftSize/2; ++i) {
Chris@148 1057
Chris@148 1058 fftsample mag = sqrtf(m_fftOutput[i][0] * m_fftOutput[i][0] +
Chris@148 1059 m_fftOutput[i][1] * m_fftOutput[i][1]);
Chris@148 1060 mag /= m_windowSize / 2;
Chris@148 1061
Chris@148 1062 if (mag > factor) factor = mag;
Chris@148 1063
Chris@148 1064 fftsample phase = atan2f(m_fftOutput[i][1], m_fftOutput[i][0]);
Chris@148 1065 phase = princargf(phase);
Chris@148 1066
Chris@148 1067 m_workbuffer[i] = mag;
Chris@203 1068 m_workbuffer[i + m_fftSize/2+1] = phase;
Chris@148 1069 }
Chris@148 1070
Chris@148 1071 cache->setColumnAt(col,
Chris@148 1072 m_workbuffer,
Chris@203 1073 m_workbuffer + m_fftSize/2+1,
Chris@148 1074 factor);
Chris@154 1075
Chris@183 1076 if (m_suspended) {
Chris@183 1077 // std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
Chris@183 1078 // resume();
Chris@183 1079 }
Chris@148 1080 }
Chris@148 1081
Chris@148 1082 size_t
Chris@148 1083 FFTDataServer::getFillCompletion() const
Chris@148 1084 {
Chris@148 1085 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 1086 else return 100;
Chris@148 1087 }
Chris@148 1088
Chris@148 1089 size_t
Chris@148 1090 FFTDataServer::getFillExtent() const
Chris@148 1091 {
Chris@148 1092 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 1093 else return m_model->getEndFrame();
Chris@148 1094 }
Chris@148 1095
Chris@148 1096 QString
Chris@148 1097 FFTDataServer::generateFileBasename() const
Chris@148 1098 {
Chris@148 1099 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 1100 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 1101 m_polar);
Chris@148 1102 }
Chris@148 1103
Chris@148 1104 QString
Chris@148 1105 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 1106 int channel,
Chris@148 1107 WindowType windowType,
Chris@148 1108 size_t windowSize,
Chris@148 1109 size_t windowIncrement,
Chris@148 1110 size_t fftSize,
Chris@148 1111 bool polar)
Chris@148 1112 {
Chris@148 1113 char buffer[200];
Chris@148 1114
Chris@148 1115 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 1116 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 1117 (unsigned int)(channel + 1),
Chris@148 1118 (unsigned int)windowType,
Chris@148 1119 (unsigned int)windowSize,
Chris@148 1120 (unsigned int)windowIncrement,
Chris@148 1121 (unsigned int)fftSize,
Chris@148 1122 polar ? "-p" : "-r");
Chris@148 1123
Chris@148 1124 return buffer;
Chris@148 1125 }
Chris@148 1126
Chris@148 1127 void
Chris@148 1128 FFTDataServer::FillThread::run()
Chris@148 1129 {
Chris@148 1130 m_extent = 0;
Chris@148 1131 m_completion = 0;
Chris@148 1132
Chris@148 1133 size_t start = m_server.m_model->getStartFrame();
Chris@148 1134 size_t end = m_server.m_model->getEndFrame();
Chris@148 1135 size_t remainingEnd = end;
Chris@148 1136
Chris@148 1137 int counter = 0;
Chris@246 1138 int updateAt = 1;
Chris@246 1139 int maxUpdateAt = (end / m_server.m_windowIncrement) / 20;
Chris@246 1140 if (maxUpdateAt < 100) maxUpdateAt = 100;
Chris@148 1141
Chris@148 1142 if (m_fillFrom > start) {
Chris@148 1143
Chris@148 1144 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 1145
Chris@148 1146 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1147
Chris@148 1148 if (m_server.m_exiting) return;
Chris@148 1149
Chris@148 1150 while (m_server.m_suspended) {
Chris@148 1151 #ifdef DEBUG_FFT_SERVER
Chris@193 1152 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1153 #endif
Chris@244 1154 {
Chris@244 1155 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1156 "FFTDataServer::m_writeMutex[run/1]");
Chris@244 1157 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1158 }
Chris@159 1159 #ifdef DEBUG_FFT_SERVER
Chris@193 1160 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << std::endl;
Chris@159 1161 #endif
Chris@148 1162 if (m_server.m_exiting) return;
Chris@148 1163 }
Chris@148 1164
Chris@148 1165 if (++counter == updateAt) {
Chris@148 1166 m_extent = f;
Chris@148 1167 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 1168 float(end - start)));
Chris@148 1169 counter = 0;
Chris@246 1170 if (updateAt < maxUpdateAt) {
Chris@246 1171 updateAt *= 2;
Chris@246 1172 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1173 }
Chris@148 1174 }
Chris@148 1175 }
Chris@148 1176
Chris@148 1177 remainingEnd = m_fillFrom;
Chris@148 1178 if (remainingEnd > start) --remainingEnd;
Chris@148 1179 else remainingEnd = start;
Chris@148 1180 }
Chris@148 1181
Chris@148 1182 size_t baseCompletion = m_completion;
Chris@148 1183
Chris@148 1184 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 1185
Chris@148 1186 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 1187
Chris@148 1188 if (m_server.m_exiting) return;
Chris@148 1189
Chris@148 1190 while (m_server.m_suspended) {
Chris@148 1191 #ifdef DEBUG_FFT_SERVER
Chris@193 1192 std::cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << std::endl;
Chris@148 1193 #endif
Chris@244 1194 {
Chris@244 1195 MutexLocker locker(&m_server.m_writeMutex,
Chris@244 1196 "FFTDataServer::m_writeMutex[run/2]");
Chris@244 1197 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@244 1198 }
Chris@148 1199 if (m_server.m_exiting) return;
Chris@148 1200 }
Chris@148 1201
Chris@148 1202 if (++counter == updateAt) {
Chris@148 1203 m_extent = f;
Chris@148 1204 m_completion = baseCompletion +
Chris@148 1205 size_t(100 * fabsf(float(f - start) /
Chris@148 1206 float(end - start)));
Chris@148 1207 counter = 0;
Chris@246 1208 if (updateAt < maxUpdateAt) {
Chris@246 1209 updateAt *= 2;
Chris@246 1210 if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
Chris@246 1211 }
Chris@148 1212 }
Chris@148 1213 }
Chris@148 1214
Chris@148 1215 m_completion = 100;
Chris@148 1216 m_extent = end;
Chris@148 1217 }
Chris@148 1218