annotate data/model/WaveFileModel.cpp @ 392:183ee2a55fc7

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