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