annotate data/model/ReadOnlyWaveFileModel.cpp @ 1337:3dcd83595bc8 3.0-integration

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