mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-08 07:42:39 +02:00
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:
commit
e3355c3c7e
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
180
src/AudioIO.cpp
180
src/AudioIO.cpp
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 ¶mName,
|
||||
|
||||
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.
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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++;
|
||||
|
@ -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)
|
||||
|
@ -237,6 +237,9 @@ private:
|
||||
wxTextCtrl *mCommandText;
|
||||
wxCheckBox *mVersionCheckBox;
|
||||
|
||||
bool mError{ false };
|
||||
wxFileName mFailedFileName;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
|
||||
friend class NyquistEffectsModule;
|
||||
|
@ -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) );
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) );
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user