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