annotate data/model/WaveFileModel.cpp @ 823:f0558e69a074

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