mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-05 14:49:25 +02:00
Exception safety in: general effect performing functions
This commit is contained in:
parent
2cbdd1cc43
commit
79c3bef2ce
@ -131,7 +131,8 @@ public:
|
||||
|
||||
virtual bool IsReady() = 0;
|
||||
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) = 0;
|
||||
virtual bool ProcessFinalize() = 0;
|
||||
// This may be called during stack unwinding:
|
||||
virtual bool ProcessFinalize() /* noexcept */ = 0;
|
||||
virtual size_t ProcessBlock(float **inBlock, float **outBlock, size_t blockLen) = 0;
|
||||
|
||||
virtual bool RealtimeInitialize() = 0;
|
||||
|
@ -3628,6 +3628,27 @@ bool AudacityProject::OnEffect(const PluginID & ID, int flags)
|
||||
WaveTrack *newTrack{};
|
||||
wxWindow *focus = wxWindow::FindFocus();
|
||||
|
||||
bool success = false;
|
||||
auto cleanup = finally( [&] {
|
||||
|
||||
if (!success) {
|
||||
if (newTrack) {
|
||||
mTracks->Remove(newTrack);
|
||||
mTrackPanel->Refresh(false);
|
||||
}
|
||||
|
||||
// For now, we're limiting realtime preview to a single effect, so
|
||||
// make sure the menus reflect that fact that one may have just been
|
||||
// opened.
|
||||
UpdateMenus(false);
|
||||
}
|
||||
|
||||
if (focus != NULL) {
|
||||
focus->SetFocus();
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
//double prevEndTime = mTracks->GetEndTime();
|
||||
int count = 0;
|
||||
bool clean = true;
|
||||
@ -3650,24 +3671,13 @@ bool AudacityProject::OnEffect(const PluginID & ID, int flags)
|
||||
|
||||
EffectManager & em = EffectManager::Get();
|
||||
|
||||
bool success = em.DoEffect(ID, this, mRate,
|
||||
success = em.DoEffect(ID, this, mRate,
|
||||
GetTracks(), GetTrackFactory(),
|
||||
&mViewInfo.selectedRegion,
|
||||
(flags & OnEffectFlags::kConfigured) == 0);
|
||||
|
||||
if (!success) {
|
||||
if (newTrack) {
|
||||
mTracks->Remove(newTrack);
|
||||
mTrackPanel->Refresh(false);
|
||||
}
|
||||
|
||||
// For now, we're limiting realtime preview to a single effect, so
|
||||
// make sure the menus reflect that fact that one may have just been
|
||||
// opened.
|
||||
UpdateMenus(false);
|
||||
|
||||
if (!success)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (em.GetSkipStateFlag())
|
||||
flags = flags | OnEffectFlags::kSkipState;
|
||||
@ -3681,7 +3691,7 @@ bool AudacityProject::OnEffect(const PluginID & ID, int flags)
|
||||
|
||||
if (!(flags & OnEffectFlags::kDontRepeatLast))
|
||||
{
|
||||
// Only remember a successful effect, don't rmemeber insert,
|
||||
// Only remember a successful effect, don't remember insert,
|
||||
// or analyze effects.
|
||||
if (type == EffectTypeProcess) {
|
||||
wxString shortDesc = em.GetEffectName(ID);
|
||||
@ -3705,9 +3715,6 @@ bool AudacityProject::OnEffect(const PluginID & ID, int flags)
|
||||
// mTrackPanel->Refresh(false);
|
||||
}
|
||||
RedrawProject();
|
||||
if (focus != NULL) {
|
||||
focus->SetFocus();
|
||||
}
|
||||
mTrackPanel->EnsureVisible(mTrackPanel->GetFirstSelectedTrack());
|
||||
|
||||
mTrackPanel->Refresh(false);
|
||||
|
@ -1118,16 +1118,6 @@ void Effect::SetBatchProcessing(bool start)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct SetProgress {
|
||||
SetProgress(ProgressDialog *& mProgress_, ProgressDialog *progress)
|
||||
: mProgress(mProgress_)
|
||||
{ mProgress = progress; }
|
||||
~SetProgress() { mProgress = nullptr; }
|
||||
ProgressDialog *& mProgress;
|
||||
};
|
||||
}
|
||||
|
||||
bool Effect::DoEffect(wxWindow *parent,
|
||||
double projectRate,
|
||||
TrackList *list,
|
||||
@ -1191,26 +1181,33 @@ bool Effect::DoEffect(wxWindow *parent,
|
||||
|
||||
// Prompting will be bypassed when applying an effect that has already
|
||||
// been configured, e.g. repeating the last effect on a different selection.
|
||||
// Prompting may call Effect::Preview
|
||||
if (shouldPrompt && IsInteractive() && !PromptUser(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cleanup = finally( [&] {
|
||||
End();
|
||||
|
||||
// In case of failed effect, be sure to free memory.
|
||||
ReplaceProcessedTracks( false );
|
||||
} );
|
||||
|
||||
bool returnVal = true;
|
||||
bool skipFlag = CheckWhetherSkipEffect();
|
||||
if (skipFlag == false)
|
||||
{
|
||||
ProgressDialog progress(GetName(),
|
||||
ProgressDialog progress{
|
||||
GetName(),
|
||||
wxString::Format(_("Applying %s..."), GetName().c_str()),
|
||||
pdlgHideStopButton);
|
||||
SetProgress sp(mProgress, &progress);
|
||||
pdlgHideStopButton
|
||||
};
|
||||
auto vr = valueRestorer( mProgress, &progress );
|
||||
|
||||
returnVal = Process();
|
||||
}
|
||||
|
||||
End();
|
||||
|
||||
mOutputTracks.reset();
|
||||
|
||||
if (returnVal)
|
||||
{
|
||||
selectedRegion->setTimes(mT0, mT1);
|
||||
@ -1286,8 +1283,8 @@ bool Effect::ProcessPass()
|
||||
bool editClipCanMove;
|
||||
gPrefs->Read(wxT("/GUI/EditClipCanMove"), &editClipCanMove, true);
|
||||
|
||||
mInBuffer.reset();
|
||||
mOutBuffer.reset();
|
||||
FloatBuffers inBuffer, outBuffer;
|
||||
ArrayOf<float *> inBufPos, outBufPos;
|
||||
|
||||
ChannelName map[3];
|
||||
|
||||
@ -1389,36 +1386,36 @@ bool Effect::ProcessPass()
|
||||
{
|
||||
// Always create the number of input buffers the client expects even if we don't have
|
||||
// the same number of channels.
|
||||
mInBufPos.reinit( mNumAudioIn );
|
||||
mInBuffer.reinit( mNumAudioIn, mBufferSize );
|
||||
inBufPos.reinit( mNumAudioIn );
|
||||
inBuffer.reinit( mNumAudioIn, mBufferSize );
|
||||
|
||||
// We won't be using more than the first 2 buffers, so clear the rest (if any)
|
||||
for (size_t i = 2; i < mNumAudioIn; i++)
|
||||
{
|
||||
for (size_t j = 0; j < mBufferSize; j++)
|
||||
{
|
||||
mInBuffer[i][j] = 0.0;
|
||||
inBuffer[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Always create the number of output buffers the client expects even if we don't have
|
||||
// the same number of channels.
|
||||
mOutBufPos.reinit( mNumAudioOut );
|
||||
// Output buffers get an extra mBlockSize worth to give extra room if
|
||||
// the plugin adds latency
|
||||
mOutBuffer.reinit( mNumAudioOut, mBufferSize + mBlockSize );
|
||||
outBufPos.reinit( mNumAudioOut );
|
||||
outBuffer.reinit( mNumAudioOut, mBufferSize + mBlockSize );
|
||||
}
|
||||
|
||||
// (Re)Set the input buffer positions
|
||||
for (size_t i = 0; i < mNumAudioIn; i++)
|
||||
{
|
||||
mInBufPos[i] = mInBuffer[i].get();
|
||||
inBufPos[i] = inBuffer[i].get();
|
||||
}
|
||||
|
||||
// (Re)Set the output buffer positions
|
||||
for (size_t i = 0; i < mNumAudioOut; i++)
|
||||
{
|
||||
mOutBufPos[i] = mOutBuffer[i].get();
|
||||
outBufPos[i] = outBuffer[i].get();
|
||||
}
|
||||
|
||||
// Clear unused input buffers
|
||||
@ -1426,13 +1423,15 @@ bool Effect::ProcessPass()
|
||||
{
|
||||
for (size_t j = 0; j < mBufferSize; j++)
|
||||
{
|
||||
mInBuffer[1][j] = 0.0;
|
||||
inBuffer[1][j] = 0.0;
|
||||
}
|
||||
clear = true;
|
||||
}
|
||||
|
||||
// Go process the track(s)
|
||||
bGoodResult = ProcessTrack(count, map, left, right, leftStart, rightStart, len);
|
||||
bGoodResult = ProcessTrack(
|
||||
count, map, left, right, leftStart, rightStart, len,
|
||||
inBuffer, outBuffer, inBufPos, outBufPos);
|
||||
if (!bGoodResult)
|
||||
{
|
||||
break;
|
||||
@ -1441,11 +1440,6 @@ bool Effect::ProcessPass()
|
||||
count++;
|
||||
}
|
||||
|
||||
mOutBuffer.reset();
|
||||
mOutBufPos.reset();
|
||||
mInBuffer.reset();
|
||||
mInBufPos.reset();
|
||||
|
||||
if (bGoodResult && GetType() == EffectTypeGenerate)
|
||||
{
|
||||
mT1 = mT0 + mDuration;
|
||||
@ -1460,7 +1454,11 @@ bool Effect::ProcessTrack(int count,
|
||||
WaveTrack *right,
|
||||
sampleCount leftStart,
|
||||
sampleCount rightStart,
|
||||
sampleCount len)
|
||||
sampleCount len,
|
||||
FloatBuffers &inBuffer,
|
||||
FloatBuffers &outBuffer,
|
||||
ArrayOf< float * > &inBufPos,
|
||||
ArrayOf< float *> &outBufPos)
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
@ -1470,6 +1468,16 @@ bool Effect::ProcessTrack(int count,
|
||||
return false;
|
||||
}
|
||||
|
||||
{ // Start scope for cleanup
|
||||
auto cleanup = finally( [&] {
|
||||
// Allow the plugin to cleanup
|
||||
if (!ProcessFinalize())
|
||||
{
|
||||
// In case of non-exceptional flow of control, set rc
|
||||
rc = false;
|
||||
}
|
||||
} );
|
||||
|
||||
// For each input block of samples, we pass it to the effect along with a
|
||||
// variable output location. This output location is simply a pointer into a
|
||||
// much larger buffer. This reduces the number of calls required to add the
|
||||
@ -1539,16 +1547,16 @@ bool Effect::ProcessTrack(int count,
|
||||
limitSampleBufferSize( mBufferSize, inputRemaining );
|
||||
|
||||
// Fill the input buffers
|
||||
left->Get((samplePtr) mInBuffer[0].get(), floatSample, inLeftPos, inputBufferCnt);
|
||||
left->Get((samplePtr) inBuffer[0].get(), floatSample, inLeftPos, inputBufferCnt);
|
||||
if (right)
|
||||
{
|
||||
right->Get((samplePtr) mInBuffer[1].get(), floatSample, inRightPos, inputBufferCnt);
|
||||
right->Get((samplePtr) inBuffer[1].get(), floatSample, inRightPos, inputBufferCnt);
|
||||
}
|
||||
|
||||
// Reset the input buffer positions
|
||||
for (size_t i = 0; i < mNumChannels; i++)
|
||||
{
|
||||
mInBufPos[i] = mInBuffer[i].get();
|
||||
inBufPos[i] = inBuffer[i].get();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1568,7 +1576,7 @@ bool Effect::ProcessTrack(int count,
|
||||
{
|
||||
for (decltype(cnt) j = 0 ; j < cnt; j++)
|
||||
{
|
||||
mInBufPos[i][j + curBlockSize] = 0.0;
|
||||
inBufPos[i][j + curBlockSize] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1595,12 +1603,12 @@ bool Effect::ProcessTrack(int count,
|
||||
// Reset the input buffer positions
|
||||
for (size_t i = 0; i < mNumChannels; i++)
|
||||
{
|
||||
mInBufPos[i] = mInBuffer[i].get();
|
||||
inBufPos[i] = inBuffer[i].get();
|
||||
|
||||
// And clear
|
||||
for (size_t j = 0; j < mBlockSize; j++)
|
||||
{
|
||||
mInBuffer[i][j] = 0.0;
|
||||
inBuffer[i][j] = 0.0;
|
||||
}
|
||||
}
|
||||
cleared = true;
|
||||
@ -1611,7 +1619,7 @@ bool Effect::ProcessTrack(int count,
|
||||
decltype(curBlockSize) processed;
|
||||
try
|
||||
{
|
||||
processed = ProcessBlock(mInBufPos.get(), mOutBufPos.get(), curBlockSize);
|
||||
processed = ProcessBlock(inBufPos.get(), outBufPos.get(), curBlockSize);
|
||||
}
|
||||
catch( const AudacityException &e )
|
||||
{
|
||||
@ -1635,7 +1643,7 @@ bool Effect::ProcessTrack(int count,
|
||||
{
|
||||
for (size_t i = 0; i < mNumChannels; i++)
|
||||
{
|
||||
mInBufPos[i] += curBlockSize;
|
||||
inBufPos[i] += curBlockSize;
|
||||
}
|
||||
inputRemaining -= curBlockSize;
|
||||
inputBufferCnt -= curBlockSize;
|
||||
@ -1672,7 +1680,7 @@ bool Effect::ProcessTrack(int count,
|
||||
curBlockSize -= delay;
|
||||
for (size_t i = 0; i < chans; i++)
|
||||
{
|
||||
memmove(mOutBufPos[i], mOutBufPos[i] + delay, sizeof(float) * curBlockSize);
|
||||
memmove(outBufPos[i], outBufPos[i] + delay, sizeof(float) * curBlockSize);
|
||||
}
|
||||
curDelay = 0;
|
||||
}
|
||||
@ -1687,7 +1695,7 @@ bool Effect::ProcessTrack(int count,
|
||||
// Bump to next output buffer position
|
||||
for (size_t i = 0; i < chans; i++)
|
||||
{
|
||||
mOutBufPos[i] += curBlockSize;
|
||||
outBufPos[i] += curBlockSize;
|
||||
}
|
||||
}
|
||||
// Output buffers have filled
|
||||
@ -1696,32 +1704,32 @@ bool Effect::ProcessTrack(int count,
|
||||
if (isProcessor)
|
||||
{
|
||||
// Write them out
|
||||
left->Set((samplePtr) mOutBuffer[0].get(), floatSample, outLeftPos, outputBufferCnt);
|
||||
left->Set((samplePtr) outBuffer[0].get(), floatSample, outLeftPos, outputBufferCnt);
|
||||
if (right)
|
||||
{
|
||||
if (chans >= 2)
|
||||
{
|
||||
right->Set((samplePtr) mOutBuffer[1].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
right->Set((samplePtr) outBuffer[1].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
right->Set((samplePtr) mOutBuffer[0].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
right->Set((samplePtr) outBuffer[0].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isGenerator)
|
||||
{
|
||||
genLeft->Append((samplePtr) mOutBuffer[0].get(), floatSample, outputBufferCnt);
|
||||
genLeft->Append((samplePtr) outBuffer[0].get(), floatSample, outputBufferCnt);
|
||||
if (genRight)
|
||||
{
|
||||
genRight->Append((samplePtr) mOutBuffer[1].get(), floatSample, outputBufferCnt);
|
||||
genRight->Append((samplePtr) outBuffer[1].get(), floatSample, outputBufferCnt);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the output buffer positions
|
||||
for (size_t i = 0; i < chans; i++)
|
||||
{
|
||||
mOutBufPos[i] = mOutBuffer[i].get();
|
||||
outBufPos[i] = outBuffer[i].get();
|
||||
}
|
||||
|
||||
// Bump to the next track position
|
||||
@ -1753,34 +1761,34 @@ bool Effect::ProcessTrack(int count,
|
||||
}
|
||||
|
||||
// Put any remaining output
|
||||
if (outputBufferCnt)
|
||||
if (rc && outputBufferCnt)
|
||||
{
|
||||
if (isProcessor)
|
||||
{
|
||||
left->Set((samplePtr) mOutBuffer[0].get(), floatSample, outLeftPos, outputBufferCnt);
|
||||
left->Set((samplePtr) outBuffer[0].get(), floatSample, outLeftPos, outputBufferCnt);
|
||||
if (right)
|
||||
{
|
||||
if (chans >= 2)
|
||||
{
|
||||
right->Set((samplePtr) mOutBuffer[1].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
right->Set((samplePtr) outBuffer[1].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
right->Set((samplePtr) mOutBuffer[0].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
right->Set((samplePtr) outBuffer[0].get(), floatSample, outRightPos, outputBufferCnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (isGenerator)
|
||||
{
|
||||
genLeft->Append((samplePtr) mOutBuffer[0].get(), floatSample, outputBufferCnt);
|
||||
genLeft->Append((samplePtr) outBuffer[0].get(), floatSample, outputBufferCnt);
|
||||
if (genRight)
|
||||
{
|
||||
genRight->Append((samplePtr) mOutBuffer[1].get(), floatSample, outputBufferCnt);
|
||||
genRight->Append((samplePtr) outBuffer[1].get(), floatSample, outputBufferCnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isGenerator)
|
||||
if (rc && isGenerator)
|
||||
{
|
||||
AudacityProject *p = GetActiveProject();
|
||||
|
||||
@ -1794,7 +1802,7 @@ bool Effect::ProcessTrack(int count,
|
||||
// The purpose was to remap split lines inside the selected region when
|
||||
// a generator replaces it with sound of different duration. But
|
||||
// the "correct" version might have the effect of mapping some splits too
|
||||
// far left, to before the seletion.
|
||||
// far left, to before the selection.
|
||||
// In practice the wrong version probably did nothing most of the time,
|
||||
// because the cutoff time for the step time warper was 44100 times too
|
||||
// far from mT0.
|
||||
@ -1813,12 +1821,7 @@ bool Effect::ProcessTrack(int count,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow the plugin to cleanup
|
||||
if (!ProcessFinalize())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End scope for cleanup
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2125,20 +2128,26 @@ auto Effect::ModifyAnalysisTrack
|
||||
// Else clear and DELETE mOutputTracks copies.
|
||||
void Effect::ReplaceProcessedTracks(const bool bGoodResult)
|
||||
{
|
||||
wxASSERT(mOutputTracks); // Make sure we at least did the CopyInputTracks().
|
||||
|
||||
if (!bGoodResult) {
|
||||
// Free resources, unless already freed.
|
||||
|
||||
// Processing failed or was cancelled so throw away the processed tracks.
|
||||
mOutputTracks->Clear();
|
||||
if ( mOutputTracks )
|
||||
mOutputTracks->Clear();
|
||||
|
||||
// Reset map
|
||||
mIMap.clear();
|
||||
mOMap.clear();
|
||||
|
||||
mOutputTracksType = Track::None;
|
||||
|
||||
//TODO:undo the non-gui ODTask transfer
|
||||
return;
|
||||
}
|
||||
|
||||
// Assume resources need to be freed.
|
||||
wxASSERT(mOutputTracks); // Make sure we at least did the CopyInputTracks().
|
||||
|
||||
auto iterOut = mOutputTracks->begin(), iterEnd = mOutputTracks->end();
|
||||
|
||||
size_t cnt = mOMap.size();
|
||||
@ -2465,142 +2474,144 @@ void Effect::Preview(bool dryOnly)
|
||||
return;
|
||||
|
||||
bool success = true;
|
||||
double oldT0 = mT0;
|
||||
double oldT1 = mT1;
|
||||
auto vr0 = valueRestorer( mT0 );
|
||||
auto vr1 = valueRestorer( mT1 );
|
||||
// Most effects should stop at t1.
|
||||
if (!mPreviewFullSelection)
|
||||
mT1 = t1;
|
||||
|
||||
{
|
||||
// Save the original track list
|
||||
TrackList *saveTracks = mTracks;
|
||||
auto cleanup = finally( [&] { mTracks = saveTracks; } );
|
||||
// Save the original track list
|
||||
TrackList *saveTracks = mTracks;
|
||||
|
||||
// Build NEW tracklist from rendering tracks
|
||||
auto uTracks = std::make_unique<TrackList>();
|
||||
mTracks = uTracks.get();
|
||||
auto cleanup = finally( [&] {
|
||||
mTracks = saveTracks;
|
||||
|
||||
// Linear Effect preview optimised by pre-mixing to one track.
|
||||
// Generators need to generate per track.
|
||||
if (mIsLinearEffect && !isGenerator) {
|
||||
WaveTrack::Holder mixLeft, mixRight;
|
||||
MixAndRender(saveTracks, mFactory, rate, floatSample, mT0, t1, mixLeft, mixRight);
|
||||
if (!mixLeft)
|
||||
return;
|
||||
|
||||
mixLeft->Offset(-mixLeft->GetStartTime());
|
||||
mixLeft->InsertSilence(0.0, mT0);
|
||||
mixLeft->SetSelected(true);
|
||||
mixLeft->SetDisplay(WaveTrack::NoDisplay);
|
||||
mTracks->Add(std::move(mixLeft));
|
||||
if (mixRight) {
|
||||
mixRight->Offset(-mixRight->GetStartTime());
|
||||
mixRight->InsertSilence(0.0, mT0);
|
||||
mixRight->SetSelected(true);
|
||||
mTracks->Add(std::move(mixRight));
|
||||
}
|
||||
}
|
||||
else {
|
||||
TrackListOfKindIterator iter(Track::Wave, saveTracks);
|
||||
WaveTrack *src = (WaveTrack *) iter.First();
|
||||
while (src)
|
||||
{
|
||||
if (src->GetSelected() || mPreviewWithNotSelected) {
|
||||
auto dest = src->Copy(mT0, t1);
|
||||
dest->InsertSilence(0.0, mT0);
|
||||
dest->SetSelected(src->GetSelected());
|
||||
static_cast<WaveTrack*>(dest.get())->SetDisplay(WaveTrack::NoDisplay);
|
||||
mTracks->Add(std::move(dest));
|
||||
}
|
||||
src = (WaveTrack *) iter.Next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update track/group counts
|
||||
CountWaveTracks();
|
||||
|
||||
// Apply effect
|
||||
// Effect is already inited; we will call Process, End, and then Init
|
||||
// again, so the state is exactly the way it was before Preview
|
||||
// was called.
|
||||
if (!dryOnly) {
|
||||
ProgressDialog progress(GetName(),
|
||||
_("Preparing preview"),
|
||||
pdlgHideCancelButton); // Have only "Stop" button.
|
||||
SetProgress sp(mProgress, &progress);
|
||||
mIsPreview = true;
|
||||
success = Process();
|
||||
mIsPreview = false;
|
||||
End();
|
||||
GuardedCall< void >( [&]{ Init(); } );
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
WaveTrackConstArray playbackTracks;
|
||||
WaveTrackArray recordingTracks;
|
||||
|
||||
SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
|
||||
WaveTrack *src = (WaveTrack *) iter.First();
|
||||
while (src) {
|
||||
playbackTracks.push_back(src);
|
||||
src = (WaveTrack *) iter.Next();
|
||||
}
|
||||
// Some effects (Paulstretch) may need to generate more
|
||||
// than previewLen, so take the min.
|
||||
t1 = std::min(mT0 + previewLen, mT1);
|
||||
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
NoteTrackArray empty;
|
||||
#endif
|
||||
// Start audio playing
|
||||
AudioIOStartStreamOptions options { rate };
|
||||
int token =
|
||||
gAudioIO->StartStream(playbackTracks, recordingTracks,
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
empty,
|
||||
#endif
|
||||
mT0, t1, options);
|
||||
|
||||
if (token) {
|
||||
auto previewing = ProgressResult::Success;
|
||||
// The progress dialog must be deleted before stopping the stream
|
||||
// to allow events to flow to the app during StopStream processing.
|
||||
// The progress dialog blocks these events.
|
||||
{
|
||||
ProgressDialog progress
|
||||
(GetName(), _("Previewing"), pdlgHideCancelButton);
|
||||
|
||||
while (gAudioIO->IsStreamActive(token) && previewing == ProgressResult::Success) {
|
||||
::wxMilliSleep(100);
|
||||
previewing = progress.Update(gAudioIO->GetStreamTime() - mT0, t1 - mT0);
|
||||
}
|
||||
}
|
||||
|
||||
gAudioIO->StopStream();
|
||||
|
||||
while (gAudioIO->IsBusy()) {
|
||||
::wxMilliSleep(100);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wxMessageBox(_("Error opening sound device. Try changing the audio host, playback device and the project sample rate."),
|
||||
_("Error"), wxOK | wxICON_EXCLAMATION, FocusDialog);
|
||||
}
|
||||
}
|
||||
|
||||
if (FocusDialog) {
|
||||
FocusDialog->SetFocus();
|
||||
}
|
||||
|
||||
mOutputTracks.reset();
|
||||
|
||||
// In case of failed effect, be sure to free memory.
|
||||
ReplaceProcessedTracks( false );
|
||||
} );
|
||||
|
||||
// Build NEW tracklist from rendering tracks
|
||||
auto uTracks = std::make_unique<TrackList>();
|
||||
mTracks = uTracks.get();
|
||||
|
||||
// Linear Effect preview optimised by pre-mixing to one track.
|
||||
// Generators need to generate per track.
|
||||
if (mIsLinearEffect && !isGenerator) {
|
||||
WaveTrack::Holder mixLeft, mixRight;
|
||||
MixAndRender(saveTracks, mFactory, rate, floatSample, mT0, t1, mixLeft, mixRight);
|
||||
if (!mixLeft)
|
||||
return;
|
||||
|
||||
mixLeft->Offset(-mixLeft->GetStartTime());
|
||||
mixLeft->InsertSilence(0.0, mT0);
|
||||
mixLeft->SetSelected(true);
|
||||
mixLeft->SetDisplay(WaveTrack::NoDisplay);
|
||||
mTracks->Add(std::move(mixLeft));
|
||||
if (mixRight) {
|
||||
mixRight->Offset(-mixRight->GetStartTime());
|
||||
mixRight->InsertSilence(0.0, mT0);
|
||||
mixRight->SetSelected(true);
|
||||
mTracks->Add(std::move(mixRight));
|
||||
}
|
||||
}
|
||||
else {
|
||||
TrackListOfKindIterator iter(Track::Wave, saveTracks);
|
||||
WaveTrack *src = (WaveTrack *) iter.First();
|
||||
while (src)
|
||||
{
|
||||
if (src->GetSelected() || mPreviewWithNotSelected) {
|
||||
auto dest = src->Copy(mT0, t1);
|
||||
dest->InsertSilence(0.0, mT0);
|
||||
dest->SetSelected(src->GetSelected());
|
||||
static_cast<WaveTrack*>(dest.get())->SetDisplay(WaveTrack::NoDisplay);
|
||||
mTracks->Add(std::move(dest));
|
||||
}
|
||||
src = (WaveTrack *) iter.Next();
|
||||
}
|
||||
}
|
||||
|
||||
mT0 = oldT0;
|
||||
mT1 = oldT1;
|
||||
|
||||
// Effect is already inited; we call Process, End, and then Init
|
||||
// again, so the state is exactly the way it was before Preview
|
||||
// was called.
|
||||
// Update track/group counts
|
||||
CountWaveTracks();
|
||||
|
||||
// Apply effect
|
||||
if (!dryOnly) {
|
||||
End();
|
||||
Init();
|
||||
ProgressDialog progress{
|
||||
GetName(),
|
||||
_("Preparing preview"),
|
||||
pdlgHideCancelButton
|
||||
}; // Have only "Stop" button.
|
||||
auto vr = valueRestorer( mProgress, &progress );
|
||||
|
||||
auto vr2 = valueRestorer( mIsPreview, true );
|
||||
|
||||
success = Process();
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
WaveTrackConstArray playbackTracks;
|
||||
WaveTrackArray recordingTracks;
|
||||
|
||||
SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
|
||||
WaveTrack *src = (WaveTrack *) iter.First();
|
||||
while (src) {
|
||||
playbackTracks.push_back(src);
|
||||
src = (WaveTrack *) iter.Next();
|
||||
}
|
||||
// Some effects (Paulstretch) may need to generate more
|
||||
// than previewLen, so take the min.
|
||||
t1 = std::min(mT0 + previewLen, mT1);
|
||||
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
NoteTrackArray empty;
|
||||
#endif
|
||||
// Start audio playing
|
||||
AudioIOStartStreamOptions options { rate };
|
||||
int token =
|
||||
gAudioIO->StartStream(playbackTracks, recordingTracks,
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
empty,
|
||||
#endif
|
||||
mT0, t1, options);
|
||||
|
||||
if (token) {
|
||||
auto previewing = ProgressResult::Success;
|
||||
// The progress dialog must be deleted before stopping the stream
|
||||
// to allow events to flow to the app during StopStream processing.
|
||||
// The progress dialog blocks these events.
|
||||
{
|
||||
ProgressDialog progress
|
||||
(GetName(), _("Previewing"), pdlgHideCancelButton);
|
||||
|
||||
while (gAudioIO->IsStreamActive(token) && previewing == ProgressResult::Success) {
|
||||
::wxMilliSleep(100);
|
||||
previewing = progress.Update(gAudioIO->GetStreamTime() - mT0, t1 - mT0);
|
||||
}
|
||||
}
|
||||
|
||||
gAudioIO->StopStream();
|
||||
|
||||
while (gAudioIO->IsBusy()) {
|
||||
::wxMilliSleep(100);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wxMessageBox(_("Error opening sound device. Try changing the audio host, playback device and the project sample rate."),
|
||||
_("Error"), wxOK | wxICON_EXCLAMATION, FocusDialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3167,11 +3178,10 @@ void EffectUIHost::OnApply(wxCommandEvent & evt)
|
||||
// Progress dialog no longer yields, so this "shouldn't" be necessary (yet to be proven
|
||||
// for sure), but it is a nice visual cue that something is going on.
|
||||
mApplyBtn->Disable();
|
||||
auto cleanup = finally( [&] { mApplyBtn->Enable(); } );
|
||||
|
||||
mEffect->Apply();
|
||||
|
||||
mApplyBtn->Enable();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -287,7 +287,9 @@ protected:
|
||||
virtual bool InitPass2();
|
||||
virtual int GetPass();
|
||||
|
||||
// clean up any temporary memory
|
||||
// clean up any temporary memory, needed only per invocation of the
|
||||
// effect, after either successful or failed or exception-aborted processing.
|
||||
// Invoked inside a "finally" block so it must be no-throw.
|
||||
virtual void End();
|
||||
|
||||
// Most effects just use the previewLength, but time-stretching/compressing
|
||||
@ -470,8 +472,12 @@ protected:
|
||||
WaveTrack *right,
|
||||
sampleCount leftStart,
|
||||
sampleCount rightStart,
|
||||
sampleCount len);
|
||||
|
||||
sampleCount len,
|
||||
FloatBuffers &inBuffer,
|
||||
FloatBuffers &outBuffer,
|
||||
ArrayOf< float * > &inBufPos,
|
||||
ArrayOf< float *> &outBufPos);
|
||||
|
||||
//
|
||||
// private data
|
||||
//
|
||||
@ -505,11 +511,6 @@ private:
|
||||
size_t mNumAudioIn;
|
||||
size_t mNumAudioOut;
|
||||
|
||||
FloatBuffers mInBuffer, mOutBuffer;
|
||||
|
||||
using Positions = ArrayOf < float* > ; // Array of non-owning pointers into the above
|
||||
Positions mInBufPos, mOutBufPos;
|
||||
|
||||
size_t mBufferSize;
|
||||
size_t mBlockSize;
|
||||
unsigned mNumChannels;
|
||||
|
@ -17,6 +17,8 @@
|
||||
#if defined(EXPERIMENTAL_EFFECTS_RACK)
|
||||
|
||||
#include "../MemoryX.h"
|
||||
#include "../UndoManager.h"
|
||||
|
||||
#include <wx/access.h>
|
||||
#include <wx/defs.h>
|
||||
#include <wx/bmpbuttn.h>
|
||||
@ -289,17 +291,36 @@ void EffectRack::OnTimer(wxTimerEvent & WXUNUSED(evt))
|
||||
void EffectRack::OnApply(wxCommandEvent & WXUNUSED(evt))
|
||||
{
|
||||
AudacityProject *project = GetActiveProject();
|
||||
|
||||
|
||||
bool success = false;
|
||||
auto state = project->GetUndoManager()->GetCurrentState();
|
||||
auto cleanup = finally( [&] {
|
||||
if(!success)
|
||||
project->SetStateTo(state);
|
||||
} );
|
||||
|
||||
for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
|
||||
{
|
||||
if (mPowerState[i])
|
||||
{
|
||||
project->OnEffect(mEffects[i]->GetID(),
|
||||
AudacityProject::OnEffectFlags::kConfigured);
|
||||
if (!project->OnEffect(mEffects[i]->GetID(),
|
||||
AudacityProject::OnEffectFlags::kConfigured))
|
||||
// If any effect fails (or throws), then stop.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
// Only after all succeed, do the following.
|
||||
for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
|
||||
{
|
||||
if (mPowerState[i])
|
||||
{
|
||||
mPowerState[i] = false;
|
||||
|
||||
wxBitmapButton *btn = static_cast<wxBitmapButton *>(FindWindowById(ID_POWER + i));
|
||||
wxBitmapButton *btn =
|
||||
static_cast<wxBitmapButton *>(FindWindowById(ID_POWER + i));
|
||||
btn->SetBitmapLabel(mPowerRaised);
|
||||
btn->SetBitmapSelected(mPowerRaised);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user