Preliminaries for bug 900

Create WaveTrackCache as a utility class but don't use it anywhere yet.

The possible minor performance problem with effects is fixed by changes
in WaveTrack::GetBestBlockSize().
This commit is contained in:
Paul-Licameli 2015-03-28 14:46:40 -04:00 committed by Paul Licameli
parent 16e0fe3e12
commit bdc2839112
17 changed files with 365 additions and 60 deletions

View File

@ -79,6 +79,7 @@ typedef enum
// Generic pointer to sample data
// ----------------------------------------------------------------------------
typedef char *samplePtr;
typedef const char *constSamplePtr;
// ----------------------------------------------------------------------------
// The type for plugin IDs

View File

@ -301,6 +301,7 @@ writing audio.
#include "RingBuffer.h"
#include "Prefs.h"
#include "Project.h"
#include "TimeTrack.h"
#include "WaveTrack.h"
#include "AutoRecovery.h"

View File

@ -29,6 +29,7 @@ for drawing different aspects of the label and its text box.
*//*******************************************************************/
#include "Audacity.h"
#include "LabelTrack.h"
#include <stdio.h>
@ -48,7 +49,6 @@ for drawing different aspects of the label and its text box.
#include <wx/utils.h>
#include "AudioIO.h"
#include "LabelTrack.h"
#include "DirManager.h"
#include "Internat.h"
#include "Prefs.h"
@ -1117,7 +1117,12 @@ bool LabelTrack::IsTextClipSupported()
}
double LabelTrack::GetStartTime()
double LabelTrack::GetOffset() const
{
return mOffset;
}
double LabelTrack::GetStartTime() const
{
int len = mLabels.Count();
@ -1127,7 +1132,7 @@ double LabelTrack::GetStartTime()
return mLabels[0]->getT0();
}
double LabelTrack::GetEndTime()
double LabelTrack::GetEndTime() const
{
//we need to scan through all the labels, because the last
//label might not have the right-most end (if there is overlap).

View File

@ -132,8 +132,9 @@ class AUDACITY_DLL_API LabelTrack : public Track
virtual int GetKind() const { return Label; }
virtual double GetStartTime();
virtual double GetEndTime();
virtual double GetOffset() const;
virtual double GetStartTime() const;
virtual double GetEndTime() const;
virtual Track *Duplicate() { return new LabelTrack(*this); }

View File

@ -83,6 +83,7 @@ simplifies construction of menu items.
#include "NoteTrack.h"
#endif // USE_MIDI
#include "Tags.h"
#include "TimeTrack.h"
#include "Mix.h"
#include "AboutDialog.h"
#include "Benchmark.h"

View File

@ -14,20 +14,20 @@
*//*******************************************************************/
#include "Audacity.h"
#include "NoteTrack.h"
#include <wx/dc.h>
#include <wx/brush.h>
#include <wx/pen.h>
#include <wx/intl.h>
#include "Audacity.h"
#if defined(USE_MIDI)
#include <sstream>
#define ROUND(x) ((int) ((x) + 0.5))
#include "AColor.h"
#include "NoteTrack.h"
#include "DirManager.h"
#include "Internat.h"
#include "Prefs.h"
@ -171,13 +171,17 @@ Track *NoteTrack::Duplicate()
}
double NoteTrack::GetStartTime()
double NoteTrack::GetOffset() const
{
return mOffset;
}
double NoteTrack::GetStartTime() const
{
return GetOffset();
}
double NoteTrack::GetEndTime()
double NoteTrack::GetEndTime() const
{
return GetStartTime() + (mSeq ? mSeq->get_real_dur() : 0.0);
}

View File

@ -61,8 +61,9 @@ class AUDACITY_DLL_API NoteTrack:public Track {
virtual int GetKind() const { return Note; }
virtual double GetStartTime();
virtual double GetEndTime();
virtual double GetOffset() const;
virtual double GetStartTime() const;
virtual double GetEndTime() const;
void WarpAndTransposeNotes(double t0, double t1,
const TimeWarper &warper, double semitones);

View File

@ -113,6 +113,7 @@ scroll information. It also has some status flags.
#include "Prefs.h"
#include "Snap.h"
#include "Tags.h"
#include "TimeTrack.h"
#include "Track.h"
#include "TrackPanel.h"
#include "WaveTrack.h"

View File

@ -51,6 +51,46 @@ const wxChar *GetSampleFormatStr(sampleFormat format);
AUDACITY_DLL_API samplePtr NewSamples(int count, sampleFormat format);
AUDACITY_DLL_API void DeleteSamples(samplePtr p);
// RAII version of above
class SampleBuffer {
public:
SampleBuffer()
: mCount(0), mPtr(0)
{}
SampleBuffer(int count, sampleFormat format)
: mCount(count), mPtr(NewSamples(mCount, format))
{}
~SampleBuffer()
{
Free();
}
// WARNING! May not preserve contents.
void Resize(int count, sampleFormat format)
{
if (mCount < count) {
Free();
mPtr = NewSamples(count, format);
mCount = count;
}
}
void Free()
{
DeleteSamples(mPtr);
mPtr = 0;
mCount = 0;
}
samplePtr ptr() const { return mPtr; }
private:
samplePtr mPtr;
int mCount;
};
//
// Copying, Converting and Clearing Samples
//

View File

@ -799,6 +799,12 @@ unsigned int Sequence::GetODFlags()
return ret;
}
sampleCount Sequence::GetBlockStart(sampleCount position) const
{
int b = FindBlock(position);
return mBlock->Item(b)->start;
}
sampleCount Sequence::GetBestBlockSize(sampleCount start) const
{
// This method returns a nice number of samples you should try to grab in

View File

@ -159,9 +159,10 @@ class Sequence: public XMLTagHandler {
float * outRMS) const;
//
// Getting block size information
// Getting block size and alignment information
//
sampleCount GetBlockStart(sampleCount position) const;
sampleCount GetBestBlockSize(sampleCount start) const;
sampleCount GetMaxBlockSize() const;
sampleCount GetIdealBlockSize() const;

View File

@ -18,7 +18,7 @@
#include "Spectrum.h"
#include "FFT.h"
bool ComputeSpectrum(float * data, int width,
bool ComputeSpectrum(const float * data, int width,
int windowSize,
double WXUNUSED(rate), float *output,
bool autocorrelation, int windowFunc)

View File

@ -22,7 +22,7 @@
calculates windowSize/2 frequency samples
*/
bool ComputeSpectrum(float * data, int width, int windowSize,
bool ComputeSpectrum(const float * data, int width, int windowSize,
double rate, float *out, bool autocorrelation,
int windowFunc = eWinFuncHanning);

View File

@ -43,11 +43,11 @@ class TimeTrack: public Track {
// TimeTrack parameters
virtual double GetOffset() { return 0.0; };
virtual void SetOffset(double /* t */) {};
virtual double GetOffset() const { return 0.0; }
virtual void SetOffset(double /* t */) {}
virtual double GetStartTime() { return 0.0; };
virtual double GetEndTime() { return 0.0; };
virtual double GetStartTime() const { return 0.0; }
virtual double GetEndTime() const { return 0.0; }
void Draw(wxDC & dc, const wxRect & r, double h, double pps);

View File

@ -165,7 +165,7 @@ class AUDACITY_DLL_API Track: public XMLTagHandler
void SetSolo (bool s) { mSolo = s; }
int GetChannel() const { return mChannel; }
virtual double GetOffset () { return mOffset; }
virtual double GetOffset() const = 0;
void Offset(double t) { SetOffset(GetOffset() + t); }
virtual void SetOffset (double o) { mOffset = o; }
@ -202,8 +202,8 @@ class AUDACITY_DLL_API Track: public XMLTagHandler
// open the track from XML
virtual bool GetErrorOpening() { return false; }
virtual double GetStartTime() { return 0.0; }
virtual double GetEndTime() { return 0.0; }
virtual double GetStartTime() const = 0;
virtual double GetEndTime() const = 0;
// Checks if sync-lock is on and any track in its sync-lock group is selected.
bool IsSyncLockSelected();
@ -220,7 +220,7 @@ class AUDACITY_DLL_API TrackListIterator
{
public:
TrackListIterator(TrackList * val = NULL);
virtual ~TrackListIterator() {};
virtual ~TrackListIterator() {}
// Iterate functions
virtual Track *First(TrackList * val = NULL);
@ -243,8 +243,8 @@ class AUDACITY_DLL_API TrackListCondIterator: public TrackListIterator
{
public:
TrackListCondIterator(TrackList *val = NULL)
: TrackListIterator(val) {};
virtual ~TrackListCondIterator() {};
: TrackListIterator(val) {}
virtual ~TrackListCondIterator() {}
// Iteration functions
Track *First(TrackList *val = NULL);
@ -266,7 +266,7 @@ class AUDACITY_DLL_API TrackListOfKindIterator: public TrackListCondIterator
{
public:
TrackListOfKindIterator(int kind, TrackList * val = NULL);
virtual ~TrackListOfKindIterator() {};
virtual ~TrackListOfKindIterator() {}
protected:
virtual bool Condition(Track *t);
@ -283,8 +283,8 @@ class AUDACITY_DLL_API TrackListOfKindIterator: public TrackListCondIterator
class AUDACITY_DLL_API SelectedTrackListOfKindIterator: public TrackListOfKindIterator
{
public:
SelectedTrackListOfKindIterator(int kind, TrackList * val = NULL) : TrackListOfKindIterator(kind, val) {};
virtual ~SelectedTrackListOfKindIterator() {};
SelectedTrackListOfKindIterator(int kind, TrackList * val = NULL) : TrackListOfKindIterator(kind, val) {}
virtual ~SelectedTrackListOfKindIterator() {}
protected:
bool Condition(Track *t);
@ -299,7 +299,7 @@ class AUDACITY_DLL_API VisibleTrackIterator: public TrackListCondIterator
{
public:
VisibleTrackIterator(AudacityProject *project);
virtual ~VisibleTrackIterator() {};
virtual ~VisibleTrackIterator() {}
protected:
bool Condition(Track *t);
@ -316,7 +316,7 @@ class AUDACITY_DLL_API SyncLockedTracksIterator : public TrackListIterator
{
public:
SyncLockedTracksIterator(TrackList * val);
virtual ~SyncLockedTracksIterator() {};
virtual ~SyncLockedTracksIterator() {}
// Iterate functions
Track *First(Track *member);

View File

@ -160,7 +160,7 @@ WaveTrack::~WaveTrack()
}
double WaveTrack::GetOffset()
double WaveTrack::GetOffset() const
{
return GetStartTime();
}
@ -311,7 +311,7 @@ void WaveTrack::VirtualStereoInit()
}
#endif
float WaveTrack::GetChannelGain(int channel)
float WaveTrack::GetChannelGain(int channel) const
{
float left = 1.0;
float right = 1.0;
@ -1361,18 +1361,34 @@ unsigned int WaveTrack::GetODFlags()
}
sampleCount WaveTrack::GetBestBlockSize(sampleCount s)
sampleCount WaveTrack::GetBlockStart(sampleCount s) const
{
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
{
WaveClip* clip = it->GetData();
const sampleCount startSample = (sampleCount)floor(0.5 + clip->GetStartTime()*mRate);
const sampleCount endSample = startSample + clip->GetNumSamples();
if (s >= startSample && s < endSample)
return startSample + clip->GetSequence()->GetBlockStart(s - startSample);
}
return -1;
}
sampleCount WaveTrack::GetBestBlockSize(sampleCount s) const
{
sampleCount bestBlockSize = GetMaxBlockSize();
for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext())
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
{
WaveClip* clip = it->GetData();
sampleCount startSample = (sampleCount)floor(clip->GetStartTime()*mRate + 0.5);
sampleCount endSample = startSample + clip->GetNumSamples();
if (s >= startSample && s < endSample)
{
bestBlockSize = clip->GetSequence()->GetMaxBlockSize();
bestBlockSize = clip->GetSequence()->GetBestBlockSize(s - startSample);
break;
}
}
@ -1380,10 +1396,11 @@ sampleCount WaveTrack::GetBestBlockSize(sampleCount s)
return bestBlockSize;
}
sampleCount WaveTrack::GetMaxBlockSize()
sampleCount WaveTrack::GetMaxBlockSize() const
{
int maxblocksize = 0;
for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext())
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
{
WaveClip* clip = it->GetData();
if (clip->GetSequence()->GetMaxBlockSize() > maxblocksize)
@ -1619,7 +1636,7 @@ double WaveTrack::LongSamplesToTime(sampleCount pos)
return ((double)pos) / mRate;
}
double WaveTrack::GetStartTime()
double WaveTrack::GetStartTime() const
{
bool found = false;
double best = 0.0;
@ -1627,7 +1644,8 @@ double WaveTrack::GetStartTime()
if (mClips.IsEmpty())
return 0;
for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext())
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
if (!found)
{
found = true;
@ -1638,7 +1656,7 @@ double WaveTrack::GetStartTime()
return best;
}
double WaveTrack::GetEndTime()
double WaveTrack::GetEndTime() const
{
bool found = false;
double best = 0.0;
@ -1646,7 +1664,8 @@ double WaveTrack::GetEndTime()
if (mClips.IsEmpty())
return 0;
for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext())
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
if (!found)
{
found = true;
@ -1747,7 +1766,7 @@ bool WaveTrack::GetRMS(float *rms, double t0, double t1)
}
bool WaveTrack::Get(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len, fillFormat fill )
sampleCount start, sampleCount len, fillFormat fill ) const
{
// Simple optimization: When this buffer is completely contained within one clip,
// don't clear anything (because we won't have to). Otherwise, just clear
@ -1755,9 +1774,9 @@ bool WaveTrack::Get(samplePtr buffer, sampleFormat format,
WaveClipList::compatibility_iterator it;
bool doClear = true;
for (it=GetClipIterator(); it; it=it->GetNext())
for (it = const_cast<WaveTrack&>(*this).GetClipIterator(); it; it = it->GetNext())
{
WaveClip *clip = it->GetData();
const WaveClip *const clip = it->GetData();
if (start >= clip->GetStartSample() && start+len <= clip->GetEndSample())
{
doClear = false;
@ -1783,9 +1802,9 @@ bool WaveTrack::Get(samplePtr buffer, sampleFormat format,
}
}
for (it=GetClipIterator(); it; it=it->GetNext())
for (it = const_cast<WaveTrack&>(*this).GetClipIterator(); it; it = it->GetNext())
{
WaveClip *clip = it->GetData();
const WaveClip *const clip = it->GetData();
sampleCount clipStart = clip->GetStartSample();
sampleCount clipEnd = clip->GetEndSample();
@ -1858,7 +1877,7 @@ bool WaveTrack::Set(samplePtr buffer, sampleFormat format,
}
void WaveTrack::GetEnvelopeValues(double *buffer, int bufferLen,
double t0, double tstep)
double t0, double tstep) const
{
// Possibly nothing to do.
if( bufferLen <= 0 )
@ -1881,9 +1900,10 @@ void WaveTrack::GetEnvelopeValues(double *buffer, int bufferLen,
double startTime = t0;
double endTime = t0+tstep*bufferLen;
for (WaveClipList::compatibility_iterator it=GetClipIterator(); it; it=it->GetNext())
for (WaveClipList::compatibility_iterator it = const_cast<WaveTrack&>(*this).GetClipIterator();
it; it = it->GetNext())
{
WaveClip *clip = it->GetData();
WaveClip *const clip = it->GetData();
// IF clip intersects startTime..endTime THEN...
double dClipStartTime = clip->GetStartTime();
@ -2393,3 +2413,180 @@ void WaveTrack::SetAutoSaveIdent(int ident)
{
mAutoSaveIdent = ident;
}
WaveTrackCache::~WaveTrackCache()
{
Free();
}
void WaveTrackCache::SetTrack(const WaveTrack *pTrack)
{
if (mPTrack != pTrack) {
if (pTrack) {
mBufferSize = pTrack->GetMaxBlockSize();
if (!mPTrack ||
mPTrack->GetMaxBlockSize() != mBufferSize) {
Free();
mBuffers[0].data = new float[mBufferSize];
mBuffers[1].data = new float[mBufferSize];
}
}
else
Free();
mPTrack = pTrack;
mNValidBuffers = 0;
}
}
constSamplePtr WaveTrackCache::Get(sampleFormat format,
sampleCount start, sampleCount len)
{
if (format == floatSample && len > 0) {
const sampleCount end = start + len;
bool fillFirst = (mNValidBuffers < 1);
bool fillSecond = (mNValidBuffers < 2);
// Discard cached results that we no longer need
if (mNValidBuffers > 0 &&
(end <= mBuffers[0].start ||
start >= mBuffers[mNValidBuffers - 1].end())) {
// Complete miss
fillFirst = true;
fillSecond = true;
}
else if (mNValidBuffers == 2 &&
start >= mBuffers[1].start &&
end > mBuffers[1].end()) {
// Request starts in the second buffer and extends past it.
// Discard the first buffer.
// (But don't deallocate the buffer space.)
float *save = mBuffers[0].data;
mBuffers[0] = mBuffers[1];
mBuffers[1].data = save;
fillSecond = true;
mNValidBuffers = 1;
}
else if (mNValidBuffers > 0 &&
start < mBuffers[0].start &&
0 <= mPTrack->GetBlockStart(start)) {
// Request is not a total miss but starts before the cache,
// and there is a clip to fetch from.
// Not the access pattern for drawing spectrogram or playback,
// but maybe scrubbing causes this.
// Move the first buffer into second place, and later
// refill the first.
// (This case might be useful when marching backwards through
// the track, as with scrubbing.)
float *save = mBuffers[1].data;
mBuffers[1] = mBuffers[0];
mBuffers[0].data = save;
fillFirst = true;
// Cache is not in a consistent state yet
mNValidBuffers = 0;
}
// Refill buffers as needed
if (fillFirst) {
const sampleCount start0 = mPTrack->GetBlockStart(start);
if (start0 >= 0) {
const sampleCount len0 = mPTrack->GetBestBlockSize(start0);
wxASSERT(len0 <= mBufferSize);
if (!mPTrack->Get(samplePtr(mBuffers[0].data), floatSample, start0, len0))
return false;
mBuffers[0].start = start0;
mBuffers[0].len = len0;
if (!fillSecond &&
mBuffers[0].end() != mBuffers[1].start)
fillSecond = true;
// Keep the partially updated state consistent:
mNValidBuffers = fillSecond ? 1 : 2;
}
else {
// Request may fall between the clips of a track.
// Invalidate all. WaveTrack::Get() will return zeroes.
mNValidBuffers = 0;
fillSecond = false;
}
}
wxASSERT(!fillSecond || mNValidBuffers > 0);
if (fillSecond) {
mNValidBuffers = 1;
const sampleCount end0 = mBuffers[0].end();
if (end > end0) {
const sampleCount start1 = mPTrack->GetBlockStart(end0);
if (start1 == end0) {
const sampleCount len1 = mPTrack->GetBestBlockSize(start1);
wxASSERT(len1 <= mBufferSize);
if (!mPTrack->Get(samplePtr(mBuffers[1].data), floatSample, start1, len1))
return false;
mBuffers[1].start = start1;
mBuffers[1].len = len1;
mNValidBuffers = 2;
}
}
}
wxASSERT(mNValidBuffers < 2 || mBuffers[0].end() == mBuffers[1].start);
samplePtr buffer = 0;
sampleCount remaining = len;
// Possibly get an initial portion that is uncached
const sampleCount initLen =
mNValidBuffers < 1 ? len : std::min(len, mBuffers[0].start - start);
if (initLen > 0) {
// This might be fetching zeroes between clips
mOverlapBuffer.Resize(len, format);
if (!mPTrack->Get(mOverlapBuffer.ptr(), format, start, initLen))
return 0;
remaining -= initLen;
start += initLen;
buffer = mOverlapBuffer.ptr() + initLen * SAMPLE_SIZE(format);
}
// Now satisfy the request from the buffers
for (int ii = 0; ii < mNValidBuffers && remaining > 0; ++ii) {
const sampleCount starti = start - mBuffers[ii].start;
const sampleCount leni = std::min(remaining, mBuffers[ii].len - starti);
if (leni == len) {
// All is contiguous already. We can completely avoid copying
return samplePtr(mBuffers[ii].data + starti);
}
else if (leni > 0) {
if (buffer == 0) {
mOverlapBuffer.Resize(len, format);
buffer = mOverlapBuffer.ptr();
}
const size_t size = sizeof(float) * leni;
memcpy(buffer, mBuffers[ii].data + starti, size);
remaining -= leni;
start += leni;
buffer += size;
}
}
if (remaining > 0) {
// Very big request!
// Fall back to direct fetch
if (!mPTrack->Get(buffer, format, start, remaining))
return false;
}
return mOverlapBuffer.ptr();
}
// Cache works only for float format.
mOverlapBuffer.Resize(len, format);
if (mPTrack->Get(mOverlapBuffer.ptr(), format, start, len))
return mOverlapBuffer.ptr();
else
return 0;
}
void WaveTrackCache::Free()
{
mBuffers[0].Free();
mBuffers[1].Free();
mOverlapBuffer.Free();
mNValidBuffers = 0;
}

View File

@ -95,21 +95,21 @@ class AUDACITY_DLL_API WaveTrack: public Track {
};
virtual ~WaveTrack();
virtual double GetOffset();
virtual double GetOffset() const;
virtual void SetOffset (double o);
/** @brief Get the time at which the first clip in the track starts
*
* @return time in seconds, or zero if there are no clips in the track
*/
double GetStartTime();
double GetStartTime() const;
/** @brief Get the time at which the last clip in the track ends, plus
* recorded stuff
*
* @return time in seconds, or zero if there are no clips in the track.
*/
double GetEndTime();
double GetEndTime() const;
//
// Identifying the type of track
@ -138,7 +138,7 @@ class AUDACITY_DLL_API WaveTrack: public Track {
void SetPan(float newPan);
#endif
// Takes gain and pan into account
float GetChannelGain(int channel);
float GetChannelGain(int channel) const;
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
void SetVirtualState(bool state, bool half=false);
#endif
@ -231,11 +231,11 @@ class AUDACITY_DLL_API WaveTrack: public Track {
/// guaranteed that the same samples are affected.
///
bool Get(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len, fillFormat fill=fillZero);
sampleCount start, sampleCount len, fillFormat fill=fillZero) const;
bool Set(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len);
void GetEnvelopeValues(double *buffer, int bufferLen,
double t0, double tstep);
double t0, double tstep) const;
bool GetMinMax(float *min, float *max,
double t0, double t1);
bool GetRMS(float *rms, double t0, double t1);
@ -255,11 +255,12 @@ class AUDACITY_DLL_API WaveTrack: public Track {
//
// Getting information about the track's internal block sizes
// for efficiency
// and alignment for efficiency
//
sampleCount GetBestBlockSize(sampleCount t);
sampleCount GetMaxBlockSize();
sampleCount GetBlockStart(sampleCount t) const;
sampleCount GetBestBlockSize(sampleCount t) const;
sampleCount GetMaxBlockSize() const;
sampleCount GetIdealBlockSize();
//
@ -465,4 +466,49 @@ class AUDACITY_DLL_API WaveTrack: public Track {
int mAutoSaveIdent;
};
// This is meant to be a short-lived object, during whose lifetime,
// the contents of the WaveTrack are known not to change. It can replace
// repeated calls to WaveTrack::Get() (each of which opens and closes at least
// one block file).
class WaveTrackCache {
public:
explicit WaveTrackCache(const WaveTrack *pTrack = 0)
: mPTrack(0)
, mBufferSize(0)
, mOverlapBuffer()
, mNValidBuffers(0)
{
SetTrack(pTrack);
}
~WaveTrackCache();
const WaveTrack *GetTrack() const { return mPTrack; }
void SetTrack(const WaveTrack *pTrack);
// Uses fillZero always
// Returns null on failure
// Returned pointer may be invalidated if Get is called again
// Do not delete[] the pointer
constSamplePtr Get(sampleFormat format, sampleCount start, sampleCount len);
private:
void Free();
struct Buffer {
float *data;
sampleCount start;
sampleCount len;
Buffer() : data(0), start(0), len(0) {}
void Free() { delete[] data; data = 0; start = 0; len = 0; }
sampleCount end() const { return start + len; }
};
const WaveTrack *mPTrack;
sampleCount mBufferSize;
Buffer mBuffers[2];
SampleBuffer mOverlapBuffer;
int mNValidBuffers;
};
#endif // __AUDACITY_WAVETRACK__