1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-03 06:03:13 +02:00

Free four effect-releated files from the big s.c.c. ...

... They are:
Effect
EffectManager, EffectRack (in a cycle of 2)
Nyquist

There is also one new file, RealtimeEffectManager.  Making AudioIO use that,
instead of the whole of EffectManager, was essential to this improvement of
the graph.
This commit is contained in:
Paul Licameli 2019-06-23 23:20:44 -04:00
commit ba4fe34947
29 changed files with 824 additions and 664 deletions

View File

@ -156,7 +156,9 @@ public:
virtual int GetMidiOutCount() = 0;
virtual void SetSampleRate(double rate) = 0;
// Suggest a block size, but the return is the size that was really set:
virtual size_t SetBlockSize(size_t maxBlockSize) = 0;
virtual size_t GetBlockSize() const = 0;
virtual sampleCount GetLatency() = 0;
virtual size_t GetTailSize() = 0;

View File

@ -427,6 +427,8 @@ src/effects/Paulstretch.cpp
src/effects/Paulstretch.h
src/effects/Phaser.cpp
src/effects/Phaser.h
src/effects/RealtimeEffectManager.cpp
src/effects/RealtimeEffectManager.h
src/effects/Repair.cpp
src/effects/Repair.h
src/effects/Repeat.cpp

View File

@ -1235,6 +1235,7 @@
5E19D655217D51190024D0B1 /* PluginMenus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E19D64C217D51190024D0B1 /* PluginMenus.cpp */; };
5E19F59922A9665500E3F88E /* AutoRecoveryDialog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E19F59722A9665500E3F88E /* AutoRecoveryDialog.cpp */; };
5E2A19941EED688500217B58 /* SelectionState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2A19921EED688500217B58 /* SelectionState.cpp */; };
5E2B3E6222BF9621005042E1 /* RealtimeEffectManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2B3E6022BF9621005042E1 /* RealtimeEffectManager.cpp */; };
5E2BF3852193A2A500995694 /* TrackView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2BF3832193A2A500995694 /* TrackView.cpp */; };
5E2BF3882193A2BD00995694 /* TimeTrackView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2BF3862193A2BD00995694 /* TimeTrackView.cpp */; };
5E2BF38B2193A2E400995694 /* WaveTrackView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E2BF3892193A2E400995694 /* WaveTrackView.cpp */; };
@ -3237,6 +3238,8 @@
5E19F59822A9665500E3F88E /* AutoRecoveryDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoRecoveryDialog.h; sourceTree = "<group>"; };
5E2A19921EED688500217B58 /* SelectionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectionState.cpp; sourceTree = "<group>"; };
5E2A19931EED688500217B58 /* SelectionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectionState.h; sourceTree = "<group>"; };
5E2B3E6022BF9621005042E1 /* RealtimeEffectManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RealtimeEffectManager.cpp; sourceTree = "<group>"; };
5E2B3E6122BF9621005042E1 /* RealtimeEffectManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealtimeEffectManager.h; sourceTree = "<group>"; };
5E2BF3832193A2A500995694 /* TrackView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackView.cpp; sourceTree = "<group>"; };
5E2BF3842193A2A500995694 /* TrackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackView.h; sourceTree = "<group>"; };
5E2BF3862193A2BD00995694 /* TimeTrackView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeTrackView.cpp; sourceTree = "<group>"; };
@ -4716,10 +4719,12 @@
1790B03209883BFD008A330A /* Normalize.cpp */,
1790B03309883BFD008A330A /* Normalize.h */,
1790B03409883BFD008A330A /* nyquist */,
1790B03A09883BFD008A330A /* Phaser.cpp */,
1790B03B09883BFD008A330A /* Phaser.h */,
EDF3B7AF1588C0D50032D35F /* Paulstretch.cpp */,
EDF3B7AE1588C0D50032D35F /* Paulstretch.h */,
1790B03A09883BFD008A330A /* Phaser.cpp */,
1790B03B09883BFD008A330A /* Phaser.h */,
5E2B3E6022BF9621005042E1 /* RealtimeEffectManager.cpp */,
5E2B3E6122BF9621005042E1 /* RealtimeEffectManager.h */,
28EBA7FA0A78FADE00C8BB1F /* Repair.cpp */,
28EBA7FB0A78FADE00C8BB1F /* Repair.h */,
1790B03E09883BFD008A330A /* Repeat.cpp */,
@ -8477,6 +8482,7 @@
1790B19009883BFD008A330A /* Sequence.cpp in Sources */,
5E36A0AF217FA2430068E082 /* ViewMenus.cpp in Sources */,
1790B19109883BFD008A330A /* Shuttle.cpp in Sources */,
5E2B3E6222BF9621005042E1 /* RealtimeEffectManager.cpp in Sources */,
1790B19209883BFD008A330A /* Spectrum.cpp in Sources */,
1790B19309883BFD008A330A /* Tags.cpp in Sources */,
1790B19409883BFD008A330A /* TimeTrack.cpp in Sources */,

View File

@ -459,7 +459,7 @@ time warp info and AudioIOListener and whether the playback is looped.
#include "WaveTrack.h"
#include "AutoRecovery.h"
#include "effects/EffectManager.h"
#include "effects/RealtimeEffectManager.h"
#include "prefs/QualityPrefs.h"
#include "prefs/RecordingPrefs.h"
#include "toolbars/ControlToolBar.h"
@ -1634,7 +1634,7 @@ int AudioIO::StartStream(const TransportTracks &tracks,
if (mNumPlaybackChannels > 0)
{
EffectManager & em = EffectManager::Get();
auto & em = RealtimeEffectManager::Get();
// Setup for realtime playback at the rate of the realtime
// stream, not the rate of the track.
em.RealtimeInitialize(mRate);
@ -1987,7 +1987,7 @@ void AudioIO::StartStreamCleanup(bool bOnlyBuffers)
{
if (mNumPlaybackChannels > 0)
{
EffectManager::Get().RealtimeFinalize();
RealtimeEffectManager::Get().RealtimeFinalize();
}
mPlaybackBuffers.reset();
@ -2164,7 +2164,7 @@ void AudioIO::StopStream()
// No longer need effects processing
if (mNumPlaybackChannels > 0)
{
EffectManager::Get().RealtimeFinalize();
RealtimeEffectManager::Get().RealtimeFinalize();
}
//
@ -2414,11 +2414,11 @@ void AudioIO::SetPaused(bool state)
{
if (state)
{
EffectManager::Get().RealtimeSuspend();
RealtimeEffectManager::Get().RealtimeSuspend();
}
else
{
EffectManager::Get().RealtimeResume();
RealtimeEffectManager::Get().RealtimeResume();
}
}
@ -3794,7 +3794,7 @@ bool AudioIoCallback::FillOutputBuffers(
tempBufs[c] = (float *) alloca(framesPerBuffer * sizeof(float));
// ------ End of MEMORY ALLOCATION ---------------
EffectManager & em = EffectManager::Get();
auto & em = RealtimeEffectManager::Get();
em.RealtimeProcessStart();
bool selected = false;

View File

@ -34,6 +34,7 @@ processing. See also MacrosWindow and ApplyMacroDialog.
#include "PluginManager.h"
#include "Prefs.h"
#include "Shuttle.h"
#include "Track.h"
#include "export/ExportFLAC.h"
#include "export/ExportMP3.h"
#include "export/ExportOGG.h"

View File

@ -454,6 +454,8 @@ audacity_SOURCES = \
effects/Paulstretch.h \
effects/Phaser.cpp \
effects/Phaser.h \
effects/RealtimeEffectManager.cpp \
effects/RealtimeEffectManager.h \
effects/Repair.cpp \
effects/Repair.h \
effects/Repeat.cpp \

View File

@ -412,23 +412,25 @@ am__audacity_SOURCES_DIST = BlockFile.cpp BlockFile.h DirManager.cpp \
effects/NoiseRemoval.h effects/Normalize.cpp \
effects/Normalize.h effects/Paulstretch.cpp \
effects/Paulstretch.h effects/Phaser.cpp effects/Phaser.h \
effects/Repair.cpp effects/Repair.h effects/Repeat.cpp \
effects/Repeat.h effects/Reverb.cpp effects/Reverb.h \
effects/Reverb_libSoX.h effects/Reverse.cpp effects/Reverse.h \
effects/SBSMSEffect.cpp effects/SBSMSEffect.h \
effects/ScienFilter.cpp effects/ScienFilter.h \
effects/Silence.cpp effects/Silence.h effects/SimpleMono.cpp \
effects/SimpleMono.h effects/SoundTouchEffect.cpp \
effects/SoundTouchEffect.h effects/StereoToMono.cpp \
effects/StereoToMono.h effects/TimeScale.cpp \
effects/TimeScale.h effects/TimeWarper.cpp \
effects/TimeWarper.h effects/ToneGen.cpp effects/ToneGen.h \
effects/TruncSilence.cpp effects/TruncSilence.h \
effects/TwoPassSimpleMono.cpp effects/TwoPassSimpleMono.h \
effects/Wahwah.cpp effects/Wahwah.h export/Export.cpp \
export/Export.h export/ExportCL.cpp export/ExportCL.h \
export/ExportFLAC.cpp export/ExportFLAC.h export/ExportMP2.cpp \
export/ExportMP2.h export/ExportMP3.cpp export/ExportMP3.h \
effects/RealtimeEffectManager.cpp \
effects/RealtimeEffectManager.h effects/Repair.cpp \
effects/Repair.h effects/Repeat.cpp effects/Repeat.h \
effects/Reverb.cpp effects/Reverb.h effects/Reverb_libSoX.h \
effects/Reverse.cpp effects/Reverse.h effects/SBSMSEffect.cpp \
effects/SBSMSEffect.h effects/ScienFilter.cpp \
effects/ScienFilter.h effects/Silence.cpp effects/Silence.h \
effects/SimpleMono.cpp effects/SimpleMono.h \
effects/SoundTouchEffect.cpp effects/SoundTouchEffect.h \
effects/StereoToMono.cpp effects/StereoToMono.h \
effects/TimeScale.cpp effects/TimeScale.h \
effects/TimeWarper.cpp effects/TimeWarper.h \
effects/ToneGen.cpp effects/ToneGen.h effects/TruncSilence.cpp \
effects/TruncSilence.h effects/TwoPassSimpleMono.cpp \
effects/TwoPassSimpleMono.h effects/Wahwah.cpp \
effects/Wahwah.h export/Export.cpp export/Export.h \
export/ExportCL.cpp export/ExportCL.h export/ExportFLAC.cpp \
export/ExportFLAC.h export/ExportMP2.cpp export/ExportMP2.h \
export/ExportMP3.cpp export/ExportMP3.h \
export/ExportMultiple.cpp export/ExportMultiple.h \
export/ExportOGG.cpp export/ExportOGG.h export/ExportPCM.cpp \
export/ExportPCM.h import/Import.cpp import/Import.h \
@ -776,6 +778,7 @@ am_audacity_OBJECTS = $(am__objects_1) audacity-AboutDialog.$(OBJEXT) \
effects/audacity-Normalize.$(OBJEXT) \
effects/audacity-Paulstretch.$(OBJEXT) \
effects/audacity-Phaser.$(OBJEXT) \
effects/audacity-RealtimeEffectManager.$(OBJEXT) \
effects/audacity-Repair.$(OBJEXT) \
effects/audacity-Repeat.$(OBJEXT) \
effects/audacity-Reverb.$(OBJEXT) \
@ -1510,23 +1513,25 @@ audacity_SOURCES = $(libaudacity_la_SOURCES) AboutDialog.cpp \
effects/NoiseRemoval.h effects/Normalize.cpp \
effects/Normalize.h effects/Paulstretch.cpp \
effects/Paulstretch.h effects/Phaser.cpp effects/Phaser.h \
effects/Repair.cpp effects/Repair.h effects/Repeat.cpp \
effects/Repeat.h effects/Reverb.cpp effects/Reverb.h \
effects/Reverb_libSoX.h effects/Reverse.cpp effects/Reverse.h \
effects/SBSMSEffect.cpp effects/SBSMSEffect.h \
effects/ScienFilter.cpp effects/ScienFilter.h \
effects/Silence.cpp effects/Silence.h effects/SimpleMono.cpp \
effects/SimpleMono.h effects/SoundTouchEffect.cpp \
effects/SoundTouchEffect.h effects/StereoToMono.cpp \
effects/StereoToMono.h effects/TimeScale.cpp \
effects/TimeScale.h effects/TimeWarper.cpp \
effects/TimeWarper.h effects/ToneGen.cpp effects/ToneGen.h \
effects/TruncSilence.cpp effects/TruncSilence.h \
effects/TwoPassSimpleMono.cpp effects/TwoPassSimpleMono.h \
effects/Wahwah.cpp effects/Wahwah.h export/Export.cpp \
export/Export.h export/ExportCL.cpp export/ExportCL.h \
export/ExportFLAC.cpp export/ExportFLAC.h export/ExportMP2.cpp \
export/ExportMP2.h export/ExportMP3.cpp export/ExportMP3.h \
effects/RealtimeEffectManager.cpp \
effects/RealtimeEffectManager.h effects/Repair.cpp \
effects/Repair.h effects/Repeat.cpp effects/Repeat.h \
effects/Reverb.cpp effects/Reverb.h effects/Reverb_libSoX.h \
effects/Reverse.cpp effects/Reverse.h effects/SBSMSEffect.cpp \
effects/SBSMSEffect.h effects/ScienFilter.cpp \
effects/ScienFilter.h effects/Silence.cpp effects/Silence.h \
effects/SimpleMono.cpp effects/SimpleMono.h \
effects/SoundTouchEffect.cpp effects/SoundTouchEffect.h \
effects/StereoToMono.cpp effects/StereoToMono.h \
effects/TimeScale.cpp effects/TimeScale.h \
effects/TimeWarper.cpp effects/TimeWarper.h \
effects/ToneGen.cpp effects/ToneGen.h effects/TruncSilence.cpp \
effects/TruncSilence.h effects/TwoPassSimpleMono.cpp \
effects/TwoPassSimpleMono.h effects/Wahwah.cpp \
effects/Wahwah.h export/Export.cpp export/Export.h \
export/ExportCL.cpp export/ExportCL.h export/ExportFLAC.cpp \
export/ExportFLAC.h export/ExportMP2.cpp export/ExportMP2.h \
export/ExportMP3.cpp export/ExportMP3.h \
export/ExportMultiple.cpp export/ExportMultiple.h \
export/ExportOGG.cpp export/ExportOGG.h export/ExportPCM.cpp \
export/ExportPCM.h import/Import.cpp import/Import.h \
@ -2009,6 +2014,8 @@ effects/audacity-Paulstretch.$(OBJEXT): effects/$(am__dirstamp) \
effects/$(DEPDIR)/$(am__dirstamp)
effects/audacity-Phaser.$(OBJEXT): effects/$(am__dirstamp) \
effects/$(DEPDIR)/$(am__dirstamp)
effects/audacity-RealtimeEffectManager.$(OBJEXT): \
effects/$(am__dirstamp) effects/$(DEPDIR)/$(am__dirstamp)
effects/audacity-Repair.$(OBJEXT): effects/$(am__dirstamp) \
effects/$(DEPDIR)/$(am__dirstamp)
effects/audacity-Repeat.$(OBJEXT): effects/$(am__dirstamp) \
@ -2760,6 +2767,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Normalize.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Paulstretch.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Phaser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-RealtimeEffectManager.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Repair.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Repeat.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@effects/$(DEPDIR)/audacity-Reverb.Po@am__quote@
@ -5639,6 +5647,20 @@ effects/audacity-Phaser.obj: effects/Phaser.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o effects/audacity-Phaser.obj `if test -f 'effects/Phaser.cpp'; then $(CYGPATH_W) 'effects/Phaser.cpp'; else $(CYGPATH_W) '$(srcdir)/effects/Phaser.cpp'; fi`
effects/audacity-RealtimeEffectManager.o: effects/RealtimeEffectManager.cpp
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT effects/audacity-RealtimeEffectManager.o -MD -MP -MF effects/$(DEPDIR)/audacity-RealtimeEffectManager.Tpo -c -o effects/audacity-RealtimeEffectManager.o `test -f 'effects/RealtimeEffectManager.cpp' || echo '$(srcdir)/'`effects/RealtimeEffectManager.cpp
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) effects/$(DEPDIR)/audacity-RealtimeEffectManager.Tpo effects/$(DEPDIR)/audacity-RealtimeEffectManager.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='effects/RealtimeEffectManager.cpp' object='effects/audacity-RealtimeEffectManager.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o effects/audacity-RealtimeEffectManager.o `test -f 'effects/RealtimeEffectManager.cpp' || echo '$(srcdir)/'`effects/RealtimeEffectManager.cpp
effects/audacity-RealtimeEffectManager.obj: effects/RealtimeEffectManager.cpp
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT effects/audacity-RealtimeEffectManager.obj -MD -MP -MF effects/$(DEPDIR)/audacity-RealtimeEffectManager.Tpo -c -o effects/audacity-RealtimeEffectManager.obj `if test -f 'effects/RealtimeEffectManager.cpp'; then $(CYGPATH_W) 'effects/RealtimeEffectManager.cpp'; else $(CYGPATH_W) '$(srcdir)/effects/RealtimeEffectManager.cpp'; fi`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) effects/$(DEPDIR)/audacity-RealtimeEffectManager.Tpo effects/$(DEPDIR)/audacity-RealtimeEffectManager.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='effects/RealtimeEffectManager.cpp' object='effects/audacity-RealtimeEffectManager.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o effects/audacity-RealtimeEffectManager.obj `if test -f 'effects/RealtimeEffectManager.cpp'; then $(CYGPATH_W) 'effects/RealtimeEffectManager.cpp'; else $(CYGPATH_W) '$(srcdir)/effects/RealtimeEffectManager.cpp'; fi`
effects/audacity-Repair.o: effects/Repair.cpp
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT effects/audacity-Repair.o -MD -MP -MF effects/$(DEPDIR)/audacity-Repair.Tpo -c -o effects/audacity-Repair.o `test -f 'effects/Repair.cpp' || echo '$(srcdir)/'`effects/Repair.cpp
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) effects/$(DEPDIR)/audacity-Repair.Tpo effects/$(DEPDIR)/audacity-Repair.Po

View File

@ -47,7 +47,7 @@ greater use in future.
#include "audacity/ConfigInterface.h"
#include "EffectManager.h"
#include "RealtimeEffectManager.h"
#include "../AudioIO.h"
#include "../CommonCommandFlags.h"
#include "../LabelTrack.h"
@ -68,7 +68,6 @@ greater use in future.
#include "../widgets/ProgressDialog.h"
#include "../ondemand/ODManager.h"
#include "TimeWarper.h"
#include "nyquist/Nyquist.h"
#include "../widgets/HelpSystem.h"
#include "../widgets/LinkingHtmlWindow.h"
#include "../widgets/NumericTextCtrl.h"
@ -147,10 +146,6 @@ Effect::Effect()
mNumGroups = 0;
mProgress = NULL;
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount = 1; // Effects are initially suspended
mRealtimeSuspendLock.Leave();
mUIParent = NULL;
mUIDialog = NULL;
@ -374,6 +369,16 @@ size_t Effect::SetBlockSize(size_t maxBlockSize)
return mBlockSize;
}
size_t Effect::GetBlockSize() const
{
if (mClient)
{
return mClient->GetBlockSize();
}
return mBlockSize;
}
sampleCount Effect::GetLatency()
{
if (mClient)
@ -470,21 +475,7 @@ bool Effect::RealtimeFinalize()
bool Effect::RealtimeSuspend()
{
if (mClient)
{
if (mClient->RealtimeSuspend())
{
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount++;
mRealtimeSuspendLock.Leave();
return true;
}
return false;
}
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount++;
mRealtimeSuspendLock.Leave();
return mClient->RealtimeSuspend();
return true;
}
@ -492,21 +483,7 @@ bool Effect::RealtimeSuspend()
bool Effect::RealtimeResume()
{
if (mClient)
{
if (mClient->RealtimeResume())
{
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount--;
mRealtimeSuspendLock.Leave();
return true;
}
return false;
}
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount--;
mRealtimeSuspendLock.Leave();
return mClient->RealtimeResume();
return true;
}
@ -2294,194 +2271,6 @@ double Effect::CalcPreviewInputLength(double previewLength)
return previewLength;
}
// RealtimeAddProcessor and RealtimeProcess use the same method of
// determining the current processor index, so updates to one should
// be reflected in the other.
bool Effect::RealtimeAddProcessor(int group, unsigned chans, float rate)
{
auto ichans = chans;
auto ochans = chans;
auto gchans = chans;
// Reset processor index
if (group == 0)
{
mCurrentProcessor = 0;
mGroupProcessor.clear();
}
// Remember the processor starting index
mGroupProcessor.push_back(mCurrentProcessor);
// Call the client until we run out of input or output channels
while (ichans > 0 && ochans > 0)
{
// If we don't have enough input channels to accomodate the client's
// requirements, then we replicate the input channels until the
// client's needs are met.
if (ichans < mNumAudioIn)
{
// All input channels have been consumed
ichans = 0;
}
// Otherwise fullfil the client's needs with as many input channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ichans >= mNumAudioIn)
{
gchans = mNumAudioIn;
ichans -= gchans;
}
// If we don't have enough output channels to accomodate the client's
// requirements, then we provide all of the output channels and fulfill
// the client's needs with dummy buffers. These will just get tossed.
if (ochans < mNumAudioOut)
{
// All output channels have been consumed
ochans = 0;
}
// Otherwise fullfil the client's needs with as many output channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ochans >= mNumAudioOut)
{
ochans -= mNumAudioOut;
}
// Add a NEW processor
RealtimeAddProcessor(gchans, rate);
// Bump to next processor
mCurrentProcessor++;
}
return true;
}
// RealtimeAddProcessor and RealtimeProcess use the same method of
// determining the current processor group, so updates to one should
// be reflected in the other.
size_t Effect::RealtimeProcess(int group,
unsigned chans,
float **inbuf,
float **outbuf,
size_t numSamples)
{
//
// The caller passes the number of channels to process and specifies
// the number of input and output buffers. There will always be the
// same number of output buffers as there are input buffers.
//
// Effects always require a certain number of input and output buffers,
// so if the number of channels we're curently processing are different
// than what the effect expects, then we use a few methods of satisfying
// the effects requirements.
float **clientIn = (float **) alloca(mNumAudioIn * sizeof(float *));
float **clientOut = (float **) alloca(mNumAudioOut * sizeof(float *));
float *dummybuf = (float *) alloca(numSamples * sizeof(float));
decltype(numSamples) len = 0;
auto ichans = chans;
auto ochans = chans;
auto gchans = chans;
unsigned indx = 0;
unsigned ondx = 0;
int processor = mGroupProcessor[group];
// Call the client until we run out of input or output channels
while (ichans > 0 && ochans > 0)
{
// If we don't have enough input channels to accomodate the client's
// requirements, then we replicate the input channels until the
// client's needs are met.
if (ichans < mNumAudioIn)
{
for (size_t i = 0; i < mNumAudioIn; i++)
{
if (indx == ichans)
{
indx = 0;
}
clientIn[i] = inbuf[indx++];
}
// All input channels have been consumed
ichans = 0;
}
// Otherwise fullfil the client's needs with as many input channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ichans >= mNumAudioIn)
{
gchans = 0;
for (size_t i = 0; i < mNumAudioIn; i++, ichans--, gchans++)
{
clientIn[i] = inbuf[indx++];
}
}
// If we don't have enough output channels to accomodate the client's
// requirements, then we provide all of the output channels and fulfill
// the client's needs with dummy buffers. These will just get tossed.
if (ochans < mNumAudioOut)
{
for (size_t i = 0; i < mNumAudioOut; i++)
{
if (i < ochans)
{
clientOut[i] = outbuf[i];
}
else
{
clientOut[i] = dummybuf;
}
}
// All output channels have been consumed
ochans = 0;
}
// Otherwise fullfil the client's needs with as many output channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ochans >= mNumAudioOut)
{
for (size_t i = 0; i < mNumAudioOut; i++, ochans--)
{
clientOut[i] = outbuf[ondx++];
}
}
// Finally call the plugin to process the block
len = 0;
for (decltype(numSamples) block = 0; block < numSamples; block += mBlockSize)
{
auto cnt = std::min(numSamples - block, mBlockSize);
len += RealtimeProcess(processor, clientIn, clientOut, cnt);
for (size_t i = 0 ; i < mNumAudioIn; i++)
{
clientIn[i] += cnt;
}
for (size_t i = 0 ; i < mNumAudioOut; i++)
{
clientOut[i] += cnt;
}
}
// Bump to next processor
processor++;
}
return len;
}
bool Effect::IsRealtimeActive()
{
return mRealtimeSuspendCount == 0;
}
bool Effect::IsHidden()
{
return false;
@ -3906,7 +3695,7 @@ void EffectUIHost::InitializeRealtime()
{
if (mSupportsRealtime && !mInitialized)
{
EffectManager::Get().RealtimeAddEffect(mEffect);
RealtimeEffectManager::Get().RealtimeAddEffect(mEffect);
wxTheApp->Bind(EVT_AUDIOIO_PLAYBACK,
&EffectUIHost::OnPlayback,
@ -3924,7 +3713,7 @@ void EffectUIHost::CleanupRealtime()
{
if (mSupportsRealtime && mInitialized)
{
EffectManager::Get().RealtimeRemoveEffect(mEffect);
RealtimeEffectManager::Get().RealtimeRemoveEffect(mEffect);
mInitialized = false;
}

View File

@ -51,6 +51,15 @@ class TrackList;
class TrackFactory;
class WaveTrack;
/* i18n-hint: "Nyquist" is an embedded interpreted programming language in
Audacity, named in honor of the Swedish-American Harry Nyquist (or Nyqvist).
In the translations of this and other strings, you may transliterate the
name into another alphabet. */
#define NYQUISTEFFECTS_FAMILY ( EffectFamilySymbol{ XO("Nyquist") } )
#define NYQUIST_PROMPT_ID wxT("Nyquist Prompt")
#define NYQUIST_WORKER_ID wxT("Nyquist Worker")
// TODO: Apr-06-2015
// TODO: Much more cleanup of old methods and variables is needed, but
// TODO: can't be done until after all effects are using the NEW API.
@ -112,6 +121,7 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
void SetSampleRate(double rate) override;
size_t SetBlockSize(size_t maxBlockSize) override;
size_t GetBlockSize() const override;
bool IsReady() override;
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override;
@ -252,15 +262,6 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
bool Delegate( Effect &delegate, wxWindow *parent, bool shouldPrompt);
// Realtime Effect Processing
/* not virtual */ bool RealtimeAddProcessor(int group, unsigned chans, float rate);
/* not virtual */ size_t RealtimeProcess(int group,
unsigned chans,
float **inbuf,
float **outbuf,
size_t numSamples);
/* not virtual */ bool IsRealtimeActive();
virtual bool IsHidden();
// Nonvirtual
@ -534,12 +535,6 @@ private:
size_t mBlockSize;
unsigned mNumChannels;
std::vector<int> mGroupProcessor;
int mCurrentProcessor;
wxCriticalSection mRealtimeSuspendLock;
int mRealtimeSuspendCount;
const static wxString kUserPresetIdent;
const static wxString kFactoryPresetIdent;
const static wxString kCurrentSettingsIdent;
@ -551,7 +546,6 @@ private:
friend class EffectPresetsDialog;
};
// FIXME:
// FIXME: Remove this once all effects are using the NEW dialog
// FIXME:

View File

@ -23,6 +23,8 @@ effects.
#include "../Experimental.h"
#include "Effect.h"
#include <algorithm>
#include <wx/stopwatch.h>
#include <wx/tokenzr.h>
@ -52,13 +54,7 @@ EffectManager & EffectManager::Get()
EffectManager::EffectManager()
{
mRealtimeLock.Enter();
mRealtimeActive = false;
mRealtimeSuspended = true;
mRealtimeLatency = 0;
mRealtimeLock.Leave();
mSkipStateFlag = false;
#if defined(EXPERIMENTAL_EFFECTS_RACK)
mRack = NULL;
#endif
@ -511,337 +507,8 @@ void EffectManager::ShowRack()
{
GetRack()->Show(!GetRack()->IsShown());
}
void EffectManager::RealtimeSetEffects(const EffectArray & effects)
{
// Block RealtimeProcess()
RealtimeSuspend();
// Tell any effects no longer in the chain to clean up
for (auto e: mRealtimeEffects)
{
// Scan the NEW chain for the effect
for (auto e1: effects)
{
// Found it so we're done
if (e == e1)
{
e = NULL;
break;
}
}
// Must not have been in the NEW chain, so tell it to cleanup
if (e && mRealtimeActive)
{
e->RealtimeFinalize();
}
}
// Tell any NEW effects to get ready
for (auto e : effects)
{
// Scan the old chain for the effect
for (auto e1 : mRealtimeEffects)
{
// Found it so tell effect to get ready
if (e == e1)
{
e = NULL;
break;
}
}
// Must not have been in the old chain, so tell it to initialize
if (e && mRealtimeActive)
{
e->RealtimeInitialize();
}
}
// Get rid of the old chain
// And install the NEW one
mRealtimeEffects = effects;
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
#endif
bool EffectManager::RealtimeIsActive()
{
return mRealtimeEffects.size() != 0;
}
bool EffectManager::RealtimeIsSuspended()
{
return mRealtimeSuspended;
}
void EffectManager::RealtimeAddEffect(Effect *effect)
{
// Block RealtimeProcess()
RealtimeSuspend();
// Initialize effect if realtime is already active
if (mRealtimeActive)
{
// Initialize realtime processing
effect->RealtimeInitialize();
// Add the required processors
for (size_t i = 0, cnt = mRealtimeChans.size(); i < cnt; i++)
{
effect->RealtimeAddProcessor(i, mRealtimeChans[i], mRealtimeRates[i]);
}
}
// Add to list of active effects
mRealtimeEffects.push_back(effect);
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
void EffectManager::RealtimeRemoveEffect(Effect *effect)
{
// Block RealtimeProcess()
RealtimeSuspend();
if (mRealtimeActive)
{
// Cleanup realtime processing
effect->RealtimeFinalize();
}
// Remove from list of active effects
auto end = mRealtimeEffects.end();
auto found = std::find(mRealtimeEffects.begin(), end, effect);
if (found != end)
mRealtimeEffects.erase(found);
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
void EffectManager::RealtimeInitialize(double rate)
{
// The audio thread should not be running yet, but protect anyway
RealtimeSuspend();
// (Re)Set processor parameters
mRealtimeChans.clear();
mRealtimeRates.clear();
// RealtimeAdd/RemoveEffect() needs to know when we're active so it can
// initialize newly added effects
mRealtimeActive = true;
// Tell each effect to get ready for action
for (auto e : mRealtimeEffects) {
e->SetSampleRate(rate);
e->RealtimeInitialize();
}
// Get things moving
RealtimeResume();
}
void EffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate)
{
for (auto e : mRealtimeEffects)
e->RealtimeAddProcessor(group, chans, rate);
mRealtimeChans.push_back(chans);
mRealtimeRates.push_back(rate);
}
void EffectManager::RealtimeFinalize()
{
// Make sure nothing is going on
RealtimeSuspend();
// It is now safe to clean up
mRealtimeLatency = 0;
// Tell each effect to clean up as well
for (auto e : mRealtimeEffects)
e->RealtimeFinalize();
// Reset processor parameters
mRealtimeChans.clear();
mRealtimeRates.clear();
// No longer active
mRealtimeActive = false;
}
void EffectManager::RealtimeSuspend()
{
mRealtimeLock.Enter();
// Already suspended...bail
if (mRealtimeSuspended)
{
mRealtimeLock.Leave();
return;
}
// Show that we aren't going to be doing anything
mRealtimeSuspended = true;
// And make sure the effects don't either
for (auto e : mRealtimeEffects)
e->RealtimeSuspend();
mRealtimeLock.Leave();
}
void EffectManager::RealtimeResume()
{
mRealtimeLock.Enter();
// Already running...bail
if (!mRealtimeSuspended)
{
mRealtimeLock.Leave();
return;
}
// Tell the effects to get ready for more action
for (auto e : mRealtimeEffects)
e->RealtimeResume();
// And we should too
mRealtimeSuspended = false;
mRealtimeLock.Leave();
}
//
// This will be called in a different thread than the main GUI thread.
//
void EffectManager::RealtimeProcessStart()
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended.
if (!mRealtimeSuspended)
{
for (auto e : mRealtimeEffects)
{
if (e->IsRealtimeActive())
e->RealtimeProcessStart();
}
}
mRealtimeLock.Leave();
}
//
// This will be called in a different thread than the main GUI thread.
//
size_t EffectManager::RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended, so allow the samples to pass as-is.
if (mRealtimeSuspended || mRealtimeEffects.empty())
{
mRealtimeLock.Leave();
return numSamples;
}
// Remember when we started so we can calculate the amount of latency we
// are introducing
wxMilliClock_t start = wxGetUTCTimeMillis();
// Allocate the in/out buffer arrays
float **ibuf = (float **) alloca(chans * sizeof(float *));
float **obuf = (float **) alloca(chans * sizeof(float *));
// And populate the input with the buffers we've been given while allocating
// NEW output buffers
for (unsigned int i = 0; i < chans; i++)
{
ibuf[i] = buffers[i];
obuf[i] = (float *) alloca(numSamples * sizeof(float));
}
// Now call each effect in the chain while swapping buffer pointers to feed the
// output of one effect as the input to the next effect
size_t called = 0;
for (auto e : mRealtimeEffects)
{
if (e->IsRealtimeActive())
{
e->RealtimeProcess(group, chans, ibuf, obuf, numSamples);
called++;
}
for (unsigned int j = 0; j < chans; j++)
{
float *temp;
temp = ibuf[j];
ibuf[j] = obuf[j];
obuf[j] = temp;
}
}
// Once we're done, we might wind up with the last effect storing its results
// in the temporary buffers. If that's the case, we need to copy it over to
// the caller's buffers. This happens when the number of effects proccessed
// is odd.
if (called & 1)
{
for (unsigned int i = 0; i < chans; i++)
{
memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
}
}
// Remember the latency
mRealtimeLatency = (int) (wxGetUTCTimeMillis() - start).GetValue();
mRealtimeLock.Leave();
//
// This is wrong...needs to handle tails
//
return numSamples;
}
//
// This will be called in a different thread than the main GUI thread.
//
void EffectManager::RealtimeProcessEnd()
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended.
if (!mRealtimeSuspended)
{
for (auto e : mRealtimeEffects)
{
if (e->IsRealtimeActive())
e->RealtimeProcessEnd();
}
}
mRealtimeLock.Leave();
}
int EffectManager::GetRealtimeLatency()
{
return mRealtimeLatency;
}
Effect *EffectManager::GetEffect(const PluginID & ID)
{
// Must have a "valid" ID

View File

@ -14,18 +14,23 @@
#include "../Experimental.h"
#include <memory>
#include <vector>
#include "audacity/EffectInterface.h"
#include "Effect.h"
#include <unordered_map>
#include "audacity/Types.h"
class AudacityCommand;
class CommandContext;
class CommandMessageTarget;
class ComponentInterfaceSymbol;
class Effect;
class TrackFactory;
class TrackList;
class SelectedRegion;
class wxString;
typedef wxString PluginID;
using EffectArray = std::vector <Effect*> ;
using EffectMap = std::unordered_map<wxString, Effect *>;
using AudacityCommandMap = std::unordered_map<wxString, AudacityCommand *>;
using EffectOwnerMap = std::unordered_map< wxString, std::shared_ptr<Effect> >;
@ -122,22 +127,6 @@ public:
void SetSkipStateFlag(bool flag);
bool GetSkipStateFlag();
// Realtime effect processing
bool RealtimeIsActive();
bool RealtimeIsSuspended();
void RealtimeAddEffect(Effect *effect);
void RealtimeRemoveEffect(Effect *effect);
void RealtimeSetEffects(const EffectArray & mActive);
void RealtimeInitialize(double rate);
void RealtimeAddProcessor(int group, unsigned chans, float rate);
void RealtimeFinalize();
void RealtimeSuspend();
void RealtimeResume();
void RealtimeProcessStart();
size_t RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples);
void RealtimeProcessEnd();
int GetRealtimeLatency();
#if defined(EXPERIMENTAL_EFFECTS_RACK)
void ShowRack();
#endif
@ -160,14 +149,6 @@ private:
int mNumEffects;
wxCriticalSection mRealtimeLock;
EffectArray mRealtimeEffects;
int mRealtimeLatency;
bool mRealtimeSuspended;
bool mRealtimeActive;
std::vector<unsigned> mRealtimeChans;
std::vector<double> mRealtimeRates;
// Set true if we want to skip pushing state
// after processing at effect run time.
bool mSkipStateFlag;
@ -180,5 +161,4 @@ private:
};
#endif

View File

@ -16,6 +16,10 @@
#include "../Experimental.h"
#include "Effect.h"
#include "EffectManager.h"
#include "RealtimeEffectManager.h"
#if defined(EXPERIMENTAL_EFFECTS_RACK)
#include "../UndoManager.h"
@ -39,6 +43,7 @@
#include "../Prefs.h"
#include "../Project.h"
#include "../ProjectHistory.h"
#include "../widgets/wxPanelWrapper.h"
#include "../../images/EffectRack/EffectRack.h"
@ -281,7 +286,7 @@ void EffectRack::OnClose(wxCloseEvent & evt)
void EffectRack::OnTimer(wxTimerEvent & WXUNUSED(evt))
{
int latency = EffectManager::Get().GetRealtimeLatency();
int latency = RealtimeEffectManager::Get().GetRealtimeLatency();
if (latency != mLastLatency)
{
mLatency->SetLabel(wxString::Format(_("Latency: %4d"), latency));
@ -559,7 +564,9 @@ void EffectRack::UpdateActive()
}
}
EffectManager::Get().RealtimeSetEffects(mActive);
RealtimeEffectManager::Get().RealtimeSetEffects(
{ mActive.begin(), mActive.end() }
);
}
#endif

View File

@ -18,16 +18,19 @@
#if defined(EXPERIMENTAL_EFFECTS_RACK)
#include <vector>
#include <wx/defs.h>
#include <wx/frame.h> // to inherit
#include <wx/timer.h> // member variable
#include "EffectManager.h"
class wxFlexGridSizer;
class wxPanel;
class wxStaticText;
class Effect;
using EffectArray = std::vector<Effect*>;
class EffectRack final : public wxFrame
{
public:

View File

@ -0,0 +1,601 @@
/**********************************************************************
Audacity: A Digital Audio Editor
RealtimeEffectManager.cpp
Paul Licameli split from EffectManager.cpp
**********************************************************************/
#include "../Audacity.h"
#include "RealtimeEffectManager.h"
#include "../Experimental.h"
#include "audacity/EffectInterface.h"
#include "MemoryX.h"
#include <atomic>
#include <wx/time.h>
class RealtimeEffectState
{
public:
explicit RealtimeEffectState( EffectClientInterface &effect );
EffectClientInterface &GetEffect() const { return mEffect; }
bool RealtimeSuspend();
bool RealtimeResume();
bool RealtimeAddProcessor(int group, unsigned chans, float rate);
size_t RealtimeProcess(int group,
unsigned chans, float **inbuf, float **outbuf, size_t numSamples);
bool IsRealtimeActive();
private:
EffectClientInterface &mEffect;
std::vector<int> mGroupProcessor;
int mCurrentProcessor;
std::atomic<int> mRealtimeSuspendCount{ 1 }; // Effects are initially suspended
};
RealtimeEffectManager & RealtimeEffectManager::Get()
{
static RealtimeEffectManager rem;
return rem;
}
RealtimeEffectManager::RealtimeEffectManager()
{
mRealtimeLock.Enter();
mRealtimeActive = false;
mRealtimeSuspended = true;
mRealtimeLatency = 0;
mRealtimeLock.Leave();
}
RealtimeEffectManager::~RealtimeEffectManager()
{
}
#if defined(EXPERIMENTAL_EFFECTS_RACK)
void RealtimeEffectManager::RealtimeSetEffects(const EffectArray & effects)
{
// Block RealtimeProcess()
RealtimeSuspend();
decltype( mStates ) newStates;
auto begin = mStates.begin(), end = mStates.end();
for ( auto pEffect : effects ) {
auto found = std::find_if( begin, end,
[=]( const decltype( mStates )::value_type &state ){
return state && &state->GetEffect() == pEffect;
}
);
if ( found == end ) {
// Tell New effect to get ready
pEffect->RealtimeInitialize();
newStates.emplace_back(
std::make_unique< RealtimeEffectState >( *pEffect ) );
}
else {
// Preserve state for effect that remains in the chain
newStates.emplace_back( std::move( *found ) );
}
}
// Remaining states that were not moved need to clean up
for ( auto &state : mStates ) {
if ( state )
state->GetEffect().RealtimeFinalize();
}
// Get rid of the old chain
// And install the NEW one
mStates.swap( newStates );
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
#endif
bool RealtimeEffectManager::RealtimeIsActive()
{
return mStates.size() != 0;
}
bool RealtimeEffectManager::RealtimeIsSuspended()
{
return mRealtimeSuspended;
}
void RealtimeEffectManager::RealtimeAddEffect(EffectClientInterface *effect)
{
// Block RealtimeProcess()
RealtimeSuspend();
// Add to list of active effects
mStates.emplace_back( std::make_unique< RealtimeEffectState >( *effect ) );
auto &state = mStates.back();
// Initialize effect if realtime is already active
if (mRealtimeActive)
{
// Initialize realtime processing
effect->RealtimeInitialize();
// Add the required processors
for (size_t i = 0, cnt = mRealtimeChans.size(); i < cnt; i++)
{
state->RealtimeAddProcessor(i, mRealtimeChans[i], mRealtimeRates[i]);
}
}
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
void RealtimeEffectManager::RealtimeRemoveEffect(EffectClientInterface *effect)
{
// Block RealtimeProcess()
RealtimeSuspend();
if (mRealtimeActive)
{
// Cleanup realtime processing
effect->RealtimeFinalize();
}
// Remove from list of active effects
auto end = mStates.end();
auto found = std::find_if( mStates.begin(), end,
[&](const decltype(mStates)::value_type &state){
return &state->GetEffect() == effect;
}
);
if (found != end)
mStates.erase(found);
// Allow RealtimeProcess() to, well, process
RealtimeResume();
}
void RealtimeEffectManager::RealtimeInitialize(double rate)
{
// The audio thread should not be running yet, but protect anyway
RealtimeSuspend();
// (Re)Set processor parameters
mRealtimeChans.clear();
mRealtimeRates.clear();
// RealtimeAdd/RemoveEffect() needs to know when we're active so it can
// initialize newly added effects
mRealtimeActive = true;
// Tell each effect to get ready for action
for (auto &state : mStates) {
state->GetEffect().SetSampleRate(rate);
state->GetEffect().RealtimeInitialize();
}
// Get things moving
RealtimeResume();
}
void RealtimeEffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate)
{
for (auto &state : mStates)
state->RealtimeAddProcessor(group, chans, rate);
mRealtimeChans.push_back(chans);
mRealtimeRates.push_back(rate);
}
void RealtimeEffectManager::RealtimeFinalize()
{
// Make sure nothing is going on
RealtimeSuspend();
// It is now safe to clean up
mRealtimeLatency = 0;
// Tell each effect to clean up as well
for (auto &state : mStates)
state->GetEffect().RealtimeFinalize();
// Reset processor parameters
mRealtimeChans.clear();
mRealtimeRates.clear();
// No longer active
mRealtimeActive = false;
}
void RealtimeEffectManager::RealtimeSuspend()
{
mRealtimeLock.Enter();
// Already suspended...bail
if (mRealtimeSuspended)
{
mRealtimeLock.Leave();
return;
}
// Show that we aren't going to be doing anything
mRealtimeSuspended = true;
// And make sure the effects don't either
for (auto &state : mStates)
state->RealtimeSuspend();
mRealtimeLock.Leave();
}
void RealtimeEffectManager::RealtimeResume()
{
mRealtimeLock.Enter();
// Already running...bail
if (!mRealtimeSuspended)
{
mRealtimeLock.Leave();
return;
}
// Tell the effects to get ready for more action
for (auto &state : mStates)
state->RealtimeResume();
// And we should too
mRealtimeSuspended = false;
mRealtimeLock.Leave();
}
//
// This will be called in a different thread than the main GUI thread.
//
void RealtimeEffectManager::RealtimeProcessStart()
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended.
if (!mRealtimeSuspended)
{
for (auto &state : mStates)
{
if (state->IsRealtimeActive())
state->GetEffect().RealtimeProcessStart();
}
}
mRealtimeLock.Leave();
}
//
// This will be called in a different thread than the main GUI thread.
//
size_t RealtimeEffectManager::RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended, so allow the samples to pass as-is.
if (mRealtimeSuspended || mStates.empty())
{
mRealtimeLock.Leave();
return numSamples;
}
// Remember when we started so we can calculate the amount of latency we
// are introducing
wxMilliClock_t start = wxGetUTCTimeMillis();
// Allocate the in/out buffer arrays
float **ibuf = (float **) alloca(chans * sizeof(float *));
float **obuf = (float **) alloca(chans * sizeof(float *));
// And populate the input with the buffers we've been given while allocating
// NEW output buffers
for (unsigned int i = 0; i < chans; i++)
{
ibuf[i] = buffers[i];
obuf[i] = (float *) alloca(numSamples * sizeof(float));
}
// Now call each effect in the chain while swapping buffer pointers to feed the
// output of one effect as the input to the next effect
size_t called = 0;
for (auto &state : mStates)
{
if (state->IsRealtimeActive())
{
state->RealtimeProcess(group, chans, ibuf, obuf, numSamples);
called++;
}
for (unsigned int j = 0; j < chans; j++)
{
float *temp;
temp = ibuf[j];
ibuf[j] = obuf[j];
obuf[j] = temp;
}
}
// Once we're done, we might wind up with the last effect storing its results
// in the temporary buffers. If that's the case, we need to copy it over to
// the caller's buffers. This happens when the number of effects proccessed
// is odd.
if (called & 1)
{
for (unsigned int i = 0; i < chans; i++)
{
memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
}
}
// Remember the latency
mRealtimeLatency = (int) (wxGetUTCTimeMillis() - start).GetValue();
mRealtimeLock.Leave();
//
// This is wrong...needs to handle tails
//
return numSamples;
}
//
// This will be called in a different thread than the main GUI thread.
//
void RealtimeEffectManager::RealtimeProcessEnd()
{
// Protect ourselves from the main thread
mRealtimeLock.Enter();
// Can be suspended because of the audio stream being paused or because effects
// have been suspended.
if (!mRealtimeSuspended)
{
for (auto &state : mStates)
{
if (state->IsRealtimeActive())
state->GetEffect().RealtimeProcessEnd();
}
}
mRealtimeLock.Leave();
}
int RealtimeEffectManager::GetRealtimeLatency()
{
return mRealtimeLatency;
}
RealtimeEffectState::RealtimeEffectState( EffectClientInterface &effect )
: mEffect{ effect }
{
}
bool RealtimeEffectState::RealtimeSuspend()
{
auto result = mEffect.RealtimeSuspend();
if ( result ) {
mRealtimeSuspendCount++;
}
return result;
}
bool RealtimeEffectState::RealtimeResume()
{
auto result = mEffect.RealtimeResume();
if ( result ) {
mRealtimeSuspendCount--;
}
return result;
}
// RealtimeAddProcessor and RealtimeProcess use the same method of
// determining the current processor index, so updates to one should
// be reflected in the other.
bool RealtimeEffectState::RealtimeAddProcessor(int group, unsigned chans, float rate)
{
auto ichans = chans;
auto ochans = chans;
auto gchans = chans;
// Reset processor index
if (group == 0)
{
mCurrentProcessor = 0;
mGroupProcessor.clear();
}
// Remember the processor starting index
mGroupProcessor.push_back(mCurrentProcessor);
const auto numAudioIn = mEffect.GetAudioInCount();
const auto numAudioOut = mEffect.GetAudioOutCount();
// Call the client until we run out of input or output channels
while (ichans > 0 && ochans > 0)
{
// If we don't have enough input channels to accomodate the client's
// requirements, then we replicate the input channels until the
// client's needs are met.
if (ichans < numAudioIn)
{
// All input channels have been consumed
ichans = 0;
}
// Otherwise fullfil the client's needs with as many input channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ichans >= numAudioIn)
{
gchans = numAudioIn;
ichans -= gchans;
}
// If we don't have enough output channels to accomodate the client's
// requirements, then we provide all of the output channels and fulfill
// the client's needs with dummy buffers. These will just get tossed.
if (ochans < numAudioOut)
{
// All output channels have been consumed
ochans = 0;
}
// Otherwise fullfil the client's needs with as many output channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ochans >= numAudioOut)
{
ochans -= numAudioOut;
}
// Add a NEW processor
mEffect.RealtimeAddProcessor(gchans, rate);
// Bump to next processor
mCurrentProcessor++;
}
return true;
}
// RealtimeAddProcessor and RealtimeProcess use the same method of
// determining the current processor group, so updates to one should
// be reflected in the other.
size_t RealtimeEffectState::RealtimeProcess(int group,
unsigned chans,
float **inbuf,
float **outbuf,
size_t numSamples)
{
//
// The caller passes the number of channels to process and specifies
// the number of input and output buffers. There will always be the
// same number of output buffers as there are input buffers.
//
// Effects always require a certain number of input and output buffers,
// so if the number of channels we're curently processing are different
// than what the effect expects, then we use a few methods of satisfying
// the effects requirements.
const auto numAudioIn = mEffect.GetAudioInCount();
const auto numAudioOut = mEffect.GetAudioOutCount();
float **clientIn = (float **) alloca(numAudioIn * sizeof(float *));
float **clientOut = (float **) alloca(numAudioOut * sizeof(float *));
float *dummybuf = (float *) alloca(numSamples * sizeof(float));
decltype(numSamples) len = 0;
auto ichans = chans;
auto ochans = chans;
auto gchans = chans;
unsigned indx = 0;
unsigned ondx = 0;
int processor = mGroupProcessor[group];
// Call the client until we run out of input or output channels
while (ichans > 0 && ochans > 0)
{
// If we don't have enough input channels to accomodate the client's
// requirements, then we replicate the input channels until the
// client's needs are met.
if (ichans < numAudioIn)
{
for (size_t i = 0; i < numAudioIn; i++)
{
if (indx == ichans)
{
indx = 0;
}
clientIn[i] = inbuf[indx++];
}
// All input channels have been consumed
ichans = 0;
}
// Otherwise fullfil the client's needs with as many input channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ichans >= numAudioIn)
{
gchans = 0;
for (size_t i = 0; i < numAudioIn; i++, ichans--, gchans++)
{
clientIn[i] = inbuf[indx++];
}
}
// If we don't have enough output channels to accomodate the client's
// requirements, then we provide all of the output channels and fulfill
// the client's needs with dummy buffers. These will just get tossed.
if (ochans < numAudioOut)
{
for (size_t i = 0; i < numAudioOut; i++)
{
if (i < ochans)
{
clientOut[i] = outbuf[i];
}
else
{
clientOut[i] = dummybuf;
}
}
// All output channels have been consumed
ochans = 0;
}
// Otherwise fullfil the client's needs with as many output channels as possible.
// After calling the client with this set, we will loop back up to process more
// of the input/output channels.
else if (ochans >= numAudioOut)
{
for (size_t i = 0; i < numAudioOut; i++, ochans--)
{
clientOut[i] = outbuf[ondx++];
}
}
// Finally call the plugin to process the block
len = 0;
const auto blockSize = mEffect.GetBlockSize();
for (decltype(numSamples) block = 0; block < numSamples; block += blockSize)
{
auto cnt = std::min(numSamples - block, blockSize);
len += mEffect.RealtimeProcess(processor, clientIn, clientOut, cnt);
for (size_t i = 0 ; i < numAudioIn; i++)
{
clientIn[i] += cnt;
}
for (size_t i = 0 ; i < numAudioOut; i++)
{
clientOut[i] += cnt;
}
}
// Bump to next processor
processor++;
}
return len;
}
bool RealtimeEffectState::IsRealtimeActive()
{
return mRealtimeSuspendCount == 0;
}

View File

@ -0,0 +1,58 @@
/**********************************************************************
Audacity: A Digital Audio Editor
RealtimeEffectManager.h
Paul Licameli split from EffectManager.h
**********************************************************************/
#ifndef __AUDACITY_REALTIME_EFFECT_MANAGER__
#define __AUDACITY_REALTIME_EFFECT_MANAGER__
#include <memory>
#include <vector>
#include <wx/thread.h>
class EffectClientInterface;
class RealtimeEffectState;
class AUDACITY_DLL_API RealtimeEffectManager final
{
public:
using EffectArray = std::vector <EffectClientInterface*> ;
/** Get the singleton instance of the RealtimeEffectManager. **/
static RealtimeEffectManager & Get();
// Realtime effect processing
bool RealtimeIsActive();
bool RealtimeIsSuspended();
void RealtimeAddEffect(EffectClientInterface *effect);
void RealtimeRemoveEffect(EffectClientInterface *effect);
void RealtimeSetEffects(const EffectArray & mActive);
void RealtimeInitialize(double rate);
void RealtimeAddProcessor(int group, unsigned chans, float rate);
void RealtimeFinalize();
void RealtimeSuspend();
void RealtimeResume();
void RealtimeProcessStart();
size_t RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples);
void RealtimeProcessEnd();
int GetRealtimeLatency();
private:
RealtimeEffectManager();
~RealtimeEffectManager();
wxCriticalSection mRealtimeLock;
std::vector< std::unique_ptr<RealtimeEffectState> > mStates;
int mRealtimeLatency;
bool mRealtimeSuspended;
bool mRealtimeActive;
std::vector<unsigned> mRealtimeChans;
std::vector<double> mRealtimeRates;
};
#endif

View File

@ -1359,6 +1359,11 @@ size_t VSTEffect::SetBlockSize(size_t maxBlockSize)
return mBlockSize;
}
size_t VSTEffect::GetBlockSize() const
{
return mBlockSize;
}
void VSTEffect::SetSampleRate(double rate)
{
mSampleRate = (float) rate;

View File

@ -132,6 +132,7 @@ class VSTEffect final : public wxEvtHandler,
void SetSampleRate(double rate) override;
size_t SetBlockSize(size_t maxBlockSize) override;
size_t GetBlockSize() const override;
bool IsReady() override;
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override;

View File

@ -1192,6 +1192,11 @@ size_t AudioUnitEffect::SetBlockSize(size_t maxBlockSize)
return mBlockSize;
}
size_t AudioUnitEffect::GetBlockSize() const
{
return mBlockSize;
}
sampleCount AudioUnitEffect::GetLatency()
{
// Retrieve the latency (can be updated via an event)

View File

@ -79,6 +79,7 @@ public:
void SetSampleRate(double rate) override;
size_t SetBlockSize(size_t maxBlockSize) override;
size_t GetBlockSize() const override;
sampleCount GetLatency() override;
size_t GetTailSize() override;

View File

@ -889,6 +889,11 @@ size_t LadspaEffect::SetBlockSize(size_t maxBlockSize)
return mBlockSize;
}
size_t LadspaEffect::GetBlockSize() const
{
return mBlockSize;
}
sampleCount LadspaEffect::GetLatency()
{
if (mUseLatency && mLatencyPort >= 0 && !mLatencyDone)

View File

@ -77,6 +77,7 @@ public:
void SetSampleRate(double rate) override;
size_t SetBlockSize(size_t maxBlockSize) override;
size_t GetBlockSize() const override;
sampleCount GetLatency() override;
size_t GetTailSize() override;

View File

@ -728,6 +728,11 @@ size_t LV2Effect::SetBlockSize(size_t maxBlockSize)
return mBlockSize;
}
size_t LV2Effect::GetBlockSize() const
{
return mBlockSize;
}
sampleCount LV2Effect::GetLatency()
{
if (mUseLatency && mLatencyPort >= 0 && !mLatencyDone)

View File

@ -137,6 +137,7 @@ public:
void SetSampleRate(double rate) override;
size_t SetBlockSize(size_t maxBlockSize) override;
size_t GetBlockSize() const override;
sampleCount GetLatency() override;
size_t GetTailSize() override;

View File

@ -30,6 +30,7 @@ Functions that find and load all LV2 plugins on the system.
#include <wx/string.h>
#include "../EffectManager.h"
#include "../../Internat.h"
#include "LV2Effect.h"
#include "lv2/lv2plug.in/ns/ext/event/event.h"

View File

@ -21,14 +21,6 @@ class wxCheckBox;
class wxTextCtrl;
#define NYQUISTEFFECTS_VERSION wxT("1.0.0.0")
/* i18n-hint: "Nyquist" is an embedded interpreted programming language in
Audacity, named in honor of the Swedish-American Harry Nyquist (or Nyqvist).
In the translations of this and other strings, you may transliterate the
name into another alphabet. */
#define NYQUISTEFFECTS_FAMILY ( EffectFamilySymbol{ XO("Nyquist") } )
#define NYQUIST_PROMPT_ID wxT("Nyquist Prompt")
#define NYQUIST_WORKER_ID wxT("Nyquist Worker")
enum NyqControlType
{

View File

@ -23,6 +23,7 @@
#include "../commands/ScreenshotCommand.h"
#include "../effects/Contrast.h"
#include "../effects/EffectManager.h"
#include "../effects/RealtimeEffectManager.h"
// private helper classes and functions
namespace {
@ -927,7 +928,7 @@ MenuTable::BaseItemPtr GenerateMenu( AudacityProject & )
const ReservedCommandFlag
IsRealtimeNotActiveFlag{
[](const AudacityProject &){
return !EffectManager::Get().RealtimeIsActive();
return !RealtimeEffectManager::Get().RealtimeIsActive();
}
}; //lll

View File

@ -28,7 +28,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../WaveTrack.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../effects/EffectManager.h"
#include "../../../../effects/RealtimeEffectManager.h"
#include "../../../../ondemand/ODManager.h"
#include "../../../../prefs/PrefsDialog.h"
#include "../../../../prefs/SpectrumPrefs.h"
@ -616,7 +616,7 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
AudacityProject *const project = ::GetActiveProject();
auto &tracks = TrackList::Get( *project );
bool unsafe = EffectManager::Get().RealtimeIsActive() &&
bool unsafe = RealtimeEffectManager::Get().RealtimeIsActive() &&
ProjectAudioIO::Get( *project ).IsAudioActive();
auto nChannels = TrackList::Channels(pTrack).size();

View File

@ -358,6 +358,7 @@
<ClCompile Include="..\..\..\src\effects\NoiseRemoval.cpp" />
<ClCompile Include="..\..\..\src\effects\Normalize.cpp" />
<ClCompile Include="..\..\..\src\effects\Paulstretch.cpp" />
<ClCompile Include="..\..\..\src\effects\RealtimeEffectManager.cpp" />
<ClCompile Include="..\..\..\src\effects\Repair.cpp" />
<ClCompile Include="..\..\..\src\effects\Repeat.cpp" />
<ClCompile Include="..\..\..\src\effects\Reverb.cpp" />
@ -747,6 +748,7 @@
<ClInclude Include="..\..\..\src\effects\NoiseRemoval.h" />
<ClInclude Include="..\..\..\src\effects\Normalize.h" />
<ClInclude Include="..\..\..\src\effects\Paulstretch.h" />
<ClInclude Include="..\..\..\src\effects\RealtimeEffectManager.h" />
<ClInclude Include="..\..\..\src\effects\Repair.h" />
<ClInclude Include="..\..\..\src\effects\Repeat.h" />
<ClInclude Include="..\..\..\src\effects\Reverb.h" />

View File

@ -461,6 +461,9 @@
<ClCompile Include="..\..\..\src\effects\Paulstretch.cpp">
<Filter>src\effects</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\effects\RealtimeEffectManager.cpp">
<Filter>src\effects</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\effects\Repair.cpp">
<Filter>src\effects</Filter>
</ClCompile>
@ -1588,6 +1591,9 @@
<ClInclude Include="..\..\..\src\effects\Paulstretch.h">
<Filter>src\effects</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\effects\RealtimeEffectManager.h">
<Filter>src\effects</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\effects\Repair.h">
<Filter>src\effects</Filter>
</ClInclude>