1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-02 08:39:46 +02:00

Break dependency cycles with Sequence, DirManager, BlockFile classes

This commit is contained in:
Paul Licameli 2019-05-18 20:33:24 -04:00
commit ddd5fa6bd6
20 changed files with 355 additions and 271 deletions

View File

@ -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)
{

View File

@ -359,7 +359,7 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
HoldPrint(true);
ZoomInfo zoomInfo(0.0, ZoomInfo::GetDefaultZoom());
auto dd = std::make_shared<DirManager>();
auto dd = DirManager::Create();
const auto t = TrackFactory{ dd, &zoomInfo }.NewWaveTrack(int16Sample);
t->SetRate(1);

View File

@ -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.

View File

@ -18,6 +18,8 @@
#include "ondemand/ODTaskThread.h"
#include <functional>
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

View File

@ -49,7 +49,7 @@ AliasedFile s.
#include <wx/dataobj.h>
#include <wx/stattext.h>
#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<SimpleBlockFile>(
std::move(filePath), buffer.ptr(), len, format);
} );
}
// Update our hash so we know what block files we've done

View File

@ -85,23 +85,14 @@
#include <sys/stat.h>
#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 <mach/mach.h>
#include <mach/vm_statistics.h>
@ -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> 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<DirManager> &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<SimpleBlockFile>
(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<PCMAliasBlockFile>
(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<ODPCMAliasBlockFile>
(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<ODDecodeBlockFile>
(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<DirManager> > 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 )

View File

@ -14,6 +14,7 @@
#include "audacity/Types.h"
#include "xml/XMLTagHandler.h"
#include <functional>
#include <unordered_map>
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;

View File

@ -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;

View File

@ -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<DirManager>();
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<DirManager>();
mDirManager = DirManager::Create();
mTrackFactory.reset(safenew TrackFactory{ mDirManager, &mViewInfo });
// mLastSavedTrack code copied from OnCloseWindow.

View File

@ -40,10 +40,10 @@
#include <wx/ffile.h>
#include <wx/log.h>
#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<SimpleBlockFile>(
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<SilentBlockFile>(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

View File

@ -23,6 +23,7 @@ class BlockFile;
using BlockFilePtr = std::shared_ptr<BlockFile>;
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);

View File

@ -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<ODPCMAliasBlockFile>(
std::move(filePath), wxFileNameWrapper{ fName },
start, len, channel)
: make_blockfile<PCMAliasBlockFile>(
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<ODDecodeBlockFile>(
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:

View File

@ -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;
}

View File

@ -17,6 +17,7 @@
#include <sndfile.h>
#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
};

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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;
}
};

View File

@ -200,3 +200,9 @@ void PCMAliasBlockFile::Recover(void)
WriteSummary();
}
static DirManager::RegisteredBlockFileDeserializer sRegistration {
"pcmaliasblockfile",
[]( DirManager &dm, const wxChar **attrs ){
return PCMAliasBlockFile::BuildFromXML( dm, attrs );
}
};

View File

@ -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 );
}
};

View File

@ -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 );
}
};