Mercurial > hg > svcore
comparison data/model/ReadOnlyWaveFileModel.cpp @ 1122:b9faee02afa5 recording
Make WritableWaveFileModel a true WaveFileModel (and ReadOnlyWaveFileModel the other sort of it). Enable recording from an empty session using that.
author | Chris Cannam |
---|---|
date | Wed, 19 Aug 2015 17:03:31 +0100 |
parents | data/model/WaveFileModel.cpp@9f4505ac9072 |
children | efea94b04d5a |
comparison
equal
deleted
inserted
replaced
1117:020277bfafcb | 1122:b9faee02afa5 |
---|---|
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 //#define DEBUG_WAVE_FILE_MODEL 1 | |
36 | |
37 PowerOfSqrtTwoZoomConstraint | |
38 ReadOnlyWaveFileModel::m_zoomConstraint; | |
39 | |
40 ReadOnlyWaveFileModel::ReadOnlyWaveFileModel(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 { | |
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 << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: 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 } | |
67 | |
68 ReadOnlyWaveFileModel::ReadOnlyWaveFileModel(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 ReadOnlyWaveFileModel::~ReadOnlyWaveFileModel() | |
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 ReadOnlyWaveFileModel::isOK() const | |
95 { | |
96 return m_reader && m_reader->isOK(); | |
97 } | |
98 | |
99 bool | |
100 ReadOnlyWaveFileModel::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 << "ReadOnlyWaveFileModel::isReady(): ready = " << ready << ", completion = " << (completion ? *completion : -1) << endl; | |
123 #endif | |
124 return ready; | |
125 } | |
126 | |
127 sv_frame_t | |
128 ReadOnlyWaveFileModel::getFrameCount() const | |
129 { | |
130 if (!m_reader) return 0; | |
131 return m_reader->getFrameCount(); | |
132 } | |
133 | |
134 int | |
135 ReadOnlyWaveFileModel::getChannelCount() const | |
136 { | |
137 if (!m_reader) return 0; | |
138 return m_reader->getChannelCount(); | |
139 } | |
140 | |
141 sv_samplerate_t | |
142 ReadOnlyWaveFileModel::getSampleRate() const | |
143 { | |
144 if (!m_reader) return 0; | |
145 return m_reader->getSampleRate(); | |
146 } | |
147 | |
148 sv_samplerate_t | |
149 ReadOnlyWaveFileModel::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 ReadOnlyWaveFileModel::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 ReadOnlyWaveFileModel::getMaker() const | |
168 { | |
169 if (m_reader) return m_reader->getMaker(); | |
170 return ""; | |
171 } | |
172 | |
173 QString | |
174 ReadOnlyWaveFileModel::getLocation() const | |
175 { | |
176 if (m_reader) return m_reader->getLocation(); | |
177 return ""; | |
178 } | |
179 | |
180 QString | |
181 ReadOnlyWaveFileModel::getLocalFilename() const | |
182 { | |
183 if (m_reader) return m_reader->getLocalFilename(); | |
184 return ""; | |
185 } | |
186 | |
187 sv_frame_t | |
188 ReadOnlyWaveFileModel::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 << "ReadOnlyWaveFileModel::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 << "ReadOnlyWaveFileModel::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 ReadOnlyWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, | |
256 sv_frame_t start, sv_frame_t count, | |
257 float **buffer) const | |
258 { | |
259 #ifdef DEBUG_WAVE_FILE_MODEL | |
260 cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << ", " << buffer << endl; | |
261 #endif | |
262 | |
263 int channels = getChannelCount(); | |
264 | |
265 if (fromchannel > tochannel) { | |
266 cerr << "ERROR: ReadOnlyWaveFileModel::getData: fromchannel (" | |
267 << fromchannel << ") > tochannel (" << tochannel << ")" | |
268 << endl; | |
269 return 0; | |
270 } | |
271 | |
272 if (tochannel >= channels) { | |
273 cerr << "ERROR: ReadOnlyWaveFileModel::getData: tochannel (" | |
274 << tochannel << ") >= channel count (" << channels << ")" | |
275 << endl; | |
276 return 0; | |
277 } | |
278 | |
279 if (fromchannel == tochannel) { | |
280 return getData(fromchannel, start, count, buffer[0]); | |
281 } | |
282 | |
283 int reqchannels = (tochannel - fromchannel) + 1; | |
284 | |
285 // Always read these directly from the file. | |
286 // This is used for e.g. audio playback. | |
287 // Could be much more efficient (although compiler optimisation will help) | |
288 | |
289 if (start >= m_startFrame) { | |
290 start -= m_startFrame; | |
291 } else { | |
292 for (int c = 0; c < reqchannels; ++c) { | |
293 for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f; | |
294 } | |
295 if (count <= m_startFrame - start) { | |
296 return 0; | |
297 } else { | |
298 count -= (m_startFrame - start); | |
299 start = 0; | |
300 } | |
301 } | |
302 | |
303 if (!m_reader || !m_reader->isOK() || count == 0) { | |
304 for (int c = 0; c < reqchannels; ++c) { | |
305 for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f; | |
306 } | |
307 return 0; | |
308 } | |
309 | |
310 SampleBlock frames = m_reader->getInterleavedFrames(start, count); | |
311 | |
312 sv_frame_t i = 0; | |
313 | |
314 sv_frame_t index = 0, available = frames.size(); | |
315 | |
316 while (i < count) { | |
317 | |
318 if (index >= available) break; | |
319 | |
320 int destc = 0; | |
321 | |
322 for (int c = 0; c < channels; ++c) { | |
323 | |
324 if (c >= fromchannel && c <= tochannel) { | |
325 buffer[destc][i] = frames[index]; | |
326 ++destc; | |
327 } | |
328 | |
329 ++index; | |
330 } | |
331 | |
332 ++i; | |
333 } | |
334 | |
335 return i; | |
336 } | |
337 | |
338 int | |
339 ReadOnlyWaveFileModel::getSummaryBlockSize(int desired) const | |
340 { | |
341 int cacheType = 0; | |
342 int power = m_zoomConstraint.getMinCachePower(); | |
343 int roundedBlockSize = m_zoomConstraint.getNearestBlockSize | |
344 (desired, cacheType, power, ZoomConstraint::RoundDown); | |
345 if (cacheType != 0 && cacheType != 1) { | |
346 // We will be reading directly from file, so can satisfy any | |
347 // blocksize requirement | |
348 return desired; | |
349 } else { | |
350 return roundedBlockSize; | |
351 } | |
352 } | |
353 | |
354 void | |
355 ReadOnlyWaveFileModel::getSummaries(int channel, sv_frame_t start, sv_frame_t count, | |
356 RangeBlock &ranges, int &blockSize) const | |
357 { | |
358 ranges.clear(); | |
359 if (!isOK()) return; | |
360 ranges.reserve((count / blockSize) + 1); | |
361 | |
362 if (start > m_startFrame) start -= m_startFrame; | |
363 else if (count <= m_startFrame - start) return; | |
364 else { | |
365 count -= (m_startFrame - start); | |
366 start = 0; | |
367 } | |
368 | |
369 int cacheType = 0; | |
370 int power = m_zoomConstraint.getMinCachePower(); | |
371 int roundedBlockSize = m_zoomConstraint.getNearestBlockSize | |
372 (blockSize, cacheType, power, ZoomConstraint::RoundDown); | |
373 | |
374 int channels = getChannelCount(); | |
375 | |
376 if (cacheType != 0 && cacheType != 1) { | |
377 | |
378 // We need to read directly from the file. We haven't got | |
379 // this cached. Hope the requested area is small. This is | |
380 // not optimal -- we'll end up reading the same frames twice | |
381 // for stereo files, in two separate calls to this method. | |
382 // We could fairly trivially handle this for most cases that | |
383 // matter by putting a single cache in getInterleavedFrames | |
384 // for short queries. | |
385 | |
386 m_directReadMutex.lock(); | |
387 | |
388 if (m_lastDirectReadStart != start || | |
389 m_lastDirectReadCount != count || | |
390 m_directRead.empty()) { | |
391 | |
392 m_directRead = m_reader->getInterleavedFrames(start, count); | |
393 m_lastDirectReadStart = start; | |
394 m_lastDirectReadCount = count; | |
395 } | |
396 | |
397 float max = 0.0, min = 0.0, total = 0.0; | |
398 sv_frame_t i = 0, got = 0; | |
399 | |
400 while (i < count) { | |
401 | |
402 sv_frame_t index = i * channels + channel; | |
403 if (index >= (sv_frame_t)m_directRead.size()) break; | |
404 | |
405 float sample = m_directRead[index]; | |
406 if (sample > max || got == 0) max = sample; | |
407 if (sample < min || got == 0) min = sample; | |
408 total += fabsf(sample); | |
409 | |
410 ++i; | |
411 ++got; | |
412 | |
413 if (got == blockSize) { | |
414 ranges.push_back(Range(min, max, total / float(got))); | |
415 min = max = total = 0.0f; | |
416 got = 0; | |
417 } | |
418 } | |
419 | |
420 m_directReadMutex.unlock(); | |
421 | |
422 if (got > 0) { | |
423 ranges.push_back(Range(min, max, total / float(got))); | |
424 } | |
425 | |
426 return; | |
427 | |
428 } else { | |
429 | |
430 QMutexLocker locker(&m_mutex); | |
431 | |
432 const RangeBlock &cache = m_cache[cacheType]; | |
433 | |
434 blockSize = roundedBlockSize; | |
435 | |
436 sv_frame_t cacheBlock, div; | |
437 | |
438 if (cacheType == 0) { | |
439 cacheBlock = (1 << m_zoomConstraint.getMinCachePower()); | |
440 div = (1 << power) / cacheBlock; | |
441 } else { | |
442 cacheBlock = sv_frame_t((1 << m_zoomConstraint.getMinCachePower()) * sqrt(2.) + 0.01); | |
443 div = sv_frame_t(((1 << power) * sqrt(2.) + 0.01) / double(cacheBlock)); | |
444 } | |
445 | |
446 sv_frame_t startIndex = start / cacheBlock; | |
447 sv_frame_t endIndex = (start + count) / cacheBlock; | |
448 | |
449 float max = 0.0, min = 0.0, total = 0.0; | |
450 sv_frame_t i = 0, got = 0; | |
451 | |
452 #ifdef DEBUG_WAVE_FILE_MODEL | |
453 cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; | |
454 #endif | |
455 | |
456 for (i = 0; i <= endIndex - startIndex; ) { | |
457 | |
458 sv_frame_t index = (i + startIndex) * channels + channel; | |
459 if (index >= (sv_frame_t)cache.size()) break; | |
460 | |
461 const Range &range = cache[index]; | |
462 if (range.max() > max || got == 0) max = range.max(); | |
463 if (range.min() < min || got == 0) min = range.min(); | |
464 total += range.absmean(); | |
465 | |
466 ++i; | |
467 ++got; | |
468 | |
469 if (got == div) { | |
470 ranges.push_back(Range(min, max, total / float(got))); | |
471 min = max = total = 0.0f; | |
472 got = 0; | |
473 } | |
474 } | |
475 | |
476 if (got > 0) { | |
477 ranges.push_back(Range(min, max, total / float(got))); | |
478 } | |
479 } | |
480 | |
481 #ifdef DEBUG_WAVE_FILE_MODEL | |
482 SVDEBUG << "returning " << ranges.size() << " ranges" << endl; | |
483 #endif | |
484 return; | |
485 } | |
486 | |
487 ReadOnlyWaveFileModel::Range | |
488 ReadOnlyWaveFileModel::getSummary(int channel, sv_frame_t start, sv_frame_t count) const | |
489 { | |
490 Range range; | |
491 if (!isOK()) return range; | |
492 | |
493 if (start > m_startFrame) start -= m_startFrame; | |
494 else if (count <= m_startFrame - start) return range; | |
495 else { | |
496 count -= (m_startFrame - start); | |
497 start = 0; | |
498 } | |
499 | |
500 int blockSize; | |
501 for (blockSize = 1; blockSize <= count; blockSize *= 2); | |
502 if (blockSize > 1) blockSize /= 2; | |
503 | |
504 bool first = false; | |
505 | |
506 sv_frame_t blockStart = (start / blockSize) * blockSize; | |
507 sv_frame_t blockEnd = ((start + count) / blockSize) * blockSize; | |
508 | |
509 if (blockStart < start) blockStart += blockSize; | |
510 | |
511 if (blockEnd > blockStart) { | |
512 RangeBlock ranges; | |
513 getSummaries(channel, blockStart, blockEnd - blockStart, ranges, blockSize); | |
514 for (int i = 0; i < (int)ranges.size(); ++i) { | |
515 if (first || ranges[i].min() < range.min()) range.setMin(ranges[i].min()); | |
516 if (first || ranges[i].max() > range.max()) range.setMax(ranges[i].max()); | |
517 if (first || ranges[i].absmean() < range.absmean()) range.setAbsmean(ranges[i].absmean()); | |
518 first = false; | |
519 } | |
520 } | |
521 | |
522 if (blockStart > start) { | |
523 Range startRange = getSummary(channel, start, blockStart - start); | |
524 range.setMin(std::min(range.min(), startRange.min())); | |
525 range.setMax(std::max(range.max(), startRange.max())); | |
526 range.setAbsmean(std::min(range.absmean(), startRange.absmean())); | |
527 } | |
528 | |
529 if (blockEnd < start + count) { | |
530 Range endRange = getSummary(channel, blockEnd, start + count - blockEnd); | |
531 range.setMin(std::min(range.min(), endRange.min())); | |
532 range.setMax(std::max(range.max(), endRange.max())); | |
533 range.setAbsmean(std::min(range.absmean(), endRange.absmean())); | |
534 } | |
535 | |
536 return range; | |
537 } | |
538 | |
539 void | |
540 ReadOnlyWaveFileModel::fillCache() | |
541 { | |
542 m_mutex.lock(); | |
543 | |
544 m_updateTimer = new QTimer(this); | |
545 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut())); | |
546 m_updateTimer->start(100); | |
547 | |
548 m_fillThread = new RangeCacheFillThread(*this); | |
549 connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled())); | |
550 | |
551 m_mutex.unlock(); | |
552 m_fillThread->start(); | |
553 | |
554 #ifdef DEBUG_WAVE_FILE_MODEL | |
555 SVDEBUG << "ReadOnlyWaveFileModel::fillCache: started fill thread" << endl; | |
556 #endif | |
557 } | |
558 | |
559 void | |
560 ReadOnlyWaveFileModel::fillTimerTimedOut() | |
561 { | |
562 if (m_fillThread) { | |
563 sv_frame_t fillExtent = m_fillThread->getFillExtent(); | |
564 #ifdef DEBUG_WAVE_FILE_MODEL | |
565 SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: extent = " << fillExtent << endl; | |
566 #endif | |
567 if (fillExtent > m_lastFillExtent) { | |
568 emit modelChangedWithin(m_lastFillExtent, fillExtent); | |
569 m_lastFillExtent = fillExtent; | |
570 } | |
571 } else { | |
572 #ifdef DEBUG_WAVE_FILE_MODEL | |
573 SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: no thread" << endl; | |
574 #endif | |
575 emit modelChanged(); | |
576 } | |
577 } | |
578 | |
579 void | |
580 ReadOnlyWaveFileModel::cacheFilled() | |
581 { | |
582 m_mutex.lock(); | |
583 delete m_fillThread; | |
584 m_fillThread = 0; | |
585 delete m_updateTimer; | |
586 m_updateTimer = 0; | |
587 m_mutex.unlock(); | |
588 if (getEndFrame() > m_lastFillExtent) { | |
589 emit modelChangedWithin(m_lastFillExtent, getEndFrame()); | |
590 } | |
591 emit modelChanged(); | |
592 emit ready(); | |
593 #ifdef DEBUG_WAVE_FILE_MODEL | |
594 SVDEBUG << "ReadOnlyWaveFileModel::cacheFilled" << endl; | |
595 #endif | |
596 } | |
597 | |
598 void | |
599 ReadOnlyWaveFileModel::RangeCacheFillThread::run() | |
600 { | |
601 int cacheBlockSize[2]; | |
602 cacheBlockSize[0] = (1 << m_model.m_zoomConstraint.getMinCachePower()); | |
603 cacheBlockSize[1] = (int((1 << m_model.m_zoomConstraint.getMinCachePower()) * | |
604 sqrt(2.) + 0.01)); | |
605 | |
606 sv_frame_t frame = 0; | |
607 const sv_frame_t readBlockSize = 16384; | |
608 SampleBlock block; | |
609 | |
610 if (!m_model.isOK()) return; | |
611 | |
612 int channels = m_model.getChannelCount(); | |
613 bool updating = m_model.m_reader->isUpdating(); | |
614 | |
615 if (updating) { | |
616 while (channels == 0 && !m_model.m_exiting) { | |
617 // SVDEBUG << "ReadOnlyWaveFileModel::fill: Waiting for channels..." << endl; | |
618 sleep(1); | |
619 channels = m_model.getChannelCount(); | |
620 } | |
621 } | |
622 | |
623 Range *range = new Range[2 * channels]; | |
624 float *means = new float[2 * channels]; | |
625 int count[2]; | |
626 count[0] = count[1] = 0; | |
627 for (int i = 0; i < 2 * channels; ++i) { | |
628 means[i] = 0.f; | |
629 } | |
630 | |
631 bool first = true; | |
632 | |
633 while (first || updating) { | |
634 | |
635 updating = m_model.m_reader->isUpdating(); | |
636 m_frameCount = m_model.getFrameCount(); | |
637 | |
638 // SVDEBUG << "ReadOnlyWaveFileModel::fill: frame = " << frame << ", count = " << m_frameCount << endl; | |
639 | |
640 while (frame < m_frameCount) { | |
641 | |
642 // SVDEBUG << "ReadOnlyWaveFileModel::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl; | |
643 | |
644 if (updating && (frame + readBlockSize > m_frameCount)) break; | |
645 | |
646 block = m_model.m_reader->getInterleavedFrames(frame, readBlockSize); | |
647 | |
648 // cerr << "block is " << block.size() << endl; | |
649 | |
650 for (sv_frame_t i = 0; i < readBlockSize; ++i) { | |
651 | |
652 if (channels * i + channels > (int)block.size()) break; | |
653 | |
654 for (int ch = 0; ch < channels; ++ch) { | |
655 | |
656 sv_frame_t index = channels * i + ch; | |
657 float sample = block[index]; | |
658 | |
659 for (int cacheType = 0; cacheType < 2; ++cacheType) { // cache type | |
660 | |
661 sv_frame_t rangeIndex = ch * 2 + cacheType; | |
662 range[rangeIndex].sample(sample); | |
663 means[rangeIndex] += fabsf(sample); | |
664 } | |
665 } | |
666 | |
667 //!!! this looks like a ludicrous way to do synchronisation | |
668 QMutexLocker locker(&m_model.m_mutex); | |
669 | |
670 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
671 | |
672 if (++count[cacheType] == cacheBlockSize[cacheType]) { | |
673 | |
674 for (int ch = 0; ch < int(channels); ++ch) { | |
675 int rangeIndex = ch * 2 + cacheType; | |
676 means[rangeIndex] = means[rangeIndex] / float(count[cacheType]); | |
677 range[rangeIndex].setAbsmean(means[rangeIndex]); | |
678 m_model.m_cache[cacheType].push_back(range[rangeIndex]); | |
679 range[rangeIndex] = Range(); | |
680 means[rangeIndex] = 0.f; | |
681 } | |
682 | |
683 count[cacheType] = 0; | |
684 } | |
685 } | |
686 | |
687 ++frame; | |
688 } | |
689 | |
690 if (m_model.m_exiting) break; | |
691 | |
692 m_fillExtent = frame; | |
693 } | |
694 | |
695 // cerr << "ReadOnlyWaveFileModel: inner loop ended" << endl; | |
696 | |
697 first = false; | |
698 if (m_model.m_exiting) break; | |
699 if (updating) { | |
700 // cerr << "sleeping..." << endl; | |
701 sleep(1); | |
702 } | |
703 } | |
704 | |
705 if (!m_model.m_exiting) { | |
706 | |
707 QMutexLocker locker(&m_model.m_mutex); | |
708 | |
709 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
710 | |
711 if (count[cacheType] > 0) { | |
712 | |
713 for (int ch = 0; ch < int(channels); ++ch) { | |
714 int rangeIndex = ch * 2 + cacheType; | |
715 means[rangeIndex] = means[rangeIndex] / float(count[cacheType]); | |
716 range[rangeIndex].setAbsmean(means[rangeIndex]); | |
717 m_model.m_cache[cacheType].push_back(range[rangeIndex]); | |
718 range[rangeIndex] = Range(); | |
719 means[rangeIndex] = 0.f; | |
720 } | |
721 | |
722 count[cacheType] = 0; | |
723 } | |
724 | |
725 const Range &rr = *m_model.m_cache[cacheType].begin(); | |
726 MUNLOCK(&rr, m_model.m_cache[cacheType].capacity() * sizeof(Range)); | |
727 } | |
728 } | |
729 | |
730 delete[] means; | |
731 delete[] range; | |
732 | |
733 m_fillExtent = m_frameCount; | |
734 | |
735 #ifdef DEBUG_WAVE_FILE_MODEL | |
736 for (int cacheType = 0; cacheType < 2; ++cacheType) { | |
737 cerr << "Cache type " << cacheType << " now contains " << m_model.m_cache[cacheType].size() << " ranges" << endl; | |
738 } | |
739 #endif | |
740 } | |
741 | |
742 void | |
743 ReadOnlyWaveFileModel::toXml(QTextStream &out, | |
744 QString indent, | |
745 QString extraAttributes) const | |
746 { | |
747 Model::toXml(out, indent, | |
748 QString("type=\"wavefile\" file=\"%1\" %2") | |
749 .arg(encodeEntities(m_path)).arg(extraAttributes)); | |
750 } | |
751 | |
752 |