annotate data/model/ReadOnlyWaveFileModel.cpp @ 1873:1d44fdc8196c csv-import-headers

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