Audacity 3.2.0
Public Member Functions | Static Public Member Functions | 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
 
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 ()
 
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)
 

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

53: mpFactory(pFactory),
54 mSampleFormats{ formats },
57{
58}
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
sampleFormat Stored() const
Definition: SampleFormat.h:91
SampleBlockFactoryPtr mpFactory
Definition: Sequence.h:236
static size_t sMaxDiskBlockSize
Definition: Sequence.h:230
size_t mMinSamples
Definition: Sequence.h:244
size_t mMaxSamples
Definition: Sequence.h:245
SampleFormats mSampleFormats
Definition: Sequence.h:239

◆ Sequence() [2/3]

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

Does not copy un-flushed append buffer data.

Definition at line 63 of file Sequence.cpp.

65: mpFactory(pFactory),
69{
70 Paste(0, &orig);
71}
void Paste(sampleCount s0, const Sequence *src)
Definition: Sequence.cpp:496

References Paste().

Here is the call graph for this function:

◆ Sequence() [3/3]

Sequence::Sequence ( const Sequence )
delete

◆ ~Sequence()

Sequence::~Sequence ( )

Definition at line 73 of file Sequence.cpp.

74{
75}

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

1349{
1350 effectiveFormat = std::min(effectiveFormat, format);
1351 const auto seqFormat = mSampleFormats.Stored();
1352 if (!mAppendBuffer.ptr())
1354
1355 bool result = false;
1356 auto blockSize = GetIdealAppendLen();
1357 for(;;) {
1358 if (mAppendBufferLen >= blockSize) {
1359 // flush some previously appended contents
1360 // use Strong-guarantee
1361 // Already dithered if needed when accumulated into mAppendBuffer
1362 DoAppend(mAppendBuffer.ptr(), seqFormat, blockSize, true);
1363 // Change our effective format now that DoAppend didn't throw
1365 result = true;
1366
1367 // use No-fail-guarantee for rest of this "if"
1368 memmove(mAppendBuffer.ptr(),
1369 mAppendBuffer.ptr() + blockSize * SAMPLE_SIZE(seqFormat),
1370 (mAppendBufferLen - blockSize) * SAMPLE_SIZE(seqFormat));
1371 mAppendBufferLen -= blockSize;
1372 blockSize = GetIdealAppendLen();
1373 }
1374
1375 if (len == 0)
1376 break;
1377
1378 // use No-fail-guarantee for rest of this "for"
1379 wxASSERT(mAppendBufferLen <= mMaxSamples);
1380 auto toCopy = std::min(len, mMaxSamples - mAppendBufferLen);
1381
1382 // If dithering of appended material is done at all, it happens here
1383 CopySamples(buffer, format,
1385 seqFormat,
1386 toCopy,
1387 (seqFormat < effectiveFormat ? gHighQualityDither : DitherType::none),
1388 stride);
1390 std::max(mAppendEffectiveFormat, effectiveFormat);
1391
1392 mAppendBufferLen += toCopy;
1393 buffer += toCopy * SAMPLE_SIZE(format) * stride;
1394 len -= toCopy;
1395 }
1396
1397 return result;
1398}
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:249
size_t mAppendBufferLen
Definition: Sequence.h:248
SeqBlock::SampleBlockPtr DoAppend(constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
Does not do any dithering.
Definition: Sequence.cpp:1401
size_t GetIdealAppendLen() const
Definition: Sequence.cpp:1295
SampleBuffer mAppendBuffer
Definition: Sequence.h:247

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

761{
762 // Quick check to make sure that it doesn't overflow
763 if (Overflows((mNumSamples.as_double()) + ((double)b.sb->GetSampleCount())))
765
766 auto sb = ShareOrCopySampleBlock( pFactory, format, b.sb );
767 SeqBlock newBlock(sb, mNumSamples);
768
769 // We can assume newBlock.sb is not null
770
771 mBlock.push_back(newBlock);
772 mNumSamples += newBlock.sb->GetSampleCount();
773
774 // Don't do a consistency check here because this
775 // function gets called in an inner loop.
776}
#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:238
sampleCount mNumSamples
Definition: Sequence.h:242
double as_double() const
Definition: SampleCount.h:46
bool Overflows(double numSamples)
Definition: Sequence.cpp:473
SampleBlockPtr ShareOrCopySampleBlock(SampleBlockFactory *pFactory, sampleFormat format, SampleBlockPtr sb)
Definition: Sequence.cpp:478

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

1795{
1796 // Any additional blocks are meant to be appended,
1797 // replacing the final block if there was one.
1798
1799 if (additionalBlocks.empty())
1800 return;
1801
1802 bool tmpValid = false;
1803 SeqBlock tmp;
1804
1805 if ( replaceLast && ! mBlock.empty() ) {
1806 tmp = mBlock.back(), tmpValid = true;
1807 mBlock.pop_back();
1808 }
1809
1810 auto prevSize = mBlock.size();
1811
1812 bool consistent = false;
1813 auto cleanup = finally( [&] {
1814 if ( !consistent ) {
1815 mBlock.resize( prevSize );
1816 if ( tmpValid )
1817 mBlock.push_back( tmp );
1818 }
1819 } );
1820
1821 std::copy( additionalBlocks.begin(), additionalBlocks.end(),
1822 std::back_inserter( mBlock ) );
1823
1824 // Check consistency only of the blocks that were added,
1825 // avoiding quadratic time for repeated checking of repeating appends
1826 ConsistencyCheck( mBlock, mMaxSamples, prevSize, numSamples, whereStr ); // may throw
1827
1828 // now commit
1829 // use No-fail-guarantee
1830
1831 mNumSamples = numSamples;
1832 consistent = true;
1833}
void ConsistencyCheck(const wxChar *whereStr, bool mayThrow=true) const
Definition: Sequence.cpp:1724
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 1311 of file Sequence.cpp.

1313{
1314 // Come here only when importing old .aup projects
1315 auto result = DoAppend( buffer, format, len, false );
1316 // Change our effective format now that DoAppend didn't throw
1318 return result;
1319}

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

1323{
1324 auto len = pBlock->GetSampleCount();
1325
1326 // Quick check to make sure that it doesn't overflow
1327 if (Overflows(mNumSamples.as_double() + ((double)len)))
1329
1330 BlockArray newBlock;
1331 newBlock.emplace_back( pBlock, mNumSamples );
1332 auto newNumSamples = mNumSamples + len;
1333
1334 AppendBlocksIfConsistent(newBlock, false,
1335 newNumSamples, wxT("Append"));
1336
1337// JKC: During generate we use Append again and again.
1338// If generating a long sequence this test would give O(n^2)
1339// performance - not good!
1340#ifdef VERY_SLOW_CHECKING
1341 ConsistencyCheck(wxT("Append"));
1342#endif
1343}
wxT("CloseDown"))
void AppendBlocksIfConsistent(BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1793

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

1527{
1528 if (len <= 0)
1529 return;
1530
1531 auto num = (len + (mMaxSamples - 1)) / mMaxSamples;
1532 list.reserve(list.size() + num);
1533
1534 for (decltype(num) i = 0; i < num; i++) {
1535 SeqBlock b;
1536
1537 const auto offset = i * len / num;
1538 b.start = start + offset;
1539 int newLen = ((i + 1) * len / num) - offset;
1540 auto bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat));
1541
1542 b.sb = factory.Create(bufStart, newLen, mSampleFormat);
1543
1544 list.push_back(b);
1545 }
1546}
sampleCount start
the sample in the global wavetrack that this block starts at.
Definition: Sequence.h:34
static RegisteredToolbarFactory factory

References cloud::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 87 of file Sequence.cpp.

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

References mBlock.

◆ CommitChangesIfConsistent()

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

Definition at line 1780 of file Sequence.cpp.

1782{
1783 ConsistencyCheck( newBlock, mMaxSamples, 0, numSamples, whereStr ); // may throw
1784
1785 // now commit
1786 // use No-fail-guarantee
1787
1788 mBlock.swap(newBlock);
1789 mNumSamples = numSamples;
1790}

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

1725{
1726 ConsistencyCheck(mBlock, mMaxSamples, 0, mNumSamples, whereStr, mayThrow);
1727}

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

139{
141 // no change
142 return false;
143
144 if (mBlock.size() == 0)
145 {
146 // Effective format can be made narrowest when there is no content
148 return true;
149 }
150
151 // Decide the new pair of formats. If becoming narrower than the effective,
152 // this will change the effective.
154
155 const auto oldFormats = mSampleFormats;
156 mSampleFormats = newFormats;
157
158 const auto oldMinSamples = mMinSamples, oldMaxSamples = mMaxSamples;
159 // These are the same calculations as in the constructor.
162
163 bool bSuccess = false;
164 auto cleanup = finally( [&] {
165 if (!bSuccess) {
166 // Conversion failed. Revert these member vars.
167 mSampleFormats = oldFormats;
168 mMaxSamples = oldMaxSamples;
169 mMinSamples = oldMinSamples;
170 }
171 } );
172
173 BlockArray newBlockArray;
174 // Use the ratio of old to NEW mMaxSamples to make a reasonable guess
175 // at allocation.
176 newBlockArray.reserve
177 (1 + mBlock.size() * ((float)oldMaxSamples / (float)mMaxSamples));
178
179 {
180 size_t oldSize = oldMaxSamples;
181 SampleBuffer bufferOld(oldSize, oldFormats.Stored());
182 size_t newSize = oldMaxSamples;
183 SampleBuffer bufferNew(newSize, format);
184
185 for (size_t i = 0, nn = mBlock.size(); i < nn; i++)
186 {
187 SeqBlock &oldSeqBlock = mBlock[i];
188 const auto &oldBlockFile = oldSeqBlock.sb;
189 const auto len = oldBlockFile->GetSampleCount();
190 ensureSampleBufferSize(bufferOld, oldFormats.Stored(), oldSize, len);
191
192 // Dither won't happen here, reading back the same as-saved format
193 Read(bufferOld.ptr(), oldFormats.Stored(), oldSeqBlock, 0, len, true);
194
195 ensureSampleBufferSize(bufferNew, format, newSize, len);
196
198 bufferOld.ptr(), oldFormats.Stored(), bufferNew.ptr(), format, len,
199 // Do not dither to reformat samples if format is at least as wide
200 // as the old effective (though format might be narrower than the
201 // old stored).
202 format < oldFormats.Effective()
205
206 // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451,
207 // using Blockify, allows (len < mMinSamples).
208 // This will happen consistently when going from more bytes per sample to fewer...
209 // This will create a block that's smaller than mMinSamples, which
210 // shouldn't be allowed, but we agreed it's okay for now.
211 //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere?
212 // If so, need to special-case (len < mMinSamples) and start combining data
213 // from the old blocks... Oh no!
214
215 // Using Blockify will handle the cases where len > the NEW mMaxSamples. Previous code did not.
216 const auto blockstart = oldSeqBlock.start;
218 newBlockArray, blockstart, bufferNew.ptr(), len);
219
220 if (progressReport)
221 progressReport(len);
222 }
223 }
224
225 // Invalidate all the old, non-aliased block files.
226 // Aliased files will be converted at save, per comment above.
227
228 // Commit the changes to block file array
230 (newBlockArray, mNumSamples, wxT("Sequence::ConvertToSampleFormat()"));
231
232 // Commit the other changes
233 bSuccess = true;
234
235 return true;
236}
@ 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:1781
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1096
static void Blockify(SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
Definition: Sequence.cpp:1523
void ensureSampleBufferSize(SampleBuffer &buffer, sampleFormat format, size_t &size, size_t required, SampleBuffer *pSecondBuffer=nullptr)
Definition: Sequence.cpp:112

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

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

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

1837{
1838 unsigned int i;
1839 decltype(mNumSamples) pos = 0;
1840
1841 for (i = 0; i < mBlock.size(); i++) {
1842 const SeqBlock &seqBlock = mBlock[i];
1843 *dest += wxString::Format
1844 (wxT(" Block %3u: start %8lld, len %8lld, refs %ld, id %lld"),
1845 i,
1846 seqBlock.start.as_long_long(),
1847 seqBlock.sb ? (long long) seqBlock.sb->GetSampleCount() : 0,
1848 seqBlock.sb ? seqBlock.sb.use_count() : 0,
1849 seqBlock.sb ? (long long) seqBlock.sb->GetBlockID() : 0);
1850
1851 if ((pos != seqBlock.start) || !seqBlock.sb)
1852 *dest += wxT(" ERROR\n");
1853 else
1854 *dest += wxT("\n");
1855
1856 if (seqBlock.sb)
1857 pos += seqBlock.sb->GetSampleCount();
1858 }
1859 if (pos != mNumSamples)
1860 *dest += wxString::Format
1861 (wxT("ERROR mNumSamples = %lld\n"), mNumSamples.as_long_long());
1862}
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 1549 of file Sequence.cpp.

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

References SampleBuffer::Allocate(), Blockify(), CommitChangesIfConsistent(), ConsistencyCheck(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), cloud::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 1401 of file Sequence.cpp.

1403{
1405
1406 if (len == 0)
1407 return result;
1408
1409 auto &factory = *mpFactory;
1410
1411 // Quick check to make sure that it doesn't overflow
1412 if (Overflows(mNumSamples.as_double() + ((double)len)))
1414
1415 BlockArray newBlock;
1416 sampleCount newNumSamples = mNumSamples;
1417
1418 // If the last block is not full, we need to add samples to it
1419 int numBlocks = mBlock.size();
1420 SeqBlock *pLastBlock;
1421 decltype(pLastBlock->sb->GetSampleCount()) length;
1422 size_t bufferSize = mMaxSamples;
1423 const auto dstFormat = mSampleFormats.Stored();
1424 SampleBuffer buffer2(bufferSize, dstFormat);
1425 bool replaceLast = false;
1426 if (coalesce &&
1427 numBlocks > 0 &&
1428 (length =
1429 (pLastBlock = &mBlock.back())->sb->GetSampleCount()) < mMinSamples) {
1430 // Enlarge a sub-minimum block at the end
1431 const SeqBlock &lastBlock = *pLastBlock;
1432 const auto addLen = std::min(mMaxSamples - length, len);
1433
1434 // Reading same format as was saved before causes no dithering
1435 Read(buffer2.ptr(), dstFormat, lastBlock, 0, length, true);
1436
1437 CopySamples(buffer,
1438 format,
1439 buffer2.ptr() + length * SAMPLE_SIZE(dstFormat),
1440 dstFormat,
1441 addLen, DitherType::none);
1442
1443 const auto newLastBlockLen = length + addLen;
1444 SampleBlockPtr pBlock = factory.Create(
1445 buffer2.ptr(),
1446 newLastBlockLen,
1447 dstFormat);
1448 SeqBlock newLastBlock(pBlock, lastBlock.start);
1449
1450 newBlock.push_back( newLastBlock );
1451
1452 len -= addLen;
1453 newNumSamples += addLen;
1454 buffer += addLen * SAMPLE_SIZE(format);
1455
1456 replaceLast = true;
1457 }
1458 // Append the rest as NEW blocks
1459 while (len) {
1460 const auto idealSamples = GetIdealBlockSize();
1461 const auto addedLen = std::min(idealSamples, len);
1462 SampleBlockPtr pBlock;
1463 if (format == dstFormat) {
1464 pBlock = factory.Create(buffer, addedLen, dstFormat);
1465 // It's expected that when not requesting coalescence, the
1466 // data should fit in one block
1467 wxASSERT( coalesce || !result );
1468 result = pBlock;
1469 }
1470 else {
1471 CopySamples(buffer, format, buffer2.ptr(), dstFormat,
1472 addedLen, DitherType::none);
1473 pBlock = factory.Create(buffer2.ptr(), addedLen, dstFormat);
1474 }
1475
1476 newBlock.push_back(SeqBlock(pBlock, newNumSamples));
1477
1478 buffer += addedLen * SAMPLE_SIZE(format);
1479 newNumSamples += addedLen;
1480 len -= addedLen;
1481 }
1482
1483 AppendBlocksIfConsistent(newBlock, replaceLast,
1484 newNumSamples, wxT("Append"));
1485
1486// JKC: During generate we use Append again and again.
1487// If generating a long sequence this test would give O(n^2)
1488// performance - not good!
1489#ifdef VERY_SLOW_CHECKING
1490 ConsistencyCheck(wxT("Append"));
1491#endif
1492
1493 return result;
1494}
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: SampleBlock.h:28
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: Sequence.h:31
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:82
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19

References AppendBlocksIfConsistent(), sampleCount::as_double(), ConsistencyCheck(), CopySamples(), cloud::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 1046 of file Sequence.cpp.

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

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

1502{
1503 if (mAppendBufferLen > 0) {
1504
1505 auto cleanup = finally( [&] {
1506 // Blow away the append buffer even in case of failure. May lose some
1507 // data but don't leave the sequence in an un-flushed state.
1508
1509 // Use No-fail-guarantee of these steps.
1510 mAppendBufferLen = 0;
1512 mAppendEffectiveFormat = narrowestSampleFormat; // defaulted again
1513 } );
1514
1515 // Already dithered if needed when accumulated into mAppendBuffer:
1517 mAppendBufferLen, true);
1518 // Change our effective format now that DoAppend didn't throw
1520 }
1521}

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

1157{
1158 bool result = true;
1159 while (len) {
1160 const SeqBlock &block = mBlock[b];
1161 // start is in block
1162 const auto bstart = (start - block.start).as_size_t();
1163 // bstart is not more than block length
1164 const auto blen = std::min(len, block.sb->GetSampleCount() - bstart);
1165
1166 if (! Read(buffer, format, block, bstart, blen, mayThrow) )
1167 result = false;
1168
1169 len -= blen;
1170 buffer += (blen * SAMPLE_SIZE(format));
1171 b++;
1172 start += blen;
1173 }
1174 return result;
1175}

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

Definition at line 1137 of file Sequence.cpp.

1139{
1140 if (start == mNumSamples) {
1141 return len == 0;
1142 }
1143
1144 if (start < 0 || start + len > mNumSamples) {
1145 if (mayThrow)
1147 ClearSamples( buffer, floatSample, 0, len );
1148 return false;
1149 }
1150 int b = FindBlock(start);
1151
1152 return Get(b, buffer, format, start, len, mayThrow);
1153}
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)

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

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

222{ return mAppendBuffer.ptr(); }

◆ GetAppendBufferLen()

size_t Sequence::GetAppendBufferLen ( ) const
inline

Definition at line 221 of file Sequence.h.

221{ return mAppendBufferLen; }

◆ GetBestBlockSize()

size_t Sequence::GetBestBlockSize ( sampleCount  start) const

Definition at line 784 of file Sequence.cpp.

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

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

218{ 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 219 of file Sequence.h.

219{ return mBlock; }

◆ GetBlockStart()

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

Definition at line 778 of file Sequence.cpp.

779{
780 int b = FindBlock(position);
781 return mBlock[b].start;
782}

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

Definition at line 175 of file Sequence.h.

175{ return mErrorOpening; }
bool mErrorOpening
Definition: Sequence.h:251

◆ GetFactory()

const SampleBlockFactoryPtr & Sequence::GetFactory ( ) const
inline

Definition at line 161 of file Sequence.h.

162 {
163 return mpFactory;
164 }

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

1119{
1120 assert(start < mNumSamples);
1121 length = limitSampleBufferSize(length, mNumSamples - start);
1122 std::vector<BlockSampleView> blockViews;
1123 // `sequenceOffset` cannot be larger than `GetMaxBlockSize()`, a `size_t` =>
1124 // no narrowing possible.
1125 const auto sequenceOffset = (start - GetBlockStart(start)).as_size_t();
1126 auto cursor = start;
1127 while (cursor < start + length)
1128 {
1129 const auto b = FindBlock(cursor);
1130 const SeqBlock& block = mBlock[b];
1131 blockViews.push_back(block.sb->GetFloatSampleView(mayThrow));
1132 cursor = block.start + block.sb->GetSampleCount();
1133 }
1134 return { std::move(blockViews), sequenceOffset, length };
1135}
sampleCount GetBlockStart(sampleCount position) const
Definition: Sequence.cpp:778

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

1296{
1297 int numBlocks = mBlock.size();
1298 const auto max = GetMaxBlockSize();
1299
1300 if (numBlocks == 0)
1301 return max;
1302
1303 const auto lastBlockLen = mBlock.back().sb->GetSampleCount();
1304 if (lastBlockLen >= max)
1305 return max;
1306 else
1307 return max - lastBlockLen;
1308}
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:77

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

83{
84 return mMaxSamples;
85}

References mMaxSamples.

Referenced by DoAppend(), WaveTrack::GetIdealBlockSize(), and InsertSilence().

Here is the caller graph for this function:

◆ GetMaxBlockSize()

size_t Sequence::GetMaxBlockSize ( ) const

Definition at line 77 of file Sequence.cpp.

78{
79 return mMaxSamples;
80}

References mMaxSamples.

Referenced by GetIdealAppendLen(), and GetWaveDisplay().

Here is the caller graph for this function:

◆ GetMaxDiskBlockSize()

size_t Sequence::GetMaxDiskBlockSize ( )
static

Definition at line 1870 of file Sequence.cpp.

1871{
1872 return sMaxDiskBlockSize;
1873}

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

240{
241 if (len == 0 || mBlock.size() == 0) {
242 return {
243 0.f,
244 // FLT_MAX? So it doesn't look like a spurious '0' to a caller?
245
246 0.f
247 // -FLT_MAX? So it doesn't look like a spurious '0' to a caller?
248 };
249 }
250
251 float min = FLT_MAX;
252 float max = -FLT_MAX;
253
254 unsigned int block0 = FindBlock(start);
255 unsigned int block1 = FindBlock(start + len - 1);
256
257 // First calculate the min/max of the blocks in the middle of this region;
258 // this is very fast because we have the min/max of every entire block
259 // already in memory.
260
261 for (unsigned b = block0 + 1; b < block1; ++b) {
262 auto results = mBlock[b].sb->GetMinMaxRMS(mayThrow);
263
264 if (results.min < min)
265 min = results.min;
266 if (results.max > max)
267 max = results.max;
268 }
269
270 // Now we take the first and last blocks into account, noting that the
271 // selection may only partly overlap these blocks. If the overall min/max
272 // of either of these blocks is within min...max, then we can ignore them.
273 // If not, we need read some samples and summaries from disk.
274 {
275 const SeqBlock &theBlock = mBlock[block0];
276 const auto &theFile = theBlock.sb;
277 auto results = theFile->GetMinMaxRMS(mayThrow);
278
279 if (results.min < min || results.max > max) {
280 // start lies within theBlock:
281 auto s0 = ( start - theBlock.start ).as_size_t();
282 const auto maxl0 = (
283 // start lies within theBlock:
284 theBlock.start + theFile->GetSampleCount() - start
285 ).as_size_t();
286 wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19
287 const auto l0 = limitSampleBufferSize ( maxl0, len );
288
289 results = theFile->GetMinMaxRMS(s0, l0, mayThrow);
290 if (results.min < min)
291 min = results.min;
292 if (results.max > max)
293 max = results.max;
294 }
295 }
296
297 if (block1 > block0)
298 {
299 const SeqBlock &theBlock = mBlock[block1];
300 const auto &theFile = theBlock.sb;
301 auto results = theFile->GetMinMaxRMS(mayThrow);
302
303 if (results.min < min || results.max > max) {
304
305 // start + len - 1 lies in theBlock:
306 const auto l0 = ( start + len - theBlock.start ).as_size_t();
307 wxASSERT(l0 <= mMaxSamples); // Vaughan, 2011-10-19
308
309 results = theFile->GetMinMaxRMS(0, l0, mayThrow);
310 if (results.min < min)
311 min = results.min;
312 if (results.max > max)
313 max = results.max;
314 }
315 }
316
317 return { min, max };
318}

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

84{ 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 320 of file Sequence.cpp.

321{
322 // len is the number of samples that we want the rms of.
323 // it may be longer than a block, and the code is carefully set up to handle that.
324 if (len == 0 || mBlock.size() == 0)
325 return 0.f;
326
327 double sumsq = 0.0;
328 sampleCount length = 0; // this is the cumulative length of the bits we have the ms of so far, and should end up == len
329
330 unsigned int block0 = FindBlock(start);
331 unsigned int block1 = FindBlock(start + len - 1);
332
333 // First calculate the rms of the blocks in the middle of this region;
334 // this is very fast because we have the rms of every entire block
335 // already in memory.
336 for (unsigned b = block0 + 1; b < block1; b++) {
337 const SeqBlock &theBlock = mBlock[b];
338 const auto &sb = theBlock.sb;
339 auto results = sb->GetMinMaxRMS(mayThrow);
340
341 const auto fileLen = sb->GetSampleCount();
342 const auto blockRMS = results.RMS;
343 sumsq += blockRMS * blockRMS * fileLen;
344 length += fileLen;
345 }
346
347 // Now we take the first and last blocks into account, noting that the
348 // selection may only partly overlap these blocks.
349 // If not, we need read some samples and summaries from disk.
350 {
351 const SeqBlock &theBlock = mBlock[block0];
352 const auto &sb = theBlock.sb;
353 // start lies within theBlock
354 auto s0 = ( start - theBlock.start ).as_size_t();
355 // start lies within theBlock
356 const auto maxl0 =
357 (theBlock.start + sb->GetSampleCount() - start).as_size_t();
358 wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19
359 const auto l0 = limitSampleBufferSize( maxl0, len );
360
361 auto results = sb->GetMinMaxRMS(s0, l0, mayThrow);
362 const auto partialRMS = results.RMS;
363 sumsq += partialRMS * partialRMS * l0;
364 length += l0;
365 }
366
367 if (block1 > block0) {
368 const SeqBlock &theBlock = mBlock[block1];
369 const auto &sb = theBlock.sb;
370
371 // start + len - 1 lies within theBlock
372 const auto l0 = ( start + len - theBlock.start ).as_size_t();
373 wxASSERT(l0 <= mMaxSamples); // PRL: I think Vaughan missed this
374
375 auto results = sb->GetMinMaxRMS(0, l0, mayThrow);
376 const auto partialRMS = results.RMS;
377 sumsq += partialRMS * partialRMS * l0;
378 length += l0;
379 }
380
381 // PRL: catch bugs like 1320:
382 wxASSERT(length == len);
383
384 return sqrt(sumsq / length.as_double() );
385}
__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 95 of file Sequence.cpp.

96{
97 return mSampleFormats;
98}

References mSampleFormats.

◆ HandleXMLChild()

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

Implements XMLTagHandler.

Definition at line 984 of file Sequence.cpp.

985{
986 if (tag == "waveblock")
987 {
988 return this;
989 }
990
991 return nullptr;
992}

◆ HandleXMLEndTag()

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

Definition at line 942 of file Sequence.cpp.

943{
944 if (tag != "sequence" != 0)
945 {
946 return;
947 }
948
949 // Make sure that the sequence is valid.
950
951 // Make sure that start times and lengths are consistent
952 sampleCount numSamples = 0;
953 for (unsigned b = 0, nn = mBlock.size(); b < nn; 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 if (mNumSamples != numSamples)
973 {
974 wxLogWarning(
975 wxT("Gap detected in project file. Correcting sequence sample count from %s to %s."),
976 // PRL: Why bother with Internat when the above is just wxT?
978 Internat::ToString(numSamples.as_double(), 0));
979 mNumSamples = numSamples;
980 mErrorOpening = true;
981 }
982}
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

References sampleCount::as_double(), mBlock, mErrorOpening, mNumSamples, SeqBlock::sb, 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 813 of file Sequence.cpp.

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

References SampleFormats::Effective(), cloud::factory, floatSample, IsValidSampleFormat(), mBlock, mErrorOpening, mMaxSamples, mNumSamples, mpFactory, mSampleFormats, SeqBlock::sb, SeqBlock::start, and SampleFormats::Stored().

Here is the call graph for this function:

◆ InsertSilence()

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

Definition at line 709 of file Sequence.cpp.

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

1876{
1877 auto nValue = static_cast<sampleFormat>(iValue);
1878 return (nValue == int16Sample) || (nValue == int24Sample) || (nValue == floatSample);
1879}

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

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

1099{
1100 const auto &sb = b.sb;
1101
1102 wxASSERT(blockRelativeStart + len <= sb->GetSampleCount());
1103
1104 // Either throws, or of !mayThrow, tells how many were really read
1105 auto result = sb->GetSamples(buffer, format, blockRelativeStart, len, mayThrow);
1106
1107 if (result != len)
1108 {
1109 wxLogWarning(wxT("Expected to read %ld samples, got %ld samples."),
1110 len, result);
1111 return false;
1112 }
1113
1114 return true;
1115}

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

1866{
1867 sMaxDiskBlockSize = bytes;
1868}

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

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

References SampleBuffer::Allocate(), ClearSamples(), CommitChangesIfConsistent(), staffpad::vo::copy(), CopySamples(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), cloud::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 702 of file Sequence.cpp.

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

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

997{
998 unsigned int b;
999
1000 xmlFile.StartTag(wxT("sequence"));
1001
1002 xmlFile.WriteAttr(wxT("maxsamples"), mMaxSamples);
1003 xmlFile.WriteAttr(wxT("sampleformat"),
1004 static_cast<size_t>( mSampleFormats.Stored() ) );
1005 // This attribute was added in 3.0.3:
1006 xmlFile.WriteAttr( wxT("effectivesampleformat"),
1007 static_cast<size_t>( mSampleFormats.Effective() ));
1008 xmlFile.WriteAttr(wxT("numsamples"), mNumSamples.as_long_long() );
1009
1010 for (b = 0; b < mBlock.size(); b++) {
1011 const SeqBlock &bb = mBlock[b];
1012
1013 // See http://bugzilla.audacityteam.org/show_bug.cgi?id=451.
1014 if (bb.sb->GetSampleCount() > mMaxSamples)
1015 {
1016 // PRL: Bill observed this error. Not sure how it was caused.
1017 // I have added code in ConsistencyCheck that should abort the
1018 // editing operation that caused this, not fixing
1019 // the problem but moving the point of detection earlier if we
1020 // find a reproducible case.
1021 using namespace BasicUI;
1022 auto sMsg =
1023 XO("Sequence has block file exceeding maximum %s samples per block.\nTruncating to this maximum length.")
1024 .Format( Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0) );
1026 sMsg,
1028 .Caption(XO("Warning - Truncating Overlong Block File"))
1029 .IconStyle(Icon::Warning)
1030 .ButtonStyle(Button::Ok));
1031 wxLogWarning(sMsg.Translation()); //Debug?
1032// bb.sb->SetLength(mMaxSamples);
1033 }
1034
1035 xmlFile.StartTag(wxT("waveblock"));
1036 xmlFile.WriteAttr(wxT("start"), bb.start.as_long_long() );
1037
1038 bb.sb->SaveXML(xmlFile);
1039
1040 xmlFile.EndTag(wxT("waveblock"));
1041 }
1042
1043 xmlFile.EndTag(wxT("sequence"));
1044}
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:277
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101

References sampleCount::as_long_long(), BasicUI::MessageBoxOptions::Caption(), SeqBlock::sb, BasicUI::ShowMessageBox(), SeqBlock::start, Internat::ToString(), wxT(), and XO().

Here is the call graph for this function:

Member Data Documentation

◆ mAppendBuffer

SampleBuffer Sequence::mAppendBuffer {}
private

Definition at line 247 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendBufferLen

size_t Sequence::mAppendBufferLen { 0 }
private

Definition at line 248 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mAppendEffectiveFormat

sampleFormat Sequence::mAppendEffectiveFormat { narrowestSampleFormat }
private

Definition at line 249 of file Sequence.h.

Referenced by Append(), and Flush().

◆ mBlock

BlockArray Sequence::mBlock
private

◆ mErrorOpening

bool Sequence::mErrorOpening { false }
private

Definition at line 251 of file Sequence.h.

Referenced by HandleXMLEndTag(), and HandleXMLTag().

◆ mMaxSamples

size_t Sequence::mMaxSamples
private

◆ mMinSamples

size_t Sequence::mMinSamples
private

Definition at line 244 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

◆ sMaxDiskBlockSize

size_t Sequence::sMaxDiskBlockSize = 1048576
staticprivate

Definition at line 230 of file Sequence.h.

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


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