From f634e44e3a88ce81b096f28f41f8a5adb2440c6b Mon Sep 17 00:00:00 2001 From: Cory Cook Date: Tue, 26 May 2015 12:05:31 -0700 Subject: [PATCH 1/4] Removed redundant operation. --- src/BatchProcessDialog.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/BatchProcessDialog.cpp b/src/BatchProcessDialog.cpp index 5a743d354..4b7beb32d 100644 --- a/src/BatchProcessDialog.cpp +++ b/src/BatchProcessDialog.cpp @@ -326,7 +326,6 @@ void BatchProcessDialog::OnApplyToFiles(wxCommandEvent & WXUNUSED(event)) mList->SetItemImage(i, 1, 1); mList->EnsureVisible(i); - project->OnRemoveTracks(); project->Import(files[i]); project->OnSelectAll(); if (!mBatchCommands.ApplyChain()) { @@ -338,8 +337,8 @@ void BatchProcessDialog::OnApplyToFiles(wxCommandEvent & WXUNUSED(event)) } UndoManager *um = project->GetUndoManager(); um->ClearStates(); - project->OnSelectAll(); - project->OnRemoveTracks(); + project->OnSelectAll(); + project->OnRemoveTracks(); } project->OnRemoveTracks(); } From 16e0fe3e1289411d4de987b017bdbb30674ad3f1 Mon Sep 17 00:00:00 2001 From: Leland Lucius Date: Tue, 26 May 2015 18:16:34 -0500 Subject: [PATCH 2/4] The real fix for bug #965 --- src/prefs/ModulePrefs.cpp | 10 ++++++---- src/prefs/ModulePrefs.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/prefs/ModulePrefs.cpp mode change 100644 => 100755 src/prefs/ModulePrefs.h diff --git a/src/prefs/ModulePrefs.cpp b/src/prefs/ModulePrefs.cpp old mode 100644 new mode 100755 index d600fd8d9..a9a2d9c30 --- a/src/prefs/ModulePrefs.cpp +++ b/src/prefs/ModulePrefs.cpp @@ -53,6 +53,7 @@ void ModulePrefs::GetAllModuleStatuses(){ // The old modules might be still around, and we do not want to use them. mModules.Clear(); mStatuses.Clear(); + mPaths.Clear(); // Iterate through all Modules listed in prefs. @@ -63,7 +64,7 @@ void ModulePrefs::GetAllModuleStatuses(){ int iStatus; gPrefs->Read( str, &iStatus, kModuleDisabled ); wxString fname; - gPrefs->Read( wxString( wxT("/Module/path:") ) + str, &fname, wxEmptyString ); + gPrefs->Read( wxString( wxT("/ModulePath/") ) + str, &fname, wxEmptyString ); if( fname != wxEmptyString && wxFileExists( fname ) ){ if( iStatus > kModuleNew ){ iStatus = kModuleNew; @@ -72,6 +73,7 @@ void ModulePrefs::GetAllModuleStatuses(){ //wxLogDebug( wxT("Entry: %s Value: %i"), str.c_str(), iStatus ); mModules.Add( str ); mStatuses.Add( iStatus ); + mPaths.Add( fname ); } bCont = gPrefs->GetNextEntry(str, dummy); } @@ -129,8 +131,8 @@ bool ModulePrefs::Apply() ShuttleGui S(this, eIsSavingToPrefs); PopulateOrExchange(S); int i; - for(i=0;i<(int)mModules.GetCount();i++) - SetModuleStatus( mModules[i], mStatuses[i] ); + for(i=0;i<(int)mPaths.GetCount();i++) + SetModuleStatus( mPaths[i], mStatuses[i] ); return true; } @@ -154,7 +156,7 @@ void ModulePrefs::SetModuleStatus( wxString fname, int iStatus ){ wxString ShortName = wxFileName( fname ).GetName(); wxString PrefName = wxString( wxT("/Module/") ) + ShortName.Lower(); gPrefs->Write( PrefName, iStatus ); - PrefName = wxString( wxT("/Module/path:") ) + ShortName.Lower(); + PrefName = wxString( wxT("/ModulePath/") ) + ShortName.Lower(); gPrefs->Write( PrefName, fname ); gPrefs->Flush(); } diff --git a/src/prefs/ModulePrefs.h b/src/prefs/ModulePrefs.h old mode 100644 new mode 100755 index 33d83d2ab..107a87fe5 --- a/src/prefs/ModulePrefs.h +++ b/src/prefs/ModulePrefs.h @@ -47,6 +47,7 @@ class ModulePrefs:public PrefsPanel void PopulateOrExchange(ShuttleGui & S); wxArrayString mModules; wxArrayInt mStatuses; + wxArrayString mPaths; }; #endif From bdc28391124863097ad095bd0dafd7b855667793 Mon Sep 17 00:00:00 2001 From: Paul-Licameli Date: Sat, 28 Mar 2015 14:46:40 -0400 Subject: [PATCH 3/4] 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(). --- include/audacity/Types.h | 1 + src/AudioIO.cpp | 1 + src/LabelTrack.cpp | 11 +- src/LabelTrack.h | 5 +- src/Menus.cpp | 1 + src/NoteTrack.cpp | 16 ++- src/NoteTrack.h | 5 +- src/Project.cpp | 1 + src/SampleFormat.h | 40 +++++++ src/Sequence.cpp | 6 + src/Sequence.h | 3 +- src/Spectrum.cpp | 2 +- src/Spectrum.h | 2 +- src/TimeTrack.h | 8 +- src/Track.h | 22 ++-- src/WaveTrack.cpp | 235 +++++++++++++++++++++++++++++++++++---- src/WaveTrack.h | 66 +++++++++-- 17 files changed, 365 insertions(+), 60 deletions(-) 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__ From e6ccd51326b046304ea35e1a54452a4a0e9ba9e1 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 26 May 2015 18:11:36 -0400 Subject: [PATCH 4/4] Use WaveTrackCache for spectrograms Zooming-in of spectrograms (or other view changes that invalidate the whole pixel cache of the WaveClip) used to do at least one opening and closing of a block file for each column of pixels. With this change, open each block file not more than once for each repopulation of the cache. Improved speed may be more noticeable on less powerful computers, or when the audio file is in a slower storage device. --- src/TrackArtist.cpp | 11 +++++--- src/TrackArtist.h | 3 ++- src/WaveClip.cpp | 64 +++++++++++++++++++++++++++++---------------- src/WaveClip.h | 4 ++- 4 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 9ffc5b409..2ab608d52 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -143,8 +143,8 @@ audio tracks. *//*******************************************************************/ #include "Audacity.h" -#include "AudacityApp.h" #include "TrackArtist.h" +#include "AudacityApp.h" #include "float_cast.h" #include @@ -1749,8 +1749,9 @@ void TrackArtist::DrawSpectrum(WaveTrack *track, return; } + WaveTrackCache cache(track); for (WaveClipList::compatibility_iterator it = track->GetClipIterator(); it; it = it->GetNext()) { - DrawClipSpectrum(track, it->GetData(), dc, r, viewInfo, autocorrelation, logF); + DrawClipSpectrum(cache, it->GetData(), dc, r, viewInfo, autocorrelation, logF); } } @@ -1822,7 +1823,7 @@ AColor::ColorGradientChoice ChooseColorSet( float bin0, float bin1, float selBin -void TrackArtist::DrawClipSpectrum(WaveTrack *track, +void TrackArtist::DrawClipSpectrum(WaveTrackCache &cache, WaveClip *clip, wxDC & dc, const wxRect & r, @@ -1830,6 +1831,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track, bool autocorrelation, bool logF) { + const WaveTrack *const track = cache.GetTrack(); + enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 }; enum { DASH_LENGTH = 10 /* pixels */ }; @@ -1962,7 +1965,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track, float *freq = new float[mid.width * half]; sampleCount *where = new sampleCount[mid.width+1]; - bool updated = clip->GetSpectrogram(freq, where, mid.width, + bool updated = clip->GetSpectrogram(cache, freq, where, mid.width, t0, pps, autocorrelation); int ifreq = lrint(rate/2); diff --git a/src/TrackArtist.h b/src/TrackArtist.h index 75800d706..fcee3033e 100644 --- a/src/TrackArtist.h +++ b/src/TrackArtist.h @@ -29,6 +29,7 @@ class wxHashTable; class Track; class WaveTrack; +class WaveTrackCache; class WaveClip; class NoteTrack; class LabelTrack; @@ -141,7 +142,7 @@ class AUDACITY_DLL_API TrackArtist { bool drawEnvelope, bool drawSamples, bool drawSliders, bool dB, bool muted); - void DrawClipSpectrum(WaveTrack *track, WaveClip *clip, + void DrawClipSpectrum(WaveTrackCache &cache, WaveClip *clip, wxDC & dc, const wxRect & r, const ViewInfo *viewInfo, bool autocorrelation, bool logF); diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 45c8a833f..61a6373a3 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -25,6 +25,8 @@ drawing). Cache's the Spectrogram frequency samples. *//*******************************************************************/ +#include "WaveClip.h" + #include #include #include @@ -32,7 +34,6 @@ drawing). Cache's the Spectrogram frequency samples. #include "Spectrum.h" #include "Prefs.h" -#include "WaveClip.h" #include "Envelope.h" #include "Resample.h" #include "Project.h" @@ -753,10 +754,11 @@ bool WaveClip::GetWaveDisplay(float *min, float *max, float *rms,int* bl, return true; } -bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, - int numPixels, - double t0, double pixelsPerSecond, - bool autocorrelation) +bool WaveClip::GetSpectrogram(WaveTrackCache &waveTrackCache, + float *freq, sampleCount *where, + int numPixels, + double t0, double pixelsPerSecond, + bool autocorrelation) { int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L); int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L); @@ -912,6 +914,7 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, } } + float *useBuffer; #ifdef EXPERIMENTAL_FFT_SKIP_POINTS float *buffer = new float[windowSize*fftSkipPoints1]; mSpecCache->fftSkipPointsOld = fftSkipPoints; @@ -952,6 +955,7 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, } else { + bool copy = !autocorrelation; float *adj = buffer; start -= windowSize >> 1; @@ -960,42 +964,58 @@ bool WaveClip::GetSpectrogram(float *freq, sampleCount *where, *adj++ = 0; len += start; start = 0; + copy = true; } #ifdef EXPERIMENTAL_FFT_SKIP_POINTS + copy = true; if (start + len*fftSkipPoints1 > mSequence->GetNumSamples()) { int newlen = (mSequence->GetNumSamples() - start)/fftSkipPoints1; for (i = newlen*fftSkipPoints1; i < (sampleCount)len*fftSkipPoints1; i++) + adj[i] = 0; + len = newlen; + } #else //!EXPERIMENTAL_FFT_SKIP_POINTS if (start + len > mSequence->GetNumSamples()) { int newlen = mSequence->GetNumSamples() - start; for (i = newlen; i < (sampleCount)len; i++) -#endif //EXPERIMENTAL_FFT_SKIP_POINTS adj[i] = 0; len = newlen; + copy = true; + } +#endif //EXPERIMENTAL_FFT_SKIP_POINTS + + if (len > 0) { +#ifdef EXPERIMENTAL_FFT_SKIP_POINTS + useBuffer = (float*)(waveTrackCache.Get(floatSample, + floor(0.5 + start + mOffset * mRate), + len * fftSkipPoints1)); + memmove(adj, useBuffer, len * fftSkipPoints1 * sizeof(float)); + if (fftSkipPoints) { + // TODO: (maybe) alternatively change Get to include skipping of points + int j=0; + for (int i=0; i < len; i++) { + adj[i]=adj[j]; + j+=fftSkipPoints1; + } + } +#else //!EXPERIMENTAL_FFT_SKIP_POINTS + useBuffer = (float*)(waveTrackCache.Get(floatSample, + floor(0.5 + start + mOffset * mRate), len)); + if (copy) + memmove(adj, useBuffer, len * sizeof(float)); +#endif //EXPERIMENTAL_FFT_SKIP_POINTS } - if (len > 0) -#ifdef EXPERIMENTAL_FFT_SKIP_POINTS - mSequence->Get((samplePtr)adj, floatSample, start, len*fftSkipPoints1); - if (fftSkipPoints) { - // TODO: (maybe) alternatively change Get to include skipping of points - int j=0; - for (int i=0; i < len; i++) { - adj[i]=adj[j]; - j+=fftSkipPoints1; - } - } -#else //!EXPERIMENTAL_FFT_SKIP_POINTS - mSequence->Get((samplePtr)adj, floatSample, start, len); -#endif //EXPERIMENTAL_FFT_SKIP_POINTS + if (copy) + useBuffer = buffer; #ifdef EXPERIMENTAL_USE_REALFFTF if(autocorrelation) { - ComputeSpectrum(buffer, windowSize, windowSize, + ComputeSpectrum(useBuffer, windowSize, windowSize, mRate, &mSpecCache->freq[half * x], autocorrelation, windowType); } else { - ComputeSpectrumUsingRealFFTf(buffer, hFFT, mWindow, mWindowSize, &mSpecCache->freq[half * x]); + ComputeSpectrumUsingRealFFTf(useBuffer, hFFT, mWindow, mWindowSize, &mSpecCache->freq[half * x]); } #else // EXPERIMENTAL_USE_REALFFTF ComputeSpectrum(buffer, windowSize, windowSize, diff --git a/src/WaveClip.h b/src/WaveClip.h index 6a296291e..f303a32f9 100644 --- a/src/WaveClip.h +++ b/src/WaveClip.h @@ -31,6 +31,7 @@ class Envelope; class WaveCache; +class WaveTrackCache; class SpecCache; class SpecPxCache { @@ -140,7 +141,8 @@ public: * calculations and Contrast */ bool GetWaveDisplay(float *min, float *max, float *rms,int* bl, sampleCount *where, int numPixels, double t0, double pixelsPerSecond, bool &isLoadingOD); - bool GetSpectrogram(float *buffer, sampleCount *where, + bool GetSpectrogram(WaveTrackCache &cache, + float *buffer, sampleCount *where, int numPixels, double t0, double pixelsPerSecond, bool autocorrelation);