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