1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-01 16:19:43 +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,38 +871,6 @@ void ProjectAudioManager::OnAudioIOStopRecording()
// Only push state if we were capturing and not monitoring
if (projectAudioIO.GetAudioIOToken() > 0)
{
auto &tracks = TrackList::Get( project );
auto gAudioIO = AudioIO::Get();
auto &intervals = gAudioIO->LostCaptureIntervals();
if (intervals.size()) {
// Make a track with labels for recording errors
auto uTrack = TrackFactory::Get( project ).NewLabelTrack();
auto pTrack = uTrack.get();
tracks.Add( uTrack );
/* i18n-hint: A name given to a track, appearing as its menu button.
The translation should be short or else it will not display well.
At most, about 11 Latin characters.
Dropout is a loss of a short sequence of audio sample data from the
recording */
pTrack->SetName(_("Dropouts"));
long counter = 1;
for (auto &interval : intervals)
pTrack->AddLabel(
SelectedRegion{ interval.first,
interval.first + interval.second },
wxString::Format(wxT("%ld"), counter++));
ShowWarningDialog(&window, wxT("DropoutDetected"), XO("\
Recorded audio was lost at the labeled locations. Possible causes:\n\
\n\
Other applications are competing with Audacity for processor time\n\
\n\
You are saving directly to a slow external storage device\n\
"
),
false,
XXO("Turn off dropout detection"));
}
auto &history = ProjectHistory::Get( project );
if (IsTimerRecordCancelled()) {
@ -910,13 +879,52 @@ You are saving directly to a slow external storage device\n\
// Reset timer record
ResetTimerRecordCancelled();
}
else
else {
// Add to history
history.PushState(XO("Recorded Audio"), XO("Record"));
}
// 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 auto-save again to get the project to a "normal" state again.
projectFileIO.AutoSave();
// 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();
if (intervals.size()) {
// Make a track with labels for recording errors
auto uTrack = TrackFactory::Get( project ).NewLabelTrack();
auto pTrack = uTrack.get();
tracks.Add( uTrack );
/* i18n-hint: A name given to a track, appearing as its menu button.
The translation should be short or else it will not display well.
At most, about 11 Latin characters.
Dropout is a loss of a short sequence of audio sample data from the
recording */
pTrack->SetName(_("Dropouts"));
long counter = 1;
for (auto &interval : intervals)
pTrack->AddLabel(
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\
Other applications are competing with Audacity for processor time\n\
\n\
You are saving directly to a slow external storage device\n\
"
),
false,
XXO("Turn off dropout detection"));
}
}
}
}
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;
}