annotate data/model/ReadOnlyWaveFileModel.cpp @ 1751:77543124651b by-id

Overhaul PlayParameters bits
author Chris Cannam
date Thu, 04 Jul 2019 18:04:21 +0100
parents 70e172e6cc59
children 6d09d68165a4
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@1406 593 emit modelChangedWithin(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@1406 600 emit modelChanged();
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@1557 619 emit modelChangedWithin(prevFillExtent, getEndFrame());
Chris@267 620 }
Chris@147 621 emit modelChanged();
Chris@411 622 emit ready();
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