Audacity  3.0.3
RealtimeEffectManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  RealtimeEffectManager.cpp
6 
7  Paul Licameli split from EffectManager.cpp
8 
9  **********************************************************************/
10 
11 
12 #include "RealtimeEffectManager.h"
13 
14 #include "EffectInterface.h"
15 #include <memory>
16 
17 #include <atomic>
18 #include <wx/time.h>
19 
21 {
22 public:
23  explicit RealtimeEffectState( EffectClientInterface &effect );
24 
26 
27  bool RealtimeSuspend();
28  bool RealtimeResume();
29  bool RealtimeAddProcessor(int group, unsigned chans, float rate);
30  size_t RealtimeProcess(int group,
31  unsigned chans, float **inbuf, float **outbuf, size_t numSamples);
32  bool IsRealtimeActive();
33 
34 private:
36 
37  std::vector<int> mGroupProcessor;
39 
40  std::atomic<int> mRealtimeSuspendCount{ 1 }; // Effects are initially suspended
41 };
42 
44 {
45  static RealtimeEffectManager rem;
46  return rem;
47 }
48 
50 {
51  mRealtimeLock.Enter();
52  mRealtimeActive = false;
53  mRealtimeSuspended = true;
54  mRealtimeLatency = 0;
55  mRealtimeLock.Leave();
56 }
57 
59 {
60 }
61 
62 #if defined(EXPERIMENTAL_EFFECTS_RACK)
63 void RealtimeEffectManager::RealtimeSetEffects(const EffectArray & effects)
64 {
65  // Block RealtimeProcess()
67 
68  decltype( mStates ) newStates;
69  auto begin = mStates.begin(), end = mStates.end();
70  for ( auto pEffect : effects ) {
71  auto found = std::find_if( begin, end,
72  [=]( const decltype( mStates )::value_type &state ){
73  return state && &state->GetEffect() == pEffect;
74  }
75  );
76  if ( found == end ) {
77  // Tell New effect to get ready
78  pEffect->RealtimeInitialize();
79  newStates.emplace_back(
80  std::make_unique< RealtimeEffectState >( *pEffect ) );
81  }
82  else {
83  // Preserve state for effect that remains in the chain
84  newStates.emplace_back( std::move( *found ) );
85  }
86  }
87 
88  // Remaining states that were not moved need to clean up
89  for ( auto &state : mStates ) {
90  if ( state )
91  state->GetEffect().RealtimeFinalize();
92  }
93 
94  // Get rid of the old chain
95  // And install the NEW one
96  mStates.swap( newStates );
97 
98  // Allow RealtimeProcess() to, well, process
100 }
101 #endif
102 
104 {
105  return mStates.size() != 0;
106 }
107 
109 {
110  return mRealtimeSuspended;
111 }
112 
114 {
115  // Block RealtimeProcess()
116  RealtimeSuspend();
117 
118  // Add to list of active effects
119  mStates.emplace_back( std::make_unique< RealtimeEffectState >( *effect ) );
120  auto &state = mStates.back();
121 
122  // Initialize effect if realtime is already active
123  if (mRealtimeActive)
124  {
125  // Initialize realtime processing
126  effect->RealtimeInitialize();
127 
128  // Add the required processors
129  for (size_t i = 0, cnt = mRealtimeChans.size(); i < cnt; i++)
130  {
131  state->RealtimeAddProcessor(i, mRealtimeChans[i], mRealtimeRates[i]);
132  }
133  }
134 
135 
136  // Allow RealtimeProcess() to, well, process
137  RealtimeResume();
138 }
139 
141 {
142  // Block RealtimeProcess()
143  RealtimeSuspend();
144 
145  if (mRealtimeActive)
146  {
147  // Cleanup realtime processing
148  effect->RealtimeFinalize();
149  }
150 
151  // Remove from list of active effects
152  auto end = mStates.end();
153  auto found = std::find_if( mStates.begin(), end,
154  [&](const decltype(mStates)::value_type &state){
155  return &state->GetEffect() == effect;
156  }
157  );
158  if (found != end)
159  mStates.erase(found);
160 
161  // Allow RealtimeProcess() to, well, process
162  RealtimeResume();
163 }
164 
166 {
167  // The audio thread should not be running yet, but protect anyway
168  RealtimeSuspend();
169 
170  // (Re)Set processor parameters
171  mRealtimeChans.clear();
172  mRealtimeRates.clear();
173 
174  // RealtimeAdd/RemoveEffect() needs to know when we're active so it can
175  // initialize newly added effects
176  mRealtimeActive = true;
177 
178  // Tell each effect to get ready for action
179  for (auto &state : mStates) {
180  state->GetEffect().SetSampleRate(rate);
181  state->GetEffect().RealtimeInitialize();
182  }
183 
184  // Get things moving
185  RealtimeResume();
186 }
187 
188 void RealtimeEffectManager::RealtimeAddProcessor(int group, unsigned chans, float rate)
189 {
190  for (auto &state : mStates)
191  state->RealtimeAddProcessor(group, chans, rate);
192 
193  mRealtimeChans.push_back(chans);
194  mRealtimeRates.push_back(rate);
195 }
196 
198 {
199  // Make sure nothing is going on
200  RealtimeSuspend();
201 
202  // It is now safe to clean up
203  mRealtimeLatency = 0;
204 
205  // Tell each effect to clean up as well
206  for (auto &state : mStates)
207  state->GetEffect().RealtimeFinalize();
208 
209  // Reset processor parameters
210  mRealtimeChans.clear();
211  mRealtimeRates.clear();
212 
213  // No longer active
214  mRealtimeActive = false;
215 }
216 
218 {
219  mRealtimeLock.Enter();
220 
221  // Already suspended...bail
222  if (mRealtimeSuspended)
223  {
224  mRealtimeLock.Leave();
225  return;
226  }
227 
228  // Show that we aren't going to be doing anything
229  mRealtimeSuspended = true;
230 
231  // And make sure the effects don't either
232  for (auto &state : mStates)
233  state->RealtimeSuspend();
234 
235  mRealtimeLock.Leave();
236 }
237 
239 {
240  auto begin = mStates.begin(), end = mStates.end();
241  auto found = std::find_if( begin, end,
242  [&effect]( const decltype( mStates )::value_type &state ){
243  return state && &state->GetEffect() == &effect;
244  }
245  );
246  if ( found != end )
247  (*found)->RealtimeSuspend();
248 }
249 
251 {
252  mRealtimeLock.Enter();
253 
254  // Already running...bail
255  if (!mRealtimeSuspended)
256  {
257  mRealtimeLock.Leave();
258  return;
259  }
260 
261  // Tell the effects to get ready for more action
262  for (auto &state : mStates)
263  state->RealtimeResume();
264 
265  // And we should too
266  mRealtimeSuspended = false;
267 
268  mRealtimeLock.Leave();
269 }
270 
272 {
273  auto begin = mStates.begin(), end = mStates.end();
274  auto found = std::find_if( begin, end,
275  [&effect]( const decltype( mStates )::value_type &state ){
276  return state && &state->GetEffect() == &effect;
277  }
278  );
279  if ( found != end )
280  (*found)->RealtimeResume();
281 }
282 
283 //
284 // This will be called in a different thread than the main GUI thread.
285 //
287 {
288  // Protect ourselves from the main thread
289  mRealtimeLock.Enter();
290 
291  // Can be suspended because of the audio stream being paused or because effects
292  // have been suspended.
293  if (!mRealtimeSuspended)
294  {
295  for (auto &state : mStates)
296  {
297  if (state->IsRealtimeActive())
298  state->GetEffect().RealtimeProcessStart();
299  }
300  }
301 
302  mRealtimeLock.Leave();
303 }
304 
305 //
306 // This will be called in a different thread than the main GUI thread.
307 //
308 size_t RealtimeEffectManager::RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
309 {
310  // Protect ourselves from the main thread
311  mRealtimeLock.Enter();
312 
313  // Can be suspended because of the audio stream being paused or because effects
314  // have been suspended, so allow the samples to pass as-is.
315  if (mRealtimeSuspended || mStates.empty())
316  {
317  mRealtimeLock.Leave();
318  return numSamples;
319  }
320 
321  // Remember when we started so we can calculate the amount of latency we
322  // are introducing
323  wxMilliClock_t start = wxGetUTCTimeMillis();
324 
325  // Allocate the in/out buffer arrays
326  float **ibuf = (float **) alloca(chans * sizeof(float *));
327  float **obuf = (float **) alloca(chans * sizeof(float *));
328 
329  // And populate the input with the buffers we've been given while allocating
330  // NEW output buffers
331  for (unsigned int i = 0; i < chans; i++)
332  {
333  ibuf[i] = buffers[i];
334  obuf[i] = (float *) alloca(numSamples * sizeof(float));
335  }
336 
337  // Now call each effect in the chain while swapping buffer pointers to feed the
338  // output of one effect as the input to the next effect
339  size_t called = 0;
340  for (auto &state : mStates)
341  {
342  if (state->IsRealtimeActive())
343  {
344  state->RealtimeProcess(group, chans, ibuf, obuf, numSamples);
345  called++;
346  }
347 
348  for (unsigned int j = 0; j < chans; j++)
349  {
350  float *temp;
351  temp = ibuf[j];
352  ibuf[j] = obuf[j];
353  obuf[j] = temp;
354  }
355  }
356 
357  // Once we're done, we might wind up with the last effect storing its results
358  // in the temporary buffers. If that's the case, we need to copy it over to
359  // the caller's buffers. This happens when the number of effects processed
360  // is odd.
361  if (called & 1)
362  {
363  for (unsigned int i = 0; i < chans; i++)
364  {
365  memcpy(buffers[i], ibuf[i], numSamples * sizeof(float));
366  }
367  }
368 
369  // Remember the latency
370  mRealtimeLatency = (int) (wxGetUTCTimeMillis() - start).GetValue();
371 
372  mRealtimeLock.Leave();
373 
374  //
375  // This is wrong...needs to handle tails
376  //
377  return numSamples;
378 }
379 
380 //
381 // This will be called in a different thread than the main GUI thread.
382 //
384 {
385  // Protect ourselves from the main thread
386  mRealtimeLock.Enter();
387 
388  // Can be suspended because of the audio stream being paused or because effects
389  // have been suspended.
390  if (!mRealtimeSuspended)
391  {
392  for (auto &state : mStates)
393  {
394  if (state->IsRealtimeActive())
395  state->GetEffect().RealtimeProcessEnd();
396  }
397  }
398 
399  mRealtimeLock.Leave();
400 }
401 
403 {
404  return mRealtimeLatency;
405 }
406 
408  : mEffect{ effect }
409 {
410 }
411 
413 {
414  auto result = mEffect.RealtimeSuspend();
415  if ( result ) {
417  }
418  return result;
419 }
420 
422 {
423  auto result = mEffect.RealtimeResume();
424  if ( result ) {
426  }
427  return result;
428 }
429 
430 // RealtimeAddProcessor and RealtimeProcess use the same method of
431 // determining the current processor index, so updates to one should
432 // be reflected in the other.
433 bool RealtimeEffectState::RealtimeAddProcessor(int group, unsigned chans, float rate)
434 {
435  auto ichans = chans;
436  auto ochans = chans;
437  auto gchans = chans;
438 
439  // Reset processor index
440  if (group == 0)
441  {
442  mCurrentProcessor = 0;
443  mGroupProcessor.clear();
444  }
445 
446  // Remember the processor starting index
448 
449  const auto numAudioIn = mEffect.GetAudioInCount();
450  const auto numAudioOut = mEffect.GetAudioOutCount();
451 
452  // Call the client until we run out of input or output channels
453  while (ichans > 0 && ochans > 0)
454  {
455  // If we don't have enough input channels to accommodate the client's
456  // requirements, then we replicate the input channels until the
457  // client's needs are met.
458  if (ichans < numAudioIn)
459  {
460  // All input channels have been consumed
461  ichans = 0;
462  }
463  // Otherwise fulfill the client's needs with as many input channels as possible.
464  // After calling the client with this set, we will loop back up to process more
465  // of the input/output channels.
466  else if (ichans >= numAudioIn)
467  {
468  gchans = numAudioIn;
469  ichans -= gchans;
470  }
471 
472  // If we don't have enough output channels to accommodate the client's
473  // requirements, then we provide all of the output channels and fulfill
474  // the client's needs with dummy buffers. These will just get tossed.
475  if (ochans < numAudioOut)
476  {
477  // All output channels have been consumed
478  ochans = 0;
479  }
480  // Otherwise fulfill the client's needs with as many output channels as possible.
481  // After calling the client with this set, we will loop back up to process more
482  // of the input/output channels.
483  else if (ochans >= numAudioOut)
484  {
485  ochans -= numAudioOut;
486  }
487 
488  // Add a NEW processor
489  mEffect.RealtimeAddProcessor(gchans, rate);
490 
491  // Bump to next processor
493  }
494 
495  return true;
496 }
497 
498 // RealtimeAddProcessor and RealtimeProcess use the same method of
499 // determining the current processor group, so updates to one should
500 // be reflected in the other.
502  unsigned chans,
503  float **inbuf,
504  float **outbuf,
505  size_t numSamples)
506 {
507  //
508  // The caller passes the number of channels to process and specifies
509  // the number of input and output buffers. There will always be the
510  // same number of output buffers as there are input buffers.
511  //
512  // Effects always require a certain number of input and output buffers,
513  // so if the number of channels we're currently processing are different
514  // than what the effect expects, then we use a few methods of satisfying
515  // the effects requirements.
516  const auto numAudioIn = mEffect.GetAudioInCount();
517  const auto numAudioOut = mEffect.GetAudioOutCount();
518 
519  float **clientIn = (float **) alloca(numAudioIn * sizeof(float *));
520  float **clientOut = (float **) alloca(numAudioOut * sizeof(float *));
521  float *dummybuf = (float *) alloca(numSamples * sizeof(float));
522  decltype(numSamples) len = 0;
523  auto ichans = chans;
524  auto ochans = chans;
525  auto gchans = chans;
526  unsigned indx = 0;
527  unsigned ondx = 0;
528 
529  int processor = mGroupProcessor[group];
530 
531  // Call the client until we run out of input or output channels
532  while (ichans > 0 && ochans > 0)
533  {
534  // If we don't have enough input channels to accommodate the client's
535  // requirements, then we replicate the input channels until the
536  // client's needs are met.
537  if (ichans < numAudioIn)
538  {
539  for (size_t i = 0; i < numAudioIn; i++)
540  {
541  if (indx == ichans)
542  {
543  indx = 0;
544  }
545  clientIn[i] = inbuf[indx++];
546  }
547 
548  // All input channels have been consumed
549  ichans = 0;
550  }
551  // Otherwise fulfill the client's needs with as many input channels as possible.
552  // After calling the client with this set, we will loop back up to process more
553  // of the input/output channels.
554  else if (ichans >= numAudioIn)
555  {
556  gchans = 0;
557  for (size_t i = 0; i < numAudioIn; i++, ichans--, gchans++)
558  {
559  clientIn[i] = inbuf[indx++];
560  }
561  }
562 
563  // If we don't have enough output channels to accommodate the client's
564  // requirements, then we provide all of the output channels and fulfill
565  // the client's needs with dummy buffers. These will just get tossed.
566  if (ochans < numAudioOut)
567  {
568  for (size_t i = 0; i < numAudioOut; i++)
569  {
570  if (i < ochans)
571  {
572  clientOut[i] = outbuf[i];
573  }
574  else
575  {
576  clientOut[i] = dummybuf;
577  }
578  }
579 
580  // All output channels have been consumed
581  ochans = 0;
582  }
583  // Otherwise fulfill the client's needs with as many output channels as possible.
584  // After calling the client with this set, we will loop back up to process more
585  // of the input/output channels.
586  else if (ochans >= numAudioOut)
587  {
588  for (size_t i = 0; i < numAudioOut; i++, ochans--)
589  {
590  clientOut[i] = outbuf[ondx++];
591  }
592  }
593 
594  // Finally call the plugin to process the block
595  len = 0;
596  const auto blockSize = mEffect.GetBlockSize();
597  for (decltype(numSamples) block = 0; block < numSamples; block += blockSize)
598  {
599  auto cnt = std::min(numSamples - block, blockSize);
600  len += mEffect.RealtimeProcess(processor, clientIn, clientOut, cnt);
601 
602  for (size_t i = 0 ; i < numAudioIn; i++)
603  {
604  clientIn[i] += cnt;
605  }
606 
607  for (size_t i = 0 ; i < numAudioOut; i++)
608  {
609  clientOut[i] += cnt;
610  }
611  }
612 
613  // Bump to next processor
614  processor++;
615  }
616 
617  return len;
618 }
619 
621 {
622  return mRealtimeSuspendCount == 0;
623 }
RealtimeEffectManager::mRealtimeChans
std::vector< unsigned > mRealtimeChans
Definition: RealtimeEffectManager.h:56
EffectClientInterface::RealtimeAddProcessor
virtual bool RealtimeAddProcessor(unsigned numChannels, float sampleRate)=0
RealtimeEffectManager::RealtimeSetEffects
void RealtimeSetEffects(const EffectArray &mActive)
EffectClientInterface::RealtimeInitialize
virtual bool RealtimeInitialize()=0
RealtimeEffectState::IsRealtimeActive
bool IsRealtimeActive()
Definition: RealtimeEffectManager.cpp:620
RealtimeEffectState::RealtimeAddProcessor
bool RealtimeAddProcessor(int group, unsigned chans, float rate)
Definition: RealtimeEffectManager.cpp:433
EffectInterface.h
EffectClientInterface::RealtimeFinalize
virtual bool RealtimeFinalize()=0
RealtimeEffectManager::RealtimeInitialize
void RealtimeInitialize(double rate)
Definition: RealtimeEffectManager.cpp:165
RealtimeEffectManager::mRealtimeRates
std::vector< double > mRealtimeRates
Definition: RealtimeEffectManager.h:57
RealtimeEffectManager::RealtimeResumeOne
void RealtimeResumeOne(EffectClientInterface &effect)
Definition: RealtimeEffectManager.cpp:271
RealtimeEffectManager::RealtimeSuspendOne
void RealtimeSuspendOne(EffectClientInterface &effect)
Definition: RealtimeEffectManager.cpp:238
RealtimeEffectState
Definition: RealtimeEffectManager.cpp:21
RealtimeEffectManager::RealtimeProcess
size_t RealtimeProcess(int group, unsigned chans, float **buffers, size_t numSamples)
Definition: RealtimeEffectManager.cpp:308
EffectClientInterface::RealtimeSuspend
virtual bool RealtimeSuspend()=0
EffectClientInterface::GetAudioOutCount
virtual unsigned GetAudioOutCount()=0
RealtimeEffectManager::mRealtimeLock
wxCriticalSection mRealtimeLock
Definition: RealtimeEffectManager.h:51
RealtimeEffectState::GetEffect
EffectClientInterface & GetEffect() const
Definition: RealtimeEffectManager.cpp:25
RealtimeEffectManager.h
RealtimeEffectState::mCurrentProcessor
int mCurrentProcessor
Definition: RealtimeEffectManager.cpp:38
RealtimeEffectManager::RealtimeIsSuspended
bool RealtimeIsSuspended()
Definition: RealtimeEffectManager.cpp:108
RealtimeEffectManager::RealtimeFinalize
void RealtimeFinalize()
Definition: RealtimeEffectManager.cpp:197
RealtimeEffectManager::RealtimeAddEffect
void RealtimeAddEffect(EffectClientInterface *effect)
Definition: RealtimeEffectManager.cpp:113
RealtimeEffectState::RealtimeSuspend
bool RealtimeSuspend()
Definition: RealtimeEffectManager.cpp:412
RealtimeEffectManager::RealtimeEffectManager
RealtimeEffectManager()
Definition: RealtimeEffectManager.cpp:49
RealtimeEffectManager::RealtimeRemoveEffect
void RealtimeRemoveEffect(EffectClientInterface *effect)
Definition: RealtimeEffectManager.cpp:140
RealtimeEffectManager::GetRealtimeLatency
int GetRealtimeLatency()
Definition: RealtimeEffectManager.cpp:402
RealtimeEffectManager::RealtimeIsActive
bool RealtimeIsActive()
Definition: RealtimeEffectManager.cpp:103
RealtimeEffectManager::Get
static RealtimeEffectManager & Get()
Definition: RealtimeEffectManager.cpp:43
RealtimeEffectManager::RealtimeSuspend
void RealtimeSuspend()
Definition: RealtimeEffectManager.cpp:217
EffectClientInterface
EffectClientInterface provides the ident interface to Effect, and is what makes Effect into a plug-in...
Definition: EffectInterface.h:185
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
RealtimeEffectManager::RealtimeProcessStart
void RealtimeProcessStart()
Definition: RealtimeEffectManager.cpp:286
RealtimeEffectState::mGroupProcessor
std::vector< int > mGroupProcessor
Definition: RealtimeEffectManager.cpp:37
RealtimeEffectManager
Definition: RealtimeEffectManager.h:22
RealtimeEffectManager::mStates
std::vector< std::unique_ptr< RealtimeEffectState > > mStates
Definition: RealtimeEffectManager.h:52
EffectClientInterface::GetAudioInCount
virtual unsigned GetAudioInCount()=0
RealtimeEffectState::mEffect
EffectClientInterface & mEffect
Definition: RealtimeEffectManager.cpp:35
RealtimeEffectManager::RealtimeResume
void RealtimeResume()
Definition: RealtimeEffectManager.cpp:250
EffectClientInterface::GetBlockSize
virtual size_t GetBlockSize() const =0
RealtimeEffectState::RealtimeResume
bool RealtimeResume()
Definition: RealtimeEffectManager.cpp:421
RealtimeEffectManager::RealtimeProcessEnd
void RealtimeProcessEnd()
Definition: RealtimeEffectManager.cpp:383
RealtimeEffectManager::mRealtimeLatency
int mRealtimeLatency
Definition: RealtimeEffectManager.h:53
RealtimeEffectManager::mRealtimeActive
bool mRealtimeActive
Definition: RealtimeEffectManager.h:55
EffectClientInterface::RealtimeProcess
virtual size_t RealtimeProcess(int group, float **inBuf, float **outBuf, size_t numSamples)=0
RealtimeEffectManager::mRealtimeSuspended
bool mRealtimeSuspended
Definition: RealtimeEffectManager.h:54
RealtimeEffectState::mRealtimeSuspendCount
std::atomic< int > mRealtimeSuspendCount
Definition: RealtimeEffectManager.cpp:40
EffectClientInterface::RealtimeResume
virtual bool RealtimeResume()=0
RealtimeEffectManager::~RealtimeEffectManager
~RealtimeEffectManager()
Definition: RealtimeEffectManager.cpp:58
RealtimeEffectManager::RealtimeAddProcessor
void RealtimeAddProcessor(int group, unsigned chans, float rate)
Definition: RealtimeEffectManager.cpp:188
RealtimeEffectState::RealtimeEffectState
RealtimeEffectState(EffectClientInterface &effect)
Definition: RealtimeEffectManager.cpp:407
RealtimeEffectState::RealtimeProcess
size_t RealtimeProcess(int group, unsigned chans, float **inbuf, float **outbuf, size_t numSamples)
Definition: RealtimeEffectManager.cpp:501