Mercurial > hg > svcore
comparison data/model/WaveFileModel.cpp @ 1126:39019ce29178 tony-2.0-integration
Merge through to branch for Tony 2.0
author | Chris Cannam |
---|---|
date | Thu, 20 Aug 2015 14:54:21 +0100 |
parents | efea94b04d5a |
children |
comparison
equal
deleted
inserted
replaced
1119:e22bfe8ca248 | 1126:39019ce29178 |
---|---|
13 COPYING included with this distribution for more information. | 13 COPYING included with this distribution for more information. |
14 */ | 14 */ |
15 | 15 |
16 #include "WaveFileModel.h" | 16 #include "WaveFileModel.h" |
17 | 17 |
18 #include "fileio/AudioFileReader.h" | 18 WaveFileModel::~WaveFileModel() |
19 #include "fileio/AudioFileReaderFactory.h" | |
20 | |
21 #include "system/System.h" | |
22 | |
23 #include "base/Preferences.h" | |
24 | |
25 #include <QFileInfo> | |
26 #include <QTextStream> | |
27 | |
28 #include <iostream> | |
29 #include <unistd.h> | |
30 #include <cmath> | |
31 #include <sndfile.h> | |
32 | |
33 #include <cassert> | |
34 | |
35 //#define DEBUG_WAVE_FILE_MODEL 1 | |
36 | |
37 PowerOfSqrtTwoZoomConstraint | |
38 WaveFileModel::m_zoomConstraint; | |
39 | |
40 WaveFileModel::WaveFileModel(FileSource source, sv_samplerate_t targetRate) : | |
41 m_source(source), | |
42 m_path(source.getLocation()), | |
43 m_reader(0), | |
44 m_myReader(true), | |
45 m_startFrame(0), | |
46 m_fillThread(0), | |
47 m_updateTimer(0), | |
48 m_lastFillExtent(0), | |
49 m_exiting(false), | |
50 m_lastDirectReadStart(0), | |
51 m_lastDirectReadCount(0) | |
52 { | 19 { |
53 m_source.waitForData(); | |
54 if (m_source.isOK()) { | |
55 bool normalise = Preferences::getInstance()->getNormaliseAudio(); | |
56 m_reader = AudioFileReaderFactory::createThreadingReader | |
57 (m_source, targetRate, normalise); | |
58 if (m_reader) { | |
59 SVDEBUG << "WaveFileModel::WaveFileModel: reader rate: " | |
60 << m_reader->getSampleRate() << endl; | |
61 } | |
62 } | |
63 if (m_reader) setObjectName(m_reader->getTitle()); | |
64 if (objectName() == "") setObjectName(QFileInfo(m_path).fileName()); | |
65 if (isOK()) fillCache(); | |
66 } | 20 } |
67 | 21 |
68 WaveFileModel::WaveFileModel(FileSource source, AudioFileReader *reader) : | |
69 m_source(source), | |
70 m_path(source.getLocation()), | |
71 m_reader(0), | |
72 m_myReader(false), | |
73 m_startFrame(0), | |
74 m_fillThread(0), | |
75 m_updateTimer(0), | |
76 m_lastFillExtent(0), | |
77 m_exiting(false) | |
78 { | |
79 m_reader = reader; | |
80 if (m_reader) setObjectName(m_reader->getTitle()); | |
81 if (objectName() == "") setObjectName(QFileInfo(m_path).fileName()); | |
82 fillCache(); | |
83 } | |
84 | |
85 WaveFileModel::~WaveFileModel() | |
86 { | |
87 m_exiting = true; | |
88 if (m_fillThread) m_fillThread->wait(); | |
89 if (m_myReader) delete m_reader; | |
90 m_reader = 0; | |
91 } | |
92 | |
93 bool | |
94 WaveFileModel::isOK() const | |
95 { | |
96 return m_reader && m_reader->isOK(); | |
97 } | |
98 | |
99 bool | |
100 WaveFileModel::isReady(int *completion) const | |
101 { | |
102 bool ready = (isOK() && (m_fillThread == 0)); | |
103 double c = double(m_lastFillExtent) / double(getEndFrame() - getStartFrame()); | |
104 static int prevCompletion = 0; | |
105 if (completion) { | |
106 *completion = int(c * 100.0 + 0.01); | |
107 if (m_reader) { | |
108 int decodeCompletion = m_reader->getDecodeCompletion(); | |
109 if (decodeCompletion < 90) *completion = decodeCompletion; | |
110 else *completion = std::min(*completion, decodeCompletion); | |
111 } | |
112 if (*completion != 0 && | |
113 *completion != 100 && | |
114 prevCompletion != 0 && | |
115 prevCompletion > *completion) { | |
116 // just to avoid completion going backwards | |
117 *completion = prevCompletion; | |
118 } | |
119 prevCompletion = *completion; | |
120 } | |
121 #ifdef DEBUG_WAVE_FILE_MODEL | |
122 SVDEBUG << "WaveFileModel::isReady(): ready = " << ready << ", completion = " << (completion ? *completion : -1) << endl; | |
123 #endif | |
124 return ready; | |
125 } | |
126 | |
127 sv_frame_t | |
128 WaveFileModel::getFrameCount() const | |
129 { | |
130 if (!m_reader) return 0; | |
131 return m_reader->getFrameCount(); | |
132 } | |
133 | |
134 int | |
135 WaveFileModel::getChannelCount() const | |
136 { | |
137 if (!m_reader) return 0; | |
138 return m_reader->getChannelCount(); | |
139 } | |
140 | |
141 sv_samplerate_t | |
142 WaveFileModel::getSampleRate() const | |
143 { | |
144 if (!m_reader) return 0; | |
145 return m_reader->getSampleRate(); | |
146 } | |
147 | |
148 sv_samplerate_t | |
149 WaveFileModel::getNativeRate() const | |
150 { | |
151 if (!m_reader) return 0; | |
152 sv_samplerate_t rate = m_reader->getNativeRate(); | |
153 if (rate == 0) rate = getSampleRate(); | |
154 return rate; | |
155 } | |
156 | |
157 QString | |
158 WaveFileModel::getTitle() const | |
159 { | |
160 QString title; | |
161 if (m_reader) title = m_reader->getTitle(); | |
162 if (title == "") title = objectName(); | |
163 return title; | |
164 } | |
165 | |
166 QString | |
167 WaveFileModel::getMaker() const | |
168 { | |
169 if (m_reader) return m_reader->getMaker(); | |
170 return ""; | |
171 } | |
172 | |
173 QString | |
174 WaveFileModel::getLocation() const | |
175 { | |
176 if (m_reader) return m_reader->getLocation(); | |
177 return ""; | |
178 } | |
179 | |
180 QString | |
181 WaveFileModel::getLocalFilename() const | |
182 { | |
183 if (m_reader) return m_reader->getLocalFilename(); | |
184 return ""; | |
185 } | |
186 | |
187 sv_frame_t | |
188 WaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count, | |
189 float *buffer) const | |
190 { | |
191 // Always read these directly from the file. | |
192 // This is used for e.g. audio playback. | |
193 // Could be much more efficient (although compiler optimisation will help) | |
194 | |
195 #ifdef DEBUG_WAVE_FILE_MODEL | |
196 cout << "WaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << ", " << buffer << endl; | |
197 #endif | |
198 | |
199 if (start >= m_startFrame) { | |
200 start -= m_startFrame; | |
201 } else { | |
202 for (sv_frame_t i = 0; i < count; ++i) { | |
203 buffer[i] = 0.f; | |
204 } | |
205 if (count <= m_startFrame - start) { | |
206 return 0; | |
207 } else { | |
208 count -= (m_startFrame - start); | |
209 start = 0; | |
210 } | |
211 } | |
212 | |
213 if (!m_reader || !m_reader->isOK() || count == 0) { | |
214 for (sv_frame_t i = 0; i < count; ++i) buffer[i] = 0.f; | |
215 return 0; | |
216 } | |
217 | |
218 #ifdef DEBUG_WAVE_FILE_MODEL | |
219 // SVDEBUG << "WaveFileModel::getValues(" << channel << ", " | |
220 // << start << ", " << end << "): calling reader" << endl; | |
221 #endif | |
222 | |
223 int channels = getChannelCount(); | |
224 | |
225 SampleBlock frames = m_reader->getInterleavedFrames(start, count); | |
226 | |
227 sv_frame_t i = 0; | |
228 | |
229 int ch0 = channel, ch1 = channel; | |
230 if (channel == -1) { | |
231 ch0 = 0; | |
232 ch1 = channels - 1; | |
233 } | |
234 | |
235 while (i < count) { | |
236 | |
237 buffer[i] = 0.0; | |
238 | |
239 for (int ch = ch0; ch <= ch1; ++ch) { | |
240 | |
241 sv_frame_t index = i * channels + ch; | |
242 if (index >= (sv_frame_t)frames.size()) break; | |
243 | |
244 float sample = frames[index]; | |
245 buffer[i] += sample; | |
246 } | |
247 | |
248 ++i; | |
249 } | |
250 | |
251 return i; | |
252 } | |
253 | |
254 sv_frame_t | |
255 WaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count, | |
256 double *buffer) const | |
257 { | |
258 #ifdef DEBUG_WAVE_FILE_MODEL | |
259 cout << "WaveFileModel::getData(double)[" << this << "]: " << channel << ", " << start << ", " << count << ", " << buffer << endl; | |
260 #endif | |
261 | |
262 if (start > m_startFrame) { | |
263 start -= m_startFrame; | |
264 } else { | |
265 for (sv_frame_t i = 0; i < count; ++i) buffer[i] = 0.0; | |
266 if (count <= m_startFrame - start) { | |
267 return 0; | |
268 } else { | |
269 count -= (m_startFrame - start); | |
270 start = 0; | |
271 } | |
272 } | |
273 | |
274 if (!m_reader || !m_reader->isOK() || count == 0) { | |
275 for (sv_frame_t i = 0; i < count; ++i) buffer[i] = 0.0; | |
276 return 0; | |
277 } | |
278 | |
279 int channels = getChannelCount(); | |
280 | |
281 SampleBlock frames = m_reader->getInterleavedFrames(start, count); | |
282 | |
283 sv_frame_t i = 0; | |
284 | |
285 int ch0 = channel, ch1 = channel; | |
286 if (channel == -1) { | |
287 ch0 = 0; | |
288 ch1 = channels - 1; | |
289 } | |
290 | |
291 while (i < count) { | |
292 | |
293 buffer[i] = 0.0; | |
294 | |
295 for (int ch = ch0; ch <= ch1; ++ch) { | |
296 | |
297 sv_frame_t index = i * channels + ch; | |
298 if (index >= (sv_frame_t)frames.size()) break; | |
299 | |
300 float sample = frames[index]; | |
301 buffer[i] += sample; | |
302 } | |
303 | |
304 ++i; | |
305 } | |
306 | |
307 return i; | |
308 } | |
309 | |
310 sv_frame_t | |
311 WaveFileModel::getData(int fromchannel, int tochannel, | |
312 sv_frame_t start, sv_frame_t count, | |
313 float **buffer) const | |
314 { | |
315 #ifdef DEBUG_WAVE_FILE_MODEL | |
316 cout << "WaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << ", " << buffer << endl; | |
317 #endif | |
318 | |
319 int channels = getChannelCount(); | |
320 | |
321 if (fromchannel > tochannel) { | |
322 cerr << "ERROR: WaveFileModel::getData: fromchannel (" | |
323 << fromchannel << ") > tochannel (" << tochannel << ")" | |
324 << endl; | |
325 return 0; | |
326 } | |
327 | |
328 if (tochannel >= channels) { | |
329 cerr << "ERROR: WaveFileModel::getData: tochannel (" | |
330 << tochannel << ") >= channel count (" << channels << ")" | |
331 << endl; | |
332 return 0; | |
333 } | |
334 | |
335 if (fromchannel == tochannel) { | |
336 return getData(fromchannel, start, count, buffer[0]); | |
337 } | |
338 | |
339 int reqchannels = (tochannel - fromchannel) + 1; | |
340 | |
341 // Always read these directly from the file. | |
342 // This is used for e.g. audio playback. | |
343 // Could be much more efficient (although compiler optimisation will help) | |
344 | |
345 if (start >= m_startFrame) { | |
346 start -= m_startFrame; | |
347 } else { | |
348 for (int c = 0; c < reqchannels; ++c) { | |
349 for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f; | |
350 } | |
351 if (count <= m_startFrame - start) { | |
352 return 0; | |
353 } else { | |
354 count -= (m_startFrame - start); | |
355 start = 0; | |
356 } | |
357 } | |
358 | |
359 if (!m_reader || !m_reader->isOK() || count == 0) { | |
360 for (int c = 0; c < reqchannels; ++c) { | |
361 for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f; | |
362 } | |
363 return 0; | |
364 } | |
365 | |
366 SampleBlock frames = m_reader->getInterleavedFrames(start, count); | |
367 | |
368 sv_frame_t i = 0; | |
369 | |
370 sv_frame_t index = 0, available = frames.size(); | |
371 | |
372 while (i < count) { | |
373 | |
374 if (index >= available) break; | |
375 | |
376 int destc = 0; | |
377 | |
378 for (int c = 0; c < channels; ++c) { | |
379 | |
380 if (c >= fromchannel && c <= tochannel) { | |
381 buffer[destc][i] = frames[index]; | |
382 ++destc; | |
383 } | |
384 | |
385 ++index; | |
386 } | |
387 | |
388 ++i; | |
389 } | |
390 | |
391 return i; | |
392 } | |
393 | |
394 int | |
395 WaveFileModel::getSummaryBlockSize(int desired) const | |
396 { | |
397 int cacheType = 0; | |
398 int power = m_zoomConstraint.getMinCachePower(); | |
399 int roundedBlockSize = m_zoomConstraint.getNearestBlockSize | |
400 (desired, cacheType, power, ZoomConstraint::RoundDown); | |
401 if (cacheType != 0 && cacheType != 1) { | |
402 // We will be reading directly from file, so can satisfy any | |
403 // blocksize requirement | |
404 return desired; | |
405 } else { | |
406 return roundedBlockSize; | |
407 } | |
408 } | |
409 | |
410 void | |
411 WaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count, | |
412 RangeBlock &ranges, int &blockSize) const | |
413 { | |
414 ranges.clear(); | |
415 if (!isOK()) return; | |
416 ranges.reserve((count / blockSize) + 1); | |
417 | |
418 if (start > m_startFrame) start -= m_startFrame; | |
419 else if (count <= m_startFrame - start) return; | |
420 else { | |
421 count -= (m_startFrame - start); | |
422 start = 0; | |
423 } | |
424 | |
425 int cacheType = 0; | |
426 int power = m_zoomConstraint.getMinCachePower(); | |
427 int roundedBlockSize = m_zoomConstraint.getNearestBlockSize | |
428 (blockSize, cacheType, power, ZoomConstraint::RoundDown); | |
429 | |
430 int channels = getChannelCount(); | |
431 | |
432 if (cacheType != 0 && cacheType != 1) { | |
433 | |
434 // We need to read directly from the file. We haven't got | |
435 // this cached. Hope the requested area is small. This is | |
436 // not optimal -- we'll end up reading the same frames twice | |
437 // for stereo files, in two separate calls to this method. | |
438 // We could fairly trivially handle this for most cases that | |
439 // matter by putting a single cache in getInterleavedFrames | |
440 // for short queries. | |
441 | |
442 m_directReadMutex.lock(); | |
443 | |
444 if (m_lastDirectReadStart != start || | |
445 m_lastDirectReadCount != count || | |
446 m_directRead.empty()) { | |
447 | |
448 m_directRead = m_reader->getInterleavedFrames(start, count); | |
449 m_lastDirectReadStart = start; | |
450 m_lastDirectReadCount = count; | |
451 } | |
452 | |
453 float max = 0.0, min = 0.0, total = 0.0; | |
454 sv_frame_t i = 0, got = 0; | |
455 | |
456 while (i < count) { | |
457 | |
458 sv_frame_t index = i * channels + channel; | |
459 if (index >= (sv_frame_t)m_directRead.size()) break; | |
460 | |
461 float sample = m_directRead[index]; | |
462 if (sample > max || got == 0) max = sample; | |
463 if (sample < min || got == 0) min = sample; | |
464 total += fabsf(sample); | |
465 | |
466 ++i; | |
467 ++got; | |
468 | |
469 if (got == blockSize) { | |
470 ranges.push_back(Range(min, max, total / float(got))); | |
471 min = max = total = 0.0f; | |
472 got = 0; | |
473 } | |
474 } | |
475 | |
476 m_directReadMutex.unlock(); | |
477 | |
478 if (got > 0) { | |
479 ranges.push_back(Range(min, max, total / float(got))); | |
480 } | |
481 | |
482 return; | |
483 | |
484 } else { | |
485 | |
486 QMutexLocker locker(&m_mutex); | |
487 | |
488 const RangeBlock &cache = m_cache[cacheType]; | |
489 | |
490 blockSize = roundedBlockSize; | |
491 | |
492 sv_frame_t cacheBlock, div; | |
493 | |
494 if (cacheType == 0) { | |
495 cacheBlock = (1 << m_zoomConstraint.getMinCachePower()); | |
496 div = (1 << power) / cacheBlock; | |
497 } else { | |
498 cacheBlock = sv_frame_t((1 << m_zoomConstraint.getMinCachePower()) * sqrt(2.) + 0.01); | |
499 div = sv_frame_t(((1 << power) * sqrt(2.) + 0.01) / double(cacheBlock)); | |
500 } | |
501 | |
502 sv_frame_t startIndex = start / cacheBlock; | |
503 sv_frame_t endIndex = (start + count) / cacheBlock; | |
504 | |
505 float max = 0.0, min = 0.0, total = 0.0; | |
506 sv_frame_t i = 0, got = 0; | |
507 | |
508 #ifdef DEBUG_WAVE_FILE_MODEL | |
509 cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; | |
510 #endif | |
511 | |
512 for (i = 0; i <= endIndex - startIndex; ) { | |
513 | |
514 sv_frame_t index = (i + startIndex) * channels + channel; | |
515 if (index >= (sv_frame_t)cache.size()) break; | |
516 | |
517 const Range &range = cache[index]; | |
518 if (range.max() > max || got == 0) max = range.max(); | |
519 if (range.min() < min || got == 0) min = range.min(); | |
520 total += range.absmean(); | |
521 | |
522 ++i; | |
523 ++got; | |
524 | |
525 if (got == div) { | |
526 ranges.push_back(Range(min, max, total / float(got))); | |
527 min = max = total = 0.0f; | |
528 got = 0; | |
529 } | |
530 } | |
531 | |
532 if (got > 0) { | |
533 ranges.push_back(Range(min, max, total / float(got))); | |
534 } | |
535 } | |
536 | |
537 #ifdef DEBUG_WAVE_FILE_MODEL | |
538 SVDEBUG << "returning " << ranges.size() << " ranges" << endl; | |
539 #endif | |
540 return; | |
541 } | |
542 | |
543 WaveFileModel::Range | |
544 WaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t count) const | |
545 { | |
546 Range range; | |
547 if (!isOK()) return range; | |
548 | |
549 if (start > m_startFrame) start -= m_startFrame; | |
550 else if (count <= m_startFrame - start) return range; | |
551 else { | |
552 count -= (m_startFrame - start); | |
553 start = 0; | |
554 } | |
555 | |
556 int blockSize; | |
557 for (blockSize = 1; blockSize <= count; blockSize *= 2); | |
558 if (blockSize > 1) blockSize /= 2; | |
559 | |
560 bool first = false; | |
561 | |
562 sv_frame_t blockStart = (start / blockSize) * blockSize; | |
563 sv_frame_t blockEnd = ((start + count) / blockSize) * blockSize; | |
564 | |
565 if (blockStart < start) blockStart += blockSize; | |
566 | |
567 if (blockEnd > blockStart) { | |
568 RangeBlock ranges; | |
569 getSummaries(channel, blockStart, blockEnd - blockStart, ranges, blockSize); | |
570 for (int i = 0; i < (int)ranges.size(); ++i) { | |
571 if (first || ranges[i].min() < range.min()) range.setMin(ranges[i].min()); | |
572 if (first || ranges[i].max() > range.max()) range.setMax(ranges[i].max()); | |
573 if (first || ranges[i].absmean() < range.absmean()) range.setAbsmean(ranges[i].absmean()); | |
574 first = false; | |
575 } | |
576 } | |
577 | |
578 if (blockStart > start) { | |
579 Range startRange = getSummary(channel, start, blockStart - start); | |
580 range.setMin(std::min(range.min(), startRange.min())); | |
581 range.setMax(std::max(range.max(), startRange.max())); | |
582 range.setAbsmean(std::min(range.absmean(), startRange.absmean())); | |
583 } | |
584 | |
585 if (blockEnd < start + count) { | |
586 Range endRange = getSummary(channel, blockEnd, start + count - blockEnd); | |
587 range.setMin(std::min(range.min(), endRange.min())); | |
588 range.setMax(std::max(range.max(), endRange.max())); | |
589 range.setAbsmean(std::min(range.absmean(), endRange.absmean())); | |
590 } | |
591 | |
592 return range; | |
593 } | |
594 | |
595 void | |
596 WaveFileModel::fillCache() | |
597 { | |
598 m_mutex.lock(); | |
599 | |
600 m_updateTimer = new QTimer(this); | |
601 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut())); | |
602 m_updateTimer->start(100); | |
603 | |
604 m_fillThread = new RangeCacheFillThread(*this); | |
605 connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled())); | |
606 | |
607 m_mutex.unlock(); | |
608 m_fillThread->start(); | |
609 | |
610 #ifdef DEBUG_WAVE_FILE_MODEL | |
611 SVDEBUG << "WaveFileModel::fillCache: started fill thread" << endl; | |
612 #endif | |
613 } | |
614 | |
615 void | |
616 WaveFileModel::fillTimerTimedOut() | |
617 { | |
618 if (m_fillThread) { | |
619 sv_frame_t fillExtent = m_fillThread->getFillExtent(); | |
620 #ifdef DEBUG_WAVE_FILE_MODEL | |
621 SVDEBUG << "WaveFileModel::fillTimerTimedOut: extent = " << fillExtent << endl; | |
622 #endif | |
623 if (fillExtent > m_lastFillExtent) { | |
624 emit modelChangedWithin(m_lastFillExtent, fillExtent); | |
625 m_lastFillExtent = fillExtent; | |
626 } | |
627 } else { | |
628 #ifdef DEBUG_WAVE_FILE_MODEL | |
629 SVDEBUG << "WaveFileModel::fillTimerTimedOut: no thread" << endl; | |
630 #endif | |
631 emit modelChanged(); | |
632 } | |
633 } | |
634 | |
635 void | |
636 WaveFileModel::cacheFilled() | |
637 { | |
638 m_mutex.lock(); | |
639 delete m_fillThread; | |
640 m_fillThread = 0; | |
641 delete m_updateTimer; | |
642 m_updateTimer = 0; | |
643 m_mutex.unlock(); | |
644 if (getEndFrame() > m_lastFillExtent) { | |
645 emit modelChangedWithin(m_lastFillExtent, getEndFrame()); | |
646 } | |
647 emit modelChanged(); | |
648 emit ready(); | |
649 #ifdef DEBUG_WAVE_FILE_MODEL | |
650 SVDEBUG << "WaveFileModel::cacheFilled" << endl; | |
651 #endif | |
652 } | |
653 | |
654 void | |
655 WaveFileModel::RangeCacheFillThread::run() | |
656 { | |
657 int cacheBlockSize[2]; | |
658 cacheBlockSize[0] = (1 << m_model.m_zoomConstraint.getMinCachePower()); | |
659 cacheBlockSize[1] = (int((1 << m_model.m_zoomConstraint.getMinCachePower()) * | |
660 sqrt(2.) + 0.01)); | |
661 | |
662 sv_frame_t frame = 0; | |
663 const sv_frame_t readBlockSize = 16384; | |
664 SampleBlock block; | |
665 | |
666 if (!m_model.isOK()) return; | |
667 | |
668 int channels = m_model.getChannelCount(); | |
669 bool updating = m_model.m_reader->isUpdating(); | |
670 | |
671 if (updating) { | |
672 while (channels == 0 && !m_model.m_exiting) { | |
673 // SVDEBUG << "WaveFileModel::fill: Waiting for channels..." << endl; | |
674 sleep(1); | |
675 channels = m_model.getChannelCount(); | |
676 } | |
677 } | |
678 | |
679 Range *range = new Range[2 * channels]; | |
680 float *means = new float[2 * channels]; | |
681 int count[2]; | |
682 count[0] = count[1] = 0; | |
683 for (int i = 0; i < 2 * channels; ++i) { | |
684 means[i] = 0.f; | |
685 } | |
686 | |
687 bool first = true; | |
688 | |
689 while (first || updating) { | |
690 | |
691 updating = m_model.m_reader->isUpdating(); | |
692 m_frameCount = m_model.getFrameCount(); | |
693 | |
694 // SVDEBUG << "WaveFileModel::fill: frame = " << frame << ", count = " << m_frameCount << endl; | |
695 | |
696 while (frame < m_frameCount) { | |
697 | |
698 // SVDEBUG << "WaveFileModel::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; | |
699 | |
700 if (updating && (frame + readBlockSize > m_frameCount)) break; | |
701 | |
702 block = m_model.m_reader->getInterleavedFrames(frame, readBlockSize); | |
703 | |
704 // cerr << "block is " << block.size() << endl; | |
705 | |
706 for (sv_frame_t i = 0; i < readBlockSize; ++i) { | |
707 | |
708 if (channels * i + channels > (int)block.size()) break; | |
709 | |
710 for (int ch = 0; ch < channels; ++ch) { | |
711 | |
712 sv_frame_t index = channels * i + ch; | |
713 float sample = block[index]; | |
714 | |
715 for (int cacheType = 0; cacheType < 2; ++cacheType) { // cache type | |
716 | |
717 sv_frame_t rangeIndex = ch * 2 + cacheType; | |
718 range[rangeIndex].sample(sample); | |
719 means[rangeIndex] += fabsf(sample); | |
720 } | |
721 } | |
722 | |
723 //!!! this looks like a ludicrous way to do synchronisation | |
724 QMutexLocker locker(&m_model.m_mutex); | |
725 | |
726 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
727 | |
728 if (++count[cacheType] == cacheBlockSize[cacheType]) { | |
729 | |
730 for (int ch = 0; ch < int(channels); ++ch) { | |
731 int rangeIndex = ch * 2 + cacheType; | |
732 means[rangeIndex] = means[rangeIndex] / float(count[cacheType]); | |
733 range[rangeIndex].setAbsmean(means[rangeIndex]); | |
734 m_model.m_cache[cacheType].push_back(range[rangeIndex]); | |
735 range[rangeIndex] = Range(); | |
736 means[rangeIndex] = 0.f; | |
737 } | |
738 | |
739 count[cacheType] = 0; | |
740 } | |
741 } | |
742 | |
743 ++frame; | |
744 } | |
745 | |
746 if (m_model.m_exiting) break; | |
747 | |
748 m_fillExtent = frame; | |
749 } | |
750 | |
751 // cerr << "WaveFileModel: inner loop ended" << endl; | |
752 | |
753 first = false; | |
754 if (m_model.m_exiting) break; | |
755 if (updating) { | |
756 // cerr << "sleeping..." << endl; | |
757 sleep(1); | |
758 } | |
759 } | |
760 | |
761 if (!m_model.m_exiting) { | |
762 | |
763 QMutexLocker locker(&m_model.m_mutex); | |
764 | |
765 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
766 | |
767 if (count[cacheType] > 0) { | |
768 | |
769 for (int ch = 0; ch < int(channels); ++ch) { | |
770 int rangeIndex = ch * 2 + cacheType; | |
771 means[rangeIndex] = means[rangeIndex] / float(count[cacheType]); | |
772 range[rangeIndex].setAbsmean(means[rangeIndex]); | |
773 m_model.m_cache[cacheType].push_back(range[rangeIndex]); | |
774 range[rangeIndex] = Range(); | |
775 means[rangeIndex] = 0.f; | |
776 } | |
777 | |
778 count[cacheType] = 0; | |
779 } | |
780 | |
781 const Range &rr = *m_model.m_cache[cacheType].begin(); | |
782 MUNLOCK(&rr, m_model.m_cache[cacheType].capacity() * sizeof(Range)); | |
783 } | |
784 } | |
785 | |
786 delete[] means; | |
787 delete[] range; | |
788 | |
789 m_fillExtent = m_frameCount; | |
790 | |
791 #ifdef DEBUG_WAVE_FILE_MODEL | |
792 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
793 cerr << "Cache type " << cacheType << " now contains " << m_model.m_cache[cacheType].size() << " ranges" << endl; | |
794 } | |
795 #endif | |
796 } | |
797 | |
798 void | |
799 WaveFileModel::toXml(QTextStream &out, | |
800 QString indent, | |
801 QString extraAttributes) const | |
802 { | |
803 Model::toXml(out, indent, | |
804 QString("type=\"wavefile\" file=\"%1\" %2") | |
805 .arg(encodeEntities(m_path)).arg(extraAttributes)); | |
806 } | |
807 | |
808 |