diff --git a/src/AutoRecovery.cpp b/src/AutoRecovery.cpp index 2980c8961..f999fe420 100644 --- a/src/AutoRecovery.cpp +++ b/src/AutoRecovery.cpp @@ -340,10 +340,9 @@ bool RecordingRecoveryHandler::HandleXMLTag(const wxChar *tag, const auto &dirManager = mProject->GetDirManager(); dirManager->SetLoadingFormat(seq->GetSampleFormat()); - BlockArray array; - array.resize(1); - dirManager->SetLoadingTarget(&array, 0); - auto &blockFile = array[0].f; + BlockFilePtr blockFile; + dirManager->SetLoadingTarget( + [&]() -> BlockFilePtr& { return blockFile; } ); if (!dirManager->HandleXMLTag(tag, attrs) || !blockFile) { diff --git a/src/Benchmark.cpp b/src/Benchmark.cpp index 4a6c3e5f1..2ac45f94b 100644 --- a/src/Benchmark.cpp +++ b/src/Benchmark.cpp @@ -359,7 +359,7 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event)) HoldPrint(true); ZoomInfo zoomInfo(0.0, ZoomInfo::GetDefaultZoom()); - auto dd = std::make_shared(); + auto dd = DirManager::Create(); const auto t = TrackFactory{ dd, &zoomInfo }.NewWaveTrack(int16Sample); t->SetRate(1); diff --git a/src/BlockFile.cpp b/src/BlockFile.cpp index 10fd14263..4bdcbfd58 100644 --- a/src/BlockFile.cpp +++ b/src/BlockFile.cpp @@ -53,7 +53,6 @@ out. #include "sndfile.h" #include "FileException.h" #include "FileFormats.h" -#include "MissingAliasFileDialog.h" // msmeyer: Define this to add debug output via wxPrintf() //#define DEBUG_BLOCKFILE @@ -134,6 +133,16 @@ void BlockFile::SetFileName(wxFileNameWrapper &&name) mFileName=std::move(name); } +const wxFileNameWrapper &BlockFile::GetExternalFileName() const +{ + static wxFileNameWrapper empty; + return empty; +} + +void BlockFile::SetExternalFileName( wxFileNameWrapper && ) +{ + wxASSERT( false ); +} /// Marks this BlockFile as "locked." A locked BlockFile may not /// be moved or deleted, only copied. Locking a BlockFile prevents @@ -479,6 +488,23 @@ bool BlockFile::Read64K(float *buffer, return result; } +namespace { + BlockFile::MissingAliasFileFoundHook &GetMissingAliasFileFound() + { + static BlockFile::MissingAliasFileFoundHook theHook; + return theHook; + } +} + +auto BlockFile::SetMissingAliasFileFound( MissingAliasFileFoundHook hook ) + -> MissingAliasFileFoundHook +{ + auto &theHook = GetMissingAliasFileFound(); + auto result = theHook; + theHook = hook; + return result; +} + size_t BlockFile::CommonReadData( bool mayThrow, const wxFileName &fileName, bool &mSilentLog, @@ -537,8 +563,9 @@ size_t BlockFile::CommonReadData( if (pAliasFile) { // Set a marker to display an error message for the silence - if (!MissingAliasFilesDialog::ShouldShow()) - MissingAliasFilesDialog::Mark(pAliasFile); + auto hook = GetMissingAliasFileFound(); + if (hook) + hook( pAliasFile ); } } } @@ -707,6 +734,16 @@ AliasBlockFile::~AliasBlockFile() { } +const wxFileNameWrapper &AliasBlockFile::GetExternalFileName() const +{ + return GetAliasedFileName(); +} + +void AliasBlockFile::SetExternalFileName( wxFileNameWrapper &&newName ) +{ + ChangeAliasedFileName( std::move( newName ) ); +} + /// Read the summary of this alias block from disk. Since the audio data /// is elsewhere, this consists of reading the entire summary file. /// Fill with zeroes and return false if data are unavailable for any reason. diff --git a/src/BlockFile.h b/src/BlockFile.h index cab8b5f20..08af66e34 100644 --- a/src/BlockFile.h +++ b/src/BlockFile.h @@ -18,6 +18,8 @@ #include "ondemand/ODTaskThread.h" +#include + class XMLWriter; class SummaryInfo { @@ -49,6 +51,14 @@ inline std::shared_ptr< Result > make_blockfile (Args && ... args) class PROFILE_DLL_API BlockFile /* not final, abstract */ { public: + // Type of function to be called when opening of an alias block file for read + // discovers that the other audio file it depends on is absent + using MissingAliasFileFoundHook = + std::function< void(const AliasBlockFile*) >; + // Install a hook, and return the previous hook + static MissingAliasFileFoundHook + SetMissingAliasFileFound( MissingAliasFileFoundHook hook ); + // Constructor / Destructor /// Construct a BlockFile. @@ -104,6 +114,12 @@ class PROFILE_DLL_API BlockFile /* not final, abstract */ { virtual GetFileNameResult GetFileName() const; virtual void SetFileName(wxFileNameWrapper &&name); + // Managing an external file dependency + // Default always returns empty + virtual const wxFileNameWrapper &GetExternalFileName() const; + // Default does nothing (and gives assertion violation in debug) + virtual void SetExternalFileName( wxFileNameWrapper &&newName ); + size_t GetLength() const { return mLen; } void SetLength(size_t newLen) { mLen = newLen; } @@ -268,10 +284,13 @@ class AliasBlockFile /* not final */ : public BlockFile // // These methods are for advanced use only! // - const wxFileName &GetAliasedFileName() const { return mAliasedFileName; } + const wxFileNameWrapper &GetAliasedFileName() const { return mAliasedFileName; } void ChangeAliasedFileName(wxFileNameWrapper &&newAliasedFile); bool IsAlias() const override { return true; } + const wxFileNameWrapper &GetExternalFileName() const override; + void SetExternalFileName( wxFileNameWrapper &&newName ) override; + protected: // Introduce a NEW virtual. /// Write the summary to disk, using the derived ReadData() to get the data diff --git a/src/Dependencies.cpp b/src/Dependencies.cpp index e3e340182..2bae89040 100644 --- a/src/Dependencies.cpp +++ b/src/Dependencies.cpp @@ -49,7 +49,7 @@ AliasedFile s. #include #include -#include "BlockFile.h" +#include "blockfile/SimpleBlockFile.h" #include "DirManager.h" #include "Prefs.h" #include "Project.h" @@ -204,11 +204,14 @@ static void RemoveDependencies(AudacityProject *project, BlockFilePtr newBlockFile; { SampleBuffer buffer(len, format); - // We tolerate exceptions from NewSimpleBlockFile and so we - // can allow exceptions from ReadData too + // We tolerate exceptions from NewBlockFile + // and so we can allow exceptions from ReadData too f->ReadData(buffer.ptr(), format, 0, len); newBlockFile = - dirManager->NewSimpleBlockFile(buffer.ptr(), len, format); + dirManager->NewBlockFile( [&]( wxFileNameWrapper filePath ) { + return make_blockfile( + std::move(filePath), buffer.ptr(), len, format); + } ); } // Update our hash so we know what block files we've done diff --git a/src/DirManager.cpp b/src/DirManager.cpp index df5a8a3b7..0e86b9cbc 100644 --- a/src/DirManager.cpp +++ b/src/DirManager.cpp @@ -85,23 +85,14 @@ #include #endif -#include "Clipboard.h" +#include "BlockFile.h" #include "FileNames.h" -#include "blockfile/LegacyBlockFile.h" -#include "blockfile/LegacyAliasBlockFile.h" -#include "blockfile/SilentBlockFile.h" -#include "blockfile/ODPCMAliasBlockFile.h" -#include "blockfile/ODDecodeBlockFile.h" #include "InconsistencyException.h" -#include "Project.h" #include "Prefs.h" -#include "Sequence.h" #include "widgets/Warning.h" #include "widgets/ErrorDialog.h" #include "widgets/ProgressDialog.h" -#include "ondemand/ODManager.h" - #if defined(__WXMAC__) #include #include @@ -356,6 +347,19 @@ wxString DirManager::globaltemp; int DirManager::numDirManagers = 0; bool DirManager::dontDeleteTempFiles = false; +namespace { + +// Global tracking of all outstanding DirManagers +std::vector< std::weak_ptr< DirManager > > sDirManagers; + +} + +std::shared_ptr DirManager::Create() +{ + auto result = std::shared_ptr< DirManager >( safenew DirManager ); + sDirManagers.push_back( result ); + return result; +} DirManager::DirManager() { @@ -385,8 +389,6 @@ DirManager::DirManager() projPath = wxT(""); projName = wxT(""); - mLoadingTarget = NULL; - mLoadingTargetIdx = 0; mMaxSamples = ~size_t(0); // toplevel pool hash is fully populated to begin @@ -410,6 +412,13 @@ DirManager::DirManager() DirManager::~DirManager() { + auto start = sDirManagers.begin(), finish = sDirManagers.end(), + iter = std::remove_if( start, finish, + [=]( const std::weak_ptr &ptr ){ + return ptr.expired() || ptr.lock().get() == this; + } ); + sDirManagers.erase( iter, finish ); + numDirManagers--; if (numDirManagers == 0) { CleanTempDir(); @@ -1171,71 +1180,17 @@ wxFileNameWrapper DirManager::MakeBlockFileName() return ret; } -BlockFilePtr DirManager::NewSimpleBlockFile( - samplePtr sampleData, size_t sampleLen, - sampleFormat format, - bool allowDeferredWrite) +BlockFilePtr DirManager::NewBlockFile( const BlockFileFactory &factory ) { wxFileNameWrapper filePath{ MakeBlockFileName() }; const wxString fileName{ filePath.GetName() }; - - auto newBlockFile = make_blockfile - (std::move(filePath), sampleData, sampleLen, format, allowDeferredWrite); - + auto newBlockFile = factory( std::move(filePath) ); mBlockFileHash[fileName] = newBlockFile; - - return newBlockFile; -} - -BlockFilePtr DirManager::NewAliasBlockFile( - const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel) -{ - wxFileNameWrapper filePath{ MakeBlockFileName() }; - const wxString fileName = filePath.GetName(); - - auto newBlockFile = make_blockfile - (std::move(filePath), wxFileNameWrapper{aliasedFile}, - aliasStart, aliasLen, aliasChannel); - - mBlockFileHash[fileName]=newBlockFile; - aliasList.push_back(aliasedFile); - - return newBlockFile; -} - -BlockFilePtr DirManager::NewODAliasBlockFile( - const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel) -{ - wxFileNameWrapper filePath{ MakeBlockFileName() }; - const wxString fileName{ filePath.GetName() }; - - auto newBlockFile = make_blockfile - (std::move(filePath), wxFileNameWrapper{aliasedFile}, - aliasStart, aliasLen, aliasChannel); - - mBlockFileHash[fileName]=newBlockFile; - aliasList.push_back(aliasedFile); - - return newBlockFile; -} - -BlockFilePtr DirManager::NewODDecodeBlockFile( - const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel, int decodeType) -{ - wxFileNameWrapper filePath{ MakeBlockFileName() }; - const wxString fileName{ filePath.GetName() }; - - auto newBlockFile = make_blockfile - (std::move(filePath), wxFileNameWrapper{ aliasedFile }, - aliasStart, aliasLen, aliasChannel, decodeType); - - mBlockFileHash[fileName]=newBlockFile; - aliasList.push_back(aliasedFile); //OD TODO: check to see if we need to remove this when done decoding. - //I don't immediately see a place where aliased files remove when a file is closed. - + auto &aliasName = newBlockFile->GetExternalFileName(); + if ( aliasName.IsOk() ) + //OD TODO: check to see if we need to remove this when done decoding. + //I don't immediately see a place where aliased files remove when a file is closed. + aliasList.push_back( aliasName.GetFullPath() ); return newBlockFile; } @@ -1324,69 +1279,49 @@ BlockFilePtr DirManager::CopyBlockFile(const BlockFilePtr &b) return b2; } +namespace { + +using Deserializers = + std::unordered_map< wxString, DirManager::BlockFileDeserializer >; +Deserializers &GetDeserializers() +{ + static Deserializers sDeserializers; + return sDeserializers; +} + +} + +DirManager::RegisteredBlockFileDeserializer::RegisteredBlockFileDeserializer( + const wxString &tag, BlockFileDeserializer function ) +{ + GetDeserializers()[tag] = function; +} + bool DirManager::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { - if( mLoadingTarget == NULL ) + if( !mLoadingTarget ) return false; BlockFilePtr pBlockFile {}; - BlockFilePtr &target = mLoadingTarget->at(mLoadingTargetIdx).f; + BlockFilePtr &target = mLoadingTarget(); + mLoadingTarget = nullptr; - 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 - target = SilentBlockFile::BuildFromXML(*this, attrs); - return true; - } - else if ( !wxStricmp(tag, wxT("simpleblockfile")) ) - pBlockFile = SimpleBlockFile::BuildFromXML(*this, attrs); - else if( !wxStricmp(tag, wxT("pcmaliasblockfile")) ) - pBlockFile = PCMAliasBlockFile::BuildFromXML(*this, attrs); - else if( !wxStricmp(tag, wxT("odpcmaliasblockfile")) ) - { - pBlockFile = ODPCMAliasBlockFile::BuildFromXML(*this, attrs); - //in the case of loading an OD file, we need to schedule the ODManager to begin OD computing of summary - //However, because we don't have access to the track or even the Sequence from this call, we mark a flag - //in the ODMan and check it later. - ODManager::MarkLoadedODFlag(); - } - else if( !wxStricmp(tag, wxT("oddecodeblockfile")) ) - { - pBlockFile = ODDecodeBlockFile::BuildFromXML(*this, attrs); - ODManager::MarkLoadedODFlag(); - } - else if( !wxStricmp(tag, wxT("blockfile")) || - !wxStricmp(tag, wxT("legacyblockfile")) ) { - // Support Audacity version 1.1.1 project files - - int i=0; - bool alias = false; - - while(attrs[i]) { - if (!wxStricmp(attrs[i], wxT("alias"))) { - if (wxAtoi(attrs[i+1])==1) - alias = true; - } - i++; - if (attrs[i]) - i++; - } - - if (alias) - pBlockFile = LegacyAliasBlockFile::BuildFromXML(projFull, attrs); - else - pBlockFile = LegacyBlockFile::BuildFromXML(projFull, attrs, - mLoadingBlockLen, - mLoadingFormat); - } - else + auto &table = GetDeserializers(); + auto iter = table.find( tag ); + if ( iter == table.end() ) return false; + pBlockFile = iter->second( *this, attrs ); if (!pBlockFile) // BuildFromXML failed, or we didn't find a valid blockfile tag. return false; + if (!pBlockFile->GetFileName().name.IsOk()) + // Silent blocks don't actually have a file associated, so + // we don't need to worry about the hash table at all + return true; + // Check the length here so we don't have to do it in each BuildFromXML method. if ((mMaxSamples != ~size_t(0)) && // is initialized (pBlockFile->GetLength() > mMaxSamples)) @@ -1564,25 +1499,11 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName) { BlockFilePtr b = iter->second.lock(); if (b) { - // don't worry, we don't rely on this cast unless IsAlias is true - auto ab = static_cast< AliasBlockFile * > ( &*b ); - - // don't worry, we don't rely on this cast unless ISDataAvailable is false - // which means that it still needs to access the file. - auto db = static_cast< ODDecodeBlockFile * > ( &*b ); - - if (b->IsAlias() && ab->GetAliasedFileName() == fName) { + if (fName.IsOk() && b->GetExternalFileName() == fName) { needToRename = true; //ODBlocks access the aliased file on another thread, so we need to pause them before this continues. - readLocks.push_back( ab->LockForRead() ); - } - //now for encoded OD blocks (e.g. flac) - else if (!b->IsDataAvailable() && db->GetEncodedAudioFilename() == fName) { - needToRename = true; - - //ODBlocks access the aliased file on another thread, so we need to pause them before this continues. - readLocks.push_back( db->LockForRead() ); + readLocks.push_back( b->LockForRead() ); } } ++iter; @@ -1614,19 +1535,12 @@ bool DirManager::EnsureSafeFilename(const wxFileName &fName) { BlockFilePtr b = iter2->second.lock(); if (b) { - auto ab = static_cast< AliasBlockFile * > ( &*b ); - auto db = static_cast< ODDecodeBlockFile * > ( &*b ); - - if (b->IsAlias() && ab->GetAliasedFileName() == fName) - { - ab->ChangeAliasedFileName(wxFileNameWrapper{ renamedFileName }); + if (fName.IsOk() && b->GetExternalFileName() == fName) { + b->SetExternalFileName(wxFileNameWrapper{ renamedFileName }); wxPrintf(_("Changed block %s to new alias name\n"), b->GetFileName().name.GetFullName()); } - else if (!b->IsDataAvailable() && db->GetEncodedAudioFilename() == fName) { - db->ChangeAudioFile(wxFileNameWrapper{ renamedFileName }); - } } ++iter2; } @@ -1744,7 +1658,12 @@ void DirManager::FindOrphanBlockFiles( const FilePaths &filePathArray, // input: all files in project directory FilePaths &orphanFilePathArray) // output: orphan files { - DirManager *clipboardDM = NULL; + std::vector< std::shared_ptr > otherDirManagers; + for ( auto &wPtr : sDirManagers ) { + auto sPtr = wPtr.lock(); + if ( sPtr && sPtr.get() != this ) + otherDirManagers.push_back( sPtr ); + } for (size_t i = 0; i < filePathArray.size(); i++) { @@ -1757,17 +1676,15 @@ void DirManager::FindOrphanBlockFiles( (ext.IsSameAs(wxT("au"), false) || ext.IsSameAs(wxT("auf"), false))) { - if (!clipboardDM) { - auto &clipTracks = Clipboard::Get().GetTracks(); - - auto track = *clipTracks.Any().first; - if (track) - clipboardDM = track->GetDirManager().get(); - } - // Ignore it if it exists in the clipboard (from a previously closed project) - if (!(clipboardDM && clipboardDM->ContainsBlockFile(basename))) - orphanFilePathArray.push_back(fullname.GetFullPath()); + if ( std::any_of( otherDirManagers.begin(), otherDirManagers.end(), + [&]( const std::shared_ptr< DirManager > &ptr ){ + return ptr->ContainsBlockFile( basename ); + } + ) ) + continue; + + orphanFilePathArray.push_back(fullname.GetFullPath()); } } for ( const auto &orphan : orphanFilePathArray ) diff --git a/src/DirManager.h b/src/DirManager.h index 71d30576d..20f11bb52 100644 --- a/src/DirManager.h +++ b/src/DirManager.h @@ -14,6 +14,7 @@ #include "audacity/Types.h" #include "xml/XMLTagHandler.h" +#include #include class wxFileNameWrapper; @@ -67,9 +68,25 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler { static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias, int flags, const wxChar* message = nullptr); + // Type of a function that builds a block file, using attributes from XML + using BlockFileDeserializer = + std::function< BlockFilePtr( DirManager&, const wxChar ** ) >; + // Typically a statically declared object, + // registers a function for an XML tag + struct RegisteredBlockFileDeserializer { + RegisteredBlockFileDeserializer( + const wxString &tag, BlockFileDeserializer function ); + }; + + private: // MM: Construct DirManager + // Don't call this directly but use Create() instead DirManager(); + public: + + static std::shared_ptr< DirManager > Create(); + virtual ~DirManager(); size_t NumBlockFiles() const { return mBlockFileHash.size(); } @@ -107,23 +124,8 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler { wxLongLong GetFreeDiskSpace(); - BlockFilePtr - NewSimpleBlockFile(samplePtr sampleData, - size_t sampleLen, - sampleFormat format, - bool allowDeferredWrite = false); - - BlockFilePtr - NewAliasBlockFile( const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel); - - BlockFilePtr - NewODAliasBlockFile( const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel); - - BlockFilePtr - NewODDecodeBlockFile( const FilePath &aliasedFile, sampleCount aliasStart, - size_t aliasLen, int aliasChannel, int decodeType); + using BlockFileFactory = std::function< BlockFilePtr( wxFileNameWrapper ) >; + BlockFilePtr NewBlockFile( const BlockFileFactory &factory ); /// Returns true if the blockfile pointed to by b is contained by the DirManager bool ContainsBlockFile(const BlockFile *b) const; @@ -150,12 +152,14 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler { bool EnsureSafeFilename(const wxFileName &fName); - void SetLoadingTarget(BlockArray *pArray, unsigned idx) + using LoadingTarget = std::function< BlockFilePtr &() >; + void SetLoadingTarget( LoadingTarget loadingTarget ) { - mLoadingTarget = pArray; - mLoadingTargetIdx = idx; + mLoadingTarget = loadingTarget; } + sampleFormat GetLoadingFormat() const { return mLoadingFormat; } void SetLoadingFormat(sampleFormat format) { mLoadingFormat = format; } + size_t GetLoadingBlockLength() const { return mLoadingBlockLen; } void SetLoadingBlockLength(size_t len) { mLoadingBlockLen = len; } // Note: following affects only the loading of block files when opening a project @@ -248,8 +252,7 @@ class PROFILE_DLL_API DirManager final : public XMLTagHandler { FilePaths aliasList; - BlockArray *mLoadingTarget; - unsigned mLoadingTargetIdx; + LoadingTarget mLoadingTarget; sampleFormat mLoadingFormat; size_t mLoadingBlockLen; diff --git a/src/MissingAliasFileDialog.cpp b/src/MissingAliasFileDialog.cpp index ee895ad5d..d0f00ddb9 100644 --- a/src/MissingAliasFileDialog.cpp +++ b/src/MissingAliasFileDialog.cpp @@ -131,3 +131,12 @@ namespace MissingAliasFilesDialog { } } + +// Arrange callback from low levels of block file I/O to detect missing files +static struct InstallHook{ InstallHook() { + auto hook = [](const AliasBlockFile *pAliasFile){ + if (!MissingAliasFilesDialog::ShouldShow()) + MissingAliasFilesDialog::Mark(pAliasFile); + }; + BlockFile::SetMissingAliasFileFound( hook ); +} } installHook; diff --git a/src/Project.cpp b/src/Project.cpp index 79a2cfe0e..c43ad8a08 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -1130,8 +1130,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, MissingAliasFilesDialog::SetShouldShow(true); // MM: DirManager is created dynamically, freed on demand via ref-counting - // MM: We don't need to Ref() here because it start with refcount=1 - mDirManager = std::make_shared(); + mDirManager = DirManager::Create(); mLastSavedTracks.reset(); @@ -5371,7 +5370,7 @@ void AudacityProject::ResetProjectToEmpty() { SelectActions::DoSelectAll(*this); TrackActions::DoRemoveTracks(*this); // A new DirManager. - mDirManager = std::make_shared(); + mDirManager = DirManager::Create(); mTrackFactory.reset(safenew TrackFactory{ mDirManager, &mViewInfo }); // mLastSavedTrack code copied from OnCloseWindow. diff --git a/src/Sequence.cpp b/src/Sequence.cpp index c5936bee7..48ebadc30 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -40,10 +40,10 @@ #include #include -#include "blockfile/ODDecodeBlockFile.h" #include "DirManager.h" #include "blockfile/SilentBlockFile.h" +#include "blockfile/SimpleBlockFile.h" #include "InconsistencyException.h" @@ -457,6 +457,17 @@ namespace { { return numSamples > wxLL(9223372036854775807); } + + BlockFilePtr NewSimpleBlockFile( DirManager &dm, + samplePtr sampleData, size_t sampleLen, + sampleFormat format, + bool allowDeferredWrite = false) + { + return dm.NewBlockFile( [&]( wxFileNameWrapper filePath ) { + return make_blockfile( + std::move(filePath), sampleData, sampleLen, format, allowDeferredWrite); + } ); + } } void Sequence::Paste(sampleCount s, const Sequence *src) @@ -549,7 +560,7 @@ void Sequence::Paste(sampleCount s, const Sequence *src) splitPoint, length - splitPoint, true); auto file = - mDirManager->NewSimpleBlockFile( + NewSimpleBlockFile( *mDirManager, // largerBlockLen is not more than mMaxSamples... buffer.ptr(), largerBlockLen.as_size_t(), mSampleFormat); @@ -709,41 +720,6 @@ void Sequence::InsertSilence(sampleCount s0, sampleCount len) Paste(s0, &sTrack); } -void Sequence::AppendAlias(const FilePath &fullPath, - sampleCount start, - size_t len, int channel, bool useOD) -// STRONG-GUARANTEE -{ - // Quick check to make sure that it doesn't overflow - if (Overflows((mNumSamples.as_double()) + ((double)len))) - THROW_INCONSISTENCY_EXCEPTION; - - SeqBlock newBlock( - useOD? - mDirManager->NewODAliasBlockFile(fullPath, start, len, channel): - mDirManager->NewAliasBlockFile(fullPath, start, len, channel), - mNumSamples - ); - mBlock.push_back(newBlock); - mNumSamples += len; -} - -void Sequence::AppendCoded(const FilePath &fName, sampleCount start, - size_t len, int channel, int decodeType) -// STRONG-GUARANTEE -{ - // Quick check to make sure that it doesn't overflow - if (Overflows((mNumSamples.as_double()) + ((double)len))) - THROW_INCONSISTENCY_EXCEPTION; - - SeqBlock newBlock( - mDirManager->NewODDecodeBlockFile(fName, start, len, channel, decodeType), - mNumSamples - ); - mBlock.push_back(newBlock); - mNumSamples += len; -} - void Sequence::AppendBlock (DirManager &mDirManager, BlockArray &mBlock, sampleCount &mNumSamples, const SeqBlock &b) @@ -765,20 +741,6 @@ void Sequence::AppendBlock // function gets called in an inner loop. } -///gets an int with OD flags so that we can determine which ODTasks should be run on this track after save/open, etc. -unsigned int Sequence::GetODFlags() -{ - unsigned int ret = 0; - for (unsigned int i = 0; i < mBlock.size(); i++) { - const auto &file = mBlock[i].f; - if(!file->IsDataAvailable()) - ret |= (static_cast< ODDecodeBlockFile * >( &*file ))->GetDecodeType(); - else if(!file->IsSummaryAvailable()) - ret |= ODTask::eODPCMSummary; - } - return ret; -} - sampleCount Sequence::GetBlockStart(sampleCount position) const { int b = FindBlock(position); @@ -867,7 +829,9 @@ bool Sequence::HandleXMLTag(const wxChar *tag, const wxChar **attrs) } // while mBlock.push_back(wb); - mDirManager->SetLoadingTarget(&mBlock, mBlock.size() - 1); + auto index = mBlock.size() - 1; + mDirManager->SetLoadingTarget( + [this, index] () -> BlockFilePtr& { return mBlock[index].f; } ); return true; } @@ -1265,13 +1229,13 @@ void Sequence::SetSamples(samplePtr buffer, sampleFormat format, else ClearSamples(scratch.ptr(), mSampleFormat, bstart, blen); - block.f = mDirManager->NewSimpleBlockFile( + block.f = NewSimpleBlockFile( *mDirManager, scratch.ptr(), fileLength, mSampleFormat); } else { // Avoid reading the disk when the replacement is total if (useBuffer) - block.f = mDirManager->NewSimpleBlockFile( + block.f = NewSimpleBlockFile( *mDirManager, useBuffer, fileLength, mSampleFormat); else block.f = make_blockfile(fileLength); @@ -1597,7 +1561,7 @@ void Sequence::Append(samplePtr buffer, sampleFormat format, const auto newLastBlockLen = length + addLen; SeqBlock newLastBlock( - mDirManager->NewSimpleBlockFile( + NewSimpleBlockFile( *mDirManager, buffer2.ptr(), newLastBlockLen, mSampleFormat, blockFileLog != NULL ), @@ -1623,12 +1587,12 @@ void Sequence::Append(samplePtr buffer, sampleFormat format, const auto addedLen = std::min(idealSamples, len); BlockFilePtr pFile; if (format == mSampleFormat) { - pFile = mDirManager->NewSimpleBlockFile( + pFile = NewSimpleBlockFile( *mDirManager, buffer, addedLen, mSampleFormat, blockFileLog != NULL); } else { CopySamples(buffer, format, buffer2.ptr(), mSampleFormat, addedLen); - pFile = mDirManager->NewSimpleBlockFile( + pFile = NewSimpleBlockFile( *mDirManager, buffer2.ptr(), addedLen, mSampleFormat, blockFileLog != NULL); } @@ -1671,7 +1635,7 @@ void Sequence::Blockify int newLen = ((i + 1) * len / num) - offset; samplePtr bufStart = buffer + (offset * SAMPLE_SIZE(mSampleFormat)); - b.f = mDirManager.NewSimpleBlockFile(bufStart, newLen, mSampleFormat); + b.f = NewSimpleBlockFile( mDirManager, bufStart, newLen, mSampleFormat ); list.push_back(b); } @@ -1733,7 +1697,7 @@ void Sequence::Delete(sampleCount start, sampleCount len) ( pos + len ).as_size_t(), newLen - pos, true); auto newFile = - mDirManager->NewSimpleBlockFile(scratch.ptr(), newLen, mSampleFormat); + NewSimpleBlockFile( *mDirManager, scratch.ptr(), newLen, mSampleFormat ); // Don't make a duplicate array. We can still give STRONG-GUARANTEE // if we modify only one block in place. @@ -1777,7 +1741,7 @@ void Sequence::Delete(sampleCount start, sampleCount len) ensureSampleBufferSize(scratch, mSampleFormat, scratchSize, preBufferLen); Read(scratch.ptr(), mSampleFormat, preBlock, 0, preBufferLen, true); auto pFile = - mDirManager->NewSimpleBlockFile(scratch.ptr(), preBufferLen, mSampleFormat); + NewSimpleBlockFile( *mDirManager, scratch.ptr(), preBufferLen, mSampleFormat ); newBlock.push_back(SeqBlock(pFile, preBlock.start)); } else { @@ -1823,7 +1787,7 @@ void Sequence::Delete(sampleCount start, sampleCount len) auto pos = (start + len - postBlock.start).as_size_t(); Read(scratch.ptr(), mSampleFormat, postBlock, pos, postBufferLen, true); auto file = - mDirManager->NewSimpleBlockFile(scratch.ptr(), postBufferLen, mSampleFormat); + NewSimpleBlockFile( *mDirManager, scratch.ptr(), postBufferLen, mSampleFormat ); newBlock.push_back(SeqBlock(file, start)); } else { @@ -2014,6 +1978,23 @@ size_t Sequence::GetMaxDiskBlockSize() return sMaxDiskBlockSize; } +void Sequence::AppendBlockFile( const BlockFileFactory &factory, size_t len ) +// STRONG-GUARANTEE +{ + // Quick check to make sure that it doesn't overflow + if (Overflows((mNumSamples.as_double()) + ((double)len))) + THROW_INCONSISTENCY_EXCEPTION; + + SeqBlock newBlock( + mDirManager->NewBlockFile( [&]( wxFileNameWrapper filePath ){ + return factory( std::move( filePath ), len ); + } ), + mNumSamples + ); + mBlock.push_back(newBlock); + mNumSamples += len; +} + void Sequence::AppendBlockFile(const BlockFilePtr &blockFile) { // We assume blockFile has the correct ref count already diff --git a/src/Sequence.h b/src/Sequence.h index 26dbb0040..bcd0d98d7 100644 --- a/src/Sequence.h +++ b/src/Sequence.h @@ -23,6 +23,7 @@ class BlockFile; using BlockFilePtr = std::shared_ptr; class DirManager; +class wxFileNameWrapper; // This is an internal data structure! For advanced use only. class SeqBlock { @@ -107,21 +108,18 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{ void Append(samplePtr buffer, sampleFormat format, size_t len, XMLWriter* blockFileLog=NULL); void Delete(sampleCount start, sampleCount len); - void AppendAlias(const FilePath &fullPath, - sampleCount start, - size_t len, int channel, bool useOD); - void AppendCoded(const FilePath &fName, sampleCount start, - size_t len, int channel, int decodeType); - - ///gets an int with OD flags so that we can determine which ODTasks should be run on this track after save/open, etc. - unsigned int GetODFlags(); + using BlockFileFactory = + std::function< BlockFilePtr( wxFileNameWrapper, size_t /* len */ ) >; + // An overload of AppendBlockFile that passes the factory to DirManager + // which supplies it with a file name + void AppendBlockFile( const BlockFileFactory &factory, size_t len ); // Append a blockfile. The blockfile pointer is then "owned" by the // sequence. This function is used by the recording log crash recovery // code, but may be useful for other purposes. The blockfile must already // be registered within the dir manager hash. This is the case - // when the blockfile is created using DirManager::NewSimpleBlockFile or + // when the blockfile is created using SimpleBlockFile or // loaded from an XML file via DirManager::HandleXMLTag void AppendBlockFile(const BlockFilePtr &blockFile); diff --git a/src/WaveClip.cpp b/src/WaveClip.cpp index ea27a62f3..08facd7db 100644 --- a/src/WaveClip.cpp +++ b/src/WaveClip.cpp @@ -1448,30 +1448,51 @@ void WaveClip::Append(samplePtr buffer, sampleFormat format, } } +#include "blockfile/ODPCMAliasBlockFile.h" void WaveClip::AppendAlias(const FilePath &fName, sampleCount start, size_t len, int channel,bool useOD) // STRONG-GUARANTEE { // use STRONG-GUARANTEE - mSequence->AppendAlias(fName, start, len, channel,useOD); + mSequence->AppendBlockFile( + [&]( wxFileNameWrapper filePath, size_t len ) { + return useOD + ? make_blockfile( + std::move(filePath), wxFileNameWrapper{ fName }, + start, len, channel) + : make_blockfile( + std::move(filePath), wxFileNameWrapper{ fName }, + start, len, channel); + }, + len + ); // use NOFAIL-GUARANTEE UpdateEnvelopeTrackLen(); MarkChanged(); } +#include "blockfile/ODDecodeBlockFile.h" void WaveClip::AppendCoded(const FilePath &fName, sampleCount start, size_t len, int channel, int decodeType) // STRONG-GUARANTEE { // use STRONG-GUARANTEE - mSequence->AppendCoded(fName, start, len, channel, decodeType); + mSequence->AppendBlockFile( + [&]( wxFileNameWrapper filePath, size_t len ) { + return make_blockfile( + std::move(filePath), wxFileNameWrapper{ fName }, + start, len, channel, decodeType); + }, + len + ) ; // use NOFAIL-GUARANTEE UpdateEnvelopeTrackLen(); MarkChanged(); } + void WaveClip::Flush() // NOFAIL-GUARANTEE that the clip will be in a flushed state. // PARTIAL-GUARANTEE in case of exceptions: diff --git a/src/WaveTrack.cpp b/src/WaveTrack.cpp index 739013647..b4b5a8ed0 100644 --- a/src/WaveTrack.cpp +++ b/src/WaveTrack.cpp @@ -1599,12 +1599,21 @@ void WaveTrack::AppendCoded(const FilePath &fName, sampleCount start, } ///gets an int with OD flags so that we can determine which ODTasks should be run on this track after save/open, etc. +#include "blockfile/ODDecodeBlockFile.h" unsigned int WaveTrack::GetODFlags() const { unsigned int ret = 0; for (const auto &clip : mClips) { - ret = ret | clip->GetSequence()->GetODFlags(); + auto sequence = clip->GetSequence(); + const auto &blocks = sequence->GetBlockArray(); + for ( const auto &block : blocks ) { + const auto &file = block.f; + if(!file->IsDataAvailable()) + ret |= (static_cast< ODDecodeBlockFile * >( &*file ))->GetDecodeType(); + else if(!file->IsSummaryAvailable()) + ret |= ODTask::eODPCMSummary; + } } return ret; } diff --git a/src/blockfile/LegacyAliasBlockFile.cpp b/src/blockfile/LegacyAliasBlockFile.cpp index 98260a336..4b95047a7 100644 --- a/src/blockfile/LegacyAliasBlockFile.cpp +++ b/src/blockfile/LegacyAliasBlockFile.cpp @@ -17,6 +17,7 @@ #include #include "LegacyBlockFile.h" +#include "../DirManager.h" #include "../FileFormats.h" #include "../xml/XMLTagHandler.h" @@ -147,3 +148,36 @@ BlockFilePtr LegacyAliasBlockFile::BuildFromXML(const FilePath &projDir, const w void LegacyAliasBlockFile::Recover(){ WriteSummary(); } + +static const auto sFactory = []( DirManager &dm, const wxChar **attrs ){ + + // Support Audacity version 1.1.1 project files + + int i=0; + bool alias = false; + + while(attrs[i]) { + if (!wxStricmp(attrs[i], wxT("alias"))) { + if (wxAtoi(attrs[i+1])==1) + alias = true; + } + i++; + if (attrs[i]) + i++; + } + + if (alias) + return LegacyAliasBlockFile::BuildFromXML( + dm.GetProjectDataDir(), attrs); + else + return LegacyBlockFile::BuildFromXML(dm.GetProjectDataDir(), attrs, + dm.GetLoadingBlockLength(), + dm.GetLoadingFormat()); +}; + +static DirManager::RegisteredBlockFileDeserializer sRegistration1 { + "blockfile", sFactory +}; +static DirManager::RegisteredBlockFileDeserializer sRegistration2 { + "legacyblockfile", sFactory +}; diff --git a/src/blockfile/ODDecodeBlockFile.cpp b/src/blockfile/ODDecodeBlockFile.cpp index e2d498585..f9692ca2d 100644 --- a/src/blockfile/ODDecodeBlockFile.cpp +++ b/src/blockfile/ODDecodeBlockFile.cpp @@ -29,6 +29,7 @@ The summary is eventually computed and written to a file in a background thread. #include "../DirManager.h" #include "../FileFormats.h" +#include "../ondemand/ODManager.h" #include "NotYetAvailableException.h" const int bheaderTagLen = 20; @@ -502,6 +503,18 @@ void ODDecodeBlockFile::UnlockRead() const mReadDataMutex.Unlock(); } +const wxFileNameWrapper &ODDecodeBlockFile::GetExternalFileName() const +{ + if ( !IsDataAvailable() ) + return GetEncodedAudioFilename(); + return SimpleBlockFile::GetExternalFileName(); +} + +void ODDecodeBlockFile::SetExternalFileName( wxFileNameWrapper &&newName ) +{ + ChangeAudioFile( std::move( newName ) ); +} + /// Modify this block to point at a different file. This is generally /// looked down on, but it is necessary in one case: see /// DirManager::EnsureSafeFilename(). @@ -512,3 +525,11 @@ void ODDecodeBlockFile::ChangeAudioFile(wxFileNameWrapper &&newAudioFile) +static DirManager::RegisteredBlockFileDeserializer sRegistration { + "oddecodeblockfile", + []( DirManager &dm, const wxChar **attrs ){ + auto result = ODDecodeBlockFile::BuildFromXML( dm, attrs ); + ODManager::MarkLoadedODFlag(); + return result; + } +}; diff --git a/src/blockfile/ODDecodeBlockFile.h b/src/blockfile/ODDecodeBlockFile.h index e67962c5a..01f1e6513 100644 --- a/src/blockfile/ODDecodeBlockFile.h +++ b/src/blockfile/ODDecodeBlockFile.h @@ -56,6 +56,9 @@ class ODDecodeBlockFile final : public SimpleBlockFile /// Returns TRUE if the summary has not yet been written, but is actively being computed and written to disk bool IsSummaryBeingComputed() override { return false; } + const wxFileNameWrapper &GetExternalFileName() const override; + void SetExternalFileName( wxFileNameWrapper &&newName ) override; + //Calls that rely on summary files need to be overidden DiskByteCount GetSpaceUsage() const override; /// Gets extreme values for the specified region @@ -138,7 +141,7 @@ class ODDecodeBlockFile final : public SimpleBlockFile ///// Get the name of the file where the audio data for this block is /// stored. - const wxFileName &GetEncodedAudioFilename() + const wxFileNameWrapper &GetEncodedAudioFilename() const { return mAudioFileName; } diff --git a/src/blockfile/ODPCMAliasBlockFile.cpp b/src/blockfile/ODPCMAliasBlockFile.cpp index 7b698a40e..22df522ee 100644 --- a/src/blockfile/ODPCMAliasBlockFile.cpp +++ b/src/blockfile/ODPCMAliasBlockFile.cpp @@ -561,4 +561,15 @@ void ODPCMAliasBlockFile::UnlockRead() const mReadDataMutex.Unlock(); } +static DirManager::RegisteredBlockFileDeserializer sRegistration { + "odpcmaliasblockfile", + []( DirManager &dm, const wxChar **attrs ){ + auto result = ODPCMAliasBlockFile::BuildFromXML( dm, attrs ); + //in the case of loading an OD file, we need to schedule the ODManager to begin OD computing of summary + //However, because we don't have access to the track or even the Sequence from this call, we mark a flag + //in the ODMan and check it later. + ODManager::MarkLoadedODFlag(); + return result; + } +}; diff --git a/src/blockfile/PCMAliasBlockFile.cpp b/src/blockfile/PCMAliasBlockFile.cpp index 2fca965bd..19035ed6d 100644 --- a/src/blockfile/PCMAliasBlockFile.cpp +++ b/src/blockfile/PCMAliasBlockFile.cpp @@ -200,3 +200,9 @@ void PCMAliasBlockFile::Recover(void) WriteSummary(); } +static DirManager::RegisteredBlockFileDeserializer sRegistration { + "pcmaliasblockfile", + []( DirManager &dm, const wxChar **attrs ){ + return PCMAliasBlockFile::BuildFromXML( dm, attrs ); + } +}; diff --git a/src/blockfile/SilentBlockFile.cpp b/src/blockfile/SilentBlockFile.cpp index 6ace01f3a..00b3470db 100644 --- a/src/blockfile/SilentBlockFile.cpp +++ b/src/blockfile/SilentBlockFile.cpp @@ -12,6 +12,7 @@ #include "SilentBlockFile.h" #include "../FileFormats.h" +#include "../DirManager.h" #include "../xml/XMLTagHandler.h" SilentBlockFile::SilentBlockFile(size_t sampleLen): @@ -91,3 +92,9 @@ auto SilentBlockFile::GetSpaceUsage() const -> DiskByteCount return 0; } +static DirManager::RegisteredBlockFileDeserializer sRegistration { + "silentblockfile", + []( DirManager &dm, const wxChar **attrs ){ + return SilentBlockFile::BuildFromXML( dm, attrs ); + } +}; diff --git a/src/blockfile/SimpleBlockFile.cpp b/src/blockfile/SimpleBlockFile.cpp index b5e284051..42db44c88 100644 --- a/src/blockfile/SimpleBlockFile.cpp +++ b/src/blockfile/SimpleBlockFile.cpp @@ -610,3 +610,10 @@ bool SimpleBlockFile::GetCache() return false; #endif } + +static DirManager::RegisteredBlockFileDeserializer sRegistration { + "simpleblockfile", + []( DirManager &dm, const wxChar **attrs ){ + return SimpleBlockFile::BuildFromXML( dm, attrs ); + } +};