annotate data/model/ReadOnlyWaveFileModel.cpp @ 1520:954d0cf29ca7 import-audio-data

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