Exception safety in: some effects and generators...

... Those that directly call WaveTrack functions in their Process() routines,
which might throw exceptions for disk space exhaustion.
This commit is contained in:
Paul Licameli 2016-12-15 07:30:14 -05:00
parent 1fad6292a2
commit 22a12c6852
15 changed files with 106 additions and 97 deletions

View File

@ -179,13 +179,10 @@ bool EffectAmplify::Init()
void EffectAmplify::Preview(bool dryOnly)
{
double ratio = mRatio;
double peak = mPeak;
auto cleanup1 = valueRestorer( mRatio );
auto cleanup2 = valueRestorer( mPeak );
Effect::Preview(dryOnly);
mRatio = ratio;
mPeak = peak;
}
void EffectAmplify::PopulateOrExchange(ShuttleGui & S)

View File

@ -1092,9 +1092,7 @@ bool EffectEqualization::ProcessOne(int count, WaveTrack * t,
for(size_t j = mM - 1; j < wcopy; j++)
buffer[i+j] = thisWindow[j];
float *tempP = thisWindow;
thisWindow = lastWindow;
lastWindow = tempP;
std::swap( thisWindow, lastWindow );
} //next i, lump of this block
output->Append((samplePtr)buffer.get(), floatSample, block);

View File

@ -16,6 +16,7 @@
#include "../Audacity.h"
#include "../Experimental.h"
#ifdef EXPERIMENTAL_EQ_SSE_THREADED
#include "../MemoryX.h"
#include "../Project.h"
#include "Equalization.h"
#include "../WaveTrack.h"
@ -296,6 +297,7 @@ bool EffectEqualization48x::Process(EffectEqualization* effectEqualization)
if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
AllocateBuffersWorkers(sMathPath&MATH_FUNCTION_THREADED);
auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
SelectedTrackListOfKindIterator iter(Track::Wave, mEffectEqualization->mOutputTracks.get());
WaveTrack *track = (WaveTrack *) iter.First();
int count = 0;
@ -316,7 +318,6 @@ bool EffectEqualization48x::Process(EffectEqualization* effectEqualization)
track = (WaveTrack *) iter.Next();
count++;
}
FreeBuffersWorkers();
mEffectEqualization->ReplaceProcessedTracks(!bBreakLoop);
return !bBreakLoop;
@ -331,6 +332,7 @@ bool EffectEqualization48x::TrackCompare()
if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
AllocateBuffersWorkers(sMathPath&MATH_FUNCTION_THREADED);
auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
// Reset map
// PRL: These two maps aren't really used
std::vector<Track*> SecondIMap;
@ -402,9 +404,8 @@ bool EffectEqualization48x::TrackCompare()
track = (WaveTrack *) iter.Next();
track2 = (WaveTrack *) iter2.Next();
}
FreeBuffersWorkers();
mEffectEqualization->ReplaceProcessedTracks(!bBreakLoop);
return bBreakLoop;
mEffectEqualization->ReplaceProcessedTracks(!bBreakLoop);
return bBreakLoop; // return !bBreakLoop ?
}
bool EffectEqualization48x::DeltaTrack(WaveTrack * t, WaveTrack * t2, sampleCount start, sampleCount len)
@ -446,6 +447,7 @@ bool EffectEqualization48x::Benchmark(EffectEqualization* effectEqualization)
if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
AllocateBuffersWorkers(MATH_FUNCTION_THREADED);
auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
SelectedTrackListOfKindIterator
iter(Track::Wave, mEffectEqualization->mOutputTracks.get());
long times[] = { 0,0,0,0,0 };
@ -494,7 +496,6 @@ bool EffectEqualization48x::Benchmark(EffectEqualization* effectEqualization)
times[i]=timer.Time();
}
}
FreeBuffersWorkers();
mBenching=false;
bBreakLoop=false;
mEffectEqualization->ReplaceProcessedTracks(bBreakLoop);
@ -507,7 +508,7 @@ bool EffectEqualization48x::Benchmark(EffectEqualization* effectEqualization)
wxMessageBox(wxString::Format(_("Benchmark times:\nOriginal: %s\nDefault Segmented: %s\nDefault Threaded: %s\nSSE: %s\nSSE Threaded: %s\n"),tsDefault.Format(wxT("%M:%S.%l")).c_str(),
tsDefaultEnhanced.Format(wxT("%M:%S.%l")).c_str(), tsDefaultThreaded.Format(wxT("%M:%S.%l")).c_str(),tsSSE.Format(wxT("%M:%S.%l")).c_str(),tsSSEThreaded.Format(wxT("%M:%S.%l")).c_str()));
return bBreakLoop;
return bBreakLoop; // return !bBreakLoop ?
}
bool EffectEqualization48x::ProcessTail(WaveTrack * t, WaveTrack * output, sampleCount start, sampleCount len)
@ -862,30 +863,32 @@ bool EffectEqualization48x::ProcessOne4x(int count, WaveTrack * t,
ProcessTail(t, output.get(), start, len);
return bBreakLoop;
}
void *EQWorker::Entry()
{
while(!mExitLoop) {
mMutex->Lock();
bool bufferAquired=false;
for(int i=0;i<mBufferInfoCount;i++)
if(mBufferInfoList[i].mBufferStatus==BufferReady) { // we found an unlocked ready buffer
bufferAquired=true;
mBufferInfoList[i].mBufferStatus=BufferBusy; // we own it now
mMutex->Unlock();
switch (mProcessingType)
{
int i = 0;
{
wxMutexLocker locker( mMutex );
for(; i < mBufferInfoCount; i++) {
if(mBufferInfoList[i].mBufferStatus==BufferReady) { // we found an unlocked ready buffer
mBufferInfoList[i].mBufferStatus=BufferBusy; // we own it now
break;
}
}
}
if ( i < mBufferInfoCount ) {
switch (mProcessingType)
{
case 1:
mEffectEqualization48x->ProcessBuffer1x(&mBufferInfoList[i]);
break;
case 4:
case 4:
mEffectEqualization48x->ProcessBuffer4x(&mBufferInfoList[i]);
break;
}
mBufferInfoList[i].mBufferStatus=BufferDone; // we're done
break;
}
if(!bufferAquired)
mMutex->Unlock();
}
mBufferInfoList[i].mBufferStatus=BufferDone; // we're done
}
}
return NULL;
}
@ -940,7 +943,7 @@ bool EffectEqualization48x::ProcessOne1x4xThreaded(int count, WaveTrack * t,
bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigBlocksWritten)/bigRuns.as_double());
if( bBreakLoop )
break;
mDataMutex.Lock(); // Get in line for data
wxMutexLocker locker( mDataMutex ); // Get in line for data
// process as many blocks as we can
while((mBufferInfo[currentIndex].mBufferStatus==BufferDone) && (bigBlocksWritten<bigRuns)) { // data is ours
output->Append((samplePtr)&mBufferInfo[currentIndex].mBufferDest[0][(bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)], floatSample, subBufferSize-((bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)));
@ -961,7 +964,6 @@ bool EffectEqualization48x::ProcessOne1x4xThreaded(int count, WaveTrack * t,
} else mBufferInfo[currentIndex].mBufferStatus=BufferEmpty; // this is completely unecessary
currentIndex=(currentIndex+1)%mWorkerDataCount;
}
mDataMutex.Unlock(); // Get back in line for data
}
if(singleProcessLength && !bBreakLoop) {
t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
@ -1237,7 +1239,7 @@ bool EffectEqualization48x::ProcessOne8xThreaded(int count, WaveTrack * t,
{
break;
}
mDataMutex.Lock(); // Get in line for data
wxMutexLocker locker( mDataMutex ); // Get in line for data
// process as many blocks as we can
while((mBufferInfo[currentIndex].mBufferStatus==BufferDone) && (bigBlocksWritten<bigRuns)) { // data is ours
output->Append((samplePtr)&mBufferInfo[currentIndex].mBufferDest[0][(bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)], floatSample, mSubBufferSize-((bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)));
@ -1258,7 +1260,6 @@ bool EffectEqualization48x::ProcessOne8xThreaded(int count, WaveTrack * t,
} else mBufferInfo[currentIndex].mBufferStatus=BufferEmpty; // this is completely unecessary
currentIndex=(currentIndex+1)%mWorkerDataCount;
}
mDataMutex.Unlock(); // Get back in line for data
}
if(singleProcessLength && !bBreakLoop) {
t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));

View File

@ -1656,14 +1656,11 @@ void EffectNoiseReduction::Dialog::OnPreview(wxCommandEvent & WXUNUSED(event))
return;
// Save & restore parameters around Preview, because we didn't do OK.
EffectNoiseReduction::Settings oldSettings(*m_pSettings);
auto cleanup = valueRestorer( *m_pSettings );
*m_pSettings = mTempSettings;
m_pSettings->mDoProfile = false;
m_pEffect->Preview();
*m_pSettings = oldSettings;
}
void EffectNoiseReduction::Dialog::OnReduceNoise( wxCommandEvent & WXUNUSED(event))

View File

@ -224,7 +224,6 @@ bool EffectNoiseRemoval::Process()
auto len = end - start;
if (!ProcessOne(count, track, start, len)) {
Cleanup();
bGoodResult = false;
break;
}
@ -238,8 +237,6 @@ bool EffectNoiseRemoval::Process()
mDoProfile = false;
}
if (bGoodResult)
Cleanup();
this->ReplaceProcessedTracks(bGoodResult);
return bGoodResult;
}
@ -305,7 +302,7 @@ void EffectNoiseRemoval::Initialize()
}
}
void EffectNoiseRemoval::Cleanup()
void EffectNoiseRemoval::End()
{
hFFT.reset();
@ -322,6 +319,8 @@ void EffectNoiseRemoval::Cleanup()
mInWaveBuffer.reset();
mWindow.reset();
mOutOverlapBuffer.reset();
mOutputTrack.reset();
}
void EffectNoiseRemoval::StartNewTrack()
@ -579,9 +578,6 @@ bool EffectNoiseRemoval::ProcessOne(int count, WaveTrack * track,
bool bResult = track->ClearAndPaste(t0, t0 + tLen, mOutputTrack.get(), true, false);
wxASSERT(bResult); // TO DO: Actually handle this.
}
// Delete the outputTrack now that its data is inserted in place
mOutputTrack.reset();
}
return bLoopSuccess;
@ -688,14 +684,16 @@ void NoiseRemovalDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
m_pEffect->mFreqSmoothingHz = mFreq;
m_pEffect->mAttackDecayTime = mTime;
m_pEffect->Preview();
auto cleanup = finally( [&] {
m_pEffect->mSensitivity = oldSensitivity;
m_pEffect->mNoiseGain = oldGain;
m_pEffect->mFreqSmoothingHz = oldFreq;
m_pEffect->mAttackDecayTime = oldTime;
m_pEffect->mbLeaveNoise = oldLeaveNoise;
m_pEffect->mDoProfile = oldDoProfile;
} );
m_pEffect->mSensitivity = oldSensitivity;
m_pEffect->mNoiseGain = oldGain;
m_pEffect->mFreqSmoothingHz = oldFreq;
m_pEffect->mAttackDecayTime = oldTime;
m_pEffect->mbLeaveNoise = oldLeaveNoise;
m_pEffect->mDoProfile = oldDoProfile;
m_pEffect->Preview();
}
void NoiseRemovalDialog::OnRemoveNoise( wxCommandEvent & WXUNUSED(event))

View File

@ -61,6 +61,7 @@ public:
bool Init() override;
bool CheckWhetherSkipEffect() override;
bool Process() override;
void End() override;
private:
@ -98,7 +99,6 @@ private:
void RemoveNoise();
void RotateHistoryWindows();
void FinishTrack();
void Cleanup();
// Variables that only exist during processing
std::unique_ptr<WaveTrack> mOutputTrack;

View File

@ -34,8 +34,6 @@ public:
ResampleBuf()
{
processed = 0;
outputLeftTrack = NULL;
outputRightTrack = NULL;
}
~ResampleBuf()

View File

@ -103,10 +103,9 @@ bool EffectSimpleMono::ProcessOne(WaveTrack * track,
track->Get((samplePtr) buffer.get(), floatSample, s, block);
//Process the buffer. If it fails, clean up and exit.
if (!ProcessSimpleMono(buffer.get(), block)) {
if (!ProcessSimpleMono(buffer.get(), block))
//Return false because the effect failed.
return false;
}
//Processing succeeded. copy the newly-changed samples back
//onto the track.
@ -118,9 +117,8 @@ bool EffectSimpleMono::ProcessOne(WaveTrack * track,
//Update the Progress meter
if (TrackProgress(mCurTrackNum,
(s - start).as_double() /
len)) {
len))
return false;
}
}
//Return true because the effect processing succeeded.

View File

@ -159,14 +159,17 @@ bool EffectSoundTouch::ProcessWithTimeWarper(const TimeWarper &warper)
if (bGoodResult)
ReplaceProcessedTracks(bGoodResult);
mSoundTouch.reset();
// mT0 = mCurT0;
// mT1 = mCurT0 + m_maxNewLength; // Update selection.
return bGoodResult;
}
void EffectSoundTouch::End()
{
mSoundTouch.reset();
}
//ProcessOne() takes a track, transforms it to bunch of buffer-blocks,
//and executes ProcessSoundTouch on these blocks
bool EffectSoundTouch::ProcessOne(WaveTrack *track,
@ -345,7 +348,7 @@ bool EffectSoundTouch::ProcessStereo(
return true;
}
bool EffectSoundTouch::ProcessStereoResults(const unsigned int outputCount,
bool EffectSoundTouch::ProcessStereoResults(const size_t outputCount,
WaveTrack* outputLeftTrack,
WaveTrack* outputRightTrack)
{

View File

@ -39,6 +39,10 @@ class EffectSoundTouch /* not final */ : public Effect
{
public:
// Effect implementation
void End() override;
// EffectSoundTouch implementation
#ifdef USE_MIDI
@ -66,7 +70,7 @@ private:
bool ProcessStereo(WaveTrack* leftTrack, WaveTrack* rightTrack,
sampleCount start, sampleCount end,
const TimeWarper &warper);
bool ProcessStereoResults(const unsigned int outputCount,
bool ProcessStereoResults(const size_t outputCount,
WaveTrack* outputLeftTrack,
WaveTrack* outputRightTrack);

View File

@ -125,11 +125,15 @@ bool EffectStereoToMono::Process()
count++;
}
mOutTrack.reset();
this->ReplaceProcessedTracks(bGoodResult);
return bGoodResult;
}
void EffectStereoToMono::End()
{
mOutTrack.reset();
}
bool EffectStereoToMono::ProcessOne(int count)
{
float curLeftFrame;

View File

@ -41,6 +41,7 @@ public:
// Effect implementation
bool Process() override;
void End() override;
bool IsHidden() override;
private:

View File

@ -165,9 +165,8 @@ double EffectTimeScale::CalcPreviewInputLength(double previewLength)
void EffectTimeScale::Preview(bool dryOnly)
{
previewSelectedDuration = Effect::GetDuration();
bPreview = true;
auto cleanup = valueRestorer( bPreview, true );
Effect::Preview(dryOnly);
bPreview = false;
}
bool EffectTimeScale::Process()

View File

@ -117,6 +117,8 @@ END_EVENT_TABLE()
NyquistEffect::NyquistEffect(const wxString &fName)
{
mOutputTrack[0] = mOutputTrack[1] = nullptr;
mAction = _("Applying Nyquist Effect...");
mInputCmd = wxEmptyString;
mCmd = wxEmptyString;
@ -670,6 +672,13 @@ _("Selection too long for Nyquist code.\nMaximum allowed selection is %ld sample
nyx_set_os_callback(StaticOSCallback, (void *)this);
nyx_capture_output(StaticOutputCallback, (void *)this);
auto cleanup = finally( [&] {
nyx_capture_output(NULL, (void *)NULL);
nyx_set_os_callback(NULL, (void *)NULL);
nyx_cleanup();
} );
if (mVersion >= 4)
{
mPerTrackProps = wxEmptyString;
@ -709,10 +718,6 @@ _("Selection too long for Nyquist code.\nMaximum allowed selection is %ld sample
success = ProcessOne();
nyx_capture_output(NULL, (void *)NULL);
nyx_set_os_callback(NULL, (void *)NULL);
nyx_cleanup();
// Reset previous locale
wxSetlocale(LC_NUMERIC, prevlocale);
@ -1110,16 +1115,23 @@ bool NyquistEffect::ProcessOne()
cmd += mCmd;
}
int i;
for (i = 0; i < mCurNumChannels; i++) {
// Put the fetch buffers in a clean initial state
for (size_t i = 0; i < mCurNumChannels; i++)
mCurBuffer[i].Free();
}
// Guarantee release of memory when done
auto cleanup = finally( [&] {
for (size_t i = 0; i < mCurNumChannels; i++)
mCurBuffer[i].Free();
} );
// Evaluate the expression, which may invoke the get callback, but often does
// not, leaving that to delayed evaluation of the output sound
rval = nyx_eval_expression(cmd.mb_str(wxConvUTF8));
// Audacity has no idea how long Nyquist processing will take, but
// can monitor audio being returned.
// Anything other than audio should be returmed almost instantly
// Anything other than audio should be returned almost instantly
// so notify the user that process has completed (bug 558)
if ((rval != nyx_audio) && ((mCount + mCurNumChannels) == mNumSelectedChannels)) {
if (mCurNumChannels == 1) {
@ -1225,19 +1237,30 @@ bool NyquistEffect::ProcessOne()
return false;
}
std::unique_ptr<WaveTrack> outputTrack[2];
double rate = mCurTrack[0]->GetRate();
for (i = 0; i < outChannels; i++) {
for (size_t i = 0; i < outChannels; i++) {
sampleFormat format = mCurTrack[i]->GetSampleFormat();
if (outChannels == (int)mCurNumChannels) {
rate = mCurTrack[i]->GetRate();
}
mOutputTrack[i] = mFactory->NewWaveTrack(format, rate);
outputTrack[i] = mFactory->NewWaveTrack(format, rate);
// Clean the initial buffer states again for the get callbacks
// -- is this really needed?
mCurBuffer[i].Free();
}
int success = nyx_get_audio(StaticPutCallback, (void *)this);
// Now fully evaluate the sound
int success;
{
auto vr0 = valueRestorer( mOutputTrack[0], outputTrack[0].get() );
auto vr1 = valueRestorer( mOutputTrack[1], outputTrack[1].get() );
success = nyx_get_audio(StaticPutCallback, (void *)this);
}
// See if GetCallback found read errors
if (mFailedFileName.IsOk())
@ -1250,38 +1273,29 @@ bool NyquistEffect::ProcessOne()
// what, then?
success = false;
if (!success) {
for(i = 0; i < outChannels; i++) {
mOutputTrack[i].reset();
}
if (!success)
return false;
}
for (i = 0; i < outChannels; i++) {
mOutputTrack[i]->Flush();
mCurBuffer[i].Free();
mOutputTime = mOutputTrack[i]->GetEndTime();
for (size_t i = 0; i < outChannels; i++) {
outputTrack[i]->Flush();
mOutputTime = outputTrack[i]->GetEndTime();
if (mOutputTime <= 0) {
wxMessageBox(_("Nyquist did not return audio.\n"),
wxT("Nyquist"),
wxOK | wxCENTRE, mUIParent);
for (int j = 0; j < outChannels; j++) {
mOutputTrack[j].reset();
}
return true;
}
}
for (i = 0; i < mCurNumChannels; i++) {
for (size_t i = 0; i < mCurNumChannels; i++) {
WaveTrack *out;
if (outChannels == (int)mCurNumChannels) {
out = mOutputTrack[i].get();
out = outputTrack[i].get();
}
else {
out = mOutputTrack[0].get();
out = outputTrack[0].get();
}
if (mMergeClips < 0) {
@ -1310,9 +1324,6 @@ bool NyquistEffect::ProcessOne()
mFirstInGroup = false;
}
for (i = 0; i < outChannels; i++) {
mOutputTrack[i].reset();
}
mProjectChanged = true;
return true;
}

View File

@ -224,7 +224,7 @@ private:
sampleCount mCurBufferStart[2];
size_t mCurBufferLen[2];
std::unique_ptr<WaveTrack> mOutputTrack[2];
WaveTrack *mOutputTrack[2];
wxArrayString mCategories;