annotate data/model/ReadOnlyWaveFileModel.cpp @ 1881:b504df98c3be

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