1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

More exception-safety for recording...

... Do not call Autosave (which might fail) when recovering from exceptions in
recording, but rely on the last good Autosave that happened during recording.

If dropout labels are needed, then immediately modify the undo history item, but
we can accept it if that modification fails.

Also, be sure NOT to skip Autosave in all other places that push the undo
history.  Make Autosave the default unless otherwise specified.

Finally removed one unnecessary call to Autosave.
This commit is contained in:
Paul Licameli 2020-07-10 13:19:59 -04:00
parent 975ee0cc07
commit 06f22e942b
8 changed files with 56 additions and 48 deletions

View File

@ -28,6 +28,7 @@ Paul Licameli split from ProjectManager.cpp
#include "ProjectStatus.h"
#include "TimeTrack.h"
#include "TrackPanelAx.h"
#include "UndoManager.h"
#include "ViewInfo.h"
#include "WaveTrack.h"
#include "toolbars/ToolManager.h"
@ -870,6 +871,24 @@ void ProjectAudioManager::OnAudioIOStopRecording()
// Only push state if we were capturing and not monitoring
if (projectAudioIO.GetAudioIOToken() > 0)
{
auto &history = ProjectHistory::Get( project );
if (IsTimerRecordCancelled()) {
// discard recording
history.RollbackState();
// Reset timer record
ResetTimerRecordCancelled();
}
else {
// Add to history
// We want this to have NOFAIL-GUARANTEE if we get here from exception
// handling of recording, and that means we rely on the last autosave
// successully committed to the database, not risking a failure
history.PushState(XO("Recorded Audio"), XO("Record"),
UndoPush::NOAUTOSAVE);
// Now, we may add a label track to give information about
// dropouts. We allow failure of this.
auto &tracks = TrackList::Get( project );
auto gAudioIO = AudioIO::Get();
auto &intervals = gAudioIO->LostCaptureIntervals();
@ -890,6 +909,9 @@ void ProjectAudioManager::OnAudioIOStopRecording()
SelectedRegion{ interval.first,
interval.first + interval.second },
wxString::Format(wxT("%ld"), counter++));
history.ModifyState( true ); // this might fail and throw
ShowWarningDialog(&window, wxT("DropoutDetected"), XO("\
Recorded audio was lost at the labeled locations. Possible causes:\n\
\n\
@ -901,22 +923,8 @@ You are saving directly to a slow external storage device\n\
false,
XXO("Turn off dropout detection"));
}
auto &history = ProjectHistory::Get( project );
if (IsTimerRecordCancelled()) {
// discard recording
history.RollbackState();
// Reset timer record
ResetTimerRecordCancelled();
}
else
// Add to history
history.PushState(XO("Recorded Audio"), XO("Record"));
}
// Now we auto-save again to get the project to a "normal" state again.
projectFileIO.AutoSave();
}
void ProjectAudioManager::OnAudioIONewBlockFiles(const WaveTrackArray *tracks)

View File

@ -87,7 +87,7 @@ namespace {
void ProjectHistory::PushState(
const TranslatableString &desc, const TranslatableString &shortDesc)
{
PushState(desc, shortDesc, UndoPush::AUTOSAVE);
PushState(desc, shortDesc, UndoPush::NONE);
}
void ProjectHistory::PushState(const TranslatableString &desc,
@ -96,7 +96,7 @@ void ProjectHistory::PushState(const TranslatableString &desc,
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get( project );
if((flags & UndoPush::AUTOSAVE) != UndoPush::MINIMAL)
if((flags & UndoPush::NOAUTOSAVE) == UndoPush::NONE)
AutoSaveOrThrow( projectFileIO );
// remaining no-fail operations "commit" the changes of undo manager state

View File

@ -281,7 +281,7 @@ void UndoManager::PushState(const TrackList * l,
{
unsigned int i;
if ( ((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) &&
if ( (flags & UndoPush::CONSOLIDATE) != UndoPush::NONE &&
// compare full translations not msgids!
lastAction.Translation() == longDescription.Translation() &&
mayConsolidate ) {

View File

@ -97,9 +97,9 @@ using SpaceArray = std::vector <unsigned long long> ;
// Default is AUTOSAVE
// Frequent/faster actions use CONSOLIDATE
enum class UndoPush : unsigned char {
MINIMAL = 0,
NONE = 0,
CONSOLIDATE = 1 << 0,
AUTOSAVE = 1 << 1
NOAUTOSAVE = 1 << 1
};
inline UndoPush operator | (UndoPush a, UndoPush b)
@ -126,7 +126,7 @@ class AUDACITY_DLL_API UndoManager final
const std::shared_ptr<Tags> &tags,
const TranslatableString &longDescription,
const TranslatableString &shortDescription,
UndoPush flags = UndoPush::AUTOSAVE);
UndoPush flags = UndoPush::NONE);
void ModifyState(const TrackList * l,
const SelectedRegion &selectedRegion, const std::shared_ptr<Tags> &tags);
void ClearStates();

View File

@ -168,7 +168,7 @@ void DoPanTracks(AudacityProject &project, float PanValue)
for (auto left : count == 0 ? range : selectedRange )
left->SetPan( PanValue );
auto flags = UndoPush::AUTOSAVE;
auto flags = UndoPush::NONE;
ProjectHistory::Get( project )
/*i18n-hint: One or more audio tracks have been panned*/
.PushState(XO("Panned audio track(s)"), XO("Pan Track"), flags);
@ -799,7 +799,7 @@ void OnResample(const CommandContext &context)
}
int ndx = 0;
auto flags = UndoPush::AUTOSAVE;
auto flags = UndoPush::NONE;
for (auto wt : tracks.Selected< WaveTrack >())
{
auto msg = XO("Resampling track %d").Format( ++ndx );

View File

@ -261,7 +261,7 @@ UIHandle::Result StretchHandle::Release
or present tense is fine here. If unsure, go for whichever is
shorter.*/
XO("Stretch"),
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
UndoPush::CONSOLIDATE);
return RefreshAll;
}

View File

@ -422,7 +422,7 @@ UIHandle::Result SampleHandle::Release
mClickedTrack.reset(); //Set this to NULL so it will catch improper drag events.
ProjectHistory::Get( *pProject ).PushState(XO("Moved Samples"),
XO("Sample Edit"),
UndoPush::CONSOLIDATE | UndoPush::AUTOSAVE);
UndoPush::CONSOLIDATE);
// No change to draw since last drag
return RefreshCode::RefreshNone;

View File

@ -837,7 +837,7 @@ UIHandle::Result TimeShiftHandle::Release
consolidate = true;
}
ProjectHistory::Get( *pProject ).PushState(msg, XO("Time-Shift"),
consolidate ? (UndoPush::CONSOLIDATE) : (UndoPush::AUTOSAVE));
consolidate ? (UndoPush::CONSOLIDATE) : (UndoPush::NONE));
return result | FixScrollbars;
}