annotate data/fft/FFTDataServer.cpp @ 282:d9319859a4cf tip

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