Audacity  3.0.3
Sequence.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Sequence.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
29 
30 
31 
32 #include "Sequence.h"
33 
34 #include <algorithm>
35 #include <float.h>
36 #include <math.h>
37 
38 #include <wx/intl.h>
39 #include <wx/filefn.h>
40 #include <wx/ffile.h>
41 #include <wx/log.h>
42 
43 #include "SampleBlock.h"
44 #include "InconsistencyException.h"
46 
47 size_t Sequence::sMaxDiskBlockSize = 1048576;
48 
49 // Sequence methods
51  const SampleBlockFactoryPtr &pFactory, sampleFormat format)
52 : mpFactory(pFactory),
53  mSampleFormat(format),
54  mMinSamples(sMaxDiskBlockSize / SAMPLE_SIZE(mSampleFormat) / 2),
55  mMaxSamples(mMinSamples * 2)
56 {
57 }
58 
59 // essentially a copy constructor - but you must pass in the
60 // current project, because we might be copying from one
61 // project to another
63  const Sequence &orig, const SampleBlockFactoryPtr &pFactory)
64 : mpFactory(pFactory),
65  mSampleFormat(orig.mSampleFormat),
66  mMinSamples(orig.mMinSamples),
67  mMaxSamples(orig.mMaxSamples)
68 {
69  Paste(0, &orig);
70 }
71 
73 {
74 }
75 
77 {
78  return mMaxSamples;
79 }
80 
82 {
83  return mMaxSamples;
84 }
85 
87 {
88  for (unsigned int i = 0; i < mBlock.size(); i++)
89  mBlock[i].sb->CloseLock();
90 
91  return true;
92 }
93 
95 {
96  return mSampleFormat;
97 }
98 
99 /*
100 bool Sequence::SetSampleFormat(sampleFormat format)
101 {
102  if (mBlock.size() > 0 || mNumSamples > 0)
103  return false;
104 
105  mSampleFormat = format;
106  return true;
107 }
108 */
109 
110 namespace {
112  size_t &size, size_t required,
113  SampleBuffer *pSecondBuffer = nullptr)
114  {
115  // This should normally do nothing, but it is a defense against corrupt
116  // projects than might have inconsistent block files bigger than the
117  // expected maximum size.
118  if (size < required) {
119  // reallocate
120  buffer.Allocate(required, format);
121  if (pSecondBuffer && pSecondBuffer->ptr())
122  pSecondBuffer->Allocate(required, format);
123  if (!buffer.ptr() || (pSecondBuffer && !pSecondBuffer->ptr())) {
124  // malloc failed
125  // Perhaps required is a really crazy value,
126  // and perhaps we should throw an AudacityException, but that is
127  // a second-order concern
129  }
130  size = required;
131  }
132  }
133 }
134 
137  const std::function<void(size_t)> & progressReport)
138 {
139  if (format == mSampleFormat)
140  // no change
141  return false;
142 
143  if (mBlock.size() == 0)
144  {
146  return true;
147  }
148 
149  const sampleFormat oldFormat = mSampleFormat;
151 
152  const auto oldMinSamples = mMinSamples, oldMaxSamples = mMaxSamples;
153  // These are the same calculations as in the constructor.
155  mMaxSamples = mMinSamples * 2;
156 
157  bool bSuccess = false;
158  auto cleanup = finally( [&] {
159  if (!bSuccess) {
160  // Conversion failed. Revert these member vars.
161  mSampleFormat = oldFormat;
162  mMaxSamples = oldMaxSamples;
163  mMinSamples = oldMinSamples;
164  }
165  } );
166 
167  BlockArray newBlockArray;
168  // Use the ratio of old to NEW mMaxSamples to make a reasonable guess
169  // at allocation.
170  newBlockArray.reserve
171  (1 + mBlock.size() * ((float)oldMaxSamples / (float)mMaxSamples));
172 
173  {
174  size_t oldSize = oldMaxSamples;
175  SampleBuffer bufferOld(oldSize, oldFormat);
176  size_t newSize = oldMaxSamples;
177  SampleBuffer bufferNew(newSize, format);
178 
179  for (size_t i = 0, nn = mBlock.size(); i < nn; i++)
180  {
181  SeqBlock &oldSeqBlock = mBlock[i];
182  const auto &oldBlockFile = oldSeqBlock.sb;
183  const auto len = oldBlockFile->GetSampleCount();
184  ensureSampleBufferSize(bufferOld, oldFormat, oldSize, len);
185  Read(bufferOld.ptr(), oldFormat, oldSeqBlock, 0, len, true);
186 
187  ensureSampleBufferSize(bufferNew, format, newSize, len);
188  CopySamples(bufferOld.ptr(), oldFormat, bufferNew.ptr(), format, len);
189 
190  // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451,
191  // using Blockify, allows (len < mMinSamples).
192  // This will happen consistently when going from more bytes per sample to fewer...
193  // This will create a block that's smaller than mMinSamples, which
194  // shouldn't be allowed, but we agreed it's okay for now.
195  //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere?
196  // If so, need to special-case (len < mMinSamples) and start combining data
197  // from the old blocks... Oh no!
198 
199  // Using Blockify will handle the cases where len > the NEW mMaxSamples. Previous code did not.
200  const auto blockstart = oldSeqBlock.start;
202  newBlockArray, blockstart, bufferNew.ptr(), len);
203 
204  if (progressReport)
205  progressReport(len);
206  }
207  }
208 
209  // Invalidate all the old, non-aliased block files.
210  // Aliased files will be converted at save, per comment above.
211 
212  // Commit the changes to block file array
214  (newBlockArray, mNumSamples, wxT("Sequence::ConvertToSampleFormat()"));
215 
216  // Commit the other changes
217  bSuccess = true;
218 
219  return true;
220 }
221 
222 std::pair<float, float> Sequence::GetMinMax(
223  sampleCount start, sampleCount len, bool mayThrow) const
224 {
225  if (len == 0 || mBlock.size() == 0) {
226  return {
227  0.f,
228  // FLT_MAX? So it doesn't look like a spurious '0' to a caller?
229 
230  0.f
231  // -FLT_MAX? So it doesn't look like a spurious '0' to a caller?
232  };
233  }
234 
235  float min = FLT_MAX;
236  float max = -FLT_MAX;
237 
238  unsigned int block0 = FindBlock(start);
239  unsigned int block1 = FindBlock(start + len - 1);
240 
241  // First calculate the min/max of the blocks in the middle of this region;
242  // this is very fast because we have the min/max of every entire block
243  // already in memory.
244 
245  for (unsigned b = block0 + 1; b < block1; ++b) {
246  auto results = mBlock[b].sb->GetMinMaxRMS(mayThrow);
247 
248  if (results.min < min)
249  min = results.min;
250  if (results.max > max)
251  max = results.max;
252  }
253 
254  // Now we take the first and last blocks into account, noting that the
255  // selection may only partly overlap these blocks. If the overall min/max
256  // of either of these blocks is within min...max, then we can ignore them.
257  // If not, we need read some samples and summaries from disk.
258  {
259  const SeqBlock &theBlock = mBlock[block0];
260  const auto &theFile = theBlock.sb;
261  auto results = theFile->GetMinMaxRMS(mayThrow);
262 
263  if (results.min < min || results.max > max) {
264  // start lies within theBlock:
265  auto s0 = ( start - theBlock.start ).as_size_t();
266  const auto maxl0 = (
267  // start lies within theBlock:
268  theBlock.start + theFile->GetSampleCount() - start
269  ).as_size_t();
270  wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19
271  const auto l0 = limitSampleBufferSize ( maxl0, len );
272 
273  results = theFile->GetMinMaxRMS(s0, l0, mayThrow);
274  if (results.min < min)
275  min = results.min;
276  if (results.max > max)
277  max = results.max;
278  }
279  }
280 
281  if (block1 > block0)
282  {
283  const SeqBlock &theBlock = mBlock[block1];
284  const auto &theFile = theBlock.sb;
285  auto results = theFile->GetMinMaxRMS(mayThrow);
286 
287  if (results.min < min || results.max > max) {
288 
289  // start + len - 1 lies in theBlock:
290  const auto l0 = ( start + len - theBlock.start ).as_size_t();
291  wxASSERT(l0 <= mMaxSamples); // Vaughan, 2011-10-19
292 
293  results = theFile->GetMinMaxRMS(0, l0, mayThrow);
294  if (results.min < min)
295  min = results.min;
296  if (results.max > max)
297  max = results.max;
298  }
299  }
300 
301  return { min, max };
302 }
303 
304 float Sequence::GetRMS(sampleCount start, sampleCount len, bool mayThrow) const
305 {
306  // len is the number of samples that we want the rms of.
307  // it may be longer than a block, and the code is carefully set up to handle that.
308  if (len == 0 || mBlock.size() == 0)
309  return 0.f;
310 
311  double sumsq = 0.0;
312  sampleCount length = 0; // this is the cumulative length of the bits we have the ms of so far, and should end up == len
313 
314  unsigned int block0 = FindBlock(start);
315  unsigned int block1 = FindBlock(start + len - 1);
316 
317  // First calculate the rms of the blocks in the middle of this region;
318  // this is very fast because we have the rms of every entire block
319  // already in memory.
320  for (unsigned b = block0 + 1; b < block1; b++) {
321  const SeqBlock &theBlock = mBlock[b];
322  const auto &sb = theBlock.sb;
323  auto results = sb->GetMinMaxRMS(mayThrow);
324 
325  const auto fileLen = sb->GetSampleCount();
326  const auto blockRMS = results.RMS;
327  sumsq += blockRMS * blockRMS * fileLen;
328  length += fileLen;
329  }
330 
331  // Now we take the first and last blocks into account, noting that the
332  // selection may only partly overlap these blocks.
333  // If not, we need read some samples and summaries from disk.
334  {
335  const SeqBlock &theBlock = mBlock[block0];
336  const auto &sb = theBlock.sb;
337  // start lies within theBlock
338  auto s0 = ( start - theBlock.start ).as_size_t();
339  // start lies within theBlock
340  const auto maxl0 =
341  (theBlock.start + sb->GetSampleCount() - start).as_size_t();
342  wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19
343  const auto l0 = limitSampleBufferSize( maxl0, len );
344 
345  auto results = sb->GetMinMaxRMS(s0, l0, mayThrow);
346  const auto partialRMS = results.RMS;
347  sumsq += partialRMS * partialRMS * l0;
348  length += l0;
349  }
350 
351  if (block1 > block0) {
352  const SeqBlock &theBlock = mBlock[block1];
353  const auto &sb = theBlock.sb;
354 
355  // start + len - 1 lies within theBlock
356  const auto l0 = ( start + len - theBlock.start ).as_size_t();
357  wxASSERT(l0 <= mMaxSamples); // PRL: I think Vaughan missed this
358 
359  auto results = sb->GetMinMaxRMS(0, l0, mayThrow);
360  const auto partialRMS = results.RMS;
361  sumsq += partialRMS * partialRMS * l0;
362  length += l0;
363  }
364 
365  // PRL: catch bugs like 1320:
366  wxASSERT(length == len);
367 
368  return sqrt(sumsq / length.as_double() );
369 }
370 
371 // Must pass in the correct factory for the result. If it's not the same
372 // as in this, then block contents must be copied.
373 std::unique_ptr<Sequence> Sequence::Copy( const SampleBlockFactoryPtr &pFactory,
374  sampleCount s0, sampleCount s1) const
375 {
376  // Make a new Sequence object for the specified factory:
377  auto dest = std::make_unique<Sequence>(pFactory, mSampleFormat);
378  if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) {
379  return dest;
380  }
381 
382  // Decide whether to share sample blocks or make new copies, when whole block
383  // contents are used -- must copy if factories are different:
384  auto pUseFactory = (pFactory == mpFactory) ? nullptr : pFactory.get();
385 
386  int numBlocks = mBlock.size();
387 
388  int b0 = FindBlock(s0);
389  const int b1 = FindBlock(s1 - 1);
390  wxASSERT(b0 >= 0);
391  wxASSERT(b0 < numBlocks);
392  wxASSERT(b1 < numBlocks);
393  wxUnusedVar(numBlocks);
394  wxASSERT(b0 <= b1);
395 
396  dest->mBlock.reserve(b1 - b0 + 1);
397 
398  auto bufferSize = mMaxSamples;
399  SampleBuffer buffer(bufferSize, mSampleFormat);
400 
401  int blocklen;
402 
403  // Do any initial partial block
404 
405  const SeqBlock &block0 = mBlock[b0];
406  if (s0 != block0.start) {
407  const auto &sb = block0.sb;
408  // Nonnegative result is length of block0 or less:
409  blocklen =
410  ( std::min(s1, block0.start + sb->GetSampleCount()) - s0 ).as_size_t();
411  wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
412  ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen);
413  Get(b0, buffer.ptr(), mSampleFormat, s0, blocklen, true);
414 
415  dest->Append(buffer.ptr(), mSampleFormat, blocklen);
416  }
417  else
418  --b0;
419 
420  // If there are blocks in the middle, use the blocks whole
421  for (int bb = b0 + 1; bb < b1; ++bb)
422  AppendBlock(pUseFactory, mSampleFormat,
423  dest->mBlock, dest->mNumSamples, mBlock[bb]);
424  // Increase ref count or duplicate file
425 
426  // Do the last block
427  if (b1 > b0) {
428  // Probable case of a partial block
429  const SeqBlock &block = mBlock[b1];
430  const auto &sb = block.sb;
431  // s1 is within block:
432  blocklen = (s1 - block.start).as_size_t();
433  wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
434  if (blocklen < (int)sb->GetSampleCount()) {
435  ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen);
436  Get(b1, buffer.ptr(), mSampleFormat, block.start, blocklen, true);
437  dest->Append(buffer.ptr(), mSampleFormat, blocklen);
438  }
439  else
440  // Special case of a whole block
441  AppendBlock(pUseFactory, mSampleFormat,
442  dest->mBlock, dest->mNumSamples, block);
443  // Increase ref count or duplicate file
444  }
445 
446  dest->ConsistencyCheck(wxT("Sequence::Copy()"));
447 
448  return dest;
449 }
450 
451 namespace {
452  inline bool Overflows(double numSamples)
453  {
454  return numSamples > wxLL(9223372036854775807);
455  }
456 
459  {
460  if ( pFactory ) {
461  // must copy contents to a fresh SampleBlock object in another database
462  auto sampleCount = sb->GetSampleCount();
463  SampleBuffer buffer{ sampleCount, format };
464  sb->GetSamples( buffer.ptr(), format, 0, sampleCount );
465  sb = pFactory->Create( buffer.ptr(), sampleCount, format );
466  }
467  else
468  // Can just share
469  ;
470  return sb;
471  }
472 }
473 
476 {
477  if ((s < 0) || (s > mNumSamples))
478  {
479  wxLogError(
480  wxT("Sequence::Paste: sampleCount s %s is < 0 or > mNumSamples %s)."),
481  // PRL: Why bother with Internat when the above is just wxT?
485  }
486 
487  // Quick check to make sure that it doesn't overflow
489  {
490  wxLogError(
491  wxT("Sequence::Paste: mNumSamples %s + src->mNumSamples %s would overflow."),
492  // PRL: Why bother with Internat when the above is just wxT?
496  }
497 
498  if (src->mSampleFormat != mSampleFormat)
499  {
500  wxLogError(
501  wxT("Sequence::Paste: Sample format to be pasted, %s, does not match destination format, %s."),
505  }
506 
507  const BlockArray &srcBlock = src->mBlock;
508  auto addedLen = src->mNumSamples;
509  const unsigned int srcNumBlocks = srcBlock.size();
510  auto sampleSize = SAMPLE_SIZE(mSampleFormat);
511 
512  if (addedLen == 0 || srcNumBlocks == 0)
513  return;
514 
515  const size_t numBlocks = mBlock.size();
516 
517  // Decide whether to share sample blocks or make new copies, when whole block
518  // contents are used -- must copy if factories are different:
519  auto pUseFactory =
520  (src->mpFactory == mpFactory) ? nullptr : mpFactory.get();
521 
522  if (numBlocks == 0 ||
523  (s == mNumSamples && mBlock.back().sb->GetSampleCount() >= mMinSamples)) {
524  // Special case: this track is currently empty, or it's safe to append
525  // onto the end because the current last block is longer than the
526  // minimum size
527 
528  // Build and swap a copy so there is a strong exception safety guarantee
529  BlockArray newBlock{ mBlock };
530  sampleCount samples = mNumSamples;
531  for (unsigned int i = 0; i < srcNumBlocks; i++)
532  // AppendBlock may throw for limited disk space, if pasting from
533  // one project into another.
534  AppendBlock(pUseFactory, mSampleFormat,
535  newBlock, samples, srcBlock[i]);
536 
538  (newBlock, samples, wxT("Paste branch one"));
539  return;
540  }
541 
542  const int b = (s == mNumSamples) ? mBlock.size() - 1 : FindBlock(s);
543  wxASSERT((b >= 0) && (b < (int)numBlocks));
544  SeqBlock *const pBlock = &mBlock[b];
545  const auto length = pBlock->sb->GetSampleCount();
546  const auto largerBlockLen = addedLen + length;
547  // PRL: when insertion point is the first sample of a block,
548  // and the following test fails, perhaps we could test
549  // whether coalescence with the previous block is possible.
550  if (largerBlockLen <= mMaxSamples) {
551  // Special case: we can fit all of the NEW samples inside of
552  // one block!
553 
554  SeqBlock &block = *pBlock;
555  // largerBlockLen is not more than mMaxSamples...
556  SampleBuffer buffer(largerBlockLen.as_size_t(), mSampleFormat);
557 
558  // ...and addedLen is not more than largerBlockLen
559  auto sAddedLen = addedLen.as_size_t();
560  // s lies within block:
561  auto splitPoint = ( s - block.start ).as_size_t();
562  Read(buffer.ptr(), mSampleFormat, block, 0, splitPoint, true);
563  src->Get(0, buffer.ptr() + splitPoint*sampleSize,
564  mSampleFormat, 0, sAddedLen, true);
565  Read(buffer.ptr() + (splitPoint + sAddedLen) * sampleSize,
566  mSampleFormat, block,
567  splitPoint, length - splitPoint, true);
568 
569  // largerBlockLen is not more than mMaxSamples...
570  block.sb = mpFactory->Create(
571  buffer.ptr(),
572  largerBlockLen.as_size_t(),
573  mSampleFormat);
574 
575  // Don't make a duplicate array. We can still give Strong-guarantee
576  // if we modify only one block in place.
577 
578  // use No-fail-guarantee in remaining steps
579  for (unsigned int i = b + 1; i < numBlocks; i++)
580  mBlock[i].start += addedLen;
581 
582  mNumSamples += addedLen;
583 
584  // This consistency check won't throw, it asserts.
585  // Proof that we kept consistency is not hard.
586  ConsistencyCheck(wxT("Paste branch two"), false);
587  return;
588  }
589 
590  // Case three: if we are inserting four or fewer blocks,
591  // it's simplest to just lump all the data together
592  // into one big block along with the split block,
593  // then resplit it all
594  BlockArray newBlock;
595  newBlock.reserve(numBlocks + srcNumBlocks + 2);
596  newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b);
597 
598  SeqBlock &splitBlock = mBlock[b];
599  auto splitLen = splitBlock.sb->GetSampleCount();
600  // s lies within splitBlock
601  auto splitPoint = ( s - splitBlock.start ).as_size_t();
602 
603  unsigned int i;
604  if (srcNumBlocks <= 4) {
605 
606  // addedLen is at most four times maximum block size
607  auto sAddedLen = addedLen.as_size_t();
608  const auto sum = splitLen + sAddedLen;
609 
610  SampleBuffer sumBuffer(sum, mSampleFormat);
611  Read(sumBuffer.ptr(), mSampleFormat, splitBlock, 0, splitPoint, true);
612  src->Get(0, sumBuffer.ptr() + splitPoint * sampleSize,
614  0, sAddedLen, true);
615  Read(sumBuffer.ptr() + (splitPoint + sAddedLen) * sampleSize, mSampleFormat,
616  splitBlock, splitPoint,
617  splitLen - splitPoint, true);
618 
620  newBlock, splitBlock.start, sumBuffer.ptr(), sum);
621  } else {
622 
623  // The final case is that we're inserting at least five blocks.
624  // We divide these into three groups: the first two get merged
625  // with the first half of the split block, the middle ones get
626  // used whole, and the last two get merged with the last
627  // half of the split block.
628 
629  const auto srcFirstTwoLen =
630  srcBlock[0].sb->GetSampleCount() + srcBlock[1].sb->GetSampleCount();
631  const auto leftLen = splitPoint + srcFirstTwoLen;
632 
633  const SeqBlock &penultimate = srcBlock[srcNumBlocks - 2];
634  const auto srcLastTwoLen =
635  penultimate.sb->GetSampleCount() +
636  srcBlock[srcNumBlocks - 1].sb->GetSampleCount();
637  const auto rightSplit = splitBlock.sb->GetSampleCount() - splitPoint;
638  const auto rightLen = rightSplit + srcLastTwoLen;
639 
640  SampleBuffer sampleBuffer(std::max(leftLen, rightLen), mSampleFormat);
641 
642  Read(sampleBuffer.ptr(), mSampleFormat, splitBlock, 0, splitPoint, true);
643  src->Get(0, sampleBuffer.ptr() + splitPoint*sampleSize,
644  mSampleFormat, 0, srcFirstTwoLen, true);
645 
647  newBlock, splitBlock.start, sampleBuffer.ptr(), leftLen);
648 
649  for (i = 2; i < srcNumBlocks - 2; i++) {
650  const SeqBlock &block = srcBlock[i];
651  auto sb = ShareOrCopySampleBlock(
652  pUseFactory, mSampleFormat, block.sb );
653  newBlock.push_back(SeqBlock(sb, block.start + s));
654  }
655 
656  auto lastStart = penultimate.start;
657  src->Get(srcNumBlocks - 2, sampleBuffer.ptr(), mSampleFormat,
658  lastStart, srcLastTwoLen, true);
659  Read(sampleBuffer.ptr() + srcLastTwoLen * sampleSize, mSampleFormat,
660  splitBlock, splitPoint, rightSplit, true);
661 
663  newBlock, s + lastStart, sampleBuffer.ptr(), rightLen);
664  }
665 
666  // Copy remaining blocks to NEW block array and
667  // swap the NEW block array in for the old
668  for (i = b + 1; i < numBlocks; i++)
669  newBlock.push_back(mBlock[i].Plus(addedLen));
670 
672  (newBlock, mNumSamples + addedLen, wxT("Paste branch three"));
673 }
674 
677 {
678  SetSamples(NULL, mSampleFormat, s0, len);
679 }
680 
683 {
684  auto &factory = *mpFactory;
685 
686  // Quick check to make sure that it doesn't overflow
687  if (Overflows((mNumSamples.as_double()) + (len.as_double())))
689 
690  if (len <= 0)
691  return;
692 
693  // Create a NEW track containing as much silence as we
694  // need to insert, and then call Paste to do the insertion.
695 
697 
698  auto idealSamples = GetIdealBlockSize();
699 
700  sampleCount pos = 0;
701 
702  // Could nBlocks overflow a size_t? Not very likely. You need perhaps
703  // 2 ^ 52 samples which is over 3000 years at 44.1 kHz.
704  auto nBlocks = (len + idealSamples - 1) / idealSamples;
705  sTrack.mBlock.reserve(nBlocks.as_size_t());
706 
707  if (len >= idealSamples) {
708  auto silentFile = factory.CreateSilent(
709  idealSamples,
710  mSampleFormat);
711  while (len >= idealSamples) {
712  sTrack.mBlock.push_back(SeqBlock(silentFile, pos));
713 
714  pos += idealSamples;
715  len -= idealSamples;
716  }
717  }
718  if (len != 0) {
719  // len is not more than idealSamples:
720  sTrack.mBlock.push_back(SeqBlock(
721  factory.CreateSilent(len.as_size_t(), mSampleFormat), pos));
722  pos += len;
723  }
724 
725  sTrack.mNumSamples = pos;
726 
727  // use Strong-guarantee
728  Paste(s0, &sTrack);
729 }
730 
732  BlockArray &mBlock, sampleCount &mNumSamples, const SeqBlock &b)
733 {
734  // Quick check to make sure that it doesn't overflow
735  if (Overflows((mNumSamples.as_double()) + ((double)b.sb->GetSampleCount())))
737 
738  auto sb = ShareOrCopySampleBlock( pFactory, format, b.sb );
739  SeqBlock newBlock(sb, mNumSamples);
740 
741  // We can assume newBlock.sb is not null
742 
743  mBlock.push_back(newBlock);
744  mNumSamples += newBlock.sb->GetSampleCount();
745 
746  // Don't do a consistency check here because this
747  // function gets called in an inner loop.
748 }
749 
751 {
752  int b = FindBlock(position);
753  return mBlock[b].start;
754 }
755 
757 {
758  // This method returns a nice number of samples you should try to grab in
759  // one big chunk in order to land on a block boundary, based on the starting
760  // sample. The value returned will always be nonzero and will be no larger
761  // than the value of GetMaxBlockSize()
762 
763  if (start < 0 || start >= mNumSamples)
764  return mMaxSamples;
765 
766  int b = FindBlock(start);
767  int numBlocks = mBlock.size();
768 
769  const SeqBlock &block = mBlock[b];
770  // start is in block:
771  auto result = (block.start + block.sb->GetSampleCount() - start).as_size_t();
772 
773  decltype(result) length;
774  while(result < mMinSamples && b+1<numBlocks &&
775  ((length = mBlock[b+1].sb->GetSampleCount()) + result) <= mMaxSamples) {
776  b++;
777  result += length;
778  }
779 
780  wxASSERT(result > 0 && result <= mMaxSamples);
781 
782  return result;
783 }
784 
785 bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
786 {
787  auto &factory = *mpFactory;
788 
789  /* handle waveblock tag and its attributes */
790  if (!wxStrcmp(tag, wxT("waveblock")))
791  {
792  SeqBlock wb;
793 
794  // Give SampleBlock a go at the attributes first
795  wb.sb = factory.CreateFromXML(mSampleFormat, attrs);
796  if (wb.sb == nullptr)
797  {
798  mErrorOpening = true;
799  return false;
800  }
801 
802  // loop through attrs, which is a null-terminated list of attribute-value pairs
803  while(*attrs)
804  {
805  const wxChar *attr = *attrs++;
806  const wxChar *value = *attrs++;
807 
808  if (!value)
809  {
810  break;
811  }
812 
813  long long nValue = 0;
814 
815  const wxString strValue = value; // promote string, we need this for all
816 
817  if (wxStrcmp(attr, wxT("start")) == 0)
818  {
819  // This attribute is a sample offset, so can be 64bit
820  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
821  {
822  mErrorOpening = true;
823  return false;
824  }
825 
826  wb.start = nValue;
827  }
828  }
829 
830  mBlock.push_back(wb);
831 
832  return true;
833  }
834 
835  /* handle sequence tag and its attributes */
836  if (!wxStrcmp(tag, wxT("sequence")))
837  {
838  while(*attrs)
839  {
840  const wxChar *attr = *attrs++;
841  const wxChar *value = *attrs++;
842 
843  if (!value)
844  {
845  break;
846  }
847 
848  long long nValue = 0;
849 
850  const wxString strValue = value; // promote string, we need this for all
851 
852  if (!wxStrcmp(attr, wxT("maxsamples")))
853  {
854  // This attribute is a sample count, so can be 64bit
855  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
856  {
857  mErrorOpening = true;
858  return false;
859  }
860 
861  // Dominic, 12/10/2006:
862  // Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024
863  // - that's a pretty wide range of reasonable values.
864  if ((nValue < 1024) || (nValue > 64 * 1024 * 1024))
865  {
866  mErrorOpening = true;
867  return false;
868  }
869 
870  // nValue is now safe for size_t
871  mMaxSamples = nValue;
872  }
873  else if (!wxStrcmp(attr, wxT("sampleformat")))
874  {
875  // This attribute is a sample format, normal int
876  long fValue;
877  if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&fValue) || (fValue < 0) || !IsValidSampleFormat(fValue))
878  {
879  mErrorOpening = true;
880  return false;
881  }
882  mSampleFormat = (sampleFormat)fValue;
883  }
884  else if (!wxStrcmp(attr, wxT("numsamples")))
885  {
886  // This attribute is a sample count, so can be 64bit
887  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
888  {
889  mErrorOpening = true;
890  return false;
891  }
892  mNumSamples = nValue;
893  }
894  } // while
895 
896  return true;
897  }
898 
899  return false;
900 }
901 
902 void Sequence::HandleXMLEndTag(const wxChar *tag)
903 {
904  if (wxStrcmp(tag, wxT("sequence")) != 0)
905  {
906  return;
907  }
908 
909  // Make sure that the sequence is valid.
910 
911  // Make sure that start times and lengths are consistent
912  sampleCount numSamples = 0;
913  for (unsigned b = 0, nn = mBlock.size(); b < nn; b++)
914  {
915  SeqBlock &block = mBlock[b];
916  if (block.start != numSamples)
917  {
918  wxLogWarning(
919  wxT("Gap detected in project file.\n")
920  wxT(" Start (%s) for block file %lld is not one sample past end of previous block (%s).\n")
921  wxT(" Moving start so blocks are contiguous."),
922  // PRL: Why bother with Internat when the above is just wxT?
923  Internat::ToString(block.start.as_double(), 0),
924  block.sb->GetBlockID(),
925  Internat::ToString(numSamples.as_double(), 0));
926  block.start = numSamples;
927  mErrorOpening = true;
928  }
929  numSamples += block.sb->GetSampleCount();
930  }
931 
932  if (mNumSamples != numSamples)
933  {
934  wxLogWarning(
935  wxT("Gap detected in project file. Correcting sequence sample count from %s to %s."),
936  // PRL: Why bother with Internat when the above is just wxT?
938  Internat::ToString(numSamples.as_double(), 0));
939  mNumSamples = numSamples;
940  mErrorOpening = true;
941  }
942 }
943 
945 {
946  if (!wxStrcmp(tag, wxT("waveblock")))
947  {
948  return this;
949  }
950 
951  return nullptr;
952 }
953 
954 // Throws exceptions rather than reporting errors.
955 void Sequence::WriteXML(XMLWriter &xmlFile) const
956 // may throw
957 {
958  unsigned int b;
959 
960  xmlFile.StartTag(wxT("sequence"));
961 
962  xmlFile.WriteAttr(wxT("maxsamples"), mMaxSamples);
963  xmlFile.WriteAttr(wxT("sampleformat"), (size_t)mSampleFormat);
964  xmlFile.WriteAttr(wxT("numsamples"), mNumSamples.as_long_long() );
965 
966  for (b = 0; b < mBlock.size(); b++) {
967  const SeqBlock &bb = mBlock[b];
968 
969  // See http://bugzilla.audacityteam.org/show_bug.cgi?id=451.
970  if (bb.sb->GetSampleCount() > mMaxSamples)
971  {
972  // PRL: Bill observed this error. Not sure how it was caused.
973  // I have added code in ConsistencyCheck that should abort the
974  // editing operation that caused this, not fixing
975  // the problem but moving the point of detection earlier if we
976  // find a reproducible case.
977  auto sMsg =
978  XO("Sequence has block file exceeding maximum %s samples per block.\nTruncating to this maximum length.")
979  .Format( Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0) );
981  sMsg,
982  XO("Warning - Truncating Overlong Block File"),
983  wxICON_EXCLAMATION | wxOK);
984  wxLogWarning(sMsg.Translation()); //Debug?
985 // bb.sb->SetLength(mMaxSamples);
986  }
987 
988  xmlFile.StartTag(wxT("waveblock"));
989  xmlFile.WriteAttr(wxT("start"), bb.start.as_long_long() );
990 
991  bb.sb->SaveXML(xmlFile);
992 
993  xmlFile.EndTag(wxT("waveblock"));
994  }
995 
996  xmlFile.EndTag(wxT("sequence"));
997 }
998 
1000 {
1001  wxASSERT(pos >= 0 && pos < mNumSamples);
1002 
1003  if (pos == 0)
1004  return 0;
1005 
1006  int numBlocks = mBlock.size();
1007 
1008  size_t lo = 0, hi = numBlocks, guess;
1009  sampleCount loSamples = 0, hiSamples = mNumSamples;
1010 
1011  while (true) {
1012  //this is not a binary search, but a
1013  //dictionary search where we guess something smarter than the binary division
1014  //of the unsearched area, since samples are usually proportional to block file number.
1015  const double frac = (pos - loSamples).as_double() /
1016  (hiSamples - loSamples).as_double();
1017  guess = std::min(hi - 1, lo + size_t(frac * (hi - lo)));
1018  const SeqBlock &block = mBlock[guess];
1019 
1020  wxASSERT(block.sb->GetSampleCount() > 0);
1021  wxASSERT(lo <= guess && guess < hi && lo < hi);
1022 
1023  if (pos < block.start) {
1024  wxASSERT(lo != guess);
1025  hi = guess;
1026  hiSamples = block.start;
1027  }
1028  else {
1029  const sampleCount nextStart = block.start + block.sb->GetSampleCount();
1030  if (pos < nextStart)
1031  break;
1032  else {
1033  wxASSERT(guess < hi - 1);
1034  lo = guess + 1;
1035  loSamples = nextStart;
1036  }
1037  }
1038  }
1039 
1040  const int rval = guess;
1041  wxASSERT(rval >= 0 && rval < numBlocks &&
1042  pos >= mBlock[rval].start &&
1043  pos < mBlock[rval].start + mBlock[rval].sb->GetSampleCount());
1044 
1045  return rval;
1046 }
1047 
1048 //static
1050  const SeqBlock &b, size_t blockRelativeStart, size_t len,
1051  bool mayThrow)
1052 {
1053  const auto &sb = b.sb;
1054 
1055  wxASSERT(blockRelativeStart + len <= sb->GetSampleCount());
1056 
1057  // Either throws, or of !mayThrow, tells how many were really read
1058  auto result = sb->GetSamples(buffer, format, blockRelativeStart, len, mayThrow);
1059 
1060  if (result != len)
1061  {
1062  wxLogWarning(wxT("Expected to read %ld samples, got %ld samples."),
1063  len, result);
1064  return false;
1065  }
1066 
1067  return true;
1068 }
1069 
1071  sampleCount start, size_t len, bool mayThrow) const
1072 {
1073  if (start == mNumSamples) {
1074  return len == 0;
1075  }
1076 
1077  if (start < 0 || start + len > mNumSamples) {
1078  if (mayThrow)
1080  ClearSamples( buffer, floatSample, 0, len );
1081  return false;
1082  }
1083  int b = FindBlock(start);
1084 
1085  return Get(b, buffer, format, start, len, mayThrow);
1086 }
1087 
1089  sampleCount start, size_t len, bool mayThrow) const
1090 {
1091  bool result = true;
1092  while (len) {
1093  const SeqBlock &block = mBlock[b];
1094  // start is in block
1095  const auto bstart = (start - block.start).as_size_t();
1096  // bstart is not more than block length
1097  const auto blen = std::min(len, block.sb->GetSampleCount() - bstart);
1098 
1099  if (! Read(buffer, format, block, bstart, blen, mayThrow) )
1100  result = false;
1101 
1102  len -= blen;
1103  buffer += (blen * SAMPLE_SIZE(format));
1104  b++;
1105  start += blen;
1106  }
1107  return result;
1108 }
1109 
1110 // Pass NULL to set silence
1113  sampleCount start, sampleCount len)
1114 {
1115  auto &factory = *mpFactory;
1116 
1117  const auto size = mBlock.size();
1118 
1119  if (start < 0 || start + len > mNumSamples)
1121 
1122  size_t tempSize = mMaxSamples;
1123  // to do: allocate this only on demand
1124  SampleBuffer scratch(tempSize, mSampleFormat);
1125 
1126  SampleBuffer temp;
1127  if (buffer && format != mSampleFormat) {
1128  temp.Allocate(tempSize, mSampleFormat);
1129  }
1130 
1131  int b = FindBlock(start);
1132  BlockArray newBlock;
1133  std::copy( mBlock.begin(), mBlock.begin() + b, std::back_inserter(newBlock) );
1134 
1135  while (len > 0
1136  // Redundant termination condition,
1137  // but it guards against infinite loop in case of inconsistencies
1138  // (too-small files, not yet seen?)
1139  // that cause the loop to make no progress because blen == 0
1140  && b < (int)size
1141  ) {
1142  newBlock.push_back( mBlock[b] );
1143  SeqBlock &block = newBlock.back();
1144  // start is within block
1145  const auto bstart = ( start - block.start ).as_size_t();
1146  const auto fileLength = block.sb->GetSampleCount();
1147 
1148  // the std::min is a guard against inconsistent Sequence
1149  const auto blen =
1150  limitSampleBufferSize( fileLength - std::min( bstart, fileLength ),
1151  len );
1152  wxASSERT(blen == 0 || bstart + blen <= fileLength);
1153 
1154 #if 0
1155  // PRL: This inconsistency (too-big file) has been seen in "the wild"
1156  // in 2.2.0. It is the least problematic kind of inconsistency.
1157  // We will tolerate it for 2.2.1.
1158  // Not known whether it is only in projects saved in earlier versions.
1159  // After 2.2.1, we should detect and correct it at file loading time.
1160  if (fileLength > mMaxSamples) {
1162  }
1163 #endif
1164 
1165  ensureSampleBufferSize(scratch, mSampleFormat, tempSize, fileLength,
1166  &temp);
1167 
1168  auto useBuffer = buffer;
1169  if (buffer && format != mSampleFormat)
1170  {
1171  // To do: remove the extra movement.
1172  // Note: we ensured temp can hold fileLength. blen is not more
1173  CopySamples(buffer, format, temp.ptr(), mSampleFormat, blen);
1174  useBuffer = temp.ptr();
1175  }
1176 
1177  // We don't ever write to an existing block; to support Undo,
1178  // we copy the old block entirely into memory, dereference it,
1179  // make the change, and then write the NEW block to disk.
1180 
1181  if ( bstart > 0 || blen < fileLength ) {
1182  // First or last block is only partially overwritten
1183  Read(scratch.ptr(), mSampleFormat, block, 0, fileLength, true);
1184 
1185  if (useBuffer) {
1186  auto sampleSize = SAMPLE_SIZE(mSampleFormat);
1187  memcpy(scratch.ptr() +
1188  bstart * sampleSize, useBuffer, blen * sampleSize);
1189  }
1190  else
1191  ClearSamples(scratch.ptr(), mSampleFormat, bstart, blen);
1192 
1193  block.sb = factory.Create(
1194  scratch.ptr(),
1195  fileLength,
1196  mSampleFormat);
1197  }
1198  else {
1199  // Avoid reading the disk when the replacement is total
1200  if (useBuffer)
1201  block.sb = factory.Create(useBuffer, fileLength, mSampleFormat);
1202  else
1203  block.sb = factory.CreateSilent(fileLength, mSampleFormat);
1204  }
1205 
1206  // blen might be zero for inconsistent Sequence...
1207  if( buffer )
1208  buffer += (blen * SAMPLE_SIZE(format));
1209 
1210  len -= blen;
1211  start += blen;
1212 
1213  // ... but this, at least, always guarantees some loop progress:
1214  b++;
1215  }
1216 
1217  std::copy( mBlock.begin() + b, mBlock.end(), std::back_inserter(newBlock) );
1218 
1219  CommitChangesIfConsistent( newBlock, mNumSamples, wxT("SetSamples") );
1220 }
1221 
1222 namespace {
1223 
1225 {
1226  MinMaxSumsq(const float *pv, int count, int divisor)
1227  {
1228  min = FLT_MAX, max = -FLT_MAX, sumsq = 0.0f;
1229  while (count--) {
1230  float v;
1231  switch (divisor) {
1232  default:
1233  case 1:
1234  // array holds samples
1235  v = *pv++;
1236  if (v < min)
1237  min = v;
1238  if (v > max)
1239  max = v;
1240  sumsq += v * v;
1241  break;
1242  case 256:
1243  case 65536:
1244  // array holds triples of min, max, and rms values
1245  v = *pv++;
1246  if (v < min)
1247  min = v;
1248  v = *pv++;
1249  if (v > max)
1250  max = v;
1251  v = *pv++;
1252  sumsq += v * v;
1253  break;
1254  }
1255  }
1256  }
1257 
1258  float min;
1259  float max;
1260  float sumsq;
1261 };
1262 
1263 }
1264 
1265 bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl,
1266  size_t len, const sampleCount *where) const
1267 {
1268  wxASSERT(len > 0);
1269  const auto s0 = std::max(sampleCount(0), where[0]);
1270  if (s0 >= mNumSamples)
1271  // None of the samples asked for are in range. Abandon.
1272  return false;
1273 
1274  // In case where[len - 1] == where[len], raise the limit by one,
1275  // so we load at least one pixel for column len - 1
1276  // ... unless the mNumSamples ceiling applies, and then there are other defenses
1277  const auto s1 =
1278  std::min(mNumSamples, std::max(1 + where[len - 1], where[len]));
1279  Floats temp{ mMaxSamples };
1280 
1281  decltype(len) pixel = 0;
1282 
1283  auto srcX = s0;
1284  decltype(srcX) nextSrcX = 0;
1285  int lastRmsDenom = 0;
1286  int lastDivisor = 0;
1287  auto whereNow = std::min(s1 - 1, where[0]);
1288  decltype(whereNow) whereNext = 0;
1289  // Loop over block files, opening and reading and closing each
1290  // not more than once
1291  unsigned nBlocks = mBlock.size();
1292  const unsigned int block0 = FindBlock(s0);
1293  for (unsigned int b = block0; b < nBlocks; ++b) {
1294  if (b > block0)
1295  srcX = nextSrcX;
1296  if (srcX >= s1)
1297  break;
1298 
1299  // Find the range of sample values for this block that
1300  // are in the display.
1301  const SeqBlock &seqBlock = mBlock[b];
1302  const auto start = seqBlock.start;
1303  nextSrcX = std::min(s1, start + seqBlock.sb->GetSampleCount());
1304 
1305  // The column for pixel p covers samples from
1306  // where[p] up to but excluding where[p + 1].
1307 
1308  // Find the range of pixels covered by the current block file
1309  // (Their starting samples covered by it, to be exact)
1310  decltype(len) nextPixel;
1311  if (nextSrcX >= s1)
1312  // last pass
1313  nextPixel = len;
1314  else {
1315  nextPixel = pixel;
1316  // Taking min with s1 - 1, here and elsewhere, is another defense
1317  // to be sure the last pixel column gets at least one sample
1318  while (nextPixel < len &&
1319  (whereNext = std::min(s1 - 1, where[nextPixel])) < nextSrcX)
1320  ++nextPixel;
1321  }
1322  if (nextPixel == pixel)
1323  // The entire block's samples fall within one pixel column.
1324  // Either it's a rare odd block at the end, or else,
1325  // we must be really zoomed out!
1326  // Omit the entire block's contents from min/max/rms
1327  // calculation, which is not correct, but correctness might not
1328  // be worth the compute time if this happens every pixel
1329  // column. -- PRL
1330  continue;
1331  if (nextPixel == len)
1332  whereNext = s1;
1333 
1334  // Decide the summary level
1335  const double samplesPerPixel =
1336  (whereNext - whereNow).as_double() / (nextPixel - pixel);
1337  const int divisor =
1338  (samplesPerPixel >= 65536) ? 65536
1339  : (samplesPerPixel >= 256) ? 256
1340  : 1;
1341 
1342  int blockStatus = b;
1343 
1344  // How many samples or triples are needed?
1345 
1346  const size_t startPosition =
1347  // srcX and start are in the same block
1348  std::max(sampleCount(0), (srcX - start) / divisor).as_size_t();
1349  const size_t inclusiveEndPosition =
1350  // nextSrcX - 1 and start are in the same block
1351  std::min((sampleCount(mMaxSamples) / divisor) - 1,
1352  (nextSrcX - 1 - start) / divisor).as_size_t();
1353  const auto num = 1 + inclusiveEndPosition - startPosition;
1354  if (num <= 0) {
1355  // What? There was a zero length block file?
1356  wxASSERT(false);
1357  // Do some defense against this case anyway
1358  while (pixel < nextPixel) {
1359  min[pixel] = max[pixel] = rms[pixel] = 0;
1360  bl[pixel] = blockStatus;//MC
1361  ++pixel;
1362  }
1363  continue;
1364  }
1365 
1366  // Read from the block file or its summary
1367  switch (divisor) {
1368  default:
1369  case 1:
1370  // Read samples
1371  // no-throw for display operations!
1372  Read((samplePtr)temp.get(), floatSample, seqBlock, startPosition, num, false);
1373  break;
1374  case 256:
1375  // Read triples
1376  // Ignore the return value.
1377  // This function fills with zeroes if read fails
1378  seqBlock.sb->GetSummary256(temp.get(), startPosition, num);
1379  break;
1380  case 65536:
1381  // Read triples
1382  // Ignore the return value.
1383  // This function fills with zeroes if read fails
1384  seqBlock.sb->GetSummary64k(temp.get(), startPosition, num);
1385  break;
1386  }
1387 
1388  auto filePosition = startPosition;
1389 
1390  // The previous pixel column might straddle blocks.
1391  // If so, impute some of the data to it.
1392  if (b > block0 && pixel > 0) {
1393  // whereNow and start are in the same block
1394  auto midPosition = ((whereNow - start) / divisor).as_size_t();
1395  int diff(midPosition - filePosition);
1396  if (diff > 0) {
1397  MinMaxSumsq values(temp.get(), diff, divisor);
1398  const int lastPixel = pixel - 1;
1399  float &lastMin = min[lastPixel];
1400  lastMin = std::min(lastMin, values.min);
1401  float &lastMax = max[lastPixel];
1402  lastMax = std::max(lastMax, values.max);
1403  float &lastRms = rms[lastPixel];
1404  int lastNumSamples = lastRmsDenom * lastDivisor;
1405  lastRms = sqrt(
1406  (lastRms * lastRms * lastNumSamples + values.sumsq * divisor) /
1407  (lastNumSamples + diff * divisor)
1408  );
1409 
1410  filePosition = midPosition;
1411  }
1412  }
1413 
1414  // Loop over file positions
1415  int rmsDenom = 0;
1416  for (; filePosition <= inclusiveEndPosition;) {
1417  // Find range of pixel columns for this file position
1418  // (normally just one, but maybe more when zoomed very close)
1419  // and the range of positions for those columns
1420  // (normally one or more, for that one column)
1421  auto pixelX = pixel + 1;
1422  decltype(filePosition) positionX = 0;
1423  while (pixelX < nextPixel &&
1424  filePosition ==
1425  (positionX = (
1426  // s1 - 1 or where[pixelX] and start are in the same block
1427  (std::min(s1 - 1, where[pixelX]) - start) / divisor).as_size_t() )
1428  )
1429  ++pixelX;
1430  if (pixelX >= nextPixel)
1431  positionX = 1 + inclusiveEndPosition;
1432 
1433  // Find results to assign
1434  rmsDenom = (positionX - filePosition);
1435  wxASSERT(rmsDenom > 0);
1436  const float *const pv =
1437  temp.get() + (filePosition - startPosition) * (divisor == 1 ? 1 : 3);
1438  MinMaxSumsq values(pv, std::max(0, rmsDenom), divisor);
1439 
1440  // Assign results
1441  std::fill(&min[pixel], &min[pixelX], values.min);
1442  std::fill(&max[pixel], &max[pixelX], values.max);
1443  std::fill(&bl[pixel], &bl[pixelX], blockStatus);
1444  std::fill(&rms[pixel], &rms[pixelX], (float)sqrt(values.sumsq / rmsDenom));
1445 
1446  pixel = pixelX;
1447  filePosition = positionX;
1448  }
1449 
1450  wxASSERT(pixel == nextPixel);
1451  whereNow = whereNext;
1452  pixel = nextPixel;
1453  lastDivisor = divisor;
1454  lastRmsDenom = rmsDenom;
1455  } // for each block file
1456 
1457  wxASSERT(pixel == len);
1458 
1459  return true;
1460 }
1461 
1463 {
1464  int numBlocks = mBlock.size();
1465  const auto max = GetMaxBlockSize();
1466 
1467  if (numBlocks == 0)
1468  return max;
1469 
1470  const auto lastBlockLen = mBlock.back().sb->GetSampleCount();
1471  if (lastBlockLen >= max)
1472  return max;
1473  else
1474  return max - lastBlockLen;
1475 }
1476 
1479  constSamplePtr buffer, sampleFormat format, size_t len)
1480 {
1481  return DoAppend( buffer, format, len, false );
1482 }
1483 
1486 {
1487  auto len = pBlock->GetSampleCount();
1488 
1489  // Quick check to make sure that it doesn't overflow
1490  if (Overflows(mNumSamples.as_double() + ((double)len)))
1492 
1493  BlockArray newBlock;
1494  newBlock.emplace_back( pBlock, mNumSamples );
1495  auto newNumSamples = mNumSamples + len;
1496 
1497  AppendBlocksIfConsistent(newBlock, false,
1498  newNumSamples, wxT("Append"));
1499 
1500 // JKC: During generate we use Append again and again.
1501 // If generating a long sequence this test would give O(n^2)
1502 // performance - not good!
1503 #ifdef VERY_SLOW_CHECKING
1504  ConsistencyCheck(wxT("Append"));
1505 #endif
1506 }
1507 
1510 {
1511  DoAppend(buffer, format, len, true);
1512 }
1513 
1516  constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
1517 {
1518  SeqBlock::SampleBlockPtr result;
1519 
1520  if (len == 0)
1521  return result;
1522 
1523  auto &factory = *mpFactory;
1524 
1525  // Quick check to make sure that it doesn't overflow
1526  if (Overflows(mNumSamples.as_double() + ((double)len)))
1528 
1529  BlockArray newBlock;
1530  sampleCount newNumSamples = mNumSamples;
1531 
1532  // If the last block is not full, we need to add samples to it
1533  int numBlocks = mBlock.size();
1534  SeqBlock *pLastBlock;
1535  decltype(pLastBlock->sb->GetSampleCount()) length;
1536  size_t bufferSize = mMaxSamples;
1537  SampleBuffer buffer2(bufferSize, mSampleFormat);
1538  bool replaceLast = false;
1539  if (coalesce &&
1540  numBlocks > 0 &&
1541  (length =
1542  (pLastBlock = &mBlock.back())->sb->GetSampleCount()) < mMinSamples) {
1543  // Enlarge a sub-minimum block at the end
1544  const SeqBlock &lastBlock = *pLastBlock;
1545  const auto addLen = std::min(mMaxSamples - length, len);
1546 
1547  Read(buffer2.ptr(), mSampleFormat, lastBlock, 0, length, true);
1548 
1549  CopySamples(buffer,
1550  format,
1551  buffer2.ptr() + length * SAMPLE_SIZE(mSampleFormat),
1552  mSampleFormat,
1553  addLen);
1554 
1555  const auto newLastBlockLen = length + addLen;
1556  SampleBlockPtr pBlock = factory.Create(
1557  buffer2.ptr(),
1558  newLastBlockLen,
1559  mSampleFormat);
1560  SeqBlock newLastBlock(pBlock, lastBlock.start);
1561 
1562  newBlock.push_back( newLastBlock );
1563 
1564  len -= addLen;
1565  newNumSamples += addLen;
1566  buffer += addLen * SAMPLE_SIZE(format);
1567 
1568  replaceLast = true;
1569  }
1570  // Append the rest as NEW blocks
1571  while (len) {
1572  const auto idealSamples = GetIdealBlockSize();
1573  const auto addedLen = std::min(idealSamples, len);
1574  SampleBlockPtr pBlock;
1575  if (format == mSampleFormat) {
1576  pBlock = factory.Create(buffer, addedLen, mSampleFormat);
1577  // It's expected that when not requesting coalescence, the
1578  // data should fit in one block
1579  wxASSERT( coalesce || !result );
1580  result = pBlock;
1581  }
1582  else {
1583  CopySamples(buffer, format, buffer2.ptr(), mSampleFormat, addedLen);
1584  pBlock = factory.Create(buffer2.ptr(), addedLen, mSampleFormat);
1585  }
1586 
1587  newBlock.push_back(SeqBlock(pBlock, newNumSamples));
1588 
1589  buffer += addedLen * SAMPLE_SIZE(format);
1590  newNumSamples += addedLen;
1591  len -= addedLen;
1592  }
1593 
1594  AppendBlocksIfConsistent(newBlock, replaceLast,
1595  newNumSamples, wxT("Append"));
1596 
1597 // JKC: During generate we use Append again and again.
1598 // If generating a long sequence this test would give O(n^2)
1599 // performance - not good!
1600 #ifdef VERY_SLOW_CHECKING
1601  ConsistencyCheck(wxT("Append"));
1602 #endif
1603 
1604  return result;
1605 }
1606 
1608  size_t mMaxSamples, sampleFormat mSampleFormat,
1609  BlockArray &list, sampleCount start,
1610  constSamplePtr buffer, size_t len)
1611 {
1612  if (len <= 0)
1613  return;
1614 
1615  auto num = (len + (mMaxSamples - 1)) / mMaxSamples;
1616  list.reserve(list.size() + num);
1617 
1618  for (decltype(num) i = 0; i < num; i++) {
1619  SeqBlock b;
1620 
1621  const auto offset = i * len / num;
1622  b.start = start + offset;
1623  int newLen = ((i + 1) * len / num) - offset;
1624  auto bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat));
1625 
1626  b.sb = factory.Create(bufStart, newLen, mSampleFormat);
1627 
1628  list.push_back(b);
1629  }
1630 }
1631 
1634 {
1635  if (len == 0)
1636  return;
1637 
1638  if (len < 0 || start < 0 || start + len > mNumSamples)
1640 
1641  auto &factory = *mpFactory;
1642 
1643  const unsigned int numBlocks = mBlock.size();
1644 
1645  const unsigned int b0 = FindBlock(start);
1646  unsigned int b1 = FindBlock(start + len - 1);
1647 
1648  auto sampleSize = SAMPLE_SIZE(mSampleFormat);
1649 
1650  SeqBlock *pBlock;
1651  decltype(pBlock->sb->GetSampleCount()) length;
1652 
1653  // One buffer for reuse in various branches here
1654  SampleBuffer scratch;
1655  // The maximum size that should ever be needed
1656  auto scratchSize = mMaxSamples + mMinSamples;
1657 
1658  // Special case: if the samples to DELETE are all within a single
1659  // block and the resulting length is not too small, perform the
1660  // deletion within this block:
1661  if (b0 == b1 &&
1662  (length = (pBlock = &mBlock[b0])->sb->GetSampleCount()) - len >= mMinSamples) {
1663  SeqBlock &b = *pBlock;
1664  // start is within block
1665  auto pos = ( start - b.start ).as_size_t();
1666 
1667  // Guard against failure of this anyway below with limitSampleBufferSize
1668  wxASSERT(len < length);
1669 
1670  // len must be less than length
1671  // because start + len - 1 is also in the block...
1672  auto newLen = ( length - limitSampleBufferSize( length, len ) );
1673 
1674  scratch.Allocate(scratchSize, mSampleFormat);
1675  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize, newLen);
1676 
1677  Read(scratch.ptr(), mSampleFormat, b, 0, pos, true);
1678  Read(scratch.ptr() + (pos * sampleSize), mSampleFormat,
1679  b,
1680  // ... and therefore pos + len
1681  // is not more than the length of the block
1682  ( pos + len ).as_size_t(), newLen - pos, true);
1683 
1684  b.sb = factory.Create(scratch.ptr(), newLen, mSampleFormat);
1685 
1686  // Don't make a duplicate array. We can still give Strong-guarantee
1687  // if we modify only one block in place.
1688 
1689  // use No-fail-guarantee in remaining steps
1690 
1691  for (unsigned int j = b0 + 1; j < numBlocks; j++)
1692  mBlock[j].start -= len;
1693 
1694  mNumSamples -= len;
1695 
1696  // This consistency check won't throw, it asserts.
1697  // Proof that we kept consistency is not hard.
1698  ConsistencyCheck(wxT("Delete - branch one"), false);
1699  return;
1700  }
1701 
1702  // Create a NEW array of blocks
1703  BlockArray newBlock;
1704  newBlock.reserve(numBlocks - (b1 - b0) + 2);
1705 
1706  // Copy the blocks before the deletion point over to
1707  // the NEW array
1708  newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b0);
1709  unsigned int i;
1710 
1711  // First grab the samples in block b0 before the deletion point
1712  // into preBuffer. If this is enough samples for its own block,
1713  // or if this would be the first block in the array, write it out.
1714  // Otherwise combine it with the previous block (splitting them
1715  // 50/50 if necessary).
1716  const SeqBlock &preBlock = mBlock[b0];
1717  // start is within preBlock
1718  auto preBufferLen = ( start - preBlock.start ).as_size_t();
1719  if (preBufferLen) {
1720  if (preBufferLen >= mMinSamples || b0 == 0) {
1721  if (!scratch.ptr())
1722  scratch.Allocate(scratchSize, mSampleFormat);
1723  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize, preBufferLen);
1724  Read(scratch.ptr(), mSampleFormat, preBlock, 0, preBufferLen, true);
1725  auto pFile =
1726  factory.Create(scratch.ptr(), preBufferLen, mSampleFormat);
1727 
1728  newBlock.push_back(SeqBlock(pFile, preBlock.start));
1729  } else {
1730  const SeqBlock &prepreBlock = mBlock[b0 - 1];
1731  const auto prepreLen = prepreBlock.sb->GetSampleCount();
1732  const auto sum = prepreLen + preBufferLen;
1733 
1734  if (!scratch.ptr())
1735  scratch.Allocate(scratchSize, mSampleFormat);
1736  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize,
1737  sum);
1738 
1739  Read(scratch.ptr(), mSampleFormat, prepreBlock, 0, prepreLen, true);
1740  Read(scratch.ptr() + prepreLen*sampleSize, mSampleFormat,
1741  preBlock, 0, preBufferLen, true);
1742 
1743  newBlock.pop_back();
1745  newBlock, prepreBlock.start, scratch.ptr(), sum);
1746  }
1747  }
1748  else {
1749  // The sample where we begin deletion happens to fall
1750  // right on the beginning of a block.
1751  }
1752 
1753  // Now, symmetrically, grab the samples in block b1 after the
1754  // deletion point into postBuffer. If this is enough samples
1755  // for its own block, or if this would be the last block in
1756  // the array, write it out. Otherwise combine it with the
1757  // subsequent block (splitting them 50/50 if necessary).
1758  const SeqBlock &postBlock = mBlock[b1];
1759  // start + len - 1 lies within postBlock
1760  const auto postBufferLen = (
1761  (postBlock.start + postBlock.sb->GetSampleCount()) - (start + len)
1762  ).as_size_t();
1763  if (postBufferLen) {
1764  if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) {
1765  if (!scratch.ptr())
1766  // Last use of scratch, can ask for smaller
1767  scratch.Allocate(postBufferLen, mSampleFormat);
1768  // start + len - 1 lies within postBlock
1769  auto pos = (start + len - postBlock.start).as_size_t();
1770  Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen, true);
1771  auto file =
1772  factory.Create(scratch.ptr(), postBufferLen, mSampleFormat);
1773 
1774  newBlock.push_back(SeqBlock(file, start));
1775  } else {
1776  SeqBlock &postpostBlock = mBlock[b1 + 1];
1777  const auto postpostLen = postpostBlock.sb->GetSampleCount();
1778  const auto sum = postpostLen + postBufferLen;
1779 
1780  if (!scratch.ptr())
1781  // Last use of scratch, can ask for smaller
1782  scratch.Allocate(sum, mSampleFormat);
1783  // start + len - 1 lies within postBlock
1784  auto pos = (start + len - postBlock.start).as_size_t();
1785  Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen, true);
1786  Read(scratch.ptr() + (postBufferLen * sampleSize), mSampleFormat,
1787  postpostBlock, 0, postpostLen, true);
1788 
1790  newBlock, start, scratch.ptr(), sum);
1791  b1++;
1792  }
1793  }
1794  else {
1795  // The sample where we begin deletion happens to fall
1796  // right on the end of a block.
1797  }
1798 
1799  // Copy the remaining blocks over from the old array
1800  for (i = b1 + 1; i < numBlocks; i++)
1801  newBlock.push_back(mBlock[i].Plus(-len));
1802 
1804  (newBlock, mNumSamples - len, wxT("Delete - branch two"));
1805 }
1806 
1807 void Sequence::ConsistencyCheck(const wxChar *whereStr, bool mayThrow) const
1808 {
1809  ConsistencyCheck(mBlock, mMaxSamples, 0, mNumSamples, whereStr, mayThrow);
1810 }
1811 
1813  (const BlockArray &mBlock, size_t maxSamples, size_t from,
1814  sampleCount mNumSamples, const wxChar *whereStr,
1815  bool WXUNUSED(mayThrow))
1816 {
1817  // Construction of the exception at the appropriate line of the function
1818  // gives a little more discrimination
1820 
1821  unsigned int numBlocks = mBlock.size();
1822 
1823  unsigned int i;
1824  sampleCount pos = from < numBlocks ? mBlock[from].start : mNumSamples;
1825  if ( from == 0 && pos != 0 )
1827 
1828  for (i = from; !ex && i < numBlocks; i++) {
1829  const SeqBlock &seqBlock = mBlock[i];
1830  if (pos != seqBlock.start)
1832 
1833  if ( seqBlock.sb ) {
1834  const auto length = seqBlock.sb->GetSampleCount();
1835  if (length > maxSamples)
1837  pos += length;
1838  }
1839  else
1841  }
1842  if ( !ex && pos != mNumSamples )
1844 
1845  if ( ex )
1846  {
1847  wxLogError(wxT("*** Consistency check failed at %d after %s. ***"),
1848  ex->GetLine(), whereStr);
1849  wxString str;
1851  wxLogError(wxT("%s"), str);
1852  wxLogError(wxT("*** Please report this error to https://forum.audacityteam.org/. ***\n\n")
1853  wxT("Recommended course of action:\n")
1854  wxT("Undo the failed operation(s), then export or save your work and quit."));
1855 
1856  //if (mayThrow)
1857  //throw *ex;
1858  //else
1859  wxASSERT(false);
1860  }
1861 }
1862 
1864  (BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr)
1865 {
1866  ConsistencyCheck( newBlock, mMaxSamples, 0, numSamples, whereStr ); // may throw
1867 
1868  // now commit
1869  // use No-fail-guarantee
1870 
1871  mBlock.swap(newBlock);
1872  mNumSamples = numSamples;
1873 }
1874 
1876 (BlockArray &additionalBlocks, bool replaceLast,
1877  sampleCount numSamples, const wxChar *whereStr)
1878 {
1879  // Any additional blocks are meant to be appended,
1880  // replacing the final block if there was one.
1881 
1882  if (additionalBlocks.empty())
1883  return;
1884 
1885  bool tmpValid = false;
1886  SeqBlock tmp;
1887 
1888  if ( replaceLast && ! mBlock.empty() ) {
1889  tmp = mBlock.back(), tmpValid = true;
1890  mBlock.pop_back();
1891  }
1892 
1893  auto prevSize = mBlock.size();
1894 
1895  bool consistent = false;
1896  auto cleanup = finally( [&] {
1897  if ( !consistent ) {
1898  mBlock.resize( prevSize );
1899  if ( tmpValid )
1900  mBlock.push_back( tmp );
1901  }
1902  } );
1903 
1904  std::copy( additionalBlocks.begin(), additionalBlocks.end(),
1905  std::back_inserter( mBlock ) );
1906 
1907  // Check consistency only of the blocks that were added,
1908  // avoiding quadratic time for repeated checking of repeating appends
1909  ConsistencyCheck( mBlock, mMaxSamples, prevSize, numSamples, whereStr ); // may throw
1910 
1911  // now commit
1912  // use No-fail-guarantee
1913 
1914  mNumSamples = numSamples;
1915  consistent = true;
1916 }
1917 
1919  (const BlockArray &mBlock, sampleCount mNumSamples, wxString *dest)
1920 {
1921  unsigned int i;
1922  decltype(mNumSamples) pos = 0;
1923 
1924  for (i = 0; i < mBlock.size(); i++) {
1925  const SeqBlock &seqBlock = mBlock[i];
1926  *dest += wxString::Format
1927  (wxT(" Block %3u: start %8lld, len %8lld, refs %ld, id %lld"),
1928  i,
1929  seqBlock.start.as_long_long(),
1930  seqBlock.sb ? (long long) seqBlock.sb->GetSampleCount() : 0,
1931  seqBlock.sb ? seqBlock.sb.use_count() : 0,
1932  seqBlock.sb ? (long long) seqBlock.sb->GetBlockID() : 0);
1933 
1934  if ((pos != seqBlock.start) || !seqBlock.sb)
1935  *dest += wxT(" ERROR\n");
1936  else
1937  *dest += wxT("\n");
1938 
1939  if (seqBlock.sb)
1940  pos += seqBlock.sb->GetSampleCount();
1941  }
1942  if (pos != mNumSamples)
1943  *dest += wxString::Format
1944  (wxT("ERROR mNumSamples = %lld\n"), mNumSamples.as_long_long());
1945 }
1946 
1947 // static
1949 {
1950  sMaxDiskBlockSize = bytes;
1951 }
1952 
1954 {
1955  return sMaxDiskBlockSize;
1956 }
1957 
1958 bool Sequence::IsValidSampleFormat(const int nValue)
1959 {
1960  return (nValue == int16Sample) || (nValue == int24Sample) || (nValue == floatSample);
1961 }
XMLWriter
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Definition: XMLWriter.h:23
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
Sequence::mNumSamples
sampleCount mNumSamples
Definition: Sequence.h:207
Sequence::sMaxDiskBlockSize
static size_t sMaxDiskBlockSize
Definition: Sequence.h:195
anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize
void ensureSampleBufferSize(SampleBuffer &buffer, sampleFormat format, size_t &size, size_t required, SampleBuffer *pSecondBuffer=nullptr)
Definition: Sequence.cpp:111
Optional::emplace
X & emplace(Args &&... args)
Definition: MemoryX.h:193
ClearSamples
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
Definition: SampleFormat.cpp:77
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:84
Sequence::ConsistencyCheck
void ConsistencyCheck(const wxChar *whereStr, bool mayThrow=true) const
Definition: Sequence.cpp:1807
Sequence::CloseLock
bool CloseLock()
Definition: Sequence.cpp:86
anonymous_namespace{Sequence.cpp}::MinMaxSumsq::min
float min
Definition: Sequence.cpp:1258
Sequence::AppendSharedBlock
void AppendSharedBlock(const SeqBlock::SampleBlockPtr &pBlock)
Append a complete block, not coalescing.
Definition: Sequence.cpp:1485
Sequence::Delete
void Delete(sampleCount start, sampleCount len)
Definition: Sequence.cpp:1633
Optional
Like a smart pointer, allows for object to not exist (nullptr)
Definition: MemoryX.h:144
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
Sequence::mMaxSamples
size_t mMaxSamples
Definition: Sequence.h:210
Sequence::GetWaveDisplay
bool GetWaveDisplay(float *min, float *max, float *rms, int *bl, size_t len, const sampleCount *where) const
Definition: Sequence.cpp:1265
Sequence::FindBlock
int FindBlock(sampleCount pos) const
Definition: Sequence.cpp:999
XMLValueChecker::IsGoodInt
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
Definition: XMLTagHandler.cpp:157
Sequence::ConvertToSampleFormat
bool ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
Definition: Sequence.cpp:136
str
#define str(a)
Definition: DBConnection.cpp:30
InconsistencyException.h
MessageBoxException for violation of preconditions or assertions.
SampleBlockPtr
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: SampleBlock.h:23
Sequence::mMinSamples
size_t mMinSamples
Definition: Sequence.h:209
Sequence::Blockify
static void Blockify(SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
Definition: Sequence.cpp:1607
Sequence::SetSilence
void SetSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:676
Sequence::mBlock
BlockArray mBlock
Definition: Sequence.h:203
Sequence::SetSamples
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, sampleCount len)
Definition: Sequence.cpp:1112
XMLValueChecker::IsGoodInt64
static bool IsGoodInt64(const wxString &strInt)
Check that the supplied string can be converted to a 64bit integer.
Definition: XMLTagHandler.cpp:163
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
Sequence::GetMaxDiskBlockSize
static size_t GetMaxDiskBlockSize()
Definition: Sequence.cpp:1953
Sequence::AppendNewBlock
SeqBlock::SampleBlockPtr AppendNewBlock(constSamplePtr buffer, sampleFormat format, size_t len)
Append data, not coalescing blocks, returning a pointer to the new block.
Definition: Sequence.cpp:1478
XO
#define XO(s)
Definition: Internat.h:31
Sequence::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:76
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
Sequence::~Sequence
~Sequence()
Definition: Sequence.cpp:72
Sequence::GetBestBlockSize
size_t GetBestBlockSize(sampleCount start) const
Definition: Sequence.cpp:756
TranslatableString::Debug
wxString Debug() const
Format as an English string for debugging logs and developers' eyes, not for end users.
Definition: TranslatableString.h:82
Sequence::Append
void Append(constSamplePtr buffer, sampleFormat format, size_t len)
Definition: Sequence.cpp:1509
Sequence
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:61
SeqBlock::SampleBlockPtr
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: Sequence.h:30
Sequence::AppendBlocksIfConsistent
void AppendBlocksIfConsistent(BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1876
Sequence::DebugPrintf
static void DebugPrintf(const BlockArray &block, sampleCount numSamples, wxString *dest)
Definition: Sequence.cpp:1919
Sequence::SetMaxDiskBlockSize
static void SetMaxDiskBlockSize(size_t bytes)
Definition: Sequence.cpp:1948
Sequence::mErrorOpening
bool mErrorOpening
Definition: Sequence.h:212
Sequence::Read
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1049
InconsistencyException::GetLine
unsigned GetLine() const
Definition: InconsistencyException.h:49
Sequence::HandleXMLTag
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override
Definition: Sequence.cpp:785
SampleBlock.h
floatSample
@ floatSample
Definition: SampleFormat.h:34
SampleBlockFactory::Create
SampleBlockPtr Create(constSamplePtr src, size_t numsamples, sampleFormat srcformat)
Definition: SampleBlock.cpp:41
Sequence::mSampleFormat
sampleFormat mSampleFormat
Definition: Sequence.h:204
anonymous_namespace{Sequence.cpp}::MinMaxSumsq::sumsq
float sumsq
Definition: Sequence.cpp:1260
Sequence::IsValidSampleFormat
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
Definition: Sequence.cpp:1958
Sequence::Sequence
Sequence(const SampleBlockFactoryPtr &pFactory, sampleFormat format)
Definition: Sequence.cpp:50
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:817
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
SeqBlock::sb
SampleBlockPtr sb
Definition: Sequence.h:31
CopySamples
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
Definition: SampleFormat.cpp:111
Sequence::GetIdealAppendLen
size_t GetIdealAppendLen() const
Definition: Sequence.cpp:1462
Sequence::Copy
std::unique_ptr< Sequence > Copy(const SampleBlockFactoryPtr &pFactory, sampleCount s0, sampleCount s1) const
Definition: Sequence.cpp:373
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
anonymous_namespace{Sequence.cpp}::MinMaxSumsq::MinMaxSumsq
MinMaxSumsq(const float *pv, int count, int divisor)
Definition: Sequence.cpp:1226
constSamplePtr
const char * constSamplePtr
Definition: SampleFormat.h:50
anonymous_namespace{Sequence.cpp}::MinMaxSumsq::max
float max
Definition: Sequence.cpp:1259
Sequence::CommitChangesIfConsistent
void CommitChangesIfConsistent(BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1864
format
int format
Definition: ExportPCM.cpp:56
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
Sequence::GetMinMax
std::pair< float, float > GetMinMax(sampleCount start, sampleCount len, bool mayThrow) const
Definition: Sequence.cpp:222
Sequence::HandleXMLChild
XMLTagHandler * HandleXMLChild(const wxChar *tag) override
Definition: Sequence.cpp:944
Sequence::HandleXMLEndTag
void HandleXMLEndTag(const wxChar *tag) override
Definition: Sequence.cpp:902
Sequence::mpFactory
SampleBlockFactoryPtr mpFactory
Definition: Sequence.h:201
sampleCount::as_size_t
size_t as_size_t() const
Definition: SampleCount.cpp:17
XMLTagHandler
This class is an interface which should be implemented by classes which wish to be able to load and s...
Definition: XMLTagHandler.h:62
SampleBuffer
Definition: SampleFormat.h:69
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
Sequence::DoAppend
SeqBlock::SampleBlockPtr DoAppend(constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
Definition: Sequence.cpp:1515
SeqBlock
Data structure containing pointer to a sample block and a start time. Element of a BlockArray.
Definition: Sequence.h:28
Sequence::InsertSilence
void InsertSilence(sampleCount s0, sampleCount len)
Definition: Sequence.cpp:682
BlockArray
Definition: Sequence.h:49
Sequence::Paste
void Paste(sampleCount s0, const Sequence *src)
Definition: Sequence.cpp:475
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
anonymous_namespace{Sequence.cpp}::ShareOrCopySampleBlock
SampleBlockPtr ShareOrCopySampleBlock(SampleBlockFactory *pFactory, sampleFormat format, SampleBlockPtr sb)
Definition: Sequence.cpp:457
anonymous_namespace{Sequence.cpp}::MinMaxSumsq
Definition: Sequence.cpp:1225
AudacityMessageBox.h
CONSTRUCT_INCONSISTENCY_EXCEPTION
#define CONSTRUCT_INCONSISTENCY_EXCEPTION
Construct InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:71
Sequence.h
SampleBlockFactory
abstract base class with methods to produce SampleBlock objects
Definition: SampleBlock.h:106
Sequence::GetSampleFormat
sampleFormat GetSampleFormat() const
Definition: Sequence.cpp:94
Sequence::GetRMS
float GetRMS(sampleCount start, sampleCount len, bool mayThrow) const
Definition: Sequence.cpp:304
GetSampleFormatStr
TranslatableString GetSampleFormatStr(sampleFormat format)
Definition: SampleFormat.cpp:60
SampleBlockFactoryPtr
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
Definition: SampleBlock.h:25
Sequence::AppendBlock
static void AppendBlock(SampleBlockFactory *pFactory, sampleFormat format, BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b)
Definition: Sequence.cpp:731
Sequence::WriteXML
void WriteXML(XMLWriter &xmlFile) const
Definition: Sequence.cpp:955
values
const wxChar * values
Definition: Equalization.cpp:472
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
ArrayOf< float >
Internat::ToString
static wxString ToString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, always uses the dot as decimal separator.
Definition: Internat.cpp:151
SeqBlock::start
sampleCount start
the sample in the global wavetrack that this block starts at.
Definition: Sequence.h:33
Sequence::Get
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow) const
Definition: Sequence.cpp:1070
anonymous_namespace{Sequence.cpp}::Overflows
bool Overflows(double numSamples)
Definition: Sequence.cpp:452
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98
Sequence::GetIdealBlockSize
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:81
Sequence::GetBlockStart
sampleCount GetBlockStart(sampleCount position) const
Definition: Sequence.cpp:750