ReadOnlyWaveFileModel.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 Chris Cannam and QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "ReadOnlyWaveFileModel.h"
17 
18 #include "fileio/AudioFileReader.h"
20 
21 #include "system/System.h"
22 
23 #include "base/Preferences.h"
25 
26 #include <QFileInfo>
27 #include <QTextStream>
28 
29 #include <iostream>
30 #include <cmath>
31 #include <sndfile.h>
32 
33 #include <cassert>
34 
35 using namespace std;
36 
37 //#define DEBUG_WAVE_FILE_MODEL 1
38 //#define DEBUG_WAVE_FILE_MODEL_READ 1
39 
42 
44  m_source(source),
45  m_path(source.getLocation()),
46  m_reader(nullptr),
47  m_myReader(true),
48  m_startFrame(0),
49  m_fillThread(nullptr),
50  m_updateTimer(nullptr),
51  m_lastFillExtent(0),
52  m_prevCompletion(0),
53  m_exiting(false),
54  m_lastDirectReadStart(0),
55  m_lastDirectReadCount(0)
56 {
57  Profiler profiler("ReadOnlyWaveFileModel::ReadOnlyWaveFileModel");
58 
59  SVDEBUG << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: path "
60  << m_path << ", target rate " << targetRate << endl;
61 
63 
64  if (m_source.isOK()) {
65 
67 
69  params.targetRate = targetRate;
70 
71  params.normalisation = prefs->getNormaliseAudio() ?
74 
75  params.gaplessMode = prefs->getUseGaplessMode() ?
78 
80 
82  if (m_reader) {
83  SVDEBUG << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: reader rate: "
84  << m_reader->getSampleRate() << endl;
85  }
86  }
87 
88  if (m_reader) setObjectName(m_reader->getTitle());
89  if (objectName() == "") setObjectName(QFileInfo(m_path).fileName());
90  if (isOK()) fillCache();
91 
93  (getId().untyped, this);
94 }
95 
97  m_source(source),
98  m_path(source.getLocation()),
99  m_reader(nullptr),
100  m_myReader(false),
101  m_startFrame(0),
102  m_fillThread(nullptr),
103  m_updateTimer(nullptr),
104  m_lastFillExtent(0),
105  m_prevCompletion(0),
106  m_exiting(false)
107 {
108  Profiler profiler("ReadOnlyWaveFileModel::ReadOnlyWaveFileModel (with reader)");
109 
110  SVDEBUG << "ReadOnlyWaveFileModel::ReadOnlyWaveFileModel: path "
111  << m_path << ", with reader" << endl;
112 
113  m_reader = reader;
114  if (m_reader) setObjectName(m_reader->getTitle());
115  if (objectName() == "") setObjectName(QFileInfo(m_path).fileName());
116  fillCache();
117 
119  (getId().untyped, this);
120 }
121 
123 {
124  Profiler profiler("ReadOnlyWaveFileModel::~ReadOnlyWaveFileModel");
125 
127  (getId().untyped);
128 
129  m_exiting = true;
130  if (m_fillThread) m_fillThread->wait();
131  if (m_myReader) delete m_reader;
132  m_reader = nullptr;
133 
134  SVDEBUG << "ReadOnlyWaveFileModel: Destructor exiting; we had caches of "
135  << (m_cache[0].size() * sizeof(Range)) << " and "
136  << (m_cache[1].size() * sizeof(Range)) << " bytes" << endl;
137 }
138 
139 bool
141 {
142  return m_reader && m_reader->isOK();
143 }
144 
145 bool
146 ReadOnlyWaveFileModel::isReady(int *completion) const
147 {
148  bool ready = true;
149  if (!isOK()) ready = false;
150  if (m_fillThread) ready = false;
151  if (m_reader && m_reader->isUpdating()) ready = false;
152 
153  double c = double(m_lastFillExtent) /
154  double(getEndFrame() - getStartFrame());
155 
156  if (completion) {
157  *completion = int(c * 100.0 + 0.01);
158  if (m_reader) {
159  int decodeCompletion = m_reader->getDecodeCompletion();
160  if (decodeCompletion < 90) *completion = decodeCompletion;
161  else *completion = min(*completion, decodeCompletion);
162  }
163  if (*completion != 0 &&
164  *completion != 100 &&
165  m_prevCompletion != 0 &&
166  m_prevCompletion > *completion) {
167  // just to avoid completion going backwards
168  *completion = m_prevCompletion;
169  }
170  m_prevCompletion = *completion;
171  }
172 
173 #ifdef DEBUG_WAVE_FILE_MODEL
174  if (completion) {
175  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::isReady(): ready = " << ready << ", m_fillThread = " << m_fillThread << ", m_lastFillExtent = " << m_lastFillExtent << ", end frame = " << getEndFrame() << ", start frame = " << getStartFrame() << ", c = " << c << ", completion = " << *completion << endl;
176  } else {
177  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::isReady(): ready = " << ready << ", m_fillThread = " << m_fillThread << ", m_lastFillExtent = " << m_lastFillExtent << ", end frame = " << getEndFrame() << ", start frame = " << getStartFrame() << ", c = " << c << ", completion not requested" << endl;
178  }
179 #endif
180  return ready;
181 }
182 
185 {
186  if (!m_reader) return 0;
187  return m_reader->getFrameCount();
188 }
189 
190 int
192 {
193  if (!m_reader) return 0;
194  return m_reader->getChannelCount();
195 }
196 
199 {
200  if (!m_reader) return 0;
201  return m_reader->getSampleRate();
202 }
203 
206 {
207  if (!m_reader) return 0;
209  if (rate == 0) rate = getSampleRate();
210  return rate;
211 }
212 
213 QString
215 {
216  QString title;
217  if (m_reader) title = m_reader->getTitle();
218  if (title == "") title = objectName();
219  return title;
220 }
221 
222 QString
224 {
225  if (m_reader) return m_reader->getMaker();
226  return "";
227 }
228 
229 QString
231 {
232  if (m_reader) return m_reader->getLocation();
233  return "";
234 }
235 
236 QString
238 {
239 #ifdef DEBUG_WAVE_FILE_MODEL
240  SVCERR << "ReadOnlyWaveFileModel::getLocalFilename: reader is "
241  << m_reader << ", returning "
242  << (m_reader ? m_reader->getLocalFilename() : "(none)") << endl;
243 #endif
244  if (m_reader) return m_reader->getLocalFilename();
245  return "";
246 }
247 
250  sv_frame_t start,
251  sv_frame_t count)
252  const
253 {
254  // Read a single channel (if channel >= 0) or a mixdown of all
255  // channels (if channel == -1) directly from the file. This is
256  // used for e.g. audio playback or input to transforms.
257 
258  Profiler profiler("ReadOnlyWaveFileModel::getData");
259 
260 #ifdef DEBUG_WAVE_FILE_MODEL_READ
261  cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << endl;
262 #endif
263 
264  int channels = getChannelCount();
265 
266  if (channel >= channels) {
267  SVCERR << "ERROR: WaveFileModel::getData: channel ("
268  << channel << ") >= channel count (" << channels << ")"
269  << endl;
270  return {};
271  }
272 
273  if (!m_reader || !m_reader->isOK() || count == 0) {
274  return {};
275  }
276 
277  if (start >= m_startFrame) {
278  start -= m_startFrame;
279  } else {
280  if (count <= m_startFrame - start) {
281  return {};
282  } else {
283  count -= (m_startFrame - start);
284  start = 0;
285  }
286  }
287 
288  floatvec_t interleaved = m_reader->getInterleavedFrames(start, count);
289  if (channels == 1) return interleaved;
290 
291  sv_frame_t obtained = interleaved.size() / channels;
292 
293  floatvec_t result(obtained, 0.f);
294 
295  if (channel != -1) {
296  // get a single channel
297  for (int i = 0; i < obtained; ++i) {
298  result[i] = interleaved[i * channels + channel];
299  }
300  } else {
301  // channel == -1, mix down all channels
302  for (int i = 0; i < obtained; ++i) {
303  for (int c = 0; c < channels; ++c) {
304  result[i] += interleaved[i * channels + c];
305  }
306  }
307  }
308 
309  return result;
310 }
311 
312 vector<floatvec_t>
313 ReadOnlyWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
314  sv_frame_t start, sv_frame_t count) const
315 {
316  // Read a set of channels directly from the file. This is used
317  // for e.g. audio playback or input to transforms.
318 
319  Profiler profiler("ReadOnlyWaveFileModel::getMultiChannelData");
320 
321 #ifdef DEBUG_WAVE_FILE_MODEL_READ
322  cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << endl;
323 #endif
324 
325  int channels = getChannelCount();
326 
327  if (fromchannel > tochannel) {
328  SVCERR << "ERROR: ReadOnlyWaveFileModel::getMultiChannelData: "
329  << "fromchannel (" << fromchannel
330  << ") > tochannel (" << tochannel << ")"
331  << endl;
332  return {};
333  }
334 
335  if (tochannel >= channels) {
336  SVCERR << "ERROR: ReadOnlyWaveFileModel::getMultiChannelData: "
337  << "tochannel (" << tochannel
338  << ") >= channel count (" << channels << ")"
339  << endl;
340  return {};
341  }
342 
343  if (!m_reader || !m_reader->isOK() || count == 0) {
344  return {};
345  }
346 
347  int reqchannels = (tochannel - fromchannel) + 1;
348 
349  if (start >= m_startFrame) {
350  start -= m_startFrame;
351  } else {
352  if (count <= m_startFrame - start) {
353  return {};
354  } else {
355  count -= (m_startFrame - start);
356  start = 0;
357  }
358  }
359 
360  floatvec_t interleaved = m_reader->getInterleavedFrames(start, count);
361  if (channels == 1) return { interleaved };
362 
363  sv_frame_t obtained = interleaved.size() / channels;
364  vector<floatvec_t> result(reqchannels, floatvec_t(obtained, 0.f));
365 
366  for (int c = fromchannel; c <= tochannel; ++c) {
367  int destc = c - fromchannel;
368  for (int i = 0; i < obtained; ++i) {
369  result[destc][i] = interleaved[i * channels + c];
370  }
371  }
372 
373  return result;
374 }
375 
376 int
378 {
379  int cacheType = 0;
380  int power = m_zoomConstraint.getMinCachePower();
381  int roundedBlockSize = m_zoomConstraint.getNearestBlockSize
382  (desired, cacheType, power, ZoomConstraint::RoundDown);
383 
384  if (cacheType != 0 && cacheType != 1) {
385  // We will be reading directly from file, so can satisfy any
386  // blocksize requirement
387  return desired;
388  } else {
389  return roundedBlockSize;
390  }
391 }
392 
393 void
395  RangeBlock &ranges, int &blockSize) const
396 {
397  ranges.clear();
398  if (!isOK()) return;
399  ranges.reserve((count / blockSize) + 1);
400 
401  if (start > m_startFrame) start -= m_startFrame;
402  else if (count <= m_startFrame - start) return;
403  else {
404  count -= (m_startFrame - start);
405  start = 0;
406  }
407 
408  int cacheType = 0;
409  int power = m_zoomConstraint.getMinCachePower();
410  int roundedBlockSize = m_zoomConstraint.getNearestBlockSize
411  (blockSize, cacheType, power, ZoomConstraint::RoundDown);
412 
413  int channels = getChannelCount();
414 
415  if (cacheType != 0 && cacheType != 1) {
416 
417  // We need to read directly from the file. We haven't got
418  // this cached. Hope the requested area is small. This is
419  // not optimal -- we'll end up reading the same frames twice
420  // for stereo files, in two separate calls to this method.
421  // We could fairly trivially handle this for most cases that
422  // matter by putting a single cache in getInterleavedFrames
423  // for short queries.
424 
425  m_directReadMutex.lock();
426 
427  if (m_lastDirectReadStart != start ||
428  m_lastDirectReadCount != count ||
429  m_directRead.empty()) {
430 
431  m_directRead = m_reader->getInterleavedFrames(start, count);
432  m_lastDirectReadStart = start;
433  m_lastDirectReadCount = count;
434  }
435 
436  float max = 0.0, min = 0.0, total = 0.0;
437  sv_frame_t i = 0, got = 0;
438 
439  while (i < count) {
440 
441  sv_frame_t index = i * channels + channel;
442  if (index >= (sv_frame_t)m_directRead.size()) break;
443 
444  float sample = m_directRead[index];
445  if (sample > max || got == 0) max = sample;
446  if (sample < min || got == 0) min = sample;
447  total += fabsf(sample);
448 
449  ++i;
450  ++got;
451 
452  if (got == blockSize) {
453  ranges.push_back(Range(min, max, total / float(got)));
454  min = max = total = 0.0f;
455  got = 0;
456  }
457  }
458 
459  m_directReadMutex.unlock();
460 
461  if (got > 0) {
462  ranges.push_back(Range(min, max, total / float(got)));
463  }
464 
465  return;
466 
467  } else {
468 
469  QMutexLocker locker(&m_mutex);
470 
471  const RangeBlock &cache = m_cache[cacheType];
472 
473  blockSize = roundedBlockSize;
474 
475  sv_frame_t cacheBlock, div;
476 
477  cacheBlock = (sv_frame_t(1) << m_zoomConstraint.getMinCachePower());
478  if (cacheType == 1) {
479  cacheBlock = sv_frame_t(double(cacheBlock) * sqrt(2.) + 0.01);
480  }
481  div = blockSize / cacheBlock;
482 
483  sv_frame_t startIndex = start / cacheBlock;
484  sv_frame_t endIndex = (start + count) / cacheBlock;
485 
486  float max = 0.0, min = 0.0, total = 0.0;
487  sv_frame_t i = 0, got = 0;
488 
489 #ifdef DEBUG_WAVE_FILE_MODEL_READ
490  cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl;
491 #endif
492 
493  for (i = 0; i <= endIndex - startIndex; ) {
494 
495  sv_frame_t index = (i + startIndex) * channels + channel;
496  if (!in_range_for(cache, index)) break;
497 
498  const Range &range = cache[index];
499  if (range.max() > max || got == 0) max = range.max();
500  if (range.min() < min || got == 0) min = range.min();
501  total += range.absmean();
502 
503  ++i;
504  ++got;
505 
506  if (got == div) {
507  ranges.push_back(Range(min, max, total / float(got)));
508  min = max = total = 0.0f;
509  got = 0;
510  }
511  }
512 
513  if (got > 0) {
514  ranges.push_back(Range(min, max, total / float(got)));
515  }
516  }
517 
518 #ifdef DEBUG_WAVE_FILE_MODEL_READ
519  cerr << "returning " << ranges.size() << " ranges" << endl;
520 #endif
521  return;
522 }
523 
526 {
527  Range range;
528  if (!isOK()) return range;
529 
530  if (start > m_startFrame) start -= m_startFrame;
531  else if (count <= m_startFrame - start) return range;
532  else {
533  count -= (m_startFrame - start);
534  start = 0;
535  }
536 
537  int blockSize;
538  for (blockSize = 1; blockSize <= count; blockSize *= 2);
539  if (blockSize > 1) blockSize /= 2;
540 
541  bool first = false;
542 
543  sv_frame_t blockStart = (start / blockSize) * blockSize;
544  sv_frame_t blockEnd = ((start + count) / blockSize) * blockSize;
545 
546  if (blockStart < start) blockStart += blockSize;
547 
548  if (blockEnd > blockStart) {
549  RangeBlock ranges;
550  getSummaries(channel, blockStart, blockEnd - blockStart, ranges, blockSize);
551  for (int i = 0; i < (int)ranges.size(); ++i) {
552  if (first || ranges[i].min() < range.min()) range.setMin(ranges[i].min());
553  if (first || ranges[i].max() > range.max()) range.setMax(ranges[i].max());
554  if (first || ranges[i].absmean() < range.absmean()) range.setAbsmean(ranges[i].absmean());
555  first = false;
556  }
557  }
558 
559  if (blockStart > start) {
560  Range startRange = getSummary(channel, start, blockStart - start);
561  range.setMin(min(range.min(), startRange.min()));
562  range.setMax(max(range.max(), startRange.max()));
563  range.setAbsmean(min(range.absmean(), startRange.absmean()));
564  }
565 
566  if (blockEnd < start + count) {
567  Range endRange = getSummary(channel, blockEnd, start + count - blockEnd);
568  range.setMin(min(range.min(), endRange.min()));
569  range.setMax(max(range.max(), endRange.max()));
570  range.setAbsmean(min(range.absmean(), endRange.absmean()));
571  }
572 
573  return range;
574 }
575 
576 void
578 {
579  m_mutex.lock();
580 
581  m_updateTimer = new QTimer(this);
582  connect(m_updateTimer, SIGNAL(timeout()), this, SLOT(fillTimerTimedOut()));
583  m_updateTimer->start(100);
584 
585  m_fillThread = new RangeCacheFillThread(*this);
586  connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled()));
587 
588  m_mutex.unlock();
589  m_fillThread->start();
590 
591 #ifdef DEBUG_WAVE_FILE_MODEL
592  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::fillCache: started fill thread" << endl;
593 #endif
594 }
595 
596 void
598 {
599  if (m_fillThread) {
600  sv_frame_t fillExtent = m_fillThread->getFillExtent();
601 #ifdef DEBUG_WAVE_FILE_MODEL
602  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::fillTimerTimedOut: extent = " << fillExtent << endl;
603 #endif
604  if (fillExtent > m_lastFillExtent) {
605  emit modelChangedWithin(getId(), m_lastFillExtent, fillExtent);
606  m_lastFillExtent = fillExtent;
607  }
608  } else {
609 #ifdef DEBUG_WAVE_FILE_MODEL
610  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::fillTimerTimedOut: no thread" << endl;
611 #endif
612  emit modelChanged(getId());
613  }
614 }
615 
616 void
618 {
619  m_mutex.lock();
620  delete m_fillThread;
621  m_fillThread = nullptr;
622  delete m_updateTimer;
623  m_updateTimer = nullptr;
624  auto prevFillExtent = m_lastFillExtent;
626  m_mutex.unlock();
627 #ifdef DEBUG_WAVE_FILE_MODEL
628  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::cacheFilled, about to emit things" << endl;
629 #endif
630  if (getEndFrame() > prevFillExtent) {
631  emit modelChangedWithin(getId(), prevFillExtent, getEndFrame());
632  }
633  emit modelChanged(getId());
634  emit ready(getId());
635 }
636 
637 void
639 {
640  int cacheBlockSize[2];
641  cacheBlockSize[0] = (1 << m_model.m_zoomConstraint.getMinCachePower());
642  cacheBlockSize[1] = (int((1 << m_model.m_zoomConstraint.getMinCachePower()) *
643  sqrt(2.) + 0.01));
644 
645  sv_frame_t frame = 0;
646  const sv_frame_t readBlockSize = 32768;
647  floatvec_t block;
648 
649  if (!m_model.isOK()) return;
650 
651  int channels = m_model.getChannelCount();
652  bool updating = m_model.m_reader->isUpdating();
653 
654  if (updating) {
655  while (channels == 0 && !m_model.m_exiting) {
656 #ifdef DEBUG_WAVE_FILE_MODEL
657  SVCERR << "ReadOnlyWaveFileModel(" << objectName() << ")::fill: Waiting for channels..." << endl;
658 #endif
659  sleep(1);
660  channels = m_model.getChannelCount();
661  }
662  if (m_model.m_exiting) {
663  return;
664  }
665  }
666 
667  Range *range = new Range[2 * channels];
668  float *means = new float[2 * channels];
669  int count[2];
670  count[0] = count[1] = 0;
671  for (int i = 0; i < 2 * channels; ++i) {
672  means[i] = 0.f;
673  }
674 
675  bool first = true;
676 
677  while (first || updating) {
678 
679  updating = m_model.m_reader->isUpdating();
680  m_frameCount = m_model.getFrameCount();
681 
682  m_model.m_mutex.lock();
683 
684  while (frame < m_frameCount) {
685 
686  m_model.m_mutex.unlock();
687 
688 #ifdef DEBUG_WAVE_FILE_MODEL_READ
689  cout << "ReadOnlyWaveFileModel(" << m_model.objectName() << ")::fill inner loop: frame = " << frame << ", count = " << m_frameCount << ", blocksize " << readBlockSize << endl;
690 #endif
691 
692  if (updating && (frame + readBlockSize > m_frameCount)) {
693  m_model.m_mutex.lock(); // must be locked on exiting loop
694  break;
695  }
696 
697  block = m_model.m_reader->getInterleavedFrames(frame, readBlockSize);
698 
699  sv_frame_t gotBlockSize = block.size() / channels;
700 
701  m_model.m_mutex.lock();
702 
703  for (sv_frame_t i = 0; i < gotBlockSize; ++i) {
704 
705  for (int ch = 0; ch < channels; ++ch) {
706 
707  sv_frame_t index = channels * i + ch;
708  float sample = block[index];
709 
710  for (int cacheType = 0; cacheType < 2; ++cacheType) {
711  sv_frame_t rangeIndex = ch * 2 + cacheType;
712  range[rangeIndex].sample(sample);
713  means[rangeIndex] += fabsf(sample);
714  }
715  }
716 
717  for (int cacheType = 0; cacheType < 2; ++cacheType) {
718 
719  if (++count[cacheType] == cacheBlockSize[cacheType]) {
720 
721  for (int ch = 0; ch < int(channels); ++ch) {
722  int rangeIndex = ch * 2 + cacheType;
723  means[rangeIndex] = means[rangeIndex] / float(count[cacheType]);
724  range[rangeIndex].setAbsmean(means[rangeIndex]);
725  m_model.m_cache[cacheType].push_back(range[rangeIndex]);
726  range[rangeIndex] = Range();
727  means[rangeIndex] = 0.f;
728  }
729 
730  count[cacheType] = 0;
731  }
732  }
733 
734  ++frame;
735  }
736 
737  if (m_model.m_exiting) break;
738  m_fillExtent = frame;
739  }
740 
741  m_model.m_mutex.unlock();
742 
743  first = false;
744  if (m_model.m_exiting) break;
745  if (updating) {
746  usleep(100000);
747  if (m_model.m_exiting) break;
748  }
749  }
750 
751  if (!m_model.m_exiting) {
752 
753  QMutexLocker locker(&m_model.m_mutex);
754 
755  for (int cacheType = 0; cacheType < 2; ++cacheType) {
756 
757  if (count[cacheType] > 0) {
758 
759  for (int ch = 0; ch < int(channels); ++ch) {
760  int rangeIndex = ch * 2 + cacheType;
761  means[rangeIndex] = means[rangeIndex] / float(count[cacheType]);
762  range[rangeIndex].setAbsmean(means[rangeIndex]);
763  m_model.m_cache[cacheType].push_back(range[rangeIndex]);
764  range[rangeIndex] = Range();
765  means[rangeIndex] = 0.f;
766  }
767 
768  count[cacheType] = 0;
769  }
770 
771  const Range &rr = *m_model.m_cache[cacheType].begin();
772  MUNLOCK(&rr, m_model.m_cache[cacheType].capacity() * sizeof(Range));
773  }
774  }
775 
776  delete[] means;
777  delete[] range;
778 
779  m_fillExtent = m_frameCount;
780 
781 #ifdef DEBUG_WAVE_FILE_MODEL
782  for (int cacheType = 0; cacheType < 2; ++cacheType) {
783  SVCERR << "ReadOnlyWaveFileModel(" << m_model.objectName() << "): Cache type " << cacheType << " now contains " << m_model.m_cache[cacheType].size() << " ranges" << endl;
784  }
785 #endif
786 }
787 
788 void
790  QString indent,
791  QString extraAttributes) const
792 {
793  Model::toXml(out, indent,
794  QString("type=\"wavefile\" file=\"%1\" %2")
795  .arg(encodeEntities(m_path)).arg(extraAttributes));
796 }
797 
798 
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
virtual sv_samplerate_t getNativeRate() const
Return the native samplerate of the file.
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:204
static PlayParameterRepository * getInstance()
std::atomic< bool > m_exiting
bool isReady(int *) const override
Return true if the model has finished loading or calculating all its data, for a model that is capabl...
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
virtual QString getMaker() const =0
Return the "maker" of the work in the audio file, if known.
sv_frame_t getFrameCount() const override
RangeCacheFillThread * m_fillThread
virtual floatvec_t getInterleavedFrames(sv_frame_t start, sv_frame_t count) const =0
Return interleaved samples for count frames from index start.
GaplessMode gaplessMode
Gapless mode to use.
virtual int getNearestBlockSize(int requestedBlockSize, int &type, int &power, RoundingDirection dir=RoundNearest) const
sv_samplerate_t getNativeRate() const override
Return the frame rate of the underlying material, if the model itself has already been resampled...
virtual QString getTitle() const =0
Return the title of the work in the audio file, if known.
void start()
Definition: Thread.cpp:34
If the reader supports threaded decoding, it will be used and the file will be decoded in a backgroun...
#define MUNLOCK(a, b)
Definition: System.h:91
QString getMaker() const override
Return the "artist" or "maker" of the model, if known.
void ready(ModelId myId)
Emitted when internal processing is complete (i.e.
virtual bool isUpdating() const
Return true if decoding is still in progress and the frame count may change.
static Preferences * getInstance()
Definition: Preferences.cpp:31
void waitForData()
Block on a sub-event-loop until the whole of the data has been retrieved (if it is remote)...
Definition: FileSource.cpp:570
bool isOK() const override
Return true if the model was constructed successfully.
Id getId() const
Return an id for this object.
Definition: ById.h:193
sv_samplerate_t targetRate
Sample rate to open the file at.
sv_frame_t getEndFrame() const
Return the audio frame at the end of the model, i.e.
Definition: Model.h:87
std::vector< float, breakfastquay::StlAllocator< float > > floatvec_t
Definition: BaseTypes.h:53
static QString encodeEntities(QString)
int getSummaryBlockSize(int desired) const override
bool getNormaliseAudio() const
True if audio files should be loaded with normalisation (max == 1)
Definition: Preferences.h:82
static AudioFileReader * createReader(FileSource source, Parameters parameters, ProgressReporter *reporter=0)
Return an audio file reader initialised to the file at the given path, or NULL if no suitable reader ...
static PowerOfSqrtTwoZoomConstraint m_zoomConstraint
Any encoder delay and padding found in file metadata will be compensated for, giving gapless decoding...
virtual QString getLocation() const =0
Return the location of the audio data in the reader (as passed in to the FileSource constructor...
No delay compensation will happen and the results will be equivalent to the behaviour of audio reader...
QString getTitle() const override
Return the "work title" of the model, if known.
Normalise file data to abs(max) == 1.0.
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
void removePlayable(int id)
Unregister a playable.
virtual QString getLocalFilename() const =0
Return the local file path of the audio data.
QString getLocation() const override
Return the location of the data in this model (e.g.
bool in_range_for(const C &container, T i)
Check whether an integer index is in range for a container, avoiding overflows and signed/unsigned co...
Definition: BaseTypes.h:37
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
#define SVDEBUG
Definition: Debug.h:106
bool isOK() const
Return true if the file was opened successfully and no error has subsequently occurred.
Range getSummary(int channel, sv_frame_t start, sv_frame_t count) const override
Return the range from the given start frame, corresponding to the given number of underlying sample f...
void toXml(QTextStream &out, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
void addPlayable(int id, const Playable *)
Register a playable.
sv_frame_t getStartFrame() const override
Return the first audio frame spanned by the model.
ReadOnlyWaveFileModel(FileSource source, sv_samplerate_t targetRate=0)
Construct a WaveFileModel from a source path and optional resampling target rate. ...
#define SVCERR
Definition: Debug.h:109
virtual int getDecodeCompletion() const
Return a percentage value indicating how far through decoding the audio file we are.
sv_frame_t getFrameCount() const
Return the number of audio sample frames (i.e.
sv_samplerate_t getSampleRate() const override
Return the frame rate in frames per second.
Normalisation normalisation
Normalisation to use.
int getChannelCount() const override
Return the number of distinct channels for this model.
floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from the given channel of the model in single-precision floating-poi...
void modelChanged(ModelId myId)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
ThreadingMode threadingMode
Threading mode.
void getSummaries(int channel, sv_frame_t start, sv_frame_t count, RangeBlock &ranges, int &blockSize) const override
Return ranges from the given start frame, corresponding to the given number of underlying sample fram...
sv_samplerate_t getSampleRate() const
Return the samplerate at which the file is being read.
bool getUseGaplessMode() const
True if mp3 files should be loaded "gaplessly", i.e. compensating for encoder/decoder delay and paddi...
Definition: Preferences.h:79
int getChannelCount() const
Return the number of channels in the file.
std::vector< floatvec_t > getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from given contiguous range of channels of the model in single-preci...
Profile point instance class.
Definition: Profiler.h:93
bool isOK() const
Return true if the FileSource object is valid and neither error nor cancellation occurred while retri...
Definition: FileSource.cpp:586