diff --git a/include/audacity/Types.h b/include/audacity/Types.h index 32f82b120..bf8b6651f 100644 --- a/include/audacity/Types.h +++ b/include/audacity/Types.h @@ -79,6 +79,7 @@ typedef enum // Generic pointer to sample data // ---------------------------------------------------------------------------- typedef char *samplePtr; +typedef const char *constSamplePtr; // ---------------------------------------------------------------------------- // The type for plugin IDs diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 5f62cb6af..9b52f186c 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -301,6 +301,7 @@ writing audio. #include "RingBuffer.h" #include "Prefs.h" #include "Project.h" +#include "TimeTrack.h" #include "WaveTrack.h" #include "AutoRecovery.h" diff --git a/src/LabelTrack.cpp b/src/LabelTrack.cpp index d19158126..b688f86db 100644 --- a/src/LabelTrack.cpp +++ b/src/LabelTrack.cpp @@ -29,6 +29,7 @@ for drawing different aspects of the label and its text box. *//*******************************************************************/ #include "Audacity.h" +#include "LabelTrack.h" #include @@ -48,7 +49,6 @@ for drawing different aspects of the label and its text box. #include #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). diff --git a/src/LabelTrack.h b/src/LabelTrack.h index c45112a1c..6129b06cc 100644 --- a/src/LabelTrack.h +++ b/src/LabelTrack.h @@ -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); } diff --git a/src/Menus.cpp b/src/Menus.cpp index 6643dbf2b..d8b755636 100755 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -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" diff --git a/src/NoteTrack.cpp b/src/NoteTrack.cpp index d0c5d2e1d..6af6cf698 100644 --- a/src/NoteTrack.cpp +++ b/src/NoteTrack.cpp @@ -14,20 +14,20 @@ *//*******************************************************************/ +#include "Audacity.h" +#include "NoteTrack.h" + #include #include #include #include -#include "Audacity.h" - #if defined(USE_MIDI) #include #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); } diff --git a/src/NoteTrack.h b/src/NoteTrack.h index 0ea4337ab..90f9b7e77 100644 --- a/src/NoteTrack.h +++ b/src/NoteTrack.h @@ -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); diff --git a/src/Project.cpp b/src/Project.cpp index ef66ccce5..3ac1d05db 100755 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -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" diff --git a/src/SampleFormat.h b/src/SampleFormat.h index 6d0dd9902..17823c305 100644 --- a/src/SampleFormat.h +++ b/src/SampleFormat.h @@ -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 // diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 9cae9db43..ec955f1d7 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -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 diff --git a/src/Sequence.h b/src/Sequence.h index 642814dff..d577704fb 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -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; diff --git a/src/Spectrum.cpp b/src/Spectrum.cpp index 572eebc11..b80966a66 100644 --- a/src/Spectrum.cpp +++ b/src/Spectrum.cpp @@ -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) diff --git a/src/Spectrum.h b/src/Spectrum.h index 3d23320a1..64e0e28cb 100644 --- a/src/Spectrum.h +++ b/src/Spectrum.h @@ -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); diff --git a/src/TimeTrack.h b/src/TimeTrack.h index 6b4f14a37..4525c99df 100644 --- a/src/TimeTrack.h +++ b/src/TimeTrack.h @@ -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); diff --git a/src/Track.h b/src/Track.h index d1f05cb9e..6a6446837 100644 --- a/src/Track.h +++ b/src/Track.h @@ -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); diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 436485b30..5b0771cac 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -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(*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(*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(*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(*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(*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(*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(*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(*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; +} diff --git a/src/WaveTrack.h b/src/WaveTrack.h index b256a9277..019f9379c 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -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__