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, Track *pTrack, const PluginID &id)
 Main thread appends a global or per-track effect. More...
 
std::shared_ptr< RealtimeEffectStateReplaceState (RealtimeEffects::InitializationScope *pScope, Track *pTrack, size_t index, const PluginID &id)
 Main thread replaces a global or per-track effect. More...
 
void RemoveState (RealtimeEffects::InitializationScope *pScope, Track *pTrack, std::shared_ptr< RealtimeEffectState > pState)
 Main thread removes a global or per-track effect. More...
 
std::optional< size_t > FindState (Track *pTrack, const std::shared_ptr< RealtimeEffectState > &pState) const
 Report the position of a state in the global or a per-track 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, Track *pLeader, const PluginID &id)
 
void Initialize (RealtimeEffects::InitializationScope &scope, double sampleRate)
 Main thread begins to define a set of tracks for playback. More...
 
void AddTrack (RealtimeEffects::InitializationScope &scope, const Track &track, unsigned chans, float rate)
 
void Finalize () noexcept
 Main thread cleans up after playback. More...
 
void ProcessStart (bool suspended)
 
size_t Process (bool suspended, const Track &track, 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 (Track &leader, const StateVisitor &func)
 Visit the per-project states first, then states for leader if not null. More...
 
template<typename StateVisitor >
void VisitGroup (const Track &leader, const StateVisitor &func)
 
template<typename StateVisitor >
void VisitAll (const StateVisitor &func)
 Visit the per-project states first, then all tracks from AddTrack. More...
 

Private Attributes

AudacityProjectmProject
 
Latency mLatency { 0 }
 
std::atomic< bool > mSuspended { true }
 
bool mActive { false }
 
std::vector< const Track * > mGroupLeaders
 all are non-null More...
 
std::unordered_map< const Track *, 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 47 of file RealtimeEffectManager.h.

Member Typedef Documentation

◆ Latency

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

Definition at line 52 of file RealtimeEffectManager.h.

Constructor & Destructor Documentation

◆ RealtimeEffectManager() [1/2]

RealtimeEffectManager::RealtimeEffectManager ( AudacityProject project)

Definition at line 39 of file RealtimeEffectManager.cpp.

40 : mProject(project)
41{
42}

◆ ~RealtimeEffectManager()

RealtimeEffectManager::~RealtimeEffectManager ( )

Definition at line 44 of file RealtimeEffectManager.cpp.

45{
46}

◆ RealtimeEffectManager() [2/2]

RealtimeEffectManager::RealtimeEffectManager ( const RealtimeEffectManager )
privatedelete

Member Function Documentation

◆ AddState()

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

Main thread appends a global or per-track effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pTrackif 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 278 of file RealtimeEffectManager.cpp.

281{
282 auto [pLeader, states] = FindStates(mProject, pTrack);
283 auto pState = MakeNewState(pScope, pTrack, id);
284 if (!pState)
285 return nullptr;
286
287 // Only now add the completed state to the list, under a lock guard
288 if (!states.AddState(pState))
289 return nullptr;
290 Publish({
292 pLeader ? pLeader->shared_from_this() : nullptr
293 });
294 return pState;
295}
CallbackReturn Publish(const RealtimeEffectManagerMessage &message)
Send a message to connected callbacks.
Definition: Observer.h:207
std::shared_ptr< RealtimeEffectState > MakeNewState(RealtimeEffects::InitializationScope *pScope, Track *pLeader, const PluginID &id)
std::pair< Track *, RealtimeEffectList & > FindStates(AudacityProject &project, Track *pTrack)

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:

◆ AddTrack()

void RealtimeEffectManager::AddTrack ( RealtimeEffects::InitializationScope scope,
const Track track,
unsigned  chans,
float  rate 
)
private

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

Definition at line 73 of file RealtimeEffectManager.cpp.

76{
77 auto leader = *track.GetOwner()->FindLeader(&track);
78 // This should never return a null
79 wxASSERT(leader);
80 mGroupLeaders.push_back(leader);
81 mRates.insert({leader, rate});
82
83 VisitGroup(*leader,
84 [&](RealtimeEffectState & state, bool) {
85 scope.mInstances.push_back(state.AddTrack(*leader, chans, rate));
86 }
87 );
88}
static RealtimeEffectState::EffectFactory::Scope scope
Inject a factory for realtime effects.
Definition: EffectUI.cpp:1560
std::unordered_map< const Track *, double > mRates
std::vector< const Track * > mGroupLeaders
all are non-null
void VisitGroup(Track &leader, const StateVisitor &func)
Visit the per-project states first, then states for leader if not null.
std::shared_ptr< EffectInstance > AddTrack(const Track &track, unsigned chans, float sampleRate)
Main thread sets up this state before adding it to lists.
std::shared_ptr< TrackList > GetOwner() const
Definition: Track.h:410

References RealtimeEffectState::AddTrack(), Track::GetOwner(), mGroupLeaders, mRates, scope, and VisitGroup().

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

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

91{
92 // Reenter suspended state
93 SetSuspended(true);
94
95 // Assume it is now safe to clean up
96 mLatency = std::chrono::microseconds(0);
97
98 VisitAll([](RealtimeEffectState &state, bool){ state.Finalize(); });
99
100 // Reset processor parameters
101 mGroupLeaders.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 tracks from AddTrack.
void SetSuspended(bool value)
To be called only from main thread.
bool Finalize() noexcept
Main thread cleans up playback.

References RealtimeEffectState::Finalize(), mActive, mGroupLeaders, 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 ( Track pTrack,
const std::shared_ptr< RealtimeEffectState > &  pState 
) const

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

Definition at line 337 of file RealtimeEffectManager.cpp.

339{
340 auto [_, states] = FindStates(mProject, pTrack);
341 return states.FindState(pState);
342}
#define _(s)
Definition: Internat.h:73

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().

Here is the call graph for this function:

◆ GetSuspended()

bool RealtimeEffectManager::GetSuspended ( ) const
inline

Definition at line 107 of file RealtimeEffectManager.h.

108 { 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 tracks for playback.

Definition at line 53 of file RealtimeEffectManager.cpp.

55{
56 // (Re)Set processor parameters
57 mRates.clear();
58 mGroupLeaders.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
65 VisitAll([&scope, sampleRate](RealtimeEffectState &state, bool) {
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, mGroupLeaders, mRates, 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,
Track pLeader,
const PluginID id 
)
private

Definition at line 239 of file RealtimeEffectManager.cpp.

242{
243 if (!pScope && mActive)
244 return nullptr;
245 auto pNewState = RealtimeEffectState::make_shared(id);
246 auto &state = *pNewState;
247 if (pScope && mActive) {
248 // Adding a state while playback is in-flight
249 auto pInstance = state.Initialize(pScope->mSampleRate);
250 pScope->mInstances.push_back(pInstance);
251 for (auto &leader : mGroupLeaders) {
252 // Add all tracks to a per-project state, but add only the same track
253 // to a state in the per-track list
254 if (pLeader && pLeader != leader)
255 continue;
256 auto rate = mRates[leader];
257 auto pInstance2 =
258 state.AddTrack(*leader, pScope->mNumPlaybackChannels, rate);
259 if (pInstance2 != pInstance)
260 pScope->mInstances.push_back(pInstance2);
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:522

References mActive, SharedNonInterfering< RealtimeEffectState >::make_shared(), mGroupLeaders, 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 Track track,
float *const *  buffers,
float *const *  scratch,
float *  dummy,
unsigned  nBuffers,
size_t  numSamples 
)
private

Definition at line 124 of file RealtimeEffectManager.cpp.

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

References PackedArray::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 187 of file RealtimeEffectManager.cpp.

188{
189 // Can be suspended because of the audio stream being paused or because effects
190 // have been suspended.
191 VisitAll([suspended](RealtimeEffectState &state, bool){
192 state.ProcessEnd();
193 });
194}
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 effects
114 // 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,
Track pTrack,
std::shared_ptr< RealtimeEffectState pState 
)

Main thread removes a global or per-track effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pTrackif 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 321 of file RealtimeEffectManager.cpp.

324{
325 auto [pLeader, states] = FindStates(mProject, pTrack);
326
327 // Remove the state from processing (under the lock guard) before finalizing
328 states.RemoveState(pState);
329 if (mActive)
330 pState->Finalize();
331 Publish({
333 pLeader ? pLeader->shared_from_this() : nullptr
334 });
335}

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,
Track pTrack,
size_t  index,
const PluginID id 
)

Main thread replaces a global or per-track effect.

Parameters
pScopeif realtime is active but scope is absent, there is no effect
pTrackif 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 297 of file RealtimeEffectManager.cpp.

300{
301 auto [pLeader, states] = FindStates(mProject, pTrack);
302 auto pOldState = states.GetStateAt(index);
303 if (!pOldState)
304 return nullptr;
305 auto pNewState = MakeNewState(pScope, pTrack, id);
306 if (!pNewState)
307 return nullptr;
308
309 // Only now swap the completed state into the list, under a lock guard
310 if (!states.ReplaceState(index, pNewState))
311 return nullptr;
312 if (mActive)
313 pOldState->Finalize();
314 Publish({
316 pLeader ? pLeader->shared_from_this() : nullptr
317 });
318 return pNewState;
319}

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

118 { 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 tracks from AddTrack.

Tracks are visited in unspecified order

Definition at line 186 of file RealtimeEffectManager.h.

187 {
188 // Call the function for each effect on the master list
190
191 // And all track lists
192 for (auto leader : mGroupLeaders)
193 RealtimeEffectList::Get(*leader).Visit(func);
194 }
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 ( const Track leader,
const StateVisitor &  func 
)
inlineprivate

Definition at line 174 of file RealtimeEffectManager.h.

175 {
176 // Call the function for each effect on the master list
178
179 // Call the function for each effect on the track list
180 RealtimeEffectList::Get(leader).Visit(func);
181 }

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

Here is the call graph for this function:

◆ VisitGroup() [2/2]

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

Visit the per-project states first, then states for leader if not null.

Definition at line 164 of file RealtimeEffectManager.h.

165 {
166 // Call the function for each effect on the master list
168
169 // Call the function for each effect on the track list
170 RealtimeEffectList::Get(leader).Visit(func);
171 }

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

Referenced by AddTrack(), and Process().

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

Member Data Documentation

◆ mActive

bool RealtimeEffectManager::mActive { false }
private

◆ mGroupLeaders

std::vector<const Track *> RealtimeEffectManager::mGroupLeaders
private

◆ mLatency

Latency RealtimeEffectManager::mLatency { 0 }
private

Definition at line 197 of file RealtimeEffectManager.h.

Referenced by Finalize(), and Process().

◆ mProject

AudacityProject& RealtimeEffectManager::mProject
private

◆ mRates

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

Definition at line 207 of file RealtimeEffectManager.h.

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

◆ mSuspended

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

Definition at line 199 of file RealtimeEffectManager.h.


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