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
 
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
 
std::atomic< size_t > mBlockCount { 0 }
 
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:248
static size_t sMaxDiskBlockSize
Definition: Sequence.h:242
size_t mMinSamples
Definition: Sequence.h:256
size_t mMaxSamples
Definition: Sequence.h:257
SampleFormats mSampleFormats
Definition: Sequence.h:251

◆ 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:490

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 1372 of file Sequence.cpp.

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}
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:261
size_t mAppendBufferLen
Definition: Sequence.h:260
SeqBlock::SampleBlockPtr DoAppend(constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
Does not do any dithering.
Definition: Sequence.cpp:1427
size_t GetIdealAppendLen() const
Definition: Sequence.cpp:1320
SampleBuffer mAppendBuffer
Definition: Sequence.h:259

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 752 of file Sequence.cpp.

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}
#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:250
sampleCount mNumSamples
Definition: Sequence.h:254
double as_double() const
Definition: SampleCount.h:46
bool Overflows(double numSamples)
Definition: Sequence.cpp:467
SampleBlockPtr ShareOrCopySampleBlock(SampleBlockFactory *pFactory, sampleFormat format, SampleBlockPtr sb)
Definition: Sequence.cpp:472

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 1817 of file Sequence.cpp.

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}
void ConsistencyCheck(const wxChar *whereStr, bool mayThrow=true) const
Definition: Sequence.cpp:1748
std::atomic< size_t > mBlockCount
Definition: Sequence.h:249
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40

References ConsistencyCheck(), staffpad::vo::copy(), mBlock, mBlockCount, 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 1337 of file Sequence.cpp.

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}

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 1348 of file Sequence.cpp.

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}
wxT("CloseDown"))
void AppendBlocksIfConsistent(BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1818

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 1549 of file Sequence.cpp.

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}
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 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}

References mBlock, and mBlockCount.

◆ CommitChangesIfConsistent()

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

Definition at line 1804 of file Sequence.cpp.

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}

References ConsistencyCheck(), mBlock, mBlockCount, 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 1748 of file Sequence.cpp.

1749{
1750 ConsistencyCheck(mBlock, mMaxSamples, 0, mNumSamples, whereStr, mayThrow);
1751}

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 131 of file Sequence.cpp.

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}
@ 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:1805
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1100
static void Blockify(SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
Definition: Sequence.cpp:1549
void ensureSampleBufferSize(SampleBuffer &buffer, sampleFormat format, size_t &size, size_t required, SampleBuffer *pSecondBuffer=nullptr)
Definition: Sequence.cpp:106

References Blockify(), CommitChangesIfConsistent(), CopySamples(), SampleFormats::Effective(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), anonymous_namespace{ExportPCM.cpp}::format, gHighQualityDither, mBlock, mBlockCount, 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 385 of file Sequence.cpp.

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}
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
static void AppendBlock(SampleBlockFactory *pFactory, sampleFormat format, BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b)
Definition: Sequence.cpp:752
int FindBlock(sampleCount pos) const
Definition: Sequence.cpp:1050

References AppendBlock(), SampleFormats::Effective(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), FindBlock(), anonymous_namespace{ExportPCM.cpp}::format, Get(), mBlock, mBlockCount, 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 1862 of file Sequence.cpp.

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}
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 1574 of file Sequence.cpp.

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}
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 1427 of file Sequence.cpp.

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}
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 1050 of file Sequence.cpp.

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}

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

Referenced by Copy(), Delete(), Get(), GetBestBlockSize(), GetBlockStart(), GetFloatSampleView(), GetMinMax(), GetRMS(), 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 1527 of file Sequence.cpp.

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}

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 1180 of file Sequence.cpp.

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}

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 1141 of file Sequence.cpp.

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}
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 234 of file Sequence.h.

234{ return mAppendBuffer.ptr(); }

◆ GetAppendBufferLen()

size_t Sequence::GetAppendBufferLen ( ) const
inline

Definition at line 233 of file Sequence.h.

233{ return mAppendBufferLen; }

◆ GetBestBlockSize()

size_t Sequence::GetBestBlockSize ( sampleCount  start) const

Definition at line 777 of file Sequence.cpp.

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}

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

Here is the call graph for this function:

◆ GetBlockArray()

const BlockArray & Sequence::GetBlockArray ( ) const
inline

Definition at line 231 of file Sequence.h.

231{ return mBlock; }

Referenced by GetAllSeqBlocks().

Here is the caller graph for this function:

◆ GetBlockStart()

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

Definition at line 771 of file Sequence.cpp.

772{
773 int b = FindBlock(position);
774 return mBlock[b].start;
775}

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:263

◆ 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 1121 of file Sequence.cpp.

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}
sampleCount GetBlockStart(sampleCount position) const
Definition: Sequence.cpp:771

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 1320 of file Sequence.cpp.

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}
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:80

References GetMaxBlockSize(), mBlock, and mBlockCount.

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().

Here is the caller graph for this function:

◆ GetMaxDiskBlockSize()

size_t Sequence::GetMaxDiskBlockSize ( )
static

Definition at line 1897 of file Sequence.cpp.

1898{
1899 return sMaxDiskBlockSize;
1900}

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 230 of file Sequence.cpp.

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}

References FindBlock(), limitSampleBufferSize(), mBlock, mBlockCount, 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().

Here is the caller graph for this function:

◆ GetRMS()

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

Definition at line 314 of file Sequence.cpp.

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}
__finl float_x4 __vecc sqrt(const float_x4 &a)

References sampleCount::as_double(), FindBlock(), limitSampleBufferSize(), mBlock, mBlockCount, 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 100 of file Sequence.cpp.

101{
102 return mSampleFormats;
103}

References mSampleFormats.

◆ HandleXMLChild()

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

Implements XMLTagHandler.

Definition at line 986 of file Sequence.cpp.

987{
988 if (tag == WaveBlock_tag)
989 {
990 return this;
991 }
992
993 return nullptr;
994}
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 940 of file Sequence.cpp.

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}
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, mBlockCount, 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 812 of file Sequence.cpp.

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}
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: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
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
Definition: Sequence.cpp:1902
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 703 of file Sequence.cpp.

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}
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 1902 of file Sequence.cpp.

1903{
1904 auto nValue = static_cast<sampleFormat>(iValue);
1905 return (nValue == int16Sample) || (nValue == int24Sample) || (nValue == floatSample);
1906}

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 490 of file Sequence.cpp.

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}
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, mBlockCount, 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 1100 of file Sequence.cpp.

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}

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

Referenced by ConvertToSampleFormat(), Delete(), DoAppend(), Get(), 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 1892 of file Sequence.cpp.

1893{
1894 sMaxDiskBlockSize = bytes;
1895}

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 1204 of file Sequence.cpp.

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}

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 696 of file Sequence.cpp.

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

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 997 of file Sequence.cpp.

999{
1000 unsigned int b;
1001
1002 xmlFile.StartTag(Sequence_tag);
1003
1006 static_cast<size_t>( mSampleFormats.Stored() ) );
1007 // This attribute was added in 3.0.3:
1009 static_cast<size_t>( mSampleFormats.Effective() ));
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}
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:287
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 259 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendBufferLen

size_t Sequence::mAppendBufferLen { 0 }
private

Definition at line 260 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendEffectiveFormat

sampleFormat Sequence::mAppendEffectiveFormat { narrowestSampleFormat }
private

Definition at line 261 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mBlock

BlockArray Sequence::mBlock
private

◆ mBlockCount

std::atomic<size_t> Sequence::mBlockCount { 0 }
private

◆ mErrorOpening

bool Sequence::mErrorOpening { false }
private

Definition at line 263 of file Sequence.h.

Referenced by HandleXMLEndTag(), and HandleXMLTag().

◆ mMaxSamples

size_t Sequence::mMaxSamples
private

◆ mMinSamples

size_t Sequence::mMinSamples
private

Definition at line 256 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 242 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: