Audacity  3.0.3
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, sampleFormat format)
 
 Sequence (const Sequence &orig, const SampleBlockFactoryPtr &pFactory)
 
 Sequence (const Sequence &)=delete
 
Sequenceoperator= (const Sequence &) PROHIBITED
 
 ~Sequence ()
 
sampleCount GetNumSamples () const
 
bool Get (samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow) const
 
void SetSamples (constSamplePtr buffer, sampleFormat format, sampleCount start, sampleCount len)
 
bool GetWaveDisplay (float *min, float *max, float *rms, int *bl, size_t len, const sampleCount *where) const
 
std::unique_ptr< SequenceCopy (const SampleBlockFactoryPtr &pFactory, sampleCount s0, sampleCount s1) const
 
void Paste (sampleCount s0, const Sequence *src)
 
size_t GetIdealAppendLen () const
 
void Append (constSamplePtr buffer, sampleFormat format, size_t len)
 
SeqBlock::SampleBlockPtr AppendNewBlock (constSamplePtr buffer, sampleFormat format, size_t len)
 Append data, not coalescing blocks, returning a pointer to the new block. More...
 
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 ()
 
bool HandleXMLTag (const wxChar *tag, const wxChar **attrs) override
 
void HandleXMLEndTag (const wxChar *tag) override
 
XMLTagHandlerHandleXMLChild (const wxChar *tag) override
 
void WriteXML (XMLWriter &xmlFile) const
 
bool GetErrorOpening ()
 
bool CloseLock ()
 
sampleFormat GetSampleFormat () 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
 
sampleCount GetBlockStart (sampleCount position) const
 
size_t GetBestBlockSize (sampleCount start) const
 
size_t GetMaxBlockSize () const
 
size_t GetIdealBlockSize () const
 
BlockArrayGetBlockArray ()
 
const BlockArrayGetBlockArray () const
 
void ConsistencyCheck (const wxChar *whereStr, bool mayThrow=true) const
 
- Public Member Functions inherited from XMLTagHandler
 XMLTagHandler ()
 
virtual ~XMLTagHandler ()
 
virtual void HandleXMLEndTag (const wxChar *WXUNUSED(tag))
 
virtual void HandleXMLContent (const wxString &WXUNUSED(content))
 
bool ReadXMLTag (const char *tag, const char **attrs)
 
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 void DebugPrintf (const BlockArray &block, sampleCount numSamples, wxString *dest)
 

Private Member Functions

int FindBlock (sampleCount pos) const
 
SeqBlock::SampleBlockPtr DoAppend (constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
 
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 bool Read (samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
 
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
 
sampleFormat mSampleFormat
 
sampleCount mNumSamples { 0 }
 
size_t mMinSamples
 
size_t mMaxSamples
 
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 61 of file Sequence.h.

Constructor & Destructor Documentation

◆ Sequence() [1/3]

Sequence::Sequence ( const SampleBlockFactoryPtr pFactory,
sampleFormat  format 
)

Definition at line 50 of file Sequence.cpp.

52 : mpFactory(pFactory),
56 {
57 }

◆ Sequence() [2/3]

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

Definition at line 62 of file Sequence.cpp.

64 : mpFactory(pFactory),
68 {
69  Paste(0, &orig);
70 }

References Paste().

Here is the call graph for this function:

◆ Sequence() [3/3]

Sequence::Sequence ( const Sequence )
delete

◆ ~Sequence()

Sequence::~Sequence ( )

Definition at line 72 of file Sequence.cpp.

73 {
74 }

Member Function Documentation

◆ Append()

void Sequence::Append ( constSamplePtr  buffer,
sampleFormat  format,
size_t  len 
)
Exception safety guarantee:
Strong

Definition at line 1509 of file Sequence.cpp.

1510 {
1511  DoAppend(buffer, format, len, true);
1512 }

References DoAppend(), and format.

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

733 {
734  // Quick check to make sure that it doesn't overflow
735  if (Overflows((mNumSamples.as_double()) + ((double)b.sb->GetSampleCount())))
737 
738  auto sb = ShareOrCopySampleBlock( pFactory, format, b.sb );
739  SeqBlock newBlock(sb, mNumSamples);
740 
741  // We can assume newBlock.sb is not null
742 
743  mBlock.push_back(newBlock);
744  mNumSamples += newBlock.sb->GetSampleCount();
745 
746  // Don't do a consistency check here because this
747  // function gets called in an inner loop.
748 }

References sampleCount::as_double(), 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 1875 of file Sequence.cpp.

1878 {
1879  // Any additional blocks are meant to be appended,
1880  // replacing the final block if there was one.
1881 
1882  if (additionalBlocks.empty())
1883  return;
1884 
1885  bool tmpValid = false;
1886  SeqBlock tmp;
1887 
1888  if ( replaceLast && ! mBlock.empty() ) {
1889  tmp = mBlock.back(), tmpValid = true;
1890  mBlock.pop_back();
1891  }
1892 
1893  auto prevSize = mBlock.size();
1894 
1895  bool consistent = false;
1896  auto cleanup = finally( [&] {
1897  if ( !consistent ) {
1898  mBlock.resize( prevSize );
1899  if ( tmpValid )
1900  mBlock.push_back( tmp );
1901  }
1902  } );
1903 
1904  std::copy( additionalBlocks.begin(), additionalBlocks.end(),
1905  std::back_inserter( mBlock ) );
1906 
1907  // Check consistency only of the blocks that were added,
1908  // avoiding quadratic time for repeated checking of repeating appends
1909  ConsistencyCheck( mBlock, mMaxSamples, prevSize, numSamples, whereStr ); // may throw
1910 
1911  // now commit
1912  // use No-fail-guarantee
1913 
1914  mNumSamples = numSamples;
1915  consistent = true;
1916 }

References ConsistencyCheck(), 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.

Exception safety guarantee:
Strong

Definition at line 1478 of file Sequence.cpp.

1480 {
1481  return DoAppend( buffer, format, len, false );
1482 }

References DoAppend(), and format.

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

1486 {
1487  auto len = pBlock->GetSampleCount();
1488 
1489  // Quick check to make sure that it doesn't overflow
1490  if (Overflows(mNumSamples.as_double() + ((double)len)))
1492 
1493  BlockArray newBlock;
1494  newBlock.emplace_back( pBlock, mNumSamples );
1495  auto newNumSamples = mNumSamples + len;
1496 
1497  AppendBlocksIfConsistent(newBlock, false,
1498  newNumSamples, wxT("Append"));
1499 
1500 // JKC: During generate we use Append again and again.
1501 // If generating a long sequence this test would give O(n^2)
1502 // performance - not good!
1503 #ifdef VERY_SLOW_CHECKING
1504  ConsistencyCheck(wxT("Append"));
1505 #endif
1506 }

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

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

1611 {
1612  if (len <= 0)
1613  return;
1614 
1615  auto num = (len + (mMaxSamples - 1)) / mMaxSamples;
1616  list.reserve(list.size() + num);
1617 
1618  for (decltype(num) i = 0; i < num; i++) {
1619  SeqBlock b;
1620 
1621  const auto offset = i * len / num;
1622  b.start = start + offset;
1623  int newLen = ((i + 1) * len / num) - offset;
1624  auto bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat));
1625 
1626  b.sb = factory.Create(bufStart, newLen, mSampleFormat);
1627 
1628  list.push_back(b);
1629  }
1630 }

References factory, mMaxSamples, mSampleFormat, 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 ( )

Definition at line 86 of file Sequence.cpp.

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

References mBlock.

Referenced by WaveClip::CloseLock().

Here is the caller graph for this function:

◆ CommitChangesIfConsistent()

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

Definition at line 1863 of file Sequence.cpp.

1865 {
1866  ConsistencyCheck( newBlock, mMaxSamples, 0, numSamples, whereStr ); // may throw
1867 
1868  // now commit
1869  // use No-fail-guarantee
1870 
1871  mBlock.swap(newBlock);
1872  mNumSamples = numSamples;
1873 }

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

1808 {
1809  ConsistencyCheck(mBlock, mMaxSamples, 0, mNumSamples, whereStr, mayThrow);
1810 }

References mBlock, mMaxSamples, and mNumSamples.

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

Here is the caller graph for this function:

◆ ConvertToSampleFormat()

bool Sequence::ConvertToSampleFormat ( sampleFormat  format,
const std::function< void(size_t)> &  progressReport = {} 
)
Exception safety guarantee:
Strong

Definition at line 136 of file Sequence.cpp.

138 {
139  if (format == mSampleFormat)
140  // no change
141  return false;
142 
143  if (mBlock.size() == 0)
144  {
146  return true;
147  }
148 
149  const sampleFormat oldFormat = mSampleFormat;
151 
152  const auto oldMinSamples = mMinSamples, oldMaxSamples = mMaxSamples;
153  // These are the same calculations as in the constructor.
155  mMaxSamples = mMinSamples * 2;
156 
157  bool bSuccess = false;
158  auto cleanup = finally( [&] {
159  if (!bSuccess) {
160  // Conversion failed. Revert these member vars.
161  mSampleFormat = oldFormat;
162  mMaxSamples = oldMaxSamples;
163  mMinSamples = oldMinSamples;
164  }
165  } );
166 
167  BlockArray newBlockArray;
168  // Use the ratio of old to NEW mMaxSamples to make a reasonable guess
169  // at allocation.
170  newBlockArray.reserve
171  (1 + mBlock.size() * ((float)oldMaxSamples / (float)mMaxSamples));
172 
173  {
174  size_t oldSize = oldMaxSamples;
175  SampleBuffer bufferOld(oldSize, oldFormat);
176  size_t newSize = oldMaxSamples;
177  SampleBuffer bufferNew(newSize, format);
178 
179  for (size_t i = 0, nn = mBlock.size(); i < nn; i++)
180  {
181  SeqBlock &oldSeqBlock = mBlock[i];
182  const auto &oldBlockFile = oldSeqBlock.sb;
183  const auto len = oldBlockFile->GetSampleCount();
184  ensureSampleBufferSize(bufferOld, oldFormat, oldSize, len);
185  Read(bufferOld.ptr(), oldFormat, oldSeqBlock, 0, len, true);
186 
187  ensureSampleBufferSize(bufferNew, format, newSize, len);
188  CopySamples(bufferOld.ptr(), oldFormat, bufferNew.ptr(), format, len);
189 
190  // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451,
191  // using Blockify, allows (len < mMinSamples).
192  // This will happen consistently when going from more bytes per sample to fewer...
193  // This will create a block that's smaller than mMinSamples, which
194  // shouldn't be allowed, but we agreed it's okay for now.
195  //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere?
196  // If so, need to special-case (len < mMinSamples) and start combining data
197  // from the old blocks... Oh no!
198 
199  // Using Blockify will handle the cases where len > the NEW mMaxSamples. Previous code did not.
200  const auto blockstart = oldSeqBlock.start;
202  newBlockArray, blockstart, bufferNew.ptr(), len);
203 
204  if (progressReport)
205  progressReport(len);
206  }
207  }
208 
209  // Invalidate all the old, non-aliased block files.
210  // Aliased files will be converted at save, per comment above.
211 
212  // Commit the changes to block file array
214  (newBlockArray, mNumSamples, wxT("Sequence::ConvertToSampleFormat()"));
215 
216  // Commit the other changes
217  bSuccess = true;
218 
219  return true;
220 }

References Blockify(), CommitChangesIfConsistent(), CopySamples(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), format, mBlock, mMaxSamples, mMinSamples, mNumSamples, mpFactory, mSampleFormat, SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, sMaxDiskBlockSize, and SeqBlock::start.

Referenced by AUPImportFileHandle::HandleSequence().

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

◆ Copy()

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

Definition at line 373 of file Sequence.cpp.

375 {
376  // Make a new Sequence object for the specified factory:
377  auto dest = std::make_unique<Sequence>(pFactory, mSampleFormat);
378  if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) {
379  return dest;
380  }
381 
382  // Decide whether to share sample blocks or make new copies, when whole block
383  // contents are used -- must copy if factories are different:
384  auto pUseFactory = (pFactory == mpFactory) ? nullptr : pFactory.get();
385 
386  int numBlocks = mBlock.size();
387 
388  int b0 = FindBlock(s0);
389  const int b1 = FindBlock(s1 - 1);
390  wxASSERT(b0 >= 0);
391  wxASSERT(b0 < numBlocks);
392  wxASSERT(b1 < numBlocks);
393  wxUnusedVar(numBlocks);
394  wxASSERT(b0 <= b1);
395 
396  dest->mBlock.reserve(b1 - b0 + 1);
397 
398  auto bufferSize = mMaxSamples;
399  SampleBuffer buffer(bufferSize, mSampleFormat);
400 
401  int blocklen;
402 
403  // Do any initial partial block
404 
405  const SeqBlock &block0 = mBlock[b0];
406  if (s0 != block0.start) {
407  const auto &sb = block0.sb;
408  // Nonnegative result is length of block0 or less:
409  blocklen =
410  ( std::min(s1, block0.start + sb->GetSampleCount()) - s0 ).as_size_t();
411  wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
412  ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen);
413  Get(b0, buffer.ptr(), mSampleFormat, s0, blocklen, true);
414 
415  dest->Append(buffer.ptr(), mSampleFormat, blocklen);
416  }
417  else
418  --b0;
419 
420  // If there are blocks in the middle, use the blocks whole
421  for (int bb = b0 + 1; bb < b1; ++bb)
422  AppendBlock(pUseFactory, mSampleFormat,
423  dest->mBlock, dest->mNumSamples, mBlock[bb]);
424  // Increase ref count or duplicate file
425 
426  // Do the last block
427  if (b1 > b0) {
428  // Probable case of a partial block
429  const SeqBlock &block = mBlock[b1];
430  const auto &sb = block.sb;
431  // s1 is within block:
432  blocklen = (s1 - block.start).as_size_t();
433  wxASSERT(blocklen <= (int)mMaxSamples); // Vaughan, 2012-02-29
434  if (blocklen < (int)sb->GetSampleCount()) {
435  ensureSampleBufferSize(buffer, mSampleFormat, bufferSize, blocklen);
436  Get(b1, buffer.ptr(), mSampleFormat, block.start, blocklen, true);
437  dest->Append(buffer.ptr(), mSampleFormat, blocklen);
438  }
439  else
440  // Special case of a whole block
441  AppendBlock(pUseFactory, mSampleFormat,
442  dest->mBlock, dest->mNumSamples, block);
443  // Increase ref count or duplicate file
444  }
445 
446  dest->ConsistencyCheck(wxT("Sequence::Copy()"));
447 
448  return dest;
449 }

References AppendBlock(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), FindBlock(), Get(), mBlock, min(), mMaxSamples, mNumSamples, mpFactory, mSampleFormat, SampleBuffer::ptr(), SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ DebugPrintf()

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

Definition at line 1918 of file Sequence.cpp.

1920 {
1921  unsigned int i;
1922  decltype(mNumSamples) pos = 0;
1923 
1924  for (i = 0; i < mBlock.size(); i++) {
1925  const SeqBlock &seqBlock = mBlock[i];
1926  *dest += wxString::Format
1927  (wxT(" Block %3u: start %8lld, len %8lld, refs %ld, id %lld"),
1928  i,
1929  seqBlock.start.as_long_long(),
1930  seqBlock.sb ? (long long) seqBlock.sb->GetSampleCount() : 0,
1931  seqBlock.sb ? seqBlock.sb.use_count() : 0,
1932  seqBlock.sb ? (long long) seqBlock.sb->GetBlockID() : 0);
1933 
1934  if ((pos != seqBlock.start) || !seqBlock.sb)
1935  *dest += wxT(" ERROR\n");
1936  else
1937  *dest += wxT("\n");
1938 
1939  if (seqBlock.sb)
1940  pos += seqBlock.sb->GetSampleCount();
1941  }
1942  if (pos != mNumSamples)
1943  *dest += wxString::Format
1944  (wxT("ERROR mNumSamples = %lld\n"), mNumSamples.as_long_long());
1945 }

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

Here is the call graph for this function:

◆ Delete()

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

Definition at line 1633 of file Sequence.cpp.

1634 {
1635  if (len == 0)
1636  return;
1637 
1638  if (len < 0 || start < 0 || start + len > mNumSamples)
1640 
1641  auto &factory = *mpFactory;
1642 
1643  const unsigned int numBlocks = mBlock.size();
1644 
1645  const unsigned int b0 = FindBlock(start);
1646  unsigned int b1 = FindBlock(start + len - 1);
1647 
1648  auto sampleSize = SAMPLE_SIZE(mSampleFormat);
1649 
1650  SeqBlock *pBlock;
1651  decltype(pBlock->sb->GetSampleCount()) length;
1652 
1653  // One buffer for reuse in various branches here
1654  SampleBuffer scratch;
1655  // The maximum size that should ever be needed
1656  auto scratchSize = mMaxSamples + mMinSamples;
1657 
1658  // Special case: if the samples to DELETE are all within a single
1659  // block and the resulting length is not too small, perform the
1660  // deletion within this block:
1661  if (b0 == b1 &&
1662  (length = (pBlock = &mBlock[b0])->sb->GetSampleCount()) - len >= mMinSamples) {
1663  SeqBlock &b = *pBlock;
1664  // start is within block
1665  auto pos = ( start - b.start ).as_size_t();
1666 
1667  // Guard against failure of this anyway below with limitSampleBufferSize
1668  wxASSERT(len < length);
1669 
1670  // len must be less than length
1671  // because start + len - 1 is also in the block...
1672  auto newLen = ( length - limitSampleBufferSize( length, len ) );
1673 
1674  scratch.Allocate(scratchSize, mSampleFormat);
1675  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize, newLen);
1676 
1677  Read(scratch.ptr(), mSampleFormat, b, 0, pos, true);
1678  Read(scratch.ptr() + (pos * sampleSize), mSampleFormat,
1679  b,
1680  // ... and therefore pos + len
1681  // is not more than the length of the block
1682  ( pos + len ).as_size_t(), newLen - pos, true);
1683 
1684  b.sb = factory.Create(scratch.ptr(), newLen, mSampleFormat);
1685 
1686  // Don't make a duplicate array. We can still give Strong-guarantee
1687  // if we modify only one block in place.
1688 
1689  // use No-fail-guarantee in remaining steps
1690 
1691  for (unsigned int j = b0 + 1; j < numBlocks; j++)
1692  mBlock[j].start -= len;
1693 
1694  mNumSamples -= len;
1695 
1696  // This consistency check won't throw, it asserts.
1697  // Proof that we kept consistency is not hard.
1698  ConsistencyCheck(wxT("Delete - branch one"), false);
1699  return;
1700  }
1701 
1702  // Create a NEW array of blocks
1703  BlockArray newBlock;
1704  newBlock.reserve(numBlocks - (b1 - b0) + 2);
1705 
1706  // Copy the blocks before the deletion point over to
1707  // the NEW array
1708  newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b0);
1709  unsigned int i;
1710 
1711  // First grab the samples in block b0 before the deletion point
1712  // into preBuffer. If this is enough samples for its own block,
1713  // or if this would be the first block in the array, write it out.
1714  // Otherwise combine it with the previous block (splitting them
1715  // 50/50 if necessary).
1716  const SeqBlock &preBlock = mBlock[b0];
1717  // start is within preBlock
1718  auto preBufferLen = ( start - preBlock.start ).as_size_t();
1719  if (preBufferLen) {
1720  if (preBufferLen >= mMinSamples || b0 == 0) {
1721  if (!scratch.ptr())
1722  scratch.Allocate(scratchSize, mSampleFormat);
1723  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize, preBufferLen);
1724  Read(scratch.ptr(), mSampleFormat, preBlock, 0, preBufferLen, true);
1725  auto pFile =
1726  factory.Create(scratch.ptr(), preBufferLen, mSampleFormat);
1727 
1728  newBlock.push_back(SeqBlock(pFile, preBlock.start));
1729  } else {
1730  const SeqBlock &prepreBlock = mBlock[b0 - 1];
1731  const auto prepreLen = prepreBlock.sb->GetSampleCount();
1732  const auto sum = prepreLen + preBufferLen;
1733 
1734  if (!scratch.ptr())
1735  scratch.Allocate(scratchSize, mSampleFormat);
1736  ensureSampleBufferSize(scratch, mSampleFormat, scratchSize,
1737  sum);
1738 
1739  Read(scratch.ptr(), mSampleFormat, prepreBlock, 0, prepreLen, true);
1740  Read(scratch.ptr() + prepreLen*sampleSize, mSampleFormat,
1741  preBlock, 0, preBufferLen, true);
1742 
1743  newBlock.pop_back();
1745  newBlock, prepreBlock.start, scratch.ptr(), sum);
1746  }
1747  }
1748  else {
1749  // The sample where we begin deletion happens to fall
1750  // right on the beginning of a block.
1751  }
1752 
1753  // Now, symmetrically, grab the samples in block b1 after the
1754  // deletion point into postBuffer. If this is enough samples
1755  // for its own block, or if this would be the last block in
1756  // the array, write it out. Otherwise combine it with the
1757  // subsequent block (splitting them 50/50 if necessary).
1758  const SeqBlock &postBlock = mBlock[b1];
1759  // start + len - 1 lies within postBlock
1760  const auto postBufferLen = (
1761  (postBlock.start + postBlock.sb->GetSampleCount()) - (start + len)
1762  ).as_size_t();
1763  if (postBufferLen) {
1764  if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) {
1765  if (!scratch.ptr())
1766  // Last use of scratch, can ask for smaller
1767  scratch.Allocate(postBufferLen, mSampleFormat);
1768  // start + len - 1 lies within postBlock
1769  auto pos = (start + len - postBlock.start).as_size_t();
1770  Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen, true);
1771  auto file =
1772  factory.Create(scratch.ptr(), postBufferLen, mSampleFormat);
1773 
1774  newBlock.push_back(SeqBlock(file, start));
1775  } else {
1776  SeqBlock &postpostBlock = mBlock[b1 + 1];
1777  const auto postpostLen = postpostBlock.sb->GetSampleCount();
1778  const auto sum = postpostLen + postBufferLen;
1779 
1780  if (!scratch.ptr())
1781  // Last use of scratch, can ask for smaller
1782  scratch.Allocate(sum, mSampleFormat);
1783  // start + len - 1 lies within postBlock
1784  auto pos = (start + len - postBlock.start).as_size_t();
1785  Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen, true);
1786  Read(scratch.ptr() + (postBufferLen * sampleSize), mSampleFormat,
1787  postpostBlock, 0, postpostLen, true);
1788 
1790  newBlock, start, scratch.ptr(), sum);
1791  b1++;
1792  }
1793  }
1794  else {
1795  // The sample where we begin deletion happens to fall
1796  // right on the end of a block.
1797  }
1798 
1799  // Copy the remaining blocks over from the old array
1800  for (i = b1 + 1; i < numBlocks; i++)
1801  newBlock.push_back(mBlock[i].Plus(-len));
1802 
1804  (newBlock, mNumSamples - len, wxT("Delete - branch two"));
1805 }

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

Referenced by WaveClip::ClearAndAddCutLine(), and WaveClip::ClearSequence().

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

◆ DoAppend()

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

Definition at line 1515 of file Sequence.cpp.

1517 {
1518  SeqBlock::SampleBlockPtr result;
1519 
1520  if (len == 0)
1521  return result;
1522 
1523  auto &factory = *mpFactory;
1524 
1525  // Quick check to make sure that it doesn't overflow
1526  if (Overflows(mNumSamples.as_double() + ((double)len)))
1528 
1529  BlockArray newBlock;
1530  sampleCount newNumSamples = mNumSamples;
1531 
1532  // If the last block is not full, we need to add samples to it
1533  int numBlocks = mBlock.size();
1534  SeqBlock *pLastBlock;
1535  decltype(pLastBlock->sb->GetSampleCount()) length;
1536  size_t bufferSize = mMaxSamples;
1537  SampleBuffer buffer2(bufferSize, mSampleFormat);
1538  bool replaceLast = false;
1539  if (coalesce &&
1540  numBlocks > 0 &&
1541  (length =
1542  (pLastBlock = &mBlock.back())->sb->GetSampleCount()) < mMinSamples) {
1543  // Enlarge a sub-minimum block at the end
1544  const SeqBlock &lastBlock = *pLastBlock;
1545  const auto addLen = std::min(mMaxSamples - length, len);
1546 
1547  Read(buffer2.ptr(), mSampleFormat, lastBlock, 0, length, true);
1548 
1549  CopySamples(buffer,
1550  format,
1551  buffer2.ptr() + length * SAMPLE_SIZE(mSampleFormat),
1552  mSampleFormat,
1553  addLen);
1554 
1555  const auto newLastBlockLen = length + addLen;
1556  SampleBlockPtr pBlock = factory.Create(
1557  buffer2.ptr(),
1558  newLastBlockLen,
1559  mSampleFormat);
1560  SeqBlock newLastBlock(pBlock, lastBlock.start);
1561 
1562  newBlock.push_back( newLastBlock );
1563 
1564  len -= addLen;
1565  newNumSamples += addLen;
1566  buffer += addLen * SAMPLE_SIZE(format);
1567 
1568  replaceLast = true;
1569  }
1570  // Append the rest as NEW blocks
1571  while (len) {
1572  const auto idealSamples = GetIdealBlockSize();
1573  const auto addedLen = std::min(idealSamples, len);
1574  SampleBlockPtr pBlock;
1575  if (format == mSampleFormat) {
1576  pBlock = factory.Create(buffer, addedLen, mSampleFormat);
1577  // It's expected that when not requesting coalescence, the
1578  // data should fit in one block
1579  wxASSERT( coalesce || !result );
1580  result = pBlock;
1581  }
1582  else {
1583  CopySamples(buffer, format, buffer2.ptr(), mSampleFormat, addedLen);
1584  pBlock = factory.Create(buffer2.ptr(), addedLen, mSampleFormat);
1585  }
1586 
1587  newBlock.push_back(SeqBlock(pBlock, newNumSamples));
1588 
1589  buffer += addedLen * SAMPLE_SIZE(format);
1590  newNumSamples += addedLen;
1591  len -= addedLen;
1592  }
1593 
1594  AppendBlocksIfConsistent(newBlock, replaceLast,
1595  newNumSamples, wxT("Append"));
1596 
1597 // JKC: During generate we use Append again and again.
1598 // If generating a long sequence this test would give O(n^2)
1599 // performance - not good!
1600 #ifdef VERY_SLOW_CHECKING
1601  ConsistencyCheck(wxT("Append"));
1602 #endif
1603 
1604  return result;
1605 }

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

Referenced by Append(), and AppendNewBlock().

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

◆ FindBlock()

int Sequence::FindBlock ( sampleCount  pos) const
private

Definition at line 999 of file Sequence.cpp.

1000 {
1001  wxASSERT(pos >= 0 && pos < mNumSamples);
1002 
1003  if (pos == 0)
1004  return 0;
1005 
1006  int numBlocks = mBlock.size();
1007 
1008  size_t lo = 0, hi = numBlocks, guess;
1009  sampleCount loSamples = 0, hiSamples = mNumSamples;
1010 
1011  while (true) {
1012  //this is not a binary search, but a
1013  //dictionary search where we guess something smarter than the binary division
1014  //of the unsearched area, since samples are usually proportional to block file number.
1015  const double frac = (pos - loSamples).as_double() /
1016  (hiSamples - loSamples).as_double();
1017  guess = std::min(hi - 1, lo + size_t(frac * (hi - lo)));
1018  const SeqBlock &block = mBlock[guess];
1019 
1020  wxASSERT(block.sb->GetSampleCount() > 0);
1021  wxASSERT(lo <= guess && guess < hi && lo < hi);
1022 
1023  if (pos < block.start) {
1024  wxASSERT(lo != guess);
1025  hi = guess;
1026  hiSamples = block.start;
1027  }
1028  else {
1029  const sampleCount nextStart = block.start + block.sb->GetSampleCount();
1030  if (pos < nextStart)
1031  break;
1032  else {
1033  wxASSERT(guess < hi - 1);
1034  lo = guess + 1;
1035  loSamples = nextStart;
1036  }
1037  }
1038  }
1039 
1040  const int rval = guess;
1041  wxASSERT(rval >= 0 && rval < numBlocks &&
1042  pos >= mBlock[rval].start &&
1043  pos < mBlock[rval].start + mBlock[rval].sb->GetSampleCount());
1044 
1045  return rval;
1046 }

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

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

Here is the call graph for this function:
Here is the caller 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 1088 of file Sequence.cpp.

1090 {
1091  bool result = true;
1092  while (len) {
1093  const SeqBlock &block = mBlock[b];
1094  // start is in block
1095  const auto bstart = (start - block.start).as_size_t();
1096  // bstart is not more than block length
1097  const auto blen = std::min(len, block.sb->GetSampleCount() - bstart);
1098 
1099  if (! Read(buffer, format, block, bstart, blen, mayThrow) )
1100  result = false;
1101 
1102  len -= blen;
1103  buffer += (blen * SAMPLE_SIZE(format));
1104  b++;
1105  start += blen;
1106  }
1107  return result;
1108 }

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

1072 {
1073  if (start == mNumSamples) {
1074  return len == 0;
1075  }
1076 
1077  if (start < 0 || start + len > mNumSamples) {
1078  if (mayThrow)
1080  ClearSamples( buffer, floatSample, 0, len );
1081  return false;
1082  }
1083  int b = FindBlock(start);
1084 
1085  return Get(b, buffer, format, start, len, mayThrow);
1086 }

References ClearSamples(), FindBlock(), floatSample, format, mNumSamples, 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:

◆ GetBestBlockSize()

size_t Sequence::GetBestBlockSize ( sampleCount  start) const

Definition at line 756 of file Sequence.cpp.

757 {
758  // This method returns a nice number of samples you should try to grab in
759  // one big chunk in order to land on a block boundary, based on the starting
760  // sample. The value returned will always be nonzero and will be no larger
761  // than the value of GetMaxBlockSize()
762 
763  if (start < 0 || start >= mNumSamples)
764  return mMaxSamples;
765 
766  int b = FindBlock(start);
767  int numBlocks = mBlock.size();
768 
769  const SeqBlock &block = mBlock[b];
770  // start is in block:
771  auto result = (block.start + block.sb->GetSampleCount() - start).as_size_t();
772 
773  decltype(result) length;
774  while(result < mMinSamples && b+1<numBlocks &&
775  ((length = mBlock[b+1].sb->GetSampleCount()) + result) <= mMaxSamples) {
776  b++;
777  result += length;
778  }
779 
780  wxASSERT(result > 0 && result <= mMaxSamples);
781 
782  return result;
783 }

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

186 { return mBlock; }

Referenced by GetAllSeqBlocks().

Here is the caller graph for this function:

◆ GetBlockArray() [2/2]

const BlockArray& Sequence::GetBlockArray ( ) const
inline

Definition at line 187 of file Sequence.h.

187 { return mBlock; }

◆ GetBlockStart()

sampleCount Sequence::GetBlockStart ( sampleCount  position) const

Definition at line 750 of file Sequence.cpp.

751 {
752  int b = FindBlock(position);
753  return mBlock[b].start;
754 }

References FindBlock(), and mBlock.

Here is the call graph for this function:

◆ GetErrorOpening()

bool Sequence::GetErrorOpening ( )
inline

Definition at line 142 of file Sequence.h.

142 { return mErrorOpening; }

◆ GetFactory()

const SampleBlockFactoryPtr& Sequence::GetFactory ( )
inline

Definition at line 131 of file Sequence.h.

131 { return mpFactory; }

Referenced by WaveTrack::AddClip().

Here is the caller graph for this function:

◆ GetIdealAppendLen()

size_t Sequence::GetIdealAppendLen ( ) const

Definition at line 1462 of file Sequence.cpp.

1463 {
1464  int numBlocks = mBlock.size();
1465  const auto max = GetMaxBlockSize();
1466 
1467  if (numBlocks == 0)
1468  return max;
1469 
1470  const auto lastBlockLen = mBlock.back().sb->GetSampleCount();
1471  if (lastBlockLen >= max)
1472  return max;
1473  else
1474  return max - lastBlockLen;
1475 }

References GetMaxBlockSize(), and mBlock.

Here is the call graph for this function:

◆ GetIdealBlockSize()

size_t Sequence::GetIdealBlockSize ( ) const

Definition at line 81 of file Sequence.cpp.

82 {
83  return mMaxSamples;
84 }

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

77 {
78  return mMaxSamples;
79 }

References mMaxSamples.

Referenced by GetIdealAppendLen().

Here is the caller graph for this function:

◆ GetMaxDiskBlockSize()

size_t Sequence::GetMaxDiskBlockSize ( )
static

Definition at line 1953 of file Sequence.cpp.

1954 {
1955  return sMaxDiskBlockSize;
1956 }

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

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

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

91 { return mNumSamples; }

◆ GetRMS()

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

Definition at line 304 of file Sequence.cpp.

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

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

Here is the call graph for this function:

◆ GetSampleFormat()

sampleFormat Sequence::GetSampleFormat ( ) const

Definition at line 94 of file Sequence.cpp.

95 {
96  return mSampleFormat;
97 }

References mSampleFormat.

◆ GetWaveDisplay()

bool Sequence::GetWaveDisplay ( float *  min,
float *  max,
float *  rms,
int *  bl,
size_t  len,
const sampleCount where 
) const

Definition at line 1265 of file Sequence.cpp.

1267 {
1268  wxASSERT(len > 0);
1269  const auto s0 = std::max(sampleCount(0), where[0]);
1270  if (s0 >= mNumSamples)
1271  // None of the samples asked for are in range. Abandon.
1272  return false;
1273 
1274  // In case where[len - 1] == where[len], raise the limit by one,
1275  // so we load at least one pixel for column len - 1
1276  // ... unless the mNumSamples ceiling applies, and then there are other defenses
1277  const auto s1 =
1278  std::min(mNumSamples, std::max(1 + where[len - 1], where[len]));
1279  Floats temp{ mMaxSamples };
1280 
1281  decltype(len) pixel = 0;
1282 
1283  auto srcX = s0;
1284  decltype(srcX) nextSrcX = 0;
1285  int lastRmsDenom = 0;
1286  int lastDivisor = 0;
1287  auto whereNow = std::min(s1 - 1, where[0]);
1288  decltype(whereNow) whereNext = 0;
1289  // Loop over block files, opening and reading and closing each
1290  // not more than once
1291  unsigned nBlocks = mBlock.size();
1292  const unsigned int block0 = FindBlock(s0);
1293  for (unsigned int b = block0; b < nBlocks; ++b) {
1294  if (b > block0)
1295  srcX = nextSrcX;
1296  if (srcX >= s1)
1297  break;
1298 
1299  // Find the range of sample values for this block that
1300  // are in the display.
1301  const SeqBlock &seqBlock = mBlock[b];
1302  const auto start = seqBlock.start;
1303  nextSrcX = std::min(s1, start + seqBlock.sb->GetSampleCount());
1304 
1305  // The column for pixel p covers samples from
1306  // where[p] up to but excluding where[p + 1].
1307 
1308  // Find the range of pixels covered by the current block file
1309  // (Their starting samples covered by it, to be exact)
1310  decltype(len) nextPixel;
1311  if (nextSrcX >= s1)
1312  // last pass
1313  nextPixel = len;
1314  else {
1315  nextPixel = pixel;
1316  // Taking min with s1 - 1, here and elsewhere, is another defense
1317  // to be sure the last pixel column gets at least one sample
1318  while (nextPixel < len &&
1319  (whereNext = std::min(s1 - 1, where[nextPixel])) < nextSrcX)
1320  ++nextPixel;
1321  }
1322  if (nextPixel == pixel)
1323  // The entire block's samples fall within one pixel column.
1324  // Either it's a rare odd block at the end, or else,
1325  // we must be really zoomed out!
1326  // Omit the entire block's contents from min/max/rms
1327  // calculation, which is not correct, but correctness might not
1328  // be worth the compute time if this happens every pixel
1329  // column. -- PRL
1330  continue;
1331  if (nextPixel == len)
1332  whereNext = s1;
1333 
1334  // Decide the summary level
1335  const double samplesPerPixel =
1336  (whereNext - whereNow).as_double() / (nextPixel - pixel);
1337  const int divisor =
1338  (samplesPerPixel >= 65536) ? 65536
1339  : (samplesPerPixel >= 256) ? 256
1340  : 1;
1341 
1342  int blockStatus = b;
1343 
1344  // How many samples or triples are needed?
1345 
1346  const size_t startPosition =
1347  // srcX and start are in the same block
1348  std::max(sampleCount(0), (srcX - start) / divisor).as_size_t();
1349  const size_t inclusiveEndPosition =
1350  // nextSrcX - 1 and start are in the same block
1351  std::min((sampleCount(mMaxSamples) / divisor) - 1,
1352  (nextSrcX - 1 - start) / divisor).as_size_t();
1353  const auto num = 1 + inclusiveEndPosition - startPosition;
1354  if (num <= 0) {
1355  // What? There was a zero length block file?
1356  wxASSERT(false);
1357  // Do some defense against this case anyway
1358  while (pixel < nextPixel) {
1359  min[pixel] = max[pixel] = rms[pixel] = 0;
1360  bl[pixel] = blockStatus;//MC
1361  ++pixel;
1362  }
1363  continue;
1364  }
1365 
1366  // Read from the block file or its summary
1367  switch (divisor) {
1368  default:
1369  case 1:
1370  // Read samples
1371  // no-throw for display operations!
1372  Read((samplePtr)temp.get(), floatSample, seqBlock, startPosition, num, false);
1373  break;
1374  case 256:
1375  // Read triples
1376  // Ignore the return value.
1377  // This function fills with zeroes if read fails
1378  seqBlock.sb->GetSummary256(temp.get(), startPosition, num);
1379  break;
1380  case 65536:
1381  // Read triples
1382  // Ignore the return value.
1383  // This function fills with zeroes if read fails
1384  seqBlock.sb->GetSummary64k(temp.get(), startPosition, num);
1385  break;
1386  }
1387 
1388  auto filePosition = startPosition;
1389 
1390  // The previous pixel column might straddle blocks.
1391  // If so, impute some of the data to it.
1392  if (b > block0 && pixel > 0) {
1393  // whereNow and start are in the same block
1394  auto midPosition = ((whereNow - start) / divisor).as_size_t();
1395  int diff(midPosition - filePosition);
1396  if (diff > 0) {
1397  MinMaxSumsq values(temp.get(), diff, divisor);
1398  const int lastPixel = pixel - 1;
1399  float &lastMin = min[lastPixel];
1400  lastMin = std::min(lastMin, values.min);
1401  float &lastMax = max[lastPixel];
1402  lastMax = std::max(lastMax, values.max);
1403  float &lastRms = rms[lastPixel];
1404  int lastNumSamples = lastRmsDenom * lastDivisor;
1405  lastRms = sqrt(
1406  (lastRms * lastRms * lastNumSamples + values.sumsq * divisor) /
1407  (lastNumSamples + diff * divisor)
1408  );
1409 
1410  filePosition = midPosition;
1411  }
1412  }
1413 
1414  // Loop over file positions
1415  int rmsDenom = 0;
1416  for (; filePosition <= inclusiveEndPosition;) {
1417  // Find range of pixel columns for this file position
1418  // (normally just one, but maybe more when zoomed very close)
1419  // and the range of positions for those columns
1420  // (normally one or more, for that one column)
1421  auto pixelX = pixel + 1;
1422  decltype(filePosition) positionX = 0;
1423  while (pixelX < nextPixel &&
1424  filePosition ==
1425  (positionX = (
1426  // s1 - 1 or where[pixelX] and start are in the same block
1427  (std::min(s1 - 1, where[pixelX]) - start) / divisor).as_size_t() )
1428  )
1429  ++pixelX;
1430  if (pixelX >= nextPixel)
1431  positionX = 1 + inclusiveEndPosition;
1432 
1433  // Find results to assign
1434  rmsDenom = (positionX - filePosition);
1435  wxASSERT(rmsDenom > 0);
1436  const float *const pv =
1437  temp.get() + (filePosition - startPosition) * (divisor == 1 ? 1 : 3);
1438  MinMaxSumsq values(pv, std::max(0, rmsDenom), divisor);
1439 
1440  // Assign results
1441  std::fill(&min[pixel], &min[pixelX], values.min);
1442  std::fill(&max[pixel], &max[pixelX], values.max);
1443  std::fill(&bl[pixel], &bl[pixelX], blockStatus);
1444  std::fill(&rms[pixel], &rms[pixelX], (float)sqrt(values.sumsq / rmsDenom));
1445 
1446  pixel = pixelX;
1447  filePosition = positionX;
1448  }
1449 
1450  wxASSERT(pixel == nextPixel);
1451  whereNow = whereNext;
1452  pixel = nextPixel;
1453  lastDivisor = divisor;
1454  lastRmsDenom = rmsDenom;
1455  } // for each block file
1456 
1457  wxASSERT(pixel == len);
1458 
1459  return true;
1460 }

References FindBlock(), floatSample, mBlock, min(), mMaxSamples, mNumSamples, Read(), SeqBlock::sb, SeqBlock::start, and values.

Here is the call graph for this function:

◆ HandleXMLChild()

XMLTagHandler * Sequence::HandleXMLChild ( const wxChar *  tag)
overridevirtual

Implements XMLTagHandler.

Definition at line 944 of file Sequence.cpp.

945 {
946  if (!wxStrcmp(tag, wxT("waveblock")))
947  {
948  return this;
949  }
950 
951  return nullptr;
952 }

◆ HandleXMLEndTag()

void Sequence::HandleXMLEndTag ( const wxChar *  tag)
override

Definition at line 902 of file Sequence.cpp.

903 {
904  if (wxStrcmp(tag, wxT("sequence")) != 0)
905  {
906  return;
907  }
908 
909  // Make sure that the sequence is valid.
910 
911  // Make sure that start times and lengths are consistent
912  sampleCount numSamples = 0;
913  for (unsigned b = 0, nn = mBlock.size(); b < nn; b++)
914  {
915  SeqBlock &block = mBlock[b];
916  if (block.start != numSamples)
917  {
918  wxLogWarning(
919  wxT("Gap detected in project file.\n")
920  wxT(" Start (%s) for block file %lld is not one sample past end of previous block (%s).\n")
921  wxT(" Moving start so blocks are contiguous."),
922  // PRL: Why bother with Internat when the above is just wxT?
923  Internat::ToString(block.start.as_double(), 0),
924  block.sb->GetBlockID(),
925  Internat::ToString(numSamples.as_double(), 0));
926  block.start = numSamples;
927  mErrorOpening = true;
928  }
929  numSamples += block.sb->GetSampleCount();
930  }
931 
932  if (mNumSamples != numSamples)
933  {
934  wxLogWarning(
935  wxT("Gap detected in project file. Correcting sequence sample count from %s to %s."),
936  // PRL: Why bother with Internat when the above is just wxT?
938  Internat::ToString(numSamples.as_double(), 0));
939  mNumSamples = numSamples;
940  mErrorOpening = true;
941  }
942 }

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

Here is the call graph for this function:

◆ HandleXMLTag()

bool Sequence::HandleXMLTag ( const wxChar *  tag,
const wxChar **  attrs 
)
overridevirtual

Implements XMLTagHandler.

Definition at line 785 of file Sequence.cpp.

786 {
787  auto &factory = *mpFactory;
788 
789  /* handle waveblock tag and its attributes */
790  if (!wxStrcmp(tag, wxT("waveblock")))
791  {
792  SeqBlock wb;
793 
794  // Give SampleBlock a go at the attributes first
795  wb.sb = factory.CreateFromXML(mSampleFormat, attrs);
796  if (wb.sb == nullptr)
797  {
798  mErrorOpening = true;
799  return false;
800  }
801 
802  // loop through attrs, which is a null-terminated list of attribute-value pairs
803  while(*attrs)
804  {
805  const wxChar *attr = *attrs++;
806  const wxChar *value = *attrs++;
807 
808  if (!value)
809  {
810  break;
811  }
812 
813  long long nValue = 0;
814 
815  const wxString strValue = value; // promote string, we need this for all
816 
817  if (wxStrcmp(attr, wxT("start")) == 0)
818  {
819  // This attribute is a sample offset, so can be 64bit
820  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
821  {
822  mErrorOpening = true;
823  return false;
824  }
825 
826  wb.start = nValue;
827  }
828  }
829 
830  mBlock.push_back(wb);
831 
832  return true;
833  }
834 
835  /* handle sequence tag and its attributes */
836  if (!wxStrcmp(tag, wxT("sequence")))
837  {
838  while(*attrs)
839  {
840  const wxChar *attr = *attrs++;
841  const wxChar *value = *attrs++;
842 
843  if (!value)
844  {
845  break;
846  }
847 
848  long long nValue = 0;
849 
850  const wxString strValue = value; // promote string, we need this for all
851 
852  if (!wxStrcmp(attr, wxT("maxsamples")))
853  {
854  // This attribute is a sample count, so can be 64bit
855  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
856  {
857  mErrorOpening = true;
858  return false;
859  }
860 
861  // Dominic, 12/10/2006:
862  // Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024
863  // - that's a pretty wide range of reasonable values.
864  if ((nValue < 1024) || (nValue > 64 * 1024 * 1024))
865  {
866  mErrorOpening = true;
867  return false;
868  }
869 
870  // nValue is now safe for size_t
871  mMaxSamples = nValue;
872  }
873  else if (!wxStrcmp(attr, wxT("sampleformat")))
874  {
875  // This attribute is a sample format, normal int
876  long fValue;
877  if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&fValue) || (fValue < 0) || !IsValidSampleFormat(fValue))
878  {
879  mErrorOpening = true;
880  return false;
881  }
882  mSampleFormat = (sampleFormat)fValue;
883  }
884  else if (!wxStrcmp(attr, wxT("numsamples")))
885  {
886  // This attribute is a sample count, so can be 64bit
887  if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0))
888  {
889  mErrorOpening = true;
890  return false;
891  }
892  mNumSamples = nValue;
893  }
894  } // while
895 
896  return true;
897  }
898 
899  return false;
900 }

References factory, XMLValueChecker::IsGoodInt(), XMLValueChecker::IsGoodInt64(), IsValidSampleFormat(), mBlock, mErrorOpening, mMaxSamples, mNumSamples, mpFactory, mSampleFormat, SeqBlock::sb, and SeqBlock::start.

Here is the call graph for this function:

◆ InsertSilence()

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

Definition at line 682 of file Sequence.cpp.

683 {
684  auto &factory = *mpFactory;
685 
686  // Quick check to make sure that it doesn't overflow
687  if (Overflows((mNumSamples.as_double()) + (len.as_double())))
689 
690  if (len <= 0)
691  return;
692 
693  // Create a NEW track containing as much silence as we
694  // need to insert, and then call Paste to do the insertion.
695 
697 
698  auto idealSamples = GetIdealBlockSize();
699 
700  sampleCount pos = 0;
701 
702  // Could nBlocks overflow a size_t? Not very likely. You need perhaps
703  // 2 ^ 52 samples which is over 3000 years at 44.1 kHz.
704  auto nBlocks = (len + idealSamples - 1) / idealSamples;
705  sTrack.mBlock.reserve(nBlocks.as_size_t());
706 
707  if (len >= idealSamples) {
708  auto silentFile = factory.CreateSilent(
709  idealSamples,
710  mSampleFormat);
711  while (len >= idealSamples) {
712  sTrack.mBlock.push_back(SeqBlock(silentFile, pos));
713 
714  pos += idealSamples;
715  len -= idealSamples;
716  }
717  }
718  if (len != 0) {
719  // len is not more than idealSamples:
720  sTrack.mBlock.push_back(SeqBlock(
721  factory.CreateSilent(len.as_size_t(), mSampleFormat), pos));
722  pos += len;
723  }
724 
725  sTrack.mNumSamples = pos;
726 
727  // use Strong-guarantee
728  Paste(s0, &sTrack);
729 }

References sampleCount::as_double(), sampleCount::as_size_t(), factory, GetIdealBlockSize(), mBlock, mNumSamples, mpFactory, mSampleFormat, anonymous_namespace{Sequence.cpp}::Overflows(), Paste(), and THROW_INCONSISTENCY_EXCEPTION.

Referenced by WaveClip::InsertSilence().

Here is the call graph for this function:
Here is the caller 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 1958 of file Sequence.cpp.

1959 {
1960  return (nValue == int16Sample) || (nValue == int24Sample) || (nValue == floatSample);
1961 }

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 )

◆ Paste()

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

Definition at line 475 of file Sequence.cpp.

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

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

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 
)
staticprivate

Definition at line 1049 of file Sequence.cpp.

1052 {
1053  const auto &sb = b.sb;
1054 
1055  wxASSERT(blockRelativeStart + len <= sb->GetSampleCount());
1056 
1057  // Either throws, or of !mayThrow, tells how many were really read
1058  auto result = sb->GetSamples(buffer, format, blockRelativeStart, len, mayThrow);
1059 
1060  if (result != len)
1061  {
1062  wxLogWarning(wxT("Expected to read %ld samples, got %ld samples."),
1063  len, result);
1064  return false;
1065  }
1066 
1067  return true;
1068 }

References format, and SeqBlock::sb.

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

Here is the caller graph for this function:

◆ SetMaxDiskBlockSize()

void Sequence::SetMaxDiskBlockSize ( size_t  bytes)
static

Definition at line 1948 of file Sequence.cpp.

1949 {
1950  sMaxDiskBlockSize = bytes;
1951 }

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 
)
Exception safety guarantee:
Strong

Definition at line 1112 of file Sequence.cpp.

1114 {
1115  auto &factory = *mpFactory;
1116 
1117  const auto size = mBlock.size();
1118 
1119  if (start < 0 || start + len > mNumSamples)
1121 
1122  size_t tempSize = mMaxSamples;
1123  // to do: allocate this only on demand
1124  SampleBuffer scratch(tempSize, mSampleFormat);
1125 
1126  SampleBuffer temp;
1127  if (buffer && format != mSampleFormat) {
1128  temp.Allocate(tempSize, mSampleFormat);
1129  }
1130 
1131  int b = FindBlock(start);
1132  BlockArray newBlock;
1133  std::copy( mBlock.begin(), mBlock.begin() + b, std::back_inserter(newBlock) );
1134 
1135  while (len > 0
1136  // Redundant termination condition,
1137  // but it guards against infinite loop in case of inconsistencies
1138  // (too-small files, not yet seen?)
1139  // that cause the loop to make no progress because blen == 0
1140  && b < (int)size
1141  ) {
1142  newBlock.push_back( mBlock[b] );
1143  SeqBlock &block = newBlock.back();
1144  // start is within block
1145  const auto bstart = ( start - block.start ).as_size_t();
1146  const auto fileLength = block.sb->GetSampleCount();
1147 
1148  // the std::min is a guard against inconsistent Sequence
1149  const auto blen =
1150  limitSampleBufferSize( fileLength - std::min( bstart, fileLength ),
1151  len );
1152  wxASSERT(blen == 0 || bstart + blen <= fileLength);
1153 
1154 #if 0
1155  // PRL: This inconsistency (too-big file) has been seen in "the wild"
1156  // in 2.2.0. It is the least problematic kind of inconsistency.
1157  // We will tolerate it for 2.2.1.
1158  // Not known whether it is only in projects saved in earlier versions.
1159  // After 2.2.1, we should detect and correct it at file loading time.
1160  if (fileLength > mMaxSamples) {
1162  }
1163 #endif
1164 
1165  ensureSampleBufferSize(scratch, mSampleFormat, tempSize, fileLength,
1166  &temp);
1167 
1168  auto useBuffer = buffer;
1169  if (buffer && format != mSampleFormat)
1170  {
1171  // To do: remove the extra movement.
1172  // Note: we ensured temp can hold fileLength. blen is not more
1173  CopySamples(buffer, format, temp.ptr(), mSampleFormat, blen);
1174  useBuffer = temp.ptr();
1175  }
1176 
1177  // We don't ever write to an existing block; to support Undo,
1178  // we copy the old block entirely into memory, dereference it,
1179  // make the change, and then write the NEW block to disk.
1180 
1181  if ( bstart > 0 || blen < fileLength ) {
1182  // First or last block is only partially overwritten
1183  Read(scratch.ptr(), mSampleFormat, block, 0, fileLength, true);
1184 
1185  if (useBuffer) {
1186  auto sampleSize = SAMPLE_SIZE(mSampleFormat);
1187  memcpy(scratch.ptr() +
1188  bstart * sampleSize, useBuffer, blen * sampleSize);
1189  }
1190  else
1191  ClearSamples(scratch.ptr(), mSampleFormat, bstart, blen);
1192 
1193  block.sb = factory.Create(
1194  scratch.ptr(),
1195  fileLength,
1196  mSampleFormat);
1197  }
1198  else {
1199  // Avoid reading the disk when the replacement is total
1200  if (useBuffer)
1201  block.sb = factory.Create(useBuffer, fileLength, mSampleFormat);
1202  else
1203  block.sb = factory.CreateSilent(fileLength, mSampleFormat);
1204  }
1205 
1206  // blen might be zero for inconsistent Sequence...
1207  if( buffer )
1208  buffer += (blen * SAMPLE_SIZE(format));
1209 
1210  len -= blen;
1211  start += blen;
1212 
1213  // ... but this, at least, always guarantees some loop progress:
1214  b++;
1215  }
1216 
1217  std::copy( mBlock.begin() + b, mBlock.end(), std::back_inserter(newBlock) );
1218 
1219  CommitChangesIfConsistent( newBlock, mNumSamples, wxT("SetSamples") );
1220 }

References SampleBuffer::Allocate(), ClearSamples(), CommitChangesIfConsistent(), CopySamples(), anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize(), factory, FindBlock(), format, limitSampleBufferSize(), mBlock, min(), mMaxSamples, mNumSamples, mpFactory, mSampleFormat, SampleBuffer::ptr(), Read(), SAMPLE_SIZE, SeqBlock::sb, size, SeqBlock::start, and THROW_INCONSISTENCY_EXCEPTION.

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

677 {
678  SetSamples(NULL, mSampleFormat, s0, len);
679 }

References mSampleFormat, and SetSamples().

Referenced by WaveClip::SetSilence().

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

◆ WriteXML()

void Sequence::WriteXML ( XMLWriter xmlFile) const

Definition at line 955 of file Sequence.cpp.

957 {
958  unsigned int b;
959 
960  xmlFile.StartTag(wxT("sequence"));
961 
962  xmlFile.WriteAttr(wxT("maxsamples"), mMaxSamples);
963  xmlFile.WriteAttr(wxT("sampleformat"), (size_t)mSampleFormat);
964  xmlFile.WriteAttr(wxT("numsamples"), mNumSamples.as_long_long() );
965 
966  for (b = 0; b < mBlock.size(); b++) {
967  const SeqBlock &bb = mBlock[b];
968 
969  // See http://bugzilla.audacityteam.org/show_bug.cgi?id=451.
970  if (bb.sb->GetSampleCount() > mMaxSamples)
971  {
972  // PRL: Bill observed this error. Not sure how it was caused.
973  // I have added code in ConsistencyCheck that should abort the
974  // editing operation that caused this, not fixing
975  // the problem but moving the point of detection earlier if we
976  // find a reproducible case.
977  auto sMsg =
978  XO("Sequence has block file exceeding maximum %s samples per block.\nTruncating to this maximum length.")
979  .Format( Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0) );
981  sMsg,
982  XO("Warning - Truncating Overlong Block File"),
983  wxICON_EXCLAMATION | wxOK);
984  wxLogWarning(sMsg.Translation()); //Debug?
985 // bb.sb->SetLength(mMaxSamples);
986  }
987 
988  xmlFile.StartTag(wxT("waveblock"));
989  xmlFile.WriteAttr(wxT("start"), bb.start.as_long_long() );
990 
991  bb.sb->SaveXML(xmlFile);
992 
993  xmlFile.EndTag(wxT("waveblock"));
994  }
995 
996  xmlFile.EndTag(wxT("sequence"));
997 }

References sampleCount::as_long_long(), AudacityMessageBox(), SeqBlock::sb, SeqBlock::start, Internat::ToString(), and XO.

Here is the call graph for this function:

Member Data Documentation

◆ mBlock

BlockArray Sequence::mBlock
private

◆ mErrorOpening

bool Sequence::mErrorOpening { false }
private

Definition at line 212 of file Sequence.h.

Referenced by HandleXMLEndTag(), and HandleXMLTag().

◆ mMaxSamples

size_t Sequence::mMaxSamples
private

◆ mMinSamples

size_t Sequence::mMinSamples
private

Definition at line 209 of file Sequence.h.

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

◆ mNumSamples

sampleCount Sequence::mNumSamples { 0 }
private

◆ mpFactory

SampleBlockFactoryPtr Sequence::mpFactory
private

◆ mSampleFormat

sampleFormat Sequence::mSampleFormat
private

◆ sMaxDiskBlockSize

size_t Sequence::sMaxDiskBlockSize = 1048576
staticprivate

Definition at line 195 of file Sequence.h.

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


The documentation for this class was generated from the following files:
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
Sequence::mNumSamples
sampleCount mNumSamples
Definition: Sequence.h:207
XMLWriter::EndTag
virtual void EndTag(const wxString &name)
Definition: XMLWriter.cpp:99
Sequence::sMaxDiskBlockSize
static size_t sMaxDiskBlockSize
Definition: Sequence.h:195
anonymous_namespace{Sequence.cpp}::ensureSampleBufferSize
void ensureSampleBufferSize(SampleBuffer &buffer, sampleFormat format, size_t &size, size_t required, SampleBuffer *pSecondBuffer=nullptr)
Definition: Sequence.cpp:111
ClearSamples
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
Definition: SampleFormat.cpp:77
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:84
Sequence::ConsistencyCheck
void ConsistencyCheck(const wxChar *whereStr, bool mayThrow=true) const
Definition: Sequence.cpp:1807
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
Sequence::mMaxSamples
size_t mMaxSamples
Definition: Sequence.h:210
Sequence::FindBlock
int FindBlock(sampleCount pos) const
Definition: Sequence.cpp:999
XMLValueChecker::IsGoodInt
static bool IsGoodInt(const wxString &strInt)
Check that the supplied string can be converted to a long (32bit) integer.
Definition: XMLTagHandler.cpp:157
SampleBlockPtr
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: SampleBlock.h:23
Sequence::mMinSamples
size_t mMinSamples
Definition: Sequence.h:209
Sequence::Blockify
static void Blockify(SampleBlockFactory &factory, size_t maxSamples, sampleFormat format, BlockArray &list, sampleCount start, constSamplePtr buffer, size_t len)
Definition: Sequence.cpp:1607
Sequence::mBlock
BlockArray mBlock
Definition: Sequence.h:203
Sequence::SetSamples
void SetSamples(constSamplePtr buffer, sampleFormat format, sampleCount start, sampleCount len)
Definition: Sequence.cpp:1112
XMLValueChecker::IsGoodInt64
static bool IsGoodInt64(const wxString &strInt)
Check that the supplied string can be converted to a 64bit integer.
Definition: XMLTagHandler.cpp:163
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
XO
#define XO(s)
Definition: Internat.h:31
Sequence::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: Sequence.cpp:76
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
TranslatableString::Debug
wxString Debug() const
Format as an English string for debugging logs and developers' eyes, not for end users.
Definition: TranslatableString.h:82
Sequence
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:61
SeqBlock::SampleBlockPtr
std::shared_ptr< SampleBlock > SampleBlockPtr
Definition: Sequence.h:30
Sequence::AppendBlocksIfConsistent
void AppendBlocksIfConsistent(BlockArray &additionalBlocks, bool replaceLast, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1876
Sequence::mErrorOpening
bool mErrorOpening
Definition: Sequence.h:212
Sequence::Read
static bool Read(samplePtr buffer, sampleFormat format, const SeqBlock &b, size_t blockRelativeStart, size_t len, bool mayThrow)
Definition: Sequence.cpp:1049
floatSample
@ floatSample
Definition: SampleFormat.h:34
Sequence::mSampleFormat
sampleFormat mSampleFormat
Definition: Sequence.h:204
Sequence::IsValidSampleFormat
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
Definition: Sequence.cpp:1958
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:817
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
SeqBlock::sb
SampleBlockPtr sb
Definition: Sequence.h:31
CopySamples
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.
Definition: SampleFormat.cpp:111
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
Sequence::CommitChangesIfConsistent
void CommitChangesIfConsistent(BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr)
Definition: Sequence.cpp:1864
format
int format
Definition: ExportPCM.cpp:56
THROW_INCONSISTENCY_EXCEPTION
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
Definition: InconsistencyException.h:79
Sequence::mpFactory
SampleBlockFactoryPtr mpFactory
Definition: Sequence.h:201
sampleCount::as_size_t
size_t as_size_t() const
Definition: SampleCount.cpp:17
SampleBuffer
Definition: SampleFormat.h:69
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
Sequence::DoAppend
SeqBlock::SampleBlockPtr DoAppend(constSamplePtr buffer, sampleFormat format, size_t len, bool coalesce)
Definition: Sequence.cpp:1515
SeqBlock
Data structure containing pointer to a sample block and a start time. Element of a BlockArray.
Definition: Sequence.h:28
BlockArray
Definition: Sequence.h:49
Sequence::Paste
void Paste(sampleCount s0, const Sequence *src)
Definition: Sequence.cpp:475
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
anonymous_namespace{Sequence.cpp}::ShareOrCopySampleBlock
SampleBlockPtr ShareOrCopySampleBlock(SampleBlockFactory *pFactory, sampleFormat format, SampleBlockPtr sb)
Definition: Sequence.cpp:457
XMLWriter::WriteAttr
void WriteAttr(const wxString &name, const Identifier &value)
Definition: XMLWriter.h:34
GetSampleFormatStr
TranslatableString GetSampleFormatStr(sampleFormat format)
Definition: SampleFormat.cpp:60
Sequence::AppendBlock
static void AppendBlock(SampleBlockFactory *pFactory, sampleFormat format, BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b)
Definition: Sequence.cpp:731
values
const wxChar * values
Definition: Equalization.cpp:472
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
ArrayOf< float >
XMLWriter::StartTag
virtual void StartTag(const wxString &name)
Definition: XMLWriter.cpp:76
Internat::ToString
static wxString ToString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, always uses the dot as decimal separator.
Definition: Internat.cpp:151
SeqBlock::start
sampleCount start
the sample in the global wavetrack that this block starts at.
Definition: Sequence.h:33
Sequence::Get
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, bool mayThrow) const
Definition: Sequence.cpp:1070
anonymous_namespace{Sequence.cpp}::Overflows
bool Overflows(double numSamples)
Definition: Sequence.cpp:452
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98
Sequence::GetIdealBlockSize
size_t GetIdealBlockSize() const
Definition: Sequence.cpp:81