Audacity 3.2.0
Classes | Public Types | Public Member Functions | Static Public Member Functions | 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)
 

Private Member Functions

std::shared_ptr< RealtimeEffectStateMakeNewState (RealtimeEffects::InitializationScope *pScope, ChannelGroup *pGroup, const PluginID &id)
 
void Initialize (RealtimeEffects::InitializationScope &scope, 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 the per-project states first, then states for group. 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
 
Latency mLatency { 0 }
 
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

- Static Public Attributes inherited from Observer::Publisher< RealtimeEffectManagerMessage >
static constexpr bool notifies_all
 
- 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 53 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 73 of file RealtimeEffectManager.cpp.

76{
77 mGroups.push_back(&group);
78 mRates.insert({&group, rate});
79
80 VisitGroup(group,
81 [&](RealtimeEffectState & state, bool) {
82 scope.mInstances.push_back(state.AddGroup(group, chans, rate));
83 }
84 );
85}
void VisitGroup(ChannelGroup &group, const StateVisitor &func)
Visit the per-project states first, then states for group.
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 273 of file RealtimeEffectManager.cpp.

276{
277 auto &states = FindStates(mProject, pGroup);
278 auto pState = MakeNewState(pScope, pGroup, id);
279 if (!pState)
280 return nullptr;
281
282 // Only now add the completed state to the list, under a lock guard
283 if (!states.AddState(pState))
284 return nullptr;
285 Publish({
287 pGroup ? pGroup : nullptr
288 });
289 return pState;
290}
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)
RealtimeEffectList & FindStates(AudacityProject &project, ChannelGroup *pGroup)

References RealtimeEffectManagerMessage::EffectAdded, anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), MakeNewState(), 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 87 of file RealtimeEffectManager.cpp.

88{
89 // Reenter suspended state
90 SetSuspended(true);
91
92 // Assume it is now safe to clean up
93 mLatency = std::chrono::microseconds(0);
94
95 VisitAll([](RealtimeEffectState &state, bool){ state.Finalize(); });
96
97 // Reset processor parameters
98 mGroups.clear();
99 mRates.clear();
100
101 // No longer active
102 mActive = false;
103}
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, mLatency, 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 332 of file RealtimeEffectManager.cpp.

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

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 112 of file RealtimeEffectManager.h.

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

◆ Initialize()

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

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

Definition at line 53 of file RealtimeEffectManager.cpp.

55{
56 // (Re)Set processor parameters
57 mRates.clear();
58 mGroups.clear();
59
60 // RealtimeAdd/RemoveEffect() needs to know when we're active so it can
61 // initialize newly added effects
62 mActive = true;
63
64 // Tell each state to get ready for action
66 scope.mInstances.push_back(state.Initialize(sampleRate));
67 });
68
69 // Leave suspended state
70 SetSuspended(false);
71}
std::shared_ptr< EffectInstance > Initialize(double rate)
Main thread sets up for playback.

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

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 237 of file RealtimeEffectManager.cpp.

240{
241 if (!pScope && mActive)
242 return nullptr;
243 auto pNewState = RealtimeEffectState::make_shared(id);
244 auto &state = *pNewState;
245 if (pScope && mActive) {
246 // Adding a state while playback is in-flight
247 auto pInstance = state.Initialize(pScope->mSampleRate);
248 pScope->mInstances.push_back(pInstance);
249 for (const auto group : mGroups) {
250 // Add all groups to a per-project state, but add only the same
251 // group to a state in the per-group list
252 if (pGroup && pGroup != group)
253 continue;
254 auto rate = mRates[group];
255 auto pInstance2 =
256 state.AddGroup(*group, pScope->mNumPlaybackChannels, rate);
257 if (pInstance2 != pInstance)
258 pScope->mInstances.push_back(pInstance2);
259 }
260 }
261 return pNewState;
262}
std::vector< std::shared_ptr< EffectInstance > > mInstances
static std::shared_ptr< RealtimeEffectState > make_shared(Args &&...args)
Definition: MemoryX.h:299

References mActive, SharedNonInterfering< RealtimeEffectState >::make_shared(), 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 121 of file RealtimeEffectManager.cpp.

125{
126 // Can be suspended because of the audio stream being paused or because
127 // effects have been suspended, so allow the samples to pass as-is.
128 if (suspended)
129 return 0;
130
131 // Remember when we started so we can calculate the amount of latency we
132 // are introducing
133 auto start = std::chrono::steady_clock::now();
134
135 // Allocate the in and out buffer arrays
136 const auto ibuf =
137 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
138 const auto obuf =
139 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
140
141 // And populate the input with the buffers we've been given while allocating
142 // NEW output buffers
143 for (unsigned int i = 0; i < nBuffers; i++) {
144 ibuf[i] = buffers[i];
145 obuf[i] = scratch[i];
146 }
147
148 // Now call each effect in the chain while swapping buffer pointers to feed
149 // the output of one effect as the input to the next effect
150 // Tracks how many processors were called
151 size_t called = 0;
152 size_t discardable = 0;
153 VisitGroup(group,
154 [&](RealtimeEffectState &state, bool)
155 {
156 discardable +=
157 state.Process(group, nBuffers, ibuf, obuf, dummy, numSamples);
158 for (auto i = 0; i < nBuffers; ++i)
159 std::swap(ibuf[i], obuf[i]);
160 called++;
161 }
162 );
163
164 // Once we're done, we might wind up with the last effect storing its results
165 // in the temporary buffers. If that's the case, we need to copy it over to
166 // the caller's buffers. This happens when the number of effects processed
167 // is odd.
168 if (called & 1)
169 for (unsigned int i = 0; i < nBuffers; i++)
170 memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
171
172 // Remember the latency
173 auto end = std::chrono::steady_clock::now();
174 mLatency = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
175
176 //
177 // This is wrong...needs to handle tails
178 //
179 return discardable;
180}
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
const char * end(const char *str) noexcept
Definition: StringUtils.h:106

References details::end(), mLatency, 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 185 of file RealtimeEffectManager.cpp.

186{
187 // Can be suspended because of the audio stream being paused or because
188 // effects have been suspended.
189 VisitAll([suspended](RealtimeEffectState &state, bool){
190 state.ProcessEnd();
191 });
192}
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 108 of file RealtimeEffectManager.cpp.

109{
110 // Can be suspended because of the audio stream being paused or because
111 // effects have been suspended.
112 VisitAll([suspended](RealtimeEffectState &state, bool listIsActive){
113 state.ProcessStart(!suspended && listIsActive);
114 });
115}
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 315 of file RealtimeEffectManager.cpp.

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

References RealtimeEffectManagerMessage::EffectRemoved, anonymous_namespace{RealtimeEffectManager.cpp}::FindStates(), mActive, 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 292 of file RealtimeEffectManager.cpp.

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

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 122 of file RealtimeEffectManager.h.

123 { 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 the per-project states first, then states for group.

Definition at line 171 of file RealtimeEffectManager.h.

172 {
173 // Call the function for each effect on the master list
175
176 // Call the function for each effect on the group list
177 RealtimeEffectList::Get(group).Visit(func);
178 }

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

Referenced by AddGroup(), 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 181 of file RealtimeEffectManager.h.

183 {
184 // Call the function for each effect on the master list
186
187 // Call the function for each effect on the group list
188 RealtimeEffectList::Get(group).Visit(func);
189 }

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

Here is the call graph for this function:

Member Data Documentation

◆ mActive

bool RealtimeEffectManager::mActive { false }
private

◆ mGroups

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

◆ mLatency

Latency RealtimeEffectManager::mLatency { 0 }
private

Definition at line 205 of file RealtimeEffectManager.h.

Referenced by Finalize(), and Process().

◆ 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: