1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-26 09:28:07 +02:00

Gracefully handle disk exhaustion exceptions during recording

This commit is contained in:
Paul Licameli 2016-11-22 14:10:53 -05:00
parent f4e2fb5eac
commit eeb301e50d
2 changed files with 112 additions and 61 deletions

View File

@ -1650,6 +1650,8 @@ int AudioIO::StartStream(const ConstWaveTrackArray &playbackTracks,
double t0, double t1,
const AudioIOStartStreamOptions &options)
{
auto cleanup = finally ( [this] { ClearRecordingException(); } );
if( IsBusy() )
return 0;
@ -2250,6 +2252,8 @@ void AudioIO::SetMeters()
void AudioIO::StopStream()
{
auto cleanup = finally ( [this] { ClearRecordingException(); } );
if( mPortStreamV19 == NULL
#ifdef EXPERIMENTAL_MIDI_OUT
&& mMidiStream == NULL
@ -3420,6 +3424,25 @@ void AudioIO::FillBuffers()
{
unsigned int i;
auto delayedHandler = [this] ( AudacityException * pException ) {
// In the main thread, stop recording
// This is one place where the application handles disk
// exhaustion exceptions from wave track operations, without rolling
// back to the last pushed undo state. Instead, partial recording
// results are pushed as a NEW undo state. For this reason, as
// commented elsewhere, we want an exception safety guarantee for
// the output wave tracks, after the failed append operation, that
// the tracks remain as they were after the previous successful
// (block-level) appends.
// Note that the Flush in StopStream() may throw another exception,
// but StopStream() contains that exception, and the logic in
// AudacityException::DelayedHandlerAction prevents redundant message
// boxes.
StopStream();
DefaultDelayedHandlerAction{}( pException );
};
if (mPlaybackTracks.size() > 0)
{
// Though extremely unlikely, it is possible that some buffers
@ -3595,8 +3618,10 @@ void AudioIO::FillBuffers()
}
} // end of playback buffering
if (mCaptureTracks.size() > 0) // start record buffering
{
if (!mRecordingException &&
mCaptureTracks.size() > 0)
GuardedCall<void>( [&] {
// start record buffering
auto commonlyAvail = GetCommonlyAvailCapture();
//
@ -3627,6 +3652,7 @@ void AudioIO::FillBuffers()
// wxASSERT(got == avail);
// but we can't assert in this thread
wxUnusedVar(got);
// see comment in second handler about guarantee
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat, avail, 1,
&appendLog);
}
@ -3648,6 +3674,7 @@ void AudioIO::FillBuffers()
mResample[i]->Process(mFactor, (float *)temp1.ptr(), avail,
!IsStreamActive(), (float *)temp2.ptr(), size);
size = results.second;
// see comment in second handler about guarantee
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample, size, 1,
&appendLog);
}
@ -3666,7 +3693,22 @@ void AudioIO::FillBuffers()
if (mListener && !blockFileLog.IsEmpty())
mListener->OnAudioIONewBlockFiles(blockFileLog);
}
} // end of record buffering
// end of record buffering
},
// handler
[this] ( AudacityException *pException ) {
if ( pException ) {
// So that we don't attempt to fill the recording buffer again
// before the main thread stops recording
SetRecordingException();
return ;
}
else
// Don't want to intercept other exceptions (?)
throw;
},
delayedHandler
);
}
void AudioIO::SetListener(AudioIOListener* listener)

View File

@ -19,6 +19,7 @@
#include "MemoryX.h"
#include <vector>
#include <wx/atomic.h>
#ifdef USE_MIDI
@ -692,6 +693,14 @@ private:
bool mSilentScrub;
sampleCount mScrubDuration;
#endif
// A flag tested and set in one thread, cleared in another. Perhaps
// this guarantee of atomicity is more cautious than necessary.
wxAtomicInt mRecordingException {};
void SetRecordingException()
{ wxAtomicInc( mRecordingException ); }
void ClearRecordingException()
{ if (mRecordingException) wxAtomicDec( mRecordingException ); }
};
#endif