annotate data/model/ReadOnlyWaveFileModel.cpp @ 1825:029d7d3f0a3a

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