Audacity 3.2.0
Classes | Public Types | Public Member Functions | Static Public Member Functions | Static Public Attributes | Private Member Functions | Private Attributes | List of all members
RealtimeEffectManager Class Referencefinal

#include <RealtimeEffectManager.h>

Inheritance diagram for RealtimeEffectManager:
[legend]
Collaboration diagram for RealtimeEffectManager:
[legend]

Classes

struct  AllListsLock
 

Public Types

using Latency = std::chrono::microseconds
 
- Public Types inherited from Observer::Publisher< RealtimeEffectManagerMessage >
using message_type = RealtimeEffectManagerMessage
 
using CallbackReturn = std::conditional_t< true, void, bool >
 
using Callback = std::function< CallbackReturn(const RealtimeEffectManagerMessage &) >
 Type of functions that can be connected to the Publisher. More...
 

Public Member Functions

 RealtimeEffectManager (AudacityProject &project)
 
 ~RealtimeEffectManager ()
 
bool IsActive () const noexcept
 To be called only from main thread. More...
 
std::shared_ptr< RealtimeEffectStateAddState (RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, const PluginID &id)
 Main thread appends a global or per-group effect. More...
 
std::shared_ptr< RealtimeEffectStateReplaceState (RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, size_t index, const PluginID &id)
 Main thread replaces a global or per-group effect. More...
 
void RemoveState (RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, std::shared_ptr< RealtimeEffectState > pState)
 Main thread removes a global or per-group effect. More...
 
std::optional< size_t > FindState (ChannelGroup *pGroup, const std::shared_ptr< RealtimeEffectState > &pState) const
 Report the position of a state in the global or a per-group list. More...
 
bool GetSuspended () const
 
void SetSuspended (bool value)
 To be called only from main thread. More...
 
- Public Member Functions inherited from ClientData::Base
virtual ~Base ()
 
- Public Member Functions inherited from Observer::Publisher< RealtimeEffectManagerMessage >
 Publisher (ExceptionPolicy *pPolicy=nullptr, Alloc a={})
 Constructor supporting type-erased custom allocation/deletion. More...
 
 Publisher (Publisher &&)=default
 
Publisheroperator= (Publisher &&)=default
 
Subscription Subscribe (Callback callback)
 Connect a callback to the Publisher; later-connected are called earlier. More...
 
Subscription Subscribe (Object &obj, Return(Object::*callback)(Args...))
 Overload of Subscribe takes an object and pointer-to-member-function. More...
 

Static Public Member Functions

static RealtimeEffectManagerGet (AudacityProject &project)
 
static const RealtimeEffectManagerGet (const AudacityProject &project)
 

Static Public Attributes

static constexpr ChannelGroupMasterGroup = nullptr
 
- Static Public Attributes inherited from Observer::Publisher< RealtimeEffectManagerMessage >
static constexpr bool notifies_all
 

Private Member Functions

std::shared_ptr< RealtimeEffectStateMakeNewState (RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, const PluginID &id)
 
void Initialize (RealtimeEffects::InitializationScope &scope, unsigned numPlaybackChannels, double sampleRate)
 Main thread begins to define a set of groups for playback. More...
 
void AddGroup (RealtimeEffects::InitializationScope &scope, const ChannelGroup &group, unsigned chans, float rate)
 
void Finalize () noexcept
 Main thread cleans up after playback. More...
 
void ProcessStart (bool suspended)
 
size_t Process (bool suspended, const ChannelGroup *group, float *const *buffers, float *const *scratch, float *dummy, unsigned nBuffers, size_t numSamples)
 
void ProcessEnd (bool suspended) noexcept
 
 RealtimeEffectManager (const RealtimeEffectManager &)=delete
 
RealtimeEffectManageroperator= (const RealtimeEffectManager &)=delete
 
template<typename StateVisitor >
void VisitGroup (ChannelGroup *group, const StateVisitor &func)
 Visit states for group or for the master when group is null. More...
 
template<typename StateVisitor >
void VisitGroup (const ChannelGroup *group, const StateVisitor &func)
 
template<typename StateVisitor >
void VisitAll (const StateVisitor &func)
 Visit the per-project states first, then all groups from AddGroup. More...
 

Private Attributes

AudacityProjectmProject
 
std::atomic< bool > mSuspended { true }
 
bool mActive { false }
 
std::vector< const ChannelGroup * > mGroups
 all are non-null More...
 
std::unordered_map< const ChannelGroup *, double > mRates
 

Additional Inherited Members

- Protected Member Functions inherited from Observer::Publisher< RealtimeEffectManagerMessage >
CallbackReturn Publish (const RealtimeEffectManagerMessage &message)
 Send a message to connected callbacks. More...
 

Detailed Description

Definition at line 48 of file RealtimeEffectManager.h.

Member Typedef Documentation

◆ Latency

using RealtimeEffectManager::Latency = std::chrono::microseconds

Definition at line 57 of file RealtimeEffectManager.h.

Constructor & Destructor Documentation

◆ RealtimeEffectManager() [1/2]

RealtimeEffectManager::RealtimeEffectManager ( AudacityProject project)

Definition at line 39 of file RealtimeEffectManager.cpp.

41{
42}
const auto project

◆ ~RealtimeEffectManager()

RealtimeEffectManager::~RealtimeEffectManager ( )

Definition at line 44 of file RealtimeEffectManager.cpp.

45{
46}

◆ RealtimeEffectManager() [2/2]

RealtimeEffectManager::RealtimeEffectManager ( const RealtimeEffectManager )
privatedelete

Member Function Documentation

◆ AddGroup()

void RealtimeEffectManager::AddGroup ( RealtimeEffects::InitializationScope scope,
const ChannelGroup group,
unsigned  chans,
float  rate 
)
private

Main thread adds one group (passing the first of one or more channels), still before playback

Definition at line 79 of file RealtimeEffectManager.cpp.

82{
83 mGroups.push_back(&group);
84 mRates.insert({&group, rate});
85
86 VisitGroup(&group,
87 [&](RealtimeEffectState & state, bool) {
88 scope.mInstances.push_back(state.AddGroup(&group, chans, rate));
89 }
90 );
91}
void VisitGroup(ChannelGroup *group, const StateVisitor &func)
Visit states for group or for the master when group is null.
std::unordered_map< const ChannelGroup *, double > mRates
std::vector< const ChannelGroup * > mGroups
all are non-null
std::shared_ptr< EffectInstance > AddGroup(const ChannelGroup *group, unsigned chans, float sampleRate)
Main thread sets up this state before adding it to lists.
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope

References RealtimeEffectState::AddGroup(), mGroups, mRates, anonymous_namespace{wxCommandTargets.cpp}::scope, and VisitGroup().

Referenced by RealtimeEffects::InitializationScope::AddGroup().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AddState()

std::shared_ptr< RealtimeEffectState > RealtimeEffectManager::AddState ( RealtimeEffects::InitializationScope pScope,
ChannelGroup pGroup,
const PluginID id 
)

Main thread appends a global or per-group effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pGroupif null, then state is added to the global list
ididentifies the effect
Returns
if null, the given id was not found
Postcondition
result: !result || result->GetEffect() != nullptr

Definition at line 275 of file RealtimeEffectManager.cpp.

278{
279 auto &states = FindStates(mProject, pGroup);
280 auto pState = MakeNewState(pScope, pGroup, id);
281 if (!pState)
282 return nullptr;
283
284 // Only now add the completed state to the list, under a lock guard
285 if (!states.AddState(pState))
286 return nullptr;
287 Publish({
289 pGroup ? pGroup : MasterGroup
290 });
291 return pState;
292}
CallbackReturn Publish(const RealtimeEffectManagerMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
std::shared_ptr< RealtimeEffectState > MakeNewState(RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, const PluginID &id)
static constexpr ChannelGroup * MasterGroup
RealtimeEffectList & FindStates(AudacityProject &project, ChannelGroup *pGroup)

References RealtimeEffectManagerMessage::EffectAdded, anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), MakeNewState(), MasterGroup, mProject, and Observer::Publisher< RealtimeEffectManagerMessage >::Publish().

Referenced by AudioIO::AddState().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Finalize()

void RealtimeEffectManager::Finalize ( )
privatenoexcept

Main thread cleans up after playback.

Definition at line 93 of file RealtimeEffectManager.cpp.

94{
95 // Reenter suspended state
96 SetSuspended(true);
97
98 VisitAll([](RealtimeEffectState &state, bool){ state.Finalize(); });
99
100 // Reset processor parameters
101 mGroups.clear();
102 mRates.clear();
103
104 // No longer active
105 mActive = false;
106}
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.
bool Finalize() noexcept
Main thread cleans up playback.

References RealtimeEffectState::Finalize(), mActive, mGroups, mRates, SetSuspended(), and VisitAll().

Referenced by RealtimeEffects::InitializationScope::~InitializationScope().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ FindState()

std::optional< size_t > RealtimeEffectManager::FindState ( ChannelGroup pGroup,
const std::shared_ptr< RealtimeEffectState > &  pState 
) const

Report the position of a state in the global or a per-group list.

Definition at line 334 of file RealtimeEffectManager.cpp.

337{
338 auto &states = FindStates(mProject, pGroup);
339 return states.FindState(pState);
340}

References anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), and mProject.

Here is the call graph for this function:

◆ Get() [1/2]

RealtimeEffectManager & RealtimeEffectManager::Get ( AudacityProject project)
static

◆ Get() [2/2]

const RealtimeEffectManager & RealtimeEffectManager::Get ( const AudacityProject project)
static

Definition at line 34 of file RealtimeEffectManager.cpp.

35{
36 return Get(const_cast<AudacityProject &>(project));
37}
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static RealtimeEffectManager & Get(AudacityProject &project)

References Get(), and project.

Here is the call graph for this function:

◆ GetSuspended()

bool RealtimeEffectManager::GetSuspended ( ) const
inline

Definition at line 116 of file RealtimeEffectManager.h.

117 { return mSuspended.load(std::memory_order_relaxed); }
std::atomic< bool > mSuspended

◆ Initialize()

void RealtimeEffectManager::Initialize ( RealtimeEffects::InitializationScope scope,
unsigned  numPlaybackChannels,
double  sampleRate 
)
private

Main thread begins to define a set of groups for playback.

Definition at line 53 of file RealtimeEffectManager.cpp.

57{
58 // (Re)Set processor parameters
59 mRates.clear();
60 mGroups.clear();
61
62 // RealtimeAdd/RemoveEffect() needs to know when we're active so it can
63 // initialize newly added effects
64 mActive = true;
65
66 // Tell each state to get ready for action
68 scope.mInstances.push_back(state.Initialize(sampleRate));
69 });
70
71 // Leave suspended state
72 SetSuspended(false);
73
74 VisitGroup(MasterGroup, [&](RealtimeEffectState& state, bool) {
75 scope.mInstances.push_back(state.AddGroup(MasterGroup, numPlaybackChannels, sampleRate));
76 });
77}
std::shared_ptr< EffectInstance > Initialize(double rate)
Main thread sets up for playback.

References RealtimeEffectState::AddGroup(), RealtimeEffectState::Initialize(), mActive, MasterGroup, mGroups, mRates, anonymous_namespace{ClipSegmentTest.cpp}::sampleRate, anonymous_namespace{wxCommandTargets.cpp}::scope, SetSuspended(), VisitAll(), and VisitGroup().

Referenced by RealtimeEffects::InitializationScope::InitializationScope().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsActive()

bool RealtimeEffectManager::IsActive ( ) const
noexcept

To be called only from main thread.

Definition at line 48 of file RealtimeEffectManager.cpp.

49{
50 return mActive;
51}

References mActive.

Referenced by anonymous_namespace{PluginMenus.cpp}::IsRealtimeNotActiveFlag().

Here is the caller graph for this function:

◆ MakeNewState()

std::shared_ptr< RealtimeEffectState > RealtimeEffectManager::MakeNewState ( RealtimeEffects::InitializationScope pScope,
ChannelGroup pGroup,
const PluginID id 
)
private

Definition at line 230 of file RealtimeEffectManager.cpp.

233{
234 if (!pScope && mActive)
235 return nullptr;
236 auto pNewState = RealtimeEffectState::make_shared(id);
237 auto &state = *pNewState;
238 if (pScope && mActive) {
239 // Adding a state while playback is in-flight
240 auto pInstance = state.Initialize(pScope->mSampleRate);
241 pScope->mInstances.push_back(pInstance);
242
243 if(pGroup == MasterGroup)
244 {
245 auto pInstance2 = state.AddGroup(MasterGroup, pScope->mNumPlaybackChannels, pScope->mSampleRate);
246 if(pInstance2 != pInstance)
247 pScope->mInstances.push_back(pInstance2);
248 }
249 else
250 {
251 for (const auto group : mGroups) {
252 if (pGroup != group)
253 continue;
254 auto pInstance2 =
255 state.AddGroup(group, pScope->mNumPlaybackChannels, mRates[group]);
256 if (pInstance2 != pInstance)
257 pScope->mInstances.push_back(pInstance2);
258 }
259 }
260
261
262 }
263 return pNewState;
264}
std::vector< std::shared_ptr< EffectInstance > > mInstances
static std::shared_ptr< RealtimeEffectState > make_shared(Args &&...args)
Definition: MemoryX.h:300

References mActive, SharedNonInterfering< RealtimeEffectState >::make_shared(), MasterGroup, mGroups, RealtimeEffects::InitializationScope::mInstances, RealtimeEffects::InitializationScope::mNumPlaybackChannels, mRates, and RealtimeEffects::InitializationScope::mSampleRate.

Referenced by AddState(), and ReplaceState().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator=()

RealtimeEffectManager & RealtimeEffectManager::operator= ( const RealtimeEffectManager )
privatedelete

◆ Process()

size_t RealtimeEffectManager::Process ( bool  suspended,
const ChannelGroup group,
float *const *  buffers,
float *const *  scratch,
float *  dummy,
unsigned  nBuffers,
size_t  numSamples 
)
private

Definition at line 122 of file RealtimeEffectManager.cpp.

126{
127 // Can be suspended because of the audio stream being paused or because
128 // effects have been suspended, so allow the samples to pass as-is.
129 if (suspended)
130 return 0;
131
132 // Allocate the in and out buffer arrays
133 const auto ibuf =
134 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
135 const auto obuf =
136 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
137
138 // And populate the input with the buffers we've been given while allocating
139 // NEW output buffers
140 for (unsigned int i = 0; i < nBuffers; i++) {
141 ibuf[i] = buffers[i];
142 obuf[i] = scratch[i];
143 }
144
145 // Now call each effect in the chain while swapping buffer pointers to feed
146 // the output of one effect as the input to the next effect
147 // Tracks how many processors were called
148 size_t called = 0;
149 size_t discardable = 0;
150 VisitGroup(group,
151 [&](RealtimeEffectState &state, bool)
152 {
153 discardable +=
154 state.Process(group, nBuffers, ibuf, obuf, dummy, numSamples);
155 for (auto i = 0; i < nBuffers; ++i)
156 std::swap(ibuf[i], obuf[i]);
157 called++;
158 }
159 );
160
161 // Once we're done, we might wind up with the last effect storing its results
162 // in the temporary buffers. If that's the case, we need to copy it over to
163 // the caller's buffers. This happens when the number of effects processed
164 // is odd.
165 if (called & 1)
166 for (unsigned int i = 0; i < nBuffers; i++)
167 memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
168
169 //
170 // This is wrong...needs to handle tails
171 //
172 return discardable;
173}
size_t Process(const ChannelGroup *group, unsigned chans, const float *const *inbuf, float *const *outbuf, float *dummybuf, size_t numSamples)
Worker thread processes part of a batch of samples.
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628

References RealtimeEffectState::Process(), anonymous_namespace{NoteTrack.cpp}::swap(), and VisitGroup().

Referenced by RealtimeEffects::ProcessingScope::Process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProcessEnd()

void RealtimeEffectManager::ProcessEnd ( bool  suspended)
privatenoexcept

Definition at line 178 of file RealtimeEffectManager.cpp.

179{
180 // Can be suspended because of the audio stream being paused or because
181 // effects have been suspended.
182 VisitAll([suspended](RealtimeEffectState &state, bool){
183 state.ProcessEnd();
184 });
185}
bool ProcessEnd()
Worker thread finishes a batch of samples.

References RealtimeEffectState::ProcessEnd().

Referenced by RealtimeEffects::ProcessingScope::~ProcessingScope().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProcessStart()

void RealtimeEffectManager::ProcessStart ( bool  suspended)
private

Definition at line 111 of file RealtimeEffectManager.cpp.

112{
113 // Can be suspended because of the audio stream being paused or because
114 // effects have been suspended.
115 VisitAll([suspended](RealtimeEffectState &state, bool listIsActive){
116 state.ProcessStart(!suspended && listIsActive);
117 });
118}
bool ProcessStart(bool running)
Worker thread begins a batch of samples.

References RealtimeEffectState::ProcessStart(), and VisitAll().

Referenced by RealtimeEffects::ProcessingScope::ProcessingScope().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ RemoveState()

void RealtimeEffectManager::RemoveState ( RealtimeEffects::InitializationScope pScope,
ChannelGroup pGroup,
std::shared_ptr< RealtimeEffectState pState 
)

Main thread removes a global or per-group effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pGroupif null, then state is added to the global list
statethe state to be removed

No effect if realtime is active but scope is not supplied

Definition at line 317 of file RealtimeEffectManager.cpp.

321{
322 auto &states = FindStates(mProject, pGroup);
323
324 // Remove the state from processing (under the lock guard) before finalizing
325 states.RemoveState(pState);
326 if (mActive)
327 pState->Finalize();
328 Publish({
330 pGroup ? pGroup : MasterGroup
331 });
332}

References RealtimeEffectManagerMessage::EffectRemoved, anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), mActive, MasterGroup, mProject, and Observer::Publisher< RealtimeEffectManagerMessage >::Publish().

Referenced by AudioIO::RemoveState().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReplaceState()

std::shared_ptr< RealtimeEffectState > RealtimeEffectManager::ReplaceState ( RealtimeEffects::InitializationScope pScope,
ChannelGroup pGroup,
size_t  index,
const PluginID id 
)

Main thread replaces a global or per-group effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pGroupif null, then state is added to the global list
indexposition in the list to replace; no effect if out of range
ididentifies the effect
Returns
if null, the given id was not found and the old state remains
Postcondition
result: !result || result->GetEffect() != nullptr

Definition at line 294 of file RealtimeEffectManager.cpp.

297{
298 auto &states = FindStates(mProject, pGroup);
299 auto pOldState = states.GetStateAt(index);
300 if (!pOldState)
301 return nullptr;
302 auto pNewState = MakeNewState(pScope, pGroup, id);
303 if (!pNewState)
304 return nullptr;
305
306 // Only now swap the completed state into the list, under a lock guard
307 if (!states.ReplaceState(index, pNewState))
308 return nullptr;
309 if (mActive)
310 pOldState->Finalize();
311 Publish({
313 });
314 return pNewState;
315}

References RealtimeEffectManagerMessage::EffectReplaced, anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), mActive, MakeNewState(), mProject, and Observer::Publisher< RealtimeEffectManagerMessage >::Publish().

Referenced by AudioIO::ReplaceState().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetSuspended()

void RealtimeEffectManager::SetSuspended ( bool  value)
inline

To be called only from main thread.

Each time a processing scope starts in the audio thread, suspension state is tested, and plug-in instances may need to do things in response to the transitions. Playback of samples may continue but with effects switched off in suspended state.

Definition at line 126 of file RealtimeEffectManager.h.

127 { mSuspended.store(value, std::memory_order_relaxed); }

Referenced by Finalize(), and Initialize().

Here is the caller graph for this function:

◆ VisitAll()

template<typename StateVisitor >
void RealtimeEffectManager::VisitAll ( const StateVisitor &  func)
inlineprivate

Visit the per-project states first, then all groups from AddGroup.

Groups are visited in unspecified order

Definition at line 194 of file RealtimeEffectManager.h.

195 {
196 // Call the function for each effect on the master list
198
199 // And all group lists
200 for (auto group : mGroups)
201 RealtimeEffectList::Get(*group).Visit(func);
202 }
void Visit(const StateVisitor &func)
Apply the function to all states sequentially.
static RealtimeEffectList & Get(AudacityProject &project)

References RealtimeEffectList::Get(), and RealtimeEffectList::Visit().

Referenced by Finalize(), Initialize(), and ProcessStart().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ VisitGroup() [1/2]

template<typename StateVisitor >
void RealtimeEffectManager::VisitGroup ( ChannelGroup group,
const StateVisitor &  func 
)
inlineprivate

Visit states for group or for the master when group is null.

Definition at line 176 of file RealtimeEffectManager.h.

177 {
178 if(group == nullptr)
180 else
181 // Call the function for each effect on the group list
182 RealtimeEffectList::Get(*group).Visit(func);
183 }

References RealtimeEffectList::Get(), and RealtimeEffectList::Visit().

Referenced by AddGroup(), Initialize(), and Process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ VisitGroup() [2/2]

template<typename StateVisitor >
void RealtimeEffectManager::VisitGroup ( const ChannelGroup group,
const StateVisitor &  func 
)
inlineprivate

Definition at line 186 of file RealtimeEffectManager.h.

187 {
188 VisitGroup(const_cast<ChannelGroup*>(group), func);
189 }

Member Data Documentation

◆ mActive

bool RealtimeEffectManager::mActive { false }
private

◆ MasterGroup

constexpr ChannelGroup* RealtimeEffectManager::MasterGroup = nullptr
staticconstexpr

Special value used to identify special effects stack applied to every playable track

Definition at line 55 of file RealtimeEffectManager.h.

Referenced by AddState(), Initialize(), MakeNewState(), AudioIO::ProcessPlaybackSlices(), and RemoveState().

◆ mGroups

std::vector<const ChannelGroup *> RealtimeEffectManager::mGroups
private

◆ mProject

AudacityProject& RealtimeEffectManager::mProject
private

◆ mRates

std::unordered_map<const ChannelGroup *, double> RealtimeEffectManager::mRates
private

Definition at line 215 of file RealtimeEffectManager.h.

Referenced by AddGroup(), Finalize(), Initialize(), and MakeNewState().

◆ mSuspended

std::atomic<bool> RealtimeEffectManager::mSuspended { true }
private

Definition at line 207 of file RealtimeEffectManager.h.


The documentation for this class was generated from the following files: