annotate data/model/ReadOnlyWaveFileModel.cpp @ 1247:8f076d02569a piper

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