Audacity 3.2.0
Public Member Functions | Static Public Member Functions | Static Public Attributes | Private Member Functions | Static Private Member Functions | Private Attributes | Static Private Attributes | List of all members
Sequence Class Referencefinal

A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interface to an array of SeqBlock instances, corresponding to the audio sample blocks in the database. Contrast with RingBuffer. More...

#include <Sequence.h>

Inheritance diagram for Sequence:
[legend]
Collaboration diagram for Sequence:
[legend]

Public Member Functions

 Sequence (const SampleBlockFactoryPtr &pFactory, SampleFormats formats)
 
 Sequence (const Sequence &orig, const SampleBlockFactoryPtr &pFactory)
 Does not copy un-flushed append buffer data. More...
 
 Sequence (const Sequence &)=delete
 
Sequenceoperator= (const Sequence &)=delete
 
 ~Sequence ()
 
sampleCount GetNumSamples () const
 
bool Get (samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow) const
 Get a range of samples from the sequence. More...
 
AudioSegmentSampleView GetFloatSampleView (sampleCount start, size_t len, bool mayThrow) const
 
void SetSamples (constSamplePtr buffer, sampleFormat format, sampleCount start, sampleCount len, sampleFormat effectiveFormat)
 Pass nullptr to set silence. More...
 
std::unique_ptr< SequenceCopy (const SampleBlockFactoryPtr &pFactory, sampleCount s0, sampleCount s1) const
 
void Paste (sampleCount s0, const Sequence *src)
 
size_t GetIdealAppendLen () const
 
bool Append (constSamplePtr buffer, sampleFormat format, size_t len, size_t stride, sampleFormat effectiveFormat)
 
void Flush ()
 
SeqBlock::SampleBlockPtr AppendNewBlock (constSamplePtr buffer, sampleFormat format, size_t len)
 
void AppendSharedBlock (const SeqBlock::SampleBlockPtr &pBlock)
 Append a complete block, not coalescing. More...
 
void Delete (sampleCount start, sampleCount len)
 
void SetSilence (sampleCount s0, sampleCount len)
 
void InsertSilence (sampleCount s0, sampleCount len)
 
const SampleBlockFactoryPtrGetFactory () const
 
bool HandleXMLTag (const std::string_view &tag, const AttributesList &attrs) override
 
void HandleXMLEndTag (const std::string_view &tag) override
 
XMLTagHandlerHandleXMLChild (const std::string_view &tag) override
 
void WriteXML (XMLWriter &xmlFile) const
 
bool GetErrorOpening () const
 
bool CloseLock () noexcept
 Should be called upon project close. Not balanced by unlocking calls. More...
 
SampleFormats GetSampleFormats () const
 
bool ConvertToSampleFormat (sampleFormat format, const std::function< void(size_t)> &progressReport={})
 
std::pair< float, float > GetMinMax (sampleCount start, sampleCount len, bool mayThrow) const
 
float GetRMS (sampleCount start, sampleCount len, bool mayThrow) const
 
size_t GetBestBlockSize (sampleCount start) const
 
size_t GetMaxBlockSize () const
 
size_t GetIdealBlockSize () const
 
BlockArrayGetBlockArray ()
 
const BlockArrayGetBlockArray () const
 
size_t GetAppendBufferLen () const
 
constSamplePtr GetAppendBuffer () const
 
int FindBlock (sampleCount pos) const
 
void ConsistencyCheck (const wxChar *whereStr, bool mayThrow=true) const
 
- Public Member Functions inherited from XMLTagHandler
 XMLTagHandler ()
 
virtual ~XMLTagHandler ()
 
virtual bool HandleXMLTag (const std::string_view &tag, const AttributesList &attrs)=0
 
virtual void HandleXMLEndTag (const std::string_view &WXUNUSED(tag))
 
virtual void HandleXMLContent (const std::string_view &WXUNUSED(content))
 
virtual XMLTagHandlerHandleXMLChild (const std::string_view &tag)=0
 
void ReadXMLEndTag (const char *tag)
 
void ReadXMLContent (const char *s, int len)
 
XMLTagHandlerReadXMLChild (const char *tag)
 

Static Public Member Functions

static void SetMaxDiskBlockSize (size_t bytes)
 
static size_t GetMaxDiskBlockSize ()
 
static bool IsValidSampleFormat (const int nValue)
 true if nValue is one of the sampleFormat enum values More...
 
static bool Read (samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
 
static void DebugPrintf (const BlockArray &block, sampleCount numSamples, wxString *dest)
 

Static Public Attributes

static const char * Sequence_tag = "sequence"
 
static const char * WaveBlock_tag = "waveblock"
 

Private Member Functions

sampleCount GetBlockStart (sampleCount position) const
 
SeqBlock::SampleBlockPtr DoAppend (constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
 Does not do any dithering. More...
 
bool Get (int b, samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow) const
 
void CommitChangesIfConsistent (BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr)
 
void AppendBlocksIfConsistent (BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
 

Static Private Member Functions

static void AppendBlock (SampleBlockFactory *pFactory, sampleFormat format, BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b)
 
static void Blockify (SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
 
static void ConsistencyCheck (const BlockArray &block, size_t maxSamples, size_t from, sampleCount numSamples, const wxChar *whereStr, bool mayThrow=true)
 

Private Attributes

SampleBlockFactoryPtr mpFactory
 
BlockArray mBlock
 
SampleFormats mSampleFormats
 
sampleCount mNumSamples { 0 }
 
size_t mMinSamples
 
size_t mMaxSamples
 
SampleBuffer mAppendBuffer {}
 
size_t mAppendBufferLen { 0 }
 
sampleFormat mAppendEffectiveFormat { narrowestSampleFormat }
 
bool mErrorOpening { false }
 

Static Private Attributes

static size_t sMaxDiskBlockSize = 1048576
 

Detailed Description

A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interface to an array of SeqBlock instances, corresponding to the audio sample blocks in the database. Contrast with RingBuffer.

Definition at line 53 of file Sequence.h.

Constructor & Destructor Documentation

◆ Sequence() [1/3]

Sequence::Sequence ( const SampleBlockFactoryPtr pFactory,
SampleFormats  formats 
)

Definition at line 54 of file Sequence.cpp.

56: mpFactory(pFactory),
57 mSampleFormats{ formats },
60{
61}
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
sampleFormat Stored() const
Definition: SampleFormat.h:91
SampleBlockFactoryPtr mpFactory
Definition: Sequence.h:250
static size_t sMaxDiskBlockSize
Definition: Sequence.h:244
size_t mMinSamples
Definition: Sequence.h:258
size_t mMaxSamples
Definition: Sequence.h:259
SampleFormats mSampleFormats
Definition: Sequence.h:253

◆ Sequence() [2/3]

Sequence::Sequence ( const Sequence orig,
const SampleBlockFactoryPtr pFactory 
)

Does not copy un-flushed append buffer data.

Definition at line 66 of file Sequence.cpp.

68: mpFactory(pFactory),
72{
73 Paste(0, &orig);
74}
void Paste(sampleCount s0, const Sequence *src)
Definition: Sequence.cpp:499

References Paste().

Here is the call graph for this function:

◆ Sequence() [3/3]

Sequence::Sequence ( const Sequence )
delete

◆ ~Sequence()

Sequence::~Sequence ( )

Definition at line 76 of file Sequence.cpp.

77{
78}

Member Function Documentation

◆ Append()

bool Sequence::Append ( constSamplePtr  buffer,
sampleFormat  format,
size_t  len,
size_t  stride,
sampleFormat  effectiveFormat 
)

Samples may be retained in a memory buffer, pending Flush() If there are exceptions, an unspecified prefix of buffer may be appended

Returns
true if at least one sample block was added
Exception safety guarantee:
Weak
Exception safety guarantee:
Weak
Parameters
effectiveFormatMake the effective format of the data at least the minumum of this value and `format`. (Maybe wider, if merging with preexistent data.) If the data are later narrowed from stored format, but not narrower than the effective, then no dithering will occur.

Definition at line 1376 of file Sequence.cpp.

1379{
1380 effectiveFormat = std::min(effectiveFormat, format);
1381 const auto seqFormat = mSampleFormats.Stored();
1382 if (!mAppendBuffer.ptr())
1384
1385 bool result = false;
1386 auto blockSize = GetIdealAppendLen();
1387 for(;;) {
1388 if (mAppendBufferLen >= blockSize) {
1389 // flush some previously appended contents
1390 // use Strong-guarantee
1391 // Already dithered if needed when accumulated into mAppendBuffer
1392 DoAppend(mAppendBuffer.ptr(), seqFormat, blockSize, true);
1393 // Change our effective format now that DoAppend didn't throw
1395 result = true;
1396
1397 // use No-fail-guarantee for rest of this "if"
1398 memmove(mAppendBuffer.ptr(),
1399 mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
1400 (mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
1401 mAppendBufferLen -= blockSize;
1402 blockSize = GetIdealAppendLen();
1403 }
1404
1405 if (len == 0)
1406 break;
1407
1408 // use No-fail-guarantee for rest of this "for"
1409 wxASSERT(mAppendBufferLen <= mMaxSamples);
1410 auto toCopy = std::min(len, mMaxSamples - mAppendBufferLen);
1411
1412 // If dithering of appended material is done at all, it happens here
1413 CopySamples(buffer, format,
1415 seqFormat,
1416 toCopy,
1417 (seqFormat < effectiveFormat ? gHighQualityDither : DitherType::none),
1418 stride);
1420 std::max(mAppendEffectiveFormat, effectiveFormat);
1421
1422 mAppendBufferLen += toCopy;
1423 buffer += toCopy * SAMPLE_SIZE(format) * stride;
1424 len -= toCopy;
1425 }
1426
1427 return result;
1428}
int min(int a, int b)
@ none
Definition: Dither.h:20
DitherType gHighQualityDither
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.
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:151
samplePtr ptr() const
Definition: SampleFormat.h:165
void UpdateEffective(sampleFormat effective)
Update the effective format, for insertion of more samples into the sequence.
Definition: SampleFormat.h:98
sampleFormat mAppendEffectiveFormat
Definition: Sequence.h:263
size_t mAppendBufferLen
Definition: Sequence.h:262
SeqBlock::SampleBlockPtr DoAppend(constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
Does not do any dithering.
Definition: Sequence.cpp:1431
size_t GetIdealAppendLen() const
Definition: Sequence.cpp:1325
SampleBuffer mAppendBuffer
Definition: Sequence.h:261

References SampleBuffer::Allocate(), CopySamples(), DoAppend(), anonymous_namespace{ExportPCM.cpp}::format, GetIdealAppendLen(), gHighQualityDither, mAppendBuffer, mAppendBufferLen, mAppendEffectiveFormat, min(), mMaxSamples, mSampleFormats, none, SampleBuffer::ptr(), SAMPLE_SIZE, SampleFormats::Stored(), and SampleFormats::UpdateEffective().

Here is the call graph for this function:

◆ AppendBlock()

void Sequence::AppendBlock ( SampleBlockFactory pFactory,
sampleFormat  format,
BlockArray blocks,
sampleCount numSamples,
const SeqBlock b 
)
staticprivate

Definition at line 762 of file Sequence.cpp.

764{
765 // Quick check to make sure that it doesn't overflow
766 if (Overflows((mNumSamples.as_double()) + ((double)b.sb->GetSampleCount())))
768
769 auto sb = ShareOrCopySampleBlock( pFactory, format, b.sb );
770 SeqBlock newBlock(sb, mNumSamples);
771
772 // We can assume newBlock.sb is not null
773
774 mBlock.push_back(newBlock);
775 mNumSamples += newBlock.sb->GetSampleCount();
776
777 // Don't do a consistency check here because this
778 // function gets called in an inner loop.
779}
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Data structure containing pointer to a sample block and a start time. Element of a BlockArray.
Definition: Sequence.h:29
SampleBlockPtr sb
Definition: Sequence.h:32
BlockArray mBlock
Definition: Sequence.h:252
sampleCount mNumSamples
Definition: Sequence.h:256
double as_double() const
Definition: SampleCount.h:46
bool Overflows(double numSamples)
Definition: Sequence.cpp:476
SampleBlockPtr ShareOrCopySampleBlock(SampleBlockFactory *pFactory, sampleFormat format, SampleBlockPtr sb)
Definition: Sequence.cpp:481

References sampleCount::as_double(), anonymous_namespace{ExportPCM.cpp}::format, mBlock, mNumSamples, anonymous_namespace{Sequence.cpp}::Overflows(), SeqBlock::sb, anonymous_namespace{Sequence.cpp}::ShareOrCopySampleBlock(), and THROW_INCONSISTENCY_EXCEPTION.

Referenced by Copy(), and Paste().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AppendBlocksIfConsistent()

void Sequence::AppendBlocksIfConsistent ( BlockArray additionalBlocks,
bool  replaceLast,
sampleCount  numSamples,
const wxChar *  whereStr 
)
private

Definition at line 1822 of file Sequence.cpp.

1825{
1826 // Any additional blocks are meant to be appended,
1827 // replacing the final block if there was one.
1828
1829 if (additionalBlocks.empty())
1830 return;
1831
1832 bool tmpValid = false;
1833 SeqBlock tmp;
1834
1835 if ( replaceLast && ! mBlock.empty() ) {
1836 tmp = mBlock.back(), tmpValid = true;
1837 mBlock.pop_back();
1838 }
1839
1840 auto prevSize = mBlock.size();
1841
1842 bool consistent = false;
1843 auto cleanup = finally( [&] {
1844 if ( !consistent ) {
1845 mBlock.resize( prevSize );
1846 if ( tmpValid )
1847 mBlock.push_back( tmp );
1848 }
1849 } );
1850
1851 std::copy( additionalBlocks.begin(), additionalBlocks.end(),
1852 std::back_inserter( mBlock ) );
1853
1854 // Check consistency only of the blocks that were added,
1855 // avoiding quadratic time for repeated checking of repeating appends
1856 ConsistencyCheck( mBlock, mMaxSamples, prevSize, numSamples, whereStr ); // may throw
1857
1858 // now commit
1859 // use No-fail-guarantee
1860
1861 mNumSamples = numSamples;
1862 consistent = true;
1863}
void ConsistencyCheck(const wxChar *whereStr, bool mayThrow=true) const
Definition: Sequence.cpp:1754
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40

References ConsistencyCheck(), staffpad::vo::copy(), mBlock, mMaxSamples, and mNumSamples.

Referenced by AppendSharedBlock(), and DoAppend().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AppendNewBlock()

SeqBlock::SampleBlockPtr Sequence::AppendNewBlock ( constSamplePtr  buffer,
sampleFormat  format,
size_t  len 
)

Append data, not coalescing blocks, returning a pointer to the new block. No dithering applied.

Exception safety guarantee:
Strong
Exception safety guarantee:
Strong

Definition at line 1341 of file Sequence.cpp.

1343{
1344 // Come here only when importing old .aup projects
1345 auto result = DoAppend( buffer, format, len, false );
1346 // Change our effective format now that DoAppend didn't throw
1348 return result;
1349}

References DoAppend(), anonymous_namespace{ExportPCM.cpp}::format, mSampleFormats, and SampleFormats::UpdateEffective().

Here is the call graph for this function:

◆ AppendSharedBlock()

void Sequence::AppendSharedBlock ( const SeqBlock::SampleBlockPtr pBlock)

Append a complete block, not coalescing.

Exception safety guarantee:
Strong

Definition at line 1352 of file Sequence.cpp.

1353{
1354 auto len = pBlock->GetSampleCount();
1355
1356 // Quick check to make sure that it doesn't overflow
1357 if (Overflows(mNumSamples.as_double() + ((double)len)))
1359
1360 BlockArray newBlock;
1361 newBlock.emplace_back( pBlock, mNumSamples );
1362 auto newNumSamples = mNumSamples + len;
1363
1364 AppendBlocksIfConsistent(newBlock, false,
1365 newNumSamples, wxT("Append"));
1366
1367// JKC: During generate we use Append again and again.
1368// If generating a long sequence this test would give O(n^2)
1369// performance - not good!
1370#ifdef VERY_SLOW_CHECKING
1371 ConsistencyCheck(wxT("Append"));
1372#endif
1373}
wxT("CloseDown"))
void AppendBlocksIfConsistent(BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1823

References AppendBlocksIfConsistent(), sampleCount::as_double(), ConsistencyCheck(), mNumSamples, anonymous_namespace{Sequence.cpp}::Overflows(), THROW_INCONSISTENCY_EXCEPTION, and wxT().

Here is the call graph for this function:

◆ Blockify()

void Sequence::Blockify ( SampleBlockFactory factory,
size_t  maxSamples,
sampleFormat  format,
BlockArray list,
sampleCount  start,
constSamplePtr  buffer,
size_t  len 
)
staticprivate

Definition at line 1553 of file Sequence.cpp.

1557{
1558 if (len <= 0)
1559 return;
1560
1561 auto num = (len + (mMaxSamples - 1)) / mMaxSamples;
1562 list.reserve(list.size() + num);
1563
1564 for (decltype(num) i = 0; i < num; i++) {
1565 SeqBlock b;
1566
1567 const auto offset = i * len / num;
1568 b.start = start + offset;
1569 int newLen = ((i + 1) * len / num) - offset;
1570 auto bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat));
1571
1572 b.sb = factory.Create(bufStart, newLen, mSampleFormat);
1573
1574 list.push_back(b);
1575 }
1576}
static RegisteredToolbarFactory factory
sampleCount start
the sample in the global wavetrack that this block starts at.
Definition: Sequence.h:34

References factory, mMaxSamples, SAMPLE_SIZE, SeqBlock::sb, and SeqBlock::start.

Referenced by ConvertToSampleFormat(), Delete(), and Paste().

Here is the caller graph for this function:

◆ CloseLock()

bool Sequence::CloseLock ( )
noexcept

Should be called upon project close. Not balanced by unlocking calls.

Exception safety guarantee:
No-fail

Definition at line 90 of file Sequence.cpp.

91{
92 for (unsigned int i = 0; i < mBlock.size(); i++)
93 mBlock[i].sb->CloseLock();
94
95 return true;
96}

References mBlock.

◆ CommitChangesIfConsistent()

void Sequence::CommitChangesIfConsistent ( BlockArray newBlock,
sampleCount  numSamples,
const wxChar *  whereStr 
)
private

Definition at line 1810 of file Sequence.cpp.

1812{
1813 ConsistencyCheck( newBlock, mMaxSamples, 0, numSamples, whereStr ); // may throw
1814
1815 // now commit
1816 // use No-fail-guarantee
1817
1818 mBlock.swap(newBlock);
1819 mNumSamples = numSamples;
1820}

References ConsistencyCheck(), mBlock, mMaxSamples, and mNumSamples.

Referenced by ConvertToSampleFormat(), Delete(), Paste(), and SetSamples().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ConsistencyCheck() [1/2]

static void Sequence::ConsistencyCheck ( const BlockArray block,
size_t  maxSamples,
size_t  from,
sampleCount  numSamples,
const wxChar *  whereStr,
bool  mayThrow = true 
)
staticprivate

◆ ConsistencyCheck() [2/2]

void Sequence::ConsistencyCheck ( const wxChar *  whereStr,
bool  mayThrow = true 
) const

Definition at line 1754 of file Sequence.cpp.

1755{
1756 ConsistencyCheck(mBlock, mMaxSamples, 0, mNumSamples, whereStr, mayThrow);
1757}

References ConsistencyCheck(), mBlock, mMaxSamples, and mNumSamples.

Referenced by AppendBlocksIfConsistent(), AppendSharedBlock(), CommitChangesIfConsistent(), ConsistencyCheck(), Delete(), DoAppend(), and Paste().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ConvertToSampleFormat()

bool Sequence::ConvertToSampleFormat ( sampleFormat  format,
const std::function< void(size_t)> &  progressReport = {} 
)
Returns
whether there was a change of format
Exception safety guarantee:
Strong
Exception safety guarantee:
Strong

Definition at line 140 of file Sequence.cpp.

142{
144 // no change
145 return false;
146
147 if (mBlock.size() == 0)
148 {
149 // Effective format can be made narrowest when there is no content
151 return true;
152 }
153
154 // Decide the new pair of formats. If becoming narrower than the effective,
155 // this will change the effective.
157
158 const auto oldFormats = mSampleFormats;
159 mSampleFormats = newFormats;
160
161 const auto oldMinSamples = mMinSamples, oldMaxSamples = mMaxSamples;
162 // These are the same calculations as in the constructor.
165
166 bool bSuccess = false;
167 auto cleanup = finally( [&] {
168 if (!bSuccess) {
169 // Conversion failed. Revert these member vars.
170 mSampleFormats = oldFormats;
171 mMaxSamples = oldMaxSamples;
172 mMinSamples = oldMinSamples;
173 }
174 } );
175
176 BlockArray newBlockArray;
177 // Use the ratio of old to NEW mMaxSamples to make a reasonable guess
178 // at allocation.
179 newBlockArray.reserve
180 (1 + mBlock.size() * ((float)oldMaxSamples / (float)mMaxSamples));
181
182 {
183 size_t oldSize = oldMaxSamples;
184 SampleBuffer bufferOld(oldSize, oldFormats.Stored());
185 size_t newSize = oldMaxSamples;
186 SampleBuffer bufferNew(newSize, format);
187
188 for (size_t i = 0, nn = mBlock.size(); i < nn; i++)
189 {
190 SeqBlock &oldSeqBlock = mBlock[i];
191 const auto &oldBlockFile = oldSeqBlock.sb;
192 const auto len = oldBlockFile->GetSampleCount();
193 ensureSampleBufferSize(bufferOld, oldFormats.Stored(), oldSize, len);
194
195 // Dither won't happen here, reading back the same as-saved format
196 Read(bufferOld.ptr(), oldFormats.Stored(), oldSeqBlock, 0, len, true);
197
198 ensureSampleBufferSize(bufferNew, format, newSize, len);
199
201 bufferOld.ptr(), oldFormats.Stored(), bufferNew.ptr(), format, len,
202 // Do not dither to reformat samples if format is at least as wide
203 // as the old effective (though format might be narrower than the
204 // old stored).
205 format < oldFormats.Effective()
208
209 // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451,
210 // using Blockify, allows (len < mMinSamples).
211 // This will happen consistently when going from more bytes per sample to fewer...
212 // This will create a block that's smaller than mMinSamples, which
213 // shouldn't be allowed, but we agreed it's okay for now.
214 //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere?
215 // If so, need to special-case (len < mMinSamples) and start combining data
216 // from the old blocks... Oh no!
217
218 // Using Blockify will handle the cases where len > the NEW mMaxSamples. Previous code did not.
219 const auto blockstart = oldSeqBlock.start;
221 newBlockArray, blockstart, bufferNew.ptr(), len);
222
223 if (progressReport)
224 progressReport(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}
@ narrowestSampleFormat
Two synonyms for previous values that might change if more values were added.
Two sample formats, remembering format of original source and describing stored format.
Definition: SampleFormat.h:79
sampleFormat Effective() const
Definition: SampleFormat.h:90
void CommitChangesIfConsistent(BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1811
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1105
static void Blockify(SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
Definition: Sequence.cpp:1553
void ensureSampleBufferSize(SampleBuffer &buffer, sampleFormat format, size_t &size, size_t required, SampleBuffer *pSecondBuffer=nullptr)
Definition: Sequence.cpp:115

References Blockify(), CommitChangesIfConsistent(), CopySamples(), SampleFormats::Effective(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), anonymous_namespace{ExportPCM.cpp}::format, gHighQualityDither, mBlock, mMaxSamples, mMinSamples, mNumSamples, mpFactory, mSampleFormats, narrowestSampleFormat, none, SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, sMaxDiskBlockSize, SeqBlock::start, SampleFormats::Stored(), and wxT().

Here is the call graph for this function:

◆ Copy()

std::unique_ptr< Sequence > Sequence::Copy ( const SampleBlockFactoryPtr pFactory,
sampleCount  s0,
sampleCount  s1 
) const

Definition at line 392 of file Sequence.cpp.

394{
395 // Make a new Sequence object for the specified factory:
396 auto dest = std::make_unique<Sequence>(pFactory, mSampleFormats);
397 if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) {
398 return dest;
399 }
400
401 // Decide whether to share sample blocks or make new copies, when whole block
402 // contents are used -- must copy if factories are different:
403 auto pUseFactory = (pFactory == mpFactory) ? nullptr : pFactory.get();
404
405 int numBlocks = mBlock.size();
406
407 int b0 = FindBlock(s0);
408 const int b1 = FindBlock(s1 - 1);
409 wxASSERT(b0 >= 0);
410 wxASSERT(b0 < numBlocks);
411 wxASSERT(b1 < numBlocks);
412 wxUnusedVar(numBlocks);
413 wxASSERT(b0 <= b1);
414
415 dest->mBlock.reserve(b1 - b0 + 1);
416
417 auto bufferSize = mMaxSamples;
418 const auto format = mSampleFormats.Stored();
419 SampleBuffer buffer(bufferSize, format);
420
421 int blocklen;
422
423 // Do any initial partial block
424
425 const SeqBlock &block0 = mBlock[b0];
426 if (s0 != block0.start) {
427 const auto &sb = block0.sb;
428 // Nonnegative result is length of block0 or less:
429 blocklen =
430 ( std::min(s1, block0.start + sb->GetSampleCount()) - s0 ).as_size_t();
431 wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
432 ensureSampleBufferSize(buffer, format, bufferSize, blocklen);
433 Get(b0, buffer.ptr(), format, s0, blocklen, true);
434
435 dest->Append(
436 buffer.ptr(), format, blocklen, 1, mSampleFormats.Effective());
437 dest->Flush();
438 }
439 else
440 --b0;
441
442 // If there are blocks in the middle, use the blocks whole
443 for (int bb = b0 + 1; bb < b1; ++bb)
444 AppendBlock(pUseFactory, format,
445 dest->mBlock, dest->mNumSamples, mBlock[bb]);
446 // Increase ref count or duplicate file
447
448 // Do the last block
449 if (b1 > b0) {
450 // Probable case of a partial block
451 const SeqBlock &block = mBlock[b1];
452 const auto &sb = block.sb;
453 // s1 is within block:
454 blocklen = (s1 - block.start).as_size_t();
455 wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
456 if (blocklen < (int)sb->GetSampleCount()) {
457 ensureSampleBufferSize(buffer, format, bufferSize, blocklen);
458 Get(b1, buffer.ptr(), format, block.start, blocklen, true);
459 dest->Append(
460 buffer.ptr(), format, blocklen, 1, mSampleFormats.Effective());
461 dest->Flush();
462 }
463 else
464 // Special case of a whole block
465 AppendBlock(pUseFactory, format,
466 dest->mBlock, dest->mNumSamples, block);
467 // Increase ref count or duplicate file
468 }
469
470 dest->ConsistencyCheck(wxT("Sequence::Copy()"));
471
472 return dest;
473}
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:1146
static void AppendBlock(SampleBlockFactory *pFactory, sampleFormat format, BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b)
Definition: Sequence.cpp:762
int FindBlock(sampleCount pos) const
Definition: Sequence.cpp:1055

References AppendBlock(), SampleFormats::Effective(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, Get(), mBlock, min(), mMaxSamples, mNumSamples, mpFactory, mSampleFormats, SampleBuffer::ptr(), SeqBlock::sb, SeqBlock::start, SampleFormats::Stored(), and wxT().

Here is the call graph for this function:

◆ DebugPrintf()

void Sequence::DebugPrintf ( const BlockArray block,
sampleCount  numSamples,
wxString *  dest 
)
static

Definition at line 1865 of file Sequence.cpp.

1867{
1868 unsigned int i;
1869 decltype(mNumSamples) pos = 0;
1870
1871 for (i = 0; i < mBlock.size(); i++) {
1872 const SeqBlock &seqBlock = mBlock[i];
1873 *dest += wxString::Format
1874 (wxT(" Block %3u: start %8lld, len %8lld, refs %ld, id %lld"),
1875 i,
1876 seqBlock.start.as_long_long(),
1877 seqBlock.sb ? (long long) seqBlock.sb->GetSampleCount() : 0,
1878 seqBlock.sb ? seqBlock.sb.use_count() : 0,
1879 seqBlock.sb ? (long long) seqBlock.sb->GetBlockID() : 0);
1880
1881 if ((pos != seqBlock.start) || !seqBlock.sb)
1882 *dest += wxT(" ERROR\n");
1883 else
1884 *dest += wxT("\n");
1885
1886 if (seqBlock.sb)
1887 pos += seqBlock.sb->GetSampleCount();
1888 }
1889 if (pos != mNumSamples)
1890 *dest += wxString::Format
1891 (wxT("ERROR mNumSamples = %lld\n"), mNumSamples.as_long_long());
1892}
long long as_long_long() const
Definition: SampleCount.h:48

References sampleCount::as_long_long(), mBlock, mNumSamples, SeqBlock::sb, SeqBlock::start, and wxT().

Here is the call graph for this function:

◆ Delete()

void Sequence::Delete ( sampleCount  start,
sampleCount  len 
)
Exception safety guarantee:
Strong

Definition at line 1579 of file Sequence.cpp.

1580{
1581 if (len == 0)
1582 return;
1583
1584 if (len < 0 || start < 0 || start + len > mNumSamples)
1586
1587 auto &factory = *mpFactory;
1588
1589 const unsigned int numBlocks = mBlock.size();
1590
1591 const unsigned int b0 = FindBlock(start);
1592 unsigned int b1 = FindBlock(start + len - 1);
1593
1594 const auto format = mSampleFormats.Stored();
1595 auto sampleSize = SAMPLE_SIZE(format);
1596
1597 SeqBlock *pBlock;
1598 decltype(pBlock->sb->GetSampleCount()) length;
1599
1600 // One buffer for reuse in various branches here
1601 SampleBuffer scratch;
1602 // The maximum size that should ever be needed
1603 auto scratchSize = mMaxSamples + mMinSamples;
1604
1605 // Special case: if the samples to DELETE are all within a single
1606 // block and the resulting length is not too small, perform the
1607 // deletion within this block:
1608 if (b0 == b1 &&
1609 (length = (pBlock = &mBlock[b0])->sb->GetSampleCount()) - len >= mMinSamples) {
1610 SeqBlock &b = *pBlock;
1611 // start is within block
1612 auto pos = ( start - b.start ).as_size_t();
1613
1614 // Guard against failure of this anyway below with limitSampleBufferSize
1615 wxASSERT(len < length);
1616
1617 // len must be less than length
1618 // because start + len - 1 is also in the block...
1619 auto newLen = ( length - limitSampleBufferSize( length, len ) );
1620
1621 scratch.Allocate(scratchSize, format);
1622 ensureSampleBufferSize(scratch, format, scratchSize, newLen);
1623
1624 Read(scratch.ptr(), format, b, 0, pos, true);
1625 Read(scratch.ptr() + (pos * sampleSize), format,
1626 b,
1627 // ... and therefore pos + len
1628 // is not more than the length of the block
1629 ( pos + len ).as_size_t(), newLen - pos, true);
1630
1631 b.sb = factory.Create(scratch.ptr(), newLen, format);
1632
1633 // Don't make a duplicate array. We can still give Strong-guarantee
1634 // if we modify only one block in place.
1635
1636 // use No-fail-guarantee in remaining steps
1637
1638 for (unsigned int j = b0 + 1; j < numBlocks; j++)
1639 mBlock[j].start -= len;
1640
1641 mNumSamples -= len;
1642
1643 // This consistency check won't throw, it asserts.
1644 // Proof that we kept consistency is not hard.
1645 ConsistencyCheck(wxT("Delete - branch one"), false);
1646 return;
1647 }
1648
1649 // Create a NEW array of blocks
1650 BlockArray newBlock;
1651 newBlock.reserve(numBlocks - (b1 - b0) + 2);
1652
1653 // Copy the blocks before the deletion point over to
1654 // the NEW array
1655 newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b0);
1656 unsigned int i;
1657
1658 // First grab the samples in block b0 before the deletion point
1659 // into preBuffer. If this is enough samples for its own block,
1660 // or if this would be the first block in the array, write it out.
1661 // Otherwise combine it with the previous block (splitting them
1662 // 50/50 if necessary).
1663 const SeqBlock &preBlock = mBlock[b0];
1664 // start is within preBlock
1665 auto preBufferLen = ( start - preBlock.start ).as_size_t();
1666 if (preBufferLen) {
1667 if (preBufferLen >= mMinSamples || b0 == 0) {
1668 if (!scratch.ptr())
1669 scratch.Allocate(scratchSize, format);
1670 ensureSampleBufferSize(scratch, format, scratchSize, preBufferLen);
1671 Read(scratch.ptr(), format, preBlock, 0, preBufferLen, true);
1672 auto pFile =
1673 factory.Create(scratch.ptr(), preBufferLen, format);
1674
1675 newBlock.push_back(SeqBlock(pFile, preBlock.start));
1676 } else {
1677 const SeqBlock &prepreBlock = mBlock[b0 - 1];
1678 const auto prepreLen = prepreBlock.sb->GetSampleCount();
1679 const auto sum = prepreLen + preBufferLen;
1680
1681 if (!scratch.ptr())
1682 scratch.Allocate(scratchSize, format);
1683 ensureSampleBufferSize(scratch, format, scratchSize,
1684 sum);
1685
1686 Read(scratch.ptr(), format, prepreBlock, 0, prepreLen, true);
1687 Read(scratch.ptr() + prepreLen*sampleSize, format,
1688 preBlock, 0, preBufferLen, true);
1689
1690 newBlock.pop_back();
1692 newBlock, prepreBlock.start, scratch.ptr(), sum);
1693 }
1694 }
1695 else {
1696 // The sample where we begin deletion happens to fall
1697 // right on the beginning of a block.
1698 }
1699
1700 // Now, symmetrically, grab the samples in block b1 after the
1701 // deletion point into postBuffer. If this is enough samples
1702 // for its own block, or if this would be the last block in
1703 // the array, write it out. Otherwise combine it with the
1704 // subsequent block (splitting them 50/50 if necessary).
1705 const SeqBlock &postBlock = mBlock[b1];
1706 // start + len - 1 lies within postBlock
1707 const auto postBufferLen = (
1708 (postBlock.start + postBlock.sb->GetSampleCount()) - (start + len)
1709 ).as_size_t();
1710 if (postBufferLen) {
1711 if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) {
1712 if (!scratch.ptr())
1713 // Last use of scratch, can ask for smaller
1714 scratch.Allocate(postBufferLen, format);
1715 // start + len - 1 lies within postBlock
1716 auto pos = (start + len - postBlock.start).as_size_t();
1717 Read(scratch.ptr(), format, postBlock, pos, postBufferLen, true);
1718 auto file =
1719 factory.Create(scratch.ptr(), postBufferLen, format);
1720
1721 newBlock.push_back(SeqBlock(file, start));
1722 } else {
1723 SeqBlock &postpostBlock = mBlock[b1 + 1];
1724 const auto postpostLen = postpostBlock.sb->GetSampleCount();
1725 const auto sum = postpostLen + postBufferLen;
1726
1727 if (!scratch.ptr())
1728 // Last use of scratch, can ask for smaller
1729 scratch.Allocate(sum, format);
1730 // start + len - 1 lies within postBlock
1731 auto pos = (start + len - postBlock.start).as_size_t();
1732 Read(scratch.ptr(), format, postBlock, pos, postBufferLen, true);
1733 Read(scratch.ptr() + (postBufferLen * sampleSize), format,
1734 postpostBlock, 0, postpostLen, true);
1735
1737 newBlock, start, scratch.ptr(), sum);
1738 b1++;
1739 }
1740 }
1741 else {
1742 // The sample where we begin deletion happens to fall
1743 // right on the end of a block.
1744 }
1745
1746 // Copy the remaining blocks over from the old array
1747 for (i = b1 + 1; i < numBlocks; i++)
1748 newBlock.push_back(mBlock[i].Plus(-len));
1749
1751 (newBlock, mNumSamples - len, wxT("Delete - branch two"));
1752}
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22

References SampleBuffer::Allocate(), Blockify(), CommitChangesIfConsistent(), ConsistencyCheck(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), factory, FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, limitSampleBufferSize(), mBlock, mMaxSamples, mMinSamples, mNumSamples, mpFactory, mSampleFormats, SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, SeqBlock::start, SampleFormats::Stored(), THROW_INCONSISTENCY_EXCEPTION, and wxT().

Here is the call graph for this function:

◆ DoAppend()

SeqBlock::SampleBlockPtr Sequence::DoAppend ( constSamplePtr  buffer,
sampleFormat  format,
size_t  len,
bool  coalesce 
)
private

Does not do any dithering.

Exception safety guarantee:
Strong

Definition at line 1431 of file Sequence.cpp.

1433{
1435
1436 if (len == 0)
1437 return result;
1438
1439 auto &factory = *mpFactory;
1440
1441 // Quick check to make sure that it doesn't overflow
1442 if (Overflows(mNumSamples.as_double() + ((double)len)))
1444
1445 BlockArray newBlock;
1446 sampleCount newNumSamples = mNumSamples;
1447
1448 // If the last block is not full, we need to add samples to it
1449 int numBlocks = mBlock.size();
1450 SeqBlock *pLastBlock;
1451 decltype(pLastBlock->sb->GetSampleCount()) length;
1452 size_t bufferSize = mMaxSamples;
1453 const auto dstFormat = mSampleFormats.Stored();
1454 SampleBuffer buffer2(bufferSize, dstFormat);
1455 bool replaceLast = false;
1456 if (coalesce &&
1457 numBlocks > 0 &&
1458 (length =
1459 (pLastBlock = &mBlock.back())->sb->GetSampleCount()) < mMinSamples) {
1460 // Enlarge a sub-minimum block at the end
1461 const SeqBlock &lastBlock = *pLastBlock;
1462 const auto addLen = std::min(mMaxSamples - length, len);
1463
1464 // Reading same format as was saved before causes no dithering
1465 Read(buffer2.ptr(), dstFormat, lastBlock, 0, length, true);
1466
1467 CopySamples(buffer,
1468 format,
1469 buffer2.ptr() + length * SAMPLE_SIZE(dstFormat),
1470 dstFormat,
1471 addLen, DitherType::none);
1472
1473 const auto newLastBlockLen = length + addLen;
1474 SampleBlockPtr pBlock = factory.Create(
1475 buffer2.ptr(),
1476 newLastBlockLen,
1477 dstFormat);
1478 SeqBlock newLastBlock(pBlock, lastBlock.start);
1479
1480 newBlock.push_back( newLastBlock );
1481
1482 len -= addLen;
1483 newNumSamples += addLen;
1484 buffer += addLen * SAMPLE_SIZE(format);
1485
1486 replaceLast = true;
1487 }
1488 // Append the rest as NEW blocks
1489 while (len) {
1490 const auto idealSamples = GetIdealBlockSize();
1491 const auto addedLen = std::min(idealSamples, len);
1492 SampleBlockPtr pBlock;
1493 if (format == dstFormat) {
1494 pBlock = factory.Create(buffer, addedLen, dstFormat);
1495 // It's expected that when not requesting coalescence, the
1496 // data should fit in one block
1497 wxASSERT( coalesce || !result );
1498 result = pBlock;
1499 }
1500 else {
1501 CopySamples(buffer, format, buffer2.ptr(), dstFormat,
1502 addedLen, DitherType::none);
1503 pBlock = factory.Create(buffer2.ptr(), addedLen, dstFormat);
1504 }
1505
1506 newBlock.push_back(SeqBlock(pBlock, newNumSamples));
1507
1508 buffer += addedLen * SAMPLE_SIZE(format);
1509 newNumSamples += addedLen;
1510 len -= addedLen;
1511 }
1512
1513 AppendBlocksIfConsistent(newBlock, replaceLast,
1514 newNumSamples, wxT("Append"));
1515
1516// JKC: During generate we use Append again and again.
1517// If generating a long sequence this test would give O(n^2)
1518// performance - not good!
1519#ifdef VERY_SLOW_CHECKING
1520 ConsistencyCheck(wxT("Append"));
1521#endif
1522
1523 return result;
1524}
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: CloudSyncDTO.h:25
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: Sequence.h:31
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:85
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19

References AppendBlocksIfConsistent(), sampleCount::as_double(), ConsistencyCheck(), CopySamples(), factory, anonymous_namespace{ExportPCM.cpp}::format, GetIdealBlockSize(), mBlock, min(), mMaxSamples, mMinSamples, mNumSamples, mpFactory, mSampleFormats, none, anonymous_namespace{Sequence.cpp}::Overflows(), SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, SeqBlock::start, SampleFormats::Stored(), THROW_INCONSISTENCY_EXCEPTION, and wxT().

Referenced by Append(), AppendNewBlock(), and Flush().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindBlock()

int Sequence::FindBlock ( sampleCount  pos) const

Definition at line 1055 of file Sequence.cpp.

1056{
1057 wxASSERT(pos >= 0 && pos < mNumSamples);
1058
1059 if (pos == 0)
1060 return 0;
1061
1062 int numBlocks = mBlock.size();
1063
1064 size_t lo = 0, hi = numBlocks, guess;
1065 sampleCount loSamples = 0, hiSamples = mNumSamples;
1066
1067 while (true) {
1068 //this is not a binary search, but a
1069 //dictionary search where we guess something smarter than the binary division
1070 //of the unsearched area, since samples are usually proportional to block file number.
1071 const double frac = (pos - loSamples).as_double() /
1072 (hiSamples - loSamples).as_double();
1073 guess = std::min(hi - 1, lo + size_t(frac * (hi - lo)));
1074 const SeqBlock &block = mBlock[guess];
1075
1076 wxASSERT(block.sb->GetSampleCount() > 0);
1077 wxASSERT(lo <= guess && guess < hi && lo < hi);
1078
1079 if (pos < block.start) {
1080 wxASSERT(lo != guess);
1081 hi = guess;
1082 hiSamples = block.start;
1083 }
1084 else {
1085 const sampleCount nextStart = block.start + block.sb->GetSampleCount();
1086 if (pos < nextStart)
1087 break;
1088 else {
1089 wxASSERT(guess < hi - 1);
1090 lo = guess + 1;
1091 loSamples = nextStart;
1092 }
1093 }
1094 }
1095
1096 const int rval = guess;
1097 wxASSERT(rval >= 0 && rval < numBlocks &&
1098 pos >= mBlock[rval].start &&
1099 pos < mBlock[rval].start + mBlock[rval].sb->GetSampleCount());
1100
1101 return rval;
1102}

References mBlock, min(), mNumSamples, SeqBlock::sb, and SeqBlock::start.

Referenced by Copy(), Delete(), Get(), GetBestBlockSize(), GetBlockStart(), GetFloatSampleView(), GetMinMax(), GetRMS(), GetWaveDisplay(), Paste(), and SetSamples().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Flush()

void Sequence::Flush ( )
Exception safety guarantee:
Mixed
Exception safety guarantee:
No-fail – The Sequence will be in a flushed state.
Exception safety guarantee:
Partial – Some initial portion (maybe none) of the append buffer of the Sequence gets appended; no previously flushed contents are lost.

Definition at line 1531 of file Sequence.cpp.

1532{
1533 if (mAppendBufferLen > 0) {
1534
1535 auto cleanup = finally( [&] {
1536 // Blow away the append buffer even in case of failure. May lose some
1537 // data but don't leave the sequence in an un-flushed state.
1538
1539 // Use No-fail-guarantee of these steps.
1540 mAppendBufferLen = 0;
1542 mAppendEffectiveFormat = narrowestSampleFormat; // defaulted again
1543 } );
1544
1545 // Already dithered if needed when accumulated into mAppendBuffer:
1547 mAppendBufferLen, true);
1548 // Change our effective format now that DoAppend didn't throw
1550 }
1551}

References DoAppend(), SampleBuffer::Free(), mAppendBuffer, mAppendBufferLen, mAppendEffectiveFormat, mSampleFormats, narrowestSampleFormat, SampleBuffer::ptr(), SampleFormats::Stored(), and SampleFormats::UpdateEffective().

Here is the call graph for this function:

◆ Get() [1/2]

bool Sequence::Get ( int  b,
samplePtr  buffer,
sampleFormat  format,
sampleCount  start,
size_t  len,
bool  mayThrow 
) const
private

Definition at line 1185 of file Sequence.cpp.

1187{
1188 bool result = true;
1189 while (len) {
1190 const SeqBlock &block = mBlock[b];
1191 // start is in block
1192 const auto bstart = (start - block.start).as_size_t();
1193 // bstart is not more than block length
1194 const auto blen = std::min(len, block.sb->GetSampleCount() - bstart);
1195
1196 if (! Read(buffer, format, block, bstart, blen, mayThrow) )
1197 result = false;
1198
1199 len -= blen;
1200 buffer += (blen * SAMPLE_SIZE(format));
1201 b++;
1202 start += blen;
1203 }
1204 return result;
1205}

References anonymous_namespace{ExportPCM.cpp}::format, mBlock, min(), Read(), SAMPLE_SIZE, SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ Get() [2/2]

bool Sequence::Get ( samplePtr  buffer,
sampleFormat  format,
sampleCount  start,
size_t  len,
bool  mayThrow 
) const

Get a range of samples from the sequence.

Definition at line 1146 of file Sequence.cpp.

1148{
1149 const auto sampleSize = SAMPLE_SIZE(format);
1150 bool outOfBounds = false;
1151
1152 if (start < 0) {
1153 const auto fillLen = limitSampleBufferSize(len, -start);
1154 ClearSamples(buffer, format, 0, fillLen);
1155 if (len == fillLen)
1156 return false;
1157 start = 0;
1158 buffer += fillLen * sampleSize;
1159 len -= fillLen;
1160 outOfBounds = true;
1161 }
1162
1163 if (start >= mNumSamples) {
1164 ClearSamples(buffer, format, 0, len);
1165 return false;
1166 }
1167
1168 assert(start >= 0); // Returned already if this is false
1169 assert(start < mNumSamples); // Returned already if this is false
1170 if (start + len > mNumSamples) {
1171 // Previous assertions justify as_size_t
1172 const auto excess = (start + len - mNumSamples).as_size_t();
1173 ClearSamples(buffer, format, len - excess, excess);
1174 if (len == excess)
1175 return true;
1176 len -= excess;
1177 outOfBounds = true;
1178 }
1179 int b = FindBlock(start);
1180
1181 return Get(b, buffer, format, start, len, mayThrow) &&
1182 !outOfBounds;
1183}
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)

References ClearSamples(), FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, Get(), limitSampleBufferSize(), mNumSamples, and SAMPLE_SIZE.

Referenced by Copy(), Get(), and Paste().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetAppendBuffer()

constSamplePtr Sequence::GetAppendBuffer ( ) const
inline

Definition at line 236 of file Sequence.h.

236{ return mAppendBuffer.ptr(); }

◆ GetAppendBufferLen()

size_t Sequence::GetAppendBufferLen ( ) const
inline

Definition at line 235 of file Sequence.h.

235{ return mAppendBufferLen; }

◆ GetBestBlockSize()

size_t Sequence::GetBestBlockSize ( sampleCount  start) const

Definition at line 787 of file Sequence.cpp.

788{
789 // This method returns a nice number of samples you should try to grab in
790 // one big chunk in order to land on a block boundary, based on the starting
791 // sample. The value returned will always be nonzero and will be no larger
792 // than the value of GetMaxBlockSize()
793
794 if (start < 0 || start >= mNumSamples)
795 return mMaxSamples;
796
797 int b = FindBlock(start);
798 int numBlocks = mBlock.size();
799
800 const SeqBlock &block = mBlock[b];
801 // start is in block:
802 auto result = (block.start + block.sb->GetSampleCount() - start).as_size_t();
803
804 decltype(result) length;
805 while(result < mMinSamples && b+1<numBlocks &&
806 ((length = mBlock[b+1].sb->GetSampleCount()) + result) <= mMaxSamples) {
807 b++;
808 result += length;
809 }
810
811 wxASSERT(result > 0 && result <= mMaxSamples);
812
813 return result;
814}

References FindBlock(), mBlock, mMaxSamples, mMinSamples, mNumSamples, SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ GetBlockArray() [1/2]

BlockArray & Sequence::GetBlockArray ( )
inline

Definition at line 232 of file Sequence.h.

232{ return mBlock; }

Referenced by GetAllSeqBlocks(), and GetWaveDisplay().

Here is the caller graph for this function:

◆ GetBlockArray() [2/2]

const BlockArray & Sequence::GetBlockArray ( ) const
inline

Definition at line 233 of file Sequence.h.

233{ return mBlock; }

◆ GetBlockStart()

sampleCount Sequence::GetBlockStart ( sampleCount  position) const
private
Returns
possibly a large or negative value

Definition at line 781 of file Sequence.cpp.

782{
783 int b = FindBlock(position);
784 return mBlock[b].start;
785}

References FindBlock(), and mBlock.

Referenced by GetFloatSampleView().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetErrorOpening()

bool Sequence::GetErrorOpening ( ) const
inline

Definition at line 189 of file Sequence.h.

189{ return mErrorOpening; }
bool mErrorOpening
Definition: Sequence.h:265

◆ GetFactory()

const SampleBlockFactoryPtr & Sequence::GetFactory ( ) const
inline

Definition at line 175 of file Sequence.h.

176 {
177 return mpFactory;
178 }

◆ GetFloatSampleView()

AudioSegmentSampleView Sequence::GetFloatSampleView ( sampleCount  start,
size_t  len,
bool  mayThrow 
) const

Get a view of the lesser of len samples or what remains after start

Precondition
start < GetNumSamples()

Definition at line 1126 of file Sequence.cpp.

1128{
1129 assert(start < mNumSamples);
1130 length = limitSampleBufferSize(length, mNumSamples - start);
1131 std::vector<BlockSampleView> blockViews;
1132 // `sequenceOffset` cannot be larger than `GetMaxBlockSize()`, a `size_t` =>
1133 // no narrowing possible.
1134 const auto sequenceOffset = (start - GetBlockStart(start)).as_size_t();
1135 auto cursor = start;
1136 while (cursor < start + length)
1137 {
1138 const auto b = FindBlock(cursor);
1139 const SeqBlock& block = mBlock[b];
1140 blockViews.push_back(block.sb->GetFloatSampleView(mayThrow));
1141 cursor = block.start + block.sb->GetSampleCount();
1142 }
1143 return { std::move(blockViews), sequenceOffset, length };
1144}
sampleCount GetBlockStart(sampleCount position) const
Definition: Sequence.cpp:781

References FindBlock(), GetBlockStart(), limitSampleBufferSize(), mBlock, mNumSamples, SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ GetIdealAppendLen()

size_t Sequence::GetIdealAppendLen ( ) const

Definition at line 1325 of file Sequence.cpp.

1326{
1327 int numBlocks = mBlock.size();
1328 const auto max = GetMaxBlockSize();
1329
1330 if (numBlocks == 0)
1331 return max;
1332
1333 const auto lastBlockLen = mBlock.back().sb->GetSampleCount();
1334 if (lastBlockLen >= max)
1335 return max;
1336 else
1337 return max - lastBlockLen;
1338}
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:80

References GetMaxBlockSize(), and mBlock.

Referenced by Append().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetIdealBlockSize()

size_t Sequence::GetIdealBlockSize ( ) const

Definition at line 85 of file Sequence.cpp.

86{
87 return mMaxSamples;
88}

References mMaxSamples.

Referenced by DoAppend(), and InsertSilence().

Here is the caller graph for this function:

◆ GetMaxBlockSize()

size_t Sequence::GetMaxBlockSize ( ) const

Definition at line 80 of file Sequence.cpp.

81{
82 return mMaxSamples;
83}

References mMaxSamples.

Referenced by GetIdealAppendLen(), and GetWaveDisplay().

Here is the caller graph for this function:

◆ GetMaxDiskBlockSize()

size_t Sequence::GetMaxDiskBlockSize ( )
static

Definition at line 1900 of file Sequence.cpp.

1901{
1902 return sMaxDiskBlockSize;
1903}

References sMaxDiskBlockSize.

Referenced by BenchmarkDialog::OnRun().

Here is the caller graph for this function:

◆ GetMinMax()

std::pair< float, float > Sequence::GetMinMax ( sampleCount  start,
sampleCount  len,
bool  mayThrow 
) const

Definition at line 241 of file Sequence.cpp.

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].sb->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.sb;
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->GetSampleCount() - 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.sb;
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}

References FindBlock(), limitSampleBufferSize(), mBlock, min(), mMaxSamples, SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ GetNumSamples()

sampleCount Sequence::GetNumSamples ( ) const
inline

Definition at line 87 of file Sequence.h.

87{ return mNumSamples; }

Referenced by SpecCache::CalculateOneSpectrum(), GetWaveDisplay(), and WaveClipWaveformCache::GetWaveDisplay().

Here is the caller graph for this function:

◆ GetRMS()

float Sequence::GetRMS ( sampleCount  start,
sampleCount  len,
bool  mayThrow 
) const

Definition at line 323 of file Sequence.cpp.

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 &sb = theBlock.sb;
342 auto results = sb->GetMinMaxRMS(mayThrow);
343
344 const auto fileLen = sb->GetSampleCount();
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 &sb = theBlock.sb;
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 + sb->GetSampleCount() - start).as_size_t();
361 wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19
362 const auto l0 = limitSampleBufferSize( maxl0, len );
363
364 auto results = sb->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 &sb = theBlock.sb;
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 = sb->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}
__finl float_x4 __vecc sqrt(const float_x4 &a)

References sampleCount::as_double(), FindBlock(), limitSampleBufferSize(), mBlock, mMaxSamples, SeqBlock::sb, staffpad::audio::simd::sqrt(), and SeqBlock::start.

Here is the call graph for this function:

◆ GetSampleFormats()

SampleFormats Sequence::GetSampleFormats ( ) const

Definition at line 98 of file Sequence.cpp.

99{
100 return mSampleFormats;
101}

References mSampleFormats.

◆ HandleXMLChild()

XMLTagHandler * Sequence::HandleXMLChild ( const std::string_view &  tag)
overridevirtual

Implements XMLTagHandler.

Definition at line 993 of file Sequence.cpp.

994{
995 if (tag == WaveBlock_tag)
996 {
997 return this;
998 }
999
1000 return nullptr;
1001}
static const char * WaveBlock_tag
Definition: Sequence.h:57

References WaveBlock_tag.

◆ HandleXMLEndTag()

void Sequence::HandleXMLEndTag ( const std::string_view &  tag)
override

Definition at line 951 of file Sequence.cpp.

952{
953 if ((tag != Sequence_tag) != 0)
954 {
955 return;
956 }
957
958 // Make sure that the sequence is valid.
959
960 // Make sure that start times and lengths are consistent
961 sampleCount numSamples = 0;
962 for (unsigned b = 0, nn = mBlock.size(); b < nn; b++)
963 {
964 SeqBlock &block = mBlock[b];
965 if (block.start != numSamples)
966 {
967 wxLogWarning(
968 wxT("Gap detected in project file.\n")
969 wxT(" Start (%s) for block file %lld is not one sample past end of previous block (%s).\n")
970 wxT(" Moving start so blocks are contiguous."),
971 // PRL: Why bother with Internat when the above is just wxT?
973 block.sb->GetBlockID(),
974 Internat::ToString(numSamples.as_double(), 0));
975 block.start = numSamples;
976 mErrorOpening = true;
977 }
978 numSamples += block.sb->GetSampleCount();
979 }
980
981 if (mNumSamples != numSamples)
982 {
983 wxLogWarning(
984 wxT("Gap detected in project file. Correcting sequence sample count from %s to %s."),
985 // PRL: Why bother with Internat when the above is just wxT?
987 Internat::ToString(numSamples.as_double(), 0));
988 mNumSamples = numSamples;
989 mErrorOpening = true;
990 }
991}
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
static const char * Sequence_tag
Definition: Sequence.h:56

References sampleCount::as_double(), mBlock, mErrorOpening, mNumSamples, SeqBlock::sb, Sequence_tag, SeqBlock::start, Internat::ToString(), and wxT().

Here is the call graph for this function:

◆ HandleXMLTag()

bool Sequence::HandleXMLTag ( const std::string_view &  tag,
const AttributesList attrs 
)
overridevirtual

Implements XMLTagHandler.

Definition at line 822 of file Sequence.cpp.

823{
824 auto &factory = *mpFactory;
825
826 /* handle waveblock tag and its attributes */
827 if (tag == WaveBlock_tag)
828 {
829 SeqBlock wb;
830
831 // Give SampleBlock a go at the attributes first
832 wb.sb = factory.CreateFromXML(mSampleFormats.Stored(), attrs);
833 if (wb.sb == nullptr)
834 {
835 mErrorOpening = true;
836 return false;
837 }
838
839 // loop through attrs, which is a null-terminated list of attribute-value pairs
840 for (auto pair : attrs)
841 {
842 auto attr = pair.first;
843 auto value = pair.second;
844
845 if (attr == Start_attr)
846 {
847 // This attribute is a sample offset, so can be 64bit
848 sampleCount::type start;
849
850 if (!value.TryGet(start))
851 {
852 mErrorOpening = true;
853 return false;
854 }
855
856 wb.start = start;
857 }
858 }
859
860 mBlock.push_back(wb);
861
862 return true;
863 }
864
865 /* handle sequence tag and its attributes */
866 if (tag == Sequence_tag)
867 {
868 std::optional<sampleFormat> effective;
869 sampleFormat stored = floatSample;
870 for (auto pair : attrs)
871 {
872 auto attr = pair.first;
873 auto value = pair.second;
874
875 long long nValue = 0;
876
877 if (attr == MaxSamples_attr)
878 {
879 // This attribute is a sample count, so can be 64bit
880 if (!value.TryGet(nValue))
881 {
882 mErrorOpening = true;
883 return false;
884 }
885
886 // Dominic, 12/10/2006:
887 // Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024
888 // - that's a pretty wide range of reasonable values.
889 if ((nValue < 1024) || (nValue > 64 * 1024 * 1024))
890 {
891 mErrorOpening = true;
892 return false;
893 }
894
895 // nValue is now safe for size_t
896 mMaxSamples = nValue;
897 }
898 else if (attr == SampleFormat_attr)
899 {
900 // This attribute is a sample format, normal int
901 long fValue;
902
903 if (!value.TryGet(fValue) || !IsValidSampleFormat(fValue))
904 {
905 mErrorOpening = true;
906 return false;
907 }
908 stored = static_cast<sampleFormat>( fValue );
909 }
910 else if (attr == EffectiveSampleFormat_attr)
911 {
912 // This attribute is a sample format, normal int
913 long fValue;
914
915 if (!value.TryGet(fValue) || !IsValidSampleFormat(fValue))
916 {
917 mErrorOpening = true;
918 return false;
919 }
920 effective.emplace(static_cast<sampleFormat>(fValue));
921 }
922 else if (attr == NumSamples_attr)
923 {
924 // This attribute is a sample count, so can be 64bit
925 if (!value.TryGet(nValue) || (nValue < 0))
926 {
927 mErrorOpening = true;
928 return false;
929 }
930 mNumSamples = nValue;
931 }
932 } // for
933
934 // Set at least the stored format as it was saved
936 SampleFormats{ effective.value_or(stored), stored };
937
938 // Check whether the invariant of SampleFormats changed the value
939 // effective has no value if opening a project from before 3.3
940 if (effective && mSampleFormats.Effective() != *effective) {
941 mErrorOpening = true;
942 return false;
943 }
944
945 return true;
946 }
947
948 return false;
949}
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
static constexpr auto Start_attr
Definition: Sequence.cpp:816
static constexpr auto NumSamples_attr
Definition: Sequence.cpp:820
static constexpr auto SampleFormat_attr
Definition: Sequence.cpp:818
static constexpr auto EffectiveSampleFormat_attr
Definition: Sequence.cpp:819
static constexpr auto MaxSamples_attr
Definition: Sequence.cpp:817
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
Definition: Sequence.cpp:1905
long long type
Definition: SampleCount.h:21

References SampleFormats::Effective(), EffectiveSampleFormat_attr, factory, floatSample, IsValidSampleFormat(), MaxSamples_attr, mBlock, mErrorOpening, mMaxSamples, mNumSamples, mpFactory, mSampleFormats, NumSamples_attr, SampleFormat_attr, SeqBlock::sb, Sequence_tag, SeqBlock::start, Start_attr, SampleFormats::Stored(), and WaveBlock_tag.

Here is the call graph for this function:

◆ InsertSilence()

void Sequence::InsertSilence ( sampleCount  s0,
sampleCount  len 
)
Exception safety guarantee:
Strong

Definition at line 712 of file Sequence.cpp.

713{
714 auto &factory = *mpFactory;
715
716 // Quick check to make sure that it doesn't overflow
717 if (Overflows((mNumSamples.as_double()) + (len.as_double())))
719
720 if (len <= 0)
721 return;
722
723 // Create a NEW track containing as much silence as we
724 // need to insert, and then call Paste to do the insertion.
725
727
728 auto idealSamples = GetIdealBlockSize();
729
730 sampleCount pos = 0;
731
732 // Could nBlocks overflow a size_t? Not very likely. You need perhaps
733 // 2 ^ 52 samples which is over 3000 years at 44.1 kHz.
734 auto nBlocks = (len + idealSamples - 1) / idealSamples;
735 sTrack.mBlock.reserve(nBlocks.as_size_t());
736
737 const auto format = mSampleFormats.Stored();
738 if (len >= idealSamples) {
739 auto silentFile = factory.CreateSilent(
740 idealSamples,
741 format);
742 while (len >= idealSamples) {
743 sTrack.mBlock.push_back(SeqBlock(silentFile, pos));
744
745 pos += idealSamples;
746 len -= idealSamples;
747 }
748 }
749 if (len != 0) {
750 // len is not more than idealSamples:
751 sTrack.mBlock.push_back(SeqBlock(
752 factory.CreateSilent(len.as_size_t(), format), pos));
753 pos += len;
754 }
755
756 sTrack.mNumSamples = pos;
757
758 // use Strong-guarantee
759 Paste(s0, &sTrack);
760}
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:53
size_t as_size_t() const
Definition: SampleCount.cpp:16

References sampleCount::as_double(), sampleCount::as_size_t(), factory, anonymous_namespace{ExportPCM.cpp}::format, GetIdealBlockSize(), mNumSamples, mpFactory, mSampleFormats, anonymous_namespace{Sequence.cpp}::Overflows(), Paste(), SampleFormats::Stored(), and THROW_INCONSISTENCY_EXCEPTION.

Here is the call graph for this function:

◆ IsValidSampleFormat()

bool Sequence::IsValidSampleFormat ( const int  nValue)
static

true if nValue is one of the sampleFormat enum values

Definition at line 1905 of file Sequence.cpp.

1906{
1907 auto nValue = static_cast<sampleFormat>(iValue);
1908 return (nValue == int16Sample) || (nValue == int24Sample) || (nValue == floatSample);
1909}

References floatSample, int16Sample, and int24Sample.

Referenced by AUPImportFileHandle::HandleSequence(), HandleXMLTag(), and WaveTrack::HandleXMLTag().

Here is the caller graph for this function:

◆ operator=()

Sequence & Sequence::operator= ( const Sequence )
delete

◆ Paste()

void Sequence::Paste ( sampleCount  s,
const Sequence src 
)
Exception safety guarantee:
Strong

Definition at line 499 of file Sequence.cpp.

500{
501 if ((s < 0) || (s > mNumSamples))
502 {
503 wxLogError(
504 wxT("Sequence::Paste: sampleCount s %s is < 0 or > mNumSamples %s)."),
505 // PRL: Why bother with Internat when the above is just wxT?
509 }
510
511 // Quick check to make sure that it doesn't overflow
513 {
514 wxLogError(
515 wxT("Sequence::Paste: mNumSamples %s + src->mNumSamples %s would overflow."),
516 // PRL: Why bother with Internat when the above is just wxT?
520 }
521
522 const auto format = mSampleFormats.Stored();
523 if (src->mSampleFormats.Stored() != format)
524 {
525 wxLogError(
526 wxT("Sequence::Paste: Sample format to be pasted, %s, does not match destination format, %s."),
528 GetSampleFormatStr(format).Debug());
530 }
531
532 const BlockArray &srcBlock = src->mBlock;
533 auto addedLen = src->mNumSamples;
534 const unsigned int srcNumBlocks = srcBlock.size();
535 auto sampleSize = SAMPLE_SIZE(format);
536
537 if (addedLen == 0 || srcNumBlocks == 0)
538 return;
539
540 const size_t numBlocks = mBlock.size();
541
542 // Decide whether to share sample blocks or make new copies, when whole block
543 // contents are used -- must copy if factories are different:
544 auto pUseFactory =
545 (src->mpFactory == mpFactory) ? nullptr : mpFactory.get();
546
547 if (numBlocks == 0 ||
548 (s == mNumSamples && mBlock.back().sb->GetSampleCount() >= mMinSamples)) {
549 // Special case: this track is currently empty, or it's safe to append
550 // onto the end because the current last block is longer than the
551 // minimum size
552
553 // Build and swap a copy so there is a strong exception safety guarantee
554 BlockArray newBlock{ mBlock };
555 sampleCount samples = mNumSamples;
556 for (unsigned int i = 0; i < srcNumBlocks; i++)
557 // AppendBlock may throw for limited disk space, if pasting from
558 // one project into another.
559 AppendBlock(pUseFactory, format,
560 newBlock, samples, srcBlock[i]);
561
563 (newBlock, samples, wxT("Paste branch one"));
565 return;
566 }
567
568 const int b = (s == mNumSamples) ? mBlock.size() - 1 : FindBlock(s);
569 wxASSERT((b >= 0) && (b < (int)numBlocks));
570 SeqBlock *const pBlock = &mBlock[b];
571 const auto length = pBlock->sb->GetSampleCount();
572 const auto largerBlockLen = addedLen + length;
573 // PRL: when insertion point is the first sample of a block,
574 // and the following test fails, perhaps we could test
575 // whether coalescence with the previous block is possible.
576 if (largerBlockLen <= mMaxSamples) {
577 // Special case: we can fit all of the NEW samples inside of
578 // one block!
579
580 SeqBlock &block = *pBlock;
581 // largerBlockLen is not more than mMaxSamples...
582 SampleBuffer buffer(largerBlockLen.as_size_t(), format);
583
584 // ...and addedLen is not more than largerBlockLen
585 auto sAddedLen = addedLen.as_size_t();
586 // s lies within block:
587 auto splitPoint = ( s - block.start ).as_size_t();
588 Read(buffer.ptr(), format, block, 0, splitPoint, true);
589 src->Get(0, buffer.ptr() + splitPoint*sampleSize,
590 format, 0, sAddedLen, true);
591 Read(buffer.ptr() + (splitPoint + sAddedLen) * sampleSize,
592 format, block,
593 splitPoint, length - splitPoint, true);
594
595 // largerBlockLen is not more than mMaxSamples...
596 block.sb = mpFactory->Create(
597 buffer.ptr(),
598 largerBlockLen.as_size_t(),
599 format);
600
601 // Don't make a duplicate array. We can still give Strong-guarantee
602 // if we modify only one block in place.
603
604 // use No-fail-guarantee in remaining steps
605 for (unsigned int i = b + 1; i < numBlocks; i++)
606 mBlock[i].start += addedLen;
607
608 mNumSamples += addedLen;
609
610 // This consistency check won't throw, it asserts.
611 // Proof that we kept consistency is not hard.
612 ConsistencyCheck(wxT("Paste branch two"), false);
614 return;
615 }
616
617 // Case three: if we are inserting four or fewer blocks,
618 // it's simplest to just lump all the data together
619 // into one big block along with the split block,
620 // then resplit it all
621 BlockArray newBlock;
622 newBlock.reserve(numBlocks + srcNumBlocks + 2);
623 newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b);
624
625 SeqBlock &splitBlock = mBlock[b];
626 auto splitLen = splitBlock.sb->GetSampleCount();
627 // s lies within splitBlock
628 auto splitPoint = ( s - splitBlock.start ).as_size_t();
629
630 unsigned int i;
631 if (srcNumBlocks <= 4) {
632
633 // addedLen is at most four times maximum block size
634 auto sAddedLen = addedLen.as_size_t();
635 const auto sum = splitLen + sAddedLen;
636
637 SampleBuffer sumBuffer(sum, format);
638 Read(sumBuffer.ptr(), format, splitBlock, 0, splitPoint, true);
639 src->Get(0, sumBuffer.ptr() + splitPoint * sampleSize,
640 format,
641 0, sAddedLen, true);
642 Read(sumBuffer.ptr() + (splitPoint + sAddedLen) * sampleSize, format,
643 splitBlock, splitPoint,
644 splitLen - splitPoint, true);
645
647 newBlock, splitBlock.start, sumBuffer.ptr(), sum);
648 } else {
649
650 // The final case is that we're inserting at least five blocks.
651 // We divide these into three groups: the first two get merged
652 // with the first half of the split block, the middle ones get
653 // used whole, and the last two get merged with the last
654 // half of the split block.
655
656 const auto srcFirstTwoLen =
657 srcBlock[0].sb->GetSampleCount() + srcBlock[1].sb->GetSampleCount();
658 const auto leftLen = splitPoint + srcFirstTwoLen;
659
660 const SeqBlock &penultimate = srcBlock[srcNumBlocks - 2];
661 const auto srcLastTwoLen =
662 penultimate.sb->GetSampleCount() +
663 srcBlock[srcNumBlocks - 1].sb->GetSampleCount();
664 const auto rightSplit = splitBlock.sb->GetSampleCount() - splitPoint;
665 const auto rightLen = rightSplit + srcLastTwoLen;
666
667 SampleBuffer sampleBuffer(std::max(leftLen, rightLen), format);
668
669 Read(sampleBuffer.ptr(), format, splitBlock, 0, splitPoint, true);
670 src->Get(0, sampleBuffer.ptr() + splitPoint*sampleSize,
671 format, 0, srcFirstTwoLen, true);
672
674 newBlock, splitBlock.start, sampleBuffer.ptr(), leftLen);
675
676 for (i = 2; i < srcNumBlocks - 2; i++) {
677 const SeqBlock &block = srcBlock[i];
678 auto sb = ShareOrCopySampleBlock(
679 pUseFactory, format, block.sb );
680 newBlock.push_back(SeqBlock(sb, block.start + s));
681 }
682
683 auto lastStart = penultimate.start;
684 src->Get(srcNumBlocks - 2, sampleBuffer.ptr(), format,
685 lastStart, srcLastTwoLen, true);
686 Read(sampleBuffer.ptr() + srcLastTwoLen * sampleSize, format,
687 splitBlock, splitPoint, rightSplit, true);
688
690 newBlock, s + lastStart, sampleBuffer.ptr(), rightLen);
691 }
692
693 // Copy remaining blocks to NEW block array and
694 // swap the NEW block array in for the old
695 for (i = b + 1; i < numBlocks; i++)
696 newBlock.push_back(mBlock[i].Plus(addedLen));
697
699 (newBlock, mNumSamples + addedLen, wxT("Paste branch three"));
700
702}
TranslatableString GetSampleFormatStr(sampleFormat format)
wxString Debug() const
Format as an English string for debugging logs and developers' eyes, not for end users.

References AppendBlock(), sampleCount::as_double(), Blockify(), CommitChangesIfConsistent(), ConsistencyCheck(), TranslatableString::Debug(), SampleFormats::Effective(), FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, Get(), GetSampleFormatStr(), mBlock, mMaxSamples, mMinSamples, mNumSamples, mpFactory, mSampleFormats, anonymous_namespace{Sequence.cpp}::Overflows(), SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, anonymous_namespace{Sequence.cpp}::ShareOrCopySampleBlock(), SeqBlock::start, SampleFormats::Stored(), THROW_INCONSISTENCY_EXCEPTION, Internat::ToString(), SampleFormats::UpdateEffective(), and wxT().

Referenced by InsertSilence(), and Sequence().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Read()

bool Sequence::Read ( samplePtr  buffer,
sampleFormat  format,
const SeqBlock b,
size_t  blockRelativeStart,
size_t  len,
bool  mayThrow 
)
static

Definition at line 1105 of file Sequence.cpp.

1108{
1109 const auto &sb = b.sb;
1110
1111 wxASSERT(blockRelativeStart + len <= sb->GetSampleCount());
1112
1113 // Either throws, or of !mayThrow, tells how many were really read
1114 auto result = sb->GetSamples(buffer, format, blockRelativeStart, len, mayThrow);
1115
1116 if (result != len)
1117 {
1118 wxLogWarning(wxT("Expected to read %ld samples, got %ld samples."),
1119 len, result);
1120 return false;
1121 }
1122
1123 return true;
1124}

References anonymous_namespace{ExportPCM.cpp}::format, SeqBlock::sb, and wxT().

Referenced by ConvertToSampleFormat(), Delete(), DoAppend(), Get(), GetWaveDisplay(), Paste(), and SetSamples().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetMaxDiskBlockSize()

void Sequence::SetMaxDiskBlockSize ( size_t  bytes)
static

Definition at line 1895 of file Sequence.cpp.

1896{
1897 sMaxDiskBlockSize = bytes;
1898}

References sMaxDiskBlockSize.

Referenced by AudacityApp::InitPart2(), and BenchmarkDialog::OnRun().

Here is the caller graph for this function:

◆ SetSamples()

void Sequence::SetSamples ( constSamplePtr  buffer,
sampleFormat  format,
sampleCount  start,
sampleCount  len,
sampleFormat  effectiveFormat 
)

Pass nullptr to set silence.

Note that len is not size_t, because nullptr may be passed for buffer, in which case, silence is inserted, possibly a large amount.

Exception safety guarantee:
Strong
Exception safety guarantee:
Strong
Parameters
effectiveFormatMake the effective format of the data at least the minumum of this value and `format`. (Maybe wider, if merging with preexistent data.) If the data are later narrowed from stored format, but not narrower than the effective, then no dithering will occur.

Definition at line 1209 of file Sequence.cpp.

1211{
1212 effectiveFormat = std::min(effectiveFormat, format);
1213 auto &factory = *mpFactory;
1214
1215 const auto size = mBlock.size();
1216
1217 if (start < 0 || start + len > mNumSamples)
1219
1220 size_t tempSize = mMaxSamples;
1221 const auto dstFormat = mSampleFormats.Stored();
1222 // to do: allocate this only on demand
1223 SampleBuffer scratch(tempSize, dstFormat);
1224
1225 SampleBuffer temp;
1226 if (buffer && format != dstFormat) {
1227 temp.Allocate(tempSize, dstFormat);
1228 }
1229
1230 int b = FindBlock(start);
1231 BlockArray newBlock;
1232 std::copy( mBlock.begin(), mBlock.begin() + b, std::back_inserter(newBlock) );
1233
1234 while (len > 0
1235 // Redundant termination condition,
1236 // but it guards against infinite loop in case of inconsistencies
1237 // (too-small files, not yet seen?)
1238 // that cause the loop to make no progress because blen == 0
1239 && b < (int)size
1240 ) {
1241 newBlock.push_back( mBlock[b] );
1242 SeqBlock &block = newBlock.back();
1243 // start is within block
1244 const auto bstart = ( start - block.start ).as_size_t();
1245 const auto fileLength = block.sb->GetSampleCount();
1246
1247 // the std::min is a guard against inconsistent Sequence
1248 const auto blen =
1249 limitSampleBufferSize( fileLength - std::min( bstart, fileLength ),
1250 len );
1251 wxASSERT(blen == 0 || bstart + blen <= fileLength);
1252
1253#if 0
1254 // PRL: This inconsistency (too-big file) has been seen in "the wild"
1255 // in 2.2.0. It is the least problematic kind of inconsistency.
1256 // We will tolerate it for 2.2.1.
1257 // Not known whether it is only in projects saved in earlier versions.
1258 // After 2.2.1, we should detect and correct it at file loading time.
1259 if (fileLength > mMaxSamples) {
1261 }
1262#endif
1263
1264 ensureSampleBufferSize(scratch, dstFormat, tempSize, fileLength,
1265 &temp);
1266
1267 auto useBuffer = buffer;
1268 if (buffer && format != dstFormat)
1269 {
1270 // To do: remove the extra movement.
1271 // Note: we ensured temp can hold fileLength. blen is not more
1272 CopySamples(buffer, format, temp.ptr(), dstFormat, blen,
1273 (dstFormat < effectiveFormat
1275 useBuffer = temp.ptr();
1276 }
1277
1278 // We don't ever write to an existing block; to support Undo,
1279 // we copy the old block entirely into memory, dereference it,
1280 // make the change, and then write the NEW block to disk.
1281
1282 if ( bstart > 0 || blen < fileLength ) {
1283 // First or last block is only partially overwritten
1284 Read(scratch.ptr(), dstFormat, block, 0, fileLength, true);
1285
1286 if (useBuffer) {
1287 auto sampleSize = SAMPLE_SIZE(dstFormat);
1288 memcpy(scratch.ptr() +
1289 bstart * sampleSize, useBuffer, blen * sampleSize);
1290 }
1291 else
1292 ClearSamples(scratch.ptr(), dstFormat, bstart, blen);
1293
1294 block.sb = factory.Create(
1295 scratch.ptr(),
1296 fileLength,
1297 dstFormat);
1298 }
1299 else {
1300 // Avoid reading the disk when the replacement is total
1301 if (useBuffer)
1302 block.sb = factory.Create(useBuffer, fileLength, dstFormat);
1303 else
1304 block.sb = factory.CreateSilent(fileLength, dstFormat);
1305 }
1306
1307 // blen might be zero for inconsistent Sequence...
1308 if( buffer )
1309 buffer += (blen * SAMPLE_SIZE(format));
1310
1311 len -= blen;
1312 start += blen;
1313
1314 // ... but this, at least, always guarantees some loop progress:
1315 b++;
1316 }
1317
1318 std::copy( mBlock.begin() + b, mBlock.end(), std::back_inserter(newBlock) );
1319
1320 CommitChangesIfConsistent( newBlock, mNumSamples, wxT("SetSamples") );
1321
1322 mSampleFormats.UpdateEffective(effectiveFormat);
1323}

References SampleBuffer::Allocate(), ClearSamples(), CommitChangesIfConsistent(), staffpad::vo::copy(), CopySamples(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), factory, FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, gHighQualityDither, limitSampleBufferSize(), mBlock, min(), mMaxSamples, mNumSamples, mpFactory, mSampleFormats, none, SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, size, SeqBlock::start, SampleFormats::Stored(), THROW_INCONSISTENCY_EXCEPTION, SampleFormats::UpdateEffective(), and wxT().

Referenced by SetSilence().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetSilence()

void Sequence::SetSilence ( sampleCount  s0,
sampleCount  len 
)
Exception safety guarantee:
Strong

Definition at line 705 of file Sequence.cpp.

706{
707 // Exact zeroes won't need dithering
709}
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, sampleCount len, sampleFormat effectiveFormat)
Pass nullptr to set silence.
Definition: Sequence.cpp:1209

References mSampleFormats, narrowestSampleFormat, SetSamples(), and SampleFormats::Stored().

Here is the call graph for this function:

◆ WriteXML()

void Sequence::WriteXML ( XMLWriter xmlFile) const

Definition at line 1004 of file Sequence.cpp.

1006{
1007 unsigned int b;
1008
1009 xmlFile.StartTag(Sequence_tag);
1010
1013 static_cast<size_t>( mSampleFormats.Stored() ) );
1014 // This attribute was added in 3.0.3:
1016 static_cast<size_t>( mSampleFormats.Effective() ));
1018
1019 for (b = 0; b < mBlock.size(); b++) {
1020 const SeqBlock &bb = mBlock[b];
1021
1022 // See http://bugzilla.audacityteam.org/show_bug.cgi?id=451.
1023 if (bb.sb->GetSampleCount() > mMaxSamples)
1024 {
1025 // PRL: Bill observed this error. Not sure how it was caused.
1026 // I have added code in ConsistencyCheck that should abort the
1027 // editing operation that caused this, not fixing
1028 // the problem but moving the point of detection earlier if we
1029 // find a reproducible case.
1030 using namespace BasicUI;
1031 auto sMsg =
1032 XO("Sequence has block file exceeding maximum %s samples per block.\nTruncating to this maximum length.")
1033 .Format( Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0) );
1035 sMsg,
1037 .Caption(XO("Warning - Truncating Overlong Block File"))
1038 .IconStyle(Icon::Warning)
1039 .ButtonStyle(Button::Ok));
1040 wxLogWarning(sMsg.Translation()); //Debug?
1041// bb.sb->SetLength(mMaxSamples);
1042 }
1043
1044 xmlFile.StartTag(WaveBlock_tag);
1045 xmlFile.WriteAttr(Start_attr, bb.start.as_long_long());
1046
1047 bb.sb->SaveXML(xmlFile);
1048
1049 xmlFile.EndTag(WaveBlock_tag);
1050 }
1051
1052 xmlFile.EndTag(Sequence_tag);
1053}
XO("Cut/Copy/Paste")
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:79
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:36
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:102
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:279
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101

References sampleCount::as_long_long(), BasicUI::MessageBoxOptions::Caption(), EffectiveSampleFormat_attr, MaxSamples_attr, NumSamples_attr, SampleFormat_attr, SeqBlock::sb, BasicUI::ShowMessageBox(), SeqBlock::start, Start_attr, Internat::ToString(), and XO().

Here is the call graph for this function:

Member Data Documentation

◆ mAppendBuffer

SampleBuffer Sequence::mAppendBuffer {}
private

Definition at line 261 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendBufferLen

size_t Sequence::mAppendBufferLen { 0 }
private

Definition at line 262 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendEffectiveFormat

sampleFormat Sequence::mAppendEffectiveFormat { narrowestSampleFormat }
private

Definition at line 263 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mBlock

BlockArray Sequence::mBlock
private

◆ mErrorOpening

bool Sequence::mErrorOpening { false }
private

Definition at line 265 of file Sequence.h.

Referenced by HandleXMLEndTag(), and HandleXMLTag().

◆ mMaxSamples

size_t Sequence::mMaxSamples
private

◆ mMinSamples

size_t Sequence::mMinSamples
private

Definition at line 258 of file Sequence.h.

Referenced by ConvertToSampleFormat(), Delete(), DoAppend(), GetBestBlockSize(), and Paste().

◆ mNumSamples

sampleCount Sequence::mNumSamples { 0 }
private

◆ mpFactory

SampleBlockFactoryPtr Sequence::mpFactory
private

◆ mSampleFormats

SampleFormats Sequence::mSampleFormats
private

◆ Sequence_tag

const char * Sequence::Sequence_tag = "sequence"
static

◆ sMaxDiskBlockSize

size_t Sequence::sMaxDiskBlockSize = 1048576
staticprivate

Definition at line 244 of file Sequence.h.

Referenced by ConvertToSampleFormat(), GetMaxDiskBlockSize(), and SetMaxDiskBlockSize().

◆ WaveBlock_tag

const char * Sequence::WaveBlock_tag = "waveblock"
static

The documentation for this class was generated from the following files: