annotate data/model/WaveFileModel.cpp @ 1008:d9e0e59a1581

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