diff --git a/.travis.yml b/.travis.yml index ebaa75cbf..d9cf1937b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,17 @@ +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.8 + - gcc-4.8 before_install: - sudo apt-get update -qq - sudo apt-get install -y libwxgtk3.0-dev libgtk2.0-dev - git show -s --format="wxT(\"%h of %cd\")" - git show -s --format="wxT(\"%h of %cd\")" > ./src/RevisionIdent.h + - export CXX="g++-4.8" CC="gcc-4.8" language: - cpp -compiler: - - gcc -script: +script: - aclocal --install -I m4 && automake && ./configure && make - diff --git a/lib-src/FileDialog/Makefile.in b/lib-src/FileDialog/Makefile.in index 52ce4c53e..35dd3c07d 100644 --- a/lib-src/FileDialog/Makefile.in +++ b/lib-src/FileDialog/Makefile.in @@ -216,6 +216,10 @@ OBJCXXLINK = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ --mode=link $(OBJCXXLD) $(AM_OBJCXXFLAGS) $(OBJCXXFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_OBJCXXLD = $(am__v_OBJCXXLD_@AM_V@) +# Overwrite C++ variables with values for Objective-C, because in the end there +# is only one linker command that should be configured correctly. +@MAC_TRUE@CXXLINK = $(OBJCXXLINK) +@MAC_TRUE@AM_V_CXXLD = $(AM_V_OBJCXXLD) am__v_OBJCXXLD_ = $(am__v_OBJCXXLD_@AM_DEFAULT_V@) am__v_OBJCXXLD_0 = @echo " OBJCXXLD" $@; am__v_OBJCXXLD_1 = @@ -520,8 +524,8 @@ win/$(DEPDIR)/$(am__dirstamp): win/libFileDialog_la-FileDialogPrivate.lo: win/$(am__dirstamp) \ win/$(DEPDIR)/$(am__dirstamp) -libFileDialog.la: $(libFileDialog_la_OBJECTS) $(libFileDialog_la_DEPENDENCIES) $(EXTRA_libFileDialog_la_DEPENDENCIES) - $(AM_V_OBJCXXLD)$(OBJCXXLINK) -rpath $(libdir) $(libFileDialog_la_OBJECTS) $(libFileDialog_la_LIBADD) $(LIBS) +libFileDialog.la: $(libFileDialog_la_OBJECTS) $(libFileDialog_la_DEPENDENCIES) $(EXTRA_libFileDialog_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) -rpath $(libdir) $(libFileDialog_la_OBJECTS) $(libFileDialog_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) diff --git a/mac/Build.txt b/mac/Build.txt index 845ae5b1c..b546ba0e1 100644 --- a/mac/Build.txt +++ b/mac/Build.txt @@ -3,38 +3,40 @@ is 10.6. To build Audacity using Xcode 5.1 or newer, you need to extract the 10.6 SDK from an earlier version of Xcode. In the instructions below, Xcode 4.3.3 (for the 10.6 SDK) and -Xcode 5.1.1 will be used. Xcode 6.1.1, 6.3, 6.4.1, and 7.0 +Xcode 5.1.1 will be used. Xcode 6.1.1, 6.3, 6.4.1, and 7.1 have been verified to work as well. -1) Download Xcode 5.1.1 or greater and install it to /Applications -2) Download Xcode 4.3.3 and open it in Finder -3) Right click the "Xcode.app" bundle and select "Show Package Contents" -4) Navigate down the directories to get to: +1) Download Xcode 5.1.1 or greater and install it to /Applications. +2) Download Xcode 4.3.3 (it includes the 10.6 and 10.7 SDK's despite + the downloads page says it only has the 10.7 SDK). +3) Open the DMG in Finder. +4) Right-click the "Xcode.app" bundle and select "Show Package Contents". +5) Navigate down the directories to get to: - Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ + Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ . -5) Copy the MacOSX10.6.sdk folder to: +6) Copy the MacOSX10.6.sdk folder to: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ Xcode is now ready. -6) Download wxWidgets. We currently require version 3.0.2, which can be downloaded from: +7) Download wxWidgets. We currently require version 3.0.2, which can be downloaded from: http://sourceforge.net/projects/wxwindows/files/3.0.2/wxWidgets-3.0.2.tar.bz2 -7) Once the download is complete, untar it (your browser may have already done this): +8) Once the download is complete, untar it (your browser may have already done this): - tar xf wxWidgets-3.0.2.tar.bz2 + tar xf wxWidgets-3.0.2.tar.bz2 . -8) Apply the following fixes to wxWidgets: +9) Apply the following fixes to wxWidgets: cd wxWidgets-3.0.2 - patch -p0 -i /mac/wxMac_additions/wxMac-3.0.2-fixes.patch + patch -p0 -i /mac/wxMac_additions/wxMac-3.0.2-fixes.patch . -9) And finally build/install wxWidgets: +10) And finally build/install wxWidgets: - sudo /mac/build_wxwidgets + sudo /mac/build_wxwidgets . You should now be able to build Audacity using either the Xcode application or the xcodebuild command line tool. diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 798438c97..f5d613bd5 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1107,8 +1107,13 @@ void AudioIO::HandleDeviceChange() return; // get the selected record and playback devices - int playDeviceNum = getPlayDevIndex(); - int recDeviceNum = getRecordDevIndex(); + const int playDeviceNum = getPlayDevIndex(); + const int recDeviceNum = getRecordDevIndex(); + + // If no change needed, return + if (mCachedPlaybackIndex == playDeviceNum && + mCachedCaptureIndex == recDeviceNum) + return; // cache playback/capture rates mCachedPlaybackRates = GetSupportedPlaybackRates(playDeviceNum); diff --git a/src/AutoRecovery.cpp b/src/AutoRecovery.cpp index 8a68ecfaf..00e1f33f7 100644 --- a/src/AutoRecovery.cpp +++ b/src/AutoRecovery.cpp @@ -283,6 +283,28 @@ RecordingRecoveryHandler::RecordingRecoveryHandler(AudacityProject* proj) mNumChannels = -1; } +int RecordingRecoveryHandler::FindTrack() const +{ + WaveTrackArray tracks = mProject->GetTracks()->GetWaveTrackArray(false); + int index; + if (mAutoSaveIdent) + { + for (index = 0; index < (int)tracks.GetCount(); index++) + { + if (tracks[index]->GetAutoSaveIdent() == mAutoSaveIdent) + { + break; + } + } + } + else + { + index = tracks.GetCount() - mNumChannels + mChannel; + } + + return index; +} + bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { @@ -296,23 +318,9 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, return false; } - // We need to find the track and sequence where the blockfile belongs WaveTrackArray tracks = mProject->GetTracks()->GetWaveTrackArray(false); - int index; - if (mAutoSaveIdent) - { - for (index = 0; index < (int) tracks.GetCount(); index++) - { - if (tracks[index]->GetAutoSaveIdent() == mAutoSaveIdent) - { - break; - } - } - } - else - { - index = tracks.GetCount() - mNumChannels + mChannel; - } + int index = FindTrack(); + // We need to find the track and sequence where the blockfile belongs if (index < 0 || index >= (int) tracks.GetCount()) { @@ -326,10 +334,14 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, Sequence* seq = clip->GetSequence(); // Load the blockfile from the XML - BlockFile* blockFile = NULL; DirManager* dirManager = mProject->GetDirManager(); dirManager->SetLoadingFormat(seq->GetSampleFormat()); - dirManager->SetLoadingTarget(&blockFile); + + BlockArray array; + array.resize(1); + dirManager->SetLoadingTarget(&array, 0); + BlockFile *& blockFile = array[0].f; + if (!dirManager->HandleXMLTag(tag, attrs) || !blockFile) { // This should only happen if there is a bug @@ -387,6 +399,29 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, return true; } +void RecordingRecoveryHandler::HandleXMLEndTag(const wxChar *tag) +{ + if (wxStrcmp(tag, wxT("simpleblockfile")) == 0) + // Still in inner looop + return; + + WaveTrackArray tracks = mProject->GetTracks()->GetWaveTrackArray(false); + int index = FindTrack(); + // We need to find the track and sequence where the blockfile belongs + + if (index < 0 || index >= (int)tracks.GetCount()) { + // This should only happen if there is a bug + wxASSERT(false); + } + else { + WaveTrack* track = tracks.Item(index); + WaveClip* clip = track->NewestOrNewClip(); + Sequence* seq = clip->GetSequence(); + + seq->ConsistencyCheck(wxT("RecordingRecoveryHandler::HandleXMLEndTag")); + } +} + XMLTagHandler* RecordingRecoveryHandler::HandleXMLChild(const wxChar *tag) { if (wxStrcmp(tag, wxT("simpleblockfile")) == 0) diff --git a/src/AutoRecovery.h b/src/AutoRecovery.h index 8d5ee548b..66239401e 100644 --- a/src/AutoRecovery.h +++ b/src/AutoRecovery.h @@ -46,12 +46,16 @@ class RecordingRecoveryHandler: public XMLTagHandler public: RecordingRecoveryHandler(AudacityProject* proj); virtual bool HandleXMLTag(const wxChar *tag, const wxChar **attrs); + virtual void HandleXMLEndTag(const wxChar *tag); virtual XMLTagHandler *HandleXMLChild(const wxChar *tag); // This class only knows reading tags virtual void WriteXML(XMLWriter & WXUNUSED(xmlFile)) { wxASSERT(false); } private: + + int FindTrack() const; + AudacityProject* mProject; int mChannel; int mNumChannels; diff --git a/src/Dependencies.cpp b/src/Dependencies.cpp index 77cf4fce9..8e576dec6 100644 --- a/src/Dependencies.cpp +++ b/src/Dependencies.cpp @@ -60,7 +60,7 @@ WX_DECLARE_HASH_MAP(BlockFile *, bool, // in the current set of tracks. Enumerating that array allows // you to process all block files in the current set. static void GetAllSeqBlocks(AudacityProject *project, - BlockArray *outBlocks) + BlockPtrArray *outBlocks) { TrackList *tracks = project->GetTracks(); TrackListIterator iter(tracks); @@ -72,10 +72,10 @@ static void GetAllSeqBlocks(AudacityProject *project, while(node) { WaveClip *clip = node->GetData(); Sequence *sequence = clip->GetSequence(); - BlockArray *blocks = sequence->GetBlockArray(); + BlockArray &blocks = sequence->GetBlockArray(); int i; - for (i = 0; i < (int)blocks->GetCount(); i++) - outBlocks->Add(blocks->Item(i)); + for (i = 0; i < (int)blocks.size(); i++) + outBlocks->push_back(&blocks.at(i)); node = node->GetNext(); } } @@ -92,11 +92,11 @@ static void ReplaceBlockFiles(AudacityProject *project, ReplacedBlockFileHash &hash) { DirManager *dirManager = project->GetDirManager(); - BlockArray blocks; + BlockPtrArray blocks; GetAllSeqBlocks(project, &blocks); int i; - for (i = 0; i < (int)blocks.GetCount(); i++) { + for (i = 0; i < (int)blocks.size(); i++) { if (hash.count(blocks[i]->f) > 0) { BlockFile *src = blocks[i]->f; BlockFile *dst = hash[src]; @@ -114,14 +114,14 @@ void FindDependencies(AudacityProject *project, { sampleFormat format = project->GetDefaultFormat(); - BlockArray blocks; + BlockPtrArray blocks; GetAllSeqBlocks(project, &blocks); AliasedFileHash aliasedFileHash; BoolBlockFileHash blockFileHash; int i; - for (i = 0; i < (int)blocks.GetCount(); i++) { + for (i = 0; i < (int)blocks.size(); i++) { BlockFile *f = blocks[i]->f; if (f->IsAlias() && (blockFileHash.count(f) == 0)) { @@ -181,13 +181,13 @@ static void RemoveDependencies(AudacityProject *project, aliasedFileHash[fileNameStr] = &aliasedFiles->Item(i); } - BlockArray blocks; + BlockPtrArray blocks; GetAllSeqBlocks(project, &blocks); const sampleFormat format = project->GetDefaultFormat(); ReplacedBlockFileHash blockFileHash; wxLongLong completedBytes = 0; - for (i = 0; i < blocks.GetCount(); i++) { + for (i = 0; i < blocks.size(); i++) { BlockFile *f = blocks[i]->f; if (f->IsAlias() && (blockFileHash.count(f) == 0)) { diff --git a/src/DirManager.cpp b/src/DirManager.cpp index 847df38c8..df16bb4b5 100644 --- a/src/DirManager.cpp +++ b/src/DirManager.cpp @@ -98,6 +98,7 @@ #include "Internat.h" #include "Project.h" #include "Prefs.h" +#include "Sequence.h" #include "widgets/Warning.h" #include "widgets/MultiDialog.h" @@ -349,6 +350,7 @@ DirManager::DirManager() projName = wxT(""); mLoadingTarget = NULL; + mLoadingTargetIdx = 0; mMaxSamples = -1; // toplevel pool hash is fully populated to begin @@ -1009,10 +1011,12 @@ bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs) BlockFile* pBlockFile = NULL; - if( !wxStricmp(tag, wxT("silentblockfile")) ) { + BlockFile *&target = mLoadingTarget->at(mLoadingTargetIdx).f; + + if (!wxStricmp(tag, wxT("silentblockfile"))) { // Silent blocks don't actually have a file associated, so // we don't need to worry about the hash table at all - *mLoadingTarget = SilentBlockFile::BuildFromXML(*this, attrs); + target = SilentBlockFile::BuildFromXML(*this, attrs); return true; } else if ( !wxStricmp(tag, wxT("simpleblockfile")) ) @@ -1074,7 +1078,7 @@ bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs) return false; } else - *mLoadingTarget = pBlockFile; + target = pBlockFile; // // If the block we loaded is already in the hash table, then the @@ -1082,21 +1086,21 @@ bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs) // return a reference to the existing object instead. // - wxString name = (*mLoadingTarget)->GetFileName().GetName(); + wxString name = target->GetFileName().GetName(); BlockFile *retrieved = mBlockFileHash[name]; if (retrieved) { // Lock it in order to delete it safely, i.e. without having // it delete the file, too... - (*mLoadingTarget)->Lock(); - delete (*mLoadingTarget); + target->Lock(); + delete target; Ref(retrieved); // Add one to its reference count - *mLoadingTarget = retrieved; + target = retrieved; return true; } // This is a new object - mBlockFileHash[name]=*mLoadingTarget; + mBlockFileHash[name] = target; // MakeBlockFileName wasn't used so we must add the directory // balancing information BalanceInfoAdd(name); diff --git a/src/DirManager.h b/src/DirManager.h index dc8af0fc8..2ab32d078 100644 --- a/src/DirManager.h +++ b/src/DirManager.h @@ -21,6 +21,7 @@ #include "xml/XMLTagHandler.h" class wxHashTable; +class BlockArray; class BlockFile; class SequenceTest; @@ -101,10 +102,17 @@ class DirManager: public XMLTagHandler { // For debugging only int GetRefCount(BlockFile * f); - void SetLoadingTarget(BlockFile **target) { mLoadingTarget = target; } + void SetLoadingTarget(BlockArray *pArray, unsigned idx) + { + mLoadingTarget = pArray; + mLoadingTargetIdx = idx; + } void SetLoadingFormat(sampleFormat format) { mLoadingFormat = format; } void SetLoadingBlockLength(sampleCount len) { mLoadingBlockLen = len; } - void SetMaxSamples(sampleCount max) { mMaxSamples = max; } + + // Note: following affects only the loading of block files when opening a project + void SetLoadingMaxSamples(sampleCount max) { mMaxSamples = max; } + bool HandleXMLTag(const wxChar *tag, const wxChar **attrs); XMLTagHandler *HandleXMLChild(const wxChar * WXUNUSED(tag)) { return NULL; } void WriteXML(XMLWriter & WXUNUSED(xmlFile)) { wxASSERT(false); } // This class only reads tags. @@ -186,7 +194,8 @@ class DirManager: public XMLTagHandler { wxArrayString aliasList; - BlockFile **mLoadingTarget; + BlockArray *mLoadingTarget; + unsigned mLoadingTargetIdx; sampleFormat mLoadingFormat; sampleCount mLoadingBlockLen; diff --git a/src/Makefile.am b/src/Makefile.am index aab92081a..38968a4b8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,6 +47,7 @@ libaudacity_la_SOURCES = \ $(NULL) audacity_CPPFLAGS = \ + -std=c++11 \ -Wno-deprecated-declarations \ -D__STDC_CONSTANT_MACROS \ -DLIBDIR=\"$(libdir)\" \ diff --git a/src/Project.cpp b/src/Project.cpp index 9df141fe3..513925f31 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -3737,10 +3737,11 @@ void AudacityProject::AddImportedTracks(wxString fileName, if (newTracks[i]->GetKind() == WaveTrack::Wave) { WaveClip* clip = ((WaveTrack*)newTracks[i])->GetClipByIndex(0); - if (clip && clip->GetSequence()->GetBlockArray()->GetCount()) + BlockArray &blocks = clip->GetSequence()->GetBlockArray(); + if (clip && blocks.size()) { - SeqBlock* block = clip->GetSequence()->GetBlockArray()->Item(0); - if (block->f->IsAlias()) + SeqBlock& block = blocks.at(0); + if (block.f->IsAlias()) { mImportedDependencies = true; } @@ -5189,7 +5190,7 @@ void AudacityProject::CaptureKeyboard(wxWindow *handler) } // static -void AudacityProject::ReleaseKeyboard(wxWindow *handler) +void AudacityProject::ReleaseKeyboard(wxWindow * /* handler */) { AudacityProject *project = GetActiveProject(); if (project) diff --git a/src/Sequence.cpp b/src/Sequence.cpp index e84cdf942..6862600f6 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -53,33 +53,28 @@ int Sequence::sMaxDiskBlockSize = 1048576; // Sequence methods Sequence::Sequence(DirManager * projDirManager, sampleFormat format) + : mDirManager(projDirManager) + , mSampleFormat(format) + , mNumSamples(0) + , mMinSamples(sMaxDiskBlockSize / SAMPLE_SIZE(mSampleFormat) / 2) + , mMaxSamples(mMinSamples * 2) + , mErrorOpening(false) { - mDirManager = projDirManager; mDirManager->Ref(); - mNumSamples = 0; - mSampleFormat = format; - mBlock = new BlockArray(); - - mMinSamples = sMaxDiskBlockSize / SAMPLE_SIZE(mSampleFormat) / 2; - mMaxSamples = mMinSamples * 2; - mErrorOpening = false; } +// essentially a copy constructor - but you must pass in the +// current project's DirManager, because we might be copying +// from one project to another Sequence::Sequence(const Sequence &orig, DirManager *projDirManager) + : mDirManager(projDirManager) + , mNumSamples(0) + , mSampleFormat(orig.mSampleFormat) + , mMinSamples(orig.mMinSamples) + , mMaxSamples(orig.mMaxSamples) + , mErrorOpening(false) { - // essentially a copy constructor - but you must pass in the - // current project's DirManager, because we might be copying - // from one project to another - - mDirManager = projDirManager; mDirManager->Ref(); - mNumSamples = 0; - mSampleFormat = orig.mSampleFormat; - mMaxSamples = orig.mMaxSamples; - mMinSamples = orig.mMinSamples; - mErrorOpening = false; - - mBlock = new BlockArray(); bool bResult = Paste(0, &orig); wxASSERT(bResult); // TO DO: Actually handle this. @@ -88,16 +83,22 @@ Sequence::Sequence(const Sequence &orig, DirManager *projDirManager) Sequence::~Sequence() { - for (unsigned int i = 0; i < mBlock->GetCount(); i++) { - if (mBlock->Item(i)->f) - mDirManager->Deref(mBlock->Item(i)->f); - delete mBlock->Item(i); - } - - delete mBlock; + DerefAllFiles(); mDirManager->Deref(); } +void Sequence::DerefAllFiles() +{ + for (size_t i = 0, nn = mBlock.size(); i < nn; i++) + { + BlockFile *& pOldFile = mBlock[i].f; + if (pOldFile) { + mDirManager->Deref(pOldFile); + pOldFile = NULL; + } + } +} + sampleCount Sequence::GetMaxBlockSize() const { return mMaxSamples; @@ -110,24 +111,24 @@ sampleCount Sequence::GetIdealBlockSize() const bool Sequence::Lock() { - for (unsigned int i = 0; i < mBlock->GetCount(); i++) - mBlock->Item(i)->f->Lock(); + for (unsigned int i = 0; i < mBlock.size(); i++) + mBlock[i].f->Lock(); return true; } bool Sequence::CloseLock() { - for (unsigned int i = 0; i < mBlock->GetCount(); i++) - mBlock->Item(i)->f->CloseLock(); + for (unsigned int i = 0; i < mBlock.size(); i++) + mBlock[i].f->CloseLock(); return true; } bool Sequence::Unlock() { - for (unsigned int i = 0; i < mBlock->GetCount(); i++) - mBlock->Item(i)->f->Unlock(); + for (unsigned int i = 0; i < mBlock.size(); i++) + mBlock[i].f->Unlock(); return true; } @@ -137,14 +138,16 @@ sampleFormat Sequence::GetSampleFormat() const return mSampleFormat; } +/* bool Sequence::SetSampleFormat(sampleFormat format) { - if (mBlock->GetCount() > 0 || mNumSamples > 0) + if (mBlock.size() > 0 || mNumSamples > 0) return false; mSampleFormat = format; return true; } +*/ bool Sequence::ConvertToSampleFormat(sampleFormat format, bool* pbChanged) { @@ -155,86 +158,71 @@ bool Sequence::ConvertToSampleFormat(sampleFormat format, bool* pbChanged) if (format == mSampleFormat) return true; - if (mBlock->GetCount() == 0) + if (mBlock.size() == 0) { mSampleFormat = format; *pbChanged = true; return true; } - sampleFormat oldFormat = mSampleFormat; + const sampleFormat oldFormat = mSampleFormat; mSampleFormat = format; - sampleCount oldMaxSamples = mMaxSamples; + const sampleCount oldMinSamples = mMinSamples, oldMaxSamples = mMaxSamples; // These are the same calculations as in the constructor. mMinSamples = sMaxDiskBlockSize / SAMPLE_SIZE(mSampleFormat) / 2; mMaxSamples = mMinSamples * 2; - BlockArray* pNewBlockArray = new BlockArray(); + BlockArray newBlockArray; // Use the ratio of old to new mMaxSamples to make a reasonable guess at allocation. - pNewBlockArray->Alloc(mBlock->GetCount() * ((float)oldMaxSamples / (float)mMaxSamples)); + newBlockArray.reserve(1 + mBlock.size() * ((float)oldMaxSamples / (float)mMaxSamples)); bool bSuccess = true; - for (size_t i = 0; (i < mBlock->GetCount() && bSuccess); i++) { - SeqBlock* pOldSeqBlock = mBlock->Item(i); - BlockFile* pOldBlockFile = pOldSeqBlock->f; + SampleBuffer bufferOld(mMaxSamples, oldFormat); + SampleBuffer bufferNew(mMaxSamples, format); - sampleCount len = pOldSeqBlock->f->GetLength(); - samplePtr bufferOld = NewSamples(len, oldFormat); - samplePtr bufferNew = NewSamples(len, mSampleFormat); - - bSuccess = (pOldBlockFile->ReadData(bufferOld, oldFormat, 0, len) > 0); - if (!bSuccess) + for (size_t i = 0, nn = mBlock.size(); i < nn && bSuccess; i++) { - DeleteSamples(bufferNew); - DeleteSamples(bufferOld); - break; + SeqBlock &oldSeqBlock = mBlock[i]; + BlockFile* oldBlockFile = oldSeqBlock.f; + + sampleCount len = oldBlockFile->GetLength(); + + bSuccess = (oldBlockFile->ReadData(bufferOld.ptr(), oldFormat, 0, len) > 0); + if (!bSuccess) + break; + + CopySamples(bufferOld.ptr(), oldFormat, bufferNew.ptr(), format, len); + + // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451, + // using Blockify, allows (len < mMinSamples). + // This will happen consistently when going from more bytes per sample to fewer... + // This will create a block that's smaller than mMinSamples, which + // shouldn't be allowed, but we agreed it's okay for now. + //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere? + // If so, need to special-case (len < mMinSamples) and start combining data + // from the old blocks... Oh no! + + // Using Blockify will handle the cases where len > the new mMaxSamples. Previous code did not. + const sampleCount blockstart = oldSeqBlock.start; + const unsigned prevSize = newBlockArray.size(); + Blockify(newBlockArray, blockstart, bufferNew.ptr(), len); + bSuccess = (newBlockArray.size() > prevSize); + if (bSuccess) + *pbChanged = true; } - - CopySamples(bufferOld, oldFormat, bufferNew, mSampleFormat, len); - - // Note this fix for http://bugzilla.audacityteam.org/show_bug.cgi?id=451, - // using Blockify, allows (len < mMinSamples). - // This will happen consistently when going from more bytes per sample to fewer... - // This will create a block that's smaller than mMinSamples, which - // shouldn't be allowed, but we agreed it's okay for now. - //vvv ANSWER-ME: Does this cause any bugs, or failures on write, elsewhere? - // If so, need to special-case (len < mMinSamples) and start combining data - // from the old blocks... Oh no! - - // Using Blockify will handle the cases where len > the new mMaxSamples. Previous code did not. - BlockArray* pSplitBlockArray = Blockify(bufferNew, len); - bSuccess = (pSplitBlockArray->GetCount() > 0); - if (bSuccess) - { - for (size_t j = 0; j < pSplitBlockArray->GetCount(); j++) - { - pSplitBlockArray->Item(j)->start += mBlock->Item(i)->start; - pNewBlockArray->Add(pSplitBlockArray->Item(j)); - } - *pbChanged = true; - } - delete pSplitBlockArray; - - DeleteSamples(bufferNew); - DeleteSamples(bufferOld); } if (bSuccess) { // Invalidate all the old, non-aliased block files. // Aliased files will be converted at save, per comment above. - for (size_t i = 0; (i < mBlock->GetCount() && bSuccess); i++) - { - SeqBlock* pOldSeqBlock = mBlock->Item(i); - mDirManager->Deref(pOldSeqBlock->f); - pOldSeqBlock->f = NULL; //vvv ...so we don't delete the file when we delete mBlock, next. ANSWER-ME: Right, or delete? - } - delete mBlock; + + DerefAllFiles(); // Replace with new blocks. - mBlock = pNewBlockArray; + mBlock.swap(newBlockArray); } else { @@ -242,12 +230,16 @@ bool Sequence::ConvertToSampleFormat(sampleFormat format, bool* pbChanged) it just assumes the conversion was successful. TODO: Uncomment this section when TrackPanel::OnFormatChange() is upgraded to check the results. - // Conversion failed. Revert these member vars. - mSampleFormat = oldFormat; - mMaxSamples = oldMaxSamples; + PRL: I don't understand why the comment above justifies leaving the sequence in an inconsistent state. + If this function must fail, better to leave it as a no-op on this sequence. I am uncommenting the + lines below, and adding one to revert mMinSamples too. */ - delete pNewBlockArray; // Failed. Throw out the scratch array. + // Conversion failed. Revert these member vars. + mSampleFormat = oldFormat; + mMaxSamples = oldMaxSamples; + mMinSamples = oldMinSamples; + *pbChanged = false; // Revert overall change flag, in case we had some partial success in the loop. } @@ -259,7 +251,7 @@ bool Sequence::ConvertToSampleFormat(sampleFormat format, bool* pbChanged) bool Sequence::GetMinMax(sampleCount start, sampleCount len, float * outMin, float * outMax) const { - if (len == 0 || mBlock->GetCount() == 0) { + if (len == 0 || mBlock.size() == 0) { *outMin = float(0.0); // FLT_MAX? So it doesn't look like a spurious '0' to a caller? *outMax = float(0.0); // -FLT_MAX? So it doesn't look like a spurious '0' to a caller? return true; @@ -269,18 +261,17 @@ bool Sequence::GetMinMax(sampleCount start, sampleCount len, float max = -FLT_MAX; unsigned int block0 = FindBlock(start); - unsigned int block1 = FindBlock(start + len); + unsigned int block1 = FindBlock(start + len - 1); sampleCount s0, l0, maxl0; // First calculate the min/max of the blocks in the middle of this region; // this is very fast because we have the min/max of every entire block // already in memory. - unsigned int b; - for (b = block0 + 1; b < block1; b++) { + for (unsigned b = block0 + 1; b < block1; ++b) { float blockMin, blockMax, blockRMS; - mBlock->Item(b)->f->GetMinMax(&blockMin, &blockMax, &blockRMS); + mBlock[b].f->GetMinMax(&blockMin, &blockMax, &blockRMS); if (blockMin < min) min = blockMin; @@ -292,43 +283,51 @@ bool Sequence::GetMinMax(sampleCount start, sampleCount len, // selection may only partly overlap these blocks. If the overall min/max // of either of these blocks is within min...max, then we can ignore them. // If not, we need read some samples and summaries from disk. - float block0Min, block0Max, block0RMS; - mBlock->Item(block0)->f->GetMinMax(&block0Min, &block0Max, &block0RMS); + { + float block0Min, block0Max, block0RMS; + const SeqBlock &theBlock = mBlock[block0]; + BlockFile *const theFile = theBlock.f; + theFile->GetMinMax(&block0Min, &block0Max, &block0RMS); - if (block0Min < min || block0Max > max) { - s0 = start - mBlock->Item(block0)->start; - l0 = len; - maxl0 = mBlock->Item(block0)->start + mBlock->Item(block0)->f->GetLength() - start; - wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19 - if (l0 > maxl0) - l0 = maxl0; + if (block0Min < min || block0Max > max) { + s0 = start - theBlock.start; + l0 = len; + maxl0 = theBlock.start + theFile->GetLength() - start; + wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19 + if (l0 > maxl0) + l0 = maxl0; - float partialMin, partialMax, partialRMS; - mBlock->Item(block0)->f->GetMinMax(s0, l0, - &partialMin, &partialMax, &partialRMS); - if (partialMin < min) - min = partialMin; - if (partialMax > max) - max = partialMax; + float partialMin, partialMax, partialRMS; + theFile->GetMinMax(s0, l0, + &partialMin, &partialMax, &partialRMS); + if (partialMin < min) + min = partialMin; + if (partialMax > max) + max = partialMax; + } } - float block1Min, block1Max, block1RMS; - mBlock->Item(block1)->f->GetMinMax(&block1Min, &block1Max, &block1RMS); + if (block1 > block0) + { + float block1Min, block1Max, block1RMS; + const SeqBlock &theBlock = mBlock[block1]; + BlockFile *const theFile = theBlock.f; + theFile->GetMinMax(&block1Min, &block1Max, &block1RMS); - if (block1 > block0 && - (block1Min < min || block1Max > max)) { + if (block1Min < min || block1Max > max) { - s0 = 0; - l0 = (start + len) - mBlock->Item(block1)->start; - wxASSERT(l0 <= mMaxSamples); // Vaughan, 2011-10-19 + s0 = 0; + l0 = (start + len) - theBlock.start; + wxASSERT(l0 <= mMaxSamples); // Vaughan, 2011-10-19 - float partialMin, partialMax, partialRMS; - mBlock->Item(block1)->f->GetMinMax(s0, l0, - &partialMin, &partialMax, &partialRMS); - if (partialMin < min) - min = partialMin; - if (partialMax > max) - max = partialMax; + float partialMin, partialMax, partialRMS; + theFile->GetMinMax(s0, l0, + &partialMin, &partialMax, &partialRMS); + if (partialMin < min) + min = partialMin; + if (partialMax > max) + max = partialMax; + } } *outMin = min; @@ -342,7 +341,7 @@ bool Sequence::GetRMS(sampleCount start, sampleCount len, { // len is the number of samples that we want the rms of. // it may be longer than a block, and the code is carefully set up to handle that. - if (len == 0 || mBlock->GetCount() == 0) { + if (len == 0 || mBlock.size() == 0) { *outRMS = float(0.0); return true; } @@ -351,45 +350,54 @@ bool Sequence::GetRMS(sampleCount start, sampleCount len, sampleCount length = 0; // this is the cumulative length of the bits we have the ms of so far, and should end up == len unsigned int block0 = FindBlock(start); - unsigned int block1 = FindBlock(start + len); + unsigned int block1 = FindBlock(start + len - 1); sampleCount s0, l0, maxl0; // First calculate the rms of the blocks in the middle of this region; // this is very fast because we have the rms of every entire block // already in memory. - unsigned int b; - - for (b = block0 + 1; b < block1; b++) { + for (unsigned b = block0 + 1; b < block1; b++) { float blockMin, blockMax, blockRMS; - mBlock->Item(b)->f->GetMinMax(&blockMin, &blockMax, &blockRMS); + const SeqBlock &theBlock = mBlock[b]; + BlockFile *const theFile = theBlock.f; + theFile->GetMinMax(&blockMin, &blockMax, &blockRMS); - sumsq += blockRMS * blockRMS * mBlock->Item(b)->f->GetLength(); - length += mBlock->Item(b)->f->GetLength(); + const sampleCount fileLen = theFile->GetLength(); + sumsq += blockRMS * blockRMS * fileLen; + length += fileLen; } // Now we take the first and last blocks into account, noting that the // selection may only partly overlap these blocks. // If not, we need read some samples and summaries from disk. - s0 = start - mBlock->Item(block0)->start; - l0 = len; - maxl0 = mBlock->Item(block0)->start + mBlock->Item(block0)->f->GetLength() - start; - wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19 - if (l0 > maxl0) - l0 = maxl0; + { + const SeqBlock &theBlock = mBlock[block0]; + BlockFile *const theFile = theBlock.f; + s0 = start - theBlock.start; + l0 = len; + maxl0 = theBlock.start + theFile->GetLength() - start; + wxASSERT(maxl0 <= mMaxSamples); // Vaughan, 2011-10-19 + if (l0 > maxl0) + l0 = maxl0; - float partialMin, partialMax, partialRMS; - mBlock->Item(block0)->f->GetMinMax(s0, l0, &partialMin, &partialMax, &partialRMS); + float partialMin, partialMax, partialRMS; + theFile->GetMinMax(s0, l0, &partialMin, &partialMax, &partialRMS); - sumsq += partialRMS * partialRMS * l0; - length += l0; + sumsq += partialRMS * partialRMS * l0; + length += l0; + } if (block1 > block0) { - s0 = 0; - l0 = (start + len) - mBlock->Item(block1)->start; + const SeqBlock &theBlock = mBlock[block1]; + BlockFile *const theFile = theBlock.f; - mBlock->Item(block1)->f->GetMinMax(s0, l0, - &partialMin, &partialMax, &partialRMS); + s0 = 0; + l0 = (start + len) - theBlock.start; + wxASSERT(l0 <= mMaxSamples); // PRL: I think Vaughan missed this + + float partialMin, partialMax, partialRMS; + theFile->GetMinMax(s0, l0, &partialMin, &partialMax, &partialRMS); sumsq += partialRMS * partialRMS * l0; length += l0; } @@ -406,53 +414,65 @@ bool Sequence::Copy(sampleCount s0, sampleCount s1, Sequence **dest) { *dest = 0; - if (s0 >= s1 || s0 >= mNumSamples || s1 < 0) + if (s0 >= s1 || s0 >= mNumSamples || s1 >= mNumSamples || s1 < 0) return false; - int numBlocks = mBlock->GetCount(); + int numBlocks = mBlock.size(); int b0 = FindBlock(s0); - int b1 = FindBlock(s1); - - if (s1 == mNumSamples) - b1 = numBlocks; + const int b1 = FindBlock(s1 - 1); + wxASSERT(b0 >= 0); + wxASSERT(b0 < numBlocks); + wxASSERT(b1 < numBlocks); + wxASSERT(b0 <= b1); *dest = new Sequence(mDirManager, mSampleFormat); + (*dest)->mBlock.reserve(b1 - b0 + 1); - samplePtr buffer = NewSamples(mMaxSamples, mSampleFormat); + SampleBuffer buffer(mMaxSamples, mSampleFormat); int blocklen; // Do the first block - if (b0 >= 0 && b0 < numBlocks && s0 != mBlock->Item(b0)->start) { + const SeqBlock &block0 = mBlock[b0]; + if (s0 != block0.start) { + BlockFile *const file = block0.f; + blocklen = std::min(s1, block0.start + file->GetLength()) - s0; + wxASSERT(file->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 + Get(b0, buffer.ptr(), mSampleFormat, s0, blocklen); - blocklen = (mBlock->Item(b0)->start + mBlock->Item(b0)->f->GetLength() - s0); - if (blocklen > (s1 - s0)) - blocklen = s1 - s0; - wxASSERT(mBlock->Item(b0)->f->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 - Get(buffer, mSampleFormat, s0, blocklen); - - (*dest)->Append(buffer, mSampleFormat, blocklen); + (*dest)->Append(buffer.ptr(), mSampleFormat, blocklen); } + else + --b0; - if (b0 >= 0 && b0 < numBlocks && s0 == mBlock->Item(b0)->start) { - b0--; - } // If there are blocks in the middle, copy the blockfiles directly - for (int b = b0 + 1; b < b1; b++) - ((Sequence *)*dest)->AppendBlock(mBlock->Item(b)); + for (int bb = b0 + 1; bb < b1; ++bb) + (*dest)->AppendBlock(mBlock[bb]); // Increase ref count or duplicate file // Do the last block - if (b1 > b0 && b1 < numBlocks) { - blocklen = (s1 - mBlock->Item(b1)->start); - wxASSERT(mBlock->Item(b1)->f->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 - Get(buffer, mSampleFormat, mBlock->Item(b1)->start, blocklen); - (*dest)->Append(buffer, mSampleFormat, blocklen); + if (b1 > b0) { + const SeqBlock &block = mBlock[b1]; + BlockFile *const file = block.f; + blocklen = (s1 - block.start); + wxASSERT(file->IsAlias() || (blocklen <= mMaxSamples)); // Vaughan, 2012-02-29 + if (blocklen < file->GetLength()) { + Get(b1, buffer.ptr(), mSampleFormat, block.start, blocklen); + (*dest)->Append(buffer.ptr(), mSampleFormat, blocklen); + } + else + // Special case, copy exactly + (*dest)->AppendBlock(block); // Increase ref count or duplicate file } - DeleteSamples(buffer); + return ConsistencyCheck(wxT("Sequence::Copy()")); +} - return true; +namespace { + inline bool Overflows(double numSamples) + { + return numSamples > wxLL(9223372036854775807); + } } bool Sequence::Paste(sampleCount s, const Sequence *src) @@ -461,6 +481,7 @@ bool Sequence::Paste(sampleCount s, const Sequence *src) { wxLogError( wxT("Sequence::Paste: sampleCount s %s is < 0 or > mNumSamples %s)."), + // PRL: Why bother with Internat when the above is just wxT? Internat::ToString(((wxLongLong)s).ToDouble(), 0).c_str(), Internat::ToString(((wxLongLong)mNumSamples).ToDouble(), 0).c_str()); wxASSERT(false); @@ -468,10 +489,11 @@ bool Sequence::Paste(sampleCount s, const Sequence *src) } // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)src->mNumSamples) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)src->mNumSamples))) { wxLogError( wxT("Sequence::Paste: mNumSamples %s + src->mNumSamples %s would overflow."), + // PRL: Why bother with Internat when the above is just wxT? Internat::ToString(((wxLongLong)mNumSamples).ToDouble(), 0).c_str(), Internat::ToString(((wxLongLong)src->mNumSamples).ToDouble(), 0).c_str()); wxASSERT(false); @@ -487,111 +509,92 @@ bool Sequence::Paste(sampleCount s, const Sequence *src) return false; } - BlockArray *srcBlock = src->mBlock; + const BlockArray &srcBlock = src->mBlock; sampleCount addedLen = src->mNumSamples; - unsigned int srcNumBlocks = srcBlock->GetCount(); + const unsigned int srcNumBlocks = srcBlock.size(); int sampleSize = SAMPLE_SIZE(mSampleFormat); if (addedLen == 0 || srcNumBlocks == 0) return true; - int b = FindBlock(s); - size_t numBlocks = mBlock->GetCount(); + const size_t numBlocks = mBlock.size(); if (numBlocks == 0 || - (s == mNumSamples && mBlock->Item(numBlocks-1)->f->GetLength() >= mMinSamples)) { + (s == mNumSamples && mBlock.back().f->GetLength() >= mMinSamples)) { // Special case: this track is currently empty, or it's safe to append // onto the end because the current last block is longer than the // minimum size for (unsigned int i = 0; i < srcNumBlocks; i++) - AppendBlock(srcBlock->Item(i)); + AppendBlock(srcBlock[i]); // Increase ref count or duplicate file return ConsistencyCheck(wxT("Paste branch one")); } - if ((b >= 0 ) && (b < (int)numBlocks) - && ((mBlock->Item(b)->f->GetLength() + addedLen) < mMaxSamples)) { + const int b = (s == mNumSamples) ? mBlock.size() - 1 : FindBlock(s); + wxASSERT((b >= 0) && (b < (int)numBlocks)); + SeqBlock *const pBlock = &mBlock[b]; + const sampleCount length = pBlock->f->GetLength(); + const sampleCount largerBlockLen = addedLen + length; + // PRL: when insertion point is the first sample of a block, + // and the following test fails, perhaps we could test + // whether coalescence with the previous block is possible. + if (largerBlockLen <= mMaxSamples) { // Special case: we can fit all of the new samples inside of // one block! - samplePtr buffer = NewSamples(mMaxSamples, mSampleFormat); + SeqBlock &block = *pBlock; + SampleBuffer buffer(largerBlockLen, mSampleFormat); - int splitPoint = s - mBlock->Item(b)->start; - Read(buffer, mSampleFormat, mBlock->Item(b), 0, splitPoint); - src->Get(buffer + splitPoint*sampleSize, + int splitPoint = s - block.start; + Read(buffer.ptr(), mSampleFormat, block, 0, splitPoint); + src->Get(0, buffer.ptr() + splitPoint*sampleSize, mSampleFormat, 0, addedLen); - Read(buffer + (splitPoint + addedLen)*sampleSize, - mSampleFormat, mBlock->Item(b), - splitPoint, mBlock->Item(b)->f->GetLength() - splitPoint); + Read(buffer.ptr() + (splitPoint + addedLen)*sampleSize, + mSampleFormat, block, + splitPoint, length - splitPoint); - SeqBlock *largerBlock = new SeqBlock(); - largerBlock->start = mBlock->Item(b)->start; - sampleCount largerBlockLen = mBlock->Item(b)->f->GetLength() + addedLen; - if (largerBlockLen > mMaxSamples) - { - wxLogError( - wxT("Sequence::Paste: (largerBlockLen %s > mMaxSamples %s). largerBlockLen truncated to mMaxSamples."), - Internat::ToString(((wxLongLong)largerBlockLen).ToDouble(), 0).c_str(), - Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0).c_str()); - largerBlockLen = mMaxSamples; // Prevent overruns, per NGS report for UmixIt. - } - largerBlock->f = - mDirManager->NewSimpleBlockFile(buffer, largerBlockLen, mSampleFormat); + BlockFile *const file = + mDirManager->NewSimpleBlockFile(buffer.ptr(), largerBlockLen, mSampleFormat); - mDirManager->Deref(mBlock->Item(b)->f); - delete mBlock->Item(b); - mBlock->Item(b) = largerBlock; + mDirManager->Deref(block.f); + block.f = file; for (unsigned int i = b + 1; i < numBlocks; i++) - mBlock->Item(i)->start += addedLen; + mBlock[i].start += addedLen; mNumSamples += addedLen; - DeleteSamples(buffer); - return ConsistencyCheck(wxT("Paste branch two")); } - // Case two: if we are inserting four or fewer blocks, + // Case three: if we are inserting four or fewer blocks, // it's simplest to just lump all the data together // into one big block along with the split block, // then resplit it all - BlockArray *newBlock = new BlockArray(); - newBlock->Alloc(numBlocks + srcNumBlocks + 2); - int newNumBlocks = 0; + BlockArray newBlock; + newBlock.reserve(numBlocks + srcNumBlocks + 2); + newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b); - for (int i = 0; i < b; i++) { - newBlock->Add(mBlock->Item(i)); - newNumBlocks++; - } - - SeqBlock *splitBlock = mBlock->Item(b); - sampleCount splitLen = mBlock->Item(b)->f->GetLength(); - int splitPoint = s - splitBlock->start; + SeqBlock &splitBlock = mBlock[b]; + sampleCount splitLen = splitBlock.f->GetLength(); + int splitPoint = s - splitBlock.start; unsigned int i; if (srcNumBlocks <= 4) { sampleCount sum = splitLen + addedLen; - samplePtr sumBuffer = NewSamples(sum, mSampleFormat); - Read(sumBuffer, mSampleFormat, splitBlock, 0, splitPoint); - src->Get(sumBuffer + splitPoint * sampleSize, + SampleBuffer sumBuffer(sum, mSampleFormat); + Read(sumBuffer.ptr(), mSampleFormat, splitBlock, 0, splitPoint); + src->Get(0, sumBuffer.ptr() + splitPoint * sampleSize, mSampleFormat, 0, addedLen); - Read(sumBuffer + (splitPoint + addedLen) * sampleSize, mSampleFormat, + Read(sumBuffer.ptr() + (splitPoint + addedLen) * sampleSize, mSampleFormat, splitBlock, splitPoint, - splitBlock->f->GetLength() - splitPoint); + splitLen - splitPoint); - BlockArray *split = Blockify(sumBuffer, sum); - for (i = 0; i < split->GetCount(); i++) { - split->Item(i)->start += splitBlock->start; - newBlock->Add(split->Item(i)); - newNumBlocks++; - } - delete split; - DeleteSamples(sumBuffer); + Blockify(newBlock, splitBlock.start, sumBuffer.ptr(), sum); } else { // The final case is that we're inserting at least five blocks. @@ -600,79 +603,53 @@ bool Sequence::Paste(sampleCount s, const Sequence *src) // copied in as is, and the last two get merged with the last // half of the split block. - sampleCount srcFirstTwoLen = - srcBlock->Item(0)->f->GetLength() + srcBlock->Item(1)->f->GetLength(); - sampleCount leftLen = splitPoint + srcFirstTwoLen; + const sampleCount srcFirstTwoLen = + srcBlock[0].f->GetLength() + srcBlock[1].f->GetLength(); + const sampleCount leftLen = splitPoint + srcFirstTwoLen; - samplePtr leftBuffer = NewSamples(leftLen, mSampleFormat); - Read(leftBuffer, mSampleFormat, splitBlock, 0, splitPoint); - src->Get(leftBuffer + splitPoint*sampleSize, - mSampleFormat, 0, srcFirstTwoLen); + const SeqBlock &penultimate = srcBlock[srcNumBlocks - 2]; + const sampleCount srcLastTwoLen = + penultimate.f->GetLength() + + srcBlock[srcNumBlocks - 1].f->GetLength(); + const sampleCount rightSplit = splitBlock.f->GetLength() - splitPoint; + const sampleCount rightLen = rightSplit + srcLastTwoLen; - BlockArray *split = Blockify(leftBuffer, leftLen); - for (i = 0; i < split->GetCount(); i++) { - split->Item(i)->start += splitBlock->start; - newBlock->Add(split->Item(i)); - newNumBlocks++; - } - delete split; - DeleteSamples(leftBuffer); + SampleBuffer sampleBuffer(std::max(leftLen, rightLen), mSampleFormat); + + Read(sampleBuffer.ptr(), mSampleFormat, splitBlock, 0, splitPoint); + src->Get(0, sampleBuffer.ptr() + splitPoint*sampleSize, + mSampleFormat, 0, srcFirstTwoLen); + + Blockify(newBlock, splitBlock.start, sampleBuffer.ptr(), leftLen); for (i = 2; i < srcNumBlocks - 2; i++) { - SeqBlock *insertBlock = new SeqBlock(); - insertBlock->start = srcBlock->Item(i)->start + s; - - insertBlock->f = mDirManager->CopyBlockFile(srcBlock->Item(i)->f); - if (!insertBlock->f) { + const SeqBlock &block = srcBlock[i]; + BlockFile *const file = mDirManager->CopyBlockFile(block.f); + if (!file) { wxASSERT(false); // TODO: Handle this better, alert the user of failure. - delete insertBlock; - newBlock->Clear(); - delete newBlock; return false; } - newBlock->Add(insertBlock); - newNumBlocks++; + newBlock.push_back(SeqBlock(file, block.start + s)); } - sampleCount srcLastTwoLen = - srcBlock->Item(srcNumBlocks - 2)->f->GetLength() + - srcBlock->Item(srcNumBlocks - 1)->f->GetLength(); - sampleCount rightSplit = splitBlock->f->GetLength() - splitPoint; - sampleCount rightLen = rightSplit + srcLastTwoLen; - - samplePtr rightBuffer = NewSamples(rightLen, mSampleFormat); - sampleCount lastStart = srcBlock->Item(srcNumBlocks - 2)->start; - src->Get(rightBuffer, mSampleFormat, + sampleCount lastStart = penultimate.start; + src->Get(srcNumBlocks - 2, sampleBuffer.ptr(), mSampleFormat, lastStart, srcLastTwoLen); - Read(rightBuffer + srcLastTwoLen * sampleSize, mSampleFormat, + Read(sampleBuffer.ptr() + srcLastTwoLen * sampleSize, mSampleFormat, splitBlock, splitPoint, rightSplit); - sampleCount pos = s + lastStart; - - split = Blockify(rightBuffer, rightLen); - for (i = 0; i < split->Count(); i++) { - split->Item(i)->start += pos; - newBlock->Add(split->Item(i)); - newNumBlocks++; - } - delete split; - DeleteSamples(rightBuffer); + Blockify(newBlock, s + lastStart, sampleBuffer.ptr(), rightLen); } - mDirManager->Deref(splitBlock->f); - delete splitBlock; + mDirManager->Deref(splitBlock.f); // Copy remaining blocks to new block array and // swap the new block array in for the old - for (i = b + 1; i < numBlocks; i++) { - mBlock->Item(i)->start += addedLen; - newBlock->Add(mBlock->Item(i)); - newNumBlocks++; - } + for (i = b + 1; i < numBlocks; i++) + newBlock.push_back(mBlock[i].Plus(addedLen)); - delete mBlock; - mBlock = newBlock; + mBlock.swap(newBlock); mNumSamples += addedLen; @@ -687,59 +664,66 @@ bool Sequence::SetSilence(sampleCount s0, sampleCount len) bool Sequence::InsertSilence(sampleCount s0, sampleCount len) { // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)len))) return false; + if (len <= 0) + return true; + // Create a new track containing as much silence as we // need to insert, and then call Paste to do the insertion. // We make use of a SilentBlockFile, which takes up no // space on disk. - Sequence *sTrack = new Sequence(mDirManager, mSampleFormat); + Sequence sTrack(mDirManager, mSampleFormat); sampleCount idealSamples = GetIdealBlockSize(); sampleCount pos = 0; - while (len) { - sampleCount l = (len > idealSamples ? idealSamples : len); + sTrack.mBlock.reserve((len + idealSamples - 1) / idealSamples); - SeqBlock *w = new SeqBlock(); - w->start = pos; - w->f = new SilentBlockFile(l); + BlockFile *silentFile = 0; + if (len >= idealSamples) + silentFile = new SilentBlockFile(idealSamples); + while (len >= idealSamples) { + sTrack.mBlock.push_back(SeqBlock(silentFile, pos)); + mDirManager->Ref(silentFile); - sTrack->mBlock->Add(w); - - pos += l; - len -= l; + pos += idealSamples; + len -= idealSamples; + } + if (silentFile) + mDirManager->Deref(silentFile); + if (len) { + sTrack.mBlock.push_back(SeqBlock(new SilentBlockFile(len), pos)); + pos += len; } - sTrack->mNumSamples = pos; + sTrack.mNumSamples = pos; - bool bResult = Paste(s0, sTrack); + bool bResult = Paste(s0, &sTrack); wxASSERT(bResult); - delete sTrack; - return bResult && ConsistencyCheck(wxT("InsertSilence")); } bool Sequence::AppendAlias(wxString fullPath, sampleCount start, - sampleCount len, int channel,bool useOD) + sampleCount len, int channel, bool useOD) { // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)len))) return false; - SeqBlock *newBlock = new SeqBlock(); - - newBlock->start = mNumSamples; - newBlock->f = useOD? - mDirManager->NewODAliasBlockFile(fullPath, start, len, channel): - mDirManager->NewAliasBlockFile(fullPath, start, len, channel); - mBlock->Add(newBlock); - mNumSamples += newBlock->f->GetLength(); + SeqBlock newBlock( + useOD? + mDirManager->NewODAliasBlockFile(fullPath, start, len, channel): + mDirManager->NewAliasBlockFile(fullPath, start, len, channel), + mNumSamples + ); + mBlock.push_back(newBlock); + mNumSamples += len; return true; } @@ -748,43 +732,43 @@ bool Sequence::AppendCoded(wxString fName, sampleCount start, sampleCount len, int channel, int decodeType) { // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)len))) return false; - SeqBlock *newBlock = new SeqBlock(); - - newBlock->start = mNumSamples; - newBlock->f = mDirManager->NewODDecodeBlockFile(fName, start, len, channel, decodeType); - mBlock->Add(newBlock); - mNumSamples += newBlock->f->GetLength(); + SeqBlock newBlock( + mDirManager->NewODDecodeBlockFile(fName, start, len, channel, decodeType), + mNumSamples + ); + mBlock.push_back(newBlock); + mNumSamples += len; return true; } -bool Sequence::AppendBlock(SeqBlock * b) +bool Sequence::AppendBlock(const SeqBlock &b) { // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)b->f->GetLength()) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)b.f->GetLength()))) return false; - SeqBlock *newBlock = new SeqBlock(); - newBlock->start = mNumSamples; - newBlock->f = mDirManager->CopyBlockFile(b->f); - if (!newBlock->f) { + SeqBlock newBlock( + mDirManager->CopyBlockFile(b.f), // Bump ref count if not locked, else copy + mNumSamples + ); + if (!newBlock.f) { /// \todo Error Could not paste! (Out of disk space?) wxASSERT(false); // TODO: Handle this better, alert the user of failure. - delete newBlock; return false; } //Don't need to Ref because it was done by CopyBlockFile, above... - //mDirManager->Ref(newBlock->f); + //mDirManager->Ref(newBlock.f); - mBlock->Add(newBlock); - mNumSamples += newBlock->f->GetLength(); + mBlock.push_back(newBlock); + mNumSamples += newBlock.f->GetLength(); // Don't do a consistency check here because this - // function gets called in an inner loop + // function gets called in an inner loop. return true; } @@ -793,11 +777,12 @@ bool Sequence::AppendBlock(SeqBlock * b) unsigned int Sequence::GetODFlags() { unsigned int ret = 0; - for (unsigned int i = 0; i < mBlock->GetCount(); i++){ - if(!mBlock->Item(i)->f->IsDataAvailable()) - ret = ret|((ODDecodeBlockFile*)mBlock->Item(i)->f)->GetDecodeType(); - else if(!mBlock->Item(i)->f->IsSummaryAvailable()) - ret = ret|ODTask::eODPCMSummary; + for (unsigned int i = 0; i < mBlock.size(); i++) { + BlockFile *const file = mBlock[i].f; + if(!file->IsDataAvailable()) + ret |= (static_cast(file))->GetDecodeType(); + else if(!file->IsSummaryAvailable()) + ret |= ODTask::eODPCMSummary; } return ret; } @@ -805,7 +790,7 @@ unsigned int Sequence::GetODFlags() sampleCount Sequence::GetBlockStart(sampleCount position) const { int b = FindBlock(position); - return mBlock->Item(b)->start; + return mBlock[b].start; } sampleCount Sequence::GetBestBlockSize(sampleCount start) const @@ -813,16 +798,22 @@ sampleCount Sequence::GetBestBlockSize(sampleCount start) const // This method returns a nice number of samples you should try to grab in // one big chunk in order to land on a block boundary, based on the starting // sample. The value returned will always be nonzero and will be no larger - // than the value of GetMaxBlockSize(); + // than the value of GetMaxBlockSize() + + if (start < 0 || start >= mNumSamples) + return mMaxSamples; + int b = FindBlock(start); - int numBlocks = mBlock->GetCount(); + int numBlocks = mBlock.size(); - sampleCount result = (mBlock->Item(b)->start + mBlock->Item(b)->f->GetLength() - start); + const SeqBlock &block = mBlock[b]; + sampleCount result = (block.start + block.f->GetLength() - start); + sampleCount length; while(result < mMinSamples && b+1Item(b+1)->f->GetLength()+result) <= mMaxSamples) { + ((length = mBlock[b+1].f->GetLength()) + result) <= mMaxSamples) { b++; - result += mBlock->Item(b)->f->GetLength(); + result += length; } wxASSERT(result > 0 && result <= mMaxSamples); @@ -834,11 +825,9 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { sampleCount nValue; - /* handle waveblock tag and it's attributes */ + /* handle waveblock tag and its attributes */ if (!wxStrcmp(tag, wxT("waveblock"))) { - SeqBlock *wb = new SeqBlock(); - wb->f = NULL; - wb->start = 0; + SeqBlock wb; // loop through attrs, which is a null-terminated list of // attribute-value pairs @@ -850,12 +839,11 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) break; // Both these attributes have non-negative integer counts of samples, so - // we can test & convert here, making sure that values > 2^31 are OK - // because long clips will need them. + // we can test & convert here, making sure that values > 2^31 are OK + // because long clips will need them. const wxString strValue = value; if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) { - delete (wb); mErrorOpening = true; wxLogWarning( wxT(" Sequence has bad %s attribute value, %s, that should be a positive integer."), @@ -864,7 +852,7 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) } if (!wxStrcmp(attr, wxT("start"))) - wb->start = nValue; + wb.start = nValue; // Vaughan, 2011-10-10: I don't think we ever write a "len" attribute for "waveblock" tag, // so I think this is actually legacy code, or something intended, but not completed. @@ -878,7 +866,6 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) // if the size of the block is bigger than mMaxSamples. if (nValue > mMaxSamples) { - delete (wb); mErrorOpening = true; return false; } @@ -886,13 +873,13 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) } } // while - mBlock->Add(wb); - mDirManager->SetLoadingTarget(&wb->f); + mBlock.push_back(wb); + mDirManager->SetLoadingTarget(&mBlock, mBlock.size() - 1); return true; } - /* handle sequence tag and it's attributes */ + /* handle sequence tag and its attributes */ if (!wxStrcmp(tag, wxT("sequence"))) { while(*attrs) { const wxChar *attr = *attrs++; @@ -920,12 +907,15 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) return false; } mMaxSamples = nValue; - mDirManager->SetMaxSamples(mMaxSamples); + + // PRL: Is the following really okay? DirManager might be shared across projects! + // PRL: Yes, because it only affects DirManager's behavior in opening the project. + mDirManager->SetLoadingMaxSamples(mMaxSamples); } else if (!wxStrcmp(attr, wxT("sampleformat"))) { // This attribute is a sample format, normal int - long fValue; + long fValue; if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&fValue) || (fValue < 0) || !XMLValueChecker::IsValidSampleFormat(fValue)) { mErrorOpening = true; @@ -934,7 +924,7 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) mSampleFormat = (sampleFormat)fValue; } else if (!wxStrcmp(attr, wxT("numsamples"))) - { + { // This attribute is a sample count, so can be 64bit if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&nValue) || (nValue < 0)) { @@ -942,7 +932,7 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) return false; } mNumSamples = nValue; - } + } } // while //// Both mMaxSamples and mSampleFormat should have been set. @@ -967,15 +957,15 @@ void Sequence::HandleXMLEndTag(const wxChar *tag) // Make sure that the sequence is valid. // First, replace missing blockfiles with SilentBlockFiles - unsigned int b; - for (b = 0; b < mBlock->GetCount(); b++) { - if (!mBlock->Item(b)->f) { + for (unsigned b = 0, nn = mBlock.size(); b < nn; b++) { + SeqBlock &block = mBlock[b]; + if (!block.f) { sampleCount len; - if (b < mBlock->GetCount()-1) - len = mBlock->Item(b+1)->start - mBlock->Item(b)->start; + if (b < nn - 1) + len = mBlock[b+1].start - block.start; else - len = mNumSamples - mBlock->Item(b)->start; + len = mNumSamples - block.start; if (len > mMaxSamples) { @@ -983,11 +973,12 @@ void Sequence::HandleXMLEndTag(const wxChar *tag) // the silent replacement to mMaxSamples. wxLogWarning( wxT(" Sequence has missing block file with length %s > mMaxSamples %s.\n Setting length to mMaxSamples. This will likely cause some block files to be considered orphans."), + // PRL: Why bother with Internat when the above is just wxT? Internat::ToString(((wxLongLong)len).ToDouble(), 0).c_str(), Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0).c_str()); len = mMaxSamples; } - mBlock->Item(b)->f = new SilentBlockFile(len); + block.f = new SilentBlockFile(len); wxLogWarning( wxT("Gap detected in project file. Replacing missing block file with silence.")); mErrorOpening = true; @@ -996,26 +987,31 @@ void Sequence::HandleXMLEndTag(const wxChar *tag) // Next, make sure that start times and lengths are consistent sampleCount numSamples = 0; - for (b = 0; b < mBlock->GetCount(); b++) { - if (mBlock->Item(b)->start != numSamples) { - wxString sFileAndExtension = mBlock->Item(b)->f->GetFileName().GetFullName(); + for (unsigned b = 0, nn = mBlock.size(); b < nn; b++) { + SeqBlock &block = mBlock[b]; + if (block.start != numSamples) { + wxString sFileAndExtension = block.f->GetFileName().GetFullName(); if (sFileAndExtension.IsEmpty()) sFileAndExtension = wxT("(replaced with silence)"); else sFileAndExtension = wxT("\"") + sFileAndExtension + wxT("\""); wxLogWarning( - wxT("Gap detected in project file.\n Start (%s) for block file %s is more than one sample past end of previous block (%s).\n Moving start back so blocks are contiguous."), - Internat::ToString(((wxLongLong)(mBlock->Item(b)->start)).ToDouble(), 0).c_str(), + wxT("Gap detected in project file.\n") + wxT(" Start (%s) for block file %s is not one sample past end of previous block (%s).\n") + wxT(" Moving start so blocks are contiguous."), + // PRL: Why bother with Internat when the above is just wxT? + Internat::ToString(((wxLongLong)(block.start)).ToDouble(), 0).c_str(), sFileAndExtension.c_str(), - Internat::ToString(((wxLongLong)(numSamples)).ToDouble(), 0).c_str()); - mBlock->Item(b)->start = numSamples; + Internat::ToString(((wxLongLong)numSamples).ToDouble(), 0).c_str()); + block.start = numSamples; mErrorOpening = true; } - numSamples += mBlock->Item(b)->f->GetLength(); + numSamples += block.f->GetLength(); } if (mNumSamples != numSamples) { wxLogWarning( wxT("Gap detected in project file. Correcting sequence sample count from %s to %s."), + // PRL: Why bother with Internat when the above is just wxT? Internat::ToString(((wxLongLong)mNumSamples).ToDouble(), 0).c_str(), Internat::ToString(((wxLongLong)numSamples).ToDouble(), 0).c_str()); mNumSamples = numSamples; @@ -1043,29 +1039,29 @@ void Sequence::WriteXML(XMLWriter &xmlFile) xmlFile.WriteAttr(wxT("sampleformat"), mSampleFormat); xmlFile.WriteAttr(wxT("numsamples"), mNumSamples); - for (b = 0; b < mBlock->GetCount(); b++) { - SeqBlock *bb = mBlock->Item(b); + for (b = 0; b < mBlock.size(); b++) { + SeqBlock &bb = mBlock[b]; // See http://bugzilla.audacityteam.org/show_bug.cgi?id=451. // Also, don't check against mMaxSamples for AliasBlockFiles, because if you convert sample format, // mMaxSample gets changed to match the format, but the number of samples in the aliased file // has not changed (because sample format conversion was not actually done in the aliased file. - if (!bb->f->IsAlias() && (bb->f->GetLength() > mMaxSamples)) + if (!bb.f->IsAlias() && (bb.f->GetLength() > mMaxSamples)) { wxString sMsg = wxString::Format( _("Sequence has block file with length %s > mMaxSamples %s.\nTruncating to mMaxSamples."), - Internat::ToString(((wxLongLong)(bb->f->GetLength())).ToDouble(), 0).c_str(), + Internat::ToString(((wxLongLong)bb.f->GetLength()).ToDouble(), 0).c_str(), Internat::ToString(((wxLongLong)mMaxSamples).ToDouble(), 0).c_str()); wxMessageBox(sMsg, _("Warning - Length in Writing Sequence"), wxICON_EXCLAMATION | wxOK); wxLogWarning(sMsg); - bb->f->SetLength(mMaxSamples); + bb.f->SetLength(mMaxSamples); } xmlFile.StartTag(wxT("waveblock")); - xmlFile.WriteAttr(wxT("start"), bb->start); + xmlFile.WriteAttr(wxT("start"), bb.start); - bb->f->SaveXML(xmlFile); + bb.f->SaveXML(xmlFile); xmlFile.EndTag(wxT("waveblock")); } @@ -1073,54 +1069,62 @@ void Sequence::WriteXML(XMLWriter &xmlFile) xmlFile.EndTag(wxT("sequence")); } -int Sequence::FindBlock(sampleCount pos, sampleCount lo, - sampleCount guess, sampleCount hi) const -{ - wxASSERT(mBlock->Item(guess)->f->GetLength() > 0); - wxASSERT(lo <= guess && guess <= hi && lo <= hi); - - if (pos >= mBlock->Item(guess)->start && - pos < mBlock->Item(guess)->start + mBlock->Item(guess)->f->GetLength()) - return guess; - - //this is a binary search, but we probably could benefit by something more like - //dictionary search where we guess something smarter than the binary division - //of the unsearched area, since samples are usually proportional to block file number. - if (pos < mBlock->Item(guess)->start) - return FindBlock(pos, lo, (lo + guess) / 2, guess); - else - return FindBlock(pos, guess + 1, (guess + 1 + hi) / 2, hi); -} - int Sequence::FindBlock(sampleCount pos) const { - wxASSERT(pos >= 0 && pos <= mNumSamples); - - int numBlocks = mBlock->GetCount(); + wxASSERT(pos >= 0 && pos < mNumSamples); if (pos == 0) return 0; - if (pos == mNumSamples) - return (numBlocks - 1); + int numBlocks = mBlock.size(); - int rval = FindBlock(pos, 0, numBlocks / 2, numBlocks); + sampleCount lo = 0, loSamples = 0; + sampleCount hi = numBlocks, hiSamples = mNumSamples; + sampleCount guess; + while (true) { + //this is not a binary search, but a + //dictionary search where we guess something smarter than the binary division + //of the unsearched area, since samples are usually proportional to block file number. + const double frac = double(pos - loSamples) / (hiSamples - loSamples); + guess = std::min(hi - 1, lo + sampleCount(frac * (hi - lo))); + const SeqBlock &block = mBlock[guess]; + + wxASSERT(block.f->GetLength() > 0); + wxASSERT(lo <= guess && guess < hi && lo < hi); + + if (pos < block.start) { + wxASSERT(lo != guess); + hi = guess; + hiSamples = block.start; + } + else { + const sampleCount nextStart = block.start + block.f->GetLength(); + if (pos < nextStart) + break; + else { + wxASSERT(guess < hi - 1); + lo = guess + 1; + loSamples = nextStart; + } + } + } + + const int rval = guess; wxASSERT(rval >= 0 && rval < numBlocks && - pos >= mBlock->Item(rval)->start && - pos < mBlock->Item(rval)->start + mBlock->Item(rval)->f->GetLength()); + pos >= mBlock[rval].start && + pos < mBlock[rval].start + mBlock[rval].f->GetLength()); return rval; } bool Sequence::Read(samplePtr buffer, sampleFormat format, - SeqBlock * b, sampleCount start, sampleCount len) const + const SeqBlock &b, sampleCount start, sampleCount len) const { - wxASSERT(b); - wxASSERT(start >= 0); - wxASSERT(start + len <= b->f->GetLength()); + BlockFile *f = b.f; - BlockFile *f = b->f; + wxASSERT(start >= 0); + wxASSERT(start + len <= f->GetLength()); int result = f->ReadData(buffer, format, start, len); @@ -1135,50 +1139,53 @@ bool Sequence::Read(samplePtr buffer, sampleFormat format, return true; } -bool Sequence::CopyWrite(samplePtr buffer, SeqBlock *b, +bool Sequence::CopyWrite(SampleBuffer &scratch, + samplePtr buffer, SeqBlock &b, sampleCount start, sampleCount len) { // We don't ever write to an existing block; to support Undo, // we copy the old block entirely into memory, dereference it, // make the change, and then write the new block to disk. - wxASSERT(b); - wxASSERT(b->f->GetLength() <= mMaxSamples); - wxASSERT(start + len <= b->f->GetLength()); + const sampleCount length = b.f->GetLength(); + wxASSERT(length <= mMaxSamples); + wxASSERT(start + len <= length); + wxASSERT(start >= 0); int sampleSize = SAMPLE_SIZE(mSampleFormat); - samplePtr newBuffer = NewSamples(mMaxSamples, mSampleFormat); - wxASSERT(newBuffer); - Read(newBuffer, mSampleFormat, b, 0, b->f->GetLength()); - memcpy(newBuffer + start*sampleSize, buffer, len*sampleSize); + Read(scratch.ptr(), mSampleFormat, b, 0, length); + memcpy(scratch.ptr() + start*sampleSize, buffer, len*sampleSize); - BlockFile *oldBlockFile = b->f; - b->f = mDirManager->NewSimpleBlockFile(newBuffer, b->f->GetLength(), mSampleFormat); + BlockFile *const oldBlockFile = b.f; + b.f = mDirManager->NewSimpleBlockFile(scratch.ptr(), length, mSampleFormat); mDirManager->Deref(oldBlockFile); - DeleteSamples(newBuffer); - return true; } bool Sequence::Get(samplePtr buffer, sampleFormat format, - sampleCount start, sampleCount len) const + sampleCount start, sampleCount len) const { - if (start < 0 || start > mNumSamples || - start+len > mNumSamples) + if (start < 0 || start >= mNumSamples || + start + len > mNumSamples) return false; int b = FindBlock(start); - while (len) { - sampleCount blen = - mBlock->Item(b)->start + mBlock->Item(b)->f->GetLength() - start; - if (blen > len) - blen = len; - sampleCount bstart = (start - (mBlock->Item(b)->start)); + return Get(b, buffer, format, start, len); +} - Read(buffer, format, mBlock->Item(b), bstart, blen); +bool Sequence::Get(int b, samplePtr buffer, sampleFormat format, + sampleCount start, sampleCount len) const +{ + while (len) { + const SeqBlock &block = mBlock[b]; + const sampleCount bstart = (start - (block.start)); + const sampleCount blen = + std::min(len, block.f->GetLength() - bstart); + + Read(buffer, format, block, bstart, blen); len -= blen; buffer += (blen * SAMPLE_SIZE(format)); @@ -1193,53 +1200,50 @@ bool Sequence::Get(samplePtr buffer, sampleFormat format, bool Sequence::Set(samplePtr buffer, sampleFormat format, sampleCount start, sampleCount len) { - if (start < 0 || start > mNumSamples || + if (start < 0 || start >= mNumSamples || start+len > mNumSamples) return false; - samplePtr temp = NULL; - if (format != mSampleFormat) { - temp = NewSamples(mMaxSamples, mSampleFormat); - wxASSERT(temp); - } + SampleBuffer scratch(mMaxSamples, mSampleFormat); - samplePtr silence = NULL; - if (!buffer) { - silence = NewSamples(mMaxSamples, format); - wxASSERT(silence); - ClearSamples(silence, format, 0, mMaxSamples); + SampleBuffer temp; + if (buffer && format != mSampleFormat) { + temp.Allocate(std::min(len, mMaxSamples), mSampleFormat); } int b = FindBlock(start); while (len) { - int blen = mBlock->Item(b)->start + mBlock->Item(b)->f->GetLength() - start; - if (blen > len) - blen = len; + SeqBlock &block = mBlock[b]; + const sampleCount bstart = start - block.start; + const sampleCount fileLength = block.f->GetLength(); + const int blen = + std::min(len, fileLength - bstart); if (buffer) { if (format == mSampleFormat) - CopyWrite(buffer, mBlock->Item(b), start - mBlock->Item(b)->start, - blen); + CopyWrite(scratch, buffer, block, bstart, blen); else { - CopySamples(buffer, format, temp, mSampleFormat, blen); - CopyWrite(temp, mBlock->Item(b), start - mBlock->Item(b)->start, - blen); + // To do: remove the extra movement. Can we copy-samples within CopyWrite? + CopySamples(buffer, format, temp.ptr(), mSampleFormat, blen); + CopyWrite(scratch, temp.ptr(), block, bstart, blen); } buffer += (blen * SAMPLE_SIZE(format)); } else { // If it's a full block of silence - if (start == mBlock->Item(b)->start && - blen == mBlock->Item(b)->f->GetLength()) { + if (start == block.start && + blen == fileLength) { - mDirManager->Deref(mBlock->Item(b)->f); - mBlock->Item(b)->f = new SilentBlockFile(blen); + mDirManager->Deref(block.f); + block.f = new SilentBlockFile(blen); } else { + // Odd partial blocks of silence at start or end. + temp.Allocate(blen, format); + ClearSamples(temp.ptr(), format, 0, blen); // Otherwise write silence just to the portion of the block - CopyWrite(silence, mBlock->Item(b), - start - mBlock->Item(b)->start, blen); + CopyWrite(scratch, temp.ptr(), block, bstart, blen); } } @@ -1248,12 +1252,6 @@ bool Sequence::Set(samplePtr buffer, sampleFormat format, b++; } - if (!buffer) - DeleteSamples(silence); - - if (format != mSampleFormat) - DeleteSamples(temp); - return ConsistencyCheck(wxT("Set")); } @@ -1325,7 +1323,7 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, sampleCount whereNext = 0; // Loop over block files, opening and reading and closing each // not more than once - unsigned nBlocks = mBlock->GetCount(); + unsigned nBlocks = mBlock.size(); const unsigned int block0 = FindBlock(s0); for (unsigned int b = block0; b < nBlocks; ++b) { if (b > block0) @@ -1335,9 +1333,9 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, // Find the range of sample values for this block that // are in the display. - SeqBlock *const pSeqBlock = mBlock->Item(b); - const sampleCount start = pSeqBlock->start; - nextSrcX = std::min(s1, start + pSeqBlock->f->GetLength()); + SeqBlock &seqBlock = mBlock[b]; + const sampleCount start = seqBlock.start; + nextSrcX = std::min(s1, start + seqBlock.f->GetLength()); // The column for pixel p covers samples from // where[p] up to but excluding where[p + 1]. @@ -1401,13 +1399,13 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, default: case 1: // Read samples - Read((samplePtr)temp, floatSample, pSeqBlock, startPosition, num); + Read((samplePtr)temp, floatSample, seqBlock, startPosition, num); break; case 256: // Read triples //check to see if summary data has been computed - if (pSeqBlock->f->IsSummaryAvailable()) - pSeqBlock->f->Read256(temp, startPosition, num); + if (seqBlock.f->IsSummaryAvailable()) + seqBlock.f->Read256(temp, startPosition, num); else //otherwise, mark the display as not yet computed blockStatus = -1 - b; @@ -1415,8 +1413,8 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, case 65536: // Read triples //check to see if summary data has been computed - if (pSeqBlock->f->IsSummaryAvailable()) - pSeqBlock->f->Read64K(temp, startPosition, num); + if (seqBlock.f->IsSummaryAvailable()) + seqBlock.f->Read64K(temp, startPosition, num); else //otherwise, mark the display as not yet computed blockStatus = -1 - b; @@ -1498,14 +1496,13 @@ bool Sequence::GetWaveDisplay(float *min, float *max, float *rms, int* bl, sampleCount Sequence::GetIdealAppendLen() { - int numBlocks = mBlock->GetCount(); - sampleCount max = GetMaxBlockSize(); - sampleCount lastBlockLen; + int numBlocks = mBlock.size(); + const sampleCount max = GetMaxBlockSize(); if (numBlocks == 0) return max; - lastBlockLen = mBlock->Item(numBlocks-1)->f->GetLength(); + const sampleCount lastBlockLen = mBlock.back().f->GetLength(); if (lastBlockLen == max) return max; else @@ -1516,92 +1513,69 @@ bool Sequence::Append(samplePtr buffer, sampleFormat format, sampleCount len, XMLWriter* blockFileLog /*=NULL*/) { // Quick check to make sure that it doesn't overflow - if (((double)mNumSamples) + ((double)len) > wxLL(9223372036854775807)) + if (Overflows(((double)mNumSamples) + ((double)len))) return false; // If the last block is not full, we need to add samples to it - int numBlocks = mBlock->GetCount(); - if (numBlocks > 0 && mBlock->Item(numBlocks - 1)->f->GetLength() < mMinSamples) { - SeqBlock *lastBlock = mBlock->Item(numBlocks - 1); - sampleCount addLen; - if (lastBlock->f->GetLength() + len < mMaxSamples) - addLen = len; - else - addLen = GetIdealBlockSize() - lastBlock->f->GetLength(); + int numBlocks = mBlock.size(); + sampleCount length; + SeqBlock *pLastBlock; + SampleBuffer buffer2(mMaxSamples, mSampleFormat); + if (numBlocks > 0 && + (length = + (pLastBlock = &mBlock.back())->f->GetLength()) < mMinSamples) { + SeqBlock &lastBlock = *pLastBlock; + const sampleCount addLen = std::min(mMaxSamples - length, len); - SeqBlock *newLastBlock = new SeqBlock(); - - samplePtr buffer2 = NewSamples((lastBlock->f->GetLength() + addLen), mSampleFormat); - Read(buffer2, mSampleFormat, lastBlock, 0, lastBlock->f->GetLength()); + Read(buffer2.ptr(), mSampleFormat, lastBlock, 0, length); CopySamples(buffer, format, - buffer2 + lastBlock->f->GetLength() * SAMPLE_SIZE(mSampleFormat), + buffer2.ptr() + length * SAMPLE_SIZE(mSampleFormat), mSampleFormat, addLen); - newLastBlock->start = lastBlock->start; - int newLastBlockLen = lastBlock->f->GetLength() + addLen; + const int newLastBlockLen = length + addLen; - newLastBlock->f = - mDirManager->NewSimpleBlockFile(buffer2, newLastBlockLen, mSampleFormat, - blockFileLog != NULL); + SeqBlock newLastBlock( + mDirManager->NewSimpleBlockFile(buffer2.ptr(), newLastBlockLen, mSampleFormat, + blockFileLog != NULL), + lastBlock.start + ); if (blockFileLog) - ((SimpleBlockFile*)newLastBlock->f)->SaveXML(*blockFileLog); + static_cast(newLastBlock.f)->SaveXML(*blockFileLog); - DeleteSamples(buffer2); - - mDirManager->Deref(lastBlock->f); - delete lastBlock; - mBlock->Item(numBlocks - 1) = newLastBlock; + mDirManager->Deref(lastBlock.f); + lastBlock = newLastBlock; len -= addLen; mNumSamples += addLen; buffer += addLen * SAMPLE_SIZE(format); } // Append the rest as new blocks - samplePtr temp = NULL; - if (format != mSampleFormat) { - temp = NewSamples(mMaxSamples, mSampleFormat); - wxASSERT(temp); - // TODO: Make error message clearer? - /* i18n-hint: Error message shown when Audacity was trying to allocate - memory to hold audio, and didn't have enough. 'New Samples' is - the name of the C++ function that failed, for use by a developer, - and should not be translated - though you could say - 'in function "NewSamples()"' to be clearer.*/ - if (!temp) { - wxMessageBox(_("Memory allocation failed -- NewSamples")); - return false; - } - } while (len) { - sampleCount idealSamples = GetIdealBlockSize(); - sampleCount l = (len > idealSamples ? idealSamples : len); - SeqBlock *w = new SeqBlock(); - w->start = mNumSamples; - + const sampleCount idealSamples = GetIdealBlockSize(); + const sampleCount l = std::min(idealSamples, len); + BlockFile *pFile; if (format == mSampleFormat) { - w->f = mDirManager->NewSimpleBlockFile(buffer, l, mSampleFormat, + pFile = mDirManager->NewSimpleBlockFile(buffer, l, mSampleFormat, blockFileLog != NULL); } else { - CopySamples(buffer, format, temp, mSampleFormat, l); - w->f = mDirManager->NewSimpleBlockFile(temp, l, mSampleFormat, + CopySamples(buffer, format, buffer2.ptr(), mSampleFormat, l); + pFile = mDirManager->NewSimpleBlockFile(buffer2.ptr(), l, mSampleFormat, blockFileLog != NULL); } if (blockFileLog) - ((SimpleBlockFile*)w->f)->SaveXML(*blockFileLog); + static_cast(pFile)->SaveXML(*blockFileLog); - mBlock->Add(w); + mBlock.push_back(SeqBlock(pFile, mNumSamples)); buffer += l * SAMPLE_SIZE(format); mNumSamples += l; len -= l; } - if (temp) - DeleteSamples(temp); // JKC: During generate we use Append again and again. // If generating a long sequence this test would give O(n^2) @@ -1613,35 +1587,30 @@ bool Sequence::Append(samplePtr buffer, sampleFormat format, return true; } -BlockArray *Sequence::Blockify(samplePtr buffer, sampleCount len) +void Sequence::Blockify(BlockArray &list, sampleCount start, samplePtr buffer, sampleCount len) { - BlockArray *list = new BlockArray(); if (len <= 0) - return list; + return; - list->Alloc(10); - - int num = (len + (mMaxSamples - 1)) / mMaxSamples; + const int num = (len + (mMaxSamples - 1)) / mMaxSamples; + list.reserve(list.size() + num); for (int i = 0; i < num; i++) { - SeqBlock *b = new SeqBlock(); + SeqBlock b; - b->start = i * len / num; - int newLen = ((i + 1) * len / num) - b->start; - samplePtr bufStart = buffer + (b->start * SAMPLE_SIZE(mSampleFormat)); + const sampleCount offset = i * len / num; + b.start = start + offset; + int newLen = ((i + 1) * len / num) - offset; + samplePtr bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat)); - b->f = mDirManager->NewSimpleBlockFile(bufStart, newLen, mSampleFormat); + b.f = mDirManager->NewSimpleBlockFile(bufStart, newLen, mSampleFormat); - list->Add(b); + list.push_back(b); } - - return list; } bool Sequence::Delete(sampleCount start, sampleCount len) { - - if (len == 0) return true; if (len < 0 || start < 0 || start >= mNumSamples) @@ -1650,12 +1619,11 @@ bool Sequence::Delete(sampleCount start, sampleCount len) //TODO: add a ref-deref mechanism to SeqBlock/BlockArray so we don't have to make this a critical section. //On-demand threads iterate over the mBlocks and the GUI thread deletes them, so for now put a mutex here over //both functions, - LockDeleteUpdateMutex(); + DeleteUpdateMutexLocker locker(*this); - unsigned int numBlocks = mBlock->GetCount(); - unsigned int newNumBlocks = 0; + const unsigned int numBlocks = mBlock.size(); - unsigned int b0 = FindBlock(start); + const unsigned int b0 = FindBlock(start); unsigned int b1 = FindBlock(start + len - 1); int sampleSize = SAMPLE_SIZE(mSampleFormat); @@ -1663,120 +1631,95 @@ bool Sequence::Delete(sampleCount start, sampleCount len) // Special case: if the samples to delete are all within a single // block and the resulting length is not too small, perform the // deletion within this block: - if (b0 == b1 && mBlock->Item(b0)->f->GetLength() - len >= mMinSamples) { - SeqBlock *b = mBlock->Item(b0); - sampleCount pos = start - b->start; - sampleCount newLen = b->f->GetLength() - len; + SeqBlock *pBlock; + sampleCount length; - samplePtr buffer = NewSamples(newLen, mSampleFormat); + // One buffer for reuse in various branches here + SampleBuffer scratch; + // The maximum size that will ever be needed + const sampleCount scratchSize = mMaxSamples + mMinSamples; - Read(buffer, mSampleFormat, b, 0, pos); - Read(buffer + (pos * sampleSize), mSampleFormat, + if (b0 == b1 && (length = (pBlock = &mBlock[b0])->f->GetLength()) - len >= mMinSamples) { + SeqBlock &b = *pBlock; + sampleCount pos = start - b.start; + sampleCount newLen = length - len; + + scratch.Allocate(scratchSize, mSampleFormat); + + Read(scratch.ptr(), mSampleFormat, b, 0, pos); + Read(scratch.ptr() + (pos * sampleSize), mSampleFormat, b, pos + len, newLen - pos); - SeqBlock *newBlock = new SeqBlock(); - newBlock->start = b->start; - newBlock->f = - mDirManager->NewSimpleBlockFile(buffer, newLen, mSampleFormat); - - mBlock->Item(b0) = newBlock; + BlockFile *const oldFile = b.f; + b = SeqBlock( + mDirManager->NewSimpleBlockFile(scratch.ptr(), newLen, mSampleFormat), + b.start + ); + mDirManager->Deref(oldFile); for (unsigned int j = b0 + 1; j < numBlocks; j++) - mBlock->Item(j)->start -= len; - - DeleteSamples(buffer); - - mDirManager->Deref(b->f); - delete b; + mBlock[j].start -= len; mNumSamples -= len; - UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch one")); } // Create a new array of blocks - BlockArray *newBlock = new BlockArray(); - newBlock->Alloc(numBlocks - (b1 - b0) + 2); + BlockArray newBlock; + newBlock.reserve(numBlocks - (b1 - b0) + 2); // Copy the blocks before the deletion point over to // the new array + newBlock.insert(newBlock.end(), mBlock.begin(), mBlock.begin() + b0); unsigned int i; - for (i = 0; i < b0; i++) { - newBlock->Add(mBlock->Item(i)); - newNumBlocks++; - } // First grab the samples in block b0 before the deletion point // into preBuffer. If this is enough samples for its own block, // or if this would be the first block in the array, write it out. // Otherwise combine it with the previous block (splitting them // 50/50 if necessary). - SeqBlock *preBlock = mBlock->Item(b0); - sampleCount preBufferLen = start - preBlock->start; + const SeqBlock &preBlock = mBlock[b0]; + sampleCount preBufferLen = start - preBlock.start; if (preBufferLen) { if (preBufferLen >= mMinSamples || b0 == 0) { - SeqBlock *insBlock = new SeqBlock(); + if (!scratch.ptr()) + scratch.Allocate(scratchSize, mSampleFormat); + Read(scratch.ptr(), mSampleFormat, preBlock, 0, preBufferLen); + BlockFile *const pFile = + mDirManager->NewSimpleBlockFile(scratch.ptr(), preBufferLen, mSampleFormat); - insBlock->start = preBlock->start; - - samplePtr preBuffer = NewSamples(preBufferLen, mSampleFormat); - Read(preBuffer, mSampleFormat, preBlock, 0, preBufferLen); - insBlock->f = - mDirManager->NewSimpleBlockFile(preBuffer, preBufferLen, mSampleFormat); - DeleteSamples(preBuffer); - - newBlock->Add(insBlock); - newNumBlocks++; - - if (b0 != b1) { - mDirManager->Deref(preBlock->f); - delete preBlock; - } + newBlock.push_back(SeqBlock(pFile, preBlock.start)); } else { - SeqBlock *prepreBlock = mBlock->Item(b0 - 1); - sampleCount prepreLen = prepreBlock->f->GetLength(); - sampleCount sum = prepreLen + preBufferLen; + const SeqBlock &prepreBlock = mBlock[b0 - 1]; + const sampleCount prepreLen = prepreBlock.f->GetLength(); + const sampleCount sum = prepreLen + preBufferLen; - samplePtr sumBuffer = NewSamples(sum, mSampleFormat); + if (!scratch.ptr()) + scratch.Allocate(scratchSize, mSampleFormat); - Read(sumBuffer, mSampleFormat, prepreBlock, 0, prepreLen); - Read(sumBuffer + prepreLen*sampleSize, mSampleFormat, + Read(scratch.ptr(), mSampleFormat, prepreBlock, 0, prepreLen); + Read(scratch.ptr() + prepreLen*sampleSize, mSampleFormat, preBlock, 0, preBufferLen); - BlockArray *split = Blockify(sumBuffer, sum); - split->Item(0)->start += prepreBlock->start; - newBlock->Item(b0 - 1) = split->Item(0); - for (i = 1; i < split->GetCount(); i++) { - split->Item(i)->start += prepreBlock->start; - newBlock->Add(split->Item(i)); - newNumBlocks++; - } - delete split; + newBlock.erase(newBlock.end() - 1); + Blockify(newBlock, prepreBlock.start, scratch.ptr(), sum); - DeleteSamples(sumBuffer); - - mDirManager->Deref(prepreBlock->f); - delete prepreBlock; - - if (b0 != b1) { - mDirManager->Deref(preBlock->f); - delete preBlock; - } + mDirManager->Deref(prepreBlock.f); } - } else { + } + else { // The sample where we begin deletion happens to fall // right on the beginning of a block. - if (b0 != b1) { - mDirManager->Deref(mBlock->Item(b0)->f); - delete mBlock->Item(b0); - } + } + + if (b0 != b1) { + mDirManager->Deref(preBlock.f); } // Next, delete blocks strictly between b0 and b1 for (i = b0 + 1; i < b1; i++) { - mDirManager->Deref(mBlock->Item(i)->f); - delete mBlock->Item(i); + mDirManager->Deref(mBlock[i].f); } // Now, symmetrically, grab the samples in block b1 after the @@ -1784,94 +1727,72 @@ bool Sequence::Delete(sampleCount start, sampleCount len) // for its own block, or if this would be the last block in // the array, write it out. Otherwise combine it with the // subsequent block (splitting them 50/50 if necessary). - SeqBlock *postBlock = mBlock->Item(b1); + const SeqBlock &postBlock = mBlock[b1]; sampleCount postBufferLen = - (postBlock->start + postBlock->f->GetLength()) - (start + len); + (postBlock.start + postBlock.f->GetLength()) - (start + len); if (postBufferLen) { if (postBufferLen >= mMinSamples || b1 == numBlocks - 1) { - SeqBlock *insBlock = new SeqBlock(); + if (!scratch.ptr()) + // Last use of scratch, can ask for smaller + scratch.Allocate(postBufferLen, mSampleFormat); + sampleCount pos = (start + len) - postBlock.start; + Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen); + BlockFile *const file = + mDirManager->NewSimpleBlockFile(scratch.ptr(), postBufferLen, mSampleFormat); - insBlock->start = start; - - samplePtr postBuffer = NewSamples(postBufferLen, mSampleFormat); - sampleCount pos = (start + len) - postBlock->start; - Read(postBuffer, mSampleFormat, postBlock, pos, postBufferLen); - insBlock->f = - mDirManager->NewSimpleBlockFile(postBuffer, postBufferLen, mSampleFormat); - - DeleteSamples(postBuffer); - - newBlock->Add(insBlock); - newNumBlocks++; - - mDirManager->Deref(postBlock->f); - delete postBlock; + newBlock.push_back(SeqBlock(file, start)); } else { - SeqBlock *postpostBlock = mBlock->Item(b1 + 1); - sampleCount postpostLen = postpostBlock->f->GetLength(); + SeqBlock &postpostBlock = mBlock[b1 + 1]; + sampleCount postpostLen = postpostBlock.f->GetLength(); sampleCount sum = postpostLen + postBufferLen; - samplePtr sumBuffer = NewSamples(sum, mSampleFormat); - sampleCount pos = (start + len) - postBlock->start; - Read(sumBuffer, mSampleFormat, postBlock, pos, postBufferLen); - Read(sumBuffer + (postBufferLen * sampleSize), mSampleFormat, + if (!scratch.ptr()) + // Last use of scratch, can ask for smaller + scratch.Allocate(sum, mSampleFormat); + sampleCount pos = (start + len) - postBlock.start; + Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen); + Read(scratch.ptr() + (postBufferLen * sampleSize), mSampleFormat, postpostBlock, 0, postpostLen); - BlockArray *split = Blockify(sumBuffer, sum); - for (i = 0; i < split->GetCount(); i++) { - split->Item(i)->start += start; - newBlock->Add(split->Item(i)); - newNumBlocks++; - } - delete split; + Blockify(newBlock, start, scratch.ptr(), sum); b1++; - DeleteSamples(sumBuffer); - - mDirManager->Deref(postpostBlock->f); - delete postpostBlock; - mDirManager->Deref(postBlock->f); - delete postBlock; + mDirManager->Deref(postpostBlock.f); } - } else { + } + else { // The sample where we begin deletion happens to fall // right on the end of a block. - mDirManager->Deref(mBlock->Item(b1)->f); - delete mBlock->Item(b1); } + mDirManager->Deref(postBlock.f); // Copy the remaining blocks over from the old array - for (i = b1 + 1; i < numBlocks; i++) { - mBlock->Item(i)->start -= len; - newBlock->Add(mBlock->Item(i)); - newNumBlocks++; - } + for (i = b1 + 1; i < numBlocks; i++) + newBlock.push_back(mBlock[i].Plus(-len)); // Substitute our new array for the old one - delete mBlock; - mBlock = newBlock; + mBlock.swap(newBlock); // Update total number of samples and do a consistency check. mNumSamples -= len; - UnlockDeleteUpdateMutex(); return ConsistencyCheck(wxT("Delete - branch two")); } -bool Sequence::ConsistencyCheck(const wxChar *whereStr) +bool Sequence::ConsistencyCheck(const wxChar *whereStr) const { unsigned int i; sampleCount pos = 0; - unsigned int numBlocks = mBlock->GetCount(); + unsigned int numBlocks = mBlock.size(); bool bError = false; - for (i = 0; i < numBlocks; i++) { - SeqBlock* pSeqBlock = mBlock->Item(i); - if (pos != pSeqBlock->start) + for (i = 0; !bError && i < numBlocks; i++) { + const SeqBlock &seqBlock = mBlock[i]; + if (pos != seqBlock.start) bError = true; - if (pSeqBlock->f) - pos += mBlock->Item(i)->f->GetLength(); + if (seqBlock.f) + pos += seqBlock.f->GetLength(); else bError = true; } @@ -1884,38 +1805,40 @@ bool Sequence::ConsistencyCheck(const wxChar *whereStr) wxString str; DebugPrintf(&str); wxLogError(wxT("%s"), str.c_str()); - wxLogError(wxT("*** Please report this error to feedback@audacityteam.org. ***\n\nRecommended course of action:\nUndo the failed operation(s), then export or save your work and quit.")); + wxLogError(wxT("*** Please report this error to feedback@audacityteam.org. ***\n\n") + wxT("Recommended course of action:\n") + wxT("Undo the failed operation(s), then export or save your work and quit.")); } return !bError; } -void Sequence::DebugPrintf(wxString *dest) +void Sequence::DebugPrintf(wxString *dest) const { unsigned int i; int pos = 0; - for (i = 0; i < mBlock->GetCount(); i++) { - SeqBlock* pSeqBlock = mBlock->Item(i); + for (i = 0; i < mBlock.size(); i++) { + const SeqBlock &seqBlock = mBlock[i]; *dest += wxString::Format (wxT(" Block %3u: start %8lld, len %8lld, refs %d, "), i, - (long long) pSeqBlock->start, - pSeqBlock->f ? (long long) pSeqBlock->f->GetLength() : 0, - pSeqBlock->f ? mDirManager->GetRefCount(pSeqBlock->f) : 0); + (long long) seqBlock.start, + seqBlock.f ? (long long) seqBlock.f->GetLength() : 0, + seqBlock.f ? mDirManager->GetRefCount(seqBlock.f) : 0); - if (pSeqBlock->f) - *dest += pSeqBlock->f->GetFileName().GetFullName(); + if (seqBlock.f) + *dest += seqBlock.f->GetFileName().GetFullName(); else *dest += wxT(""); - if ((pos != pSeqBlock->start) || !pSeqBlock->f) + if ((pos != seqBlock.start) || !seqBlock.f) *dest += wxT(" ERROR\n"); else *dest += wxT("\n"); - if (pSeqBlock->f) - pos += pSeqBlock->f->GetLength(); + if (seqBlock.f) + pos += seqBlock.f->GetLength(); } if (pos != mNumSamples) *dest += wxString::Format @@ -1935,12 +1858,14 @@ int Sequence::GetMaxDiskBlockSize() void Sequence::AppendBlockFile(BlockFile* blockFile) { - SeqBlock *w = new SeqBlock(); - w->start = mNumSamples; - w->f = blockFile; - mBlock->Add(w); + // We assume blockFile has the correct ref count already + + mBlock.push_back(SeqBlock(blockFile, mNumSamples)); mNumSamples += blockFile->GetLength(); + // PRL: I hoisted the intended consistency check out of the inner loop + // See RecordingRecoveryHandler::HandleXMLEndTag + #ifdef VERY_SLOW_CHECKING ConsistencyCheck(wxT("AppendBlockFile")); #endif diff --git a/src/Sequence.h b/src/Sequence.h index d577704fb..6f3247550 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -11,6 +11,7 @@ #ifndef __AUDACITY_SEQUENCE__ #define __AUDACITY_SEQUENCE__ +#include #include #include @@ -36,15 +37,27 @@ class SeqBlock { BlockFile * f; ///the sample in the global wavetrack that this block starts at. sampleCount start; + + SeqBlock() + : f(NULL), start(0) + {} + + SeqBlock(BlockFile *f_, sampleCount start_) + : f(f_), start(start_) + {} + + // Construct a SeqBlock with changed start, same file + SeqBlock Plus(sampleCount delta) const + { + return SeqBlock(f, start + delta); + } }; -WX_DEFINE_ARRAY(SeqBlock *, BlockArray); +class BlockArray : public std::vector {}; +WX_DEFINE_ARRAY(SeqBlock *, BlockPtrArray); class Sequence: public XMLTagHandler { public: - // Temporary: only until we delete TrackArtist once and for all - friend class TrackArtist; - // // Static methods // @@ -66,7 +79,7 @@ class Sequence: public XMLTagHandler { return new Sequence(*this, projDirManager); } - virtual ~Sequence(); + ~Sequence(); // // Editing @@ -86,7 +99,7 @@ class Sequence: public XMLTagHandler { // where[p] up to (but excluding) where[p + 1]. // bl is negative wherever data are not yet available. // Return true if successful. - bool GetWaveDisplay(float *min, float *max, float *rms,int* bl, + bool GetWaveDisplay(float *min, float *max, float *rms, int* bl, int len, const sampleCount *where); bool Copy(sampleCount s0, sampleCount s1, Sequence **dest); @@ -98,7 +111,7 @@ class Sequence: public XMLTagHandler { bool Delete(sampleCount start, sampleCount len); bool AppendAlias(wxString fullPath, sampleCount start, - sampleCount len, int channel,bool useOD); + sampleCount len, int channel, bool useOD); bool AppendCoded(wxString fName, sampleCount start, sampleCount len, int channel, int decodeType); @@ -146,7 +159,7 @@ class Sequence: public XMLTagHandler { // sampleFormat GetSampleFormat() const; - bool SetSampleFormat(sampleFormat format); + // bool SetSampleFormat(sampleFormat format); bool ConvertToSampleFormat(sampleFormat format, bool* pbChanged); // @@ -172,12 +185,27 @@ class Sequence: public XMLTagHandler { // you're doing! // - BlockArray *GetBlockArray() {return mBlock;} + BlockArray &GetBlockArray() {return mBlock;} /// void LockDeleteUpdateMutex(){mDeleteUpdateMutex.Lock();} void UnlockDeleteUpdateMutex(){mDeleteUpdateMutex.Unlock();} + // RAII idiom wrapping the functions above + struct DeleteUpdateMutexLocker { + DeleteUpdateMutexLocker(Sequence &sequence) + : mSequence(sequence) + { + mSequence.LockDeleteUpdateMutex(); + } + ~DeleteUpdateMutexLocker() + { + mSequence.UnlockDeleteUpdateMutex(); + } + private: + Sequence &mSequence; + }; + private: // @@ -192,7 +220,7 @@ class Sequence: public XMLTagHandler { DirManager *mDirManager; - BlockArray *mBlock; + BlockArray mBlock; sampleFormat mSampleFormat; sampleCount mNumSamples; @@ -208,29 +236,24 @@ class Sequence: public XMLTagHandler { // Private methods // - void CalcSummaryInfo(); + void DerefAllFiles(); int FindBlock(sampleCount pos) const; - int FindBlock(sampleCount pos, sampleCount lo, - sampleCount guess, sampleCount hi) const; - bool AppendBlock(SeqBlock *b); + bool AppendBlock(const SeqBlock &b); bool Read(samplePtr buffer, sampleFormat format, - SeqBlock * b, + const SeqBlock &b, sampleCount start, sampleCount len) const; - // These are the two ways to write data to a block - bool FirstWrite(samplePtr buffer, SeqBlock * b, sampleCount len); - bool CopyWrite(samplePtr buffer, SeqBlock * b, + bool CopyWrite(SampleBuffer &scratch, + samplePtr buffer, SeqBlock &b, sampleCount start, sampleCount len); - // Both block-writing methods and AppendAlias call this - // method to write the summary data - void *GetSummary(samplePtr buffer, sampleCount len, - float *min, float *max, float *rms); + void Blockify(BlockArray &list, sampleCount start, samplePtr buffer, sampleCount len); - BlockArray *Blockify(samplePtr buffer, sampleCount len); + bool Get(int b, samplePtr buffer, sampleFormat format, + sampleCount start, sampleCount len) const; public: @@ -240,11 +263,11 @@ class Sequence: public XMLTagHandler { // This function makes sure that the track isn't messed up // because of inconsistent block starts & lengths - bool ConsistencyCheck(const wxChar *whereStr); + bool ConsistencyCheck(const wxChar *whereStr) const; // This function prints information to stdout about the blocks in the // tracks and indicates if there are inconsistencies. - void DebugPrintf(wxString *dest); + void DebugPrintf(wxString *dest) const; }; #endif // __AUDACITY_SEQUENCE__ diff --git a/src/Theme.cpp b/src/Theme.cpp index 88f2aabfd..f7b47b58e 100644 --- a/src/Theme.cpp +++ b/src/Theme.cpp @@ -383,7 +383,7 @@ void ThemeBase::RegisterImage( int &iIndex, const wxImage &Image, const wxString wxASSERT( iIndex == -1 ); // Don't initialise same bitmap twice! mImages.Add( Image ); -#ifdef __WXMAC__ +#ifdef __APPLE__ // On Mac, bitmaps with alpha don't work. // So we convert to a mask and use that. // It isn't quite as good, as alpha gives smoother edges. diff --git a/src/UndoManager.cpp b/src/UndoManager.cpp index ba8ecbef9..3730775fa 100644 --- a/src/UndoManager.cpp +++ b/src/UndoManager.cpp @@ -80,9 +80,9 @@ void UndoManager::CalculateSpaceUsage() { // Scan all blockfiles within current clip BlockArray *blocks = it->GetData()->GetSequenceBlockArray(); - for (size_t b = 0, cnt = blocks->GetCount(); b < cnt; b++) + for (size_t b = 0, cnt = blocks->size(); b < cnt; b++) { - BlockFile *file = blocks->Item(b)->f; + BlockFile *file = blocks->at(b).f; // Accumulate space used by the file if the file didn't exist // in the previous level diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index 8dd55982a..c4d759d47 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -366,7 +366,7 @@ bool WaveClip::SetSamples(samplePtr buffer, sampleFormat format, BlockArray* WaveClip::GetSequenceBlockArray() { - return mSequence->GetBlockArray(); + return &mSequence->GetBlockArray(); } double WaveClip::GetStartTime() const @@ -1708,7 +1708,7 @@ void WaveClip::CloseLock() { GetSequence()->CloseLock(); for (WaveClipList::compatibility_iterator it = mCutLines.GetFirst(); it; it=it->GetNext()) - it->GetData()->Lock(); + it->GetData()->CloseLock(); } void WaveClip::Unlock() diff --git a/src/ondemand/ODComputeSummaryTask.cpp b/src/ondemand/ODComputeSummaryTask.cpp index 7bc0ca0c0..1d818fb03 100644 --- a/src/ondemand/ODComputeSummaryTask.cpp +++ b/src/ondemand/ODComputeSummaryTask.cpp @@ -190,7 +190,7 @@ void ODComputeSummaryTask::Update() seq = clip->GetSequence(); //This lock may be way too big since the whole file is one sequence. //TODO: test for large files and find a way to break it down. - seq->LockDeleteUpdateMutex(); + Sequence::DeleteUpdateMutexLocker locker(*seq); //See Sequence::Delete() for why need this for now.. //We don't need the mBlockFilesMutex here because it is only for the vector list. @@ -202,25 +202,27 @@ void ODComputeSummaryTask::Update() insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n) - for(i=0; i<(int)blocks->GetCount(); i++) + for(i=0; i<(int)blocks->size(); i++) { //if there is data but no summary, this blockfile needs summarizing. - if(blocks->Item(i)->f->IsDataAvailable() && !blocks->Item(i)->f->IsSummaryAvailable()) + SeqBlock &block = blocks->at(i); + BlockFile *const file = block.f; + if(file->IsDataAvailable() && !file->IsSummaryAvailable()) { - blocks->Item(i)->f->Ref(); - ((ODPCMAliasBlockFile*)blocks->Item(i)->f)->SetStart(blocks->Item(i)->start); - ((ODPCMAliasBlockFile*)blocks->Item(i)->f)->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate())); + file->Ref(); + ODPCMAliasBlockFile *const odpcmaFile = static_cast(file); + odpcmaFile->SetStart(block.start); + odpcmaFile->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate())); //these will always be linear within a sequence-lets take advantage of this by keeping a cursor. while(insertCursor<(int)tempBlocks.size()&& (sampleCount)(tempBlocks[insertCursor]->GetStart()+tempBlocks[insertCursor]->GetClipOffset()) < - (sampleCount)(((ODPCMAliasBlockFile*)blocks->Item(i)->f)->GetStart()+((ODPCMAliasBlockFile*)blocks->Item(i)->f)->GetClipOffset())) + (sampleCount)(odpcmaFile->GetStart()+odpcmaFile->GetClipOffset())) insertCursor++; - tempBlocks.insert(tempBlocks.begin()+insertCursor++,(ODPCMAliasBlockFile*)blocks->Item(i)->f); + tempBlocks.insert(tempBlocks.begin() + insertCursor++, odpcmaFile); } } - seq->UnlockDeleteUpdateMutex(); node = node->GetNext(); } } diff --git a/src/ondemand/ODDecodeTask.cpp b/src/ondemand/ODDecodeTask.cpp index bd4f483ae..b0867f5a4 100644 --- a/src/ondemand/ODDecodeTask.cpp +++ b/src/ondemand/ODDecodeTask.cpp @@ -154,22 +154,26 @@ void ODDecodeTask::Update() int insertCursor; insertCursor =0;//OD TODO:see if this works, removed from inner loop (bfore was n*n) - for(i=0; i<(int)blocks->GetCount(); i++) + for (i = 0; i<(int)blocks->size(); i++) { //since we have more than one ODBlockFile, we will need type flags to cast. - if(!blocks->Item(i)->f->IsDataAvailable() && ((ODDecodeBlockFile*)blocks->Item(i)->f)->GetDecodeType()==this->GetODType()) + SeqBlock &block = blocks->at(i); + BlockFile *const file = block.f; + ODDecodeBlockFile *oddbFile; + if (!file->IsDataAvailable() && + (oddbFile = static_cast(file))->GetDecodeType() == this->GetODType()) { - blocks->Item(i)->f->Ref(); - ((ODDecodeBlockFile*)blocks->Item(i)->f)->SetStart(blocks->Item(i)->start); - ((ODDecodeBlockFile*)blocks->Item(i)->f)->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate())); + file->Ref(); + oddbFile->SetStart(block.start); + oddbFile->SetClipOffset((sampleCount)(clip->GetStartTime()*clip->GetRate())); //these will always be linear within a sequence-lets take advantage of this by keeping a cursor. while(insertCursor<(int)tempBlocks.size()&& (sampleCount)(tempBlocks[insertCursor]->GetStart()+tempBlocks[insertCursor]->GetClipOffset()) < - (sampleCount)(((ODDecodeBlockFile*)blocks->Item(i)->f)->GetStart()+((ODDecodeBlockFile*)blocks->Item(i)->f)->GetClipOffset())) + (sampleCount)((oddbFile->GetStart()+oddbFile->GetClipOffset()))) insertCursor++; - tempBlocks.insert(tempBlocks.begin()+insertCursor++,(ODDecodeBlockFile*)blocks->Item(i)->f); + tempBlocks.insert(tempBlocks.begin()+insertCursor++, oddbFile); } }