From dee27e052f332ae3711bc7a9d5ad2ab40de2e0b9 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 08:09:58 -0400 Subject: [PATCH 1/8] Free Nyquist.cpp from cycles... ... Very easily, just by demoting two macros from Nyquist.h to Effect.h. The graph improvement is worth this bit of preprocessor namespace pollution. --- src/effects/Effect.cpp | 1 - src/effects/Effect.h | 9 +++++++++ src/effects/nyquist/Nyquist.h | 8 -------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 0e23425f9..2f06c9db1 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -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" diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 5bffba41c..1307706e8 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -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. diff --git a/src/effects/nyquist/Nyquist.h b/src/effects/nyquist/Nyquist.h index cf54f3abd..b68c91941 100644 --- a/src/effects/nyquist/Nyquist.h +++ b/src/effects/nyquist/Nyquist.h @@ -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 { From 9935b035c58fbceed8153ce514b69d19885c5fe9 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 07:40:16 -0400 Subject: [PATCH 2/8] Remove Effect.h from EffectManager.h and EffectRack.h --- src/BatchCommands.cpp | 1 + src/effects/EffectManager.cpp | 2 ++ src/effects/EffectManager.h | 13 ++++++++++--- src/effects/EffectRack.cpp | 4 ++++ src/effects/EffectRack.h | 7 +++++-- src/effects/lv2/LoadLV2.cpp | 1 + 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index e748cf268..e5a8ac7fb 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -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" diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index 817b0ae60..881ab814f 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -23,6 +23,8 @@ effects. #include "../Experimental.h" +#include "Effect.h" + #include #include #include diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index 8c99a0f6d..eea9e3e75 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -14,16 +14,23 @@ #include "../Experimental.h" +#include #include -#include "audacity/EffectInterface.h" -#include "Effect.h" - #include +#include +#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 ; using EffectMap = std::unordered_map; diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 7be9a7599..16cbfe207 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -16,6 +16,9 @@ #include "../Experimental.h" +#include "Effect.h" +#include "EffectManager.h" + #if defined(EXPERIMENTAL_EFFECTS_RACK) #include "../UndoManager.h" @@ -39,6 +42,7 @@ #include "../Prefs.h" #include "../Project.h" #include "../ProjectHistory.h" +#include "../widgets/wxPanelWrapper.h" #include "../../images/EffectRack/EffectRack.h" diff --git a/src/effects/EffectRack.h b/src/effects/EffectRack.h index fb47a3e4c..5fcc2c987 100644 --- a/src/effects/EffectRack.h +++ b/src/effects/EffectRack.h @@ -18,16 +18,19 @@ #if defined(EXPERIMENTAL_EFFECTS_RACK) +#include + #include #include // to inherit #include // member variable -#include "EffectManager.h" - class wxFlexGridSizer; class wxPanel; class wxStaticText; +class Effect; +using EffectArray = std::vector; + class EffectRack final : public wxFrame { public: diff --git a/src/effects/lv2/LoadLV2.cpp b/src/effects/lv2/LoadLV2.cpp index 6dcc4be5b..6fcaef762 100644 --- a/src/effects/lv2/LoadLV2.cpp +++ b/src/effects/lv2/LoadLV2.cpp @@ -30,6 +30,7 @@ Functions that find and load all LV2 plugins on the system. #include #include "../EffectManager.h" +#include "../../Internat.h" #include "LV2Effect.h" #include "lv2/lv2plug.in/ns/ext/event/event.h" From 04a3ed9d044709f32e312533a5a84ad69da85bfe Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 07:10:11 -0400 Subject: [PATCH 3/8] Separate class RealtimeEffectManager --- src/AudioIO.cpp | 12 ++-- src/effects/Effect.cpp | 4 +- src/effects/EffectManager.cpp | 45 +++++++++----- src/effects/EffectManager.h | 59 +++++++++++-------- src/effects/EffectRack.cpp | 4 +- src/menus/PluginMenus.cpp | 2 +- .../wavetrack/ui/WaveTrackControls.cpp | 2 +- 7 files changed, 76 insertions(+), 52 deletions(-) diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 444503f41..759c6cd8c 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -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; diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 2f06c9db1..09dda6e52 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -3905,7 +3905,7 @@ void EffectUIHost::InitializeRealtime() { if (mSupportsRealtime && !mInitialized) { - EffectManager::Get().RealtimeAddEffect(mEffect); + RealtimeEffectManager::Get().RealtimeAddEffect(mEffect); wxTheApp->Bind(EVT_AUDIOIO_PLAYBACK, &EffectUIHost::OnPlayback, @@ -3923,7 +3923,7 @@ void EffectUIHost::CleanupRealtime() { if (mSupportsRealtime && mInitialized) { - EffectManager::Get().RealtimeRemoveEffect(mEffect); + RealtimeEffectManager::Get().RealtimeRemoveEffect(mEffect); mInitialized = false; } diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index 881ab814f..5c597c885 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -52,15 +52,28 @@ EffectManager & EffectManager::Get() return em; } -EffectManager::EffectManager() +RealtimeEffectManager & RealtimeEffectManager::Get() +{ + static RealtimeEffectManager rem; + return rem; +} + +RealtimeEffectManager::RealtimeEffectManager() { mRealtimeLock.Enter(); mRealtimeActive = false; mRealtimeSuspended = true; mRealtimeLatency = 0; mRealtimeLock.Leave(); - mSkipStateFlag = false; +} +RealtimeEffectManager::~RealtimeEffectManager() +{ +} + +EffectManager::EffectManager() +{ + mSkipStateFlag = false; #if defined(EXPERIMENTAL_EFFECTS_RACK) mRack = NULL; #endif @@ -514,7 +527,7 @@ void EffectManager::ShowRack() GetRack()->Show(!GetRack()->IsShown()); } -void EffectManager::RealtimeSetEffects(const EffectArray & effects) +void RealtimeEffectManager::RealtimeSetEffects(const EffectArray & effects) { // Block RealtimeProcess() RealtimeSuspend(); @@ -570,17 +583,17 @@ void EffectManager::RealtimeSetEffects(const EffectArray & effects) } #endif -bool EffectManager::RealtimeIsActive() +bool RealtimeEffectManager::RealtimeIsActive() { return mRealtimeEffects.size() != 0; } -bool EffectManager::RealtimeIsSuspended() +bool RealtimeEffectManager::RealtimeIsSuspended() { return mRealtimeSuspended; } -void EffectManager::RealtimeAddEffect(Effect *effect) +void RealtimeEffectManager::RealtimeAddEffect(Effect *effect) { // Block RealtimeProcess() RealtimeSuspend(); @@ -605,7 +618,7 @@ void EffectManager::RealtimeAddEffect(Effect *effect) RealtimeResume(); } -void EffectManager::RealtimeRemoveEffect(Effect *effect) +void RealtimeEffectManager::RealtimeRemoveEffect(Effect *effect) { // Block RealtimeProcess() RealtimeSuspend(); @@ -626,7 +639,7 @@ void EffectManager::RealtimeRemoveEffect(Effect *effect) RealtimeResume(); } -void EffectManager::RealtimeInitialize(double rate) +void RealtimeEffectManager::RealtimeInitialize(double rate) { // The audio thread should not be running yet, but protect anyway RealtimeSuspend(); @@ -649,7 +662,7 @@ void EffectManager::RealtimeInitialize(double rate) RealtimeResume(); } -void EffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate) +void RealtimeEffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate) { for (auto e : mRealtimeEffects) e->RealtimeAddProcessor(group, chans, rate); @@ -658,7 +671,7 @@ void EffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate) mRealtimeRates.push_back(rate); } -void EffectManager::RealtimeFinalize() +void RealtimeEffectManager::RealtimeFinalize() { // Make sure nothing is going on RealtimeSuspend(); @@ -678,7 +691,7 @@ void EffectManager::RealtimeFinalize() mRealtimeActive = false; } -void EffectManager::RealtimeSuspend() +void RealtimeEffectManager::RealtimeSuspend() { mRealtimeLock.Enter(); @@ -699,7 +712,7 @@ void EffectManager::RealtimeSuspend() mRealtimeLock.Leave(); } -void EffectManager::RealtimeResume() +void RealtimeEffectManager::RealtimeResume() { mRealtimeLock.Enter(); @@ -723,7 +736,7 @@ void EffectManager::RealtimeResume() // // This will be called in a different thread than the main GUI thread. // -void EffectManager::RealtimeProcessStart() +void RealtimeEffectManager::RealtimeProcessStart() { // Protect ourselves from the main thread mRealtimeLock.Enter(); @@ -745,7 +758,7 @@ void EffectManager::RealtimeProcessStart() // // 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) +size_t RealtimeEffectManager::RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples) { // Protect ourselves from the main thread mRealtimeLock.Enter(); @@ -820,7 +833,7 @@ size_t EffectManager::RealtimeProcess(int group, unsigned chans, float **buffers // // This will be called in a different thread than the main GUI thread. // -void EffectManager::RealtimeProcessEnd() +void RealtimeEffectManager::RealtimeProcessEnd() { // Protect ourselves from the main thread mRealtimeLock.Enter(); @@ -839,7 +852,7 @@ void EffectManager::RealtimeProcessEnd() mRealtimeLock.Leave(); } -int EffectManager::GetRealtimeLatency() +int RealtimeEffectManager::GetRealtimeLatency() { return mRealtimeLatency; } diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index eea9e3e75..e9c54902b 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -129,22 +129,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 @@ -167,14 +151,6 @@ private: int mNumEffects; - wxCriticalSection mRealtimeLock; - EffectArray mRealtimeEffects; - int mRealtimeLatency; - bool mRealtimeSuspended; - bool mRealtimeActive; - std::vector mRealtimeChans; - std::vector mRealtimeRates; - // Set true if we want to skip pushing state // after processing at effect run time. bool mSkipStateFlag; @@ -187,5 +163,40 @@ private: }; +class AUDACITY_DLL_API RealtimeEffectManager final +{ +public: + + /** Get the singleton instance of the RealtimeEffectManager. **/ + static RealtimeEffectManager & Get(); + + // 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(); + +private: + RealtimeEffectManager(); + ~RealtimeEffectManager(); + + wxCriticalSection mRealtimeLock; + EffectArray mRealtimeEffects; + int mRealtimeLatency; + bool mRealtimeSuspended; + bool mRealtimeActive; + std::vector mRealtimeChans; + std::vector mRealtimeRates; +}; #endif diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 16cbfe207..7a85213cc 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -285,7 +285,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)); @@ -563,7 +563,7 @@ void EffectRack::UpdateActive() } } - EffectManager::Get().RealtimeSetEffects(mActive); + RealtimeEffectManager::Get().RealtimeSetEffects(mActive); } #endif diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index c10bf4098..e34fc1486 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -927,7 +927,7 @@ MenuTable::BaseItemPtr GenerateMenu( AudacityProject & ) const ReservedCommandFlag IsRealtimeNotActiveFlag{ [](const AudacityProject &){ - return !EffectManager::Get().RealtimeIsActive(); + return !RealtimeEffectManager::Get().RealtimeIsActive(); } }; //lll diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index f52d805bc..8fe3be65d 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -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(); From ab5a98003a797cf42ee74086cb2a8d296a526be0 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 22:48:32 -0400 Subject: [PATCH 4/8] Free EffectManager and EffectRack from the big s.c.c. ... ... though in a small cycle with each other, by moving RealtimeEffectManager to new files, which remain in the big component. Net loss of 1, the big component now has 27 files --- locale/POTFILES.in | 2 + mac/Audacity.xcodeproj/project.pbxproj | 10 +- src/AudioIO.cpp | 2 +- src/Makefile.am | 2 + src/Makefile.in | 90 +++-- src/effects/Effect.cpp | 2 +- src/effects/EffectManager.cpp | 348 ----------------- src/effects/EffectManager.h | 38 -- src/effects/EffectRack.cpp | 1 + src/effects/RealtimeEffectManager.cpp | 368 ++++++++++++++++++ src/effects/RealtimeEffectManager.h | 57 +++ src/menus/PluginMenus.cpp | 1 + .../wavetrack/ui/WaveTrackControls.cpp | 2 +- win/Projects/Audacity/Audacity.vcxproj | 2 + .../Audacity/Audacity.vcxproj.filters | 6 + 15 files changed, 506 insertions(+), 425 deletions(-) create mode 100644 src/effects/RealtimeEffectManager.cpp create mode 100644 src/effects/RealtimeEffectManager.h diff --git a/locale/POTFILES.in b/locale/POTFILES.in index 23d2ca868..0579c5f99 100644 --- a/locale/POTFILES.in +++ b/locale/POTFILES.in @@ -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 diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index e56071925..4d781fc9b 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -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 = ""; }; 5E2A19921EED688500217B58 /* SelectionState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectionState.cpp; sourceTree = ""; }; 5E2A19931EED688500217B58 /* SelectionState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SelectionState.h; sourceTree = ""; }; + 5E2B3E6022BF9621005042E1 /* RealtimeEffectManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RealtimeEffectManager.cpp; sourceTree = ""; }; + 5E2B3E6122BF9621005042E1 /* RealtimeEffectManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealtimeEffectManager.h; sourceTree = ""; }; 5E2BF3832193A2A500995694 /* TrackView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackView.cpp; sourceTree = ""; }; 5E2BF3842193A2A500995694 /* TrackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackView.h; sourceTree = ""; }; 5E2BF3862193A2BD00995694 /* TimeTrackView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimeTrackView.cpp; sourceTree = ""; }; @@ -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 */, diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 759c6cd8c..4e61b5545 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -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" diff --git a/src/Makefile.am b/src/Makefile.am index 161bd996e..8087078e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/Makefile.in b/src/Makefile.in index ac29672e0..ff2ca0b06 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -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 diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 09dda6e52..425c89a2d 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -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" diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index 5c597c885..c473389d9 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -52,25 +52,6 @@ EffectManager & EffectManager::Get() return em; } -RealtimeEffectManager & RealtimeEffectManager::Get() -{ - static RealtimeEffectManager rem; - return rem; -} - -RealtimeEffectManager::RealtimeEffectManager() -{ - mRealtimeLock.Enter(); - mRealtimeActive = false; - mRealtimeSuspended = true; - mRealtimeLatency = 0; - mRealtimeLock.Leave(); -} - -RealtimeEffectManager::~RealtimeEffectManager() -{ -} - EffectManager::EffectManager() { mSkipStateFlag = false; @@ -526,337 +507,8 @@ void EffectManager::ShowRack() { GetRack()->Show(!GetRack()->IsShown()); } - -void RealtimeEffectManager::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 RealtimeEffectManager::RealtimeIsActive() -{ - return mRealtimeEffects.size() != 0; -} - -bool RealtimeEffectManager::RealtimeIsSuspended() -{ - return mRealtimeSuspended; -} - -void RealtimeEffectManager::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 RealtimeEffectManager::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 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 e : mRealtimeEffects) { - e->SetSampleRate(rate); - e->RealtimeInitialize(); - } - - // Get things moving - RealtimeResume(); -} - -void RealtimeEffectManager::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 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 e : mRealtimeEffects) - e->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 e : mRealtimeEffects) - e->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 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 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 e : mRealtimeEffects) - { - if (e->IsRealtimeActive()) - e->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 || 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 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 e : mRealtimeEffects) - { - if (e->IsRealtimeActive()) - e->RealtimeProcessEnd(); - } - } - - mRealtimeLock.Leave(); -} - -int RealtimeEffectManager::GetRealtimeLatency() -{ - return mRealtimeLatency; -} - Effect *EffectManager::GetEffect(const PluginID & ID) { // Must have a "valid" ID diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index e9c54902b..3280290d9 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -18,7 +18,6 @@ #include #include -#include #include "audacity/Types.h" class AudacityCommand; @@ -32,7 +31,6 @@ class SelectedRegion; class wxString; typedef wxString PluginID; -using EffectArray = std::vector ; using EffectMap = std::unordered_map; using AudacityCommandMap = std::unordered_map; using EffectOwnerMap = std::unordered_map< wxString, std::shared_ptr >; @@ -163,40 +161,4 @@ private: }; -class AUDACITY_DLL_API RealtimeEffectManager final -{ -public: - - /** Get the singleton instance of the RealtimeEffectManager. **/ - static RealtimeEffectManager & Get(); - - // 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(); - -private: - RealtimeEffectManager(); - ~RealtimeEffectManager(); - - wxCriticalSection mRealtimeLock; - EffectArray mRealtimeEffects; - int mRealtimeLatency; - bool mRealtimeSuspended; - bool mRealtimeActive; - std::vector mRealtimeChans; - std::vector mRealtimeRates; -}; - #endif diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 7a85213cc..72ee5b93a 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -18,6 +18,7 @@ #include "Effect.h" #include "EffectManager.h" +#include "RealtimeEffectManager.h" #if defined(EXPERIMENTAL_EFFECTS_RACK) diff --git a/src/effects/RealtimeEffectManager.cpp b/src/effects/RealtimeEffectManager.cpp new file mode 100644 index 000000000..a3e1ef071 --- /dev/null +++ b/src/effects/RealtimeEffectManager.cpp @@ -0,0 +1,368 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + RealtimeEffectManager.cpp + + Paul Licameli split from EffectManager.cpp + + **********************************************************************/ + +#include "../Audacity.h" +#include "RealtimeEffectManager.h" + +#include "../Experimental.h" + +#include "Effect.h" + +#include + +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(); + + // 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 RealtimeEffectManager::RealtimeIsActive() +{ + return mRealtimeEffects.size() != 0; +} + +bool RealtimeEffectManager::RealtimeIsSuspended() +{ + return mRealtimeSuspended; +} + +void RealtimeEffectManager::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 RealtimeEffectManager::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 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 e : mRealtimeEffects) { + e->SetSampleRate(rate); + e->RealtimeInitialize(); + } + + // Get things moving + RealtimeResume(); +} + +void RealtimeEffectManager::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 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 e : mRealtimeEffects) + e->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 e : mRealtimeEffects) + e->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 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 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 e : mRealtimeEffects) + { + if (e->IsRealtimeActive()) + e->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 || 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 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 e : mRealtimeEffects) + { + if (e->IsRealtimeActive()) + e->RealtimeProcessEnd(); + } + } + + mRealtimeLock.Leave(); +} + +int RealtimeEffectManager::GetRealtimeLatency() +{ + return mRealtimeLatency; +} diff --git a/src/effects/RealtimeEffectManager.h b/src/effects/RealtimeEffectManager.h new file mode 100644 index 000000000..f0a762239 --- /dev/null +++ b/src/effects/RealtimeEffectManager.h @@ -0,0 +1,57 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + RealtimeEffectManager.h + + Paul Licameli split from EffectManager.h + + **********************************************************************/ + +#ifndef __AUDACITY_REALTIME_EFFECT_MANAGER__ +#define __AUDACITY_REALTIME_EFFECT_MANAGER__ + +#include +#include +#include + +class Effect; +using EffectArray = std::vector ; + +class AUDACITY_DLL_API RealtimeEffectManager final +{ +public: + + /** Get the singleton instance of the RealtimeEffectManager. **/ + static RealtimeEffectManager & Get(); + + // 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(); + +private: + RealtimeEffectManager(); + ~RealtimeEffectManager(); + + wxCriticalSection mRealtimeLock; + EffectArray mRealtimeEffects; + int mRealtimeLatency; + bool mRealtimeSuspended; + bool mRealtimeActive; + std::vector mRealtimeChans; + std::vector mRealtimeRates; +}; + +#endif diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index e34fc1486..c60724a05 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -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 { diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp index 8fe3be65d..3f9fb6ebc 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp @@ -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" diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index c121fbaf2..91dcb02fd 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -358,6 +358,7 @@ + @@ -747,6 +748,7 @@ + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index 04ba2f167..0f4586baa 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -461,6 +461,9 @@ src\effects + + src\effects + src\effects @@ -1588,6 +1591,9 @@ src\effects + + src\effects + src\effects From 0f62046313d072a63ccc4f570acd5599d4db4d39 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 19:19:32 -0400 Subject: [PATCH 5/8] Define EffectClientInterface::GetBlockSize() --- include/audacity/EffectInterface.h | 2 ++ src/effects/Effect.cpp | 10 ++++++++++ src/effects/Effect.h | 1 + src/effects/VST/VSTEffect.cpp | 5 +++++ src/effects/VST/VSTEffect.h | 1 + src/effects/audiounits/AudioUnitEffect.cpp | 5 +++++ src/effects/audiounits/AudioUnitEffect.h | 1 + src/effects/ladspa/LadspaEffect.cpp | 5 +++++ src/effects/ladspa/LadspaEffect.h | 1 + src/effects/lv2/LV2Effect.cpp | 5 +++++ src/effects/lv2/LV2Effect.h | 1 + 11 files changed, 37 insertions(+) diff --git a/include/audacity/EffectInterface.h b/include/audacity/EffectInterface.h index 2f22945ea..b6c38c031 100755 --- a/include/audacity/EffectInterface.h +++ b/include/audacity/EffectInterface.h @@ -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; diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 425c89a2d..4ec5951c1 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -373,6 +373,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) diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 1307706e8..6a41f085d 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -121,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; diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 56b531b77..cce5ff4e1 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -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; diff --git a/src/effects/VST/VSTEffect.h b/src/effects/VST/VSTEffect.h index cb693e845..27c088aa4 100644 --- a/src/effects/VST/VSTEffect.h +++ b/src/effects/VST/VSTEffect.h @@ -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; diff --git a/src/effects/audiounits/AudioUnitEffect.cpp b/src/effects/audiounits/AudioUnitEffect.cpp index d184221a8..576ff9407 100644 --- a/src/effects/audiounits/AudioUnitEffect.cpp +++ b/src/effects/audiounits/AudioUnitEffect.cpp @@ -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) diff --git a/src/effects/audiounits/AudioUnitEffect.h b/src/effects/audiounits/AudioUnitEffect.h index 06ddd0279..137bba299 100644 --- a/src/effects/audiounits/AudioUnitEffect.h +++ b/src/effects/audiounits/AudioUnitEffect.h @@ -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; diff --git a/src/effects/ladspa/LadspaEffect.cpp b/src/effects/ladspa/LadspaEffect.cpp index 30b8131d2..ebacf1310 100644 --- a/src/effects/ladspa/LadspaEffect.cpp +++ b/src/effects/ladspa/LadspaEffect.cpp @@ -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) diff --git a/src/effects/ladspa/LadspaEffect.h b/src/effects/ladspa/LadspaEffect.h index ce8de7251..bf328242a 100644 --- a/src/effects/ladspa/LadspaEffect.h +++ b/src/effects/ladspa/LadspaEffect.h @@ -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; diff --git a/src/effects/lv2/LV2Effect.cpp b/src/effects/lv2/LV2Effect.cpp index 3e19895b9..8b9c641c0 100644 --- a/src/effects/lv2/LV2Effect.cpp +++ b/src/effects/lv2/LV2Effect.cpp @@ -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) diff --git a/src/effects/lv2/LV2Effect.h b/src/effects/lv2/LV2Effect.h index b208accf4..b77cea027 100644 --- a/src/effects/lv2/LV2Effect.h +++ b/src/effects/lv2/LV2Effect.h @@ -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; From 867e6a8d9e67288c5dec3e0753aeae7f0f63f5df Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 19:23:18 -0400 Subject: [PATCH 6/8] Move fields out of Effect, into new class RealtimeEffectState... ... and simplify, using a std::atomic instead of a critical section. (But did this before, and does this now, correctly synchronize across threads? I defer that question.) --- src/effects/Effect.cpp | 116 +++++++++++++------------ src/effects/Effect.h | 39 +++++---- src/effects/RealtimeEffectManager.cpp | 118 ++++++++++++-------------- src/effects/RealtimeEffectManager.h | 3 +- 4 files changed, 137 insertions(+), 139 deletions(-) diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 4ec5951c1..e751c065c 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -146,10 +146,6 @@ Effect::Effect() mNumGroups = 0; mProgress = NULL; - mRealtimeSuspendLock.Enter(); - mRealtimeSuspendCount = 1; // Effects are initially suspended - mRealtimeSuspendLock.Leave(); - mUIParent = NULL; mUIDialog = NULL; @@ -476,46 +472,41 @@ bool Effect::RealtimeFinalize() return false; } +RealtimeEffectState::RealtimeEffectState( Effect &effect ) + : mEffect{ effect } +{ +} + +bool RealtimeEffectState::RealtimeSuspend() +{ + auto result = mEffect.RealtimeSuspend(); + if ( result ) { + mRealtimeSuspendCount++; + } + return result; +} + 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; } +bool RealtimeEffectState::RealtimeResume() +{ + auto result = mEffect.RealtimeResume(); + if ( result ) { + mRealtimeSuspendCount--; + } + return result; +} + 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; } @@ -2306,7 +2297,7 @@ double Effect::CalcPreviewInputLength(double 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) +bool RealtimeEffectState::RealtimeAddProcessor(int group, unsigned chans, float rate) { auto ichans = chans; auto ochans = chans; @@ -2322,13 +2313,16 @@ bool Effect::RealtimeAddProcessor(int group, unsigned chans, float rate) // 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 < mNumAudioIn) + if (ichans < numAudioIn) { // All input channels have been consumed ichans = 0; @@ -2336,16 +2330,16 @@ bool Effect::RealtimeAddProcessor(int group, unsigned chans, float rate) // 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) + else if (ichans >= numAudioIn) { - gchans = mNumAudioIn; + 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 < mNumAudioOut) + if (ochans < numAudioOut) { // All output channels have been consumed ochans = 0; @@ -2353,13 +2347,13 @@ bool Effect::RealtimeAddProcessor(int group, unsigned chans, float rate) // 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) + else if (ochans >= numAudioOut) { - ochans -= mNumAudioOut; + ochans -= numAudioOut; } // Add a NEW processor - RealtimeAddProcessor(gchans, rate); + mEffect.RealtimeAddProcessor(gchans, rate); // Bump to next processor mCurrentProcessor++; @@ -2371,7 +2365,7 @@ bool Effect::RealtimeAddProcessor(int group, unsigned chans, float rate) // 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, +size_t RealtimeEffectState::RealtimeProcess(int group, unsigned chans, float **inbuf, float **outbuf, @@ -2386,8 +2380,11 @@ size_t Effect::RealtimeProcess(int group, // 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 *)); + 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; @@ -2404,9 +2401,9 @@ size_t Effect::RealtimeProcess(int group, // 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) + if (ichans < numAudioIn) { - for (size_t i = 0; i < mNumAudioIn; i++) + for (size_t i = 0; i < numAudioIn; i++) { if (indx == ichans) { @@ -2421,10 +2418,10 @@ size_t Effect::RealtimeProcess(int group, // 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) + else if (ichans >= numAudioIn) { gchans = 0; - for (size_t i = 0; i < mNumAudioIn; i++, ichans--, gchans++) + for (size_t i = 0; i < numAudioIn; i++, ichans--, gchans++) { clientIn[i] = inbuf[indx++]; } @@ -2433,9 +2430,9 @@ size_t Effect::RealtimeProcess(int group, // 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) + if (ochans < numAudioOut) { - for (size_t i = 0; i < mNumAudioOut; i++) + for (size_t i = 0; i < numAudioOut; i++) { if (i < ochans) { @@ -2453,9 +2450,9 @@ size_t Effect::RealtimeProcess(int group, // 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) + else if (ochans >= numAudioOut) { - for (size_t i = 0; i < mNumAudioOut; i++, ochans--) + for (size_t i = 0; i < numAudioOut; i++, ochans--) { clientOut[i] = outbuf[ondx++]; } @@ -2463,17 +2460,18 @@ size_t Effect::RealtimeProcess(int group, // Finally call the plugin to process the block len = 0; - for (decltype(numSamples) block = 0; block < numSamples; block += mBlockSize) + const auto blockSize = mEffect.GetBlockSize(); + for (decltype(numSamples) block = 0; block < numSamples; block += blockSize) { - auto cnt = std::min(numSamples - block, mBlockSize); - len += RealtimeProcess(processor, clientIn, clientOut, cnt); + auto cnt = std::min(numSamples - block, blockSize); + len += mEffect.RealtimeProcess(processor, clientIn, clientOut, cnt); - for (size_t i = 0 ; i < mNumAudioIn; i++) + for (size_t i = 0 ; i < numAudioIn; i++) { clientIn[i] += cnt; } - for (size_t i = 0 ; i < mNumAudioOut; i++) + for (size_t i = 0 ; i < numAudioOut; i++) { clientOut[i] += cnt; } @@ -2486,7 +2484,7 @@ size_t Effect::RealtimeProcess(int group, return len; } -bool Effect::IsRealtimeActive() +bool RealtimeEffectState::IsRealtimeActive() { return mRealtimeSuspendCount == 0; } diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 6a41f085d..4fc38e157 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -16,6 +16,7 @@ #include "../Experimental.h" +#include #include #include @@ -262,15 +263,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 @@ -544,12 +536,6 @@ private: size_t mBlockSize; unsigned mNumChannels; - std::vector mGroupProcessor; - int mCurrentProcessor; - - wxCriticalSection mRealtimeSuspendLock; - int mRealtimeSuspendCount; - const static wxString kUserPresetIdent; const static wxString kFactoryPresetIdent; const static wxString kCurrentSettingsIdent; @@ -561,6 +547,29 @@ private: friend class EffectPresetsDialog; }; +class RealtimeEffectState +{ +public: + explicit RealtimeEffectState( Effect &effect ); + + Effect &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: + Effect &mEffect; + + std::vector mGroupProcessor; + int mCurrentProcessor; + + std::atomic mRealtimeSuspendCount{ 1 }; // Effects are initially suspended +}; + // FIXME: // FIXME: Remove this once all effects are using the NEW dialog diff --git a/src/effects/RealtimeEffectManager.cpp b/src/effects/RealtimeEffectManager.cpp index a3e1ef071..13e4949af 100644 --- a/src/effects/RealtimeEffectManager.cpp +++ b/src/effects/RealtimeEffectManager.cpp @@ -42,51 +42,35 @@ void RealtimeEffectManager::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; + 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 ) ); } - - // Must not have been in the NEW chain, so tell it to cleanup - if (e && mRealtimeActive) - { - e->RealtimeFinalize(); + else { + // Preserve state for effect that remains in the chain + newStates.emplace_back( std::move( *found ) ); } } - - // 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(); - } + // 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 - mRealtimeEffects = effects; + mStates.swap( newStates ); // Allow RealtimeProcess() to, well, process RealtimeResume(); @@ -95,7 +79,7 @@ void RealtimeEffectManager::RealtimeSetEffects(const EffectArray & effects) bool RealtimeEffectManager::RealtimeIsActive() { - return mRealtimeEffects.size() != 0; + return mStates.size() != 0; } bool RealtimeEffectManager::RealtimeIsSuspended() @@ -108,6 +92,10 @@ void RealtimeEffectManager::RealtimeAddEffect(Effect *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) { @@ -117,12 +105,10 @@ void RealtimeEffectManager::RealtimeAddEffect(Effect *effect) // Add the required processors for (size_t i = 0, cnt = mRealtimeChans.size(); i < cnt; i++) { - effect->RealtimeAddProcessor(i, mRealtimeChans[i], mRealtimeRates[i]); + state->RealtimeAddProcessor(i, mRealtimeChans[i], mRealtimeRates[i]); } } - // Add to list of active effects - mRealtimeEffects.push_back(effect); // Allow RealtimeProcess() to, well, process RealtimeResume(); @@ -140,10 +126,14 @@ void RealtimeEffectManager::RealtimeRemoveEffect(Effect *effect) } // Remove from list of active effects - auto end = mRealtimeEffects.end(); - auto found = std::find(mRealtimeEffects.begin(), end, effect); + 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) - mRealtimeEffects.erase(found); + mStates.erase(found); // Allow RealtimeProcess() to, well, process RealtimeResume(); @@ -163,9 +153,9 @@ void RealtimeEffectManager::RealtimeInitialize(double rate) mRealtimeActive = true; // Tell each effect to get ready for action - for (auto e : mRealtimeEffects) { - e->SetSampleRate(rate); - e->RealtimeInitialize(); + for (auto &state : mStates) { + state->GetEffect().SetSampleRate(rate); + state->GetEffect().RealtimeInitialize(); } // Get things moving @@ -174,8 +164,8 @@ void RealtimeEffectManager::RealtimeInitialize(double rate) void RealtimeEffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate) { - for (auto e : mRealtimeEffects) - e->RealtimeAddProcessor(group, chans, rate); + for (auto &state : mStates) + state->RealtimeAddProcessor(group, chans, rate); mRealtimeChans.push_back(chans); mRealtimeRates.push_back(rate); @@ -190,8 +180,8 @@ void RealtimeEffectManager::RealtimeFinalize() mRealtimeLatency = 0; // Tell each effect to clean up as well - for (auto e : mRealtimeEffects) - e->RealtimeFinalize(); + for (auto &state : mStates) + state->GetEffect().RealtimeFinalize(); // Reset processor parameters mRealtimeChans.clear(); @@ -216,8 +206,8 @@ void RealtimeEffectManager::RealtimeSuspend() mRealtimeSuspended = true; // And make sure the effects don't either - for (auto e : mRealtimeEffects) - e->RealtimeSuspend(); + for (auto &state : mStates) + state->RealtimeSuspend(); mRealtimeLock.Leave(); } @@ -234,8 +224,8 @@ void RealtimeEffectManager::RealtimeResume() } // Tell the effects to get ready for more action - for (auto e : mRealtimeEffects) - e->RealtimeResume(); + for (auto &state : mStates) + state->RealtimeResume(); // And we should too mRealtimeSuspended = false; @@ -255,10 +245,10 @@ void RealtimeEffectManager::RealtimeProcessStart() // have been suspended. if (!mRealtimeSuspended) { - for (auto e : mRealtimeEffects) + for (auto &state : mStates) { - if (e->IsRealtimeActive()) - e->RealtimeProcessStart(); + if (state->IsRealtimeActive()) + state->GetEffect().RealtimeProcessStart(); } } @@ -275,7 +265,7 @@ size_t RealtimeEffectManager::RealtimeProcess(int group, unsigned chans, float * // 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()) + if (mRealtimeSuspended || mStates.empty()) { mRealtimeLock.Leave(); return numSamples; @@ -300,11 +290,11 @@ size_t RealtimeEffectManager::RealtimeProcess(int group, unsigned chans, 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) + for (auto &state : mStates) { - if (e->IsRealtimeActive()) + if (state->IsRealtimeActive()) { - e->RealtimeProcess(group, chans, ibuf, obuf, numSamples); + state->RealtimeProcess(group, chans, ibuf, obuf, numSamples); called++; } @@ -352,10 +342,10 @@ void RealtimeEffectManager::RealtimeProcessEnd() // have been suspended. if (!mRealtimeSuspended) { - for (auto e : mRealtimeEffects) + for (auto &state : mStates) { - if (e->IsRealtimeActive()) - e->RealtimeProcessEnd(); + if (state->IsRealtimeActive()) + state->GetEffect().RealtimeProcessEnd(); } } diff --git a/src/effects/RealtimeEffectManager.h b/src/effects/RealtimeEffectManager.h index f0a762239..134884d35 100644 --- a/src/effects/RealtimeEffectManager.h +++ b/src/effects/RealtimeEffectManager.h @@ -17,6 +17,7 @@ class Effect; using EffectArray = std::vector ; +class RealtimeEffectState; class AUDACITY_DLL_API RealtimeEffectManager final { @@ -46,7 +47,7 @@ private: ~RealtimeEffectManager(); wxCriticalSection mRealtimeLock; - EffectArray mRealtimeEffects; + std::vector< std::unique_ptr > mStates; int mRealtimeLatency; bool mRealtimeSuspended; bool mRealtimeActive; From 5caeaf520b4e6881c066e5b9b03a02bed313d53c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 16:31:47 -0400 Subject: [PATCH 7/8] RealtimeEffectState needs only EffectClientInterface, not Effect --- src/effects/Effect.cpp | 2 +- src/effects/Effect.h | 6 +++--- src/effects/EffectRack.cpp | 4 +++- src/effects/RealtimeEffectManager.cpp | 4 ++-- src/effects/RealtimeEffectManager.h | 8 ++++---- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index e751c065c..05100b2f2 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -472,7 +472,7 @@ bool Effect::RealtimeFinalize() return false; } -RealtimeEffectState::RealtimeEffectState( Effect &effect ) +RealtimeEffectState::RealtimeEffectState( EffectClientInterface &effect ) : mEffect{ effect } { } diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 4fc38e157..f718fa404 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -550,9 +550,9 @@ private: class RealtimeEffectState { public: - explicit RealtimeEffectState( Effect &effect ); + explicit RealtimeEffectState( EffectClientInterface &effect ); - Effect &GetEffect() const { return mEffect; } + EffectClientInterface &GetEffect() const { return mEffect; } bool RealtimeSuspend(); bool RealtimeResume(); @@ -562,7 +562,7 @@ public: bool IsRealtimeActive(); private: - Effect &mEffect; + EffectClientInterface &mEffect; std::vector mGroupProcessor; int mCurrentProcessor; diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 72ee5b93a..cefbb7f30 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -564,7 +564,9 @@ void EffectRack::UpdateActive() } } - RealtimeEffectManager::Get().RealtimeSetEffects(mActive); + RealtimeEffectManager::Get().RealtimeSetEffects( + { mActive.begin(), mActive.end() } + ); } #endif diff --git a/src/effects/RealtimeEffectManager.cpp b/src/effects/RealtimeEffectManager.cpp index 13e4949af..15c235fd6 100644 --- a/src/effects/RealtimeEffectManager.cpp +++ b/src/effects/RealtimeEffectManager.cpp @@ -87,7 +87,7 @@ bool RealtimeEffectManager::RealtimeIsSuspended() return mRealtimeSuspended; } -void RealtimeEffectManager::RealtimeAddEffect(Effect *effect) +void RealtimeEffectManager::RealtimeAddEffect(EffectClientInterface *effect) { // Block RealtimeProcess() RealtimeSuspend(); @@ -114,7 +114,7 @@ void RealtimeEffectManager::RealtimeAddEffect(Effect *effect) RealtimeResume(); } -void RealtimeEffectManager::RealtimeRemoveEffect(Effect *effect) +void RealtimeEffectManager::RealtimeRemoveEffect(EffectClientInterface *effect) { // Block RealtimeProcess() RealtimeSuspend(); diff --git a/src/effects/RealtimeEffectManager.h b/src/effects/RealtimeEffectManager.h index 134884d35..689ede6e4 100644 --- a/src/effects/RealtimeEffectManager.h +++ b/src/effects/RealtimeEffectManager.h @@ -15,13 +15,13 @@ #include #include -class Effect; -using EffectArray = std::vector ; +class EffectClientInterface; class RealtimeEffectState; class AUDACITY_DLL_API RealtimeEffectManager final { public: + using EffectArray = std::vector ; /** Get the singleton instance of the RealtimeEffectManager. **/ static RealtimeEffectManager & Get(); @@ -29,8 +29,8 @@ public: // Realtime effect processing bool RealtimeIsActive(); bool RealtimeIsSuspended(); - void RealtimeAddEffect(Effect *effect); - void RealtimeRemoveEffect(Effect *effect); + 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); From cd9e4e3987830d7b1a213cd6641734303e5f56b5 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 23 Jun 2019 16:31:47 -0400 Subject: [PATCH 8/8] RealtimeEffectState into RealtimeEffectManager... ... which doesn't need Effect.h then. Freeing Effect and RealtimeEffectManager from cycles, leaving 25 in the big s.c.c. --- src/effects/Effect.cpp | 218 ----------------------- src/effects/Effect.h | 25 --- src/effects/RealtimeEffectManager.cpp | 245 +++++++++++++++++++++++++- 3 files changed, 244 insertions(+), 244 deletions(-) diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 05100b2f2..6dbbd0097 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -472,20 +472,6 @@ bool Effect::RealtimeFinalize() return false; } -RealtimeEffectState::RealtimeEffectState( EffectClientInterface &effect ) - : mEffect{ effect } -{ -} - -bool RealtimeEffectState::RealtimeSuspend() -{ - auto result = mEffect.RealtimeSuspend(); - if ( result ) { - mRealtimeSuspendCount++; - } - return result; -} - bool Effect::RealtimeSuspend() { if (mClient) @@ -494,15 +480,6 @@ bool Effect::RealtimeSuspend() return true; } -bool RealtimeEffectState::RealtimeResume() -{ - auto result = mEffect.RealtimeResume(); - if ( result ) { - mRealtimeSuspendCount--; - } - return result; -} - bool Effect::RealtimeResume() { if (mClient) @@ -2294,201 +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 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; -} - bool Effect::IsHidden() { return false; diff --git a/src/effects/Effect.h b/src/effects/Effect.h index f718fa404..92cbd0baa 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -16,7 +16,6 @@ #include "../Experimental.h" -#include #include #include @@ -547,30 +546,6 @@ private: friend class EffectPresetsDialog; }; -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 mGroupProcessor; - int mCurrentProcessor; - - std::atomic mRealtimeSuspendCount{ 1 }; // Effects are initially suspended -}; - - // FIXME: // FIXME: Remove this once all effects are using the NEW dialog // FIXME: diff --git a/src/effects/RealtimeEffectManager.cpp b/src/effects/RealtimeEffectManager.cpp index 15c235fd6..a29a58ca4 100644 --- a/src/effects/RealtimeEffectManager.cpp +++ b/src/effects/RealtimeEffectManager.cpp @@ -13,10 +13,35 @@ #include "../Experimental.h" -#include "Effect.h" +#include "audacity/EffectInterface.h" +#include "MemoryX.h" +#include #include +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 mGroupProcessor; + int mCurrentProcessor; + + std::atomic mRealtimeSuspendCount{ 1 }; // Effects are initially suspended +}; + RealtimeEffectManager & RealtimeEffectManager::Get() { static RealtimeEffectManager rem; @@ -356,3 +381,221 @@ 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; +}