annotate data/fileio/FFTDataServer.cpp @ 167:665342c6ec57

* Add a bit of resistance to pane dragging so as to make it harder to inadvertently drag in the other axis from the one you intended
author Chris Cannam
date Fri, 22 Sep 2006 16:46:10 +0000
parents 1a42221a1522
children
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@148 7 This file copyright 2006 Chris Cannam.
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@148 19
Chris@148 20 #include "model/DenseTimeValueModel.h"
Chris@148 21
Chris@148 22 #include "base/System.h"
Chris@148 23
Chris@148 24 //#define DEBUG_FFT_SERVER 1
Chris@148 25 //#define DEBUG_FFT_SERVER_FILL 1
Chris@148 26
Chris@148 27 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 28 #define DEBUG_FFT_SERVER
Chris@148 29 #endif
Chris@148 30
Chris@148 31 FFTDataServer::ServerMap FFTDataServer::m_servers;
Chris@148 32 QMutex FFTDataServer::m_serverMapMutex;
Chris@148 33
Chris@148 34 FFTDataServer *
Chris@148 35 FFTDataServer::getInstance(const DenseTimeValueModel *model,
Chris@148 36 int channel,
Chris@148 37 WindowType windowType,
Chris@148 38 size_t windowSize,
Chris@148 39 size_t windowIncrement,
Chris@148 40 size_t fftSize,
Chris@148 41 bool polar,
Chris@148 42 size_t fillFromColumn)
Chris@148 43 {
Chris@148 44 QString n = generateFileBasename(model,
Chris@148 45 channel,
Chris@148 46 windowType,
Chris@148 47 windowSize,
Chris@148 48 windowIncrement,
Chris@148 49 fftSize,
Chris@148 50 polar);
Chris@148 51
Chris@148 52 FFTDataServer *server = 0;
Chris@148 53
Chris@148 54 QMutexLocker locker(&m_serverMapMutex);
Chris@148 55
Chris@148 56 if ((server = findServer(n))) {
Chris@148 57 return server;
Chris@148 58 }
Chris@148 59
Chris@148 60 QString npn = generateFileBasename(model,
Chris@148 61 channel,
Chris@148 62 windowType,
Chris@148 63 windowSize,
Chris@148 64 windowIncrement,
Chris@148 65 fftSize,
Chris@148 66 !polar);
Chris@148 67
Chris@148 68 if ((server = findServer(npn))) {
Chris@148 69 return server;
Chris@148 70 }
Chris@148 71
Chris@148 72 m_servers[n] = ServerCountPair
Chris@148 73 (new FFTDataServer(n,
Chris@148 74 model,
Chris@148 75 channel,
Chris@148 76 windowType,
Chris@148 77 windowSize,
Chris@148 78 windowIncrement,
Chris@148 79 fftSize,
Chris@148 80 polar,
Chris@148 81 fillFromColumn),
Chris@148 82 1);
Chris@148 83
Chris@148 84 return m_servers[n].first;
Chris@148 85 }
Chris@148 86
Chris@148 87 FFTDataServer *
Chris@148 88 FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model,
Chris@148 89 int channel,
Chris@148 90 WindowType windowType,
Chris@148 91 size_t windowSize,
Chris@148 92 size_t windowIncrement,
Chris@148 93 size_t fftSize,
Chris@148 94 bool polar,
Chris@148 95 size_t fillFromColumn)
Chris@148 96 {
Chris@148 97 // Fuzzy matching:
Chris@148 98 //
Chris@148 99 // -- if we're asked for polar and have non-polar, use it (and
Chris@148 100 // vice versa). This one is vital, and we do it for non-fuzzy as
Chris@148 101 // well (above).
Chris@148 102 //
Chris@148 103 // -- if we're asked for an instance with a given fft size and we
Chris@148 104 // have one already with a multiple of that fft size but the same
Chris@148 105 // window size and type (and model), we can draw the results from
Chris@148 106 // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the
Chris@148 107 // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the
Chris@148 108 // same window plus zero padding).
Chris@148 109 //
Chris@148 110 // -- if we're asked for an instance with a given window type and
Chris@148 111 // size and fft size and we have one already the same but with a
Chris@148 112 // smaller increment, we can draw the results from it (provided
Chris@148 113 // our increment is a multiple of its)
Chris@148 114 //
Chris@148 115 // The FFTFuzzyAdapter knows how to interpret these things. In
Chris@148 116 // both cases we require that the larger one is a power-of-two
Chris@148 117 // multiple of the smaller (e.g. even though in principle you can
Chris@148 118 // draw the results at increment 256 from those at increment 768
Chris@148 119 // or 1536, the fuzzy adapter doesn't support this).
Chris@148 120
Chris@148 121 {
Chris@148 122 QMutexLocker locker(&m_serverMapMutex);
Chris@148 123
Chris@148 124 ServerMap::iterator best = m_servers.end();
Chris@148 125 int bestdist = -1;
Chris@148 126
Chris@148 127 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@148 128
Chris@148 129 FFTDataServer *server = i->second.first;
Chris@148 130
Chris@148 131 if (server->getModel() == model &&
Chris@148 132 (server->getChannel() == channel || model->getChannelCount() == 1) &&
Chris@148 133 server->getWindowType() == windowType &&
Chris@148 134 server->getWindowSize() == windowSize &&
Chris@148 135 server->getWindowIncrement() <= windowIncrement &&
Chris@148 136 server->getFFTSize() >= fftSize) {
Chris@148 137
Chris@148 138 if ((windowIncrement % server->getWindowIncrement()) != 0) continue;
Chris@148 139 int ratio = windowIncrement / server->getWindowIncrement();
Chris@148 140 bool poweroftwo = true;
Chris@148 141 while (ratio > 1) {
Chris@148 142 if (ratio & 0x1) {
Chris@148 143 poweroftwo = false;
Chris@148 144 break;
Chris@148 145 }
Chris@148 146 ratio >>= 1;
Chris@148 147 }
Chris@148 148 if (!poweroftwo) continue;
Chris@148 149
Chris@148 150 if ((server->getFFTSize() % fftSize) != 0) continue;
Chris@148 151 ratio = server->getFFTSize() / fftSize;
Chris@148 152 while (ratio > 1) {
Chris@148 153 if (ratio & 0x1) {
Chris@148 154 poweroftwo = false;
Chris@148 155 break;
Chris@148 156 }
Chris@148 157 ratio >>= 1;
Chris@148 158 }
Chris@148 159 if (!poweroftwo) continue;
Chris@148 160
Chris@148 161 int distance = 0;
Chris@148 162
Chris@148 163 if (server->getPolar() != polar) distance += 1;
Chris@148 164
Chris@148 165 distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15;
Chris@148 166 distance += ((server->getFFTSize() / fftSize) - 1) * 10;
Chris@148 167
Chris@148 168 if (server->getFillCompletion() < 50) distance += 100;
Chris@148 169
Chris@148 170 #ifdef DEBUG_FFT_SERVER
Chris@148 171 std::cerr << "Distance " << distance << ", best is " << bestdist << std::endl;
Chris@148 172 #endif
Chris@148 173
Chris@148 174 if (bestdist == -1 || distance < bestdist) {
Chris@148 175 bestdist = distance;
Chris@148 176 best = i;
Chris@148 177 }
Chris@148 178 }
Chris@148 179 }
Chris@148 180
Chris@148 181 if (bestdist >= 0) {
Chris@148 182 ++best->second.second;
Chris@148 183 return best->second.first;
Chris@148 184 }
Chris@148 185 }
Chris@148 186
Chris@148 187 // Nothing found, make a new one
Chris@148 188
Chris@148 189 return getInstance(model,
Chris@148 190 channel,
Chris@148 191 windowType,
Chris@148 192 windowSize,
Chris@148 193 windowIncrement,
Chris@148 194 fftSize,
Chris@148 195 polar,
Chris@148 196 fillFromColumn);
Chris@148 197 }
Chris@148 198
Chris@148 199 FFTDataServer *
Chris@148 200 FFTDataServer::findServer(QString n)
Chris@148 201 {
Chris@148 202 if (m_servers.find(n) != m_servers.end()) {
Chris@148 203 ++m_servers[n].second;
Chris@148 204 return m_servers[n].first;
Chris@148 205 }
Chris@148 206
Chris@148 207 return 0;
Chris@148 208 }
Chris@148 209
Chris@148 210 void
Chris@148 211 FFTDataServer::releaseInstance(FFTDataServer *server)
Chris@148 212 {
Chris@148 213 #ifdef DEBUG_FFT_SERVER
Chris@148 214 std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl;
Chris@148 215 #endif
Chris@148 216
Chris@148 217 QMutexLocker locker(&m_serverMapMutex);
Chris@148 218
Chris@148 219 //!!! not a good strategy. Want something like:
Chris@148 220
Chris@148 221 // -- if ref count > 0, decrement and return
Chris@148 222 // -- if the instance hasn't been used at all, delete it immediately
Chris@148 223 // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts,
Chris@148 224 // leave them hanging around
Chris@148 225 // -- if N instances with zero refcounts remain, delete the one that
Chris@148 226 // was last released first
Chris@148 227 // -- if we run out of disk space when allocating an instance, go back
Chris@148 228 // and delete the spare N instances before trying again
Chris@148 229 // -- have an additional method to indicate that a model has been
Chris@148 230 // destroyed, so that we can delete all of its fft server instances
Chris@148 231
Chris@148 232 // also:
Chris@148 233 //
Chris@148 234
Chris@148 235 for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
Chris@148 236 if (i->second.first == server) {
Chris@148 237 if (i->second.second == 0) {
Chris@148 238 std::cerr << "ERROR: FFTDataServer::releaseInstance("
Chris@148 239 << server << "): instance not allocated" << std::endl;
Chris@148 240 } else if (--i->second.second == 0) {
Chris@148 241 if (server->m_lastUsedCache == -1) { // never used
Chris@148 242 delete server;
Chris@148 243 m_servers.erase(i);
Chris@148 244 } else {
Chris@148 245 server->suspend();
Chris@148 246 purgeLimbo();
Chris@148 247 }
Chris@148 248 }
Chris@148 249 return;
Chris@148 250 }
Chris@148 251 }
Chris@148 252
Chris@148 253 std::cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
Chris@148 254 << "instance not found" << std::endl;
Chris@148 255 }
Chris@148 256
Chris@148 257 void
Chris@148 258 FFTDataServer::purgeLimbo(int maxSize)
Chris@148 259 {
Chris@148 260 ServerMap::iterator i = m_servers.end();
Chris@148 261
Chris@148 262 int count = 0;
Chris@148 263
Chris@148 264 while (i != m_servers.begin()) {
Chris@148 265 --i;
Chris@148 266 if (i->second.second == 0) {
Chris@148 267 if (++count > maxSize) {
Chris@148 268 delete i->second.first;
Chris@148 269 m_servers.erase(i);
Chris@148 270 return;
Chris@148 271 }
Chris@148 272 }
Chris@148 273 }
Chris@148 274 }
Chris@148 275
Chris@148 276 FFTDataServer::FFTDataServer(QString fileBaseName,
Chris@148 277 const DenseTimeValueModel *model,
Chris@148 278 int channel,
Chris@148 279 WindowType windowType,
Chris@148 280 size_t windowSize,
Chris@148 281 size_t windowIncrement,
Chris@148 282 size_t fftSize,
Chris@148 283 bool polar,
Chris@148 284 size_t fillFromColumn) :
Chris@148 285 m_fileBaseName(fileBaseName),
Chris@148 286 m_model(model),
Chris@148 287 m_channel(channel),
Chris@148 288 m_windower(windowType, windowSize),
Chris@148 289 m_windowSize(windowSize),
Chris@148 290 m_windowIncrement(windowIncrement),
Chris@148 291 m_fftSize(fftSize),
Chris@148 292 m_polar(polar),
Chris@148 293 m_lastUsedCache(-1),
Chris@148 294 m_fftInput(0),
Chris@148 295 m_exiting(false),
Chris@148 296 m_fillThread(0)
Chris@148 297 {
Chris@148 298 size_t start = m_model->getStartFrame();
Chris@148 299 size_t end = m_model->getEndFrame();
Chris@148 300
Chris@148 301 m_width = (end - start) / m_windowIncrement + 1;
Chris@148 302 m_height = m_fftSize / 2;
Chris@148 303
Chris@148 304 size_t maxCacheSize = 20 * 1024 * 1024;
Chris@148 305 size_t columnSize = m_height * sizeof(fftsample) * 2 + sizeof(fftsample);
Chris@148 306 if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width;
Chris@148 307 else m_cacheWidth = maxCacheSize / columnSize;
Chris@148 308
Chris@148 309 int bits = 0;
Chris@148 310 while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; }
Chris@148 311 m_cacheWidth = 2;
Chris@148 312 while (bits) { m_cacheWidth <<= 1; --bits; }
Chris@148 313
Chris@148 314 #ifdef DEBUG_FFT_SERVER
Chris@148 315 std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl;
Chris@148 316 #endif
Chris@148 317
Chris@148 318 for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) {
Chris@148 319 m_caches.push_back(0);
Chris@148 320 }
Chris@148 321
Chris@148 322 m_fftInput = (fftsample *)
Chris@148 323 fftwf_malloc(fftSize * sizeof(fftsample));
Chris@148 324
Chris@148 325 m_fftOutput = (fftwf_complex *)
Chris@148 326 fftwf_malloc(fftSize * sizeof(fftwf_complex));
Chris@148 327
Chris@148 328 m_workbuffer = (float *)
Chris@148 329 fftwf_malloc(fftSize * sizeof(float));
Chris@148 330
Chris@148 331 m_fftPlan = fftwf_plan_dft_r2c_1d(m_fftSize,
Chris@148 332 m_fftInput,
Chris@148 333 m_fftOutput,
Chris@148 334 FFTW_ESTIMATE);
Chris@148 335
Chris@148 336 if (!m_fftPlan) {
Chris@148 337 std::cerr << "ERROR: fftwf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << std::endl;
Chris@148 338 throw(0);
Chris@148 339 }
Chris@148 340
Chris@148 341 m_fillThread = new FillThread(*this, fillFromColumn);
Chris@148 342
Chris@148 343 //!!! respond appropriately when thread exits (deleteProcessingData etc)
Chris@148 344 }
Chris@148 345
Chris@148 346 FFTDataServer::~FFTDataServer()
Chris@148 347 {
Chris@148 348 #ifdef DEBUG_FFT_SERVER
Chris@148 349 std::cerr << "FFTDataServer(" << this << ")::~FFTDataServer()" << std::endl;
Chris@148 350 #endif
Chris@148 351
Chris@148 352 m_exiting = true;
Chris@148 353 m_condition.wakeAll();
Chris@148 354 if (m_fillThread) {
Chris@148 355 m_fillThread->wait();
Chris@148 356 delete m_fillThread;
Chris@148 357 }
Chris@148 358
Chris@148 359 QMutexLocker locker(&m_writeMutex);
Chris@148 360
Chris@148 361 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 362 delete *i;
Chris@148 363 }
Chris@148 364
Chris@148 365 deleteProcessingData();
Chris@148 366 }
Chris@148 367
Chris@148 368 void
Chris@148 369 FFTDataServer::deleteProcessingData()
Chris@148 370 {
Chris@148 371 if (m_fftInput) {
Chris@148 372 fftwf_destroy_plan(m_fftPlan);
Chris@148 373 fftwf_free(m_fftInput);
Chris@148 374 fftwf_free(m_fftOutput);
Chris@148 375 fftwf_free(m_workbuffer);
Chris@148 376 }
Chris@148 377 m_fftInput = 0;
Chris@148 378 }
Chris@148 379
Chris@148 380 void
Chris@148 381 FFTDataServer::suspend()
Chris@148 382 {
Chris@148 383 #ifdef DEBUG_FFT_SERVER
Chris@148 384 std::cerr << "FFTDataServer(" << this << "): suspend" << std::endl;
Chris@148 385 #endif
Chris@148 386 QMutexLocker locker(&m_writeMutex);
Chris@148 387 m_suspended = true;
Chris@148 388 for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
Chris@148 389 if (*i) (*i)->suspend();
Chris@148 390 }
Chris@148 391 }
Chris@148 392
Chris@148 393 void
Chris@148 394 FFTDataServer::resume()
Chris@148 395 {
Chris@148 396 m_suspended = false;
Chris@148 397 m_condition.wakeAll();
Chris@148 398 }
Chris@148 399
Chris@148 400 FFTCache *
Chris@148 401 FFTDataServer::getCacheAux(size_t c)
Chris@148 402 {
Chris@148 403 QMutexLocker locker(&m_writeMutex);
Chris@148 404
Chris@148 405 if (m_lastUsedCache == -1) {
Chris@148 406 m_fillThread->start();
Chris@148 407 }
Chris@148 408
Chris@148 409 if (int(c) != m_lastUsedCache) {
Chris@148 410
Chris@148 411 // std::cerr << "switch from " << m_lastUsedCache << " to " << c << std::endl;
Chris@148 412
Chris@148 413 for (IntQueue::iterator i = m_dormantCaches.begin();
Chris@148 414 i != m_dormantCaches.end(); ++i) {
Chris@148 415 if (*i == c) {
Chris@148 416 m_dormantCaches.erase(i);
Chris@148 417 break;
Chris@148 418 }
Chris@148 419 }
Chris@148 420
Chris@148 421 if (m_lastUsedCache >= 0) {
Chris@148 422 bool inDormant = false;
Chris@148 423 for (size_t i = 0; i < m_dormantCaches.size(); ++i) {
Chris@148 424 if (m_dormantCaches[i] == m_lastUsedCache) {
Chris@148 425 inDormant = true;
Chris@148 426 break;
Chris@148 427 }
Chris@148 428 }
Chris@148 429 if (!inDormant) {
Chris@148 430 m_dormantCaches.push_back(m_lastUsedCache);
Chris@148 431 }
Chris@148 432 while (m_dormantCaches.size() > 4) {
Chris@148 433 int dc = m_dormantCaches.front();
Chris@148 434 m_dormantCaches.pop_front();
Chris@148 435 m_caches[dc]->suspend();
Chris@148 436 }
Chris@148 437 }
Chris@148 438 }
Chris@148 439
Chris@148 440 if (m_caches[c]) {
Chris@148 441 m_lastUsedCache = c;
Chris@148 442 return m_caches[c];
Chris@148 443 }
Chris@148 444
Chris@148 445 QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
Chris@148 446
Chris@148 447 FFTCache *cache = new FFTFileCache(name, MatrixFile::ReadWrite,
Chris@148 448 m_polar ? FFTFileCache::Polar :
Chris@148 449 FFTFileCache::Rectangular);
Chris@148 450
Chris@148 451 size_t width = m_cacheWidth;
Chris@148 452 if (c * m_cacheWidth + width > m_width) {
Chris@148 453 width = m_width - c * m_cacheWidth;
Chris@148 454 }
Chris@148 455
Chris@148 456 cache->resize(width, m_height);
Chris@148 457 cache->reset();
Chris@148 458
Chris@148 459 m_caches[c] = cache;
Chris@148 460 m_lastUsedCache = c;
Chris@148 461
Chris@148 462 return cache;
Chris@148 463 }
Chris@148 464
Chris@148 465 float
Chris@148 466 FFTDataServer::getMagnitudeAt(size_t x, size_t y)
Chris@148 467 {
Chris@148 468 size_t col;
Chris@148 469 FFTCache *cache = getCache(x, col);
Chris@148 470
Chris@148 471 if (!cache->haveSetColumnAt(col)) {
Chris@148 472 fillColumn(x);
Chris@148 473 }
Chris@148 474 return cache->getMagnitudeAt(col, y);
Chris@148 475 }
Chris@148 476
Chris@148 477 float
Chris@148 478 FFTDataServer::getNormalizedMagnitudeAt(size_t x, size_t y)
Chris@148 479 {
Chris@148 480 size_t col;
Chris@148 481 FFTCache *cache = getCache(x, col);
Chris@148 482
Chris@148 483 if (!cache->haveSetColumnAt(col)) {
Chris@148 484 fillColumn(x);
Chris@148 485 }
Chris@148 486 return cache->getNormalizedMagnitudeAt(col, y);
Chris@148 487 }
Chris@148 488
Chris@148 489 float
Chris@148 490 FFTDataServer::getMaximumMagnitudeAt(size_t x)
Chris@148 491 {
Chris@148 492 size_t col;
Chris@148 493 FFTCache *cache = getCache(x, col);
Chris@148 494
Chris@148 495 if (!cache->haveSetColumnAt(col)) {
Chris@148 496 fillColumn(x);
Chris@148 497 }
Chris@148 498 return cache->getMaximumMagnitudeAt(col);
Chris@148 499 }
Chris@148 500
Chris@148 501 float
Chris@148 502 FFTDataServer::getPhaseAt(size_t x, size_t y)
Chris@148 503 {
Chris@148 504 size_t col;
Chris@148 505 FFTCache *cache = getCache(x, col);
Chris@148 506
Chris@148 507 if (!cache->haveSetColumnAt(col)) {
Chris@148 508 fillColumn(x);
Chris@148 509 }
Chris@148 510 return cache->getPhaseAt(col, y);
Chris@148 511 }
Chris@148 512
Chris@148 513 void
Chris@148 514 FFTDataServer::getValuesAt(size_t x, size_t y, float &real, float &imaginary)
Chris@148 515 {
Chris@148 516 size_t col;
Chris@148 517 FFTCache *cache = getCache(x, col);
Chris@148 518
Chris@148 519 if (!cache->haveSetColumnAt(col)) {
Chris@148 520 #ifdef DEBUG_FFT_SERVER
Chris@148 521 std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
Chris@148 522 #endif
Chris@148 523 fillColumn(x);
Chris@148 524 }
Chris@148 525 float magnitude = cache->getMagnitudeAt(col, y);
Chris@148 526 float phase = cache->getPhaseAt(col, y);
Chris@148 527 real = magnitude * cosf(phase);
Chris@148 528 imaginary = magnitude * sinf(phase);
Chris@148 529 }
Chris@148 530
Chris@148 531 bool
Chris@148 532 FFTDataServer::isColumnReady(size_t x)
Chris@148 533 {
Chris@148 534 if (!haveCache(x)) {
Chris@148 535 if (m_lastUsedCache == -1) {
Chris@148 536 m_fillThread->start();
Chris@148 537 }
Chris@148 538 return false;
Chris@148 539 }
Chris@148 540
Chris@148 541 size_t col;
Chris@148 542 FFTCache *cache = getCache(x, col);
Chris@148 543
Chris@148 544 return cache->haveSetColumnAt(col);
Chris@148 545 }
Chris@148 546
Chris@148 547 void
Chris@148 548 FFTDataServer::fillColumn(size_t x)
Chris@148 549 {
Chris@148 550 size_t col;
Chris@148 551 #ifdef DEBUG_FFT_SERVER_FILL
Chris@148 552 std::cout << "FFTDataServer::fillColumn(" << x << ")" << std::endl;
Chris@148 553 #endif
Chris@148 554 FFTCache *cache = getCache(x, col);
Chris@148 555
Chris@148 556 QMutexLocker locker(&m_writeMutex);
Chris@148 557
Chris@148 558 if (cache->haveSetColumnAt(col)) return;
Chris@148 559
Chris@148 560 int startFrame = m_windowIncrement * x;
Chris@148 561 int endFrame = startFrame + m_windowSize;
Chris@148 562
Chris@148 563 startFrame -= int(m_windowSize - m_windowIncrement) / 2;
Chris@148 564 endFrame -= int(m_windowSize - m_windowIncrement) / 2;
Chris@148 565 size_t pfx = 0;
Chris@148 566
Chris@148 567 size_t off = (m_fftSize - m_windowSize) / 2;
Chris@148 568
Chris@148 569 for (size_t i = 0; i < off; ++i) {
Chris@148 570 m_fftInput[i] = 0.0;
Chris@148 571 m_fftInput[m_fftSize - i - 1] = 0.0;
Chris@148 572 }
Chris@148 573
Chris@148 574 if (startFrame < 0) {
Chris@148 575 pfx = size_t(-startFrame);
Chris@148 576 for (size_t i = 0; i < pfx; ++i) {
Chris@148 577 m_fftInput[off + i] = 0.0;
Chris@148 578 }
Chris@148 579 }
Chris@148 580
Chris@148 581 size_t got = m_model->getValues(m_channel, startFrame + pfx,
Chris@148 582 endFrame, m_fftInput + off + pfx);
Chris@148 583
Chris@148 584 while (got + pfx < m_windowSize) {
Chris@148 585 m_fftInput[off + got + pfx] = 0.0;
Chris@148 586 ++got;
Chris@148 587 }
Chris@148 588
Chris@148 589 if (m_channel == -1) {
Chris@148 590 int channels = m_model->getChannelCount();
Chris@148 591 if (channels > 1) {
Chris@148 592 for (size_t i = 0; i < m_windowSize; ++i) {
Chris@148 593 m_fftInput[off + i] /= channels;
Chris@148 594 }
Chris@148 595 }
Chris@148 596 }
Chris@148 597
Chris@148 598 m_windower.cut(m_fftInput + off);
Chris@148 599
Chris@148 600 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 601 fftsample temp = m_fftInput[i];
Chris@148 602 m_fftInput[i] = m_fftInput[i + m_fftSize/2];
Chris@148 603 m_fftInput[i + m_fftSize/2] = temp;
Chris@148 604 }
Chris@148 605
Chris@148 606 fftwf_execute(m_fftPlan);
Chris@148 607
Chris@148 608 fftsample factor = 0.0;
Chris@148 609
Chris@148 610 for (size_t i = 0; i < m_fftSize/2; ++i) {
Chris@148 611
Chris@148 612 fftsample mag = sqrtf(m_fftOutput[i][0] * m_fftOutput[i][0] +
Chris@148 613 m_fftOutput[i][1] * m_fftOutput[i][1]);
Chris@148 614 mag /= m_windowSize / 2;
Chris@148 615
Chris@148 616 if (mag > factor) factor = mag;
Chris@148 617
Chris@148 618 fftsample phase = atan2f(m_fftOutput[i][1], m_fftOutput[i][0]);
Chris@148 619 phase = princargf(phase);
Chris@148 620
Chris@148 621 m_workbuffer[i] = mag;
Chris@148 622 m_workbuffer[i + m_fftSize/2] = phase;
Chris@148 623 }
Chris@148 624
Chris@148 625 cache->setColumnAt(col,
Chris@148 626 m_workbuffer,
Chris@148 627 m_workbuffer + m_fftSize/2,
Chris@148 628 factor);
Chris@148 629 }
Chris@148 630
Chris@148 631 size_t
Chris@148 632 FFTDataServer::getFillCompletion() const
Chris@148 633 {
Chris@148 634 if (m_fillThread) return m_fillThread->getCompletion();
Chris@148 635 else return 100;
Chris@148 636 }
Chris@148 637
Chris@148 638 size_t
Chris@148 639 FFTDataServer::getFillExtent() const
Chris@148 640 {
Chris@148 641 if (m_fillThread) return m_fillThread->getExtent();
Chris@148 642 else return m_model->getEndFrame();
Chris@148 643 }
Chris@148 644
Chris@148 645 QString
Chris@148 646 FFTDataServer::generateFileBasename() const
Chris@148 647 {
Chris@148 648 return generateFileBasename(m_model, m_channel, m_windower.getType(),
Chris@148 649 m_windowSize, m_windowIncrement, m_fftSize,
Chris@148 650 m_polar);
Chris@148 651 }
Chris@148 652
Chris@148 653 QString
Chris@148 654 FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
Chris@148 655 int channel,
Chris@148 656 WindowType windowType,
Chris@148 657 size_t windowSize,
Chris@148 658 size_t windowIncrement,
Chris@148 659 size_t fftSize,
Chris@148 660 bool polar)
Chris@148 661 {
Chris@148 662 char buffer[200];
Chris@148 663
Chris@148 664 sprintf(buffer, "%u-%u-%u-%u-%u-%u%s",
Chris@148 665 (unsigned int)XmlExportable::getObjectExportId(model),
Chris@148 666 (unsigned int)(channel + 1),
Chris@148 667 (unsigned int)windowType,
Chris@148 668 (unsigned int)windowSize,
Chris@148 669 (unsigned int)windowIncrement,
Chris@148 670 (unsigned int)fftSize,
Chris@148 671 polar ? "-p" : "-r");
Chris@148 672
Chris@148 673 return buffer;
Chris@148 674 }
Chris@148 675
Chris@148 676 void
Chris@148 677 FFTDataServer::FillThread::run()
Chris@148 678 {
Chris@148 679 m_extent = 0;
Chris@148 680 m_completion = 0;
Chris@148 681
Chris@148 682 size_t start = m_server.m_model->getStartFrame();
Chris@148 683 size_t end = m_server.m_model->getEndFrame();
Chris@148 684 size_t remainingEnd = end;
Chris@148 685
Chris@148 686 int counter = 0;
Chris@148 687 int updateAt = (end / m_server.m_windowIncrement) / 20;
Chris@148 688 if (updateAt < 100) updateAt = 100;
Chris@148 689
Chris@148 690 if (m_fillFrom > start) {
Chris@148 691
Chris@148 692 for (size_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
Chris@148 693
Chris@148 694 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 695
Chris@148 696 if (m_server.m_exiting) return;
Chris@148 697
Chris@148 698 while (m_server.m_suspended) {
Chris@148 699 #ifdef DEBUG_FFT_SERVER
Chris@148 700 std::cerr << "FFTDataServer(" << this << "): suspended, waiting..." << std::endl;
Chris@148 701 #endif
Chris@148 702 m_server.m_writeMutex.lock();
Chris@148 703 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@148 704 m_server.m_writeMutex.unlock();
Chris@148 705 if (m_server.m_exiting) return;
Chris@148 706 }
Chris@148 707
Chris@148 708 if (++counter == updateAt) {
Chris@148 709 m_extent = f;
Chris@148 710 m_completion = size_t(100 * fabsf(float(f - m_fillFrom) /
Chris@148 711 float(end - start)));
Chris@148 712 counter = 0;
Chris@148 713 }
Chris@148 714 }
Chris@148 715
Chris@148 716 remainingEnd = m_fillFrom;
Chris@148 717 if (remainingEnd > start) --remainingEnd;
Chris@148 718 else remainingEnd = start;
Chris@148 719 }
Chris@148 720
Chris@148 721 size_t baseCompletion = m_completion;
Chris@148 722
Chris@148 723 for (size_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
Chris@148 724
Chris@148 725 m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
Chris@148 726
Chris@148 727 if (m_server.m_exiting) return;
Chris@148 728
Chris@148 729 while (m_server.m_suspended) {
Chris@148 730 #ifdef DEBUG_FFT_SERVER
Chris@148 731 std::cerr << "FFTDataServer(" << this << "): suspended, waiting..." << std::endl;
Chris@148 732 #endif
Chris@148 733 m_server.m_writeMutex.lock();
Chris@148 734 m_server.m_condition.wait(&m_server.m_writeMutex, 10000);
Chris@148 735 m_server.m_writeMutex.unlock();
Chris@148 736 if (m_server.m_exiting) return;
Chris@148 737 }
Chris@148 738
Chris@148 739 if (++counter == updateAt) {
Chris@148 740 m_extent = f;
Chris@148 741 m_completion = baseCompletion +
Chris@148 742 size_t(100 * fabsf(float(f - start) /
Chris@148 743 float(end - start)));
Chris@148 744 counter = 0;
Chris@148 745 }
Chris@148 746 }
Chris@148 747
Chris@148 748 m_completion = 100;
Chris@148 749 m_extent = end;
Chris@148 750 }
Chris@148 751