annotate data/model/ReadOnlyWaveFileModel.cpp @ 1752:6d09d68165a4 by-id

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