annotate data/model/WaveFileModel.cpp @ 211:e2bbb58e6df6

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