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