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