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