annotate data/model/WaveFileModel.cpp @ 876:47aa3aeb687b tonioni

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