annotate data/model/ReadOnlyWaveFileModel.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +0100
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