annotate data/model/WaveFileModel.cpp @ 409:6075c90744d4

...
author Chris Cannam
date Fri, 09 May 2008 12:39:02 +0000
parents 183ee2a55fc7
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