Stops of possible file i/o exceptions in most places needing it...

... excepting uses of XMLFileWriter, still to be done.
This commit is contained in:
Paul Licameli 2017-03-16 23:51:20 -04:00
commit e3355c3c7e
20 changed files with 547 additions and 255 deletions

View File

@ -60,6 +60,7 @@ It handles initialization and termination by subclassing wxApp.
#include <sys/stat.h>
#endif
#include "AudacityException.h"
#include "AudacityLogger.h"
#include "AboutDialog.h"
#include "AColor.h"
@ -848,6 +849,11 @@ bool AudacityApp::MRUOpen(const wxString &fullPathStr) {
return(true);
}
bool AudacityApp::SafeMRUOpen(const wxString &fullPathStr)
{
return GuardedCall< bool >( [&]{ return MRUOpen( fullPathStr ); } );
}
void AudacityApp::OnMRUClear(wxCommandEvent& WXUNUSED(event))
{
mRecentFiles->Clear();
@ -865,13 +871,16 @@ void AudacityApp::OnMRUFile(wxCommandEvent& event) {
// because we don't want to RemoveFileFromHistory() just because it already exists,
// and AudacityApp::OnMacOpenFile() calls MRUOpen() directly.
// that method does not return the bad result.
// PRL: Don't call SafeMRUOpen
// -- if open fails for some exceptional reason of resource exhaustion that
// the user can correct, leave the file in history.
if (!AudacityProject::IsAlreadyOpen(fullPathStr) && !MRUOpen(fullPathStr))
mRecentFiles->RemoveFileFromHistory(n);
}
void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
{
// Filenames are queued when Audacity receives the a few of the
// Filenames are queued when Audacity receives a few of the
// AppleEvent messages (via wxWidgets). So, open any that are
// in the queue and clean the queue.
if (gInited) {
@ -900,7 +909,9 @@ void AudacityApp::OnTimer(wxTimerEvent& WXUNUSED(event))
// LL: In all but one case an appropriate message is already displayed. The
// instance that a message is NOT displayed is when a failure to write
// to the config file has occurred.
if (!MRUOpen(name)) {
// PRL: Catch any exceptions, don't try this file again, continue to
// other files.
if (!SafeMRUOpen(name)) {
wxFAIL_MSG(wxT("MRUOpen failed"));
}
}
@ -1084,6 +1095,47 @@ void AudacityApp::OnFatalException()
exit(-1);
}
bool AudacityApp::OnExceptionInMainLoop()
{
// This function is invoked from catch blocks in the wxWidgets framework,
// and throw; without argument re-throws the exception being handled,
// letting us dispatch according to its type.
try { throw; }
catch ( AudacityException &e ) {
// Here is the catch-all for our own exceptions
// Use CallAfter to delay this to the next pass of the event loop,
// rather than risk doing it inside stack unwinding.
auto pProject = ::GetActiveProject();
std::shared_ptr< AudacityException > pException { e.Move().release() };
CallAfter( [=] // Capture pException by value!
{
// Restore the state of the project to what it was before the
// failed operation
pProject->RollbackState();
pProject->RedrawProject();
// Give the user an alert
pException->DelayedHandlerAction();
} );
// Don't quit the program
return true;
}
catch ( ... ) {
// There was some other type of exception we don't know.
// Let the inherited function do throw; again and whatever else it does.
return wxApp::OnExceptionInMainLoop();
}
// Shouldn't ever reach this line
return false;
}
#if defined(EXPERIMENTAL_CRASH_REPORT)
void AudacityApp::GenerateCrashReport(wxDebugReport::Context ctx)
{
@ -1573,7 +1625,9 @@ bool AudacityApp::OnInit()
#if !defined(__WXMAC__)
for (size_t i = 0, cnt = parser->GetParamCount(); i < cnt; i++)
{
MRUOpen(parser->GetParam(i));
// PRL: Catch any exceptions, don't try this file again, continue to
// other files.
SafeMRUOpen(parser->GetParam(i));
}
#endif
}

View File

@ -63,6 +63,7 @@ class AudacityApp final : public wxApp {
bool OnInit(void) override;
int OnExit(void) override;
void OnFatalException() override;
bool OnExceptionInMainLoop() override;
int FilterEvent(wxEvent & event);
@ -86,6 +87,8 @@ class AudacityApp final : public wxApp {
void OnMRUFile(wxCommandEvent &event);
// Backend for above - returns true for success, false for failure
bool MRUOpen(const wxString &fileName);
// A wrapper of the above that does not throw
bool SafeMRUOpen(const wxString &fileName);
void OnReceiveCommand(AppCommandEvent &event);

View File

@ -296,6 +296,7 @@ writing audio.
#include <wx/txtstrm.h>
#include "AudacityApp.h"
#include "AudacityException.h"
#include "Mix.h"
#include "MixerBoard.h"
#include "Resample.h"
@ -1649,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;
@ -2249,6 +2252,8 @@ void AudioIO::SetMeters()
void AudioIO::StopStream()
{
auto cleanup = finally ( [this] { ClearRecordingException(); } );
if( mPortStreamV19 == NULL
#ifdef EXPERIMENTAL_MIDI_OUT
&& mMidiStream == NULL
@ -2436,8 +2441,13 @@ void AudioIO::StopStream()
double recordingOffset =
mLastRecordingOffset + latencyCorrection / 1000.0;
for (unsigned int i = 0; i < mCaptureTracks.size(); i++)
{
for (unsigned int i = 0; i < mCaptureTracks.size(); i++) {
// The calls to Flush, and (less likely) Clear and InsertSilence,
// may cause exceptions because of exhaustion of disk space.
// Stop those exceptions here, or else they propagate through too
// many parts of Audacity that are not effects or editing
// operations. GuardedCall ensures that the user sees a warning.
GuardedCall<void>( [&] {
WaveTrack* track = mCaptureTracks[i];
track->Flush();
@ -2475,14 +2485,16 @@ void AudioIO::StopStream()
track->SetOffset(track->GetStartTime() + recordingOffset);
if(track->GetEndTime() < 0.)
{
wxMessageDialog m(NULL, _("Latency Correction setting has caused the recorded audio to be hidden before zero.\nAudacity has brought it back to start at zero.\nYou may have to use the Time Shift Tool (<---> or F5) to drag the track to the right place."),
wxMessageDialog m(NULL, _(
"Latency Correction setting has caused the recorded audio to be hidden before zero.\nAudacity has brought it back to start at zero.\nYou may have to use the Time Shift Tool (<---> or F5) to drag the track to the right place."),
_("Latency problem"), wxOK);
m.ShowModal();
track->SetOffset(0.);
}
}
}
}
} );
}
}
}
@ -3412,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
@ -3587,78 +3618,97 @@ void AudioIO::FillBuffers()
}
} // end of playback buffering
if (mCaptureTracks.size() > 0) // start record buffering
{
auto commonlyAvail = GetCommonlyAvailCapture();
if (!mRecordingException &&
mCaptureTracks.size() > 0)
GuardedCall<void>( [&] {
// start record buffering
auto commonlyAvail = GetCommonlyAvailCapture();
//
// Determine how much this will add to captured tracks
//
double deltat = commonlyAvail / mRate;
//
// Determine how much this will add to captured tracks
//
double deltat = commonlyAvail / mRate;
if (mAudioThreadShouldCallFillBuffersOnce ||
deltat >= mMinCaptureSecsToCopy)
{
// 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; (int)i < numChannels; i++ )
if (mAudioThreadShouldCallFillBuffersOnce ||
deltat >= mMinCaptureSecsToCopy)
{
auto avail = commonlyAvail;
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
// Append captured samples to the end of the WaveTracks.
// The WaveTracks have their own buffering for efficiency.
AutoSaveFile blockFileLog;
auto numChannels = mCaptureTracks.size();
AutoSaveFile appendLog;
if( mFactor == 1.0 )
for( i = 0; (int)i < numChannels; i++ )
{
SampleBuffer temp(avail, trackFormat);
const auto got =
auto avail = commonlyAvail;
sampleFormat trackFormat = mCaptureTracks[i]->GetSampleFormat();
AutoSaveFile appendLog;
if( mFactor == 1.0 )
{
SampleBuffer temp(avail, trackFormat);
const auto got =
mCaptureBuffers[i]->Get(temp.ptr(), trackFormat, avail);
// wxASSERT(got == avail);
// but we can't assert in this thread
wxUnusedVar(got);
mCaptureTracks[i]-> Append(temp.ptr(), trackFormat, avail, 1,
&appendLog);
}
else
{
size_t size = lrint(avail * mFactor);
SampleBuffer temp1(avail, floatSample);
SampleBuffer temp2(size, floatSample);
const auto got =
// 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);
}
else
{
size_t size = lrint(avail * mFactor);
SampleBuffer temp1(avail, floatSample);
SampleBuffer temp2(size, floatSample);
const auto got =
mCaptureBuffers[i]->Get(temp1.ptr(), floatSample, avail);
// wxASSERT(got == avail);
// but we can't assert in this thread
wxUnusedVar(got);
/* we are re-sampling on the fly. The last resampling call
* must flush any samples left in the rate conversion buffer
* so that they get recorded
*/
const auto results =
// wxASSERT(got == avail);
// but we can't assert in this thread
wxUnusedVar(got);
/* we are re-sampling on the fly. The last resampling call
* must flush any samples left in the rate conversion buffer
* so that they get recorded
*/
const auto results =
mResample[i]->Process(mFactor, (float *)temp1.ptr(), avail,
!IsStreamActive(), (float *)temp2.ptr(), size);
size = results.second;
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample, size, 1,
&appendLog);
!IsStreamActive(), (float *)temp2.ptr(), size);
size = results.second;
// see comment in second handler about guarantee
mCaptureTracks[i]-> Append(temp2.ptr(), floatSample, 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"));
}
}
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"));
}
if (mListener && !blockFileLog.IsEmpty())
mListener->OnAudioIONewBlockFiles(blockFileLog);
}
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

View File

@ -33,6 +33,7 @@
#include <wx/msgdlg.h>
#include <wx/settings.h>
#include "AudacityException.h"
#include "ShuttleGui.h"
#include "Prefs.h"
#include "Project.h"
@ -192,7 +193,8 @@ void BatchProcessDialog::OnApplyToProject(wxCommandEvent & WXUNUSED(event))
bool success;
{
wxWindowDisabler wd(pD);
success = mBatchCommands.ApplyChain();
success = GuardedCall< bool >(
[this]{ return mBatchCommands.ApplyChain(); } );
}
if (!success) {
@ -356,15 +358,21 @@ void BatchProcessDialog::OnApplyToFiles(wxCommandEvent & WXUNUSED(event))
mList->SetItemImage(i, 1, 1);
mList->EnsureVisible(i);
project->Import(files[i]);
project->OnSelectAll();
if (!mBatchCommands.ApplyChain()) {
break;
}
auto success = GuardedCall< bool >( [&] {
project->Import(files[i]);
project->OnSelectAll();
if (!mBatchCommands.ApplyChain())
return false;
if (!pD->IsShown() || mAbort) {
if (!pD->IsShown() || mAbort)
return false;
return true;
} );
if (!success)
break;
}
UndoManager *um = project->GetUndoManager();
um->ClearStates();
project->OnSelectAll();

View File

@ -149,6 +149,7 @@ class PROFILE_DLL_API BlockFile /* not final, abstract */ {
//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

View File

@ -88,6 +88,7 @@
#endif
#include "AudacityApp.h"
#include "AudacityException.h"
#include "BlockFile.h"
#include "blockfile/LegacyBlockFile.h"
#include "blockfile/LegacyAliasBlockFile.h"
@ -1609,10 +1610,8 @@ _("Project check of \"%s\" folder \
wxASSERT(b);
if (b) {
auto ab = static_cast< AliasBlockFile * > ( &*b );
if (action == 1)
// Silence error logging for this block in this session.
ab->SilenceAliasLog();
else if (action == 2)
if (action == 2)
{
// silence the blockfiles by yanking the filename
// This is done, eventually, in PCMAliasBlockFile::ReadData()
@ -1621,9 +1620,22 @@ _("Project check of \"%s\" folder \
wxFileNameWrapper dummy;
dummy.Clear();
ab->ChangeAliasedFileName(std::move(dummy));
ab->Recover();
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall<void>(
[&] { ab->Recover(); },
[&] (AudacityException*) { action = 1; }
);
nResult = FSCKstatus_CHANGED | FSCKstatus_SAVE_AUP;
}
if (action == 1)
// Silence error logging for this block in this session.
ab->SilenceAliasLog();
}
++iter;
}
@ -1673,11 +1685,22 @@ _("Project check of \"%s\" folder \
BlockFilePtr b = iter->second.lock();
wxASSERT(b);
if (b) {
if(action==0){
if(action==0) {
//regenerate from data
b->Recover();
nResult |= FSCKstatus_CHANGED;
}else if (action==1){
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall<void>(
[&] {
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
if (action==1){
// Silence error logging for this block in this session.
b->SilenceLog();
}
@ -1737,11 +1760,22 @@ _("Project check of \"%s\" folder \
if (b) {
if (action == 2)
{
//regenerate with zeroes
b->Recover();
nResult = FSCKstatus_CHANGED;
//regenerate from data
// If recovery fails for one file, silence it,
// and don't try to recover other files but
// silence them too. GuardedCall will cause an appropriate
// error message for the user.
GuardedCall<void>(
[&] {
//regenerate with zeroes
b->Recover();
nResult |= FSCKstatus_CHANGED;
},
[&] (AudacityException*) { action = 1; }
);
}
else if (action == 1)
if (action == 1)
b->SilenceLog();
}
++iter;

View File

@ -91,6 +91,7 @@ scroll information. It also has some status flags.
#endif
#endif
#include "AudacityException.h"
#include "FreqWindow.h"
#include "effects/Contrast.h"
#include "AutoRecovery.h"
@ -446,19 +447,25 @@ public:
bool OnDropFiles(wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxArrayString& filenames) override
{
//sort by OD non OD. load Non OD first so user can start editing asap.
wxArrayString sortednames(filenames);
// Experiment shows that this function can be reached while there is no
// catch block above in wxWidgets. So stop all exceptions here.
return GuardedCall< bool > ( [&] {
//sort by OD non OD. load Non OD first so user can start editing asap.
wxArrayString sortednames(filenames);
ODManager::Pauser pauser;
ODManager::Pauser pauser;
sortednames.Sort(CompareNoCaseFileName);
for (unsigned int i = 0; i < sortednames.GetCount(); i++) {
sortednames.Sort(CompareNoCaseFileName);
mProject->Import(sortednames[i]);
}
mProject->HandleResize(); // Adjust scrollers for NEW track sizes.
for (unsigned int i = 0; i < sortednames.GetCount(); i++) {
return true;
mProject->Import(sortednames[i]);
}
mProject->HandleResize(); // Adjust scrollers for NEW track sizes.
return true;
} );
}
private:
@ -487,7 +494,14 @@ bool ImportXMLTagHandler::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
}
WaveTrackArray trackArray;
mProject->Import(strAttr, &trackArray);
// Guard this call so that C++ exceptions don't propagate through
// the expat library
GuardedCall< void >(
[&] { mProject->Import(strAttr, &trackArray); },
[&] (AudacityException*) { trackArray.clear(); }
);
if (trackArray.empty())
return false;
@ -2495,7 +2509,9 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event)
wxYES_NO | wxCANCEL | wxICON_QUESTION,
this);
if (result == wxCANCEL || (result == wxYES && !Save())) {
if (result == wxCANCEL || (result == wxYES &&
!GuardedCall<bool>( [&]{ return Save(); } )
)) {
event.Veto();
return;
}

View File

@ -6064,6 +6064,7 @@ void TrackPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
/// on our current state, we forward the mouse events to
/// various interested parties.
void TrackPanel::OnMouseEvent(wxMouseEvent & event)
try
{
#if defined(__WXMAC__) && defined(EVT_MAGNIFY)
// PRL:
@ -6211,6 +6212,22 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event)
EnsureVisible(t);
}
}
catch( ... )
{
// Abort any dragging, as if by hitting Esc
if ( HandleEscapeKey( true ) )
;
else {
// Ensure these steps, if escape handling did nothing
SetCapturedTrack(NULL, IsUncaptured);
if (HasCapture())
ReleaseMouse();
wxMouseEvent dummy;
HandleCursor(dummy);
Refresh(false);
}
throw;
}
namespace {
int FindMergeLine(WaveTrack *track, double time)

View File

@ -20,6 +20,7 @@ ApplyAndSendResponse, and CommandImplementation classes
#include <wx/variant.h>
#include <wx/arrstr.h>
#include "../AudacityException.h"
#include "Validators.h"
#include "CommandType.h"
#include "CommandMisc.h"
@ -71,7 +72,9 @@ bool DecoratedCommand::SetParameter(const wxString &paramName,
bool ApplyAndSendResponse::Apply(CommandExecutionContext context)
{
bool result = mCommand->Apply(context);
auto result = GuardedCall<bool>(
[&] { return mCommand->Apply(context); }
);
wxString response = GetName();
// These three strings are deliberately not localised.
// They are used in script responses and always happen in English.

View File

@ -86,6 +86,7 @@ CommandManager. It holds the callback for one command.
#include <wx/msgdlg.h>
#include <wx/tokenzr.h>
#include "../AudacityException.h"
#include "../Prefs.h"
#include "../Project.h"
@ -190,47 +191,51 @@ public:
int FilterEvent(wxEvent& event) override
{
// Quickly bail if this isn't something we want.
wxEventType type = event.GetEventType();
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP)
{
// Unguarded exception propagation may crash the program, at least
// on Mac while in the objective-C closure above
return GuardedCall< int > ( [&] {
// Quickly bail if this isn't something we want.
wxEventType type = event.GetEventType();
if (type != wxEVT_CHAR_HOOK && type != wxEVT_KEY_UP)
{
return Event_Skip;
}
// We must have a project since we will be working with the Command Manager
// and capture handler, both of which are (currently) tied to individual projects.
//
// Shouldn't they be tied to the application instead???
AudacityProject *project = GetActiveProject();
if (!project || !project->IsEnabled())
{
return Event_Skip;
}
// Make a copy of the event and (possibly) make it look like a key down
// event.
wxKeyEvent key = (wxKeyEvent &) event;
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = project->GetKeyboardCaptureHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
// Capture handler didn't want it, so ask the Command Manager.
CommandManager *manager = project->GetCommandManager();
if (manager && manager->FilterKeyEvent(project, key))
{
return Event_Processed;
}
// Give it back to WX for normal processing.
return Event_Skip;
}
// We must have a project since we will be working with the Command Manager
// and capture handler, both of which are (currently) tied to individual projects.
//
// Shouldn't they be tied to the application instead???
AudacityProject *project = GetActiveProject();
if (!project || !project->IsEnabled())
{
return Event_Skip;
}
// Make a copy of the event and (possibly) make it look like a key down
// event.
wxKeyEvent key = (wxKeyEvent &) event;
if (type == wxEVT_CHAR_HOOK)
{
key.SetEventType(wxEVT_KEY_DOWN);
}
// Give the capture handler first dibs at the event.
wxWindow *handler = project->GetKeyboardCaptureHandler();
if (handler && HandleCapture(handler, key))
{
return Event_Processed;
}
// Capture handler didn't want it, so ask the Command Manager.
CommandManager *manager = project->GetCommandManager();
if (manager && manager->FilterKeyEvent(project, key))
{
return Event_Processed;
}
// Give it back to WX for normal processing.
return Event_Skip;
}, MakeSimpleGuard( Event_Skip ) );
}
private:

View File

@ -39,6 +39,7 @@ greater use in future.
#include "audacity/ConfigInterface.h"
#include "../AudacityException.h"
#include "../AudioIO.h"
#include "../LabelTrack.h"
#include "../Mix.h"
@ -1612,8 +1613,18 @@ bool Effect::ProcessTrack(int count,
{
processed = ProcessBlock(mInBufPos.get(), mOutBufPos.get(), curBlockSize);
}
catch( const AudacityException &e )
{
// PRL: Bug 437:
// Pass this along to our application-level handler
throw;
}
catch(...)
{
// PRL:
// Exceptions for other reasons, maybe in third-party code...
// Continue treating them as we used to, but I wonder if these
// should now be treated the same way.
return false;
}
wxASSERT(processed == curBlockSize);

View File

@ -22,6 +22,7 @@ effect that uses SBSMS to do its processing (TimeScale)
#include "../WaveTrack.h"
#include "../Project.h"
#include "TimeWarper.h"
#include "../FileException.h"
enum {
SBSMSOutBlockSize = 512
@ -62,6 +63,9 @@ public:
std::unique_ptr<SBSMSQuality> quality;
std::unique_ptr<WaveTrack> outputLeftTrack;
std::unique_ptr<WaveTrack> outputRightTrack;
wxFileName failedFileName;
bool error{ false };
};
class SBSMSEffectInterface final : public SBSMSInterfaceSliding {
@ -95,8 +99,28 @@ long resampleCB(void *cb_data, SBSMSFrame *data)
);
// Get the samples from the tracks and put them in the buffers.
r->leftTrack->Get((samplePtr)(r->leftBuffer.get()), floatSample, r->offset, blockSize);
r->rightTrack->Get((samplePtr)(r->rightBuffer.get()), floatSample, r->offset, blockSize);
// I don't know if we can safely propagate errors through sbsms, and it
// does not seem to let us report error codes, so use this roundabout to
// stop the effect early.
// This would be easier with std::exception_ptr but we don't have that yet.
try {
r->leftTrack->Get(
(samplePtr)(r->leftBuffer.get()), floatSample, r->offset, blockSize);
r->rightTrack->Get(
(samplePtr)(r->rightBuffer.get()), floatSample, r->offset, blockSize);
}
catch ( const FileException& e ) {
if ( e.cause == FileException::Cause::Read )
r->failedFileName = e.fileName;
data->size = 0;
r->error = true;
return 0;
}
catch ( ... ) {
data->size = 0;
r->error = true;
return 0;
}
// convert to sbsms audio format
for(decltype(blockSize) i=0; i<blockSize; i++) {
@ -216,7 +240,7 @@ bool EffectSBSMS::Process()
mTotalStretch = rateSlide.getTotalStretch();
t = iter.First();
while (t != NULL) {
while (bGoodResult && t != NULL) {
if (t->GetKind() == Track::Label &&
(t->GetSelected() || (mustSync && t->IsSyncLockSelected())) )
{
@ -403,22 +427,34 @@ bool EffectSBSMS::Process()
if (TrackProgress(nWhichTrack, frac))
return false;
}
rb.outputLeftTrack->Flush();
if(rightTrack)
rb.outputRightTrack->Flush();
if (rb.failedFileName.IsOk())
// re-construct an exception
// I wish I had std::exception_ptr instead
// and could re-throw any AudacityException
throw FileException{
FileException::Cause::Read, rb.failedFileName };
else if (rb.error)
// well, what?
bGoodResult = false;
bool bResult =
if (bGoodResult) {
rb.outputLeftTrack->Flush();
if(rightTrack)
rb.outputRightTrack->Flush();
bool bResult =
leftTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputLeftTrack.get(),
true, false, warper.get());
wxASSERT(bResult); // TO DO: Actually handle this.
wxUnusedVar(bResult);
if(rightTrack)
{
bResult =
rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack.get(),
true, false, warper.get());
true, false, warper.get());
wxASSERT(bResult); // TO DO: Actually handle this.
wxUnusedVar(bResult);
if(rightTrack)
{
bResult =
rightTrack->ClearAndPaste(mCurT0, mCurT1, rb.outputRightTrack.get(),
true, false, warper.get());
wxASSERT(bResult); // TO DO: Actually handle this.
}
}
}
mCurTrackNum++;

View File

@ -45,6 +45,7 @@ effects from this one class.
#include <wx/numformatter.h>
#include "../../AudacityApp.h"
#include "../../FileException.h"
#include "../../FileNames.h"
#include "../../Internat.h"
#include "../../LabelTrack.h"
@ -833,6 +834,9 @@ bool NyquistEffect::TransferDataFromWindow()
bool NyquistEffect::ProcessOne()
{
mError = false;
mFailedFileName.Clear();
nyx_rval rval;
wxString cmd;
@ -1235,10 +1239,22 @@ bool NyquistEffect::ProcessOne()
int success = nyx_get_audio(StaticPutCallback, (void *)this);
// See if GetCallback found read errors
if (mFailedFileName.IsOk())
// re-construct an exception
// I wish I had std::exception_ptr instead
// and could re-throw any AudacityException
throw FileException{
FileException::Cause::Read, mFailedFileName };
else if (mError)
// what, then?
success = false;
if (!success) {
for(i = 0; i < outChannels; i++) {
mOutputTrack[i].reset();
}
return false;
}
@ -1789,11 +1805,19 @@ int NyquistEffect::GetCallback(float *buffer, int ch,
mCurStart[ch] + mCurLen - mCurBufferStart[ch] );
mCurBuffer[ch].Allocate(mCurBufferLen[ch], floatSample);
if (!mCurTrack[ch]->Get(mCurBuffer[ch].ptr(), floatSample,
mCurBufferStart[ch], mCurBufferLen[ch])) {
wxPrintf(wxT("GET error\n"));
try {
mCurTrack[ch]->Get(
mCurBuffer[ch].ptr(), floatSample,
mCurBufferStart[ch], mCurBufferLen[ch]);
}
catch ( const FileException& e ) {
if ( e.cause == FileException::Cause::Read )
mFailedFileName = e.fileName;
mError = true;
return -1;
}
catch ( ... ) {
mError = true;
return -1;
}
}
@ -1832,23 +1856,26 @@ int NyquistEffect::StaticPutCallback(float *buffer, int channel,
int NyquistEffect::PutCallback(float *buffer, int channel,
long start, long len, long totlen)
{
if (channel == 0) {
double progress = mScale*((float)(start+len)/totlen);
// Don't let C++ exceptions propagate through the Nyquist library
return GuardedCall<int>( [&] {
if (channel == 0) {
double progress = mScale*((float)(start+len)/totlen);
if (progress > mProgressOut) {
mProgressOut = progress;
if (progress > mProgressOut) {
mProgressOut = progress;
}
if (TotalProgress(mProgressIn+mProgressOut+mProgressTot)) {
return -1;
}
}
if (TotalProgress(mProgressIn+mProgressOut+mProgressTot)) {
return -1;
if (mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len)) {
return 0; // success
}
}
if (mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len)) {
return 0; // success
}
return -1; // failure
return -1; // failure
}, MakeSimpleGuard( -1 ) ); // translate all exceptions into failure
}
void NyquistEffect::StaticOutputCallback(int c, void *This)

View File

@ -237,6 +237,9 @@ private:
wxTextCtrl *mCommandText;
wxCheckBox *mVersionCheckBox;
bool mError{ false };
wxFileName mFailedFileName;
DECLARE_EVENT_TABLE()
friend class NyquistEffectsModule;

View File

@ -37,6 +37,7 @@
#include <wx/intl.h> // needed for _("translated stings") even if we
// don't have libflac available
#include "../AudacityException.h"
#include "Import.h"
#include "ImportPlugin.h"
@ -252,35 +253,38 @@ void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus WXUNUSED(status))
FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
const FLAC__int32 * const buffer[])
{
ArrayOf<short> tmp{ frame->header.blocksize };
// Don't let C++ exceptions propagate through libflac
return GuardedCall< FLAC__StreamDecoderWriteStatus > ( [&] {
auto tmp = ArrayOf< short >{ frame->header.blocksize };
auto iter = mFile->mChannels.begin();
for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
if (frame->header.bits_per_sample == 16) {
for (unsigned int s=0; s<frame->header.blocksize; s++) {
tmp[s]=buffer[chn][s];
auto iter = mFile->mChannels.begin();
for (unsigned int chn=0; chn<mFile->mNumChannels; ++iter, ++chn) {
if (frame->header.bits_per_sample == 16) {
for (unsigned int s=0; s<frame->header.blocksize; s++) {
tmp[s]=buffer[chn][s];
}
iter->get()->Append((samplePtr)tmp.get(),
int16Sample,
frame->header.blocksize);
}
else {
iter->get()->Append((samplePtr)buffer[chn],
int24Sample,
frame->header.blocksize);
}
iter->get()->Append((samplePtr)tmp.get(),
int16Sample,
frame->header.blocksize);
}
else {
iter->get()->Append((samplePtr)buffer[chn],
int24Sample,
frame->header.blocksize);
mFile->mSamplesDone += frame->header.blocksize;
mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
if (mFile->mUpdateResult != ProgressResult::Success)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
}
mFile->mSamplesDone += frame->header.blocksize;
mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
if (mFile->mUpdateResult != ProgressResult::Success)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}, MakeSimpleGuard(FLAC__STREAM_DECODER_WRITE_STATUS_ABORT) );
}

View File

@ -46,6 +46,7 @@ Licensed under the GNU General Public License v2 or later
#endif
// all the includes live here by default
#include "../AudacityException.h"
#include "../SampleFormat.h"
#include "../Tags.h"
#include "../Internat.h"
@ -489,20 +490,23 @@ inline void GstSampleUnref(GstSample *p) { gst_sample_unref(p); } // I can't use
static GstFlowReturn
GStreamerNewSample(GstAppSink *appsink, gpointer data)
{
GStreamerImportFileHandle *handle = (GStreamerImportFileHandle *)data;
static GMutex mutex;
// Don't let C++ exceptions propagate through GStreamer
return GuardedCall< GstFlowReturn > ( [&] {
GStreamerImportFileHandle *handle = (GStreamerImportFileHandle *)data;
static GMutex mutex;
// Get the sample
std::unique_ptr < GstSample, Deleter< GstSample, GstSampleUnref> >
sample{ gst_app_sink_pull_sample(appsink) };
// Get the sample
std::unique_ptr < GstSample, Deleter< GstSample, GstSampleUnref> >
sample{ gst_app_sink_pull_sample(appsink) };
// We must single thread here to prevent concurrent use of the
// Audacity track functions.
g_mutex_locker locker{ mutex };
// We must single thread here to prevent concurrent use of the
// Audacity track functions.
g_mutex_locker locker{ mutex };
handle->OnNewSample(GETCTX(appsink), sample.get());
handle->OnNewSample(GETCTX(appsink), sample.get());
return GST_FLOW_OK;
return GST_FLOW_OK;
}, MakeSimpleGuard(GST_FLOW_ERROR) );
}
// ----------------------------------------------------------------------------

View File

@ -39,6 +39,7 @@
#include <wx/defs.h>
#include <wx/intl.h>
#include "../AudacityException.h"
#include "../Prefs.h"
#include "Import.h"
#include "ImportPlugin.h"
@ -480,59 +481,61 @@ enum mad_flow output_cb(void *_data,
struct mad_header const * WXUNUSED(header),
struct mad_pcm *pcm)
{
int samplerate;
struct private_data *data = (struct private_data *)_data;
// Don't C++ exceptions propagate through mad
return GuardedCall< mad_flow > ( [&] {
int samplerate;
struct private_data *data = (struct private_data *)_data;
samplerate= pcm->samplerate;
auto channels = pcm->channels;
const auto samples = pcm->length;
samplerate= pcm->samplerate;
auto channels = pcm->channels;
const auto samples = pcm->length;
/* If this is the first run, we need to create the WaveTracks that
* will hold the data. We do this now because now is the first
* moment when we know how many channels there are. */
/* If this is the first run, we need to create the WaveTracks that
* will hold the data. We do this now because now is the first
* moment when we know how many channels there are. */
if(data->channels.empty()) {
data->channels.resize(channels);
if(data->channels.empty()) {
data->channels.resize(channels);
sampleFormat format = (sampleFormat) gPrefs->
Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
sampleFormat format = (sampleFormat) gPrefs->
Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
for(auto &channel: data->channels) {
channel = data->trackFactory->NewWaveTrack(format, samplerate);
channel->SetChannel(Track::MonoChannel);
for(auto &channel: data->channels) {
channel = data->trackFactory->NewWaveTrack(format, samplerate);
channel->SetChannel(Track::MonoChannel);
}
/* special case: 2 channels is understood to be stereo */
if(channels == 2) {
data->channels.begin()->get()->SetChannel(Track::LeftChannel);
data->channels.rbegin()->get()->SetChannel(Track::RightChannel);
data->channels.begin()->get()->SetLinked(true);
}
data->numChannels = channels;
}
else {
// This is not the first run, protect us from libmad glitching
// on the number of channels
channels = data->numChannels;
}
/* special case: 2 channels is understood to be stereo */
if(channels == 2) {
data->channels.begin()->get()->SetChannel(Track::LeftChannel);
data->channels.rbegin()->get()->SetChannel(Track::RightChannel);
data->channels.begin()->get()->SetLinked(true);
}
data->numChannels = channels;
}
else {
// This is not the first run, protect us from libmad glitching
// on the number of channels
channels = data->numChannels;
}
/* TODO: get rid of this by adding fixed-point support to SampleFormat.
* For now, we allocate temporary float buffers to convert the fixed
* point samples into something we can feed to the WaveTrack. Allocating
* big blocks of data like this isn't a great idea, but it's temporary.
*/
FloatBuffers channelBuffers{ channels, samples };
for(size_t smpl = 0; smpl < samples; smpl++)
for(int chn = 0; chn < channels; chn++)
channelBuffers[chn][smpl] = scale(pcm->samples[chn][smpl]);
/* TODO: get rid of this by adding fixed-point support to SampleFormat.
* For now, we allocate temporary float buffers to convert the fixed
* point samples into something we can feed to the WaveTrack. Allocating
* big blocks of data like this isn't a great idea, but it's temporary.
*/
FloatBuffers channelBuffers{ channels, samples };
for (size_t smpl = 0; smpl < samples; smpl++)
for(int chn = 0; chn < channels; chn++)
channelBuffers[chn][smpl] = scale(pcm->samples[chn][smpl]);
data->channels[chn]->Append((samplePtr)channelBuffers[chn].get(),
floatSample,
samples);
for (int chn = 0; chn < channels; chn++)
data->channels[chn]->Append((samplePtr)channelBuffers[chn].get(),
floatSample,
samples);
return MAD_FLOW_CONTINUE;
return MAD_FLOW_CONTINUE;
}, MakeSimpleGuard(MAD_FLOW_BREAK) );
}
enum mad_flow error_cb(void * WXUNUSED(_data), struct mad_stream * WXUNUSED(stream),

View File

@ -19,6 +19,7 @@ updating the ODPCMAliasBlockFile and the GUI of the newly available data.
#include "ODComputeSummaryTask.h"
#include "../AudacityException.h"
#include "../blockfile/ODPCMAliasBlockFile.h"
#include "../Sequence.h"
#include "../WaveTrack.h"
@ -77,9 +78,10 @@ void ODComputeSummaryTask::DoSomeInternal()
{
// WriteSummary might throw, but this is a worker thread, so stop
// the exceptions here!
success = true;
try { bf->DoWriteSummary(); }
catch(...) { success = false; }
success = GuardedCall<bool>( [&] {
bf->DoWriteSummary();
return true;
} );
blockStartSample = bf->GetStart();
blockEndSample = blockStartSample + bf->GetLength();
}

View File

@ -563,7 +563,9 @@ void AButton::Click()
{
wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
event.SetEventObject(this);
GetEventHandler()->ProcessEvent(event);
// Be sure to use SafelyProcessEvent so that exceptions do not propagate
// out of DoDefaultAction
GetEventHandler()->SafelyProcessEvent(event);
}
void AButton::SetShift(bool shift)