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, 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, Track &track, float *const *buffers, float *const *scratch, 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 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< Track * > mGroupLeaders
 all are non-null More...
 
std::unordered_map< 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 40 of file RealtimeEffectManager.cpp.

41 : mProject(project)
42{
43}

◆ ~RealtimeEffectManager()

RealtimeEffectManager::~RealtimeEffectManager ( )

Definition at line 45 of file RealtimeEffectManager.cpp.

46{
47}

◆ 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,
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 74 of file RealtimeEffectManager.cpp.

77{
78 auto leader = *track.GetOwner()->FindLeader(&track);
79 // This should never return a null
80 wxASSERT(leader);
81 mGroupLeaders.push_back(leader);
82 mRates.insert({leader, rate});
83
84 VisitGroup(*leader,
85 [&](RealtimeEffectState & state, bool) {
86 scope.mInstances.push_back(state.AddTrack(*leader, chans, rate));
87 }
88 );
89}
static TransactionScope::Factory::Scope scope
std::unordered_map< Track *, double > mRates
std::vector< 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(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:409

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

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

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

36{
37 return Get(const_cast<AudacityProject &>(project));
38}
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
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 54 of file RealtimeEffectManager.cpp.

56{
57 // (Re)Set processor parameters
58 mRates.clear();
59 mGroupLeaders.clear();
60
61 // RealtimeAdd/RemoveEffect() needs to know when we're active so it can
62 // initialize newly added effects
63 mActive = true;
64
65 // Tell each state to get ready for action
66 VisitAll([&scope, sampleRate](RealtimeEffectState &state, bool) {
67 scope.mInstances.push_back(state.Initialize(sampleRate));
68 });
69
70 // Leave suspended state
71 SetSuspended(false);
72}
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 49 of file RealtimeEffectManager.cpp.

50{
51 return mActive;
52}

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

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,
Track track,
float *const *  buffers,
float *const *  scratch,
unsigned  nBuffers,
size_t  numSamples 
)
private

Definition at line 125 of file RealtimeEffectManager.cpp.

128{
129 // Can be suspended because of the audio stream being paused or because effects
130 // have been suspended, so allow the samples to pass as-is.
131 if (suspended)
132 return 0;
133
134 // Remember when we started so we can calculate the amount of latency we
135 // are introducing
136 auto start = std::chrono::steady_clock::now();
137
138 // Allocate the in and out buffer arrays
139 const auto ibuf =
140 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
141 const auto obuf =
142 static_cast<float **>(alloca(nBuffers * sizeof(float *)));
143
144 // And populate the input with the buffers we've been given while allocating
145 // NEW output buffers
146 for (unsigned int i = 0; i < nBuffers; i++) {
147 ibuf[i] = buffers[i];
148 obuf[i] = scratch[i];
149 }
150
151 // Now call each effect in the chain while swapping buffer pointers to feed the
152 // output of one effect as the input to the next effect
153 // Tracks how many processors were called
154 size_t called = 0;
155 size_t discardable = 0;
156 VisitGroup(track,
157 [&](RealtimeEffectState &state, bool)
158 {
159 discardable += state.Process(track, nBuffers, ibuf, obuf, 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(Track &track, unsigned chans, const float *const *inbuf, float *const *outbuf, 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:753

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

113{
114 // Can be suspended because of the audio stream being paused or because effects
115 // have been suspended.
116 VisitAll([suspended](RealtimeEffectState &state, bool listIsActive){
117 state.ProcessStart(!suspended && listIsActive);
118 });
119}
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 176 of file RealtimeEffectManager.h.

177 {
178 // Call the function for each effect on the master list
180
181 // And all track lists
182 for (auto leader : mGroupLeaders)
183 RealtimeEffectList::Get(*leader).Visit(func);
184 }
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()

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<Track *> RealtimeEffectManager::mGroupLeaders
private

◆ mLatency

Latency RealtimeEffectManager::mLatency { 0 }
private

Definition at line 187 of file RealtimeEffectManager.h.

Referenced by Finalize(), and Process().

◆ mProject

AudacityProject& RealtimeEffectManager::mProject
private

◆ mRates

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

Definition at line 197 of file RealtimeEffectManager.h.

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

◆ mSuspended

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

Definition at line 189 of file RealtimeEffectManager.h.


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