Audacity 3.2.0
RealtimeEffectManager.h
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 RealtimeEffectManager.h
6
7 Paul Licameli split from EffectManager.h
8
9 **********************************************************************/
10
11#ifndef __AUDACITY_REALTIME_EFFECT_MANAGER__
12#define __AUDACITY_REALTIME_EFFECT_MANAGER__
13
14#include <atomic>
15#include <chrono>
16#include <memory>
17#include <mutex>
18#include <optional>
19#include <unordered_map>
20#include <vector>
21
22#include "ClientData.h"
23#include "Observer.h"
24#include "PluginProvider.h" // for PluginID
25#include "RealtimeEffectList.h"
26
27class ChannelGroup;
28class EffectInstance;
29
30namespace RealtimeEffects {
31 class InitializationScope;
32 class ProcessingScope;
33}
34
37{
38 enum class Type
39 {
40 EffectAdded,
41 EffectReplaced,
42 EffectRemoved
43 };
46};
47
48class REALTIME_EFFECTS_API RealtimeEffectManager final :
49 public ClientData::Base,
50 public Observer::Publisher<RealtimeEffectManagerMessage>
51{
52public:
53 using Latency = std::chrono::microseconds;
54
57
59 static const RealtimeEffectManager & Get(const AudacityProject &project);
60
61 // Realtime effect processing
62
64 bool IsActive() const noexcept;
65// Latency GetLatency() const;
66
68
77 std::shared_ptr<RealtimeEffectState> AddState(
78 RealtimeEffects::InitializationScope *pScope,
79 ChannelGroup *pGroup,
80 const PluginID & id);
81
83
92 std::shared_ptr<RealtimeEffectState> ReplaceState(
93 RealtimeEffects::InitializationScope *pScope,
94 ChannelGroup *pGroup,
95 size_t index, const PluginID & id);
96
98
104 void RemoveState(RealtimeEffects::InitializationScope *pScope,
105 ChannelGroup *pGroup,
106 std::shared_ptr<RealtimeEffectState> pState);
107
109 std::optional<size_t> FindState(
110 ChannelGroup *pGroup,
111 const std::shared_ptr<RealtimeEffectState> &pState) const;
112
113 bool GetSuspended() const
114 { return mSuspended.load(std::memory_order_relaxed); }
115
117
123 void SetSuspended(bool value)
124 { mSuspended.store(value, std::memory_order_relaxed); }
125
126private:
128
132 std::shared_ptr<RealtimeEffectState>
133 MakeNewState(RealtimeEffects::InitializationScope *pScope,
134 ChannelGroup *pGroup,
135 const PluginID &id);
136
139 double sampleRate);
142
146 const ChannelGroup &group, unsigned chans, float rate);
148 void Finalize() noexcept;
149
150 friend RealtimeEffects::ProcessingScope;
151 struct REALTIME_EFFECTS_API AllListsLock {
153 AllListsLock(RealtimeEffectManager *pManager = nullptr);
154 AllListsLock(AllListsLock &&other);
155 AllListsLock& operator= (AllListsLock &&other);
156 void Reset();
157 ~AllListsLock() { Reset(); }
158 };
159
160 void ProcessStart(bool suspended);
162 size_t Process(bool suspended,
163 const ChannelGroup &group,
164 float *const *buffers, float *const *scratch, float *dummy,
165 unsigned nBuffers, size_t numSamples);
166 void ProcessEnd(bool suspended) noexcept;
167
170
171 // Type that state visitor functions would have for out-of-line definition
172 // of VisitGroup and VisitAll
173 // using StateVisitor =
174 // std::function<void(RealtimeEffectState &state, bool listIsActive)> ;
175
177 template<typename StateVisitor>
178 void VisitGroup(ChannelGroup &group, const StateVisitor &func)
179 {
180 // Call the function for each effect on the master list
181 RealtimeEffectList::Get(mProject).Visit(func);
182
183 // Call the function for each effect on the group list
184 RealtimeEffectList::Get(group).Visit(func);
185 }
186
187 template<typename StateVisitor>
189 const ChannelGroup &group, const StateVisitor &func)
190 {
191 // Call the function for each effect on the master list
192 RealtimeEffectList::Get(mProject).Visit(func);
193
194 // Call the function for each effect on the group list
195 RealtimeEffectList::Get(group).Visit(func);
196 }
197
199
200 template<typename StateVisitor>
201 void VisitAll(const StateVisitor &func)
202 {
203 // Call the function for each effect on the master list
204 RealtimeEffectList::Get(mProject).Visit(func);
205
206 // And all group lists
207 for (auto group : mGroups)
208 RealtimeEffectList::Get(*group).Visit(func);
209 }
210
212 Latency mLatency{ 0 };
213
214 std::atomic<bool> mSuspended{ true };
215
216 bool mActive{ false };
217
218 // This member is mutated only by Initialize(), AddGroup(), Finalize()
219 // which are to be called only while there is no playback
220 std::vector<const ChannelGroup *> mGroups;
221
222 std::unordered_map<const ChannelGroup *, double> mRates;
223};
224
225namespace RealtimeEffects {
228public:
231 std::weak_ptr<AudacityProject> wProject, double sampleRate,
232 unsigned numPlaybackChannels
234 , mwProject{ move(wProject) }
235 , mNumPlaybackChannels{ numPlaybackChannels }
236 {
237 if (auto pProject = mwProject.lock())
239 }
243 {
244 if (auto pProject = mwProject.lock())
246 }
247
251 void AddGroup(const ChannelGroup &group,
252 unsigned chans, float rate)
253 {
254 if (auto pProject = mwProject.lock())
256 .AddGroup(*this, group, chans, rate);
257 }
258
259 std::vector<std::shared_ptr<EffectInstance>> mInstances;
262
263private:
264 std::weak_ptr<AudacityProject> mwProject;
265};
266
269public:
271 {
272 if (auto pProject = mwProject.lock()) {
273 auto &manager = RealtimeEffectManager::Get(*pProject);
274 mLocks = { &manager };
275 mSuspended = manager.GetSuspended();
276 }
277 }
280 std::weak_ptr<AudacityProject> wProject)
281 : mwProject{ move(wProject) }
282 {
283 if (auto pProject = mwProject.lock())
285 }
286 ProcessingScope( ProcessingScope &&other ) = default;
289 {
290 if (auto pProject = mwProject.lock())
292 }
293
295 size_t Process(const ChannelGroup &group,
296 float *const *buffers,
297 float *const *scratch,
298 float *dummy,
299 unsigned nBuffers,
300 size_t numSamples
301 )
302 {
303 if (auto pProject = mwProject.lock())
304 return RealtimeEffectManager::Get(*pProject)
305 .Process(mSuspended, group, buffers, scratch, dummy,
306 nBuffers, numSamples);
307 else
308 return 0; // consider them trivially processed
309 }
310
311private:
313 std::weak_ptr<AudacityProject> mwProject;
315};
316}
317
318#endif
Utility ClientData::Site to register hooks into a host class that attach client data.
wxString PluginID
Generalized interface for discovery of plug-ins for one protocol.
static const AttachedProjectObjects::RegisteredFactory manager
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Performs effect computation.
An object that sends messages to an open-ended list of subscribed callbacks.
Definition: Observer.h:108
void Visit(const StateVisitor &func)
Apply the function to all states sequentially.
static RealtimeEffectList & Get(AudacityProject &project)
void VisitGroup(ChannelGroup &group, const StateVisitor &func)
Visit the per-project states first, then states for leader if not null.
void VisitAll(const StateVisitor &func)
Visit the per-project states first, then all groups from AddGroup.
void SetSuspended(bool value)
To be called only from main thread.
static RealtimeEffectManager & Get(AudacityProject &project)
void Initialize(RealtimeEffects::InitializationScope &scope, double sampleRate)
Main thread begins to define a set of groups for playback.
void ProcessStart(bool suspended)
std::unordered_map< const ChannelGroup *, double > mRates
void Finalize() noexcept
Main thread cleans up after playback.
size_t Process(bool suspended, const ChannelGroup &group, float *const *buffers, float *const *scratch, float *dummy, unsigned nBuffers, size_t numSamples)
RealtimeEffectManager & operator=(const RealtimeEffectManager &)=delete
void AddGroup(RealtimeEffects::InitializationScope &scope, const ChannelGroup &group, unsigned chans, float rate)
void ProcessEnd(bool suspended) noexcept
std::chrono::microseconds Latency
void VisitGroup(const ChannelGroup &group, const StateVisitor &func)
std::vector< const ChannelGroup * > mGroups
all are non-null
RealtimeEffectManager(const RealtimeEffectManager &)=delete
Brackets processing setup and cleanup in the main thread.
void AddGroup(const ChannelGroup &group, unsigned chans, float rate)
InitializationScope(std::weak_ptr< AudacityProject > wProject, double sampleRate, unsigned numPlaybackChannels)
InitializationScope(InitializationScope &&other)=default
std::vector< std::shared_ptr< EffectInstance > > mInstances
std::weak_ptr< AudacityProject > mwProject
InitializationScope & operator=(InitializationScope &&other)=default
Brackets one block of processing in one thread.
size_t Process(const ChannelGroup &group, float *const *buffers, float *const *scratch, float *dummy, unsigned nBuffers, size_t numSamples)
ProcessingScope & operator=(ProcessingScope &&other)=default
std::weak_ptr< AudacityProject > mwProject
RealtimeEffectManager::AllListsLock mLocks
ProcessingScope(ProcessingScope &&other)=default
ProcessingScope(InitializationScope &, std::weak_ptr< AudacityProject > wProject)
Require a prior InializationScope to ensure correct nesting.
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
STL namespace.
A convenient default parameter for class template Site.
Definition: ClientData.h:28
Posted when effect is being added or removed to/from channel group or project.
ChannelGroup * group
null, if changes happened in the project scope