comparison data/model/WaveFileModel.cpp @ 147:3a13b0d4934e

* Reorganising code base. This revision will not compile.
author Chris Cannam
date Mon, 31 Jul 2006 11:44:37 +0000
parents
children 4b2ea82fd0ed
comparison
equal deleted inserted replaced
146:f90fad823cea 147:3a13b0d4934e
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "model/WaveFileModel.h"
17
18 #include "fileio/AudioFileReader.h"
19 #include "fileio/AudioFileReaderFactory.h"
20
21 #include "base/System.h"
22
23 #include <QMessageBox>
24 #include <QFileInfo>
25
26 #include <iostream>
27 #include <unistd.h>
28 #include <math.h>
29 #include <sndfile.h>
30
31 #include <cassert>
32
33 using std::cerr;
34 using std::endl;
35
36 WaveFileModel::WaveFileModel(QString path) :
37 m_path(path),
38 m_fillThread(0),
39 m_updateTimer(0),
40 m_lastFillExtent(0),
41 m_exiting(false)
42 {
43 m_reader = AudioFileReaderFactory::createReader(path);
44 setObjectName(QFileInfo(path).fileName());
45 if (isOK()) fillCache();
46 }
47
48 WaveFileModel::~WaveFileModel()
49 {
50 m_exiting = true;
51 if (m_fillThread) m_fillThread->wait();
52 delete m_reader;
53 m_reader = 0;
54 }
55
56 bool
57 WaveFileModel::isOK() const
58 {
59 return m_reader && m_reader->isOK();
60 }
61
62 bool
63 WaveFileModel::isReady(int *completion) const
64 {
65 bool ready = (isOK() && (m_fillThread == 0));
66 double c = double(m_lastFillExtent) / double(getEndFrame() - getStartFrame());
67 if (completion) *completion = int(c * 100.0 + 0.01);
68 return ready;
69 }
70
71 Model *
72 WaveFileModel::clone() const
73 {
74 WaveFileModel *model = new WaveFileModel(m_path);
75 return model;
76 }
77
78 size_t
79 WaveFileModel::getFrameCount() const
80 {
81 if (!m_reader) return 0;
82 return m_reader->getFrameCount();
83 }
84
85 size_t
86 WaveFileModel::getChannelCount() const
87 {
88 if (!m_reader) return 0;
89 return m_reader->getChannelCount();
90 }
91
92 size_t
93 WaveFileModel::getSampleRate() const
94 {
95 if (!m_reader) return 0;
96 return m_reader->getSampleRate();
97 }
98
99 size_t
100 WaveFileModel::getValues(int channel, size_t start, size_t end,
101 float *buffer) const
102 {
103 // Always read these directly from the file.
104 // This is used for e.g. audio playback.
105 // Could be much more efficient (although compiler optimisation will help)
106
107 if (end < start) {
108 std::cerr << "ERROR: WaveFileModel::getValues[float]: end < start ("
109 << end << " < " << start << ")" << std::endl;
110 assert(end >= start);
111 }
112
113 if (!m_reader || !m_reader->isOK()) return 0;
114
115 SampleBlock frames;
116 m_reader->getInterleavedFrames(start, end - start, frames);
117
118 size_t i = 0;
119
120 int ch0 = channel, ch1 = channel, channels = getChannelCount();
121 if (channel == -1) {
122 ch0 = 0;
123 ch1 = channels - 1;
124 }
125
126 while (i < end - start) {
127
128 buffer[i] = 0.0;
129
130 for (int ch = ch0; ch <= ch1; ++ch) {
131
132 size_t index = i * channels + ch;
133 if (index >= frames.size()) break;
134
135 float sample = frames[index];
136 buffer[i] += sample;
137 }
138
139 ++i;
140 }
141
142 return i;
143 }
144
145 size_t
146 WaveFileModel::getValues(int channel, size_t start, size_t end,
147 double *buffer) const
148 {
149 if (end < start) {
150 std::cerr << "ERROR: WaveFileModel::getValues[double]: end < start ("
151 << end << " < " << start << ")" << std::endl;
152 assert(end >= start);
153 }
154
155 if (!m_reader || !m_reader->isOK()) return 0;
156
157 SampleBlock frames;
158 m_reader->getInterleavedFrames(start, end - start, frames);
159
160 size_t i = 0;
161
162 int ch0 = channel, ch1 = channel, channels = getChannelCount();
163 if (channel == -1) {
164 ch0 = 0;
165 ch1 = channels - 1;
166 }
167
168 while (i < end - start) {
169
170 buffer[i] = 0.0;
171
172 for (int ch = ch0; ch <= ch1; ++ch) {
173
174 size_t index = i * channels + ch;
175 if (index >= frames.size()) break;
176
177 float sample = frames[index];
178 buffer[i] += sample;
179 }
180
181 ++i;
182 }
183
184 return i;
185 }
186
187 WaveFileModel::RangeBlock
188 WaveFileModel::getRanges(size_t channel, size_t start, size_t end,
189 size_t &blockSize) const
190 {
191 RangeBlock ranges;
192 if (!isOK()) return ranges;
193
194 if (end <= start) {
195 std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRanges (end = " << end << ", start = " << start << ", blocksize = " << blockSize << ")" << std::endl;
196 return ranges;
197 }
198
199 int cacheType = 0;
200 int power = getMinCachePower();
201 blockSize = getNearestBlockSize(blockSize, cacheType, power,
202 ZoomConstraint::RoundUp);
203
204 size_t channels = getChannelCount();
205
206 if (cacheType != 0 && cacheType != 1) {
207
208 // We need to read directly from the file. We haven't got
209 // this cached. Hope the requested area is small. This is
210 // not optimal -- we'll end up reading the same frames twice
211 // for stereo files, in two separate calls to this method.
212 // We could fairly trivially handle this for most cases that
213 // matter by putting a single cache in getInterleavedFrames
214 // for short queries.
215
216 SampleBlock frames;
217 m_reader->getInterleavedFrames(start, end - start, frames);
218 float max = 0.0, min = 0.0, total = 0.0;
219 size_t i = 0, count = 0;
220
221 while (i < end - start) {
222
223 size_t index = i * channels + channel;
224 if (index >= frames.size()) break;
225
226 float sample = frames[index];
227 if (sample > max || count == 0) max = sample;
228 if (sample < min || count == 0) min = sample;
229 total += fabsf(sample);
230
231 ++i;
232 ++count;
233
234 if (count == blockSize) {
235 ranges.push_back(Range(min, max, total / count));
236 min = max = total = 0.0f;
237 count = 0;
238 }
239 }
240
241 if (count > 0) {
242 ranges.push_back(Range(min, max, total / count));
243 }
244
245 return ranges;
246
247 } else {
248
249 QMutexLocker locker(&m_mutex);
250
251 const RangeBlock &cache = m_cache[cacheType];
252
253 size_t cacheBlock, div;
254
255 if (cacheType == 0) {
256 cacheBlock = (1 << getMinCachePower());
257 div = (1 << power) / cacheBlock;
258 } else {
259 cacheBlock = ((unsigned int)((1 << getMinCachePower()) * sqrt(2) + 0.01));
260 div = ((unsigned int)((1 << power) * sqrt(2) + 0.01)) / cacheBlock;
261 }
262
263 size_t startIndex = start / cacheBlock;
264 size_t endIndex = end / cacheBlock;
265
266 float max = 0.0, min = 0.0, total = 0.0;
267 size_t i = 0, count = 0;
268
269 //cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", end " << end << ", power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl;
270
271 for (i = 0; i < endIndex - startIndex; ) {
272
273 size_t index = (i + startIndex) * channels + channel;
274 if (index >= cache.size()) break;
275
276 const Range &range = cache[index];
277 if (range.max > max || count == 0) max = range.max;
278 if (range.min < min || count == 0) min = range.min;
279 total += range.absmean;
280
281 ++i;
282 ++count;
283
284 if (count == div) {
285 ranges.push_back(Range(min, max, total / count));
286 min = max = total = 0.0f;
287 count = 0;
288 }
289 }
290
291 if (count > 0) {
292 ranges.push_back(Range(min, max, total / count));
293 }
294 }
295
296 //cerr << "returning " << ranges.size() << " ranges" << endl;
297 return ranges;
298 }
299
300 WaveFileModel::Range
301 WaveFileModel::getRange(size_t channel, size_t start, size_t end) const
302 {
303 Range range;
304 if (!isOK()) return range;
305
306 if (end <= start) {
307 std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRange (end = " << end << ", start = " << start << ")" << std::endl;
308 return range;
309 }
310
311 size_t blockSize;
312 for (blockSize = 1; blockSize <= end - start; blockSize *= 2);
313 blockSize /= 2;
314
315 bool first = false;
316
317 size_t blockStart = (start / blockSize) * blockSize;
318 size_t blockEnd = (end / blockSize) * blockSize;
319
320 if (blockStart < start) blockStart += blockSize;
321
322 if (blockEnd > blockStart) {
323 RangeBlock ranges = getRanges(channel, blockStart, blockEnd, blockSize);
324 for (size_t i = 0; i < ranges.size(); ++i) {
325 if (first || ranges[i].min < range.min) range.min = ranges[i].min;
326 if (first || ranges[i].max > range.max) range.max = ranges[i].max;
327 if (first || ranges[i].absmean < range.absmean) range.absmean = ranges[i].absmean;
328 first = false;
329 }
330 }
331
332 if (blockStart > start) {
333 Range startRange = getRange(channel, start, blockStart);
334 range.min = std::min(range.min, startRange.min);
335 range.max = std::max(range.max, startRange.max);
336 range.absmean = std::min(range.absmean, startRange.absmean);
337 }
338
339 if (blockEnd < end) {
340 Range endRange = getRange(channel, blockEnd, end);
341 range.min = std::min(range.min, endRange.min);
342 range.max = std::max(range.max, endRange.max);
343 range.absmean = std::min(range.absmean, endRange.absmean);
344 }
345
346 return range;
347 }
348
349 void
350 WaveFileModel::fillCache()
351 {
352 m_mutex.lock();
353 m_updateTimer = new QTimer(this);
354 connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut()));
355 m_updateTimer->start(100);
356 m_fillThread = new RangeCacheFillThread(*this);
357 connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled()));
358 m_mutex.unlock();
359 m_fillThread->start();
360 }
361
362 void
363 WaveFileModel::fillTimerTimedOut()
364 {
365 if (m_fillThread) {
366 size_t fillExtent = m_fillThread->getFillExtent();
367 if (fillExtent > m_lastFillExtent) {
368 emit modelChanged(m_lastFillExtent, fillExtent);
369 m_lastFillExtent = fillExtent;
370 }
371 } else {
372 emit modelChanged();
373 }
374 }
375
376 void
377 WaveFileModel::cacheFilled()
378 {
379 m_mutex.lock();
380 delete m_fillThread;
381 m_fillThread = 0;
382 delete m_updateTimer;
383 m_updateTimer = 0;
384 m_mutex.unlock();
385 emit modelChanged();
386 // cerr << "WaveFileModel::cacheFilled" << endl;
387 }
388
389 void
390 WaveFileModel::RangeCacheFillThread::run()
391 {
392 size_t cacheBlockSize[2];
393 cacheBlockSize[0] = (1 << m_model.getMinCachePower());
394 cacheBlockSize[1] = ((unsigned int)((1 << m_model.getMinCachePower()) *
395 sqrt(2) + 0.01));
396
397 size_t frame = 0;
398 size_t readBlockSize = 16384;
399 SampleBlock block;
400
401 if (!m_model.isOK()) return;
402
403 size_t channels = m_model.getChannelCount();
404 size_t frames = m_model.getFrameCount();
405
406 Range *range = new Range[2 * channels];
407 size_t count[2];
408 count[0] = count[1] = 0;
409
410 while (frame < frames) {
411
412 m_model.m_reader->getInterleavedFrames(frame, readBlockSize, block);
413
414 for (size_t i = 0; i < readBlockSize; ++i) {
415
416 for (size_t ch = 0; ch < size_t(channels); ++ch) {
417
418 size_t index = channels * i + ch;
419 if (index >= block.size()) continue;
420 float sample = block[index];
421
422 for (size_t ct = 0; ct < 2; ++ct) {
423
424 size_t rangeIndex = ch * 2 + ct;
425
426 if (sample > range[rangeIndex].max || count[ct] == 0) {
427 range[rangeIndex].max = sample;
428 }
429 if (sample < range[rangeIndex].min || count[ct] == 0) {
430 range[rangeIndex].min = sample;
431 }
432 range[rangeIndex].absmean += fabsf(sample);
433 }
434 }
435
436 QMutexLocker locker(&m_model.m_mutex);
437 for (size_t ct = 0; ct < 2; ++ct) {
438 if (++count[ct] == cacheBlockSize[ct]) {
439 for (size_t ch = 0; ch < size_t(channels); ++ch) {
440 size_t rangeIndex = ch * 2 + ct;
441 range[rangeIndex].absmean /= count[ct];
442 m_model.m_cache[ct].push_back(range[rangeIndex]);
443 range[rangeIndex] = Range();
444 }
445 count[ct] = 0;
446 }
447 }
448
449 ++frame;
450 }
451
452 if (m_model.m_exiting) break;
453
454 m_fillExtent = frame;
455 }
456
457 QMutexLocker locker(&m_model.m_mutex);
458 for (size_t ct = 0; ct < 2; ++ct) {
459 if (count[ct] > 0) {
460 for (size_t ch = 0; ch < size_t(channels); ++ch) {
461 size_t rangeIndex = ch * 2 + ct;
462 range[rangeIndex].absmean /= count[ct];
463 m_model.m_cache[ct].push_back(range[rangeIndex]);
464 range[rangeIndex] = Range();
465 }
466 count[ct] = 0;
467 }
468
469 const Range &rr = *m_model.m_cache[ct].begin();
470 MUNLOCK(&rr, m_model.m_cache[ct].capacity() * sizeof(Range));
471 }
472
473 delete[] range;
474
475 m_fillExtent = frames;
476
477 // for (size_t ct = 0; ct < 2; ++ct) {
478 // cerr << "Cache type " << ct << " now contains " << m_model.m_cache[ct].size() << " ranges" << endl;
479 // }
480 }
481
482 QString
483 WaveFileModel::toXmlString(QString indent,
484 QString extraAttributes) const
485 {
486 return Model::toXmlString(indent,
487 QString("type=\"wavefile\" file=\"%1\" %2")
488 .arg(m_path).arg(extraAttributes));
489 }
490
491
492 #ifdef INCLUDE_MOCFILES
493 #ifdef INCLUDE_MOCFILES
494 #include "WaveFileModel.moc.cpp"
495 #endif
496 #endif
497