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