AUP3: First round of updates

!!! THERE WILL NO DOUBT BE BUGS !!!

This is a big one and there's still several things to
complete. Just want to get this in the wild to start
receiving feedback.

One big thing right now is that it will NOT load pre-aup3
files.  An importer is on the way for that.
This commit is contained in:
Leland Lucius 2020-07-01 00:45:17 -05:00
parent b1beb20ae9
commit d39590cf41
74 changed files with 2902 additions and 6057 deletions

View File

@ -75,7 +75,6 @@ It handles initialization and termination by subclassing wxApp.
#include "Benchmark.h"
#include "Clipboard.h"
#include "CrashReport.h"
#include "DirManager.h"
#include "commands/CommandHandler.h"
#include "commands/AppCommandEvent.h"
#include "widgets/ASlider.h"
@ -83,7 +82,6 @@ It handles initialization and termination by subclassing wxApp.
//#include "LangChoice.h"
#include "Languages.h"
#include "Menus.h"
#include "MissingAliasFileDialog.h"
#include "PluginManager.h"
#include "Project.h"
#include "ProjectAudioIO.h"
@ -104,7 +102,6 @@ It handles initialization and termination by subclassing wxApp.
#include "AutoRecoveryDialog.h"
#include "SplashDialog.h"
#include "FFT.h"
#include "BlockFile.h"
#include "widgets/AudacityMessageBox.h"
#include "prefs/DirectoriesPrefs.h"
#include "prefs/GUIPrefs.h"
@ -854,7 +851,7 @@ void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
FileHistory::Global().Clear();
}
//vvv Basically, anything from Recent Files is treated as a .aup, until proven otherwise,
//vvv Basically, anything from Recent Files is treated as a .aup3, until proven otherwise,
// then it tries to Import(). Very questionable handling, imo.
// Better, for example, to check the file type early on.
void AudacityApp::OnMRUFile(wxCommandEvent& event) {
@ -914,44 +911,6 @@ void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
}
}
}
// Check if a warning for missing aliased files should be displayed
if (MissingAliasFilesDialog::ShouldShow()) {
// find which project owns the blockfile
// note: there may be more than 1, but just go with the first one.
//size_t numProjects = AllProjects{}.size();
auto marked = MissingAliasFilesDialog::Marked();
auto offendingProject = marked.second;
wxString missingFileName = marked.first;
// if there are no projects open, don't show the warning (user has closed it)
if (offendingProject) {
auto &window = GetProjectFrame( *offendingProject );
window.Iconize(false);
window.Raise();
auto errorMessage = XO(
"One or more external audio files could not be found.\n\
It is possible they were moved, deleted, or the drive they \
were on was unmounted.\n\
Silence is being substituted for the affected audio.\n\
The first detected missing file is:\n\
%s\n\
There may be additional missing files.\n\
Choose Help > Diagnostics > Check Dependencies to view a list of \
locations of the missing files.").Format( missingFileName ) ;
// if an old dialog exists, raise it if it is
if ( auto dialog = MissingAliasFilesDialog::Find( *offendingProject ) )
dialog->Raise();
else {
MissingAliasFilesDialog::Show(offendingProject.get(), XO("Files Missing"),
errorMessage, wxT(""), true);
}
}
// Only show this warning once per event (playback/menu item/etc).
MissingAliasFilesDialog::SetShouldShow(false);
}
}
#if defined(__WXMSW__)
@ -1344,21 +1303,6 @@ bool AudacityApp::OnInit()
Sequence::SetMaxDiskBlockSize(lval);
}
wxString fileName;
if (parser->Found(wxT("d"), &fileName))
{
AutoSaveFile asf;
if (asf.Decode(fileName))
{
wxPrintf(_("File decoded successfully\n"));
}
else
{
wxPrintf( AutoSaveFile::FailureMessage( fileName ) .Translation() ); //Debug()?
}
exit(1);
}
// BG: Create a temporary window to set as the top window
wxImage logoimage((const char **)AudacityLogoWithName_xpm);
logoimage.Rescale(logoimage.GetWidth() / 2, logoimage.GetHeight() / 2);
@ -1493,8 +1437,6 @@ bool AudacityApp::OnInit()
bool didRecoverAnything = false;
if (!ShowAutoRecoveryDialogIfNeeded(&project, &didRecoverAnything))
{
// Important: Prevent deleting any temporary files!
DirManager::SetDontDeleteTempFiles();
QuitAudacity(true);
}
@ -1505,7 +1447,7 @@ bool AudacityApp::OnInit()
{
if (parser->Found(wxT("t")))
{
RunBenchmark( nullptr, ProjectSettings::Get( *project ) );
RunBenchmark( nullptr, *project);
QuitAudacity(true);
}
@ -1689,7 +1631,6 @@ bool AudacityApp::InitTempDir()
#endif
bool bSuccess = gPrefs->Write(wxT("/Directories/TempDir"), temp) && gPrefs->Flush();
DirManager::SetTempDir(temp);
// Make sure the temp dir isn't locked by another process.
if (!CreateSingleInstanceChecker(temp))
@ -1998,10 +1939,6 @@ std::unique_ptr<wxCmdLineParser> AudacityApp::ParseCommandLine()
parser->AddOption(wxT("b"), wxT("blocksize"), _("set max disk block size in bytes"),
wxCMD_LINE_VAL_NUMBER);
/*i18n-hint: This decodes an autosave file */
parser->AddOption(wxT("d"), wxT("decode"), _("decode an autosave file"),
wxCMD_LINE_VAL_STRING);
/*i18n-hint: This displays a list of available options */
parser->AddSwitch(wxT("h"), wxT("help"), _("this help message"),
wxCMD_LINE_OPTION_HELP);
@ -2217,107 +2154,154 @@ void AudacityApp::OnMenuExit(wxCommandEvent & event)
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
void AudacityApp::AssociateFileTypes()
{
wxRegKey associateFileTypes;
associateFileTypes.SetName(wxT("HKCR\\.AUP"));
bool bKeyExists = associateFileTypes.Exists();
if (!bKeyExists) {
// Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
associateFileTypes.SetName(wxT("HKCU\\Software\\Classes\\.AUP"));
bKeyExists = associateFileTypes.Exists();
// Check pref in case user has already decided against it.
bool bWantAssociateFiles = true;
if (gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) &&
!bWantAssociateFiles)
{
// User has already decided against it
return;
}
if (!bKeyExists) {
// File types are not currently associated.
// Check pref in case user has already decided against it.
bool bWantAssociateFiles = true;
if (!gPrefs->Read(wxT("/WantAssociateFiles"), &bWantAssociateFiles) ||
bWantAssociateFiles) {
// Either there's no pref or user does want associations
// and they got stepped on, so ask.
int wantAssoc =
AudacityMessageBox(
XO(
"Audacity project (.AUP) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
XO("Audacity Project Files"),
wxYES_NO | wxICON_QUESTION);
if (wantAssoc == wxYES) {
gPrefs->Write(wxT("/WantAssociateFiles"), true);
gPrefs->Flush();
wxString root_key;
wxRegKey associateFileTypes;
root_key = wxT("HKCU\\Software\\Classes\\");
associateFileTypes.SetName(root_key + wxT(".AUP")); // Start again with HKEY_CLASSES_ROOT.
if (!associateFileTypes.Create(true)) {
// Not at HKEY_CLASSES_USER. Try HKEY_CURRENT_ROOT.
root_key = wxT("HKCR\\");
associateFileTypes.SetName(root_key + wxT(".AUP"));
if (!associateFileTypes.Create(true)) {
// Actually, can't create keys. Empty root_key to flag failure.
root_key.Empty();
}
}
if (root_key.empty()) {
//v Warn that we can't set keys. Ask whether to set pref for no retry?
} else {
associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
auto IsDefined = [&](const wxString &type)
{
associateFileTypes.SetName(wxString::Format(wxT("HKCR\\%s"), type));
bool bKeyExists = associateFileTypes.Exists();
if (!bKeyExists)
{
// Not at HKEY_CLASSES_ROOT. Try HKEY_CURRENT_USER.
associateFileTypes.SetName(wxString::Format(wxT("HKCU\\Software\\Classes\\%s"), type));
bKeyExists = associateFileTypes.Exists();
}
return bKeyExists;
};
associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
associateFileTypes = wxT("Audacity Project File");
}
auto DefineType = [&](const wxString &type)
{
wxString root_key = wxT("HKCU\\Software\\Classes\\");
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
associateFileTypes = wxT("");
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
wxString tmpRegAudPath;
if(associateFileTypes.Exists()) {
tmpRegAudPath = associateFileTypes.QueryDefaultValue().Lower();
}
if (!associateFileTypes.Exists() ||
(tmpRegAudPath.Find(wxT("audacity.exe")) >= 0)) {
associateFileTypes.Create(true);
associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
}
#if 0
// These can be use later to support more startup messages
// like maybe "Import into existing project" or some such.
// Leaving here for an example...
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
associateFileTypes = wxT("%1");
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
associateFileTypes = IPC_APPL;
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
if(!associateFileTypes.Exists()) {
associateFileTypes.Create(true);
associateFileTypes = IPC_TOPIC;
}
#endif
}
} else {
// User said no. Set a pref so we don't keep asking.
gPrefs->Write(wxT("/WantAssociateFiles"), false);
gPrefs->Flush();
// Start with HKEY_CLASSES_CURRENT_USER.
associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
if (!associateFileTypes.Create(true))
{
// Not at HKEY_CLASSES_CURRENT_USER. Try HKEY_CURRENT_ROOT.
root_key = wxT("HKCR\\");
associateFileTypes.SetName(wxString::Format(wxT("%s%s"), root_key, type));
if (!associateFileTypes.Create(true))
{
// Actually, can't create keys. Empty root_key to flag failure.
root_key.empty();
}
}
if (!root_key.empty())
{
associateFileTypes = wxT("Audacity.Project"); // Finally set value for the key
}
return root_key;
};
// Check for legacy and UP types
if (IsDefined(wxT(".aup3")) && IsDefined(wxT(".aup")) && IsDefined(wxT("Audacity.Project")))
{
// Already defined, so bail
return;
}
// File types are not currently associated.
int wantAssoc =
AudacityMessageBox(
XO(
"Audacity project (.AUP) files are not currently \nassociated with Audacity. \n\nAssociate them, so they open on double-click?"),
XO("Audacity Project Files"),
wxYES_NO | wxICON_QUESTION);
if (wantAssoc == wxNO)
{
// User said no. Set a pref so we don't keep asking.
gPrefs->Write(wxT("/WantAssociateFiles"), false);
gPrefs->Flush();
return;
}
// Show that user wants associations
gPrefs->Write(wxT("/WantAssociateFiles"), true);
gPrefs->Flush();
wxString root_key;
root_key = DefineType(wxT(".aup3"));
if (root_key.empty())
{
//v Warn that we can't set keys. Ask whether to set pref for no retry?
}
else
{
DefineType(wxT(".aup"));
associateFileTypes = wxT("Audacity.Project"); // Finally set value for .AUP key
associateFileTypes.SetName(root_key + wxT("Audacity.Project"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
associateFileTypes = wxT("Audacity Project File");
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
associateFileTypes = wxT("");
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\command"));
wxString tmpRegAudPath;
if(associateFileTypes.Exists())
{
tmpRegAudPath = associateFileTypes.QueryDefaultValue().Lower();
}
if (!associateFileTypes.Exists() ||
(tmpRegAudPath.Find(wxT("audacity.exe")) >= 0))
{
associateFileTypes.Create(true);
associateFileTypes = (wxString)argv[0] + (wxString)wxT(" \"%1\"");
}
#if 0
// These can be use later to support more startup messages
// like maybe "Import into existing project" or some such.
// Leaving here for an example...
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
associateFileTypes = wxT("%1");
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Application"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
associateFileTypes = IPC_APPL;
}
associateFileTypes.SetName(root_key + wxT("Audacity.Project\\shell\\open\\ddeexec\\Topic"));
if (!associateFileTypes.Exists())
{
associateFileTypes.Create(true);
associateFileTypes = IPC_TOPIC;
}
#endif
}
}
#endif

View File

@ -43,9 +43,7 @@
#include "AColor.h"
#include "AudioIO.h"
#include "BlockFile.h"
#include "Diags.h"
#include "DirManager.h"
#include "Envelope.h"
#include "FFT.h"
#include "FileFormats.h"

View File

@ -455,7 +455,6 @@ time warp info and AudioIOListener and whether the playback is looped.
#include <wx/power.h>
#endif
#include "MissingAliasFileDialog.h"
#include "Mix.h"
#include "Resample.h"
#include "RingBuffer.h"
@ -1824,9 +1823,6 @@ int AudioIO::StartStream(const TransportTracks &tracks,
wxTheApp->ProcessEvent(e);
}
// Enable warning popups for unfound aliased blockfiles.
MissingAliasFilesDialog::SetShouldShow(true);
commit = true;
return mStreamToken;
}
@ -2900,14 +2896,12 @@ void AudioIO::FillBuffers()
{
// Append captured samples to the end of the WaveTracks.
// The WaveTracks have their own buffering for efficiency.
AutoSaveFile blockFileLog;
auto numChannels = mCaptureTracks.size();
for( i = 0; i < numChannels; i++ )
{
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
AutoSaveFile appendLog;
size_t discarded = 0;
if (!mRecordingSchedule.mLatencyCorrected) {
@ -2919,8 +2913,7 @@ void AudioIO::FillBuffers()
size_t size = floor( correction * mRate * mFactor);
SampleBuffer temp(size, trackFormat);
ClearSamples(temp.ptr(), trackFormat, 0, size);
mCaptureTracks[i]->Append(temp.ptr(), trackFormat,
size, 1, &appendLog);
mCaptureTracks[i]->Append(temp.ptr(), trackFormat, size, 1);
}
else {
// Leftward shift
@ -3022,19 +3015,7 @@ void AudioIO::FillBuffers()
// Now append
// see comment in second handler about guarantee
mCaptureTracks[i]->Append(temp.ptr(), format,
size, 1,
&appendLog);
if (!appendLog.IsEmpty())
{
blockFileLog.StartTag(wxT("recordingrecovery"));
blockFileLog.WriteAttr(wxT("id"), mCaptureTracks[i]->GetAutoSaveIdent());
blockFileLog.WriteAttr(wxT("channel"), (int)i);
blockFileLog.WriteAttr(wxT("numchannels"), numChannels);
blockFileLog.WriteSubTree(appendLog);
blockFileLog.EndTag(wxT("recordingrecovery"));
}
mCaptureTracks[i]->Append(temp.ptr(), format, size, 1);
} // end loop over capture channels
// Now update the recording shedule position
@ -3042,8 +3023,8 @@ void AudioIO::FillBuffers()
mRecordingSchedule.mLatencyCorrected = latencyCorrected;
auto pListener = GetListener();
if (pListener && !blockFileLog.IsEmpty())
pListener->OnAudioIONewBlockFiles(blockFileLog);
if (pListener)
pListener->OnAudioIONewBlockFiles(&mCaptureTracks);
}
// end of record buffering
},

View File

@ -27,7 +27,7 @@ public:
virtual void OnAudioIOStartRecording() = 0;
virtual void OnAudioIOStopRecording() = 0;
virtual void OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog) = 0;
virtual void OnAudioIONewBlockFiles(const WaveTrackArray *tracks) = 0;
// Commit the addition of temporary recording tracks into the project
virtual void OnCommitRecording() = 0;

File diff suppressed because it is too large Load Diff

View File

@ -18,39 +18,10 @@
#include <unordered_map>
#include "audacity/Types.h"
class wxFFile;
class AudacityProject;
//
// XML Handler for a <recordingrecovery> tag
//
class RecordingRecoveryHandler final : public XMLTagHandler
{
public:
RecordingRecoveryHandler(AudacityProject* proj);
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
void HandleXMLEndTag(const wxChar *tag) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
// This class only knows reading tags
private:
int FindTrack() const;
AudacityProject* mProject;
int mChannel;
int mNumChannels;
int mAutoSaveIdent;
};
///
/// AutoSaveFile
///
// Should be plain ASCII
#define AutoSaveIdent "<?xml autosave>"
using NameMap = std::unordered_map<wxString, short>;
using IdMap = std::unordered_map<short, wxString>;
@ -87,20 +58,23 @@ public:
bool Write(wxFFile & file) const;
bool Append(wxFFile & file) const;
bool IsEmpty() const;
const wxMemoryBuffer &GetDict() const;
const wxMemoryBuffer &GetData() const;
bool Decode(const FilePath & fileName);
bool IsEmpty() const;
bool DictChanged() const;
static wxString Decode(const wxMemoryBuffer &buffer);
private:
void WriteName(const wxString & name);
void CheckSpace(wxMemoryOutputStream & buf);
private:
wxMemoryOutputStream mBuffer;
wxMemoryOutputStream mDict;
NameMap mNames;
size_t mAllocSize;
wxMemoryBuffer mBuffer;
bool mDictChanged;
static NameMap mNames;
static wxMemoryBuffer mDict;
};
#endif

View File

@ -31,26 +31,28 @@ enum {
class AutoRecoveryDialog final : public wxDialogWrapper
{
public:
AutoRecoveryDialog(wxWindow *parent);
AutoRecoveryDialog(const FilePaths &files);
private:
void PopulateOrExchange(ShuttleGui &S);
void PopulateList();
void PopulateOrExchange(ShuttleGui & S);
void OnQuitAudacity(wxCommandEvent &evt);
void OnRecoverNone(wxCommandEvent &evt);
void OnRecoverAll(wxCommandEvent &evt);
const FilePaths & mFiles;
wxListCtrl *mFileList;
public:
DECLARE_EVENT_TABLE()
};
AutoRecoveryDialog::AutoRecoveryDialog(wxWindow *parent) :
wxDialogWrapper(parent, -1, XO("Automatic Crash Recovery"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)) // no close box
AutoRecoveryDialog::AutoRecoveryDialog(const FilePaths &files)
: wxDialogWrapper(nullptr, -1, XO("Automatic Crash Recovery"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE & (~wxCLOSE_BOX)), // no close box
mFiles(files)
{
SetName();
ShuttleGui S(this, eIsCreating);
@ -63,14 +65,13 @@ BEGIN_EVENT_TABLE(AutoRecoveryDialog, wxDialogWrapper)
EVT_BUTTON(ID_QUIT_AUDACITY, AutoRecoveryDialog::OnQuitAudacity)
END_EVENT_TABLE()
void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui& S)
void AutoRecoveryDialog::PopulateOrExchange(ShuttleGui &S)
{
S.SetBorder(5);
S.StartVerticalLay();
{
S.AddVariableText(
XO(
"Some projects were not saved properly the last time Audacity was run.\nFortunately, the following projects can be automatically recovered:"),
XO("Some projects were not saved properly the last time Audacity was run.\nFortunately, the following projects can be automatically recovered:"),
false);
S.StartStatic(XO("Recoverable projects"));
@ -110,15 +111,10 @@ void AutoRecoveryDialog::PopulateList()
{
mFileList->DeleteAllItems();
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
return;
wxString filename;
int i = 0;
for (bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
c; c = dir.GetNext(&filename))
mFileList->InsertItem(i++, wxFileName{ filename }.GetName());
for (int i = 0, cnt = mFiles.size(); i < cnt; ++i)
{
mFileList->InsertItem(i, wxFileName{ mFiles[i] }.GetName());
}
mFileList->SetColumnWidth(0, wxLIST_AUTOSIZE);
}
@ -131,8 +127,7 @@ void AutoRecoveryDialog::OnQuitAudacity(wxCommandEvent & WXUNUSED(event))
void AutoRecoveryDialog::OnRecoverNone(wxCommandEvent & WXUNUSED(event))
{
int ret = AudacityMessageBox(
XO(
"Are you sure you want to discard all recoverable projects?\n\nChoosing \"Yes\" discards all recoverable projects immediately."),
XO("Are you sure you want to discard all recoverable projects?\n\nChoosing \"Yes\" discards all recoverable projects immediately."),
XO("Confirm Discard Projects"),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
@ -147,69 +142,54 @@ void AutoRecoveryDialog::OnRecoverAll(wxCommandEvent & WXUNUSED(event))
////////////////////////////////////////////////////////////////////////////
static bool HaveFilesToRecover()
static FilePaths HaveFilesToRecover()
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
{
AudacityMessageBox(
XO("Could not enumerate files in auto save directory."),
XO("Error"),
wxICON_STOP);
return false;
}
wxString tempdir = FileNames::TempDir();
wxString pattern = wxT("*.") + FileNames::UnsavedProjectExtension();
FilePaths files;
wxString filename;
bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
wxDir::GetAllFiles(tempdir, &files, pattern, wxDIR_FILES);
return c;
return files;
}
static bool RemoveAllAutoSaveFiles()
static bool RemoveAllAutoSaveFiles(const FilePaths &files)
{
FilePaths files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.size(); i++)
for (int i = 0, cnt = files.size(); i < cnt; ++i)
{
if (!wxRemoveFile(files[i]))
FilePath file = files[i];
if (wxRemoveFile(file))
{
// I don't think this error message is actually useful.
// -dmazzoni
//AudacityMessageBox(
// XO("Could not remove auto save file: %s".Format( files[i] ),
// XO("Error"),
// wxICON_STOP);
return false;
if (wxFileExists(file + wxT("-shm")))
{
wxRemoveFile(file + wxT("-shm"));
}
if (wxFileExists(file + wxT("-wal")))
{
wxRemoveFile(file + wxT("-wal"));
}
if (wxFileExists(file + wxT("-journal")))
{
wxRemoveFile(file + wxT("-journal"));
}
}
}
return true;
}
static bool RecoverAllProjects(AudacityProject** pproj)
static bool RecoverAllProjects(const FilePaths &files,
AudacityProject **pproj)
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
{
AudacityMessageBox(
XO("Could not enumerate files in auto save directory."),
XO("Error"),
wxICON_STOP);
return false;
}
// Open a project window for each auto save file
wxString filename;
FilePaths files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.size(); i++)
for (int i = 0, cnt = files.size(); i < cnt; ++i)
{
AudacityProject* proj{};
AudacityProject *proj = nullptr;
if (*pproj)
{
// Reuse existing project window
@ -220,18 +200,20 @@ static bool RecoverAllProjects(AudacityProject** pproj)
// Open project. When an auto-save file has been opened successfully,
// the opened auto-save file is automatically deleted and a NEW one
// is created.
(void) ProjectManager::OpenProject( proj, files[i], false );
(void) ProjectManager::OpenProject(proj, files[i], false);
}
return true;
}
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject** pproj,
bool ShowAutoRecoveryDialogIfNeeded(AudacityProject **pproj,
bool *didRecoverAnything)
{
if (didRecoverAnything)
*didRecoverAnything = false;
if (HaveFilesToRecover())
FilePaths files = HaveFilesToRecover();
if (files.size())
{
// Under wxGTK3, the auto recovery dialog will not get
// the focus since the project window hasn't been allowed
@ -247,17 +229,17 @@ bool ShowAutoRecoveryDialogIfNeeded(AudacityProject** pproj,
// This must be done before "dlg" is declared.
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI);
int ret = AutoRecoveryDialog{nullptr}.ShowModal();
int ret = AutoRecoveryDialog(files).ShowModal();
switch (ret)
{
case ID_RECOVER_NONE:
return RemoveAllAutoSaveFiles();
return RemoveAllAutoSaveFiles(files);
case ID_RECOVER_ALL:
if (didRecoverAnything)
*didRecoverAnything = true;
return RecoverAllProjects(pproj);
return RecoverAllProjects(files, pproj);
default:
// This includes ID_QUIT_AUDACITY

View File

@ -25,6 +25,7 @@ processing. See also MacrosWindow and ApplyMacroDialog.
#include "Project.h"
#include "ProjectAudioManager.h"
#include "ProjectFileIO.h"
#include "ProjectHistory.h"
#include "ProjectSettings.h"
#include "ProjectWindow.h"
@ -631,6 +632,7 @@ bool MacroCommands::ApplySpecialCommand(
return true;
AudacityProject *project = &mProject;
auto &projectFileIO = ProjectFileIO::Get( *project );
unsigned numChannels = 1; //used to switch between mono and stereo export
if (IsMono( &mProject )) {
@ -650,7 +652,7 @@ bool MacroCommands::ApplySpecialCommand(
else extension = wxT("mp3");
if (mFileName.empty()) {
filename = BuildCleanFileName(project->GetFileName(), extension);
filename = BuildCleanFileName(projectFileIO.GetFileName(), extension);
}
else {
filename = BuildCleanFileName(mFileName, extension);

View File

@ -33,7 +33,6 @@ of the BlockFile system.
#include <wx/valtext.h>
#include <wx/intl.h>
#include "DirManager.h"
#include "ShuttleGui.h"
#include "Project.h"
#include "WaveClip.h"
@ -51,7 +50,7 @@ class BenchmarkDialog final : public wxDialogWrapper
{
public:
// constructors and destructors
BenchmarkDialog( wxWindow *parent, const ProjectSettings &settings );
BenchmarkDialog( wxWindow *parent, AudacityProject &project );
void MakeBenchmarkDialog();
@ -66,6 +65,7 @@ private:
void HoldPrint(bool hold);
void FlushPrint();
AudacityProject &mProject;
const ProjectSettings &mSettings;
bool mHoldPrint;
@ -85,7 +85,7 @@ private:
DECLARE_EVENT_TABLE()
};
void RunBenchmark( wxWindow *parent, const ProjectSettings &settings )
void RunBenchmark( wxWindow *parent, AudacityProject &project )
{
/*
int action = AudacityMessageBox(
@ -101,7 +101,7 @@ XO("This will close all project windows (without saving)\nand open the Audacity
GetProjectFrame( *pProject ).Close();
*/
BenchmarkDialog dlog{ parent, settings };
BenchmarkDialog dlog{ parent, project };
dlog.CentreOnParent();
@ -131,14 +131,15 @@ BEGIN_EVENT_TABLE(BenchmarkDialog, wxDialogWrapper)
END_EVENT_TABLE()
BenchmarkDialog::BenchmarkDialog(
wxWindow *parent, const ProjectSettings &settings)
wxWindow *parent, AudacityProject &project)
:
/* i18n-hint: Benchmark means a software speed test */
wxDialogWrapper( parent, 0, XO("Benchmark"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE |
wxRESIZE_BORDER)
, mSettings{ settings }
, mProject(project)
, mSettings{ ProjectSettings::Get(project) }
{
SetName();
@ -366,9 +367,10 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
HoldPrint(true);
ZoomInfo zoomInfo(0.0, ZoomInfo::GetDefaultZoom());
auto dd = DirManager::Create();
const auto t =
TrackFactory{ mSettings, dd, &zoomInfo }.NewWaveTrack(int16Sample);
TrackFactory{ mSettings,
mProject,
&zoomInfo }.NewWaveTrack(int16Sample);
t->SetRate(1);
@ -570,8 +572,6 @@ void BenchmarkDialog::OnRun( wxCommandEvent & WXUNUSED(event))
success:
dd.reset();
Printf( XO("Benchmark completed successfully.\n") );
HoldPrint(false);
}

View File

@ -11,8 +11,8 @@
#ifndef __AUDACITY_BENCHMARK__
#define __AUDACITY_BENCHMARK__
class ProjectSettings;
class AudacityProject;
void RunBenchmark( wxWindow *parent, const ProjectSettings &settings );
void RunBenchmark( wxWindow *parent, AudacityProject &project );
#endif // define __AUDACITY_BENCHMARK__

View File

@ -1,824 +0,0 @@
/**********************************************************************
Audacity: A Digital Audio Editor
BlockFile.cpp
Joshua Haberman
Dominic Mazzoni
*******************************************************************//*!
\class BlockFile
\brief A BlockFile is a chunk of immutable audio data.
A BlockFile represents a chunk of audio data. These chunks are
assembled into sequences by the class Sequence. These classes
are at the heart of how Audacity stores audio data.
BlockFile is an abstract base class that can be implemented in
many different ways. However it does have a fairly large amount
of shared code that deals with the physical file and manipulating
the summary data.
BlockFile should be thought of as an immutable class. After it
is constructed, it is essentially never changed (though there are
a few exceptions). Most notably, the audio data and summary data
are never altered once it is constructed. This is important to
some of the derived classes that are actually aliases to audio
data stored in existing files.
BlockFiles are managed by std::shared_ptr, so they are reference-counted.
*//****************************************************************//**
\class SummaryInfo
\brief Works with BlockFile to hold info about max and min and RMS
over multiple samples, which in turn allows rapid drawing when zoomed
out.
*//*******************************************************************/
#include "Audacity.h"
#include "BlockFile.h"
#include <float.h>
#include <cmath>
#include <wx/utils.h>
#include <wx/filefn.h>
#include <wx/ffile.h>
#include <wx/log.h>
#include "sndfile.h"
#include "FileException.h"
#include "FileFormats.h"
// msmeyer: Define this to add debug output via wxPrintf()
//#define DEBUG_BLOCKFILE
#ifdef DEBUG_BLOCKFILE
#define BLOCKFILE_DEBUG_OUTPUT(op, i) \
wxPrintf(wxT("[BlockFile %x %s] %s: %i\n"), (unsigned)this, \
mFileName.GetFullName(), wxT(op), i);
#else
#define BLOCKFILE_DEBUG_OUTPUT(op, i)
#endif
static const int headerTagLen = 20;
static char headerTag[headerTagLen + 1] = "AudacityBlockFile112";
SummaryInfo::SummaryInfo(size_t samples)
{
format = floatSample;
fields = 3; /* min, max, rms */
bytesPerFrame = sizeof(float) * fields;
frames64K = (samples + 65535) / 65536;
frames256 = frames64K * 256;
offset64K = headerTagLen;
offset256 = offset64K + (frames64K * bytesPerFrame);
totalSummaryBytes = offset256 + (frames256 * bytesPerFrame);
}
ArrayOf<char> BlockFile::fullSummary;
/// Initializes the base BlockFile data. The block is initially
/// unlocked and its reference count is 1.
///
/// @param fileName The name of the disk file associated with this
/// BlockFile. Not all BlockFiles will store their
/// sample data here (for example, AliasBlockFiles
/// read their data from elsewhere), but all BlockFiles
/// will store at least the summary data here.
///
/// @param samples The number of samples this BlockFile contains.
BlockFile::BlockFile(wxFileNameWrapper &&fileName, size_t samples):
mLockCount(0),
mFileName(std::move(fileName)),
mLen(samples),
mSummaryInfo(samples)
{
mSilentLog=FALSE;
}
// static
unsigned long BlockFile::gBlockFileDestructionCount { 0 };
BlockFile::~BlockFile()
{
if (!IsLocked() && mFileName.HasName())
// PRL: what should be done if this fails?
wxRemoveFile(mFileName.GetFullPath());
++gBlockFileDestructionCount;
}
/// Returns the file name of the disk file associated with this
/// BlockFile. Not all BlockFiles store their sample data here,
/// but most BlockFiles have at least their summary data here.
/// (some, i.e. SilentBlockFiles, do not correspond to a file on
/// disk and have empty file names)
auto BlockFile::GetFileName() const -> GetFileNameResult
{
return { mFileName };
}
///sets the file name the summary info will be saved in. threadsafe.
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
/// it from disappearing if the project is saved in a different location.
/// When doing a "Save As," Audacity locks all blocks belonging
/// to the already-existing project, to ensure that the existing
/// project remains valid with all the blocks it needs. Audacity
/// also locks the blocks of the last saved version of a project when
/// the project is deleted so that the files aren't deleted when their
/// refcount hits zero.
void BlockFile::Lock()
{
mLockCount++;
BLOCKFILE_DEBUG_OUTPUT("Lock", mLockCount);
}
/// Marks this BlockFile as "unlocked."
void BlockFile::Unlock()
{
mLockCount--;
BLOCKFILE_DEBUG_OUTPUT("Unlock", mLockCount);
}
/// Returns true if the block is locked.
bool BlockFile::IsLocked()
{
return mLockCount > 0;
}
/// Get a buffer containing a summary block describing this sample
/// data. This must be called by derived classes when they
/// are constructed, to allow them to construct their summary data,
/// after which they should write that data to their disk file.
///
/// This method also has the side effect of setting the mMin, mMax,
/// and mRMS members of this class.
///
/// You must not DELETE the returned buffer; it is static to this
/// method.
///
/// @param buffer A buffer containing the sample data to be analyzed
/// @param len The length of the sample data
/// @param format The format of the sample data.
void *BlockFile::CalcSummary(samplePtr buffer, size_t len,
sampleFormat format, ArrayOf<char> &cleanup)
{
// Caller has nothing to deallocate
cleanup.reset();
fullSummary.reinit(mSummaryInfo.totalSummaryBytes);
memcpy(fullSummary.get(), headerTag, headerTagLen);
float *summary64K = (float *)(fullSummary.get() + mSummaryInfo.offset64K);
float *summary256 = (float *)(fullSummary.get() + mSummaryInfo.offset256);
Floats fbuffer{ len };
CopySamples(buffer, format,
(samplePtr)fbuffer.get(), floatSample, len);
CalcSummaryFromBuffer(fbuffer.get(), len, summary256, summary64K);
return fullSummary.get();
}
void BlockFile::CalcSummaryFromBuffer(const float *fbuffer, size_t len,
float *summary256, float *summary64K)
{
decltype(len) sumLen;
float min, max;
float sumsq;
double totalSquares = 0.0;
double fraction { 0.0 };
// Recalc 256 summaries
sumLen = (len + 255) / 256;
int summaries = 256;
for (decltype(sumLen) i = 0; i < sumLen; i++) {
min = fbuffer[i * 256];
max = fbuffer[i * 256];
sumsq = ((float)min) * ((float)min);
decltype(len) jcount = 256;
if (jcount > len - i * 256) {
jcount = len - i * 256;
fraction = 1.0 - (jcount / 256.0);
}
for (decltype(jcount) j = 1; j < jcount; j++) {
float f1 = fbuffer[i * 256 + j];
sumsq += ((float)f1) * ((float)f1);
if (f1 < min)
min = f1;
else if (f1 > max)
max = f1;
}
totalSquares += sumsq;
float rms = (float)sqrt(sumsq / jcount);
summary256[i * 3] = min;
summary256[i * 3 + 1] = max;
summary256[i * 3 + 2] = rms; // The rms is correct, but this may be for less than 256 samples in last loop.
}
for (auto i = sumLen; i < mSummaryInfo.frames256; i++) {
// filling in the remaining bits with non-harming/contributing values
// rms values are not "non-harming", so keep count of them:
summaries--;
summary256[i * 3] = FLT_MAX; // min
summary256[i * 3 + 1] = -FLT_MAX; // max
summary256[i * 3 + 2] = 0.0f; // rms
}
// Calculate now while we can do it accurately
mRMS = sqrt(totalSquares/len);
// Recalc 64K summaries
sumLen = (len + 65535) / 65536;
for (decltype(sumLen) i = 0; i < sumLen; i++) {
min = summary256[3 * i * 256];
max = summary256[3 * i * 256 + 1];
sumsq = (float)summary256[3 * i * 256 + 2];
sumsq *= sumsq;
for (decltype(len) j = 1; j < 256; j++) { // we can overflow the useful summary256 values here, but have put non-harmful values in them
if (summary256[3 * (i * 256 + j)] < min)
min = summary256[3 * (i * 256 + j)];
if (summary256[3 * (i * 256 + j) + 1] > max)
max = summary256[3 * (i * 256 + j) + 1];
float r1 = summary256[3 * (i * 256 + j) + 2];
sumsq += r1*r1;
}
double denom = (i < sumLen - 1) ? 256.0 : summaries - fraction;
float rms = (float)sqrt(sumsq / denom);
summary64K[i * 3] = min;
summary64K[i * 3 + 1] = max;
summary64K[i * 3 + 2] = rms;
}
for (auto i = sumLen; i < mSummaryInfo.frames64K; i++) {
wxASSERT_MSG(false, wxT("Out of data for mSummaryInfo")); // Do we ever get here?
summary64K[i * 3] = 0.0f; // probably should be FLT_MAX, need a test case
summary64K[i * 3 + 1] = 0.0f; // probably should be -FLT_MAX, need a test case
summary64K[i * 3 + 2] = 0.0f; // just padding
}
// Recalc block-level summary (mRMS already calculated)
min = summary64K[0];
max = summary64K[1];
for (decltype(sumLen) i = 1; i < sumLen; i++) {
if (summary64K[3*i] < min)
min = summary64K[3*i];
if (summary64K[3*i+1] > max)
max = summary64K[3*i+1];
}
mMin = min;
mMax = max;
}
static void ComputeMinMax256(float *summary256,
float *outMin, float *outMax, int *outBads)
{
float min, max;
int i;
int bad = 0;
min = 1.0;
max = -1.0;
for(i=0; i<256; i++) {
if (summary256[3*i] < min)
min = summary256[3*i];
else if (!(summary256[3*i] >= min))
bad++;
if (summary256[3*i+1] > max)
max = summary256[3*i+1];
else if (!(summary256[3*i+1] <= max))
bad++;
if (std::isnan(summary256[3*i+2]))
bad++;
if (summary256[3*i+2] < -1 || summary256[3*i+2] > 1)
bad++;
}
*outMin = min;
*outMax = max;
*outBads = bad;
}
/// Byte-swap the summary data, in case it was saved by a system
/// on a different platform
void BlockFile::FixSummary(void *data)
{
if (mSummaryInfo.format != floatSample ||
mSummaryInfo.fields != 3)
return;
// These pointers point into extra 'unused' preamble space in the
// .au file. WE are using that space for summary data.
float *summary64K = (float *)((char *)data + mSummaryInfo.offset64K);
float *summary256 = (float *)((char *)data + mSummaryInfo.offset256);
float min, max;
int bad;
// Bug 2433 fix
// Previosuly we computed min and max, and a discrepancy between 64K
// summary and 256 sample summary contributed towards indicating an
// error (a byte swapped .au file, probably from a machine
// with the opposite architecture).
//
// However, that min-max test was incorrect on two counts:
// 1. Both the 256 and 64K data streams are byte-swapped or not
// together - so a consistency check served no useful purpose.
// 2. On 64 bit architecture with function inlining, calculations
// could end up being done at higher precision on one variable,
// resulting in a spurious mismatch. Exact comparison of floats
// can be problematic.
//
// Point 2 led to bug 2433 when functions were inlined, and .au
// files not being byte swapped when they should be.
//
// The fix is to not use the min max in the consistency test, and just
// use the 'bad' count which is an integer. We aim for no bad samples.
ComputeMinMax256(summary256, &min, &max, &bad);
if ( bad > 0) {
unsigned int *buffer = (unsigned int *)data;
auto len = mSummaryInfo.totalSummaryBytes / 4;
for(unsigned int i=0; i<len; i++)
buffer[i] = wxUINT32_SWAP_ALWAYS(buffer[i]);
ComputeMinMax256(summary256, &min, &max, &bad);
if ( bad == 0) {
// The byte-swapping worked!
return;
}
// Hmmm, no better, we should swap back
for(unsigned i=0; i<len; i++)
buffer[i] = wxUINT32_SWAP_ALWAYS(buffer[i]);
}
}
/// Retrieves the minimum, maximum, and maximum RMS of the
/// specified sample data in this block.
///
/// @param start The offset in this block where the region should begin
/// @param len The number of samples to include in the region
auto BlockFile::GetMinMaxRMS(size_t start, size_t len, bool mayThrow)
const -> MinMaxRMS
{
// TODO: actually use summaries
SampleBuffer blockData(len, floatSample);
this->ReadData(blockData.ptr(), floatSample, start, len, mayThrow);
float min = FLT_MAX;
float max = -FLT_MAX;
float sumsq = 0;
for( decltype(len) i = 0; i < len; i++ )
{
float sample = ((float*)blockData.ptr())[i];
if( sample > max )
max = sample;
if( sample < min )
min = sample;
sumsq += (sample*sample);
}
return { min, max, (float)sqrt(sumsq/len) };
}
/// Retrieves the minimum, maximum, and maximum RMS of this entire
/// block. This is faster than the other GetMinMax function since
/// these values are already computed.
auto BlockFile::GetMinMaxRMS(bool)
const -> MinMaxRMS
{
return { mMin, mMax, mRMS };
}
/// Retrieves a portion of the 256-byte summary buffer from this BlockFile. This
/// data provides information about the minimum value, the maximum
/// value, and the maximum RMS value for every group of 256 samples in the
/// file.
/// Fill with zeroes and return false if data are unavailable for any reason.
///
///
/// @param *buffer The area where the summary information will be
/// written. It must be at least len*3 long.
/// @param start The offset in 256-sample increments
/// @param len The number of 256-sample summary frames to read
bool BlockFile::Read256(float *buffer,
size_t start, size_t len)
{
wxASSERT(start >= 0);
ArrayOf< char > summary;
// In case of failure, summary is filled with zeroes
auto result = this->ReadSummary(summary);
start = std::min( start, mSummaryInfo.frames256 );
len = std::min( len, mSummaryInfo.frames256 - start );
CopySamples(summary.get() + mSummaryInfo.offset256 +
(start * mSummaryInfo.bytesPerFrame),
mSummaryInfo.format,
(samplePtr)buffer, floatSample, len * mSummaryInfo.fields);
if (mSummaryInfo.fields == 2) {
// No RMS info; make guess
for(auto i = len; i--;) {
buffer[3*i+2] = (fabs(buffer[2*i]) + fabs(buffer[2*i+1]))/4.0;
buffer[3*i+1] = buffer[2*i+1];
buffer[3*i] = buffer[2*i];
}
}
return result;
}
/// Retrieves a portion of the 64K summary buffer from this BlockFile. This
/// data provides information about the minimum value, the maximum
/// value, and the maximum RMS value for every group of 64K samples in the
/// file.
/// Fill with zeroes and return false if data are unavailable for any reason.
///
/// @param *buffer The area where the summary information will be
/// written. It must be at least len*3 long.
/// @param start The offset in 64K-sample increments
/// @param len The number of 64K-sample summary frames to read
bool BlockFile::Read64K(float *buffer,
size_t start, size_t len)
{
wxASSERT(start >= 0);
ArrayOf< char > summary;
// In case of failure, summary is filled with zeroes
auto result = this->ReadSummary(summary);
start = std::min( start, mSummaryInfo.frames64K );
len = std::min( len, mSummaryInfo.frames64K - start );
CopySamples(summary.get() + mSummaryInfo.offset64K +
(start * mSummaryInfo.bytesPerFrame),
mSummaryInfo.format,
(samplePtr)buffer, floatSample, len * mSummaryInfo.fields);
if (mSummaryInfo.fields == 2) {
// No RMS info; make guess
for(auto i = len; i--;) {
buffer[3*i+2] = (fabs(buffer[2*i]) + fabs(buffer[2*i+1]))/4.0;
buffer[3*i+1] = buffer[2*i+1];
buffer[3*i] = buffer[2*i];
}
}
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,
const AliasBlockFile *pAliasFile, sampleCount origin, unsigned channel,
samplePtr data, sampleFormat format, size_t start, size_t len,
const sampleFormat *pLegacyFormat, size_t legacyLen)
{
// Third party library has its own type alias, check it before
// adding origin + size_t
static_assert(sizeof(sampleCount::type) <= sizeof(sf_count_t),
"Type sf_count_t is too narrow to hold a sampleCount");
SF_INFO info;
memset(&info, 0, sizeof(info));
if ( pLegacyFormat ) {
switch( *pLegacyFormat ) {
case int16Sample:
info.format =
SF_FORMAT_RAW | SF_FORMAT_PCM_16 | SF_ENDIAN_CPU;
break;
default:
case floatSample:
info.format =
SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_CPU;
break;
case int24Sample:
info.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32 | SF_ENDIAN_CPU;
break;
}
info.samplerate = 44100; // Doesn't matter
info.channels = 1;
info.frames = legacyLen + origin.as_long_long();
}
wxFile f; // will be closed when it goes out of scope
SFFile sf;
{
Optional<wxLogNull> silence{};
if (mSilentLog)
silence.emplace();
const auto fullPath = fileName.GetFullPath();
if (wxFile::Exists(fullPath) && f.Open(fullPath)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE));
}
if (!sf) {
memset(data, 0, SAMPLE_SIZE(format)*len);
if (pAliasFile) {
// Set a marker to display an error message for the silence
auto hook = GetMissingAliasFileFound();
if (hook)
hook( pAliasFile );
}
}
}
mSilentLog = !sf;
size_t framesRead = 0;
if (sf) {
auto seek_result = SFCall<sf_count_t>(
sf_seek, sf.get(), ( origin + start ).as_long_long(), SEEK_SET);
if (seek_result < 0)
// error
;
else {
auto channels = info.channels;
wxASSERT(channels >= 1);
wxASSERT((int)channel < channels);
if (channels == 1 &&
format == int16Sample &&
sf_subtype_is_integer(info.format)) {
// If both the src and dest formats are integer formats,
// read integers directly from the file, conversions not needed
framesRead = SFCall<sf_count_t>(
sf_readf_short, sf.get(), (short *)data, len);
}
else if (channels == 1 &&
format == int24Sample &&
sf_subtype_is_integer(info.format)) {
framesRead = SFCall<sf_count_t>(
sf_readf_int, sf.get(), (int *)data, len);
// libsndfile gave us the 3 byte sample in the 3 most
// significant bytes -- we want it in the 3 least
// significant bytes.
int *intPtr = (int *)data;
for( size_t i = 0; i < framesRead; i++ )
intPtr[i] = intPtr[i] >> 8;
}
else if (format == int16Sample &&
!sf_subtype_more_than_16_bits(info.format)) {
// Special case: if the file is in 16-bit (or less) format,
// and the calling method wants 16-bit data, go ahead and
// read 16-bit data directly. This is a pretty common
// case, as most audio files are 16-bit.
SampleBuffer buffer(len * channels, int16Sample);
framesRead = SFCall<sf_count_t>(
sf_readf_short, sf.get(), (short *)buffer.ptr(), len);
for (size_t i = 0; i < framesRead; i++)
((short *)data)[i] =
((short *)buffer.ptr())[(channels * i) + channel];
}
else {
// Otherwise, let libsndfile handle the conversion and
// scaling, and pass us normalized data as floats. We can
// then convert to whatever format we want.
SampleBuffer buffer(len * channels, floatSample);
framesRead = SFCall<sf_count_t>(
sf_readf_float, sf.get(), (float *)buffer.ptr(), len);
auto bufferPtr = (samplePtr)((float *)buffer.ptr() + channel);
CopySamples(bufferPtr, floatSample,
(samplePtr)data, format,
framesRead,
true /* high quality by default */,
channels /* source stride */);
}
}
}
if ( framesRead < len ) {
if (mayThrow)
throw FileException{ FileException::Cause::Read, fileName };
ClearSamples(data, format, framesRead, len - framesRead);
}
return framesRead;
}
/// Constructs an AliasBlockFile based on the given information about
/// the aliased file.
///
/// Note that derived classes /must/ call AliasBlockFile::WriteSummary()
/// in their constructors for the summary file to be correctly written
/// to disk.
///
/// @param baseFileName The name of the summary file to be written, but
/// without an extension. This constructor will add
/// the appropriate extension before passing it to
/// BlockFile::BlockFile
/// @param aliasedFileName The name of the file where the audio data for
/// this block actually exists.
/// @param aliasStart The offset in the aliased file where this block's
/// data begins
/// @param aliasLen The length of this block's data in the aliased
/// file.
/// @param aliasChannel The channel where this block's data is located in
/// the aliased file
AliasBlockFile::AliasBlockFile(wxFileNameWrapper &&baseFileName,
wxFileNameWrapper &&aliasedFileName,
sampleCount aliasStart,
size_t aliasLen, int aliasChannel):
BlockFile {
(baseFileName.SetExt(wxT("auf")), std::move(baseFileName)),
aliasLen
},
mAliasedFileName(std::move(aliasedFileName)),
mAliasStart(aliasStart),
mAliasChannel(aliasChannel)
{
mSilentAliasLog=FALSE;
}
AliasBlockFile::AliasBlockFile(wxFileNameWrapper &&existingSummaryFileName,
wxFileNameWrapper &&aliasedFileName,
sampleCount aliasStart,
size_t aliasLen,
int aliasChannel,
float min, float max, float rms):
BlockFile{ std::move(existingSummaryFileName), aliasLen },
mAliasedFileName(std::move(aliasedFileName)),
mAliasStart(aliasStart),
mAliasChannel(aliasChannel)
{
mMin = min;
mMax = max;
mRMS = rms;
mSilentAliasLog=FALSE;
}
/// Write the summary to disk. Derived classes must call this method
/// from their constructors for the summary to be correctly written.
/// It uses the derived class's ReadData() to retrieve the data to
/// summarize.
void AliasBlockFile::WriteSummary()
{
// To build the summary data, call ReadData (implemented by the
// derived classes) to get the sample data
// Call this first, so that in case of exceptions from ReadData, there is
// no NEW output file
SampleBuffer sampleData(mLen, floatSample);
this->ReadData(sampleData.ptr(), floatSample, 0, mLen);
// Now checked carefully in the DirManager
//wxASSERT( !wxFileExists(FILENAME(mFileName.GetFullPath())));
// I would much rather have this code as part of the constructor, but
// I can't call virtual functions from the constructor. So we just
// need to ensure that every derived class calls this in *its* constructor
wxFFile summaryFile(mFileName.GetFullPath(), wxT("wb"));
if( !summaryFile.IsOpened() ){
// Never silence the Log w.r.t write errors; they always count
// as NEW errors
wxLogError(wxT("Unable to write summary data to file %s"),
mFileName.GetFullPath());
// If we can't write, there's nothing to do.
return;
}
ArrayOf<char> cleanup;
void *summaryData = BlockFile::CalcSummary(sampleData.ptr(), mLen,
floatSample, cleanup);
summaryFile.Write(summaryData, mSummaryInfo.totalSummaryBytes);
}
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.
///
/// @param *data The buffer where the summary data will be stored. It must
/// be at least mSummaryInfo.totalSummaryBytes long.
bool AliasBlockFile::ReadSummary(ArrayOf<char> &data)
{
data.reinit( mSummaryInfo.totalSummaryBytes );
wxFFile summaryFile(mFileName.GetFullPath(), wxT("rb"));
{
Optional<wxLogNull> silence{};
if (mSilentLog)
silence.emplace();
if (!summaryFile.IsOpened()){
// NEW model; we need to return valid data
memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
// we silence the logging for this operation in this object
// after first occurrence of error; it's already reported and
// spewing at the user will complicate the user's ability to
// deal
mSilentLog = TRUE;
return false;
}
else mSilentLog = FALSE; // worked properly, any future error is NEW
}
auto read = summaryFile.Read(data.get(), mSummaryInfo.totalSummaryBytes);
if (read != mSummaryInfo.totalSummaryBytes) {
memset(data.get(), 0, mSummaryInfo.totalSummaryBytes);
return false;
}
FixSummary(data.get());
return true;
}
/// 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().
void AliasBlockFile::ChangeAliasedFileName(wxFileNameWrapper &&newAliasedFile)
{
mAliasedFileName = std::move(newAliasedFile);
}
auto AliasBlockFile::GetSpaceUsage() const -> DiskByteCount
{
wxFFile summaryFile(mFileName.GetFullPath());
return summaryFile.Length();
}

View File

@ -1,287 +0,0 @@
/**********************************************************************
Audacity: A Digital Audio Editor
BlockFile.h
Joshua Haberman
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_BLOCKFILE__
#define __AUDACITY_BLOCKFILE__
#include "SampleFormat.h"
#include "wxFileNameWrapper.h" // member variable
#include <functional>
class XMLWriter;
class SummaryInfo {
public:
SummaryInfo(size_t samples);
int fields; /* Usually 3 for Min, Max, RMS */
sampleFormat format;
int bytesPerFrame;
size_t frames64K;
int offset64K;
size_t frames256;
int offset256;
size_t totalSummaryBytes;
};
class BlockFile;
class AliasBlockFile;
using BlockFilePtr = std::shared_ptr<BlockFile>;
template< typename Result, typename... Args >
inline std::shared_ptr< Result > make_blockfile (Args && ... args)
{
return std::make_shared< Result > ( std::forward< 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.
BlockFile(wxFileNameWrapper &&fileName, size_t samples);
virtual ~BlockFile();
static unsigned long gBlockFileDestructionCount;
// Reading
/// Retrieves audio data from this BlockFile
/// Returns the number of samples really read, not more than len
/// If fewer can be read than len, throws an exception if mayThrow is true,
/// otherwise fills the remainder of data with zeroes.
virtual size_t ReadData(samplePtr data, sampleFormat format,
size_t start, size_t len, bool mayThrow = true)
const = 0;
// Other Properties
/// Stores a representation of this file in XML
virtual void SaveXML(XMLWriter &xmlFile) = 0;
/// Gets the filename of the disk file associated with this BlockFile
/// (can be empty -- some BlockFiles, like SilentBlockFile, correspond to
/// no file on disk)
/// Avoids copying wxFileName by returning a reference, but for some subclasses
/// of BlockFile, you must exclude other threads from changing the name so long
/// as you have only a reference. Thus, this wrapper object that guarantees release
/// of any lock when it goes out of scope. Call mLocker.reset() to unlock it sooner.
struct GetFileNameResult {
const wxFileName &name;
GetFileNameResult(const wxFileName &name_)
: name{ name_ } {}
GetFileNameResult(const GetFileNameResult&) PROHIBITED;
GetFileNameResult &operator= (const GetFileNameResult&) PROHIBITED;
GetFileNameResult(GetFileNameResult &&that)
: name{ that.name } {}
};
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; }
/// Locks this BlockFile, to prevent it from being moved
virtual void Lock();
/// Unlock this BlockFile, allowing it to be moved
virtual void Unlock();
/// Returns TRUE if this BlockFile is locked
virtual bool IsLocked();
struct MinMaxRMS { float min, max, RMS; };
/// Gets extreme values for the specified region
virtual MinMaxRMS GetMinMaxRMS(size_t start, size_t len,
bool mayThrow = true) const;
/// Gets extreme values for the entire block
virtual MinMaxRMS GetMinMaxRMS(bool mayThrow = true) const;
/// Returns the 256 byte summary data block
virtual bool Read256(float *buffer, size_t start, size_t len);
/// Returns the 64K summary data block
virtual bool Read64K(float *buffer, size_t start, size_t len);
/// Returns TRUE if this block references another disk file
virtual bool IsAlias() const { return false; }
/// Create a NEW BlockFile identical to this, using the given filename
virtual BlockFilePtr Copy(wxFileNameWrapper &&newFileName) = 0;
// Report disk space usage.
using DiskByteCount = unsigned long long;
virtual DiskByteCount GetSpaceUsage() const = 0;
/// if the on-disk state disappeared, either recover it (if it was
//summary only), write out a placeholder of silence data (missing
//.au) or mark the blockfile to deal some other way without spewing
//errors.
// May throw exceptions for i/o errors.
virtual void Recover() = 0;
/// if we've detected an on-disk problem, the user opted to
//continue and the error persists, don't keep reporting it. The
//Object implements this functionality internally, but we want to
//be able to tell the logging to shut up from outside too.
void SilenceLog() const { mSilentLog = TRUE; }
///when the project closes, it locks the blockfiles.
///Override this in case it needs special treatment.
// not balanced by unlocking calls.
virtual void CloseLock(){Lock();}
protected:
/// Prevents a read on other threads. The basic blockfile runs on only one thread, so does nothing.
virtual void LockRead() const {}
/// Allows reading on other threads.
virtual void UnlockRead() const {}
struct ReadLocker { void operator () ( const BlockFile *p ) const {
if (p) p->LockRead(); } };
struct ReadUnlocker { void operator () ( const BlockFile *p ) const {
if (p) p->UnlockRead(); } };
using ReadLockBase =
std::unique_ptr< const BlockFile, ReadUnlocker >;
public:
class ReadLock : public ReadLockBase
{
friend BlockFile;
ReadLock ( const BlockFile *p, const BlockFile::ReadUnlocker &u )
: ReadLockBase { p, u } {}
public:
ReadLock(ReadLock&&that) : ReadLockBase{ std::move(that) } {}
using Suspension = std::unique_ptr< const BlockFile, ReadLocker >;
Suspension Suspend() const
{ if (get()) get()->UnlockRead();
return Suspension{ get(), ReadLocker{} }; }
};
// RAII wrapper about the read locking functions
ReadLock LockForRead() const { LockRead(); return { this, ReadUnlocker{} }; }
private:
protected:
/// Calculate summary data for the given sample data
/// Overrides have differing details of memory management
virtual void *CalcSummary(samplePtr buffer, size_t len,
sampleFormat format,
// This gets filled, if the caller needs to deallocate. Else it is null.
ArrayOf<char> &cleanup);
// Common, nonvirtual calculation routine for the use of the above
void CalcSummaryFromBuffer(const float *fbuffer, size_t len,
float *summary256, float *summary64K);
/// Read the summary section of the file. Derived classes implement.
virtual bool ReadSummary(ArrayOf<char> &data) = 0;
/// Byte-swap the summary data, in case it was saved by a system
/// on a different platform
virtual void FixSummary(void *data);
static size_t CommonReadData(
bool mayThrow,
const wxFileName &fileName, bool &mSilentLog,
const AliasBlockFile *pAliasFile, sampleCount origin, unsigned channel,
samplePtr data, sampleFormat format, size_t start, size_t len,
const sampleFormat *pLegacyFormat = nullptr, size_t legacyLen = 0);
private:
int mLockCount;
static ArrayOf<char> fullSummary;
protected:
wxFileNameWrapper mFileName;
size_t mLen;
SummaryInfo mSummaryInfo;
float mMin, mMax, mRMS;
mutable bool mSilentLog;
};
/// A BlockFile that refers to data in an existing file
/// An AliasBlockFile references an existing disk file for its storage
/// instead of copying the data. It still writes a file to disk, but
/// only stores summary data in it.
///
/// This is a common base class for all alias block files. It handles
/// reading and writing summary data, leaving very little for derived
/// classes to need to implement.
class AliasBlockFile /* not final */ : public BlockFile
{
public:
// Constructor / Destructor
/// Constructs an AliasBlockFile
AliasBlockFile(wxFileNameWrapper &&baseFileName,
wxFileNameWrapper &&aliasedFileName, sampleCount aliasStart,
size_t aliasLen, int aliasChannel);
AliasBlockFile(wxFileNameWrapper &&existingSummaryFileName,
wxFileNameWrapper &&aliasedFileName, sampleCount aliasStart,
size_t aliasLen, int aliasChannel,
float min, float max, float RMS);
virtual ~AliasBlockFile();
// Reading
DiskByteCount GetSpaceUsage() const override;
/// as SilentLog (which would affect Summary data access), but
// applying to Alias file access
void SilenceAliasLog() const { mSilentAliasLog = TRUE; }
//
// These methods are for advanced use only!
//
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
virtual void WriteSummary();
/// Read the summary into a buffer
bool ReadSummary(ArrayOf<char> &data) override;
wxFileNameWrapper mAliasedFileName;
sampleCount mAliasStart;
const int mAliasChannel;
mutable bool mSilentAliasLog;
};
#endif

View File

@ -96,8 +96,6 @@ list( APPEND SOURCES
BatchProcessDialog.h
Benchmark.cpp
Benchmark.h
BlockFile.cpp
BlockFile.h
CellularPanel.cpp
CellularPanel.h
ClassicThemeAsCeeCode.h
@ -110,16 +108,12 @@ list( APPEND SOURCES
CrashReport.cpp
CrashReport.h
DarkThemeAsCeeCode.h
Dependencies.cpp
Dependencies.h
DeviceChange.cpp
DeviceChange.h
DeviceManager.cpp
DeviceManager.h
Diags.cpp
Diags.h
DirManager.cpp
DirManager.h
Dither.cpp
Dither.h
Envelope.cpp
@ -178,8 +172,6 @@ list( APPEND SOURCES
MemoryX.h
Menus.cpp
Menus.h
MissingAliasFileDialog.cpp
MissingAliasFileDialog.h
Mix.cpp
Mix.h
MixerBoard.cpp
@ -199,16 +191,14 @@ list( APPEND SOURCES
Prefs.h
Printing.cpp
Printing.h
# Profiler.cpp
# Profiler.h
Project.cpp
Project.h
ProjectAudioIO.cpp
ProjectAudioIO.h
ProjectAudioManager.cpp
ProjectAudioManager.h
ProjectFSCK.cpp
ProjectFSCK.h
# ProjectFSCK.cpp
# ProjectFSCK.h
ProjectFileIO.cpp
ProjectFileIO.h
ProjectFileIORegistry.cpp
@ -241,6 +231,8 @@ list( APPEND SOURCES
Resample.h
RingBuffer.cpp
RingBuffer.h
SampleBlock.cpp
SampleBlock.h
SampleFormat.cpp
SampleFormat.h
Screenshot.cpp
@ -329,19 +321,6 @@ list( APPEND SOURCES
float_cast.h
wxFileNameWrapper.h
# Blockfile
blockfile/LegacyAliasBlockFile.cpp
blockfile/LegacyAliasBlockFile.h
blockfile/LegacyBlockFile.cpp
blockfile/LegacyBlockFile.h
blockfile/PCMAliasBlockFile.cpp
blockfile/PCMAliasBlockFile.h
blockfile/SilentBlockFile.cpp
blockfile/SilentBlockFile.h
blockfile/SimpleBlockFile.cpp
blockfile/SimpleBlockFile.h
# Commands
commands/AppCommandEvent.cpp

View File

@ -233,7 +233,7 @@ const ReservedCommandFlag&
return
undoManager.UnsavedChanges()
||
!ProjectFileIO::Get( project ).IsProjectSaved()
ProjectFileIO::Get( project ).IsModified()
;
}
}; return flag; }

File diff suppressed because it is too large Load Diff

View File

@ -1,274 +0,0 @@
/**********************************************************************
Audacity: A Digital Audio Editor
DirManager.h
Dominic Mazzoni
**********************************************************************/
#ifndef _DIRMANAGER_
#define _DIRMANAGER_
#include "audacity/Types.h"
#include "xml/XMLTagHandler.h"
#include <functional>
#include <memory>
#include <unordered_map>
#include "ClientData.h"
class wxFileNameWrapper;
class AudacityProject;
class BlockArray;
class BlockFile;
class ProgressDialog;
using DirHash = std::unordered_map<int, int>;
class BlockFile;
using BlockFilePtr = std::shared_ptr<BlockFile>;
using BlockHash = std::unordered_map< wxString, std::weak_ptr<BlockFile> >;
wxMemorySize GetFreeMemory();
enum {
kCleanTopDirToo = 1, // The top directory can be excluded from clean.
kCleanDirsOnlyIfEmpty = 2, // Otherwise non empty dirs may be removed.
kCleanFiles = 4, // Remove files
kCleanDirs = 8 // Remove dirs.
};
class PROFILE_DLL_API DirManager final
: public XMLTagHandler
, public ClientData::Base
, public std::enable_shared_from_this< DirManager >
{
public:
static DirManager &Get( AudacityProject &project );
static const DirManager &Get( const AudacityProject &project );
static DirManager &Reset( AudacityProject &project );
static void Destroy( AudacityProject &project );
static int RecursivelyEnumerate(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
bool bFiles, bool bDirs,
int progress_count = 0,
int progress_bias = 0,
ProgressDialog* progress = nullptr);
static int RecursivelyEnumerateWithProgress(const FilePath &dirPath,
FilePaths& filePathArray, // output: all files in dirPath tree
wxString dirspec,
wxString filespec,
bool bFiles, bool bDirs,
int progress_count,
const TranslatableString &message);
static int RecursivelyCountSubdirs( const FilePath &dirPath );
static int RecursivelyRemoveEmptyDirs(const FilePath &dirPath,
int nDirCount = 0,
ProgressDialog* pProgress = nullptr);
static void RecursivelyRemove(const FilePaths& filePathArray, int count, int bias,
int flags, const TranslatableString &message = {});
// 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();
DirManager( const DirManager & ) PROHIBITED;
DirManager &operator=( const DirManager & ) PROHIBITED;
virtual ~DirManager();
size_t NumBlockFiles() const { return mBlockFileHash.size(); }
static void SetTempDir(const wxString &_temp) { globaltemp = _temp; }
class ProjectSetter
{
public:
ProjectSetter(
DirManager &dirManager,
FilePath& newProjPath, // assigns it if empty
const FilePath& newProjName, const bool bCreate, bool moving);
~ProjectSetter();
bool Ok();
void Commit();
private:
struct Impl;
std::unique_ptr<Impl> mpImpl;
};
// Returns true on success.
// If SetProject is told NOT to create the directory
// but it doesn't already exist, SetProject fails and returns false.
// This function simply creates a ProjectSetter and commits it if successful.
// Using ProjectSetter directly allows separation of those steps.
bool SetProject(
FilePath& newProjPath, // assigns it if empty
const FilePath& newProjName, const bool bCreate);
FilePath GetProjectDataDir();
FilePath GetProjectName();
wxLongLong GetFreeDiskSpace();
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;
/// Check for existing using filename using complete filename
bool ContainsBlockFile(const wxString &filepath) const;
// Adds one to the reference count of the block file,
// UNLESS it is "locked", then it makes a NEW copy of
// the BlockFile.
// May throw an exception in case of disk space exhaustion, otherwise
// returns non-null.
BlockFilePtr CopyBlockFile(const BlockFilePtr &b);
BlockFile *LoadBlockFile(const wxChar **attrs, sampleFormat format);
void SaveBlockFile(BlockFile *f, int depth, FILE *fp);
#if LEGACY_PROJECT_FILE_SUPPORT
BlockFile *LoadBlockFile(wxTextFile * in, sampleFormat format);
void SaveBlockFile(BlockFile * f, wxTextFile * out);
#endif
std::pair<bool, FilePath>
LinkOrCopyToNewProjectDirectory(BlockFile *f, bool &link);
bool EnsureSafeFilename(const wxFileName &fName);
using LoadingTarget = std::function< BlockFilePtr &() >;
void SetLoadingTarget( LoadingTarget loadingTarget )
{
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
void SetLoadingMaxSamples(size_t max) { mMaxSamples = max; }
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
XMLTagHandler *HandleXMLChild(const wxChar * WXUNUSED(tag)) override
{ return NULL; }
bool AssignFile(wxFileNameWrapper &filename, const wxString &value, bool check);
// Clean the temp dir. Note that now where we have auto recovery the temp
// dir is not cleaned at start up anymore. But it is cleaned when the
// program is exited normally.
static void CleanTempDir();
static void CleanDir(
const FilePath &path,
const wxString &dirSpec,
const wxString &fileSpec,
const TranslatableString &msg,
int flags = 0);
void FindMissingAliasFiles(
BlockHash& missingAliasFilesAUFHash, // output: (.auf) AliasBlockFiles whose aliased files are missing
BlockHash& missingAliasFilesPathHash); // output: full paths of missing aliased files
void FindMissingAUFs(
BlockHash& missingAUFHash); // output: missing (.auf) AliasBlockFiles
void FindMissingAUs(
BlockHash& missingAUHash); // missing data (.au) blockfiles
// Find .au and .auf files that are not in the project.
void FindOrphanBlockFiles(
const FilePaths &filePathArray, // input: all files in project directory
FilePaths &orphanFilePathArray); // output: orphan files
// Remove all orphaned blockfiles without user interaction. This is
// generally safe, because orphaned blockfiles are not referenced by the
// project and thus worthless anyway.
void RemoveOrphanBlockfiles();
// Get directory where data files are in. Note that projects are normally
// not interested in this information, but it is important for the
// auto-save functionality
FilePath GetDataFilesDir() const;
// This should only be used by the auto save functionality
void SetLocalTempDir(const wxString &path);
// Do not DELETE any temporary files on exit. This is only called if
// auto recovery is cancelled and should be retried later
static void SetDontDeleteTempFiles() { dontDeleteTempFiles = true; }
private:
wxFileNameWrapper MakeBlockFileName();
wxFileNameWrapper MakeBlockFilePath(const wxString &value);
BlockHash mBlockFileHash; // repository for blockfiles
// Hashes for management of the sub-directory tree of _data
struct BalanceInfo
{
DirHash dirTopPool; // available toplevel dirs
DirHash dirTopFull; // full toplevel dirs
DirHash dirMidPool; // available two-level dirs
DirHash dirMidFull; // full two-level dirs
} mBalanceInfo;
// Accessor for the balance info, may need to do a delayed update for
// deletion in case other threads DELETE block files
BalanceInfo &GetBalanceInfo();
void BalanceInfoDel(const wxString&);
void BalanceInfoAdd(const wxString&);
void BalanceFileAdd(int);
int BalanceMidAdd(int, int);
FilePath projName;
FilePath projPath;
FilePath projFull;
FilePaths aliasList;
LoadingTarget mLoadingTarget;
sampleFormat mLoadingFormat;
size_t mLoadingBlockLen;
size_t mMaxSamples; // max samples per block
unsigned long mLastBlockFileDestructionCount { 0 };
static wxString globaltemp;
wxString mytemp;
static int numDirManagers;
static bool dontDeleteTempFiles;
};
#endif

View File

@ -21,7 +21,6 @@ class wxRect;
class wxMouseEvent;
class wxTextFile;
class DirManager;
class Envelope;
class EnvPoint;
@ -110,12 +109,6 @@ public:
double ClampValue(double value) { return std::max(mMinValue, std::min(mMaxValue, value)); }
#if LEGACY_PROJECT_FILE_SUPPORT
// File I/O
bool Load(wxTextFile * in, DirManager * dirManager) override;
bool Save(wxTextFile * out, bool overwrite) override;
#endif
// Newfangled XML file I/O
bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;

View File

@ -15,6 +15,8 @@
#include "audacity/Types.h"
#include "MemoryX.h"
#include "sndfile.h"
class ChoiceSetting;

View File

@ -53,7 +53,7 @@ const FileNames::FileType
FileNames::AllFiles{ XO("All files"), { wxT("") } }
/* i18n-hint an Audacity project is the state of the program, stored as
files that can be reopened to resume the session later */
, FileNames::AudacityProjects{ XO("Audacity projects"), { wxT("aup") }, true }
, FileNames::AudacityProjects{ XO("Audacity projects"), { wxT("aup3") }, true }
, FileNames::DynamicLibraries{
#if defined(__WXMSW__)
XO("Dynamically Linked Libraries"), { wxT("dll") }, true
@ -220,16 +220,6 @@ void FileNames::MakeNameUnique(FilePaths &otherNames,
otherNames.push_back(newName.GetFullName());
}
//
// Audacity user data directories
FilePath FileNames::AutoSaveDir()
{
wxFileName autoSaveDir(FileNames::DataDir(), wxT("AutoSave"));
return FileNames::MkDir(autoSaveDir.GetFullPath());
}
// The APP name has upercase first letter (so that Quit Audacity is correctly
// capitalised on Mac, but we want lower case APP name in paths.
// This function does that substitution, IF the last component of
@ -739,10 +729,36 @@ char *FileNames::VerifyFilename(const wxString &s, bool input)
}
#endif
//using this with wxStringArray::Sort will give you a list that
//is alphabetical, without depending on case. If you use the
//default sort, you will get strings with 'R' before 'a', because it is in caps.
// Using this with wxStringArray::Sort will give you a list that
// is alphabetical, without depending on case. If you use the
// default sort, you will get strings with 'R' before 'a', because it is in caps.
int FileNames::CompareNoCase(const wxString& first, const wxString& second)
{
return first.CmpNoCase(second);
}
// Create a unique filename using the passed prefix and suffix
wxString FileNames::CreateUniqueName(const wxString &prefix,
const wxString &suffix /* = wxEmptyString */)
{
static int count = 0;
return wxString::Format(wxT("%s %s N-%i.%s"),
prefix,
wxDateTime::Now().Format(wxT("%Y-%m-%d %H-%M-%S")),
++count,
suffix);
}
wxString FileNames::UnsavedProjectExtension()
{
return wxT("aup3unsaved");
}
wxString FileNames::UnsavedProjectFileName()
{
wxFileName fn(TempDir(),
CreateUniqueName(wxT("New Project"), UnsavedProjectExtension()));
return fn.GetFullPath();
}

View File

@ -43,7 +43,7 @@ namespace FileNames
// Frequently used types
extern const FileType
AllFiles // *
, AudacityProjects // *.aup
, AudacityProjects // *.aup3
, DynamicLibraries // depends on the operating system
, TextFiles // *.txt
, XMLFiles; // *.xml, *.XML
@ -95,7 +95,6 @@ namespace FileNames
* windows system */
FilePath DataDir();
FilePath ResourcesDir();
FilePath AutoSaveDir();
FilePath HtmlHelpDir();
FilePath HtmlHelpIndexFile(bool quick);
FilePath LegacyChainDir();
@ -182,6 +181,17 @@ namespace FileNames
// wxString compare function for sorting case, which is needed to load correctly.
int CompareNoCase(const wxString& first, const wxString& second);
// Create a unique filename using the passed prefix and suffix
wxString CreateUniqueName(const wxString &prefix,
const wxString &suffix = wxEmptyString);
// Create a filename for an unsaved/temporary project file
wxString UnsavedProjectFileName();
// File extension used for unsaved/temporary project files
wxString UnsavedProjectExtension();
};
// Use this macro to wrap all filenames and pathnames that get

View File

@ -68,11 +68,11 @@ static ProjectFileIORegistry::Entry registerFactory{
LabelTrack::Holder TrackFactory::NewLabelTrack()
{
return std::make_shared<LabelTrack>(mDirManager);
return std::make_shared<LabelTrack>(&mProject);
}
LabelTrack::LabelTrack(const std::shared_ptr<DirManager> &projDirManager):
Track(projDirManager),
LabelTrack::LabelTrack(AudacityProject *project):
Track(project),
mClipLen(0.0),
miLastLabel(-1)
{
@ -627,53 +627,6 @@ void LabelTrack::WriteXML(XMLWriter &xmlFile) const
xmlFile.EndTag(wxT("labeltrack"));
}
#if LEGACY_PROJECT_FILE_SUPPORT
bool LabelTrack::Load(wxTextFile * in, DirManager * dirManager)
{
if (in->GetNextLine() != wxT("NumMLabels"))
return false;
unsigned long len;
if (!(in->GetNextLine().ToULong(&len)))
return false;
mLabels.clear();
mLabels.reserve(len);
for (int i = 0; i < len; i++) {
double t0;
if (!Internat::CompatibleToDouble(in->GetNextLine(), &t0))
return false;
// Legacy file format does not include label end-times.
// PRL: nothing NEW to do, legacy file support
mLabels.push_back(LabelStruct {
SelectedRegion{ t0, t0 }, in->GetNextLine()
});
}
if (in->GetNextLine() != wxT("MLabelsEnd"))
return false;
SortLabels();
return true;
}
bool LabelTrack::Save(wxTextFile * out, bool overwrite)
{
out->AddLine(wxT("NumMLabels"));
int len = mLabels.size();
out->AddLine(wxString::Format(wxT("%d"), len));
for (auto pLabel : mLabels) {
const auto &labelStruct = *pLabel;
out->AddLine(wxString::Format(wxT("%lf"), labelStruct.selectedRegion.mT0));
out->AddLine(labelStruct.title);
}
out->AddLine(wxT("MLabelsEnd"));
return true;
}
#endif
Track::Holder LabelTrack::Cut(double t0, double t1)
{
auto tmp = Copy(t0, t1);
@ -699,7 +652,7 @@ Track::Holder LabelTrack::SplitCut(double t0, double t1)
Track::Holder LabelTrack::Copy(double t0, double t1, bool) const
{
auto tmp = std::make_shared<LabelTrack>(GetDirManager());
auto tmp = std::make_shared<LabelTrack>(GetProject());
const auto lt = static_cast<LabelTrack*>(tmp.get());
for (auto &labelStruct: mLabels) {

View File

@ -20,7 +20,6 @@
class wxTextFile;
class AudacityProject;
class DirManager;
class NotifyingSelectedRegion;
class TimeWarper;
@ -88,7 +87,7 @@ class AUDACITY_DLL_API LabelTrack final
, public wxEvtHandler
{
public:
LabelTrack(const std::shared_ptr<DirManager> &projDirManager);
LabelTrack(AudacityProject *project);
LabelTrack(const LabelTrack &orig);
virtual ~ LabelTrack();
@ -113,11 +112,6 @@ public:
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXML(XMLWriter &xmlFile) const override;
#if LEGACY_PROJECT_FILE_SUPPORT
bool Load(wxTextFile * in, DirManager * dirManager) override;
bool Save(wxTextFile * out, bool overwrite) override;
#endif
Track::Holder Cut (double t0, double t1) override;
Track::Holder Copy (double t0, double t1, bool forClipboard = true) const override;
void Clear(double t0, double t1) override;

View File

@ -1,164 +0,0 @@
/*********************************************************************
\class MissingAliasFileDialog
\brief Special case of ErrorDialog for reporting missing alias files.
*//*******************************************************************/
#include "MissingAliasFileDialog.h"
#include <mutex>
#include <wx/frame.h>
#include "BlockFile.h"
#include "DirManager.h"
#include "Project.h"
#include "widgets/ErrorDialog.h"
namespace {
using wxDialogRef = wxWeakRef< wxDialog >;
std::vector< wxDialogRef > sDialogs;
}
// special case for alias missing dialog because we keep track of if it exists.
class MissingAliasFileDialog final : public ErrorDialog
{
public:
MissingAliasFileDialog(wxWindow *parent,
const TranslatableString & dlogTitle,
const TranslatableString & message,
const wxString & helpURL,
const bool Close = true, const bool modal = true);
virtual ~MissingAliasFileDialog();
};
MissingAliasFileDialog::MissingAliasFileDialog(wxWindow *parent,
const TranslatableString & dlogTitle,
const TranslatableString & message,
const wxString & helpURL,
const bool Close, const bool modal)
: ErrorDialog( parent,
dlogTitle, message, helpURL, Close, modal )
{
sDialogs.push_back( this );
}
MissingAliasFileDialog::~MissingAliasFileDialog()
{
auto begin = sDialogs.begin(), end = sDialogs.end(),
newEnd = std::remove_if( begin, end,
[&]( const wxDialogRef &ref ){
return ref == this; } );
sDialogs.erase( newEnd, end );
}
namespace MissingAliasFilesDialog {
namespace{
bool m_missingAliasFilesWarningShouldShow{ true };
std::weak_ptr< AudacityProject > m_LastMissingBlockFileProject;
wxString m_LastMissingBlockFilePath;
std::mutex m_LastMissingBlockFileLock;
}
using Lock = std::unique_lock< std::mutex >;
void Show(AudacityProject *project,
const TranslatableString &dlogTitle,
const TranslatableString &message,
const wxString &helpPage,
const bool Close)
{
auto parent = FindProjectFrame( project );
wxASSERT(parent); // to justify safenew
ErrorDialog *dlog = safenew MissingAliasFileDialog(parent, dlogTitle, message, helpPage, Close, false);
// Don't center because in many cases (effect, export, etc) there will be a progress bar in the center that blocks this.
// instead put it just above or on the top of the project.
wxPoint point;
point.x = 0;
point.y = parent ? parent->GetPosition().y - 200 : 100;
if (point.y < 100)
point.y = 100;
dlog->SetPosition(point);
dlog->CentreOnParent(wxHORIZONTAL);
// This needs to be modeless because user may need to
// stop playback AND read dialog's instructions.
dlog->Show();
// ANSWER-ME: Vigilant Sentry flags this method as not deleting dlog, so a mem leak.
// PRL: answer is that the parent window guarantees destruction of the dialog
// but in practice Destroy() in OnOK does that
}
wxDialog *Find( const AudacityProject &project )
{
auto &window = GetProjectFrame( project );
auto begin = sDialogs.begin(), end = sDialogs.end(),
iter = std::find_if( begin, end,
[&]( const wxDialogRef &ref ){
return ref && ref->GetParent() == &window; } );
if (iter != end)
return *iter;
return nullptr;
}
void Mark(const AliasBlockFile *b)
{
Lock lock{ m_LastMissingBlockFileLock };
if (b) {
for ( auto pProject : AllProjects{} ) {
// search each project for the blockfile
if (DirManager::Get( *pProject ).ContainsBlockFile(b)) {
m_LastMissingBlockFileProject = pProject;
break;
}
}
}
else
m_LastMissingBlockFileProject = {};
if (b)
m_LastMissingBlockFilePath = b->GetAliasedFileName().GetFullPath();
else
m_LastMissingBlockFilePath = wxString{};
}
std::pair< wxString, std::shared_ptr<AudacityProject> > Marked()
{
Lock lock{ m_LastMissingBlockFileLock };
return { m_LastMissingBlockFilePath, m_LastMissingBlockFileProject.lock() };
}
bool ShouldShow()
{
Lock lock{ m_LastMissingBlockFileLock };
auto ptr = m_LastMissingBlockFileProject.lock();
return ptr && m_missingAliasFilesWarningShouldShow;
}
void SetShouldShow(bool b)
{
// Note that this is can be called by both the main thread and other threads.
// I don't believe we need a mutex because we are checking zero vs non-zero,
// and the setting from other threads will always be non-zero (true), and the
// setting from the main thread is always false.
m_missingAliasFilesWarningShouldShow = b;
// reset the warnings as they were probably marked by a previous run
if (m_missingAliasFilesWarningShouldShow) {
Mark( nullptr );
}
}
}
// 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

@ -1,46 +0,0 @@
#ifndef __AUDACITY_MISSING_ALIAS_FILES_DIALOG__
#define __AUDACITY_MISSING_ALIAS_FILES_DIALOG__
class AliasBlockFile;
class AudacityProject;
class TranslatableString;
class wxDialog;
class TranslatableString;
#include <memory>
#include <utility>
#include <wx/string.h>
namespace MissingAliasFilesDialog {
/** \brief Mark playback as having missing aliased blockfiles
*
* Playback will continue, but the missing files will be silenced
* ShouldShow can be called to determine
* if the user should be notified
*/
void Mark(const AliasBlockFile *b);
// Retrieve information left by Mark
std::pair< wxString, std::shared_ptr<AudacityProject> > Marked();
/** \brief Changes the behavior of missing aliased blockfiles warnings
*/
void SetShouldShow(bool b);
/** \brief Returns true if the user should be notified of missing alias warnings
*/
bool ShouldShow();
/// Displays a custom modeless error dialog for aliased file errors
void Show(AudacityProject *parent,
const TranslatableString &dlogTitle,
const TranslatableString &message,
const wxString &helpPage,
const bool Close = true);
wxDialog *Find( const AudacityProject &project );
}
#endif

View File

@ -24,7 +24,6 @@
#include <vector>
class Resample;
class DirManager;
class BoundedEnvelope;
class TrackFactory;
class TrackList;

View File

@ -33,7 +33,6 @@
#define ROUND(x) ((int) ((x) + 0.5))
#include "AColor.h"
#include "DirManager.h"
#include "Prefs.h"
#include "ProjectFileIORegistry.h"
#include "prefs/ImportExportPrefs.h"
@ -124,11 +123,11 @@ static ProjectFileIORegistry::Entry registerFactory{
NoteTrack::Holder TrackFactory::NewNoteTrack()
{
return std::make_shared<NoteTrack>(mDirManager);
return std::make_shared<NoteTrack>(&mProject);
}
NoteTrack::NoteTrack(const std::shared_ptr<DirManager> &projDirManager)
: NoteTrackBase(projDirManager)
NoteTrack::NoteTrack(AudacityProject *project)
: NoteTrackBase(project)
{
SetDefaultName(_("Note Track"));
SetName(GetDefaultName());
@ -173,7 +172,7 @@ Alg_seq &NoteTrack::GetSeq() const
Track::Holder NoteTrack::Clone() const
{
auto duplicate = std::make_shared<NoteTrack>(mDirManager);
auto duplicate = std::make_shared<NoteTrack>(mProject);
duplicate->Init(*this);
// The duplicate begins life in serialized state. Often the duplicate is
// pushed on the Undo stack. Then we want to un-serialize it (or a further
@ -464,7 +463,7 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
auto newTrack = std::make_shared<NoteTrack>(mDirManager);
auto newTrack = std::make_shared<NoteTrack>(mProject);
newTrack->Init(*this);
@ -478,7 +477,7 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
//AddToDuration( delta );
// What should be done with the rest of newTrack's members?
//(mBottomNote, mDirManager,
//(mBottomNote, mProject,
// mSerializationBuffer, mSerializationLength, mVisibleChannels)
return newTrack;
@ -491,7 +490,7 @@ Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
double len = t1-t0;
auto newTrack = std::make_shared<NoteTrack>(mDirManager);
auto newTrack = std::make_shared<NoteTrack>(mProject);
newTrack->Init(*this);
@ -501,7 +500,7 @@ Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
newTrack->SetOffset(0);
// What should be done with the rest of newTrack's members?
// (mBottomNote, mDirManager, mSerializationBuffer,
// (mBottomNote, mProject, mSerializationBuffer,
// mSerializationLength, mVisibleChannels)
return newTrack;

View File

@ -46,7 +46,6 @@ SONFNS(AutoSave)
class wxDC;
class wxRect;
class DirManager;
class Alg_seq; // from "allegro.h"
using NoteTrackBase =
@ -66,7 +65,7 @@ class AUDACITY_DLL_API NoteTrack final
: public NoteTrackBase
{
public:
NoteTrack(const std::shared_ptr<DirManager> &projDirManager);
NoteTrack(AudacityProject *project);
virtual ~NoteTrack();
using Holder = std::shared_ptr<NoteTrack>;

View File

@ -27,7 +27,7 @@ class PlatformCompatibility
public:
//
// On Win32, this function gets the long file name (like
// "C:\Program Files\Project.aup") from a short file name like
// "C:\Program Files\Project.aup3") from a short file name like
// "C:\PROGRA~1\PROJEC~1.AUP. On other systems, the function
// just returns the exact string it is given.
//

View File

@ -130,16 +130,14 @@ void AudacityProject::SetPanel( wxWindow *pPanel )
mPanel = pPanel;
}
wxString AudacityProject::GetProjectName() const
const wxString &AudacityProject::GetProjectName() const
{
wxString name = wxFileNameFromPath(mFileName);
return mName;
}
// Chop off the extension
size_t len = name.length();
if (len > 4 && name.Mid(len - 4) == wxT(".aup"))
name = name.Mid(0, len - 4);
return name;
void AudacityProject::SetProjectName(const wxString &name)
{
mName = name;
}
AUDACITY_DLL_API wxFrame &GetProjectFrame( AudacityProject &project )

View File

@ -24,7 +24,6 @@ class wxWindow;
class AudacityProject;
AUDACITY_DLL_API AudacityProject *GetActiveProject();
// For use by ProjectManager only:
extern void SetActiveProject(AudacityProject * project);
@ -120,17 +119,23 @@ class AUDACITY_DLL_API AudacityProject final
const wxWindow *GetPanel() const { return mPanel; }
void SetPanel( wxWindow *pPanel );
wxString GetProjectName() const;
const FilePath &GetFileName() { return mFileName; }
void SetFileName( const FilePath &fileName ) { mFileName = fileName; }
int GetProjectNumber(){ return mProjectNo;}
// Project name can be either empty or have the name of the project.
//
// If empty, it signifies that the project is empty/unmodified or
// that the project hasn't yet been saved to a permanent project
// file.
//
// If a name has been assigned, it is merely used to identify
// the project and should not be used for any other purposes.
const wxString &GetProjectName() const;
void SetProjectName(const wxString &name);
private:
// The project's name and file info
FilePath mFileName; // Note: extension-less
// The project's name
wxString mName;
static int mProjectCounter;// global counter.
int mProjectNo; // count when this project was created.

View File

@ -19,7 +19,6 @@ Paul Licameli split from ProjectManager.cpp
#include "AudioIO.h"
#include "AutoRecovery.h"
#include "CommonCommandFlags.h"
#include "DirManager.h"
#include "LabelTrack.h"
#include "Menus.h"
#include "Project.h"
@ -863,7 +862,6 @@ void ProjectAudioManager::OnAudioIOStartRecording()
void ProjectAudioManager::OnAudioIOStopRecording()
{
auto &project = mProject;
auto &dirManager = DirManager::Get( project );
auto &projectAudioIO = ProjectAudioIO::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
auto &window = GetProjectFrame( project );
@ -920,21 +918,11 @@ You are saving directly to a slow external storage device\n\
projectFileIO.AutoSave();
}
void ProjectAudioManager::OnAudioIONewBlockFiles(
const AutoSaveFile & blockFileLog)
void ProjectAudioManager::OnAudioIONewBlockFiles(const WaveTrackArray *tracks)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
// New blockfiles have been created, so add them to the auto-save file
const auto &autoSaveFileName = projectFileIO.GetAutoSaveFileName();
if ( !autoSaveFileName.empty() )
{
wxFFile f{ autoSaveFileName, wxT("ab") };
if (!f.IsOpened())
return; // Keep recording going, there's not much we can do here
blockFileLog.Append(f);
f.Close();
}
projectFileIO.AutoSave(tracks);
}
void ProjectAudioManager::OnCommitRecording()

View File

@ -14,6 +14,7 @@ Paul Licameli split from ProjectManager.h
#include <memory>
#include <vector>
#include "AudioIO.h"
#include "AudioIOListener.h" // to inherit
#include "ClientData.h" // to inherit
@ -136,7 +137,7 @@ private:
void OnAudioIORate(int rate) override;
void OnAudioIOStartRecording() override;
void OnAudioIOStopRecording() override;
void OnAudioIONewBlockFiles(const AutoSaveFile & blockFileLog) override;
void OnAudioIONewBlockFiles(const WaveTrackArray *tracks) override;
void OnCommitRecording() override;
void OnSoundActivationThreshold() override;

View File

@ -20,7 +20,6 @@
#include "widgets/AudacityMessageBox.h"
#include "Internat.h"
#include "MemoryX.h"
#include "MissingAliasFileDialog.h"
#include "widgets/MultiDialog.h"
#include "widgets/ProgressDialog.h"
@ -34,6 +33,9 @@
int ProjectFSCK(
DirManager &dm, const bool bForceError, const bool bAutoRecoverMode)
{
#pragma message( "====================================================================")
#pragma message( "Don\'t forget to redo ProjectFSCK")
#pragma message( "====================================================================")
// In earlier versions of this method, enumerations of errors were
// all done in sequence, then the user was prompted for each type of error.
// The enumerations are now interleaved with prompting, because, for example,
@ -68,7 +70,7 @@ int ProjectFSCK(
else
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
#if 0
FilePaths filePathArray; // *all* files in the project directory/subdirectories
auto dirPath = ( dm.GetDataFilesDir() );
DirManager::RecursivelyEnumerateWithProgress(
@ -403,5 +405,6 @@ other projects. \
}
MissingAliasFilesDialog::SetShouldShow(true);
#endif
return nResult;
}

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,14 @@ Paul Licameli split from AudacityProject.h
#include "Prefs.h" // to inherit
#include "xml/XMLTagHandler.h" // to inherit
#include <sqlite3.h>
class AudacityProject;
class AutoSaveFile;
class SampleBlock;
class WaveTrack;
using WaveTrackArray = std::vector < std::shared_ptr < WaveTrack > >;
///\brief Object associated with a project that manages reading and writing
/// of Audacity project file formats, and autosave
@ -35,33 +42,35 @@ public:
bool WarnOfLegacyFile( );
const FilePath &GetAutoSaveFileName() { return mAutoSaveFileName; }
// It seems odd to put this method in this class, but the results do depend
// on what is discovered while opening the file, such as whether it is a
// recovery file
void SetProjectTitle( int number = -1 );
void SetProjectTitle(int number = -1);
// Should be empty or a fully qualified file name
bool IsProjectSaved() const;
const FilePath &GetFileName() const;
void SetFileName( const FilePath &fileName );
bool IsModified() const;
bool IsTemporary() const;
bool IsRecovered() const;
void Reset();
void AutoSave();
void DeleteCurrentAutoSaveFile();
bool IsRecovered() const { return mIsRecovered; }
void SetIsRecovered( bool value ) { mIsRecovered = value; }
bool IsLoadedFromAup() const { return mbLoadedFromAup; }
void SetLoadedFromAup( bool value ) { mbLoadedFromAup = value; }
bool AutoSave(const WaveTrackArray *tracks = nullptr);
bool AutoSave(const AutoSaveFile &autosave);
bool AutoSaveDelete();
bool LoadProject(const FilePath &fileName);
bool SaveProject(const FilePath &fileName);
XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
void WriteXMLHeader(XMLWriter &xmlFile) const;
void WriteXML(XMLWriter &xmlFile, const WaveTrackArray *tracks = nullptr) /* not override */;
// If the second argument is not null, that means we are saving a
// compressed project, and the wave tracks have been exported into the
// named files
void WriteXML(
XMLWriter &xmlFile, FilePaths *strOtherNamesArray) /* not override */;
wxLongLong GetFreeDiskSpace();
const TranslatableString & GetLastError() const;
private:
// XMLTagHandler callback methods
@ -69,19 +78,52 @@ private:
void UpdatePrefs() override;
using ExecCB = std::function<int(wxString *result, int cols, char **vals, char **names)>;
using ExecFunc = int (*)(void *data, int cols, char **vals, char **names);
struct ExecParm
{
ExecCB func;
wxString *result;
};
static int ExecCallback(void *data, int cols, char **vals, char **names);
int Exec(const char *query, ExecCB callback, wxString *result);
sqlite3 *DB();
sqlite3 *OpenDB(FilePath fileName = {});
bool CloseDB();
bool DeleteDB();
bool CleanDB();
wxString GetValue(const char *sql);
bool GetBlob(const char *sql, wxMemoryBuffer &buffer);
bool CheckVersion();
bool InstallSchema();
bool UpgradeSchema();
bool CopyTo(const FilePath &destpath);
private:
// non-static data members
AudacityProject &mProject;
// Last auto-save file name and path (empty if none)
FilePath mAutoSaveFileName;
// Are we currently auto-saving or not?
bool mAutoSaving{ false };
// The project's file path
FilePath mFileName;
// Has this project been recovered from an auto-saved version
bool mIsRecovered{ false };
bool mRecovered;
bool mbLoadedFromAup{ false };
// Has this project been modified
bool mModified;
// Is this project still a temporary/unsaved project
bool mTemporary;
sqlite3 *mDB;
FilePath mDBPath;
TranslatableString mLastError;
friend SampleBlock;
};
class wxTopLevelWindow;

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,6 @@ Paul Licameli split from AudacityProject.h
class wxString;
class wxFileName;
class AudacityProject;
class ImportXMLTagHandler;
class RecordingRecoveryHandler;
class Track;
class TrackList;
class WaveTrack;
@ -48,7 +46,7 @@ public:
bool decodeError;
bool parseSuccess;
bool trackError;
TranslatableString errorString;
const TranslatableString errorString;
wxString helpUrl;
};
ReadProjectResults ReadProjectFile( const FilePath &fileName );
@ -58,9 +56,8 @@ public:
void CloseLock();
bool Save();
bool SaveAs(bool bWantSaveCopy = false, bool bLossless = false);
bool SaveAs(const wxString & newFileName, bool bWantSaveCopy = false,
bool addToHistory = true);
bool SaveAs();
bool SaveAs(const wxString & newFileName, bool addToHistory = true);
// strProjectPathName is full path for aup except extension
bool SaveFromTimerRecording( wxFileName fnFile );
@ -104,32 +101,13 @@ public:
bool GetMenuClose() const { return mMenuClose; }
void SetMenuClose(bool value) { mMenuClose = value; }
private:
void SetImportedDependencies( bool value ) { mImportedDependencies = value; }
// Push names of NEW export files onto the path list
bool SaveCopyWaveTracks(const FilePath & strProjectPathName,
bool bLossless, FilePaths &strOtherNamesArray);
bool DoSave(bool fromSaveAs, bool bWantSaveCopy, bool bLossless = false);
// Declared in this class so that they can have access to private members
static XMLTagHandler *RecordingRecoveryFactory( AudacityProject &project );
static ProjectFileIORegistry::Entry sRecoveryFactory;
static XMLTagHandler *ImportHandlerFactory( AudacityProject &project );
static ProjectFileIORegistry::Entry sImportHandlerFactory;
private:
bool DoSave(const FilePath & fileName, bool fromSaveAs);
AudacityProject &mProject;
std::shared_ptr<TrackList> mLastSavedTracks;
// The handler that handles recovery of <recordingrecovery> tags
std::unique_ptr<RecordingRecoveryHandler> mRecordingRecoveryHandler;
std::unique_ptr<ImportXMLTagHandler> mImportXMLTagHandler;
// Dependencies have been imported and a warning should be shown on save
bool mImportedDependencies{ false };
// Are we currently closing as the result of a menu command?
bool mMenuClose{ false };
};

View File

@ -15,12 +15,9 @@ Paul Licameli split from AudacityProject.cpp
#include "AdornedRulerPanel.h"
#include "AudioIO.h"
#include "AutoRecovery.h"
#include "BlockFile.h"
#include "Clipboard.h"
#include "DirManager.h"
#include "FileNames.h"
#include "Menus.h"
#include "MissingAliasFileDialog.h"
#include "ModuleManager.h"
#include "Project.h"
#include "ProjectAudioIO.h"
@ -541,7 +538,6 @@ AudacityProject *ProjectManager::New()
ProjectFileIO::Get( *p ).SetProjectTitle();
MissingAliasFilesDialog::SetShouldShow(true);
MenuManager::Get( project ).CreateMenusAndCommands( project );
projectHistory.InitialState();
@ -593,10 +589,6 @@ AudacityProject *ProjectManager::New()
return p;
}
// LL: All objects that have a reference to the DirManager should
// be deleted before the final mDirManager->Deref() in this
// routine. Failing to do so can cause unwanted recursion
// and/or attempts to DELETE objects twice.
void ProjectManager::OnCloseWindow(wxCloseEvent & event)
{
auto &project = mProject;
@ -697,7 +689,7 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
// The project is now either saved or the user doesn't want to save it,
// so there's no need to keep auto save info around anymore
projectFileIO.DeleteCurrentAutoSaveFile();
projectFileIO.AutoSaveDelete();
// DMM: Save the size of the last window the user closes
//
@ -756,15 +748,6 @@ void ProjectManager::OnCloseWindow(wxCloseEvent & event)
// references to the DirManager.
UndoManager::Get( project ).ClearStates();
// MM: Tell the DirManager it can now DELETE itself
// if it finds it is no longer needed. If it is still
// used (f.e. by the clipboard), it will recognize this
// and will destroy itself later.
//
// LL: All objects with references to the DirManager should
// have been deleted before this.
DirManager::Destroy( project );
// Remove self from the global array, but defer destruction of self
auto pSelf = AllProjects{}.Remove( project );
wxASSERT( pSelf );
@ -914,7 +897,6 @@ void ProjectManager::ResetProjectToEmpty() {
TrackUtilities::DoRemoveTracks( project );
// A new DirManager.
DirManager::Reset( project );
TrackFactory::Reset( project );
projectFileManager.Reset();
@ -936,7 +918,6 @@ void ProjectManager::OnTimer(wxTimerEvent& WXUNUSED(event))
{
auto &project = mProject;
auto &projectAudioIO = ProjectAudioIO::Get( project );
auto &dirManager = DirManager::Get( project );
auto mixerToolBar = &MixerToolBar::Get( project );
mixerToolBar->UpdateControls();
@ -944,7 +925,7 @@ void ProjectManager::OnTimer(wxTimerEvent& WXUNUSED(event))
// gAudioIO->GetNumCaptureChannels() should only be positive
// when we are recording.
if (projectAudioIO.GetAudioIOToken() > 0 && gAudioIO->GetNumCaptureChannels() > 0) {
wxLongLong freeSpace = dirManager.GetFreeDiskSpace();
wxLongLong freeSpace = ProjectFileIO::Get(project).GetFreeDiskSpace();
if (freeSpace >= 0) {
int iRecordingMins = GetEstimatedRecordingMinsLeftOnDisk(gAudioIO->GetNumCaptureChannels());
@ -1030,7 +1011,7 @@ int ProjectManager::GetEstimatedRecordingMinsLeftOnDisk(long lCaptureChannels) {
}
// Find out how much free space we have on disk
wxLongLong lFreeSpace = DirManager::Get( project ).GetFreeDiskSpace();
wxLongLong lFreeSpace = ProjectFileIO::Get( project ).GetFreeDiskSpace();
if (lFreeSpace < 0) {
return 0;
}

730
src/SampleBlock.cpp Normal file
View File

@ -0,0 +1,730 @@
/**********************************************************************
Audacity: A Digital Audio Editor
SampleBlock.cpp
**********************************************************************/
#include "Audacity.h"
#include "SampleBlock.h"
#include <float.h>
#include <wx/defs.h>
#include "ProjectFileIO.h"
#include "SampleFormat.h"
#include "xml/XMLWriter.h"
// static
SampleBlockPtr SampleBlock::Create(AudacityProject *project,
samplePtr src,
size_t numsamples,
sampleFormat srcformat)
{
auto sb = std::make_shared<SampleBlock>(project);
if (sb)
{
if (sb->SetSamples(src, numsamples, srcformat))
{
return sb;
}
}
return nullptr;
}
// static
SampleBlockPtr SampleBlock::CreateSilent(AudacityProject *project,
size_t numsamples,
sampleFormat srcformat)
{
auto sb = std::make_shared<SampleBlock>(project);
if (sb)
{
if (sb->SetSilent(numsamples, srcformat))
{
return sb;
}
}
return nullptr;
}
// static
SampleBlockPtr SampleBlock::CreateFromXML(AudacityProject *project,
sampleFormat srcformat,
const wxChar **attrs)
{
auto sb = std::make_shared<SampleBlock>(project);
sb->mSampleFormat = srcformat;
int found = 0;
// loop through attrs, which is a null-terminated list of attribute-value pairs
while(*attrs)
{
const wxChar *attr = *attrs++;
const wxChar *value = *attrs++;
if (!value)
{
break;
}
const wxString strValue = value; // promote string, we need this for all
double dblValue;
long long nValue;
if (XMLValueChecker::IsGoodInt(strValue) && strValue.ToLongLong(&nValue) && (nValue >= 0))
{
if (wxStrcmp(attr, wxT("blockid")) == 0)
{
if (!sb->Load((SampleBlockID) nValue))
{
return nullptr;
}
found++;
}
else if (wxStrcmp(attr, wxT("samplecount")) == 0)
{
sb->mSampleCount = nValue;
sb->mSampleBytes = sb->mSampleCount * SAMPLE_SIZE(sb->mSampleFormat);
found++;
}
}
else if (XMLValueChecker::IsGoodString(strValue) && Internat::CompatibleToDouble(strValue, &dblValue))
{
if (wxStricmp(attr, wxT("min")) == 0)
{
sb->mSumMin = dblValue;
found++;
}
else if (wxStricmp(attr, wxT("max")) == 0)
{
sb->mSumMax = dblValue;
found++;
}
else if ((wxStricmp(attr, wxT("rms")) == 0) && (dblValue >= 0.0))
{
sb->mSumRms = dblValue;
found++;
}
}
}
// Were all attributes found?
if (found != 5)
{
return nullptr;
}
return sb;
}
// static
SampleBlockPtr SampleBlock::Get(AudacityProject *project,
SampleBlockID sbid)
{
auto sb = std::make_shared<SampleBlock>(project);
if (sb)
{
if (!sb->Load(sbid))
{
return nullptr;
}
}
return sb;
}
SampleBlock::SampleBlock(AudacityProject *project)
: mProject(project),
mIO(ProjectFileIO::Get(*project))
{
mValid = false;
mSilent = false;
mRefCnt = 0;
mBlockID = 0;
mSampleFormat = floatSample;
mSampleBytes = 0;
mSampleCount = 0;
mSummary256Bytes = 0;
mSummary64kBytes = 0;
mSumMin = 0.0;
mSumMax = 0.0;
mSumRms = 0.0;
}
SampleBlock::~SampleBlock()
{
if (mRefCnt == 0)
{
Delete();
}
}
void SampleBlock::Lock()
{
++mRefCnt;
}
void SampleBlock::Unlock()
{
--mRefCnt;
}
void SampleBlock::CloseLock()
{
Lock();
}
SampleBlockID SampleBlock::GetBlockID()
{
return mBlockID;
}
sampleFormat SampleBlock::GetSampleFormat() const
{
return mSampleFormat;
}
size_t SampleBlock::GetSampleCount() const
{
return mSampleCount;
}
size_t SampleBlock::GetSamples(samplePtr dest,
sampleFormat destformat,
size_t sampleoffset,
size_t numsamples)
{
return GetBlob(dest,
destformat,
"samples",
mSampleFormat,
sampleoffset * SAMPLE_SIZE(mSampleFormat),
numsamples * SAMPLE_SIZE(mSampleFormat)) / SAMPLE_SIZE(mSampleFormat);
}
bool SampleBlock::SetSamples(samplePtr src,
size_t numsamples,
sampleFormat srcformat)
{
mSampleFormat = srcformat;
mSampleCount = numsamples;
mSampleBytes = mSampleCount * SAMPLE_SIZE(mSampleFormat);
mSamples.reinit(mSampleBytes);
memcpy(mSamples.get(), src, mSampleBytes);
CalcSummary();
return Commit();
}
bool SampleBlock::SetSilent(size_t numsamples, sampleFormat srcformat)
{
mSampleFormat = srcformat;
mSampleCount = numsamples;
mSampleBytes = mSampleCount * SAMPLE_SIZE(mSampleFormat);
mSamples.reinit(mSampleBytes);
memset(mSamples.get(), 0, mSampleBytes);
CalcSummary();
mSilent = true;
return Commit();
}
bool SampleBlock::GetSummary256(float *dest,
size_t frameoffset,
size_t numframes)
{
return GetSummary(dest, frameoffset, numframes, "summary256", mSummary256Bytes);
}
bool SampleBlock::GetSummary64k(float *dest,
size_t frameoffset,
size_t numframes)
{
return GetSummary(dest, frameoffset, numframes, "summary64k", mSummary64kBytes);
}
bool SampleBlock::GetSummary(float *dest,
size_t frameoffset,
size_t numframes,
const char *srccolumn,
size_t srcbytes)
{
return GetBlob(dest,
floatSample,
srccolumn,
floatSample,
frameoffset * 3 * SAMPLE_SIZE(floatSample),
numframes * 3 * SAMPLE_SIZE(floatSample)) / 3 / SAMPLE_SIZE(floatSample);
}
double SampleBlock::GetSumMin() const
{
return mSumMin;
}
double SampleBlock::GetSumMax() const
{
return mSumMax;
}
double SampleBlock::GetSumRms() const
{
return mSumRms;
}
/// Retrieves the minimum, maximum, and maximum RMS of the
/// specified sample data in this block.
///
/// @param start The offset in this block where the region should begin
/// @param len The number of samples to include in the region
MinMaxRMS SampleBlock::GetMinMaxRMS(size_t start, size_t len) const
{
float min = FLT_MAX;
float max = -FLT_MAX;
float sumsq = 0;
if (mValid && start < mSampleCount)
{
float *samples = &((float *) mSamples.get())[start];
len = std::min(len, mSampleCount - start);
for (int i = 0; i < len; ++i, ++samples)
{
float sample = *samples;
if (sample > max)
{
max = sample;
}
if (sample < min)
{
min = sample;
}
sumsq += (sample * sample);
}
}
return { min, max, (float) sqrt(sumsq / len) };
}
/// Retrieves the minimum, maximum, and maximum RMS of this entire
/// block. This is faster than the other GetMinMax function since
/// these values are already computed.
MinMaxRMS SampleBlock::GetMinMaxRMS() const
{
return { (float) mSumMin, (float) mSumMax, (float) mSumRms };
}
size_t SampleBlock::GetSpaceUsage() const
{
return mSampleCount * SAMPLE_SIZE(mSampleFormat);
}
size_t SampleBlock::GetBlob(void *dest,
sampleFormat destformat,
const char *srccolumn,
sampleFormat srcformat,
size_t srcoffset,
size_t srcbytes)
{
wxASSERT(mBlockID > 0);
if (!mValid && mBlockID)
{
Load(mBlockID);
}
int rc;
size_t minbytes = 0;
char sql[256];
sqlite3_snprintf(sizeof(sql),
sql,
"SELECT %s FROM sampleblocks WHERE blockid = %d;",
srccolumn,
mBlockID);
sqlite3_stmt *stmt = nullptr;
auto cleanup = finally([&]
{
if (stmt)
{
sqlite3_finalize(stmt);
}
});
rc = sqlite3_prepare_v2(mIO.DB(), sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
}
else
{
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
}
else
{
samplePtr src = (samplePtr) sqlite3_column_blob(stmt, 0);
size_t blobbytes = (size_t) sqlite3_column_bytes(stmt, 0);
srcoffset = std::min(srcoffset, blobbytes);
minbytes = std::min(srcbytes, blobbytes - srcoffset);
if (srcoffset != 0)
{
srcoffset += 0;
}
CopySamples(src + srcoffset,
srcformat,
(samplePtr) dest,
destformat,
minbytes / SAMPLE_SIZE(srcformat));
dest = ((samplePtr) dest) + minbytes;
}
}
if (srcbytes - minbytes)
{
memset(dest, 0, srcbytes - minbytes);
}
return srcbytes;
}
bool SampleBlock::Load(SampleBlockID sbid)
{
wxASSERT(sbid > 0);
int rc;
mValid = false;
mSummary256Bytes = 0;
mSummary64kBytes = 0;
mSampleCount = 0;
mSampleBytes = 0;
char sql[256];
sqlite3_snprintf(sizeof(sql),
sql,
"SELECT sampleformat, summin, summax, sumrms,"
" length('summary256'), length('summary64k'), length('samples')"
" FROM sampleblocks WHERE blockid = %d;",
sbid);
sqlite3_stmt *stmt = nullptr;
auto cleanup = finally([&]
{
if (stmt)
{
sqlite3_finalize(stmt);
}
});
rc = sqlite3_prepare_v2(mIO.DB(), sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
// handle error
return false;
}
rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
// handle error
return false;
}
mBlockID = sbid;
mSampleFormat = (sampleFormat) sqlite3_column_int(stmt, 0);
mSumMin = sqlite3_column_double(stmt, 1);
mSumMax = sqlite3_column_double(stmt, 2);
mSumRms = sqlite3_column_double(stmt, 3);
mSummary256Bytes = sqlite3_column_int(stmt, 4);
mSummary64kBytes = sqlite3_column_int(stmt, 5);
mSampleBytes = sqlite3_column_int(stmt, 6);
mSampleCount = mSampleBytes / SAMPLE_SIZE(mSampleFormat);
mValid = true;
return true;
}
bool SampleBlock::Commit()
{
int rc;
char sql[256];
sqlite3_snprintf(sizeof(sql),
sql,
"INSERT INTO sampleblocks (%s) VALUES(?,?,?,?,?,?,?);",
columns);
sqlite3_stmt *stmt = nullptr;
auto cleanup = finally([&]
{
if (stmt)
{
sqlite3_finalize(stmt);
}
});
rc = sqlite3_prepare_v2(mIO.DB(), sql, -1, &stmt, 0);
if (rc != SQLITE_OK)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
// handle error
return false;
}
sqlite3_bind_int(stmt, 1, mSampleFormat);
sqlite3_bind_double(stmt, 2, mSumMin);
sqlite3_bind_double(stmt, 3, mSumMax);
sqlite3_bind_double(stmt, 4, mSumRms);
sqlite3_bind_blob(stmt, 5, mSummary256.get(), mSummary256Bytes, SQLITE_STATIC);
sqlite3_bind_blob(stmt, 6, mSummary64k.get(), mSummary64kBytes, SQLITE_STATIC);
sqlite3_bind_blob(stmt, 7, mSamples.get(), mSampleBytes, SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
// handle error
return false;
}
mBlockID = sqlite3_last_insert_rowid(mIO.DB());
mSamples.reset();
mSummary256.reset();
mSummary64k.reset();
mValid = true;
return true;
}
void SampleBlock::Delete()
{
if (mBlockID)
{
int rc;
char sql[256];
sqlite3_snprintf(sizeof(sql),
sql,
"DELETE FROM sampleblocks WHERE blockid = %lld;",
mBlockID);
rc = sqlite3_exec(mIO.DB(), sql, nullptr, nullptr, nullptr);
if (rc != SQLITE_OK)
{
wxLogDebug(wxT("SQLITE error %s"), sqlite3_errmsg(mIO.DB()));
// handle error
return;
}
}
}
void SampleBlock::SaveXML(XMLWriter &xmlFile)
{
xmlFile.WriteAttr(wxT("blockid"), mBlockID);
xmlFile.WriteAttr(wxT("samplecount"), mSampleCount);
xmlFile.WriteAttr(wxT("len256"), mSummary256Bytes);
xmlFile.WriteAttr(wxT("len64k"), mSummary64kBytes);
xmlFile.WriteAttr(wxT("min"), mSumMin);
xmlFile.WriteAttr(wxT("max"), mSumMax);
xmlFile.WriteAttr(wxT("rms"), mSumRms);
}
/// Calculates summary block data describing this sample data.
///
/// This method also has the side effect of setting the mSumMin,
/// mSumMax, and mSumRms members of this class.
///
/// @param buffer A buffer containing the sample data to be analyzed
/// @param len The length of the sample data
/// @param format The format of the sample data.
void SampleBlock::CalcSummary()
{
Floats samplebuffer;
float *samples;
if (mSampleFormat == floatSample)
{
samples = (float *) mSamples.get();
}
else
{
samplebuffer.reinit((unsigned) mSampleCount);
CopySamples(mSamples.get(),
mSampleFormat,
(samplePtr) samplebuffer.get(),
floatSample,
mSampleCount);
samples = samplebuffer.get();
}
int fields = 3; /* min, max, rms */
int bytesPerFrame = fields * sizeof(float);
int frames64k = (mSampleCount + 65535) / 65536;
int frames256 = frames64k * 256;
mSummary256Bytes = frames256 * bytesPerFrame;
mSummary64kBytes = frames64k * bytesPerFrame;
mSummary256.reinit(mSummary256Bytes);
mSummary64k.reinit(mSummary64kBytes);
float *summary256 = (float *) mSummary256.get();
float *summary64k = (float *) mSummary64k.get();
float min;
float max;
float sumsq;
double totalSquares = 0.0;
double fraction = 0.0;
// Recalc 256 summaries
int sumLen = (mSampleCount + 255) / 256;
int summaries = 256;
for (int i = 0; i < sumLen; ++i)
{
min = samples[i * 256];
max = samples[i * 256];
sumsq = min * min;
int jcount = 256;
if (jcount > mSampleCount - i * 256)
{
jcount = mSampleCount - i * 256;
fraction = 1.0 - (jcount / 256.0);
}
for (int j = 1; j < jcount; ++j)
{
float f1 = samples[i * 256 + j];
sumsq += f1 * f1;
if (f1 < min)
{
min = f1;
}
else if (f1 > max)
{
max = f1;
}
}
totalSquares += sumsq;
summary256[i * 3] = min;
summary256[i * 3 + 1] = max;
// The rms is correct, but this may be for less than 256 samples in last loop.
summary256[i * 3 + 2] = (float) sqrt(sumsq / jcount);
}
for (int i = sumLen; i < frames256; ++i)
{
// filling in the remaining bits with non-harming/contributing values
// rms values are not "non-harming", so keep count of them:
summaries--;
summary256[i * 3] = FLT_MAX; // min
summary256[i * 3 + 1] = -FLT_MAX; // max
summary256[i * 3 + 2] = 0.0f; // rms
}
// Calculate now while we can do it accurately
mSumRms = sqrt(totalSquares / mSampleCount);
// Recalc 64K summaries
sumLen = (mSampleCount + 65535) / 65536;
for (int i = 0; i < sumLen; ++i)
{
min = summary256[3 * i * 256];
max = summary256[3 * i * 256 + 1];
sumsq = summary256[3 * i * 256 + 2];
sumsq *= sumsq;
for (int j = 1; j < 256; ++j)
{
// we can overflow the useful summary256 values here, but have put
// non-harmful values in them
if (summary256[3 * (i * 256 + j)] < min)
{
min = summary256[3 * (i * 256 + j)];
}
if (summary256[3 * (i * 256 + j) + 1] > max)
{
max = summary256[3 * (i * 256 + j) + 1];
}
float r1 = summary256[3 * (i * 256 + j) + 2];
sumsq += r1 * r1;
}
double denom = (i < sumLen - 1) ? 256.0 : summaries - fraction;
float rms = (float) sqrt(sumsq / denom);
summary64k[i * 3] = min;
summary64k[i * 3 + 1] = max;
summary64k[i * 3 + 2] = rms;
}
for (int i = sumLen; i < frames64k; ++i)
{
wxASSERT_MSG(false, wxT("Out of data for mSummaryInfo")); // Do we ever get here?
summary64k[i * 3] = 0.0f; // probably should be FLT_MAX, need a test case
summary64k[i * 3 + 1] = 0.0f; // probably should be -FLT_MAX, need a test case
summary64k[i * 3 + 2] = 0.0f; // just padding
}
// Recalc block-level summary (mRMS already calculated)
min = summary64k[0];
max = summary64k[1];
for (int i = 1; i < sumLen; ++i)
{
if (summary64k[i * 3] < min)
{
min = summary64k[i * 3];
}
if (summary64k[i * 3 + 1] > max)
{
max = summary64k[i * 3 + 1];
}
}
mSumMin = min;
mSumMax = max;
}

137
src/SampleBlock.h Normal file
View File

@ -0,0 +1,137 @@
/**********************************************************************
Audacity: A Digital Audio Editor
SampleBlock.h
**********************************************************************/
#ifndef __AUDACITY_SAMPLE_BLOCK__
#define __AUDACITY_SAMPLE_BLOCK__
#include "ClientData.h" // to inherit
#include <sqlite3.h>
class AudacityProject;
class ProjectFileIO;
class XMLWriter;
class SampleBlock;
using SampleBlockPtr = std::shared_ptr<SampleBlock>;
using SampleBlockID = sqlite3_int64;
class MinMaxRMS
{
public:
float min;
float max;
float RMS;
};
class SampleBlock
{
public:
SampleBlock(AudacityProject *project);
virtual ~SampleBlock();
static SampleBlockPtr Get(AudacityProject *project,
SampleBlockID sbid);
static SampleBlockPtr Create(AudacityProject *project,
samplePtr src,
size_t numsamples,
sampleFormat srcformat);
static SampleBlockPtr CreateSilent(AudacityProject *project,
size_t numsamples,
sampleFormat srcformat);
static SampleBlockPtr CreateFromXML(AudacityProject *project,
sampleFormat srcformat,
const wxChar **attrs);
void Lock();
void Unlock();
void CloseLock();
bool SetSamples(samplePtr src, size_t numsamples, sampleFormat srcformat);
bool SetSilent(size_t numsamples, sampleFormat srcformat);
bool Commit();
void Delete();
SampleBlockID GetBlockID();
size_t GetSamples(samplePtr dest,
sampleFormat destformat,
size_t sampleoffset,
size_t numsamples);
sampleFormat GetSampleFormat() const;
size_t GetSampleCount() const;
bool GetSummary256(float *dest, size_t frameoffset, size_t numframes);
bool GetSummary64k(float *dest, size_t frameoffset, size_t numframes);
double GetSumMin() const;
double GetSumMax() const;
double GetSumRms() const;
/// Gets extreme values for the specified region
MinMaxRMS GetMinMaxRMS(size_t start, size_t len) const;
/// Gets extreme values for the entire block
MinMaxRMS GetMinMaxRMS() const;
size_t GetSpaceUsage() const;
void SaveXML(XMLWriter &xmlFile);
private:
bool Load(SampleBlockID sbid);
bool GetSummary(float *dest,
size_t frameoffset,
size_t numframes,
const char *srccolumn,
size_t srcbytes);
size_t GetBlob(void *dest,
sampleFormat destformat,
const char *srccolumn,
sampleFormat srcformat,
size_t srcoffset,
size_t srcbytes);
void CalcSummary();
private:
AudacityProject *mProject;
ProjectFileIO & mIO;
bool mValid;
bool mDirty;
bool mSilent;
int mRefCnt;
SampleBlockID mBlockID;
ArrayOf<char> mSamples;
size_t mSampleBytes;
size_t mSampleCount;
sampleFormat mSampleFormat;
ArrayOf<char> mSummary256;
size_t mSummary256Bytes;
ArrayOf<char> mSummary64k;
size_t mSummary64kBytes;
double mSumMin;
double mSumMax;
double mSumRms;
const char *columns =
"sampleformat, summin, summax, sumrms, summary256, summary64k, samples";
friend class ProjectFileIO;
#if defined(WORDS_BIGENDIAN)
#error All sample block data is little endian...big endian not yet supported
#endif
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -18,31 +18,29 @@
#include "audacity/Types.h"
class BlockFile;
using BlockFilePtr = std::shared_ptr<BlockFile>;
class DirManager;
class wxFileNameWrapper;
class AudacityProject;
class SampleBlock;
// This is an internal data structure! For advanced use only.
class SeqBlock {
public:
BlockFilePtr f;
using SampleBlockPtr = std::shared_ptr<SampleBlock>;
SampleBlockPtr sb;
///the sample in the global wavetrack that this block starts at.
sampleCount start;
SeqBlock()
: f{}, start(0)
: sb{}, start(0)
{}
SeqBlock(const BlockFilePtr &f_, sampleCount start_)
: f(f_), start(start_)
SeqBlock(const SampleBlockPtr &sb_, sampleCount start_)
: sb(sb_), start(start_)
{}
// Construct a SeqBlock with changed start, same file
SeqBlock Plus(sampleCount delta) const
{
return SeqBlock(f, start + delta);
return SeqBlock(sb, start + delta);
}
};
class BlockArray : public std::vector<SeqBlock> {};
@ -62,15 +60,10 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
// Constructor / Destructor / Duplicator
//
Sequence(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format);
Sequence(AudacityProject *project, sampleFormat format);
// The copy constructor and duplicate operators take a
// DirManager as a parameter, because you might be copying
// from one project to another...
Sequence(const Sequence &orig, const std::shared_ptr<DirManager> &projDirManager);
Sequence(const Sequence &orig, AudacityProject *project);
// Sequence cannot be copied without specifying a DirManager
Sequence(const Sequence&) PROHIBITED;
Sequence& operator= (const Sequence&) PROHIBITED;
~Sequence();
@ -87,7 +80,7 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
// Note that len is not size_t, because nullptr may be passed for buffer, in
// which case, silence is inserted, possibly a large amount.
void SetSamples(samplePtr buffer, sampleFormat format,
sampleCount start, sampleCount len);
sampleCount start, sampleCount len);
// where is input, assumed to be nondecreasing, and its size is len + 1.
// min, max, rms, bl are outputs, and their lengths are len.
@ -104,28 +97,13 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
void Paste(sampleCount s0, const Sequence *src);
size_t GetIdealAppendLen() const;
void Append(samplePtr buffer, sampleFormat format, size_t len,
XMLWriter* blockFileLog=NULL);
void Append(samplePtr buffer, sampleFormat format, size_t len);
void Delete(sampleCount start, sampleCount len);
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 SimpleBlockFile or
// loaded from an XML file via DirManager::HandleXMLTag
void AppendBlockFile(const BlockFilePtr &blockFile);
void SetSilence(sampleCount s0, sampleCount len);
void InsertSilence(sampleCount s0, sampleCount len);
const std::shared_ptr<DirManager> &GetDirManager() { return mDirManager; }
AudacityProject *GetProject() { return mProject; }
//
// XMLTagHandler callback methods for loading and saving
@ -200,7 +178,7 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
// Private variables
//
std::shared_ptr<DirManager> mDirManager;
AudacityProject *mProject;
BlockArray mBlock;
sampleFormat mSampleFormat;
@ -219,23 +197,34 @@ class PROFILE_DLL_API Sequence final : public XMLTagHandler{
int FindBlock(sampleCount pos) const;
static void AppendBlock
(DirManager &dirManager,
BlockArray &blocks, sampleCount &numSamples, const SeqBlock &b);
static void AppendBlock(BlockArray &blocks,
sampleCount &numSamples,
const SeqBlock &b);
static bool Read(samplePtr buffer, sampleFormat format,
const SeqBlock &b,
size_t blockRelativeStart, size_t len, bool mayThrow);
static bool Read(samplePtr buffer,
sampleFormat format,
const SeqBlock &b,
size_t blockRelativeStart,
size_t len,
bool mayThrow);
// Accumulate NEW block files onto the end of a block array.
// Does not change this sequence. The intent is to use
// CommitChangesIfConsistent later.
static void Blockify
(DirManager &dirManager, size_t maxSamples, sampleFormat format,
BlockArray &list, sampleCount start, samplePtr buffer, size_t len);
static void Blockify(AudacityProject *project,
size_t maxSamples,
sampleFormat format,
BlockArray &list,
sampleCount start,
samplePtr buffer,
size_t len);
bool Get(int b, samplePtr buffer, sampleFormat format,
sampleCount start, size_t len, bool mayThrow) const;
bool Get(int b,
samplePtr buffer,
sampleFormat format,
sampleCount start,
size_t len,
bool mayThrow) const;
public:

View File

@ -38,7 +38,7 @@
std::shared_ptr<TimeTrack> TrackFactory::NewTimeTrack()
{
return std::make_shared<TimeTrack>(mDirManager, mZoomInfo);
return std::make_shared<TimeTrack>(&mProject, mZoomInfo);
}
static ProjectFileIORegistry::Entry registerFactory{
@ -53,8 +53,8 @@ static ProjectFileIORegistry::Entry registerFactory{
}
};
TimeTrack::TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const ZoomInfo *zoomInfo):
Track(projDirManager)
TimeTrack::TimeTrack(AudacityProject *project, const ZoomInfo *zoomInfo):
Track(project)
, mZoomInfo(zoomInfo)
{
mEnvelope = std::make_unique<BoundedEnvelope>(true, TIMETRACK_MIN, TIMETRACK_MAX, 1.0);

View File

@ -25,7 +25,7 @@ class TimeTrack final : public Track {
public:
TimeTrack(const std::shared_ptr<DirManager> &projDirManager, const ZoomInfo *zoomInfo);
TimeTrack(AudacityProject *project, const ZoomInfo *zoomInfo);
/** @brief Copy-Constructor - create a NEW TimeTrack:: which is an independent copy of the original
*
* Calls TimeTrack::Init() to copy the track metadata, then does a bunch of manipulations on the

View File

@ -40,11 +40,9 @@
#include <wx/timer.h>
#include <wx/dynlib.h> //<! For windows.h
#include "DirManager.h"
#include "ShuttleGui.h"
#include "MissingAliasFileDialog.h"
#include "Project.h"
#include "ProjectAudioManager.h"
#include "ProjectFileIO.h"
#include "ProjectFileManager.h"
#include "ProjectManager.h"
#include "Prefs.h"
@ -186,10 +184,6 @@ TimerRecordDialog::TimerRecordDialog(
m_timer.SetOwner(this, TIMER_ID);
m_timer.Start(kSlowTimerInterval);
// Do we need to tidy up when the timer recording has been completed?
m_bProjectCleanupRequired = !(this->HaveFilesToRecover());
}
TimerRecordDialog::~TimerRecordDialog()
@ -305,11 +299,13 @@ void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
// New events for timer recording automation
void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
{
auto &projectFileIO = ProjectFileIO::Get(mProject);
wxString fName = FileNames::SelectFile(FileNames::Operation::Export,
XO("Save Timer Recording As"),
m_fnAutoSaveFile.GetPath(),
m_fnAutoSaveFile.GetFullName(),
wxT("aup"),
wxT("aup3"),
{ FileNames::AudacityProjects },
wxFD_SAVE | wxRESIZE_BORDER,
this);
@ -317,11 +313,9 @@ void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(even
if (fName.empty())
return;
AudacityProject* pProject = &mProject;
// If project already exists then abort - we do not allow users to overwrite an existing project
// unless it is the current project.
if (wxFileExists(fName) && (pProject->GetFileName() != fName)) {
if (wxFileExists(fName) && (projectFileIO.GetFileName() != fName)) {
AudacityMessageDialog m(
nullptr,
XO("The selected file name could not be used\nfor Timer Recording because it \
@ -334,10 +328,10 @@ would overwrite another project.\nPlease try again and select an original name."
// Set this boolean to false so we now do a SaveAs at the end of the recording
// unless we're saving the current project.
m_bProjectAlreadySaved = pProject->GetFileName() == fName? true : false;
m_bProjectAlreadySaved = projectFileIO.GetFileName() == fName? true : false;
m_fnAutoSaveFile = fName;
m_fnAutoSaveFile.SetExt(wxT("aup"));
m_fnAutoSaveFile.SetExt(wxT("aup3"));
this->UpdateTextBoxControls();
}
@ -411,8 +405,7 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
// We don't stop the user from starting the recording
// as its possible that they plan to free up some
// space before the recording begins
AudacityProject* pProject = &mProject;
auto &projectManager = ProjectManager::Get( *pProject );
auto &projectManager = ProjectManager::Get( mProject );
// How many minutes do we have left on the disc?
int iMinsLeft = projectManager.GetEstimatedRecordingMinsLeftOnDisk();
@ -481,53 +474,10 @@ void TimerRecordDialog::UpdateTextBoxControls() {
}
}
// Copied from AutoRecovery.cpp - for use with Timer Recording Improvements
bool TimerRecordDialog::HaveFilesToRecover()
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened()) {
AudacityMessageBox(
XO("Could not enumerate files in auto save directory."),
XO("Error"),
wxICON_STOP);
return false;
}
wxString filename;
bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
return c;
}
bool TimerRecordDialog::RemoveAllAutoSaveFiles()
{
FilePaths files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.size(); i++)
{
if (!wxRemoveFile(files[i]))
{
// I don't think this error message is actually useful.
// -dmazzoni
//AudacityMessageBox(
// XO("Could not remove auto save file: %s)".Format( files[i] ),
// XO("Error"),
// wxICON_STOP);
return false;
}
}
return true;
}
/// Runs the wait for start dialog. Returns -1 if the user clicks stop while we are recording
/// or if the post recording actions fail.
int TimerRecordDialog::RunWaitDialog()
{
AudacityProject* pProject = &mProject;
auto updateResult = ProgressResult::Success;
if (m_DateTime_Start > wxDateTime::UNow())
@ -538,7 +488,7 @@ int TimerRecordDialog::RunWaitDialog()
return POST_TIMER_RECORD_CANCEL_WAIT;
} else {
// Record for specified time.
ProjectAudioManager::Get( *pProject ).OnRecord(false);
ProjectAudioManager::Get( mProject ).OnRecord(false);
bool bIsRecording = true;
auto sPostAction = Verbatim(
@ -587,7 +537,7 @@ int TimerRecordDialog::RunWaitDialog()
// Must do this AFTER the timer project dialog has been deleted to ensure the application
// responds to the AUDIOIO events...see not about bug #334 in the ProgressDialog constructor.
ProjectAudioManager::Get( *pProject ).Stop();
ProjectAudioManager::Get( mProject ).Stop();
// Let the caller handle cancellation or failure from recording progress.
if (updateResult == ProgressResult::Cancelled || updateResult == ProgressResult::Failed)
@ -607,8 +557,6 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// Finally, if there is no post-record action selected then we output
// a dialog detailing what has been carried out instead.
AudacityProject* pProject = &mProject;
bool bSaveOK = false;
bool bExportOK = false;
int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
@ -618,7 +566,7 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// Do Automatic Save?
if (m_bAutoSaveEnabled) {
auto &projectFileManager = ProjectFileManager::Get( *pProject );
auto &projectFileManager = ProjectFileManager::Get( mProject );
// MY: If this project has already been saved then simply execute a Save here
if (m_bProjectAlreadySaved) {
bSaveOK = projectFileManager.Save();
@ -630,9 +578,8 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// Do Automatic Export?
if (m_bAutoExportEnabled) {
Exporter e{ mProject };
MissingAliasFilesDialog::SetShouldShow(true);
bExportOK = e.ProcessFromTimerRecording(
false, 0.0, TrackList::Get( *pProject ).GetEndTime(),
false, 0.0, TrackList::Get( mProject ).GetEndTime(),
m_fnAutoExportFile, m_iAutoExportFormat,
m_iAutoExportSubFormat, m_iAutoExportFilterIndex);
}
@ -716,31 +663,11 @@ int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
if (iDelayOutcome != ProgressResult::Success) {
// Cancel the action!
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
// Set this to true to avoid any chance of the temp files being deleted
bErrorOverride = true;
break;
}
// If we have simply recorded, exported and then plan to Exit/Restart/Shutdown
// then we will have a temporary project setup. Let's get rid of that!
if (m_bAutoExportEnabled && !m_bAutoSaveEnabled) {
// PRL: Move the following cleanup into a finally?
// No, I think you would want to skip this, in case recording
// succeeded but then save or export threw an exception.
DirManager::CleanTempDir();
}
} while (false);
}
// Do we need to cleanup the orphaned temporary project?
if (m_bProjectCleanupRequired && !bErrorOverride) {
// PRL: Move the following cleanup into a finally?
// No, I think you would want to skip this, in case recording
// succeeded but then save or export threw an exception.
RemoveAllAutoSaveFiles();
}
// Return the action as required
return iPostRecordAction;
}
@ -917,8 +844,7 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S)
S.StartMultiColumn(3, wxEXPAND);
{
TranslatableString sInitialValue;
AudacityProject* pProject = &mProject;
auto sSaveValue = pProject->GetFileName();
auto sSaveValue = ProjectFileIO::Get(mProject).GetFileName();
if (!sSaveValue.empty()) {
m_fnAutoSaveFile.Assign(sSaveValue);
sInitialValue = XO("Current Project");

View File

@ -116,9 +116,6 @@ private:
// Timer Recording Automation Routines
void EnableDisableAutoControls(bool bEnable, int iControlGoup);
void UpdateTextBoxControls();
// Tidy up after Timer Recording
bool HaveFilesToRecover();
bool RemoveAllAutoSaveFiles();
// Add Path Controls to Form
TimerRecordPathCtrl *NewPathControl(

View File

@ -38,7 +38,6 @@ and TimeTrack.
#include "tracks/ui/CommonTrackPanelCell.h"
#include "Project.h"
#include "ProjectSettings.h"
#include "DirManager.h"
#include "InconsistencyException.h"
@ -47,9 +46,9 @@ and TimeTrack.
#pragma warning( disable : 4786 )
#endif
Track::Track(const std::shared_ptr<DirManager> &projDirManager)
Track::Track(AudacityProject *project)
: vrulerSize(36,0),
mDirManager(projDirManager)
mProject(project)
{
mSelected = false;
mLinked = false;
@ -77,7 +76,7 @@ void Track::Init(const Track &orig)
mDefaultName = orig.mDefaultName;
mName = orig.mName;
mDirManager = orig.mDirManager;
mProject = orig.mProject;
mSelected = orig.mSelected;
mLinked = orig.mLinked;
@ -1285,11 +1284,10 @@ bool TrackList::HasPendingTracks() const
#include "ViewInfo.h"
static auto TrackFactoryFactory = []( AudacityProject &project ) {
auto &dirManager = DirManager::Get( project );
auto &viewInfo = ViewInfo::Get( project );
return std::make_shared< TrackFactory >(
ProjectSettings::Get( project ),
dirManager.shared_from_this(), &viewInfo );
project, &viewInfo );
};
static const AudacityProject::AttachedObjects::RegisteredFactory key2{

View File

@ -30,7 +30,6 @@
class wxTextFile;
class CommonTrackCell;
class DirManager;
class Track;
class AudioTrack;
class PlayableTrack;
@ -310,11 +309,11 @@ private:
ChannelType mChannel;
double mOffset;
mutable std::shared_ptr<DirManager> mDirManager;
mutable AudacityProject *mProject;
public:
Track(const std::shared_ptr<DirManager> &projDirManager);
Track(AudacityProject *project);
Track(const Track &orig);
virtual ~ Track();
@ -353,11 +352,11 @@ public:
virtual void SetPan( float ){ ;}
virtual void SetPanFromChannelType(){ ;};
// AS: Note that the dirManager is mutable. This is
// AS: Note that the project is mutable. This is
// mostly to support "Duplicate" of const objects,
// but in general, mucking with the dir manager is
// separate from the Track.
const std::shared_ptr<DirManager> &GetDirManager() const { return mDirManager; }
AudacityProject *GetProject() const { return mProject; }
// Create a NEW track and modify this track
// Return non-NULL or else throw
@ -717,8 +716,8 @@ protected:
class AUDACITY_DLL_API AudioTrack /* not final */ : public Track
{
public:
AudioTrack(const std::shared_ptr<DirManager> &projDirManager)
: Track{ projDirManager } {}
AudioTrack(AudacityProject *project)
: Track{ project } {}
AudioTrack(const Track &orig) : Track{ orig } {}
// Serialize, not with tags of its own, but as attributes within a tag.
@ -732,8 +731,8 @@ public:
class AUDACITY_DLL_API PlayableTrack /* not final */ : public AudioTrack
{
public:
PlayableTrack(const std::shared_ptr<DirManager> &projDirManager)
: AudioTrack{ projDirManager } {}
PlayableTrack(AudacityProject *project)
: AudioTrack{ project } {}
PlayableTrack(const Track &orig) : AudioTrack{ orig } {}
bool GetMute () const { return mMute; }
@ -1410,12 +1409,6 @@ public:
double GetMinOffset() const;
#if LEGACY_PROJECT_FILE_SUPPORT
// File I/O
bool Load(wxTextFile * in, DirManager * dirManager) override;
bool Save(wxTextFile * out, bool overwrite) override;
#endif
private:
// Visit all tracks satisfying a predicate, mutative access
@ -1584,9 +1577,9 @@ class AUDACITY_DLL_API TrackFactory final
static void Destroy( AudacityProject &project );
TrackFactory( const ProjectSettings &settings,
const std::shared_ptr<DirManager> &dirManager, const ZoomInfo *zoomInfo)
AudacityProject &project, const ZoomInfo *zoomInfo)
: mSettings{ settings }
, mDirManager(dirManager)
, mProject(project)
, mZoomInfo(zoomInfo)
{
}
@ -1595,7 +1588,7 @@ class AUDACITY_DLL_API TrackFactory final
private:
const ProjectSettings &mSettings;
const std::shared_ptr<DirManager> mDirManager;
AudacityProject &mProject;
const ZoomInfo *const mZoomInfo;
friend class AudacityProject;
public:

View File

@ -25,10 +25,10 @@ UndoManager
#include <wx/hashset.h>
#include "BlockFile.h"
#include "Clipboard.h"
#include "Diags.h"
#include "Project.h"
#include "SampleBlock.h"
#include "Sequence.h"
#include "WaveClip.h"
#include "WaveTrack.h" // temp
@ -44,8 +44,8 @@ wxDEFINE_EVENT(EVT_UNDO_MODIFIED, wxCommandEvent);
wxDEFINE_EVENT(EVT_UNDO_OR_REDO, wxCommandEvent);
wxDEFINE_EVENT(EVT_UNDO_RESET, wxCommandEvent);
using ConstBlockFilePtr = const BlockFile*;
using Set = std::unordered_set<ConstBlockFilePtr>;
using ConstSampleBlockPtr = const SampleBlock*;
using Set = std::unordered_set<ConstSampleBlockPtr>;
struct UndoStackElem {
@ -108,19 +108,19 @@ namespace {
auto blocks = clip->GetSequenceBlockArray();
for (const auto &block : *blocks)
{
const auto &file = block.f;
const auto &sb = block.sb;
// Accumulate space used by the file if the file was not
// yet seen
if ( !seen || (seen->count( &*file ) == 0 ) )
if ( !seen || (seen->count( &*sb ) == 0 ) )
{
unsigned long long usage{ file->GetSpaceUsage() };
unsigned long long usage{ sb->GetSpaceUsage() };
result += usage;
}
// Add file to current set
if (seen)
seen->insert( &*file );
seen->insert( &*sb );
}
}
}

View File

@ -119,12 +119,12 @@ static void ComputeSpectrumUsingRealFFTf
}
}
WaveClip::WaveClip(const std::shared_ptr<DirManager> &projDirManager,
WaveClip::WaveClip(AudacityProject *project,
sampleFormat format, int rate, int colourIndex)
{
mRate = rate;
mColourIndex = colourIndex;
mSequence = std::make_unique<Sequence>(projDirManager, format);
mSequence = std::make_unique<Sequence>(project, format);
mEnvelope = std::make_unique<Envelope>(true, 1e-7, 2.0, 1.0);
@ -134,17 +134,17 @@ WaveClip::WaveClip(const std::shared_ptr<DirManager> &projDirManager,
}
WaveClip::WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
AudacityProject *project,
bool copyCutlines)
{
// essentially a copy constructor - but you must pass in the
// current project's DirManager, because we might be copying
// current project, because we might be copying
// from one project to another
mOffset = orig.mOffset;
mRate = orig.mRate;
mColourIndex = orig.mColourIndex;
mSequence = std::make_unique<Sequence>(*orig.mSequence, projDirManager);
mSequence = std::make_unique<Sequence>(*orig.mSequence, project);
mEnvelope = std::make_unique<Envelope>(*orig.mEnvelope);
@ -155,13 +155,13 @@ WaveClip::WaveClip(const WaveClip& orig,
if ( copyCutlines )
for (const auto &clip: orig.mCutLines)
mCutLines.push_back
( std::make_unique<WaveClip>( *clip, projDirManager, true ) );
( std::make_unique<WaveClip>( *clip, project, true ) );
mIsPlaceholder = orig.GetIsPlaceholder();
}
WaveClip::WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
AudacityProject *project,
bool copyCutlines,
double t0, double t1)
{
@ -199,7 +199,7 @@ WaveClip::WaveClip(const WaveClip& orig,
if (cutlinePosition >= t0 && cutlinePosition <= t1)
{
auto newCutLine =
std::make_unique< WaveClip >( *clip, projDirManager, true );
std::make_unique< WaveClip >( *clip, project, true );
newCutLine->SetOffset( cutlinePosition - t0 );
mCutLines.push_back(std::move(newCutLine));
}
@ -1203,8 +1203,7 @@ void WaveClip::GetDisplayRect(wxRect* r)
}
void WaveClip::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */,
XMLWriter* blockFileLog /*=NULL*/)
size_t len, unsigned int stride /* = 1 */)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
@ -1228,8 +1227,7 @@ void WaveClip::Append(samplePtr buffer, sampleFormat format,
if (mAppendBufferLen >= blockSize) {
// flush some previously appended contents
// use STRONG-GUARANTEE
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize,
blockFileLog);
mSequence->Append(mAppendBuffer.ptr(), seqFormat, blockSize);
// use NOFAIL-GUARANTEE for rest of this "if"
memmove(mAppendBuffer.ptr(),
@ -1259,17 +1257,6 @@ void WaveClip::Append(samplePtr buffer, sampleFormat format,
}
}
void WaveClip::AppendBlockFile( const BlockFileFactory &factory, size_t len)
// STRONG-GUARANTEE
{
// use STRONG-GUARANTEE
mSequence->AppendBlockFile( factory, 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:
@ -1351,7 +1338,7 @@ XMLTagHandler *WaveClip::HandleXMLChild(const wxChar *tag)
{
// Nested wave clips are cut lines
mCutLines.push_back(
std::make_unique<WaveClip>(mSequence->GetDirManager(),
std::make_unique<WaveClip>(mSequence->GetProject(),
mSequence->GetSampleFormat(), mRate, 0 /*colourindex*/));
return mCutLines.back().get();
}
@ -1387,7 +1374,7 @@ void WaveClip::Paste(double t0, const WaveClip* other)
if (clipNeedsResampling || clipNeedsNewFormat)
{
newClip =
std::make_unique<WaveClip>(*other, mSequence->GetDirManager(), true);
std::make_unique<WaveClip>(*other, mSequence->GetProject(), true);
if (clipNeedsResampling)
// The other clip's rate is different from ours, so resample
newClip->Resample(mRate);
@ -1408,7 +1395,7 @@ void WaveClip::Paste(double t0, const WaveClip* other)
{
newCutlines.push_back(
std::make_unique<WaveClip>
( *cutline, mSequence->GetDirManager(),
( *cutline, mSequence->GetProject(),
// Recursively copy cutlines of cutlines. They don't need
// their offsets adjusted.
true));
@ -1545,7 +1532,7 @@ void WaveClip::ClearAndAddCutLine(double t0, double t1)
const double clip_t1 = std::min( t1, GetEndTime() );
auto newClip = std::make_unique< WaveClip >
(*this, mSequence->GetDirManager(), true, clip_t0, clip_t1);
(*this, mSequence->GetProject(), true, clip_t0, clip_t1);
newClip->SetOffset( clip_t0 - mOffset );
@ -1715,7 +1702,7 @@ void WaveClip::Resample(int rate, ProgressDialog *progress)
auto numSamples = mSequence->GetNumSamples();
auto newSequence =
std::make_unique<Sequence>(mSequence->GetDirManager(), mSequence->GetSampleFormat());
std::make_unique<Sequence>(mSequence->GetProject(), mSequence->GetSampleFormat());
/**
* We want to keep going as long as we have something to feed the resampler

View File

@ -23,10 +23,10 @@
#include <vector>
class AudacityProject;
class BlockArray;
class BlockFile;
using BlockFilePtr = std::shared_ptr<BlockFile>;
class DirManager;
class Envelope;
class ProgressDialog;
class Sequence;
@ -172,26 +172,26 @@ public:
class AUDACITY_DLL_API WaveClip final : public XMLTagHandler
{
private:
// It is an error to copy a WaveClip without specifying the DirManager.
// It is an error to copy a WaveClip without specifying the project.
WaveClip(const WaveClip&) PROHIBITED;
WaveClip& operator= (const WaveClip&) PROHIBITED;
public:
// typical constructor
WaveClip(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format,
WaveClip(AudacityProject *project, sampleFormat format,
int rate, int colourIndex);
// essentially a copy constructor - but you must pass in the
// current project's DirManager, because we might be copying
// current project, because we might be copying
// from one project to another
WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
AudacityProject *project,
bool copyCutlines);
// Copy only a range from the given WaveClip
WaveClip(const WaveClip& orig,
const std::shared_ptr<DirManager> &projDirManager,
AudacityProject *project,
bool copyCutlines,
double t0, double t1);
@ -280,15 +280,10 @@ public:
/// You must call Flush after the last Append
void Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride=1,
XMLWriter* blockFileLog = NULL);
size_t len, unsigned int stride=1);
/// Flush must be called after last Append
void Flush();
using BlockFileFactory =
std::function< BlockFilePtr( wxFileNameWrapper, size_t /* len */ ) >;
void AppendBlockFile( const BlockFileFactory &factory, size_t len);
/// This name is consistent with WaveTrack::Clear. It performs a "Cut"
/// operation (but without putting the cutted audio to the clipboard)
void Clear(double t0, double t1);

View File

@ -91,11 +91,11 @@ WaveTrack::Holder TrackFactory::NewWaveTrack(sampleFormat format, double rate)
format = QualityPrefs::SampleFormatChoice();
if (rate == 0)
rate = mSettings.GetRate();
return std::make_shared<WaveTrack> ( mDirManager, format, rate );
return std::make_shared<WaveTrack> ( &mProject, format, rate );
}
WaveTrack::WaveTrack(const std::shared_ptr<DirManager> &projDirManager, sampleFormat format, double rate) :
PlayableTrack(projDirManager)
WaveTrack::WaveTrack(AudacityProject *project, sampleFormat format, double rate) :
PlayableTrack(project)
{
mLegacyProjectFileOffset = 0;
@ -113,7 +113,6 @@ WaveTrack::WaveTrack(const std::shared_ptr<DirManager> &projDirManager, sampleFo
mSpectrumMin = mSpectrumMax = -1; // so values will default to settings
mLastScaleType = -1;
mLastdBRange = -1;
mAutoSaveIdent = 0;
}
WaveTrack::WaveTrack(const WaveTrack &orig):
@ -136,7 +135,7 @@ WaveTrack::WaveTrack(const WaveTrack &orig):
for (const auto &clip : orig.mClips)
mClips.push_back
( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
( std::make_unique<WaveClip>( *clip, mProject, true ) );
}
// Copy the track metadata but not the contents.
@ -531,11 +530,11 @@ void WaveTrack::Trim (double t0, double t1)
WaveTrack::Holder WaveTrack::EmptyCopy(
const std::shared_ptr<DirManager> &pDirManager ) const
AudacityProject *pProject ) const
{
auto result = std::make_shared<WaveTrack>( mDirManager, mFormat, mRate );
auto result = std::make_shared<WaveTrack>( mProject, mFormat, mRate );
result->Init(*this);
result->mDirManager = pDirManager ? pDirManager : mDirManager;
result->mProject = pProject ? pProject : mProject;
return result;
}
@ -559,7 +558,7 @@ Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
//wxPrintf("copy: clip %i is in copy region\n", (int)clip);
newTrack->mClips.push_back
(std::make_unique<WaveClip>(*clip, mDirManager, ! forClipboard));
(std::make_unique<WaveClip>(*clip, mProject, ! forClipboard));
WaveClip *const newClip = newTrack->mClips.back().get();
newClip->Offset(-t0);
}
@ -572,7 +571,7 @@ Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
const double clip_t1 = std::min(t1, clip->GetEndTime());
auto newClip = std::make_unique<WaveClip>
(*clip, mDirManager, ! forClipboard, clip_t0, clip_t1);
(*clip, mProject, ! forClipboard, clip_t0, clip_t1);
//wxPrintf("copy: clip_t0=%f, clip_t1=%f\n", clip_t0, clip_t1);
@ -591,7 +590,7 @@ Track::Holder WaveTrack::Copy(double t0, double t1, bool forClipboard) const
if (forClipboard &&
newTrack->GetEndTime() + 1.0 / newTrack->GetRate() < t1 - t0)
{
auto placeholder = std::make_unique<WaveClip>(mDirManager,
auto placeholder = std::make_unique<WaveClip>(mProject,
newTrack->GetSampleFormat(),
static_cast<int>(newTrack->GetRate()),
0 /*colourindex*/);
@ -983,7 +982,7 @@ void WaveTrack::HandleClear(double t0, double t1,
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
auto newClip = std::make_unique<WaveClip>( *clip, mProject, true );
newClip->ClearAndAddCutLine( t0, t1 );
clipsToAdd.push_back( std::move( newClip ) );
}
@ -998,7 +997,7 @@ void WaveTrack::HandleClear(double t0, double t1,
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
auto newClip = std::make_unique<WaveClip>( *clip, mProject, true );
newClip->Clear(clip->GetStartTime(), t1);
newClip->Offset(t1-clip->GetStartTime());
@ -1010,7 +1009,7 @@ void WaveTrack::HandleClear(double t0, double t1,
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
auto newClip = std::make_unique<WaveClip>( *clip, mProject, true );
newClip->Clear(t0, clip->GetEndTime());
clipsToAdd.push_back( std::move( newClip ) );
@ -1021,12 +1020,12 @@ void WaveTrack::HandleClear(double t0, double t1,
// left
clipsToAdd.push_back
( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
( std::make_unique<WaveClip>( *clip, mProject, true ) );
clipsToAdd.back()->Clear(t0, clip->GetEndTime());
// right
clipsToAdd.push_back
( std::make_unique<WaveClip>( *clip, mDirManager, true ) );
( std::make_unique<WaveClip>( *clip, mProject, true ) );
WaveClip *const right = clipsToAdd.back().get();
right->Clear(clip->GetStartTime(), t1);
right->Offset(t1 - clip->GetStartTime());
@ -1040,7 +1039,7 @@ void WaveTrack::HandleClear(double t0, double t1,
// Don't modify this clip in place, because we want a strong
// guarantee, and might modify another clip
clipsToDelete.push_back( clip.get() );
auto newClip = std::make_unique<WaveClip>( *clip, mDirManager, true );
auto newClip = std::make_unique<WaveClip>( *clip, mProject, true );
// clip->Clear keeps points < t0 and >= t1 via Envelope::CollapseRegion
newClip->Clear(t0,t1);
@ -1106,7 +1105,7 @@ void WaveTrack::SyncLockAdjust(double oldT1, double newT1)
// AWD: Could just use InsertSilence() on its own here, but it doesn't
// follow EditClipCanMove rules (Paste() does it right)
auto tmp = std::make_shared<WaveTrack>(
mDirManager, GetSampleFormat(), GetRate() );
mProject, GetSampleFormat(), GetRate() );
tmp->InsertSilence(0.0, newT1 - oldT1);
tmp->Flush();
@ -1261,7 +1260,7 @@ void WaveTrack::Paste(double t0, const Track *src)
if (!clip->GetIsPlaceholder())
{
auto newClip =
std::make_unique<WaveClip>( *clip, mDirManager, true );
std::make_unique<WaveClip>( *clip, mProject, true );
newClip->Resample(mRate);
newClip->Offset(t0);
newClip->MarkChanged();
@ -1323,7 +1322,7 @@ void WaveTrack::InsertSilence(double t, double len)
if (mClips.empty())
{
// Special case if there is no clip yet
auto clip = std::make_unique<WaveClip>(mDirManager, mFormat, mRate, this->GetWaveColorIndex());
auto clip = std::make_unique<WaveClip>(mProject, mFormat, mRate, this->GetWaveColorIndex());
clip->InsertSilence(0, len);
// use NOFAIL-GUARANTEE
mClips.push_back( std::move( clip ) );
@ -1483,14 +1482,12 @@ void WaveTrack::Join(double t0, double t1)
}
void WaveTrack::Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride /* = 1 */,
XMLWriter *blockFileLog /* = NULL */)
size_t len, unsigned int stride /* = 1 */)
// PARTIAL-GUARANTEE in case of exceptions:
// Some prefix (maybe none) of the buffer is appended, and no content already
// flushed to disk is lost.
{
RightmostOrNewClip()->Append(buffer, format, len, stride,
blockFileLog);
RightmostOrNewClip()->Append(buffer, format, len, stride);
}
sampleCount WaveTrack::GetBlockStart(sampleCount s) const
@ -1536,7 +1533,7 @@ size_t WaveTrack::GetMaxBlockSize() const
{
// We really need the maximum block size, so create a
// temporary sequence to get it.
maxblocksize = Sequence{ mDirManager, mFormat }.GetMaxBlockSize();
maxblocksize = Sequence{ mProject, mFormat }.GetMaxBlockSize();
}
wxASSERT(maxblocksize > 0);
@ -1613,9 +1610,6 @@ bool WaveTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
else if (!wxStrcmp(attr, wxT("linked")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
SetLinked(nValue != 0);
else if (!wxStrcmp(attr, wxT("autosaveid")) &&
XMLValueChecker::IsGoodInt(strValue) && strValue.ToLong(&nValue))
mAutoSaveIdent = (int) nValue;
else if (!wxStrcmp(attr, wxT("colorindex")) &&
XMLValueChecker::IsGoodString(strValue) &&
strValue.ToLong(&nValue))
@ -1675,10 +1669,6 @@ void WaveTrack::WriteXML(XMLWriter &xmlFile) const
// may throw
{
xmlFile.StartTag(wxT("wavetrack"));
if (mAutoSaveIdent)
{
xmlFile.WriteAttr(wxT("autosaveid"), mAutoSaveIdent);
}
this->Track::WriteCommonXMLAttributes( xmlFile );
xmlFile.WriteAttr(wxT("channel"), mChannel);
xmlFile.WriteAttr(wxT("linked"), mLinked);
@ -2119,7 +2109,7 @@ Sequence* WaveTrack::GetSequenceAtX(int xcoord)
WaveClip* WaveTrack::CreateClip()
{
mClips.push_back(std::make_unique<WaveClip>(mDirManager, mFormat, mRate, GetWaveColorIndex()));
mClips.push_back(std::make_unique<WaveClip>(mProject, mFormat, mRate, GetWaveColorIndex()));
return mClips.back().get();
}
@ -2279,7 +2269,7 @@ void WaveTrack::SplitAt(double t)
if (c->WithinClip(t))
{
t = LongSamplesToTime(TimeToLongSamples(t)); // put t on a sample
auto newClip = std::make_unique<WaveClip>( *c, mDirManager, true );
auto newClip = std::make_unique<WaveClip>( *c, mProject, true );
c->Clear(t, c->GetEndTime());
newClip->Clear(c->GetStartTime(), t);
@ -2481,16 +2471,6 @@ void WaveTrack::ClearWaveCaches()
clip->ClearWaveCache();
}
int WaveTrack::GetAutoSaveIdent() const
{
return mAutoSaveIdent;
}
void WaveTrack::SetAutoSaveIdent(int ident)
{
mAutoSaveIdent = ident;
}
WaveTrackCache::~WaveTrackCache()
{
}

View File

@ -68,8 +68,7 @@ public:
// Constructor / Destructor / Duplicator
//
WaveTrack(const std::shared_ptr<DirManager> &projDirManager,
sampleFormat format, double rate);
WaveTrack(AudacityProject *project, sampleFormat format, double rate);
WaveTrack(const WaveTrack &orig);
// overwrite data excluding the sample sequence but including display
@ -160,12 +159,11 @@ private:
// Make another track copying format, rate, color, etc. but containing no
// clips
// It is important to pass the correct DirManager (that for the project
// It is important to pass the correct project (that for the project
// which will own the copy) in the unusual case that a track is copied from
// another project or the clipboard. For copies within one project, the
// default will do.
Holder EmptyCopy(
const std::shared_ptr<DirManager> &pDirManager = {} ) const;
Holder EmptyCopy(AudacityProject *pProject = {} ) const;
// If forClipboard is true,
// and there is no clip at the end time of the selection, then the result
@ -222,8 +220,7 @@ private:
* one is created.
*/
void Append(samplePtr buffer, sampleFormat format,
size_t len, unsigned int stride=1,
XMLWriter* blockFileLog=NULL);
size_t len, unsigned int stride=1);
/// Flush must be called after last Append
void Flush();
@ -524,14 +521,6 @@ private:
// Resample track (i.e. all clips in the track)
void Resample(int rate, ProgressDialog *progress = NULL);
//
// AutoSave related
//
// Retrieve the unique autosave ID
int GetAutoSaveIdent() const;
// Set the unique autosave ID
void SetAutoSaveIdent(int id);
int GetLastScaleType() const { return mLastScaleType; }
void SetLastScaleType() const;
@ -591,7 +580,6 @@ private:
wxCriticalSection mFlushCriticalSection;
wxCriticalSection mAppendCriticalSection;
double mLegacyProjectFileOffset;
int mAutoSaveIdent;
std::unique_ptr<SpectrogramSettings> mpSpectrumSettings;
std::unique_ptr<WaveformSettings> mpWaveformSettings;

View File

@ -19,6 +19,7 @@
#include "LoadCommands.h"
#include "../Project.h"
#include "../ProjectFileIO.h"
#include "../ProjectFileManager.h"
#include "../ProjectManager.h"
#include "../export/Export.h"
@ -52,7 +53,9 @@ void OpenProjectCommand::PopulateOrExchange(ShuttleGui & S)
bool OpenProjectCommand::Apply(const CommandContext & context){
auto oldFileName = context.project.GetFileName();
auto &projectFileIO = ProjectFileIO::Get(context.project);
auto oldFileName = projectFileIO.GetFileName();
if(mFileName.empty())
{
auto project = &context.project;
@ -63,7 +66,7 @@ bool OpenProjectCommand::Apply(const CommandContext & context){
ProjectFileManager::Get( context.project )
.OpenFile(mFileName, mbAddToHistory);
}
const auto &newFileName = context.project.GetFileName();
const auto &newFileName = projectFileIO.GetFileName();
// Because Open does not return a success or failure, we have to guess
// at this point, based on whether the project file name has
@ -79,7 +82,6 @@ namespace{ BuiltinCommandsModule::Registration< SaveProjectCommand > reg2; }
bool SaveProjectCommand::DefineParams( ShuttleParams & S ){
S.Define( mFileName, wxT("Filename"), "name.aup" );
S.Define( mbAddToHistory, wxT("AddToHistory"), false );
S.Define( mbCompress, wxT("Compress"), false );
return true;
}
@ -91,7 +93,6 @@ void SaveProjectCommand::PopulateOrExchange(ShuttleGui & S)
{
S.TieTextBox(XXO("File Name:"),mFileName);
S.TieCheckBox(XXO("Add to History"), mbAddToHistory );
S.TieCheckBox(XXO("Compress"), mbCompress );
}
S.EndMultiColumn();
}
@ -100,8 +101,7 @@ bool SaveProjectCommand::Apply(const CommandContext &context)
{
auto &projectFileManager = ProjectFileManager::Get( context.project );
if ( mFileName.empty() )
return projectFileManager.SaveAs(mbCompress);
return projectFileManager.SaveAs();
else
return projectFileManager.SaveAs(
mFileName, mbCompress, mbAddToHistory);
return projectFileManager.SaveAs(mFileName, mbAddToHistory);
}

View File

@ -58,7 +58,5 @@ public:
public:
wxString mFileName;
bool mbAddToHistory;
bool mbCompress;
bool bHasAddToHistory;
bool bHasCompress;
};

View File

@ -16,6 +16,7 @@
#include "../WaveTrack.h"
#include "../Prefs.h"
#include "../Project.h"
#include "../ProjectFileIO.h"
#include "../ProjectSettings.h"
#include "../ProjectWindow.h"
#include "../ShuttleGui.h"
@ -557,7 +558,7 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
<< XO("WCAG 2.0 Success Criteria 1.4.7 Contrast Results") << '\n'
<< '\n'
<< XO("Filename = %s.").Format( project->GetFileName() ) << '\n'
<< XO("Filename = %s.").Format( ProjectFileIO::Get(*project).GetFileName() ) << '\n'
<< '\n'
<< XO("Foreground") << '\n';

View File

@ -1842,7 +1842,6 @@ wxDialog *EffectUI::DialogFactory( wxWindow &parent, EffectHostInterface *pHost,
return nullptr;
};
#include "../MissingAliasFileDialog.h"
#include "../PluginManager.h"
#include "../ProjectSettings.h"
#include "../ProjectWindow.h"
@ -1885,8 +1884,6 @@ wxDialog *EffectUI::DialogFactory( wxWindow &parent, EffectHostInterface *pHost,
SelectUtilities::SelectAllIfNone( project );
}
MissingAliasFilesDialog::SetShouldShow(true);
auto nTracksOriginally = tracks.size();
wxWindow *focus = wxWindow::FindFocus();
wxWindow *parent = nullptr;

View File

@ -54,7 +54,6 @@ effects from this one class.
#include <wx/stdpaths.h>
#include "../EffectManager.h"
#include "../../DirManager.h"
#include "../../FileNames.h"
#include "../../LabelTrack.h"
#include "../../NoteTrack.h"

View File

@ -50,7 +50,6 @@
#include "../widgets/FileDialog/FileDialog.h"
#include "../DirManager.h"
#include "../FileFormats.h"
#include "../Mix.h"
#include "../Prefs.h"
@ -745,31 +744,6 @@ bool Exporter::GetFilename()
continue;
}
// Check to see if we are writing to a path that a missing aliased file existed at.
// This causes problems for the exporter, so we don't allow it.
// Overwritting non-missing aliased files is okay.
// Also, this can only happen for uncompressed audio.
bool overwritingMissingAliasFiles;
overwritingMissingAliasFiles = false;
for (auto pProject : AllProjects{}) {
AliasedFileArray aliasedFiles;
FindDependencies(pProject.get(), aliasedFiles);
for (const auto &aliasedFile : aliasedFiles) {
if (mFilename.GetFullPath() == aliasedFile.mFileName.GetFullPath() &&
!mFilename.FileExists()) {
// Warn and return to the dialog
AudacityMessageBox(XO(
"You are attempting to overwrite an aliased file that is missing.\n\
The file cannot be written because the path is needed to restore the original audio to the project.\n\
Choose Help > Diagnostics > Check Dependencies to view the locations of all missing files.\n\
If you still wish to export, please choose a different filename or folder."));
overwritingMissingAliasFiles = true;
}
}
}
if (overwritingMissingAliasFiles)
continue;
// For Mac, it's handled by the FileDialog
#if !defined(__WXMAC__)
if (mFilename.FileExists()) {
@ -800,16 +774,6 @@ If you still wish to export, please choose a different filename or folder."));
//
bool Exporter::CheckFilename()
{
//
// Ensure that exporting a file by this name doesn't overwrite
// one of the existing files in the project. (If it would
// overwrite an existing file, DirManager tries to rename the
// existing file.)
//
if (!DirManager::Get( *mProject ).EnsureSafeFilename(mFilename))
return false;
if( mFormatName.empty() )
gPrefs->Write(wxT("/Export/Format"), mPlugins[mFormat]->GetFormat(mSubFormat));
gPrefs->Write(wxT("/Export/Path"), mFilename.GetPath());

View File

@ -28,7 +28,6 @@ class wxMemoryDC;
class wxSimplebook;
class wxStaticText;
class AudacityProject;
class DirManager;
class WaveTrack;
class Tags;
class TrackList;

View File

@ -37,7 +37,6 @@
#include <wx/textctrl.h>
#include <wx/textdlg.h>
#include "../DirManager.h"
#include "../FileFormats.h"
#include "../FileNames.h"
#include "../LabelTrack.h"
@ -1062,10 +1061,6 @@ ProgressResult ExportMultipleDialog::DoExport(std::unique_ptr<ProgressDialog> &p
wxFileName backup;
if (mOverwrite->GetValue()) {
// Make sure we don't overwrite (corrupt) alias files
if (!DirManager::Get( *mProject ).EnsureSafeFilename(inName)) {
return ProgressResult::Cancelled;
}
name = inName;
backup.Assign(name);

View File

@ -90,24 +90,10 @@ static void SaveOtherFormat(int val)
static int LoadEncoding(int type)
{
// LLL: Temporary hack until I can figure out how to add an "ExportPCMCommand"
// to create a 32-bit float WAV file. It tells the ExportPCM exporter
// to use float when exporting the next WAV file.
//
// This was done as part of the resolution for bug #2062.
//
// See: ProjectFileManager.cpp, ProjectFileManager::SaveCopyWaveTracks()
if (gPrefs->HasEntry(wxT("/FileFormats/ExportFormat_SF1_ForceFloat")))
{
gPrefs->DeleteEntry(wxT("/FileFormats/ExportFormat_SF1_ForceFloat"));
gPrefs->Flush();
return SF_FORMAT_FLOAT;
}
return gPrefs->Read(wxString::Format(wxT("/FileFormats/ExportFormat_SF1_Type/%s_%x"),
sf_header_shortname(type), type), (long int) 0);
}
static void SaveEncoding(int type, int val)
{
gPrefs->Write(wxString::Format(wxT("/FileFormats/ExportFormat_SF1_Type/%s_%x"),

View File

@ -785,7 +785,7 @@ bool Importer::Import( AudacityProject &project,
}
// Audacity project
if (extension.IsSameAs(wxT("aup"), false)) {
if (extension.IsSameAs(wxT("aup3"), false)) {
errorMessage = XO(
/* i18n-hint: %s will be the filename */
"\"%s\" is an Audacity Project file. \nUse the 'File > Open' command to open Audacity Projects.")

View File

@ -24,7 +24,6 @@ and sample size to help you importing data of an unknown format.
#include "../Audacity.h"
#include "ImportRaw.h"
#include "../DirManager.h"
#include "../FileFormats.h"
#include "../Prefs.h"
#include "../ShuttleGui.h"

View File

@ -15,7 +15,6 @@
class TrackFactory;
class WaveTrack;
class DirManager;
class wxString;
class wxWindow;

View File

@ -2,7 +2,6 @@
#include "../AdornedRulerPanel.h"
#include "../Clipboard.h"
#include "../CommonCommandFlags.h"
#include "../DirManager.h"
#include "../LabelTrack.h"
#include "../Menus.h"
#include "../NoteTrack.h"
@ -76,7 +75,6 @@ bool DoPasteText(AudacityProject &project)
bool DoPasteNothingSelected(AudacityProject &project)
{
auto &tracks = TrackList::Get( project );
auto &dirManager = DirManager::Get( project );
auto &trackFactory = TrackFactory::Get( project );
auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
auto &window = ProjectWindow::Get( project );
@ -103,7 +101,7 @@ bool DoPasteNothingSelected(AudacityProject &project)
// Cause duplication of block files on disk, when copy is
// between projects
locker.emplace(wc);
uNewTrack = wc->EmptyCopy( dirManager.shared_from_this() );
uNewTrack = wc->EmptyCopy();
pNewTrack = uNewTrack.get();
},
#ifdef USE_MIDI
@ -381,7 +379,6 @@ void OnPaste(const CommandContext &context)
{
auto &project = context.project;
auto &tracks = TrackList::Get( project );
auto &dirManager = DirManager::Get( project );
auto &trackFactory = TrackFactory::Get( project );
auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
const auto &settings = ProjectSettings::Get( project );
@ -599,7 +596,7 @@ void OnPaste(const CommandContext &context)
wt->ClearAndPaste(t0, t1, wc, true, true);
}
else {
auto tmp = wt->EmptyCopy( dirManager.shared_from_this() );
auto tmp = wt->EmptyCopy();
tmp->InsertSilence( 0.0,
// MJS: Is this correct?
clipboard.Duration() );

View File

@ -5,11 +5,11 @@
#include "../CommonCommandFlags.h"
#include "../FileNames.h"
#include "../LabelTrack.h"
#include "../MissingAliasFileDialog.h"
#include "../NoteTrack.h"
#include "../Prefs.h"
#include "../Printing.h"
#include "../Project.h"
#include "../ProjectFileIO.h"
#include "../ProjectFileManager.h"
#include "../ProjectHistory.h"
#include "../ProjectManager.h"
@ -38,16 +38,16 @@ namespace {
void DoExport( AudacityProject &project, const FileExtension & Format )
{
auto &tracks = TrackList::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
Exporter e{ project };
MissingAliasFilesDialog::SetShouldShow(true);
double t0 = 0.0;
double t1 = tracks.GetEndTime();
// Prompt for file name and/or extension?
bool bPromptingRequired =
(project.mBatchMode == 0) || project.GetFileName().empty() ||
(project.mBatchMode == 0) || projectFileIO.GetFileName().empty() ||
Format.empty();
wxString filename;
@ -58,7 +58,7 @@ void DoExport( AudacityProject &project, const FileExtension & Format )
extension.MakeLower();
filename =
MacroCommands::BuildCleanFileName(project.GetFileName(), extension);
MacroCommands::BuildCleanFileName(projectFileIO.GetFileName(), extension);
// Bug 1854, No warning of file overwrite
// (when export is called from Macros).
@ -72,7 +72,7 @@ void DoExport( AudacityProject &project, const FileExtension & Format )
number.Printf("%03i", counter);
// So now the name has a number in it too.
filename = MacroCommands::BuildCleanFileName(
project.GetFileName() + number, extension);
projectFileIO.GetFileName() + number, extension);
bPromptingRequired = wxFileExists(filename);
}
// If we've run out of alternative names, we will fall back to prompting
@ -155,22 +155,6 @@ void OnSaveAs(const CommandContext &context )
projectFileManager.SaveAs();
}
void OnSaveCopy(const CommandContext &context )
{
auto &project = context.project;
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveAs(true, true);
}
#ifdef USE_LIBVORBIS
void OnSaveCompressed(const CommandContext &context)
{
auto &project = context.project;
auto &projectFileManager = ProjectFileManager::Get( project );
projectFileManager.SaveAs(true);
}
#endif
void OnExportMp3(const CommandContext &context)
{
auto &project = context.project;
@ -201,7 +185,6 @@ void OnExportSelection(const CommandContext &context)
auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
Exporter e{ project };
MissingAliasFilesDialog::SetShouldShow(true);
e.SetFileDialogTitle( XO("Export Selected Audio") );
e.Process(true, selectedRegion.t0(),
selectedRegion.t1());
@ -274,7 +257,6 @@ void OnExportMultiple(const CommandContext &context)
auto &project = context.project;
ExportMultipleDialog em(&project);
MissingAliasFilesDialog::SetShouldShow(true);
em.ShowModal();
}
@ -371,10 +353,6 @@ void OnImport(const CommandContext &context)
auto &project = context.project;
auto &window = ProjectWindow::Get( project );
// An import trigger for the alias missing dialog might not be intuitive, but
// this serves to track the file if the users zooms in and such.
MissingAliasFilesDialog::SetShouldShow(true);
auto selectedFiles = ProjectFileManager::ShowOpenDialog();
if (selectedFiles.size() == 0) {
Importer::SetLastOpenType({});
@ -619,16 +597,7 @@ BaseItemSharedPtr FileMenu()
Command( wxT("Save"), XXO("&Save Project"), FN(OnSave),
AudioIONotBusyFlag() | UnsavedChangesFlag(), wxT("Ctrl+S") ),
Command( wxT("SaveAs"), XXO("Save Project &As..."), FN(OnSaveAs),
AudioIONotBusyFlag() ),
// TODO: The next two items should be disabled if project is empty
Command( wxT("SaveCopy"), XXO("Save Lossless Copy of Project..."),
FN(OnSaveCopy), AudioIONotBusyFlag() )
#ifdef USE_LIBVORBIS
,
Command( wxT("SaveCompressed"),
XXO("&Save Compressed Copy of Project..."),
FN(OnSaveCompressed), AudioIONotBusyFlag() )
#endif
AudioIONotBusyFlag() )
)
),

View File

@ -367,12 +367,6 @@ void OnCrashReport(const CommandContext &WXUNUSED(context) )
}
#endif
void OnCheckDependencies(const CommandContext &context)
{
auto &project = context.project;
::ShowDependencyDialogIfNeeded(&project, false);
}
void OnMenuTree(const CommandContext &context)
{
auto &project = context.project;
@ -533,11 +527,8 @@ BaseItemSharedPtr HelpMenu()
AlwaysEnabledFlag ),
#if defined(EXPERIMENTAL_CRASH_REPORT)
Command( wxT("CrashReport"), XXO("&Generate Support Data..."),
FN(OnCrashReport), AlwaysEnabledFlag ),
FN(OnCrashReport), AlwaysEnabledFlag )
#endif
Command( wxT("CheckDeps"), XXO("Chec&k Dependencies..."),
FN(OnCheckDependencies),
AudioIONotBusyFlag() )
#ifdef IS_ALPHA
,

View File

@ -484,9 +484,8 @@ void OnScreenshot(const CommandContext &context )
void OnBenchmark(const CommandContext &context)
{
auto &project = context.project;
auto &settings = ProjectSettings::Get( project );
auto &window = GetProjectFrame( project );
::RunBenchmark( &window, settings );
::RunBenchmark( &window, project);
}
void OnSimulateRecordingErrors(const CommandContext &context)

View File

@ -4,7 +4,6 @@
#include "../CommonCommandFlags.h"
#include "../LabelTrack.h"
#include "../Menus.h"
#include "../MissingAliasFileDialog.h"
#include "../Mix.h"
#include "../Prefs.h"
@ -58,8 +57,6 @@ void DoMixAndRender
auto &trackPanel = TrackPanel::Get( project );
auto &window = ProjectWindow::Get( project );
MissingAliasFilesDialog::SetShouldShow(true);
WaveTrack::Holder uNewLeft, uNewRight;
::MixAndRender(
&tracks, &trackFactory, rate, defaultFormat, 0.0, 0.0, uNewLeft, uNewRight);

View File

@ -305,7 +305,7 @@ void OnTimerRecord(const CommandContext &context)
// We use this variable to display "Current Project" in the Timer Recording
// save project field
bool bProjectSaved = ProjectFileIO::Get( project ).IsProjectSaved();
bool bProjectSaved = !ProjectFileIO::Get( project ).IsModified();
//we break the prompting and waiting dialogs into two sections
//because they both give the user a chance to click cancel

View File

@ -132,6 +132,45 @@ bool XMLFileReader::Parse(XMLTagHandler *baseHandler,
}
}
bool XMLFileReader::ParseString(XMLTagHandler *baseHandler,
const wxString &xmldata)
{
const char *buffer = xmldata.mb_str().data();
int len = xmldata.mbc_str().length();
mBaseHandler = baseHandler;
if (!XML_Parse(mParser, buffer, len, true))
{
// Embedded error string from expat doesn't translate (yet)
// We could make a table of XOs if we wanted so that it could
// If we do, uncomment the second constructor argument so it's not
// a verbatim string
mLibraryErrorStr = Verbatim(
XML_ErrorString(XML_GetErrorCode(mParser)) // , {}
);
mErrorStr = XO("Error: %s at line %lu").Format(
mLibraryErrorStr,
(long unsigned int)XML_GetCurrentLineNumber(mParser)
);
return false;
}
// Even though there were no parse errors, we only succeed if
// the first-level handler actually got called, and didn't
// return false.
if (!mBaseHandler)
{
mErrorStr = XO("Could not XML");
return false;
}
return true;
}
const TranslatableString &XMLFileReader::GetErrorStr() const
{
return mErrorStr;

View File

@ -24,6 +24,8 @@ class AUDACITY_DLL_API XMLFileReader final {
bool Parse(XMLTagHandler *baseHandler,
const FilePath &fname);
bool ParseString(XMLTagHandler *baseHandler,
const wxString &xmldata);
const TranslatableString &GetErrorStr() const;
const TranslatableString &GetLibraryErrorStr() const;

View File

@ -142,8 +142,6 @@ class XMLStringWriter final : public wxString, public XMLWriter {
void Write(const wxString &data) override;
wxString Get();
private:
};