1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-05 06:39:26 +02:00
audacity/src/Sequence.h
Paul Licameli 127696879d
Unitary changes (#599)
* Define SampleBlockFactory replacing static members of SampleBlock...

... This will become an abstract base class

* Sequence and WaveTrack only store SampleBlockFactory not Project...

... This adds a dependency from Track to SampleBlock which temporarily enlarges
a cycle in the dependency graph

* Register a global factory of SampleBlockFactory...

... so that later we can make an abstract SampleBlockFactory, separate from the
concrete implementation in terms of sqlite, and inject the dependency at startup
avoiding static dependency

* New concrete classes SqliteSampleBlock, SqliteSampleBlockFactory...

... separated from abstract base classes and put into a new source file,
breaking dependency cycles, and perhaps allowing easy reimplementation for other
databases in the future.

Note that the new file is a header-less plug-in!  Nothing depends on it.  It
uses static initialization to influence the program's behavior.

* Compile dependency on sqlite3.h limited to just two .cpp files...

... these are ProjectFileIO.cpp and SqliteSampleBlock.cpp.

But there is still close cooperation of ProjectFileIO and SqliteSampleBlock.cpp.
This suggests that these files ought to be merged, and perhaps ProjectFileIO
also needs to be split into abstract and concrete classes, and there should be
another injection of a factory function at startup.  That will make the choice
of database implementation even more modular.

Also removed one unnecessary inclusion of ProjectFileIO.h

* Fix crashes cutting and pasting cross-project...

... in case the source project is closed before the paste happens.

This caused destruction of the ProjectFileIO object and a closing of the sqlite
database with the sample data in it, leaving dangling references in the
SqliteSampleBlock objects.

The fix is that the SqliteSampleBlockFactory object holds a shared_ptr to the
ProjectFileIO object.  So the clipboard may own WaveTracks, which own WaveClips,
which own Sequences, which own SqliteSampleBlockFactories, which keep the
ProjectFileIO and the database connection alive until the clipboard is cleared.

The consequence of the fix is delayed closing of the entire database associated
with the source project.

If the source project is reopened before the clipboard is cleared, will there
be correct concurrent access to the same persistent store?  My preliminary
trials suggest this is so (reopening a saved project, deleting from it, closing
it again -- the clipboard contents are still unchanged and available).
2020-07-02 18:11:38 -05:00

266 lines
7.4 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Sequence.h
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_SEQUENCE__
#define __AUDACITY_SEQUENCE__
#include <vector>
#include "SampleFormat.h"
#include "xml/XMLTagHandler.h"
#include "audacity/Types.h"
class SampleBlock;
class SampleBlockFactory;
using SampleBlockFactoryPtr = std::shared_ptr<SampleBlockFactory>;
// This is an internal data structure! For advanced use only.
class SeqBlock {
public:
using SampleBlockPtr = std::shared_ptr<SampleBlock>;
SampleBlockPtr sb;
///the sample in the global wavetrack that this block starts at.
sampleCount start;
SeqBlock()
: sb{}, start(0)
{}
SeqBlock(const SampleBlockPtr &sb_, sampleCount start_)
: sb(sb_), start(start_)
{}
// Construct a SeqBlock with changed start, same file
SeqBlock Plus(sampleCount delta) const
{
return SeqBlock(sb, start + delta);
}
};
class BlockArray : public std::vector<SeqBlock> {};
using BlockPtrArray = std::vector<SeqBlock*>; // non-owning pointers
class PROFILE_DLL_API Sequence final : public XMLTagHandler{
public:
//
// Static methods
//
static void SetMaxDiskBlockSize(size_t bytes);
static size_t GetMaxDiskBlockSize();
//
// Constructor / Destructor / Duplicator
//
Sequence(const SampleBlockFactoryPtr &pFactory, sampleFormat format);
Sequence(const Sequence &orig, const SampleBlockFactoryPtr &pFactory);
Sequence& operator= (const Sequence&) PROHIBITED;
~Sequence();
//
// Editing
//
sampleCount GetNumSamples() const { return mNumSamples; }
bool Get(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len, bool mayThrow) const;
// Note that len is not size_t, because nullptr may be passed for buffer, in
// which case, silence is inserted, possibly a large amount.
void SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len);
// where is input, assumed to be nondecreasing, and its size is len + 1.
// min, max, rms, bl are outputs, and their lengths are len.
// Each position in the output arrays corresponds to one column of pixels.
// The column for pixel p covers samples from
// where[p] up to (but excluding) where[p + 1].
// bl is negative wherever data are not yet available.
// Return true if successful.
bool GetWaveDisplay(float *min, float *max, float *rms, int* bl,
size_t len, const sampleCount *where) const;
// Return non-null, or else throw!
std::unique_ptr<Sequence> Copy(sampleCount s0, sampleCount s1) const;
void Paste(sampleCount s0, const Sequence *src);
size_t GetIdealAppendLen() const;
void Append(samplePtr buffer, sampleFormat format, size_t len);
void Delete(sampleCount start, sampleCount len);
void SetSilence(sampleCount s0, sampleCount len);
void InsertSilence(sampleCount s0, sampleCount len);
const SampleBlockFactoryPtr &GetFactory() { return mpFactory; }
//
// XMLTagHandler callback methods for loading and saving
//
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
void HandleXMLEndTag(const wxChar *tag) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXML(XMLWriter &xmlFile) const /* not override */;
bool GetErrorOpening() { return mErrorOpening; }
//
// Lock/Unlock all of this sequence's BlockFiles, keeping them
// from being moved. Call this if you want to copy a
// track to a different DirManager. See BlockFile.h
// for details.
//
bool Lock();
bool Unlock();
bool CloseLock();//similar to Lock but should be called upon project close.
// not balanced by unlocking calls.
//
// Manipulating Sample Format
//
sampleFormat GetSampleFormat() const;
// Return true iff there is a change
bool ConvertToSampleFormat(sampleFormat format);
//
// Retrieving summary info
//
std::pair<float, float> GetMinMax(
sampleCount start, sampleCount len, bool mayThrow) const;
float GetRMS(sampleCount start, sampleCount len, bool mayThrow) const;
//
// Getting block size and alignment information
//
// This returns a possibly large or negative value
sampleCount GetBlockStart(sampleCount position) const;
// These return a nonnegative number of samples meant to size a memory buffer
size_t GetBestBlockSize(sampleCount start) const;
size_t GetMaxBlockSize() const;
size_t GetIdealBlockSize() const;
//
// This should only be used if you really, really know what
// you're doing!
//
BlockArray &GetBlockArray() { return mBlock; }
const BlockArray &GetBlockArray() const { return mBlock; }
private:
//
// Private static variables
//
static size_t sMaxDiskBlockSize;
//
// Private variables
//
SampleBlockFactoryPtr mpFactory;
BlockArray mBlock;
sampleFormat mSampleFormat;
// Not size_t! May need to be large:
sampleCount mNumSamples{ 0 };
size_t mMinSamples; // min samples per block
size_t mMaxSamples; // max samples per block
bool mErrorOpening{ false };
//
// Private methods
//
int FindBlock(sampleCount pos) const;
static void AppendBlock(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);
// Accumulate NEW block files onto the end of a block array.
// Does not change this sequence. The intent is to use
// CommitChangesIfConsistent later.
static void Blockify(SampleBlockFactory &factory,
size_t maxSamples,
sampleFormat format,
BlockArray &list,
sampleCount start,
samplePtr buffer,
size_t len);
bool Get(int b,
samplePtr buffer,
sampleFormat format,
sampleCount start,
size_t len,
bool mayThrow) const;
public:
//
// Public methods
//
// This function throws if the track is messed up
// because of inconsistent block starts & lengths
void ConsistencyCheck (const wxChar *whereStr, bool mayThrow = true) const;
// This function prints information to stdout about the blocks in the
// tracks and indicates if there are inconsistencies.
static void DebugPrintf
(const BlockArray &block, sampleCount numSamples, wxString *dest);
private:
static void ConsistencyCheck
(const BlockArray &block, size_t maxSamples, size_t from,
sampleCount numSamples, const wxChar *whereStr,
bool mayThrow = true);
// The next two are used in methods that give a strong guarantee.
// They either throw because final consistency check fails, or swap the
// changed contents into place.
void CommitChangesIfConsistent
(BlockArray &newBlock, sampleCount numSamples, const wxChar *whereStr);
void AppendBlocksIfConsistent
(BlockArray &additionalBlocks, bool replaceLast,
sampleCount numSamples, const wxChar *whereStr);
};
#endif // __AUDACITY_SEQUENCE__