diff --git a/src/Project.cpp b/src/Project.cpp index 134a77eb7..65e4de40a 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -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; diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 8b82cfde1..198f2091c 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -86,6 +86,7 @@ CommandManager. It holds the callback for one command. #include #include +#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: diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index c9943fc0c..bee5dde8c 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -45,6 +45,7 @@ effects from this one class. #include #include "../../AudacityApp.h" +#include "../../AudacityException.h" #include "../../FileNames.h" #include "../../Internat.h" #include "../../LabelTrack.h" @@ -1832,23 +1833,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( [&] { + 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) diff --git a/src/import/ImportFLAC.cpp b/src/import/ImportFLAC.cpp index 172140614..3fcffba18 100644 --- a/src/import/ImportFLAC.cpp +++ b/src/import/ImportFLAC.cpp @@ -37,6 +37,7 @@ #include // 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 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; chnmNumChannels; ++iter, ++chn) { - if (frame->header.bits_per_sample == 16) { - for (unsigned int s=0; sheader.blocksize; s++) { - tmp[s]=buffer[chn][s]; + auto iter = mFile->mChannels.begin(); + for (unsigned int chn=0; chnmNumChannels; ++iter, ++chn) { + if (frame->header.bits_per_sample == 16) { + for (unsigned int s=0; sheader.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) ); } diff --git a/src/import/ImportGStreamer.cpp b/src/import/ImportGStreamer.cpp index 48a7a088a..5ad602c87 100644 --- a/src/import/ImportGStreamer.cpp +++ b/src/import/ImportGStreamer.cpp @@ -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) ); } // ---------------------------------------------------------------------------- diff --git a/src/import/ImportMP3.cpp b/src/import/ImportMP3.cpp index 0fd3e5eb1..14663e944 100644 --- a/src/import/ImportMP3.cpp +++ b/src/import/ImportMP3.cpp @@ -39,6 +39,7 @@ #include #include +#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),