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