audacia/src/WaveClip.h

383 lines
12 KiB
C
Raw Normal View History

/**********************************************************************
Audacity: A Digital Audio Editor
WaveClip.h
?? Dominic Mazzoni
?? Markus Meyer
*******************************************************************/
#ifndef __AUDACITY_WAVECLIP__
#define __AUDACITY_WAVECLIP__
#include "SampleFormat.h"
#include "xml/XMLTagHandler.h"
#include <wx/longlong.h>
#include <vector>
2020-08-28 19:07:04 +00:00
#include <functional>
2015-07-05 15:22:03 +00:00
class BlockArray;
class Envelope;
class ProgressDialog;
class SampleBlock;
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 23:11:38 +00:00
class SampleBlockFactory;
using SampleBlockFactoryPtr = std::shared_ptr<SampleBlockFactory>;
2015-07-05 15:22:03 +00:00
class Sequence;
class SpectrogramSettings;
class WaveCache;
class WaveTrackCache;
class wxFileNameWrapper;
class AUDACITY_DLL_API SpecCache {
public:
// Make invalid cache
SpecCache()
: algorithm(-1)
, pps(-1.0)
, start(-1.0)
, windowType(-1)
, frequencyGain(-1)
, dirty(-1)
{
}
~SpecCache()
{
}
bool Matches(int dirty_, double pixelsPerSecond,
const SpectrogramSettings &settings, double rate) const;
// Calculate one column of the spectrum
bool CalculateOneSpectrum
(const SpectrogramSettings &settings,
WaveTrackCache &waveTrackCache,
const int xx, sampleCount numSamples,
double offset, double rate, double pixelsPerSecond,
int lowerBoundX, int upperBoundX,
2015-08-17 01:14:41 +00:00
const std::vector<float> &gainFactors,
2016-08-25 17:41:40 +00:00
float* __restrict scratch,
float* __restrict out) const;
// Grow the cache while preserving the (possibly now invalid!) contents
void Grow(size_t len_, const SpectrogramSettings& settings,
double pixelsPerSecond, double start_);
// Calculate the dirty columns at the begin and end of the cache
void Populate
(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache,
int copyBegin, int copyEnd, size_t numPixels,
sampleCount numSamples,
double offset, double rate, double pixelsPerSecond);
size_t len { 0 }; // counts pixels, not samples
int algorithm;
double pps;
double start;
int windowType;
size_t windowSize { 0 };
unsigned zeroPaddingFactor { 0 };
int frequencyGain;
std::vector<float> freq;
std::vector<sampleCount> where;
int dirty;
};
class SpecPxCache {
public:
SpecPxCache(size_t cacheLen)
2016-04-14 16:17:59 +00:00
: len{ cacheLen }
, values{ len }
{
valid = false;
scaleType = 0;
range = gain = -1;
minFreq = maxFreq = -1;
}
size_t len;
2016-04-14 16:17:59 +00:00
Floats values;
bool valid;
int scaleType;
int range;
int gain;
int minFreq;
int maxFreq;
};
class WaveClip;
// Array of pointers that assume ownership
2017-07-09 18:09:33 +00:00
using WaveClipHolder = std::shared_ptr< WaveClip >;
using WaveClipHolders = std::vector < WaveClipHolder >;
2017-07-09 18:09:33 +00:00
using WaveClipConstHolders = std::vector < std::shared_ptr< const WaveClip > >;
// A bundle of arrays needed for drawing waveforms. The object may or may not
// own the storage for those arrays. If it does, it destroys them.
2015-06-02 23:15:29 +00:00
class WaveDisplay
{
2015-06-02 23:15:29 +00:00
public:
int width;
sampleCount *where;
float *min, *max, *rms;
int* bl;
std::vector<sampleCount> ownWhere;
std::vector<float> ownMin, ownMax, ownRms;
std::vector<int> ownBl;
public:
WaveDisplay(int w)
: width(w), where(0), min(0), max(0), rms(0), bl(0)
{
}
// Create "own" arrays.
void Allocate()
{
ownWhere.resize(width + 1);
ownMin.resize(width);
ownMax.resize(width);
ownRms.resize(width);
ownBl.resize(width);
where = &ownWhere[0];
if (width > 0) {
min = &ownMin[0];
max = &ownMax[0];
rms = &ownRms[0];
bl = &ownBl[0];
}
else {
min = max = rms = 0;
bl = 0;
}
}
~WaveDisplay()
{
}
};
class AUDACITY_DLL_API WaveClip final : public XMLTagHandler
{
private:
// It is an error to copy a WaveClip without specifying the
// sample block factory.
WaveClip(const WaveClip&) PROHIBITED;
WaveClip& operator= (const WaveClip&) PROHIBITED;
public:
// typical constructor
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 23:11:38 +00:00
WaveClip(const SampleBlockFactoryPtr &factory, sampleFormat format,
int rate, int colourIndex);
// essentially a copy constructor - but you must pass in the
// current sample block factory, because we might be copying
// from one project to another
2016-11-26 20:20:28 +00:00
WaveClip(const WaveClip& orig,
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 23:11:38 +00:00
const SampleBlockFactoryPtr &factory,
2016-11-26 20:20:28 +00:00
bool copyCutlines);
// Copy only a range from the given WaveClip
WaveClip(const WaveClip& orig,
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 23:11:38 +00:00
const SampleBlockFactoryPtr &factory,
2016-11-26 20:20:28 +00:00
bool copyCutlines,
double t0, double t1);
virtual ~WaveClip();
2020-08-28 19:07:04 +00:00
void ConvertToSampleFormat(sampleFormat format,
const std::function<void(size_t)> & progressReport = {});
// Always gives non-negative answer, not more than sample sequence length
// even if t0 really falls outside that range
void TimeToSamplesClip(double t0, sampleCount *s0) const;
int GetRate() const { return mRate; }
2014-06-03 20:30:19 +00:00
// Set rate without resampling. This will change the length of the clip
void SetRate(int rate);
2014-06-03 20:30:19 +00:00
// Resample clip. This also will set the rate, but without changing
// the length of the clip
void Resample(int rate, ProgressDialog *progress = NULL);
2014-06-03 20:30:19 +00:00
void SetColourIndex( int index ){ mColourIndex = index;};
int GetColourIndex( ) const { return mColourIndex;};
void SetOffset(double offset);
double GetOffset() const { return mOffset; }
/*! @excsafety{No-fail} */
void Offset(double delta)
{ SetOffset(GetOffset() + delta); }
double GetStartTime() const;
double GetEndTime() const;
sampleCount GetStartSample() const;
sampleCount GetEndSample() const;
2015-07-05 15:22:03 +00:00
sampleCount GetNumSamples() const;
// One and only one of the following is true for a given t (unless the clip
// has zero length -- then BeforeClip() and AfterClip() can both be true).
// Within() is true if the time is substantially within the clip
bool WithinClip(double t) const;
bool BeforeClip(double t) const;
bool AfterClip(double t) const;
bool IsClipStartAfterClip(double t) const;
bool GetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, size_t len, bool mayThrow = true) const;
void SetSamples(constSamplePtr buffer, sampleFormat format,
sampleCount start, size_t len);
2014-06-03 20:30:19 +00:00
Envelope* GetEnvelope() { return mEnvelope.get(); }
const Envelope* GetEnvelope() const { return mEnvelope.get(); }
2015-07-05 15:22:03 +00:00
BlockArray* GetSequenceBlockArray();
const BlockArray* GetSequenceBlockArray() const;
// Get low-level access to the sequence. Whenever possible, don't use this,
// but use more high-level functions inside WaveClip (or add them if you
// think they are useful for general use)
Sequence* GetSequence() { return mSequence.get(); }
const Sequence* GetSequence() const { return mSequence.get(); }
/** WaveTrack calls this whenever data in the wave clip changes. It is
* called automatically when WaveClip has a chance to know that something
* has changed, like when member functions SetSamples() etc. are called. */
/*! @excsafety{No-fail} */
void MarkChanged()
{ mDirty++; }
/** Getting high-level data for screen display and clipping
* calculations and Contrast */
bool GetWaveDisplay(WaveDisplay &display,
double t0, double pixelsPerSecond) const;
bool GetSpectrogram(WaveTrackCache &cache,
const float *& spectrogram,
const sampleCount *& where,
size_t numPixels,
double t0, double pixelsPerSecond) const;
std::pair<float, float> GetMinMax(
double t0, double t1, bool mayThrow = true) const;
float GetRMS(double t0, double t1, bool mayThrow = true) const;
/** Whenever you do an operation to the sequence that will change the number
* of samples (that is, the length of the clip), you will want to call this
* function to tell the envelope about it. */
void UpdateEnvelopeTrackLen();
//! For use in importing pre-version-3 projects to preserve sharing of blocks
std::shared_ptr<SampleBlock> AppendNewBlock(
samplePtr buffer, sampleFormat format, size_t len);
//! For use in importing pre-version-3 projects to preserve sharing of blocks
void AppendSharedBlock(const std::shared_ptr<SampleBlock> &pBlock);
/// You must call Flush after the last Append
/// @return true if at least one complete block was created
bool Append(constSamplePtr buffer, sampleFormat format,
2020-11-28 19:37:02 +00:00
size_t len, unsigned int stride);
/// Flush must be called after last Append
void Flush();
/// This name is consistent with WaveTrack::Clear. It performs a "Cut"
2021-01-12 09:54:34 +00:00
/// operation (but without putting the cut audio to the clipboard)
void Clear(double t0, double t1);
/// Clear, and add cut line that starts at t0 and contains everything until t1.
void ClearAndAddCutLine(double t0, double t1);
/// Paste data from other clip, resampling it if not equal rate
void Paste(double t0, const WaveClip* other);
/** Insert silence - note that this is an efficient operation for large
* amounts of silence */
void InsertSilence( double t, double len, double *pEnvelopeValue = nullptr );
/** Insert silence at the end, and causes the envelope to ramp
linearly to the given value */
void AppendSilence( double len, double envelopeValue );
/// Get access to cut lines list
WaveClipHolders &GetCutLines() { return mCutLines; }
const WaveClipConstHolders &GetCutLines() const
{ return reinterpret_cast< const WaveClipConstHolders& >( mCutLines ); }
size_t NumCutLines() const { return mCutLines.size(); }
2014-06-03 20:30:19 +00:00
/** Find cut line at (approximately) this position. Returns true and fills
* in cutLineStart and cutLineEnd (if specified) if a cut line at this
* position could be found. Return false otherwise. */
bool FindCutLine(double cutLinePosition,
double* cutLineStart = NULL,
double *cutLineEnd = NULL) const;
/** Expand cut line (that is, re-insert audio, then DELETE audio saved in
2020-04-11 07:08:33 +00:00
* cut line). Returns true if a cut line could be found and successfully
* expanded, false otherwise */
void ExpandCutLine(double cutLinePosition);
/// Remove cut line, without expanding the audio in it
bool RemoveCutLine(double cutLinePosition);
2014-06-03 20:30:19 +00:00
/// Offset cutlines right to time 't0' by time amount 'len'
void OffsetCutLines(double t0, double len);
2014-06-03 20:30:19 +00:00
void CloseLock(); //should be called when the project closes.
2016-04-12 03:38:33 +00:00
// not balanced by unlocking calls.
///Delete the wave cache - force redraw. Thread-safe
void ClearWaveCache();
2014-06-03 20:30:19 +00:00
//
// XMLTagHandler callback methods for loading and saving
//
2014-06-03 20:30:19 +00:00
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
void HandleXMLEndTag(const wxChar *tag) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
2017-02-22 19:23:35 +00:00
void WriteXML(XMLWriter &xmlFile) const /* not override */;
// AWD, Oct 2009: for pasting whitespace at the end of selection
bool GetIsPlaceholder() const { return mIsPlaceholder; }
void SetIsPlaceholder(bool val) { mIsPlaceholder = val; }
// used by commands which interact with clips using the keyboard
bool SharesBoundaryWithNextClip(const WaveClip* next) const;
public:
// Cache of values to colour pixels of Spectrogram - used by TrackArtist
mutable std::unique_ptr<SpecPxCache> mSpecPxCache;
protected:
double mOffset { 0 };
int mRate;
int mDirty { 0 };
int mColourIndex;
std::unique_ptr<Sequence> mSequence;
std::unique_ptr<Envelope> mEnvelope;
mutable std::unique_ptr<WaveCache> mWaveCache;
mutable std::unique_ptr<SpecCache> mSpecCache;
SampleBuffer mAppendBuffer {};
size_t mAppendBufferLen { 0 };
// Cut Lines are nothing more than ordinary wave clips, with the
// offset relative to the start of the clip.
WaveClipHolders mCutLines {};
// AWD, Oct. 2009: for whitespace-at-end-of-selection pasting
bool mIsPlaceholder { false };
};
#endif