annotate data/model/WaveFileModel.cpp @ 458:f60360209e5c

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